[COMMON] nanohub base from CHRE p-version
authorSukwon Ryoo <sw.ryoo@samsung.com>
Thu, 17 May 2018 04:05:37 +0000 (13:05 +0900)
committerEunyoung Lee <ey470.lee@samsung.com>
Wed, 20 Jun 2018 01:17:13 +0000 (10:17 +0900)
b4cbbe1 nanohub: fix out of bounds write in nanohub_spi_read
fdba416 Merge branch 'android-msm-marlin-3.18-nyc-mr2' into android-msm-marlin-3.18
c9f88a4 nanohub: detect wakeup signal stuck high and reset
e8faaee ANDROID: fix uninitilized variable
e9199f7 nanohub: error reset time calculation should use monotonic time

Change-Id: Ie2cb50046dddc3c607b5977c31813a6e8ee4fab9
Signed-off-by: Sukwon Ryoo <sw.ryoo@samsung.com>
13 files changed:
drivers/staging/nanohub/Kconfig [new file with mode: 0644]
drivers/staging/nanohub/Makefile [new file with mode: 0644]
drivers/staging/nanohub/bl.c [new file with mode: 0644]
drivers/staging/nanohub/bl.h [new file with mode: 0644]
drivers/staging/nanohub/comms.c [new file with mode: 0644]
drivers/staging/nanohub/comms.h [new file with mode: 0644]
drivers/staging/nanohub/i2c.c [new file with mode: 0644]
drivers/staging/nanohub/i2c.h [new file with mode: 0644]
drivers/staging/nanohub/main.c [new file with mode: 0644]
drivers/staging/nanohub/main.h [new file with mode: 0644]
drivers/staging/nanohub/spi.c [new file with mode: 0644]
drivers/staging/nanohub/spi.h [new file with mode: 0644]
include/linux/platform_data/nanohub.h [new file with mode: 0644]

diff --git a/drivers/staging/nanohub/Kconfig b/drivers/staging/nanohub/Kconfig
new file mode 100644 (file)
index 0000000..b798ebb
--- /dev/null
@@ -0,0 +1,33 @@
+config NANOHUB
+       tristate "Nanohub"
+       default N
+       help
+         Enable support for the nanohub sensorhub driver.
+
+         This driver supports the android nanohub sensorhub.
+
+         If in doubt, say N here.
+
+if NANOHUB
+
+config NANOHUB_SPI
+       bool "Nanohub SPI"
+       default Y
+       help
+         Enable nanohub SPI support.
+
+         Either this or NANOHUB_I2C should be selected.
+
+         If in doubt, say Y here.
+
+config NANOHUB_I2C
+       bool "Nanohub I2C"
+       default N
+       help
+         Enable nanohub I2C support.
+
+         Either this or NANOHUB_SPI should be selected.
+
+         If in doubt, say N here.
+
+endif # NANOHUB
diff --git a/drivers/staging/nanohub/Makefile b/drivers/staging/nanohub/Makefile
new file mode 100644 (file)
index 0000000..d7e0da7
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for nanohub
+#
+
+obj-$(CONFIG_NANOHUB) += nanohub.o
+nanohub-y := main.o bl.o comms.o
+nanohub-$(CONFIG_NANOHUB_SPI) += spi.o
+nanohub-$(CONFIG_NANOHUB_I2C) += i2c.o
diff --git a/drivers/staging/nanohub/bl.c b/drivers/staging/nanohub/bl.c
new file mode 100644 (file)
index 0000000..9a657f4
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/platform_data/nanohub.h>
+#include <linux/vmalloc.h>
+
+#include "main.h"
+#include "bl.h"
+
+#define MAX_BUFFER_SIZE                1024
+#define MAX_FLASH_BANKS                16
+#define READ_ACK_TIMEOUT       100000
+
+static uint8_t write_len(struct nanohub_data *data, int len)
+{
+       uint8_t buffer[sizeof(uint8_t) + 1];
+
+       buffer[0] = len - 1;
+
+       return data->bl.write_data(data, buffer, sizeof(uint8_t));
+}
+
+static uint8_t write_cnt(struct nanohub_data *data, uint16_t cnt)
+{
+       uint8_t buffer[sizeof(uint16_t) + 1];
+
+       buffer[0] = (cnt >> 8) & 0xFF;
+       buffer[1] = (cnt >> 0) & 0xFF;
+
+       return data->bl.write_data(data, buffer, sizeof(uint16_t));
+}
+
+static uint8_t write_addr(struct nanohub_data *data, uint32_t addr)
+{
+       uint8_t buffer[sizeof(uint32_t) + 1];
+
+       buffer[0] = (addr >> 24) & 0xFF;
+       buffer[1] = (addr >> 16) & 0xFF;
+       buffer[2] = (addr >> 8) & 0xFF;
+       buffer[3] = addr & 0xFF;
+
+       return data->bl.write_data(data, buffer, sizeof(uint32_t));
+}
+
+/* write length followed by the data */
+static uint8_t write_len_data(struct nanohub_data *data, int len,
+                             const uint8_t *buf)
+{
+       uint8_t buffer[sizeof(uint8_t) + 256 + sizeof(uint8_t)];
+
+       buffer[0] = len - 1;
+
+       memcpy(&buffer[1], buf, len);
+
+       return data->bl.write_data(data, buffer, sizeof(uint8_t) + len);
+}
+
+/* keep checking for ack until we receive a ack or nack */
+static uint8_t read_ack_loop(struct nanohub_data *data)
+{
+       uint8_t ret;
+       int32_t timeout = READ_ACK_TIMEOUT;
+
+       do {
+               ret = data->bl.read_ack(data);
+               if (ret != CMD_ACK && ret != CMD_NACK)
+                       schedule();
+       } while (ret != CMD_ACK && ret != CMD_NACK && timeout-- > 0);
+
+       return ret;
+}
+
+uint8_t nanohub_bl_sync(struct nanohub_data *data)
+{
+       return data->bl.sync(data);
+}
+
+int nanohub_bl_open(struct nanohub_data *data)
+{
+       int ret = -1;
+
+       data->bl.tx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+       if (!data->bl.tx_buffer)
+               goto out;
+
+       data->bl.rx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+       if (!data->bl.rx_buffer)
+               goto free_tx;
+
+       ret = data->bl.open(data);
+       if (!ret)
+               goto out;
+
+       kfree(data->bl.rx_buffer);
+free_tx:
+       kfree(data->bl.tx_buffer);
+out:
+       return ret;
+}
+
+void nanohub_bl_close(struct nanohub_data *data)
+{
+       data->bl.close(data);
+       kfree(data->bl.tx_buffer);
+       kfree(data->bl.rx_buffer);
+}
+
+static uint8_t write_bank(struct nanohub_data *data, int bank, uint32_t addr,
+                         const uint8_t *buf, size_t length)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+       uint8_t status = CMD_ACK;
+       uint32_t offset;
+
+       if (addr <= pdata->flash_banks[bank].address) {
+               offset = pdata->flash_banks[bank].address - addr;
+               if (addr + length >
+                   pdata->flash_banks[bank].address +
+                   pdata->flash_banks[bank].length)
+                       status =
+                           nanohub_bl_write_memory(data,
+                                                   pdata->flash_banks[bank].
+                                                   address,
+                                                   pdata->flash_banks[bank].
+                                                   length, buf + offset);
+               else
+                       status =
+                           nanohub_bl_write_memory(data,
+                                                   pdata->flash_banks[bank].
+                                                   address, length - offset,
+                                                   buf + offset);
+       } else {
+               if (addr + length >
+                   pdata->flash_banks[bank].address +
+                   pdata->flash_banks[bank].length)
+                       status =
+                           nanohub_bl_write_memory(data, addr,
+                                                   pdata->flash_banks[bank].
+                                                   address +
+                                                   pdata->flash_banks[bank].
+                                                   length - addr, buf);
+               else
+                       status =
+                           nanohub_bl_write_memory(data, addr, length, buf);
+       }
+
+       return status;
+}
+
+uint8_t nanohub_bl_download(struct nanohub_data *data, uint32_t addr,
+                           const uint8_t *image, size_t length)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+       uint8_t *ptr;
+       int i, j;
+       uint8_t status;
+       uint32_t offset;
+       uint8_t erase_mask[MAX_FLASH_BANKS] = { 0 };
+       uint8_t erase_write_mask[MAX_FLASH_BANKS] = { 0 };
+       uint8_t write_mask[MAX_FLASH_BANKS] = { 0 };
+
+       if (pdata->num_flash_banks > MAX_FLASH_BANKS) {
+               status = CMD_NACK;
+               goto out;
+       }
+
+       status = nanohub_bl_sync(data);
+
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_download: sync=%02x\n", status);
+               goto out;
+       }
+
+       ptr = vmalloc(length);
+       if (!ptr) {
+               status = CMD_NACK;
+               goto out;
+       }
+
+       status = nanohub_bl_read_memory(data, addr, length, ptr);
+       pr_info(
+           "nanohub: nanohub_bl_read_memory: status=%02x, addr=%08x, length=%zd\n",
+           status, addr, length);
+
+       for (i = 0; i < pdata->num_flash_banks; i++) {
+               if (addr >= pdata->flash_banks[i].address &&
+                   addr <
+                   pdata->flash_banks[i].address +
+                   pdata->flash_banks[i].length) {
+                       break;
+               }
+       }
+
+       offset = (uint32_t) (addr - pdata->flash_banks[i].address);
+       j = 0;
+       while (j < length && i < pdata->num_flash_banks) {
+               if (image[j] != 0xFF)
+                       erase_write_mask[i] = true;
+
+               if ((ptr[j] & image[j]) != image[j]) {
+                       erase_mask[i] = true;
+                       if (erase_write_mask[i]) {
+                               j += pdata->flash_banks[i].length - offset;
+                               offset = pdata->flash_banks[i].length;
+                       } else {
+                               j++;
+                               offset++;
+                       }
+               } else {
+                       if (ptr[j] != image[j])
+                               write_mask[i] = true;
+                       j++;
+                       offset++;
+               }
+
+               if (offset == pdata->flash_banks[i].length) {
+                       i++;
+                       offset = 0;
+                       if (i < pdata->num_flash_banks)
+                               j += (pdata->flash_banks[i].address -
+                                     pdata->flash_banks[i - 1].address -
+                                     pdata->flash_banks[i - 1].length);
+                       else
+                               j = length;
+               }
+       }
+
+       for (i = 0; status == CMD_ACK && i < pdata->num_flash_banks; i++) {
+               pr_info("nanohub: i=%d, erase=%d, erase_write=%d, write=%d\n",
+                       i, erase_mask[i], erase_write_mask[i], write_mask[i]);
+               if (erase_mask[i]) {
+                       status =
+                           nanohub_bl_erase_sector(data,
+                                                   pdata->flash_banks[i].bank);
+                       if (status == CMD_ACK && erase_write_mask[i])
+                               status =
+                                   write_bank(data, i, addr, image, length);
+               } else if (write_mask[i]) {
+                       status = write_bank(data, i, addr, image, length);
+               }
+       }
+
+       vfree(ptr);
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_erase_shared(struct nanohub_data *data)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+       int i;
+       uint8_t status;
+
+       if (pdata->num_shared_flash_banks > MAX_FLASH_BANKS) {
+               status = CMD_NACK;
+               goto out;
+       }
+
+       status = nanohub_bl_sync(data);
+
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_erase_shared: sync=%02x\n", status);
+               goto out;
+       }
+
+       for (i = 0;
+            status == CMD_ACK && i < pdata->num_shared_flash_banks;
+            i++) {
+               status = nanohub_bl_erase_sector(data,
+                   pdata->shared_flash_banks[i].bank);
+       }
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_erase_shared_bl(struct nanohub_data *data)
+{
+       uint8_t status;
+
+       status = nanohub_bl_sync(data);
+
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_erase_shared_bl: sync=%02x\n", status);
+               goto out;
+       }
+
+       status = nanohub_bl_erase_special(data, 0xFFF0);
+out:
+       return status;
+}
+
+/* erase a single sector */
+uint8_t nanohub_bl_erase_sector(struct nanohub_data *data, uint16_t sector)
+{
+       uint8_t ret;
+
+       data->bl.write_cmd(data, data->bl.cmd_erase);
+       ret = data->bl.read_ack(data);
+       if (ret == CMD_ACK)
+               ret = write_cnt(data, 0x0000);
+       if (ret != CMD_NACK)
+               ret = read_ack_loop(data);
+       if (ret == CMD_ACK)
+               ret = write_cnt(data, sector);
+       if (ret != CMD_NACK)
+               ret = read_ack_loop(data);
+
+       return ret;
+}
+
+/* erase special */
+uint8_t nanohub_bl_erase_special(struct nanohub_data *data, uint16_t special)
+{
+       uint8_t ret;
+
+       data->bl.write_cmd(data, data->bl.cmd_erase);
+       ret = data->bl.read_ack(data);
+       if (ret == CMD_ACK)
+               ret = write_cnt(data, special);
+       if (ret != CMD_NACK)
+               ret = read_ack_loop(data);
+
+       return ret;
+}
+
+/* read memory - this will chop the request into 256 byte reads */
+uint8_t nanohub_bl_read_memory(struct nanohub_data *data, uint32_t addr,
+                              uint32_t length, uint8_t *buffer)
+{
+       uint8_t ret = CMD_ACK;
+       uint32_t offset = 0;
+
+       while (ret == CMD_ACK && length > offset) {
+               data->bl.write_cmd(data, data->bl.cmd_read_memory);
+               ret = data->bl.read_ack(data);
+               if (ret == CMD_ACK) {
+                       write_addr(data, addr + offset);
+                       ret = read_ack_loop(data);
+                       if (ret == CMD_ACK) {
+                               if (length - offset >= 256) {
+                                       write_len(data, 256);
+                                       ret = read_ack_loop(data);
+                                       if (ret == CMD_ACK) {
+                                               data->bl.read_data(data,
+                                                                  &buffer
+                                                                  [offset],
+                                                                  256);
+                                               offset += 256;
+                                       }
+                               } else {
+                                       write_len(data, length - offset);
+                                       ret = read_ack_loop(data);
+                                       if (ret == CMD_ACK) {
+                                               data->bl.read_data(data,
+                                                                  &buffer
+                                                                  [offset],
+                                                                  length -
+                                                                  offset);
+                                               offset = length;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/* write memory - this will chop the request into 256 byte writes */
+uint8_t nanohub_bl_write_memory(struct nanohub_data *data, uint32_t addr,
+                               uint32_t length, const uint8_t *buffer)
+{
+       uint8_t ret = CMD_ACK;
+       uint32_t offset = 0;
+
+       while (ret == CMD_ACK && length > offset) {
+               data->bl.write_cmd(data, data->bl.cmd_write_memory);
+               ret = data->bl.read_ack(data);
+               if (ret == CMD_ACK) {
+                       write_addr(data, addr + offset);
+                       ret = read_ack_loop(data);
+                       if (ret == CMD_ACK) {
+                               if (length - offset >= 256) {
+                                       write_len_data(data, 256,
+                                                      &buffer[offset]);
+                                       offset += 256;
+                               } else {
+                                       write_len_data(data, length - offset,
+                                                      &buffer[offset]);
+                                       offset = length;
+                               }
+                               ret = read_ack_loop(data);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+uint8_t nanohub_bl_get_version(struct nanohub_data *data, uint8_t *version)
+{
+       uint8_t status;
+
+       status = nanohub_bl_sync(data);
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_get_version: sync=%02x\n", status);
+               goto out;
+       }
+
+       data->bl.write_cmd(data, data->bl.cmd_get_version);
+       status = data->bl.read_ack(data);
+       if (status == CMD_ACK)
+               data->bl.read_data(data, version, 1);
+       status = data->bl.read_ack(data);
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_get_id(struct nanohub_data *data, uint16_t *id)
+{
+       uint8_t status;
+       uint8_t len;
+       uint8_t buffer[256];
+
+       status = nanohub_bl_sync(data);
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_get_id: sync=%02x\n", status);
+               goto out;
+       }
+
+       data->bl.write_cmd(data, data->bl.cmd_get_id);
+       status = data->bl.read_ack(data);
+       if (status == CMD_ACK) {
+               data->bl.read_data(data, &len, 1);
+               data->bl.read_data(data, buffer, len+1);
+               *id = (buffer[0] << 8) | buffer[1];
+       }
+       status = data->bl.read_ack(data);
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_lock(struct nanohub_data *data)
+{
+       uint8_t status;
+
+       status = nanohub_bl_sync(data);
+
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_lock: sync=%02x\n", status);
+               goto out;
+       }
+
+       data->bl.write_cmd(data, data->bl.cmd_readout_protect);
+       status = data->bl.read_ack(data);
+       if (status == CMD_ACK)
+               status = read_ack_loop(data);
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_unlock(struct nanohub_data *data)
+{
+       uint8_t status;
+
+       status = nanohub_bl_sync(data);
+
+       if (status != CMD_ACK) {
+               pr_err("nanohub_bl_lock: sync=%02x\n", status);
+               goto out;
+       }
+
+       data->bl.write_cmd(data, data->bl.cmd_readout_unprotect);
+       status = data->bl.read_ack(data);
+       if (status == CMD_ACK)
+               status = read_ack_loop(data);
+out:
+       return status;
+}
+
+uint8_t nanohub_bl_update_finished(struct nanohub_data *data)
+{
+       uint8_t ret;
+
+       data->bl.write_cmd(data, data->bl.cmd_update_finished);
+       ret = read_ack_loop(data);
+
+       return ret;
+}
diff --git a/drivers/staging/nanohub/bl.h b/drivers/staging/nanohub/bl.h
new file mode 100644 (file)
index 0000000..3027058
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _NANOHUB_BL_H
+#define _NANOHUB_BL_H
+
+#include <linux/platform_data/nanohub.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+
+struct nanohub_data;
+
+struct nanohub_bl {
+       uint8_t cmd_erase;
+       uint8_t cmd_read_memory;
+       uint8_t cmd_write_memory;
+       uint8_t cmd_get_version;
+       uint8_t cmd_get_id;
+       uint8_t cmd_readout_protect;
+       uint8_t cmd_readout_unprotect;
+       uint8_t cmd_update_finished;
+
+       int (*open)(const void *);
+       void (*close)(const void *);
+       uint8_t (*sync)(const void *);
+       uint8_t (*write_data)(const void *, uint8_t *, int);
+       uint8_t (*write_cmd)(const void *, uint8_t);
+       uint8_t (*read_data)(const void *, uint8_t *, int);
+       uint8_t (*read_ack)(const void *);
+
+       uint8_t *tx_buffer;
+       uint8_t *rx_buffer;
+};
+
+int nanohub_bl_open(struct nanohub_data *);
+uint8_t nanohub_bl_sync(struct nanohub_data *);
+void nanohub_bl_close(struct nanohub_data *);
+uint8_t nanohub_bl_download(struct nanohub_data *, uint32_t addr,
+                           const uint8_t *data, size_t length);
+uint8_t nanohub_bl_erase_shared(struct nanohub_data *);
+uint8_t nanohub_bl_erase_shared_bl(struct nanohub_data *);
+uint8_t nanohub_bl_erase_sector(struct nanohub_data *, uint16_t);
+uint8_t nanohub_bl_erase_special(struct nanohub_data *, uint16_t);
+uint8_t nanohub_bl_read_memory(struct nanohub_data *, uint32_t, uint32_t,
+                              uint8_t *);
+uint8_t nanohub_bl_write_memory(struct nanohub_data *, uint32_t, uint32_t,
+                               const uint8_t *);
+uint8_t nanohub_bl_get_version(struct nanohub_data *, uint8_t *);
+uint8_t nanohub_bl_get_id(struct nanohub_data *, uint16_t *);
+uint8_t nanohub_bl_lock(struct nanohub_data *);
+uint8_t nanohub_bl_unlock(struct nanohub_data *);
+uint8_t nanohub_bl_update_finished(struct nanohub_data *);
+
+/*
+ * Bootloader commands
+ * _NS versions are no-stretch. (Only valid on I2C)
+ * will return CMD_BUSY instead of stretching the clock
+ */
+
+#define CMD_GET                                0x00
+#define CMD_GET_VERSION                        0x01
+#define CMD_GET_ID                     0x02
+#define CMD_READ_MEMORY                        0x11
+#define CMD_NACK                       0x1F
+#define CMD_GO                         0x21
+#define CMD_WRITE_MEMORY               0x31
+#define CMD_WRITE_MEMORY_NS            0x32
+#define CMD_ERASE                      0x44
+#define CMD_ERASE_NS                   0x45
+#define CMD_SOF                                0x5A
+#define CMD_WRITE_PROTECT              0x63
+#define CMD_WRITE_PROTECT_NS           0x64
+#define CMD_WRITE_UNPROTECT            0x73
+#define CMD_WRITE_UNPROTECT_NS         0x74
+#define CMD_BUSY                       0x76
+#define CMD_ACK                                0x79
+#define CMD_READOUT_PROTECT            0x82
+#define CMD_READOUT_PROTECT_NS         0x83
+#define CMD_READOUT_UNPROTECT          0x92
+#define CMD_READOUT_UNPROTECT_NS       0x93
+#define CMD_SOF_ACK                    0xA5
+#define CMD_GET_SIZES                  0xEE
+#define CMD_UPDATE_FINISHED            0xEF
+
+#endif
diff --git a/drivers/staging/nanohub/comms.c b/drivers/staging/nanohub/comms.c
new file mode 100644 (file)
index 0000000..82590d5
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+#include <linux/gpio.h>
+
+#include "main.h"
+#include "comms.h"
+
+#define READ_ACK_TIMEOUT_MS    10
+#define READ_MSG_TIMEOUT_MS    70
+
+#define RESEND_SHORT_DELAY_US   500     /* 500us - 1ms */
+#define RESEND_LONG_DELAY_US    100000  /* 100ms - 200ms */
+
+static const uint32_t crc_table[] = {
+       0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
+       0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
+       0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
+       0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
+};
+
+static uint32_t crc32_word(uint32_t crc, uint32_t data, int cnt)
+{
+       int i;
+       crc = crc ^ data;
+
+       for (i = 0; i < cnt; i++)
+               crc = (crc << 4) ^ crc_table[crc >> 28];
+
+       return crc;
+}
+
+uint32_t crc32(const uint8_t *buffer, int length, uint32_t crc)
+{
+       uint32_t *data = (uint32_t *)buffer;
+       uint32_t word;
+       int i;
+
+       /* word by word crc32 */
+       for (i = 0; i < (length >> 2); i++)
+               crc = crc32_word(crc, data[i], 8);
+
+       /* zero pad last word if required */
+       if (length & 0x3) {
+               for (i *= 4, word = 0; i < length; i++)
+                       word |= buffer[i] << ((i & 0x3) * 8);
+               crc = crc32_word(crc, word, 8);
+       }
+
+       return crc;
+}
+
+static inline size_t pad(size_t length)
+{
+       return (length + 3) & ~3;
+}
+
+static inline size_t tot_len(size_t length)
+{
+       /* [TYPE:1] [LENGTH:3] [DATA] [PAD:0-3] [CRC:4] */
+       return sizeof(uint32_t) + pad(length) + sizeof(uint32_t);
+}
+
+static struct nanohub_packet_pad *packet_alloc(int flags)
+{
+       int len =
+           sizeof(struct nanohub_packet_pad) + MAX_UINT8 +
+           sizeof(struct nanohub_packet_crc);
+       uint8_t *packet = kmalloc(len, flags);
+       memset(packet, 0xFF, len);
+       return (struct nanohub_packet_pad *)packet;
+}
+
+static int packet_create(struct nanohub_packet *packet, uint32_t seq,
+                        uint32_t reason, uint8_t len, const uint8_t *data,
+                        bool user)
+{
+       struct nanohub_packet_crc crc;
+       int ret = sizeof(struct nanohub_packet) + len +
+           sizeof(struct nanohub_packet_crc);
+
+       if (packet) {
+               packet->sync = COMMS_SYNC;
+               packet->seq = seq;
+               packet->reason = reason;
+               packet->len = len;
+               if (len > 0) {
+                       if (user) {
+                               if (copy_from_user(packet->data, data, len) !=
+                                   0)
+                                       ret = ERROR_NACK;
+                       } else {
+                               memcpy(packet->data, data, len);
+                       }
+               }
+               crc.crc =
+                   crc32((uint8_t *) packet,
+                         sizeof(struct nanohub_packet) + len, ~0);
+               memcpy(&packet->data[len], &crc.crc,
+                      sizeof(struct nanohub_packet_crc));
+       } else {
+               ret = ERROR_NACK;
+       }
+
+       return ret;
+}
+
+static int packet_verify(struct nanohub_packet *packet)
+{
+       struct nanohub_packet_crc crc;
+       int cmp;
+
+       crc.crc =
+           crc32((uint8_t *) packet,
+                 sizeof(struct nanohub_packet) + packet->len, ~0);
+
+       cmp =
+           memcmp(&crc.crc, &packet->data[packet->len],
+                  sizeof(struct nanohub_packet_crc));
+
+       if (cmp != 0) {
+               uint8_t *ptr = (uint8_t *)packet;
+               pr_debug("nanohub: gen crc: %08x, got crc: %08x\n", crc.crc,
+                        *(uint32_t *)&packet->data[packet->len]);
+               pr_debug(
+                   "nanohub: %02x [%02x %02x %02x %02x] [%02x %02x %02x %02x] [%02x] [%02x %02x %02x %02x\n",
+                   ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
+                   ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12],
+                   ptr[13]);
+       }
+
+       return cmp;
+}
+
+static void packet_free(struct nanohub_packet_pad *packet)
+{
+       kfree(packet);
+}
+
+static int read_ack(struct nanohub_data *data, struct nanohub_packet *response,
+                   int timeout)
+{
+       int ret, i;
+       const int max_size = sizeof(struct nanohub_packet) + MAX_UINT8 +
+           sizeof(struct nanohub_packet_crc);
+       unsigned long end = jiffies + msecs_to_jiffies(READ_ACK_TIMEOUT_MS);
+
+       for (i = 0; time_before_eq(jiffies, end); i++) {
+               ret =
+                   data->comms.read(data, (uint8_t *) response, max_size,
+                                    timeout);
+
+               if (ret == 0) {
+                       pr_debug("nanohub: read_ack: %d: empty packet\n", i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret < sizeof(struct nanohub_packet)) {
+                       pr_debug("nanohub: read_ack: %d: too small\n", i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret <
+                          sizeof(struct nanohub_packet) + response->len +
+                          sizeof(struct nanohub_packet_crc)) {
+                       pr_debug("nanohub: read_ack: %d: too small length\n",
+                                i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret !=
+                          sizeof(struct nanohub_packet) + response->len +
+                          sizeof(struct nanohub_packet_crc)) {
+                       pr_debug("nanohub: read_ack: %d: wrong length\n", i);
+                       ret = ERROR_NACK;
+                       break;
+               } else if (packet_verify(response) != 0) {
+                       pr_debug("nanohub: read_ack: %d: invalid crc\n", i);
+                       ret = ERROR_NACK;
+                       break;
+               } else {
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int read_msg(struct nanohub_data *data, struct nanohub_packet *response,
+                   int timeout)
+{
+       int ret, i;
+       const int max_size = sizeof(struct nanohub_packet) + MAX_UINT8 +
+           sizeof(struct nanohub_packet_crc);
+       unsigned long end = jiffies + msecs_to_jiffies(READ_MSG_TIMEOUT_MS);
+
+       for (i = 0; time_before_eq(jiffies, end); i++) {
+               ret =
+                   data->comms.read(data, (uint8_t *) response, max_size,
+                                    timeout);
+
+               if (ret == 0) {
+                       pr_debug("nanohub: read_msg: %d: empty packet\n", i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret < sizeof(struct nanohub_packet)) {
+                       pr_debug("nanohub: read_msg: %d: too small\n", i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret <
+                          sizeof(struct nanohub_packet) + response->len +
+                          sizeof(struct nanohub_packet_crc)) {
+                       pr_debug("nanohub: read_msg: %d: too small length\n",
+                                i);
+                       ret = ERROR_NACK;
+                       continue;
+               } else if (ret !=
+                          sizeof(struct nanohub_packet) + response->len +
+                          sizeof(struct nanohub_packet_crc)) {
+                       pr_debug("nanohub: read_msg: %d: wrong length\n", i);
+                       ret = ERROR_NACK;
+                       break;
+               } else if (packet_verify(response) != 0) {
+                       pr_debug("nanohub: read_msg: %d: invalid crc\n", i);
+                       ret = ERROR_NACK;
+                       break;
+               } else {
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int get_reply(struct nanohub_data *data, struct nanohub_packet *response,
+                    uint32_t seq)
+{
+       int ret;
+
+       ret = read_ack(data, response, data->comms.timeout_ack);
+
+       if (ret >= 0 && response->seq == seq) {
+               if (response->reason == CMD_COMMS_ACK) {
+                       if (response->len == sizeof(data->interrupts))
+                               memcpy(data->interrupts, response->data,
+                                      response->len);
+                       ret =
+                           read_msg(data, response, data->comms.timeout_reply);
+                       if (ret < 0)
+                               ret = ERROR_NACK;
+               } else {
+                       int i;
+                       uint8_t *b = (uint8_t *) response;
+                       for (i = 0; i < ret; i += 25)
+                               pr_debug(
+                                   "nanohub: %d: %d: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                                   ret, i, b[i], b[i + 1], b[i + 2], b[i + 3],
+                                   b[i + 4], b[i + 5], b[i + 6], b[i + 7],
+                                   b[i + 8], b[i + 9], b[i + 10], b[i + 11],
+                                   b[i + 12], b[i + 13], b[i + 14], b[i + 15],
+                                   b[i + 16], b[i + 17], b[i + 18], b[i + 19],
+                                   b[i + 20], b[i + 21], b[i + 22], b[i + 23],
+                                   b[i + 24]);
+                       if (response->reason == CMD_COMMS_NACK)
+                               ret = ERROR_NACK;
+                       else if (response->reason == CMD_COMMS_BUSY)
+                               ret = ERROR_BUSY;
+               }
+
+               if (response->seq != seq)
+                       ret = ERROR_NACK;
+       } else {
+               if (ret >= 0) {
+                       int i;
+                       uint8_t *b = (uint8_t *) response;
+                       for (i = 0; i < ret; i += 25)
+                               pr_debug(
+                                   "nanohub: %d: %d: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                                   ret, i, b[i], b[i + 1], b[i + 2], b[i + 3],
+                                   b[i + 4], b[i + 5], b[i + 6], b[i + 7],
+                                   b[i + 8], b[i + 9], b[i + 10], b[i + 11],
+                                   b[i + 12], b[i + 13], b[i + 14], b[i + 15],
+                                   b[i + 16], b[i + 17], b[i + 18], b[i + 19],
+                                   b[i + 20], b[i + 21], b[i + 22], b[i + 23],
+                                   b[i + 24]);
+               }
+               ret = ERROR_NACK;
+       }
+
+       return ret;
+}
+
+static int nanohub_comms_tx_rx(struct nanohub_data *data,
+                              struct nanohub_packet_pad *pad, int packet_size,
+                              uint32_t seq, uint8_t *rx, size_t rx_len)
+{
+       int ret;
+
+       ret = data->comms.write(data, (uint8_t *)&pad->packet, packet_size,
+                               data->comms.timeout_write);
+
+       if (ret == packet_size) {
+               ret = get_reply(data, &pad->packet, seq);
+
+               if (ret >= 0) {
+                       if (pad->packet.len > 0) {
+                               if (pad->packet.len > rx_len) {
+                                       memcpy(rx, pad->packet.data, rx_len);
+                                       ret = rx_len;
+                               } else {
+                                       memcpy(rx, pad->packet.data,
+                                              pad->packet.len);
+                                       ret = pad->packet.len;
+                               }
+                       } else {
+                               ret = 0;
+                       }
+               }
+       } else {
+               ret = ERROR_NACK;
+       }
+
+       return ret;
+}
+
+int nanohub_comms_rx_retrans_boottime(struct nanohub_data *data, uint32_t cmd,
+                                     uint8_t *rx, size_t rx_len,
+                                     int retrans_cnt, int retrans_delay)
+{
+       int packet_size = 0;
+       struct nanohub_packet_pad *pad = packet_alloc(GFP_KERNEL);
+       int delay = 0;
+       int ret;
+       uint32_t seq;
+       struct timespec ts;
+       s64 boottime;
+
+       if (pad == NULL)
+               return ERROR_NACK;
+
+       seq = data->comms.seq++;
+
+       do {
+               data->comms.open(data);
+               get_monotonic_boottime(&ts);
+               boottime = timespec_to_ns(&ts);
+               packet_size =
+                   packet_create(&pad->packet, seq, cmd, sizeof(boottime),
+                                 (uint8_t *)&boottime, false);
+
+               ret =
+                   nanohub_comms_tx_rx(data, pad, packet_size, seq, rx,
+                                       rx_len);
+
+               if (nanohub_wakeup_eom(data,
+                                      (ret == ERROR_BUSY) ||
+                                      (ret == ERROR_NACK && retrans_cnt >= 0)))
+                       ret = -EFAULT;
+
+               data->comms.close(data);
+
+               if (ret == ERROR_NACK) {
+                       retrans_cnt--;
+                       delay += retrans_delay;
+                       if (retrans_cnt >= 0)
+                               udelay(retrans_delay);
+               } else if (ret == ERROR_BUSY) {
+                       usleep_range(RESEND_LONG_DELAY_US,
+                                    RESEND_LONG_DELAY_US * 2);
+               }
+       } while ((ret == ERROR_BUSY)
+                || (ret == ERROR_NACK && retrans_cnt >= 0));
+
+       packet_free(pad);
+
+       return ret;
+}
+
+int nanohub_comms_tx_rx_retrans(struct nanohub_data *data, uint32_t cmd,
+                               const uint8_t *tx, uint8_t tx_len,
+                               uint8_t *rx, size_t rx_len, bool user,
+                               int retrans_cnt, int retrans_delay)
+{
+       int packet_size = 0;
+       struct nanohub_packet_pad *pad = packet_alloc(GFP_KERNEL);
+       int delay = 0;
+       int ret;
+       uint32_t seq;
+
+       if (pad == NULL)
+               return ERROR_NACK;
+
+       seq = data->comms.seq++;
+
+       do {
+               packet_size =
+                   packet_create(&pad->packet, seq, cmd, tx_len, tx, user);
+
+               data->comms.open(data);
+               ret =
+                   nanohub_comms_tx_rx(data, pad, packet_size, seq, rx,
+                                       rx_len);
+
+               if (nanohub_wakeup_eom(data,
+                                      (ret == ERROR_BUSY) ||
+                                      (ret == ERROR_NACK && retrans_cnt >= 0)))
+                       ret = -EFAULT;
+
+               data->comms.close(data);
+
+               if (ret == ERROR_NACK) {
+                       retrans_cnt--;
+                       delay += retrans_delay;
+                       if (retrans_cnt >= 0)
+                               udelay(retrans_delay);
+               } else if (ret == ERROR_BUSY) {
+                       usleep_range(RESEND_LONG_DELAY_US,
+                                    RESEND_LONG_DELAY_US * 2);
+               }
+       } while ((ret == ERROR_BUSY)
+                || (ret == ERROR_NACK && retrans_cnt >= 0));
+
+       packet_free(pad);
+
+       return ret;
+}
+
+struct firmware_header {
+       uint32_t size;
+       uint32_t crc;
+       uint8_t type;
+} __packed;
+
+struct firmware_chunk {
+       uint32_t offset;
+       uint8_t data[128];
+}
+__packed;
+
+static int nanohub_comms_download(struct nanohub_data *data,
+                                 const uint8_t *image, size_t length,
+                                 uint8_t type)
+{
+       uint8_t accepted;
+       struct firmware_header header;
+       struct firmware_chunk chunk;
+       int max_chunk_size = sizeof(chunk.data);
+       int chunk_size;
+       uint32_t offset = 0;
+       int ret;
+       uint8_t chunk_reply, upload_reply = 0, last_reply = 0;
+       uint32_t clear_interrupts[8] = { 0x00000008 };
+       uint32_t delay;
+
+       header.type = type;
+       header.size = cpu_to_le32(length);
+       header.crc = cpu_to_le32(~crc32(image, length, ~0));
+
+       if (request_wakeup(data))
+               return -ERESTARTSYS;
+       ret = nanohub_comms_tx_rx_retrans(data, CMD_COMMS_START_KERNEL_UPLOAD,
+                                         (const uint8_t *)&header,
+                                         sizeof(header), &accepted,
+                                         sizeof(accepted), false, 10, 10);
+       release_wakeup(data);
+
+       if (ret == 1 && accepted == 1) {
+               do {
+                       if (request_wakeup(data))
+                               continue;
+
+                       delay = 0;
+                       chunk.offset = cpu_to_le32(offset);
+                       if (offset + max_chunk_size > length)
+                               chunk_size = length - offset;
+                       else
+                               chunk_size = max_chunk_size;
+                       memcpy(chunk.data, image + offset, chunk_size);
+
+                       ret =
+                           nanohub_comms_tx_rx_retrans(data,
+                                                       CMD_COMMS_KERNEL_CHUNK,
+                                                       (const uint8_t *)&chunk,
+                                                       sizeof(uint32_t) +
+                                                       chunk_size,
+                                                       &chunk_reply,
+                                                       sizeof(chunk_reply),
+                                                       false, 10, 10);
+
+                       pr_debug("nanohub: ret=%d, chunk_reply=%d, offset=%d\n",
+                               ret, chunk_reply, offset);
+                       if (ret == sizeof(chunk_reply)) {
+                               if (chunk_reply == CHUNK_REPLY_ACCEPTED) {
+                                       offset += chunk_size;
+                               } else if (chunk_reply == CHUNK_REPLY_WAIT) {
+                                       ret = nanohub_wait_for_interrupt(data);
+                                       if (ret < 0) {
+                                               release_wakeup(data);
+                                               continue;
+                                       }
+                                       nanohub_comms_tx_rx_retrans(data,
+                                           CMD_COMMS_CLR_GET_INTR,
+                                           (uint8_t *)clear_interrupts,
+                                           sizeof(clear_interrupts),
+                                           (uint8_t *)data->interrupts,
+                                           sizeof(data->interrupts),
+                                           false, 10, 0);
+                               } else if (chunk_reply == CHUNK_REPLY_RESEND) {
+                                       if (last_reply == CHUNK_REPLY_RESEND)
+                                               delay = RESEND_LONG_DELAY_US;
+                                       else
+                                               delay = RESEND_SHORT_DELAY_US;
+                               } else if (chunk_reply == CHUNK_REPLY_RESTART)
+                                       offset = 0;
+                               else if (chunk_reply == CHUNK_REPLY_CANCEL ||
+                                   chunk_reply ==
+                                   CHUNK_REPLY_CANCEL_NO_RETRY) {
+                                       release_wakeup(data);
+                                       break;
+                               }
+                               last_reply = chunk_reply;
+                       } else if (ret <= 0) {
+                               release_wakeup(data);
+                               break;
+                       }
+                       release_wakeup(data);
+                       if (delay > 0)
+                               usleep_range(delay, delay * 2);
+               } while (offset < length);
+       }
+
+       do {
+               if (upload_reply == UPLOAD_REPLY_PROCESSING)
+                       usleep_range(RESEND_LONG_DELAY_US,
+                                    RESEND_LONG_DELAY_US * 2);
+
+               if (request_wakeup(data)) {
+                       ret = sizeof(upload_reply);
+                       upload_reply = UPLOAD_REPLY_PROCESSING;
+                       continue;
+               }
+               ret = nanohub_comms_tx_rx_retrans(data,
+                                       CMD_COMMS_FINISH_KERNEL_UPLOAD,
+                                       NULL, 0,
+                                       &upload_reply, sizeof(upload_reply),
+                                       false, 10, 10);
+               release_wakeup(data);
+       } while (ret == sizeof(upload_reply) &&
+                upload_reply == UPLOAD_REPLY_PROCESSING);
+
+       pr_info("nanohub: nanohub_comms_download: ret=%d, upload_reply=%d\n",
+               ret, upload_reply);
+
+       return 0;
+}
+
+int nanohub_comms_kernel_download(struct nanohub_data *data,
+                                 const uint8_t *image, size_t length)
+{
+       return nanohub_comms_download(data, image, length,
+                                     COMMS_FLASH_KERNEL_ID);
+}
+
+int nanohub_comms_app_download(struct nanohub_data *data, const uint8_t *image,
+                              size_t length)
+{
+       return nanohub_comms_download(data, image, length, COMMS_FLASH_APP_ID);
+}
diff --git a/drivers/staging/nanohub/comms.h b/drivers/staging/nanohub/comms.h
new file mode 100644 (file)
index 0000000..9479a9f
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _NANOHUB_COMMS_H
+#define _NANOHUB_COMMS_H
+
+struct __attribute__ ((__packed__)) nanohub_packet {
+       uint8_t sync;
+       uint32_t seq;
+       uint32_t reason;
+       uint8_t len;
+       uint8_t data[];
+};
+
+struct __attribute__ ((__packed__)) nanohub_packet_pad {
+       uint8_t pad[3];
+       struct nanohub_packet
+        packet;
+};
+
+struct __attribute__ ((__packed__)) nanohub_packet_crc {
+       uint32_t crc;
+};
+
+struct nanohub_data;
+
+struct nanohub_comms {
+       struct semaphore sem;
+       uint32_t seq;
+       int timeout_write;
+       int timeout_ack;
+       int timeout_reply;
+       int (*open)(void *);
+       void (*close)(void *);
+       int (*write)(void *, uint8_t *, int, int);
+       int (*read)(void *, uint8_t *, int, int);
+
+       union {
+               struct i2c_client *i2c_client;
+               struct spi_device *spi_device;
+       };
+
+       uint8_t *tx_buffer;
+       uint8_t *rx_buffer;
+};
+
+int nanohub_comms_kernel_download(struct nanohub_data *, const uint8_t *,
+                                 size_t);
+int nanohub_comms_app_download(struct nanohub_data *, const uint8_t *, size_t);
+int nanohub_comms_rx_retrans_boottime(struct nanohub_data *, uint32_t,
+                                     uint8_t *, size_t, int, int);
+int nanohub_comms_tx_rx_retrans(struct nanohub_data *, uint32_t,
+                               const uint8_t *, uint8_t, uint8_t *, size_t,
+                               bool, int, int);
+
+#define ERROR_NACK                     -1
+#define ERROR_BUSY                     -2
+
+#define MAX_UINT8                      ((1 << (8*sizeof(uint8_t))) - 1)
+
+#define COMMS_SYNC                     0x31
+#define COMMS_FLASH_KERNEL_ID          0x1
+#define COMMS_FLASH_EEDATA_ID          0x2
+#define COMMS_FLASH_APP_ID             0x4
+
+#define CMD_COMMS_ACK                  0x00000000
+#define CMD_COMMS_NACK                 0x00000001
+#define CMD_COMMS_BUSY                 0x00000002
+
+#define CMD_COMMS_GET_OS_HW_VERSIONS   0x00001000
+#define CMD_COMMS_GET_APP_VERSIONS     0x00001001
+#define CMD_COMMS_QUERY_APP_INFO       0x00001002
+
+#define CMD_COMMS_START_KERNEL_UPLOAD  0x00001040
+#define CMD_COMMS_KERNEL_CHUNK         0x00001041
+#define CMD_COMMS_FINISH_KERNEL_UPLOAD 0x00001042
+
+#define CMD_COMMS_START_APP_UPLOAD     0x00001050
+#define CMD_COMMS_APP_CHUNK            0x00001051
+
+#define CMD_COMMS_CLR_GET_INTR         0x00001080
+#define CMD_COMMS_MASK_INTR            0x00001081
+#define CMD_COMMS_UNMASK_INTR          0x00001082
+#define CMD_COMMS_READ                 0x00001090
+#define CMD_COMMS_WRITE                        0x00001091
+
+#define CHUNK_REPLY_ACCEPTED           0
+#define CHUNK_REPLY_WAIT                1
+#define CHUNK_REPLY_RESEND              2
+#define CHUNK_REPLY_RESTART             3
+#define CHUNK_REPLY_CANCEL              4
+#define CHUNK_REPLY_CANCEL_NO_RETRY     5
+
+#define UPLOAD_REPLY_SUCCESS                   0
+#define UPLOAD_REPLY_PROCESSING                        1
+#define UPLOAD_REPLY_WAITING_FOR_DATA          2
+#define UPLOAD_REPLY_APP_SEC_KEY_NOT_FOUND     3
+#define UPLOAD_REPLY_APP_SEC_HEADER_ERROR      4
+#define UPLOAD_REPLY_APP_SEC_TOO_MUCH_DATA     5
+#define UPLOAD_REPLY_APP_SEC_TOO_LITTLE_DATA   6
+#define UPLOAD_REPLY_APP_SEC_SIG_VERIFY_FAIL   7
+#define UPLOAD_REPLY_APP_SEC_SIG_DECODE_FAIL   8
+#define UPLOAD_REPLY_APP_SEC_SIG_ROOT_UNKNOWN  9
+#define UPLOAD_REPLY_APP_SEC_MEMORY_ERROR      10
+#define UPLOAD_REPLY_APP_SEC_INVALID_DATA      11
+#define UPLOAD_REPLY_APP_SEC_BAD               12
+
+static inline int nanohub_comms_write(struct nanohub_data *data,
+                                     const uint8_t *buffer, size_t buffer_len)
+{
+       uint8_t ret;
+       if (nanohub_comms_tx_rx_retrans
+           (data, CMD_COMMS_WRITE, buffer, buffer_len, &ret, sizeof(ret), true,
+            10, 10) == sizeof(ret)) {
+               if (ret)
+                       return buffer_len;
+               else
+                       return 0;
+       } else {
+               return ERROR_NACK;
+       }
+}
+
+#endif
diff --git a/drivers/staging/nanohub/i2c.c b/drivers/staging/nanohub/i2c.c
new file mode 100644 (file)
index 0000000..285f4ba
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#include "main.h"
+#include "bl.h"
+#include "comms.h"
+
+static uint8_t bl_checksum(const uint8_t *bytes, int length)
+{
+       int i;
+       uint8_t csum;
+
+       if (length == 1) {
+               csum = ~bytes[0];
+       } else if (length > 1) {
+               for (csum = 0, i = 0; i < length; i++)
+                       csum ^= bytes[i];
+       } else {
+               csum = 0xFF;
+       }
+
+       return csum;
+}
+
+static uint8_t i2c_bl_write_data(const struct nanohub_bl *bl, uint8_t *buffer,
+                                int length)
+{
+       buffer[length] = bl_checksum(buffer, length);
+
+       if (i2c_master_send(bl->i2c_client, buffer, length + 1) == (length + 1))
+               return CMD_ACK;
+       else
+               return CMD_NACK;
+}
+
+static uint8_t i2c_bl_write_cmd(const struct nanohub_bl *bl, uint8_t cmd)
+{
+       uint8_t buffer[sizeof(uint8_t) + 1] = {
+               cmd
+       };
+
+       return bl->write_data(bl, buffer, sizeof(uint8_t));
+}
+
+static uint8_t i2c_bl_read_data(const struct nanohub_bl *bl, uint8_t *data,
+                               int length)
+{
+       if (i2c_master_recv(bl->i2c_client, data, length) == length)
+               return CMD_ACK;
+       else
+               return CMD_NACK;
+}
+
+static uint8_t i2c_bl_read_ack(const struct nanohub_bl *bl)
+{
+       uint8_t buffer;
+
+       if (bl->read_data(bl, &buffer, sizeof(uint8_t)) == CMD_ACK)
+               return buffer;
+       else
+               return CMD_NACK;
+}
+
+static void i2c_bl_open(const struct nanohub_bl *bl)
+{
+}
+
+static void i2c_bl_close(const struct nanohub_bl *bl)
+{
+}
+
+static uint8_t i2c_bl_sync(const struct nanohub_bl *bl)
+{
+       return CMD_ACK;
+}
+
+void nanohub_i2c_bl_init(struct nanohub_bl *bl)
+{
+       bl->open = i2c_bl_open;
+       bl->sync = i2c_bl_sync;
+       bl->write_data = i2c_bl_write_data;
+       bl->write_cmd = i2c_bl_write_cmd;
+       bl->read_data = i2c_bl_read_data;
+       bl->read_ack = i2c_bl_read_ack;
+       bl->close = i2c_bl_close;
+}
+
+int nanohub_i2c_write(const struct nanohub_comms *comms, uint8_t *data,
+                     int length)
+{
+       return i2c_master_send(comms->i2c_client, data, length);
+}
+
+int nanohub_i2c_read(struct nanohub_comms *comms, uint8_t *data,
+                    int max_length, int timeout)
+{
+       const int min_size = sizeof(struct nanohub_packet) +
+           sizeof(struct nanohub_packet_crc);
+       int i, ret, read_cnt = min_size, read_len = 0, read_total = 0;
+       struct nanohub_packet *packet;
+
+       if (max_length < min_size)
+               return ERROR_NACK;
+
+       do {
+               pr_debug
+                   ("master_recv: read_len=%d, read_cnt=%d, read_total=%d\n",
+                    read_len, read_cnt, read_total);
+               ret =
+                   i2c_master_recv(comms->i2c_client, &data[read_len],
+                                   read_cnt);
+               pr_debug("master_recv: ret=%d\n", ret);
+               pr_debug(
+                   "data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                   data[0], data[1], data[2], data[3], data[4], data[5],
+                   data[6], data[7], data[8], data[9], data[10], data[11],
+                   data[12], data[13], data[14], data[15], data[16], data[17],
+                   data[18], data[19], data[20], data[21], data[22]);
+               if (ret == read_cnt) {
+                       if (read_len == 0) {
+                               for (i = 0; i < ret; i++) {
+                                       if (data[i] != 0xFF) {
+                                               if (i > 0) {
+                                                       pr_debug(
+                                                           "i=%d, read_cnt - i = %d\n",
+                                                           i, read_cnt - i);
+                                                       memmove(data, &data[i],
+                                                               read_cnt - i);
+                                               }
+                                               read_len = read_cnt - i;
+                                               break;
+                                       } else {
+                                               timeout--;
+                                       }
+                               }
+                       } else {
+                               read_len += read_cnt;
+                       }
+
+                       packet = (struct nanohub_packet *)data;
+                       if (read_len >= offsetof(struct nanohub_packet, len) +
+                           sizeof(packet->len)) {
+                               read_total = packet->len + min_size;
+                               read_cnt = read_total - read_len;
+                       } else {
+                               read_cnt = min_size - read_len;
+                       }
+               } else {
+                       break;
+               }
+
+               if (read_cnt + read_len > max_length) {
+                       ret = ERROR_NACK;
+                       break;
+               }
+       } while (timeout > 0 && (read_total == 0 || read_total != read_len));
+
+       if (ret < 0)
+               return ret;
+       else if (timeout <= 0)
+               return 0;
+       else
+               return read_len;
+}
+
+static void nanohub_i2c_open(const struct nanohub_comms *comms)
+{
+}
+
+static void nanohub_i2c_close(const struct nanohub_comms *comms)
+{
+}
+
+void nanohub_i2c_comms_init(struct nanohub_comms *comms)
+{
+       comms->timeout_ack = 128;
+       comms->timeout_reply = 512;
+       comms->open = nanohub_i2c_open;
+       comms->close = nanohub_i2c_close;
+       comms->write = nanohub_i2c_write;
+       comms->read = nanohub_i2c_read;
+}
+
+static int nanohub_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct nanohub_data *data;
+       struct iio_dev *iio_dev;
+
+       pr_info("nanohub: i2c_probe:\n");
+
+       data = nanohub_probe(&client->dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       iio_dev = iio_priv_to_dev(data);
+       i2c_set_clientdata(client, iio_dev);
+
+       data->comms.i2c_client = client;
+       nanohub_i2c_comms_init(&data->comms);
+
+       data->bl.cmd_erase = CMD_ERASE_NS;
+       data->bl.cmd_read_memory = CMD_READ_MEMORY;
+       data->bl.cmd_write_memory = CMD_WRITE_MEMORY_NS;
+       data->bl.i2c_client = i2c_new_dummy(client->adapter, 0x39);
+       i2c_set_clientdata(data->bl.i2c_client, iio_dev);
+       nanohub_i2c_bl_init(&data->bl);
+
+       return 0;
+}
+
+static int nanohub_i2c_remove(struct i2c_client *client)
+{
+       struct nanohub_data *data;
+
+       data = iio_priv(i2c_get_clientdata(client));
+
+       i2c_unregister_device(data->bl.i2c_client);
+
+       return nanohub_remove(data);
+}
+
+static struct i2c_device_id nanohub_i2c_id[] = {
+       {"nanohub", 0},
+       {},
+};
+
+static struct i2c_driver nanohub_i2c_driver = {
+       .driver = {
+                  .name = "nanohub",
+                  },
+       .probe = nanohub_i2c_probe,
+       .remove = nanohub_i2c_remove,
+       .id_table = nanohub_i2c_id,
+};
+
+static const struct iio_info nanohub_iio_info = {
+       .driver_module = THIS_MODULE,
+};
+
+int __init nanohub_i2c_init(void)
+{
+       return i2c_add_driver(&nanohub_i2c_driver);
+}
+
+void nanohub_i2c_cleanup(void)
+{
+       i2c_del_driver(&nanohub_i2c_driver);
+}
+
+MODULE_DEVICE_TABLE(i2c, nanohub_i2c_id);
diff --git a/drivers/staging/nanohub/i2c.h b/drivers/staging/nanohub/i2c.h
new file mode 100644 (file)
index 0000000..23fa27f
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _NANOHUB_I2C_H
+#define _NANOHUB_I2C_H
+
+int __init nanohub_i2c_init(void);
+void nanohub_i2c_cleanup(void);
+
+#endif
diff --git a/drivers/staging/nanohub/main.c b/drivers/staging/nanohub/main.c
new file mode 100644 (file)
index 0000000..2d3daac
--- /dev/null
@@ -0,0 +1,1801 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+#include <linux/time.h>
+#include <linux/platform_data/nanohub.h>
+
+#include "main.h"
+#include "comms.h"
+#include "bl.h"
+#include "spi.h"
+
+#define READ_QUEUE_DEPTH       10
+#define APP_FROM_HOST_EVENTID  0x000000F8
+#define FIRST_SENSOR_EVENTID   0x00000200
+#define LAST_SENSOR_EVENTID    0x000002FF
+#define APP_TO_HOST_EVENTID    0x00000401
+#define OS_LOG_EVENTID         0x3B474F4C
+#define WAKEUP_INTERRUPT       1
+#define WAKEUP_TIMEOUT_MS      1000
+#define SUSPEND_TIMEOUT_MS     100
+#define KTHREAD_ERR_TIME_NS    (60LL * NSEC_PER_SEC)
+#define KTHREAD_ERR_CNT                70
+#define KTHREAD_WARN_CNT       10
+#define WAKEUP_ERR_TIME_NS     (60LL * NSEC_PER_SEC)
+#define WAKEUP_ERR_CNT         4
+
+/**
+ * struct gpio_config - this is a binding between platform data and driver data
+ * @label:     for diagnostics
+ * @flags:     to pass to gpio_request_one()
+ * @options:   one or more of GPIO_OPT_* flags, below
+ * @pdata_off: offset of u32 field in platform data with gpio #
+ * @data_off:  offset of int field in driver data with irq # (optional)
+ */
+struct gpio_config {
+       const char *label;
+       u16 flags;
+       u16 options;
+       u16 pdata_off;
+       u16 data_off;
+};
+
+#define GPIO_OPT_HAS_IRQ       0x0001
+#define GPIO_OPT_OPTIONAL      0x8000
+
+#define PLAT_GPIO_DEF(name, _flags) \
+       .pdata_off = offsetof(struct nanohub_platform_data, name ## _gpio), \
+       .label = "nanohub_" #name, \
+       .flags = _flags \
+
+#define PLAT_GPIO_DEF_IRQ(name, _flags, _opts) \
+       PLAT_GPIO_DEF(name, _flags), \
+       .data_off = offsetof(struct nanohub_data, name), \
+       .options = GPIO_OPT_HAS_IRQ | (_opts) \
+
+static int nanohub_open(struct inode *, struct file *);
+static ssize_t nanohub_read(struct file *, char *, size_t, loff_t *);
+static ssize_t nanohub_write(struct file *, const char *, size_t, loff_t *);
+static unsigned int nanohub_poll(struct file *, poll_table *);
+static int nanohub_release(struct inode *, struct file *);
+static int nanohub_hw_reset(struct nanohub_data *data);
+
+static struct class *sensor_class;
+static int major;
+
+static const struct gpio_config gconf[] = {
+       { PLAT_GPIO_DEF(nreset, GPIOF_OUT_INIT_HIGH) },
+       { PLAT_GPIO_DEF(wakeup, GPIOF_OUT_INIT_HIGH) },
+       { PLAT_GPIO_DEF(boot0, GPIOF_OUT_INIT_LOW) },
+       { PLAT_GPIO_DEF_IRQ(irq1, GPIOF_DIR_IN, 0) },
+       { PLAT_GPIO_DEF_IRQ(irq2, GPIOF_DIR_IN, GPIO_OPT_OPTIONAL) },
+};
+
+static const struct iio_info nanohub_iio_info = {
+       .driver_module = THIS_MODULE,
+};
+
+static const struct file_operations nanohub_fileops = {
+       .owner = THIS_MODULE,
+       .open = nanohub_open,
+       .read = nanohub_read,
+       .write = nanohub_write,
+       .poll = nanohub_poll,
+       .release = nanohub_release,
+};
+
+enum {
+       ST_IDLE,
+       ST_ERROR,
+       ST_RUNNING
+};
+
+static inline bool gpio_is_optional(const struct gpio_config *_cfg)
+{
+       return _cfg->options & GPIO_OPT_OPTIONAL;
+}
+
+static inline bool gpio_has_irq(const struct gpio_config *_cfg)
+{
+       return _cfg->options & GPIO_OPT_HAS_IRQ;
+}
+
+static inline bool nanohub_has_priority_lock_locked(struct nanohub_data *data)
+{
+       return  atomic_read(&data->wakeup_lock_cnt) >
+               atomic_read(&data->wakeup_cnt);
+}
+
+static inline void nanohub_notify_thread(struct nanohub_data *data)
+{
+       atomic_set(&data->kthread_run, 1);
+       /* wake_up implementation works as memory barrier */
+       wake_up_interruptible_sync(&data->kthread_wait);
+}
+
+static inline void nanohub_io_init(struct nanohub_io *io,
+                                  struct nanohub_data *data,
+                                  struct device *dev)
+{
+       init_waitqueue_head(&io->buf_wait);
+       INIT_LIST_HEAD(&io->buf_list);
+       io->data = data;
+       io->dev = dev;
+}
+
+static inline bool nanohub_io_has_buf(struct nanohub_io *io)
+{
+       return !list_empty(&io->buf_list);
+}
+
+static struct nanohub_buf *nanohub_io_get_buf(struct nanohub_io *io,
+                                             bool wait)
+{
+       struct nanohub_buf *buf = NULL;
+       int ret;
+
+       spin_lock(&io->buf_wait.lock);
+       if (wait) {
+               ret = wait_event_interruptible_locked(io->buf_wait,
+                                                     nanohub_io_has_buf(io));
+               if (ret < 0) {
+                       spin_unlock(&io->buf_wait.lock);
+                       return ERR_PTR(ret);
+               }
+       }
+
+       if (nanohub_io_has_buf(io)) {
+               buf = list_first_entry(&io->buf_list, struct nanohub_buf, list);
+               list_del(&buf->list);
+       }
+       spin_unlock(&io->buf_wait.lock);
+
+       return buf;
+}
+
+static void nanohub_io_put_buf(struct nanohub_io *io,
+                              struct nanohub_buf *buf)
+{
+       bool was_empty;
+
+       spin_lock(&io->buf_wait.lock);
+       was_empty = !nanohub_io_has_buf(io);
+       list_add_tail(&buf->list, &io->buf_list);
+       spin_unlock(&io->buf_wait.lock);
+
+       if (was_empty) {
+               if (&io->data->free_pool == io)
+                       nanohub_notify_thread(io->data);
+               else
+                       wake_up_interruptible(&io->buf_wait);
+       }
+}
+
+static inline int plat_gpio_get(struct nanohub_data *data,
+                               const struct gpio_config *_cfg)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       return *(u32 *)(((char *)pdata) + (_cfg)->pdata_off);
+}
+
+static inline void nanohub_set_irq_data(struct nanohub_data *data,
+                                       const struct gpio_config *_cfg, int val)
+{
+       int *data_addr = ((int *)(((char *)data) + _cfg->data_off));
+
+       if ((void *)data_addr > (void *)data &&
+           (void *)data_addr < (void *)(data + 1))
+               *data_addr = val;
+       else
+               WARN(1, "No data binding defined for %s", _cfg->label);
+}
+
+static inline void mcu_wakeup_gpio_set_value(struct nanohub_data *data,
+                                            int val)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       gpio_set_value(pdata->wakeup_gpio, val);
+}
+
+static inline void mcu_wakeup_gpio_get_locked(struct nanohub_data *data,
+                                             int priority_lock)
+{
+       atomic_inc(&data->wakeup_lock_cnt);
+       if (!priority_lock && atomic_inc_return(&data->wakeup_cnt) == 1 &&
+           !nanohub_has_priority_lock_locked(data))
+               mcu_wakeup_gpio_set_value(data, 0);
+}
+
+static inline bool mcu_wakeup_gpio_put_locked(struct nanohub_data *data,
+                                             int priority_lock)
+{
+       bool gpio_done = priority_lock ?
+                        atomic_read(&data->wakeup_cnt) == 0 :
+                        atomic_dec_and_test(&data->wakeup_cnt);
+       bool done = atomic_dec_and_test(&data->wakeup_lock_cnt);
+
+       if (!nanohub_has_priority_lock_locked(data))
+               mcu_wakeup_gpio_set_value(data, gpio_done ? 1 : 0);
+
+       return done;
+}
+
+static inline bool mcu_wakeup_gpio_is_locked(struct nanohub_data *data)
+{
+       return atomic_read(&data->wakeup_lock_cnt) != 0;
+}
+
+static inline void nanohub_handle_irq1(struct nanohub_data *data)
+{
+       bool locked;
+
+       spin_lock(&data->wakeup_wait.lock);
+       locked = mcu_wakeup_gpio_is_locked(data);
+       spin_unlock(&data->wakeup_wait.lock);
+       if (!locked)
+               nanohub_notify_thread(data);
+       else
+               wake_up_interruptible_sync(&data->wakeup_wait);
+}
+
+static inline void nanohub_handle_irq2(struct nanohub_data *data)
+{
+       nanohub_notify_thread(data);
+}
+
+static inline bool mcu_wakeup_try_lock(struct nanohub_data *data, int key)
+{
+       /* implementation contains memory barrier */
+       return atomic_cmpxchg(&data->wakeup_acquired, 0, key) == 0;
+}
+
+static inline void mcu_wakeup_unlock(struct nanohub_data *data, int key)
+{
+       WARN(atomic_cmpxchg(&data->wakeup_acquired, key, 0) != key,
+            "%s: failed to unlock with key %d; current state: %d",
+            __func__, key, atomic_read(&data->wakeup_acquired));
+}
+
+static inline void nanohub_set_state(struct nanohub_data *data, int state)
+{
+       atomic_set(&data->thread_state, state);
+       smp_mb__after_atomic(); /* updated thread state is now visible */
+}
+
+static inline int nanohub_get_state(struct nanohub_data *data)
+{
+       smp_mb__before_atomic(); /* wait for all updates to finish */
+       return atomic_read(&data->thread_state);
+}
+
+static inline void nanohub_clear_err_cnt(struct nanohub_data *data)
+{
+       data->kthread_err_cnt = data->wakeup_err_cnt = 0;
+}
+
+/* the following fragment is based on wait_event_* code from wait.h */
+#define wait_event_interruptible_timeout_locked(q, cond, tmo)          \
+({                                                                     \
+       long __ret = (tmo);                                             \
+       DEFINE_WAIT(__wait);                                            \
+       if (!(cond)) {                                                  \
+               for (;;) {                                              \
+                       __wait.flags &= ~WQ_FLAG_EXCLUSIVE;             \
+                       if (list_empty(&__wait.task_list))              \
+                               __add_wait_queue_tail(&(q), &__wait);   \
+                       set_current_state(TASK_INTERRUPTIBLE);          \
+                       if ((cond))                                     \
+                               break;                                  \
+                       if (signal_pending(current)) {                  \
+                               __ret = -ERESTARTSYS;                   \
+                               break;                                  \
+                       }                                               \
+                       spin_unlock(&(q).lock);                         \
+                       __ret = schedule_timeout(__ret);                \
+                       spin_lock(&(q).lock);                           \
+                       if (!__ret) {                                   \
+                               if ((cond))                             \
+                                       __ret = 1;                      \
+                               break;                                  \
+                       }                                               \
+               }                                                       \
+               __set_current_state(TASK_RUNNING);                      \
+               if (!list_empty(&__wait.task_list))                     \
+                       list_del_init(&__wait.task_list);               \
+               else if (__ret == -ERESTARTSYS &&                       \
+                        /*reimplementation of wait_abort_exclusive() */\
+                        waitqueue_active(&(q)))                        \
+                       __wake_up_locked_key(&(q), TASK_INTERRUPTIBLE,  \
+                       NULL);                                          \
+       } else {                                                        \
+               __ret = 1;                                              \
+       }                                                               \
+       __ret;                                                          \
+})                                                                     \
+
+int request_wakeup_ex(struct nanohub_data *data, long timeout_ms,
+                     int key, int lock_mode)
+{
+       long timeout;
+       bool priority_lock = lock_mode > LOCK_MODE_NORMAL;
+       struct device *sensor_dev = data->io[ID_NANOHUB_SENSOR].dev;
+       int ret;
+       ktime_t ktime_delta;
+       ktime_t wakeup_ktime;
+
+       spin_lock(&data->wakeup_wait.lock);
+       mcu_wakeup_gpio_get_locked(data, priority_lock);
+       timeout = (timeout_ms != MAX_SCHEDULE_TIMEOUT) ?
+                  msecs_to_jiffies(timeout_ms) :
+                  MAX_SCHEDULE_TIMEOUT;
+
+       if (!priority_lock && !data->wakeup_err_cnt)
+               wakeup_ktime = ktime_get_boottime();
+       timeout = wait_event_interruptible_timeout_locked(
+                       data->wakeup_wait,
+                       ((priority_lock || nanohub_irq1_fired(data)) &&
+                        mcu_wakeup_try_lock(data, key)),
+                       timeout
+                 );
+
+       if (timeout <= 0) {
+               if (!timeout && !priority_lock) {
+                       if (!data->wakeup_err_cnt)
+                               data->wakeup_err_ktime = wakeup_ktime;
+                       ktime_delta = ktime_sub(ktime_get_boottime(),
+                                               data->wakeup_err_ktime);
+                       data->wakeup_err_cnt++;
+                       if (ktime_to_ns(ktime_delta) > WAKEUP_ERR_TIME_NS
+                               && data->wakeup_err_cnt > WAKEUP_ERR_CNT) {
+                               mcu_wakeup_gpio_put_locked(data, priority_lock);
+                               spin_unlock(&data->wakeup_wait.lock);
+                               dev_info(sensor_dev,
+                                       "wakeup: hard reset due to consistent error\n");
+                               ret = nanohub_hw_reset(data);
+                               if (ret) {
+                                       dev_info(sensor_dev,
+                                               "%s: failed to reset nanohub: ret=%d\n",
+                                               __func__, ret);
+                               }
+                               return -ETIME;
+                       }
+               }
+               mcu_wakeup_gpio_put_locked(data, priority_lock);
+
+               if (timeout == 0)
+                       timeout = -ETIME;
+       } else {
+               data->wakeup_err_cnt = 0;
+               timeout = 0;
+       }
+       spin_unlock(&data->wakeup_wait.lock);
+
+       return timeout;
+}
+
+void release_wakeup_ex(struct nanohub_data *data, int key, int lock_mode)
+{
+       bool done;
+       bool priority_lock = lock_mode > LOCK_MODE_NORMAL;
+
+       spin_lock(&data->wakeup_wait.lock);
+       done = mcu_wakeup_gpio_put_locked(data, priority_lock);
+       mcu_wakeup_unlock(data, key);
+       spin_unlock(&data->wakeup_wait.lock);
+
+       if (!done)
+               wake_up_interruptible_sync(&data->wakeup_wait);
+       else if (nanohub_irq1_fired(data) || nanohub_irq2_fired(data))
+               nanohub_notify_thread(data);
+}
+
+int nanohub_wait_for_interrupt(struct nanohub_data *data)
+{
+       int ret = -EFAULT;
+
+       /* release the wakeup line, and wait for nanohub to send
+        * us an interrupt indicating the transaction completed.
+        */
+       spin_lock(&data->wakeup_wait.lock);
+       if (mcu_wakeup_gpio_is_locked(data)) {
+               mcu_wakeup_gpio_set_value(data, 1);
+               ret = wait_event_interruptible_locked(data->wakeup_wait,
+                                                     nanohub_irq1_fired(data));
+               mcu_wakeup_gpio_set_value(data, 0);
+       }
+       spin_unlock(&data->wakeup_wait.lock);
+
+       return ret;
+}
+
+int nanohub_wakeup_eom(struct nanohub_data *data, bool repeat)
+{
+       int ret = -EFAULT;
+
+       spin_lock(&data->wakeup_wait.lock);
+       if (mcu_wakeup_gpio_is_locked(data)) {
+               mcu_wakeup_gpio_set_value(data, 1);
+               if (repeat)
+                       mcu_wakeup_gpio_set_value(data, 0);
+               ret = 0;
+       }
+       spin_unlock(&data->wakeup_wait.lock);
+
+       return ret;
+}
+
+static void __nanohub_interrupt_cfg(struct nanohub_data *data,
+                                   u8 interrupt, bool mask)
+{
+       int ret;
+       uint8_t mask_ret;
+       int cnt = 10;
+       struct device *dev = data->io[ID_NANOHUB_SENSOR].dev;
+       int cmd = mask ? CMD_COMMS_MASK_INTR : CMD_COMMS_UNMASK_INTR;
+
+       do {
+               ret = request_wakeup_timeout(data, WAKEUP_TIMEOUT_MS);
+               if (ret) {
+                       dev_err(dev,
+                               "%s: interrupt %d %smask failed: ret=%d\n",
+                               __func__, interrupt, mask ? "" : "un", ret);
+                       return;
+               }
+
+               ret =
+                   nanohub_comms_tx_rx_retrans(data, cmd,
+                                               &interrupt, sizeof(interrupt),
+                                               &mask_ret, sizeof(mask_ret),
+                                               false, 10, 0);
+               release_wakeup(data);
+               dev_dbg(dev,
+                       "%smasking interrupt %d, ret=%d, mask_ret=%d\n",
+                       mask ? "" : "un",
+                       interrupt, ret, mask_ret);
+       } while ((ret != 1 || mask_ret != 1) && --cnt > 0);
+}
+
+static inline void nanohub_mask_interrupt(struct nanohub_data *data,
+                                         u8 interrupt)
+{
+       __nanohub_interrupt_cfg(data, interrupt, true);
+}
+
+static inline void nanohub_unmask_interrupt(struct nanohub_data *data,
+                                           u8 interrupt)
+{
+       __nanohub_interrupt_cfg(data, interrupt, false);
+}
+
+static ssize_t nanohub_wakeup_query(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       nanohub_clear_err_cnt(data);
+       if (nanohub_irq1_fired(data) || nanohub_irq2_fired(data))
+               wake_up_interruptible(&data->wakeup_wait);
+
+       return scnprintf(buf, PAGE_SIZE, "WAKEUP: %d INT1: %d INT2: %d\n",
+                        gpio_get_value(pdata->wakeup_gpio),
+                        gpio_get_value(pdata->irq1_gpio),
+                        data->irq2 ? gpio_get_value(pdata->irq2_gpio) : -1);
+}
+
+static ssize_t nanohub_app_info(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       struct {
+               uint64_t appId;
+               uint32_t appVer;
+               uint32_t appSize;
+       } __packed buffer;
+       uint32_t i = 0;
+       int ret;
+       ssize_t len = 0;
+
+       do {
+               if (request_wakeup(data))
+                       return -ERESTARTSYS;
+
+               if (nanohub_comms_tx_rx_retrans
+                   (data, CMD_COMMS_QUERY_APP_INFO, (uint8_t *)&i,
+                    sizeof(i), (u8 *)&buffer, sizeof(buffer),
+                    false, 10, 10) == sizeof(buffer)) {
+                       ret =
+                           scnprintf(buf + len, PAGE_SIZE - len,
+                                     "app: %d id: %016llx ver: %08x size: %08x\n",
+                                     i, buffer.appId, buffer.appVer,
+                                     buffer.appSize);
+                       if (ret > 0) {
+                               len += ret;
+                               i++;
+                       }
+               } else {
+                       ret = -1;
+               }
+
+               release_wakeup(data);
+       } while (ret > 0);
+
+       return len;
+}
+
+static ssize_t nanohub_firmware_query(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       uint16_t buffer[6];
+
+       if (request_wakeup(data))
+               return -ERESTARTSYS;
+
+       if (nanohub_comms_tx_rx_retrans
+           (data, CMD_COMMS_GET_OS_HW_VERSIONS, NULL, 0, (uint8_t *)&buffer,
+            sizeof(buffer), false, 10, 10) == sizeof(buffer)) {
+               release_wakeup(data);
+               return scnprintf(buf, PAGE_SIZE,
+                                "hw type: %04x hw ver: %04x bl ver: %04x os ver: %04x variant ver: %08x\n",
+                                buffer[0], buffer[1], buffer[2], buffer[3],
+                                buffer[5] << 16 | buffer[4]);
+       } else {
+               release_wakeup(data);
+               return 0;
+       }
+}
+
+static inline int nanohub_wakeup_lock(struct nanohub_data *data, int mode)
+{
+       int ret;
+
+       if (data->irq2)
+               disable_irq(data->irq2);
+       else
+               nanohub_mask_interrupt(data, 2);
+
+       ret = request_wakeup_ex(data,
+                               mode == LOCK_MODE_SUSPEND_RESUME ?
+                               SUSPEND_TIMEOUT_MS : WAKEUP_TIMEOUT_MS,
+                               KEY_WAKEUP_LOCK, mode);
+       if (ret < 0) {
+               if (data->irq2)
+                       enable_irq(data->irq2);
+               else
+                       nanohub_unmask_interrupt(data, 2);
+               return ret;
+       }
+
+       if (mode == LOCK_MODE_IO || mode == LOCK_MODE_IO_BL)
+               ret = nanohub_bl_open(data);
+       if (ret < 0) {
+               release_wakeup_ex(data, KEY_WAKEUP_LOCK, mode);
+               return ret;
+       }
+       if (mode != LOCK_MODE_SUSPEND_RESUME)
+               disable_irq(data->irq1);
+
+       atomic_set(&data->lock_mode, mode);
+       mcu_wakeup_gpio_set_value(data, mode != LOCK_MODE_IO_BL);
+
+       return 0;
+}
+
+/* returns lock mode used to perform this lock */
+static inline int nanohub_wakeup_unlock(struct nanohub_data *data)
+{
+       int mode = atomic_read(&data->lock_mode);
+
+       atomic_set(&data->lock_mode, LOCK_MODE_NONE);
+       if (mode != LOCK_MODE_SUSPEND_RESUME)
+               enable_irq(data->irq1);
+       if (mode == LOCK_MODE_IO || mode == LOCK_MODE_IO_BL)
+               nanohub_bl_close(data);
+       if (data->irq2)
+               enable_irq(data->irq2);
+       release_wakeup_ex(data, KEY_WAKEUP_LOCK, mode);
+       if (!data->irq2)
+               nanohub_unmask_interrupt(data, 2);
+       nanohub_notify_thread(data);
+
+       return mode;
+}
+
+static void __nanohub_hw_reset(struct nanohub_data *data, int boot0)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       gpio_set_value(pdata->nreset_gpio, 0);
+       gpio_set_value(pdata->boot0_gpio, boot0 > 0);
+       usleep_range(30, 40);
+       gpio_set_value(pdata->nreset_gpio, 1);
+       if (boot0 > 0)
+               usleep_range(70000, 75000);
+       else if (!boot0)
+               usleep_range(750000, 800000);
+       nanohub_clear_err_cnt(data);
+}
+
+static int nanohub_hw_reset(struct nanohub_data *data)
+{
+       int ret;
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_RESET);
+
+       if (!ret) {
+               __nanohub_hw_reset(data, 0);
+               nanohub_wakeup_unlock(data);
+       }
+
+       return ret;
+}
+
+static ssize_t nanohub_try_hw_reset(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       int ret;
+
+       ret = nanohub_hw_reset(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_erase_shared(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       uint8_t status = CMD_ACK;
+       int ret;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_IO);
+       if (ret < 0)
+               return ret;
+
+       __nanohub_hw_reset(data, 1);
+
+       status = nanohub_bl_erase_shared(data);
+       dev_info(dev, "nanohub_bl_erase_shared: status=%02x\n",
+                status);
+
+       __nanohub_hw_reset(data, 0);
+       nanohub_wakeup_unlock(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_erase_shared_bl(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       uint8_t status = CMD_ACK;
+       int ret;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_IO_BL);
+       if (ret < 0)
+               return ret;
+
+       __nanohub_hw_reset(data, -1);
+
+       status = nanohub_bl_erase_shared_bl(data);
+       dev_info(dev, "%s: status=%02x\n", __func__, status);
+
+       __nanohub_hw_reset(data, 0);
+       nanohub_wakeup_unlock(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_download_bl(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       const struct nanohub_platform_data *pdata = data->pdata;
+       const struct firmware *fw_entry;
+       int ret;
+       uint8_t status = CMD_ACK;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_IO);
+       if (ret < 0)
+               return ret;
+
+       __nanohub_hw_reset(data, 1);
+
+       ret = request_firmware(&fw_entry, "nanohub.full.bin", dev);
+       if (ret) {
+               dev_err(dev, "%s: err=%d\n", __func__, ret);
+       } else {
+               status = nanohub_bl_download(data, pdata->bl_addr,
+                                            fw_entry->data, fw_entry->size);
+               dev_info(dev, "%s: status=%02x\n", __func__, status);
+               release_firmware(fw_entry);
+       }
+
+       __nanohub_hw_reset(data, 0);
+       nanohub_wakeup_unlock(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_download_kernel(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       const struct firmware *fw_entry;
+       int ret;
+
+       ret = request_firmware(&fw_entry, "nanohub.update.bin", dev);
+       if (ret) {
+               dev_err(dev, "nanohub_download_kernel: err=%d\n", ret);
+               return -EIO;
+       } else {
+               ret =
+                   nanohub_comms_kernel_download(data, fw_entry->data,
+                                                 fw_entry->size);
+
+               release_firmware(fw_entry);
+
+               return count;
+       }
+
+}
+
+static ssize_t nanohub_download_kernel_bl(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       const struct firmware *fw_entry;
+       int ret;
+       uint8_t status = CMD_ACK;
+
+       ret = request_firmware(&fw_entry, "nanohub.kernel.signed", dev);
+       if (ret) {
+               dev_err(dev, "%s: err=%d\n", __func__, ret);
+       } else {
+               ret = nanohub_wakeup_lock(data, LOCK_MODE_IO_BL);
+               if (ret < 0)
+                       return ret;
+
+               __nanohub_hw_reset(data, -1);
+
+               status = nanohub_bl_erase_shared_bl(data);
+               dev_info(dev, "%s: (erase) status=%02x\n", __func__, status);
+               if (status == CMD_ACK) {
+                       status = nanohub_bl_write_memory(data, 0x50000000,
+                                                        fw_entry->size,
+                                                        fw_entry->data);
+                       mcu_wakeup_gpio_set_value(data, 1);
+                       dev_info(dev, "%s: (write) status=%02x\n", __func__, status);
+                       if (status == CMD_ACK) {
+                               status = nanohub_bl_update_finished(data);
+                               dev_info(dev, "%s: (finish) status=%02x\n", __func__, status);
+                       }
+               } else {
+                       mcu_wakeup_gpio_set_value(data, 1);
+               }
+
+               __nanohub_hw_reset(data, 0);
+               nanohub_wakeup_unlock(data);
+
+               release_firmware(fw_entry);
+       }
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_download_app(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       const struct firmware *fw_entry;
+       char buffer[70];
+       int i, ret, ret1, ret2, file_len = 0, appid_len = 0, ver_len = 0;
+       const char *appid = NULL, *ver = NULL;
+       unsigned long version;
+       uint64_t id;
+       uint32_t cur_version;
+       bool update = true;
+
+       for (i = 0; i < count; i++) {
+               if (buf[i] == ' ') {
+                       if (i + 1 == count) {
+                               break;
+                       } else {
+                               if (appid == NULL)
+                                       appid = buf + i + 1;
+                               else if (ver == NULL)
+                                       ver = buf + i + 1;
+                               else
+                                       break;
+                       }
+               } else if (buf[i] == '\n' || buf[i] == '\r') {
+                       break;
+               } else {
+                       if (ver)
+                               ver_len++;
+                       else if (appid)
+                               appid_len++;
+                       else
+                               file_len++;
+               }
+       }
+
+       if (file_len > 64 || appid_len > 16 || ver_len > 8 || file_len < 1)
+               return -EIO;
+
+       memcpy(buffer, buf, file_len);
+       memcpy(buffer + file_len, ".napp", 5);
+       buffer[file_len + 5] = '\0';
+
+       ret = request_firmware(&fw_entry, buffer, dev);
+       if (ret) {
+               dev_err(dev, "nanohub_download_app(%s): err=%d\n",
+                       buffer, ret);
+               return -EIO;
+       }
+       if (appid_len > 0 && ver_len > 0) {
+               memcpy(buffer, appid, appid_len);
+               buffer[appid_len] = '\0';
+
+               ret1 = kstrtoull(buffer, 16, &id);
+
+               memcpy(buffer, ver, ver_len);
+               buffer[ver_len] = '\0';
+
+               ret2 = kstrtoul(buffer, 16, &version);
+
+               if (ret1 == 0 && ret2 == 0) {
+                       if (request_wakeup(data))
+                               return -ERESTARTSYS;
+                       if (nanohub_comms_tx_rx_retrans
+                           (data, CMD_COMMS_GET_APP_VERSIONS,
+                            (uint8_t *)&id, sizeof(id),
+                            (uint8_t *)&cur_version,
+                            sizeof(cur_version), false, 10,
+                            10) == sizeof(cur_version)) {
+                               if (cur_version == version)
+                                       update = false;
+                       }
+                       release_wakeup(data);
+               }
+       }
+
+       if (update)
+               ret =
+                   nanohub_comms_app_download(data, fw_entry->data,
+                                              fw_entry->size);
+
+       release_firmware(fw_entry);
+
+       return count;
+}
+
+static ssize_t nanohub_lock_bl(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       int ret;
+       uint8_t status = CMD_ACK;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_IO);
+       if (ret < 0)
+               return ret;
+
+       __nanohub_hw_reset(data, 1);
+
+       gpio_set_value(data->pdata->boot0_gpio, 0);
+       /* this command reboots itself */
+       status = nanohub_bl_lock(data);
+       dev_info(dev, "%s: status=%02x\n", __func__, status);
+       msleep(350);
+
+       nanohub_wakeup_unlock(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static ssize_t nanohub_unlock_bl(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct nanohub_data *data = dev_get_nanohub_data(dev);
+       int ret;
+       uint8_t status = CMD_ACK;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_IO);
+       if (ret < 0)
+               return ret;
+
+       __nanohub_hw_reset(data, 1);
+
+       gpio_set_value(data->pdata->boot0_gpio, 0);
+       /* this command reboots itself (erasing the flash) */
+       status = nanohub_bl_unlock(data);
+       dev_info(dev, "%s: status=%02x\n", __func__, status);
+       msleep(20);
+
+       nanohub_wakeup_unlock(data);
+
+       return ret < 0 ? ret : count;
+}
+
+static struct device_attribute attributes[] = {
+       __ATTR(wakeup, 0440, nanohub_wakeup_query, NULL),
+       __ATTR(app_info, 0440, nanohub_app_info, NULL),
+       __ATTR(firmware_version, 0440, nanohub_firmware_query, NULL),
+       __ATTR(download_bl, 0220, NULL, nanohub_download_bl),
+       __ATTR(download_kernel, 0220, NULL, nanohub_download_kernel),
+       __ATTR(download_kernel_bl, 0220, NULL, nanohub_download_kernel_bl),
+       __ATTR(download_app, 0220, NULL, nanohub_download_app),
+       __ATTR(erase_shared, 0220, NULL, nanohub_erase_shared),
+       __ATTR(erase_shared_bl, 0220, NULL, nanohub_erase_shared_bl),
+       __ATTR(reset, 0220, NULL, nanohub_try_hw_reset),
+       __ATTR(lock, 0220, NULL, nanohub_lock_bl),
+       __ATTR(unlock, 0220, NULL, nanohub_unlock_bl),
+};
+
+static inline int nanohub_create_sensor(struct nanohub_data *data)
+{
+       int i, ret;
+       struct device *sensor_dev = data->io[ID_NANOHUB_SENSOR].dev;
+
+       for (i = 0, ret = 0; i < ARRAY_SIZE(attributes); i++) {
+               ret = device_create_file(sensor_dev, &attributes[i]);
+               if (ret) {
+                       dev_err(sensor_dev,
+                               "create sysfs attr %d [%s] failed; err=%d\n",
+                               i, attributes[i].attr.name, ret);
+                       goto fail_attr;
+               }
+       }
+
+       ret = sysfs_create_link(&sensor_dev->kobj,
+                               &data->iio_dev->dev.kobj, "iio");
+       if (ret) {
+               dev_err(sensor_dev,
+                       "sysfs_create_link failed; err=%d\n", ret);
+               goto fail_attr;
+       }
+       goto done;
+
+fail_attr:
+       for (i--; i >= 0; i--)
+               device_remove_file(sensor_dev, &attributes[i]);
+done:
+       return ret;
+}
+
+static int nanohub_create_devices(struct nanohub_data *data)
+{
+       int i, ret;
+       static const char *names[ID_NANOHUB_MAX] = {
+                       "nanohub", "nanohub_comms"
+       };
+
+       for (i = 0; i < ID_NANOHUB_MAX; ++i) {
+               struct nanohub_io *io = &data->io[i];
+
+               nanohub_io_init(io, data, device_create(sensor_class, NULL,
+                                                       MKDEV(major, i),
+                                                       io, names[i]));
+               if (IS_ERR(io->dev)) {
+                       ret = PTR_ERR(io->dev);
+                       pr_err("nanohub: device_create failed for %s; err=%d\n",
+                              names[i], ret);
+                       goto fail_dev;
+               }
+       }
+
+       ret = nanohub_create_sensor(data);
+       if (!ret)
+               goto done;
+
+fail_dev:
+       for (--i; i >= 0; --i)
+               device_destroy(sensor_class, MKDEV(major, i));
+done:
+       return ret;
+}
+
+static int nanohub_match_devt(struct device *dev, const void *data)
+{
+       const dev_t *devt = data;
+
+       return dev->devt == *devt;
+}
+
+static int nanohub_open(struct inode *inode, struct file *file)
+{
+       dev_t devt = inode->i_rdev;
+       struct device *dev;
+
+       dev = class_find_device(sensor_class, NULL, &devt, nanohub_match_devt);
+       if (dev) {
+               file->private_data = dev_get_drvdata(dev);
+               nonseekable_open(inode, file);
+               return 0;
+       }
+
+       return -ENODEV;
+}
+
+static ssize_t nanohub_read(struct file *file, char *buffer, size_t length,
+                           loff_t *offset)
+{
+       struct nanohub_io *io = file->private_data;
+       struct nanohub_data *data = io->data;
+       struct nanohub_buf *buf;
+       int ret;
+
+       if (!nanohub_io_has_buf(io) && (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+
+       buf = nanohub_io_get_buf(io, true);
+       if (IS_ERR_OR_NULL(buf))
+               return PTR_ERR(buf);
+
+       ret = copy_to_user(buffer, buf->buffer, buf->length);
+       if (ret != 0)
+               ret = -EFAULT;
+       else
+               ret = buf->length;
+
+       nanohub_io_put_buf(&data->free_pool, buf);
+
+       return ret;
+}
+
+static ssize_t nanohub_write(struct file *file, const char *buffer,
+                            size_t length, loff_t *offset)
+{
+       struct nanohub_io *io = file->private_data;
+       struct nanohub_data *data = io->data;
+       int ret;
+
+       ret = request_wakeup_timeout(data, WAKEUP_TIMEOUT_MS);
+       if (ret)
+               return ret;
+
+       ret = nanohub_comms_write(data, buffer, length);
+
+       release_wakeup(data);
+
+       return ret;
+}
+
+static unsigned int nanohub_poll(struct file *file, poll_table *wait)
+{
+       struct nanohub_io *io = file->private_data;
+       unsigned int mask = POLLOUT | POLLWRNORM;
+
+       poll_wait(file, &io->buf_wait, wait);
+
+       if (nanohub_io_has_buf(io))
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
+}
+
+static int nanohub_release(struct inode *inode, struct file *file)
+{
+       file->private_data = NULL;
+
+       return 0;
+}
+
+static void nanohub_destroy_devices(struct nanohub_data *data)
+{
+       int i;
+       struct device *sensor_dev = data->io[ID_NANOHUB_SENSOR].dev;
+
+       sysfs_remove_link(&sensor_dev->kobj, "iio");
+       for (i = 0; i < ARRAY_SIZE(attributes); i++)
+               device_remove_file(sensor_dev, &attributes[i]);
+       for (i = 0; i < ID_NANOHUB_MAX; ++i)
+               device_destroy(sensor_class, MKDEV(major, i));
+}
+
+static irqreturn_t nanohub_irq1(int irq, void *dev_id)
+{
+       struct nanohub_data *data = (struct nanohub_data *)dev_id;
+
+       nanohub_handle_irq1(data);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t nanohub_irq2(int irq, void *dev_id)
+{
+       struct nanohub_data *data = (struct nanohub_data *)dev_id;
+
+       nanohub_handle_irq2(data);
+
+       return IRQ_HANDLED;
+}
+
+static bool nanohub_os_log(char *buffer, int len)
+{
+       if (le32_to_cpu((((uint32_t *)buffer)[0]) & 0x7FFFFFFF) ==
+           OS_LOG_EVENTID) {
+               char *mtype, *mdata = &buffer[5];
+
+               buffer[len] = 0x00;
+
+               switch (buffer[4]) {
+               case 'E':
+                       mtype = KERN_ERR;
+                       break;
+               case 'W':
+                       mtype = KERN_WARNING;
+                       break;
+               case 'I':
+                       mtype = KERN_INFO;
+                       break;
+               case 'D':
+                       mtype = KERN_DEBUG;
+                       break;
+               default:
+                       mtype = KERN_DEFAULT;
+                       mdata--;
+                       break;
+               }
+               printk("%snanohub: %s", mtype, mdata);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+static void nanohub_process_buffer(struct nanohub_data *data,
+                                  struct nanohub_buf **buf,
+                                  int ret)
+{
+       uint32_t event_id;
+       uint8_t interrupt;
+       bool wakeup = false;
+       struct nanohub_io *io = &data->io[ID_NANOHUB_SENSOR];
+
+       data->kthread_err_cnt = 0;
+       if (ret < 4 || nanohub_os_log((*buf)->buffer, ret)) {
+               release_wakeup(data);
+               return;
+       }
+
+       (*buf)->length = ret;
+
+       event_id = le32_to_cpu((((uint32_t *)(*buf)->buffer)[0]) & 0x7FFFFFFF);
+       if (ret >= sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t) &&
+           event_id > FIRST_SENSOR_EVENTID &&
+           event_id <= LAST_SENSOR_EVENTID) {
+               interrupt = (*buf)->buffer[sizeof(uint32_t) +
+                                          sizeof(uint64_t) + 3];
+               if (interrupt == WAKEUP_INTERRUPT)
+                       wakeup = true;
+       }
+       if (event_id == APP_TO_HOST_EVENTID) {
+               wakeup = true;
+               io = &data->io[ID_NANOHUB_COMMS];
+       }
+
+       nanohub_io_put_buf(io, *buf);
+
+       *buf = NULL;
+       /* (for wakeup interrupts): hold a wake lock for 250ms so the sensor hal
+        * has time to grab its own wake lock */
+       if (wakeup)
+               wake_lock_timeout(&data->wakelock_read, msecs_to_jiffies(250));
+       release_wakeup(data);
+}
+
+static int nanohub_kthread(void *arg)
+{
+       struct nanohub_data *data = (struct nanohub_data *)arg;
+       struct nanohub_buf *buf = NULL;
+       int ret;
+       ktime_t ktime_delta;
+       uint32_t clear_interrupts[8] = { 0x00000006 };
+       struct device *sensor_dev = data->io[ID_NANOHUB_SENSOR].dev;
+       static const struct sched_param param = {
+               .sched_priority = (MAX_USER_RT_PRIO/2)-1,
+       };
+
+       data->kthread_err_cnt = 0;
+       sched_setscheduler(current, SCHED_FIFO, &param);
+       nanohub_set_state(data, ST_IDLE);
+
+       while (!kthread_should_stop()) {
+               switch (nanohub_get_state(data)) {
+               case ST_IDLE:
+                       wait_event_interruptible(data->kthread_wait,
+                                                atomic_read(&data->kthread_run)
+                                                );
+                       nanohub_set_state(data, ST_RUNNING);
+                       break;
+               case ST_ERROR:
+                       ktime_delta = ktime_sub(ktime_get_boottime(),
+                                               data->kthread_err_ktime);
+                       if (ktime_to_ns(ktime_delta) > KTHREAD_ERR_TIME_NS
+                               && data->kthread_err_cnt > KTHREAD_ERR_CNT) {
+                               dev_info(sensor_dev,
+                                       "kthread: hard reset due to consistent error\n");
+                               ret = nanohub_hw_reset(data);
+                               if (ret) {
+                                       dev_info(sensor_dev,
+                                               "%s: failed to reset nanohub: ret=%d\n",
+                                               __func__, ret);
+                               }
+                       }
+                       msleep_interruptible(WAKEUP_TIMEOUT_MS);
+                       nanohub_set_state(data, ST_RUNNING);
+                       break;
+               case ST_RUNNING:
+                       break;
+               }
+               atomic_set(&data->kthread_run, 0);
+               if (!buf)
+                       buf = nanohub_io_get_buf(&data->free_pool,
+                                                false);
+               if (buf) {
+                       ret = request_wakeup_timeout(data, WAKEUP_TIMEOUT_MS);
+                       if (ret) {
+                               dev_info(sensor_dev,
+                                        "%s: request_wakeup_timeout: ret=%d\n",
+                                        __func__, ret);
+                               continue;
+                       }
+
+                       ret = nanohub_comms_rx_retrans_boottime(
+                           data, CMD_COMMS_READ, buf->buffer,
+                           sizeof(buf->buffer), 10, 0);
+
+                       if (ret > 0) {
+                               nanohub_process_buffer(data, &buf, ret);
+                               if (!nanohub_irq1_fired(data) &&
+                                   !nanohub_irq2_fired(data)) {
+                                       nanohub_set_state(data, ST_IDLE);
+                                       continue;
+                               }
+                       } else if (ret == 0) {
+                               /* queue empty, go to sleep */
+                               data->kthread_err_cnt = 0;
+                               data->interrupts[0] &= ~0x00000006;
+                               release_wakeup(data);
+                               nanohub_set_state(data, ST_IDLE);
+                               continue;
+                       } else {
+                               release_wakeup(data);
+                               if (data->kthread_err_cnt == 0)
+                                       data->kthread_err_ktime =
+                                               ktime_get_boottime();
+
+                               data->kthread_err_cnt++;
+                               if (data->kthread_err_cnt >= KTHREAD_WARN_CNT) {
+                                       dev_err(sensor_dev,
+                                               "%s: kthread_err_cnt=%d\n",
+                                               __func__,
+                                               data->kthread_err_cnt);
+                                       nanohub_set_state(data, ST_ERROR);
+                                       continue;
+                               }
+                       }
+               } else {
+                       if (!nanohub_irq1_fired(data) &&
+                           !nanohub_irq2_fired(data)) {
+                               nanohub_set_state(data, ST_IDLE);
+                               continue;
+                       }
+                       /* pending interrupt, but no room to read data -
+                        * clear interrupts */
+                       if (request_wakeup(data))
+                               continue;
+                       nanohub_comms_tx_rx_retrans(data,
+                                                   CMD_COMMS_CLR_GET_INTR,
+                                                   (uint8_t *)
+                                                   clear_interrupts,
+                                                   sizeof(clear_interrupts),
+                                                   (uint8_t *) data->
+                                                   interrupts,
+                                                   sizeof(data->interrupts),
+                                                   false, 10, 0);
+                       release_wakeup(data);
+                       nanohub_set_state(data, ST_IDLE);
+               }
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static struct nanohub_platform_data *nanohub_parse_dt(struct device *dev)
+{
+       struct nanohub_platform_data *pdata;
+       struct device_node *dt = dev->of_node;
+       const uint32_t *tmp;
+       struct property *prop;
+       uint32_t u, i;
+       int ret;
+
+       if (!dt)
+               return ERR_PTR(-ENODEV);
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       ret = pdata->irq1_gpio =
+           of_get_named_gpio(dt, "sensorhub,irq1-gpio", 0);
+       if (ret < 0) {
+               pr_err("nanohub: missing sensorhub,irq1-gpio in device tree\n");
+               goto free_pdata;
+       }
+
+       /* optional (strongly recommended) */
+       pdata->irq2_gpio = of_get_named_gpio(dt, "sensorhub,irq2-gpio", 0);
+
+       ret = pdata->wakeup_gpio =
+           of_get_named_gpio(dt, "sensorhub,wakeup-gpio", 0);
+       if (ret < 0) {
+               pr_err
+                   ("nanohub: missing sensorhub,wakeup-gpio in device tree\n");
+               goto free_pdata;
+       }
+
+       ret = pdata->nreset_gpio =
+           of_get_named_gpio(dt, "sensorhub,nreset-gpio", 0);
+       if (ret < 0) {
+               pr_err
+                   ("nanohub: missing sensorhub,nreset-gpio in device tree\n");
+               goto free_pdata;
+       }
+
+       /* optional (stm32f bootloader) */
+       pdata->boot0_gpio = of_get_named_gpio(dt, "sensorhub,boot0-gpio", 0);
+
+       /* optional (spi) */
+       pdata->spi_cs_gpio = of_get_named_gpio(dt, "sensorhub,spi-cs-gpio", 0);
+
+       /* optional (stm32f bootloader) */
+       of_property_read_u32(dt, "sensorhub,bl-addr", &pdata->bl_addr);
+
+       /* optional (stm32f bootloader) */
+       tmp = of_get_property(dt, "sensorhub,num-flash-banks", NULL);
+       if (tmp) {
+               pdata->num_flash_banks = be32_to_cpup(tmp);
+               pdata->flash_banks =
+                   devm_kzalloc(dev,
+                                sizeof(struct nanohub_flash_bank) *
+                                pdata->num_flash_banks, GFP_KERNEL);
+               if (!pdata->flash_banks)
+                       goto no_mem;
+
+               /* TODO: investigate replacing with of_property_read_u32_array
+                */
+               i = 0;
+               of_property_for_each_u32(dt, "sensorhub,flash-banks", prop, tmp,
+                                        u) {
+                       if (i / 3 >= pdata->num_flash_banks)
+                               break;
+                       switch (i % 3) {
+                       case 0:
+                               pdata->flash_banks[i / 3].bank = u;
+                               break;
+                       case 1:
+                               pdata->flash_banks[i / 3].address = u;
+                               break;
+                       case 2:
+                               pdata->flash_banks[i / 3].length = u;
+                               break;
+                       }
+                       i++;
+               }
+       }
+
+       /* optional (stm32f bootloader) */
+       tmp = of_get_property(dt, "sensorhub,num-shared-flash-banks", NULL);
+       if (tmp) {
+               pdata->num_shared_flash_banks = be32_to_cpup(tmp);
+               pdata->shared_flash_banks =
+                   devm_kzalloc(dev,
+                                sizeof(struct nanohub_flash_bank) *
+                                pdata->num_shared_flash_banks, GFP_KERNEL);
+               if (!pdata->shared_flash_banks)
+                       goto no_mem_shared;
+
+               /* TODO: investigate replacing with of_property_read_u32_array
+                */
+               i = 0;
+               of_property_for_each_u32(dt, "sensorhub,shared-flash-banks",
+                                        prop, tmp, u) {
+                       if (i / 3 >= pdata->num_shared_flash_banks)
+                               break;
+                       switch (i % 3) {
+                       case 0:
+                               pdata->shared_flash_banks[i / 3].bank = u;
+                               break;
+                       case 1:
+                               pdata->shared_flash_banks[i / 3].address = u;
+                               break;
+                       case 2:
+                               pdata->shared_flash_banks[i / 3].length = u;
+                               break;
+                       }
+                       i++;
+               }
+       }
+
+       return pdata;
+
+no_mem_shared:
+       devm_kfree(dev, pdata->flash_banks);
+no_mem:
+       ret = -ENOMEM;
+free_pdata:
+       devm_kfree(dev, pdata);
+       return ERR_PTR(ret);
+}
+#else
+static struct nanohub_platform_data *nanohub_parse_dt(struct device *dev)
+{
+       struct nanohub_platform_data *pdata;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       return pdata;
+}
+#endif
+
+static int nanohub_request_irqs(struct nanohub_data *data)
+{
+       int ret;
+
+       ret = request_threaded_irq(data->irq1, NULL, nanohub_irq1,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "nanohub-irq1", data);
+       if (ret < 0)
+               data->irq1 = 0;
+       else
+               disable_irq(data->irq1);
+       if (data->irq2 <= 0 || ret < 0) {
+               data->irq2 = 0;
+               return ret;
+       }
+
+       ret = request_threaded_irq(data->irq2, NULL, nanohub_irq2,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "nanohub-irq2", data);
+       if (ret < 0) {
+               data->irq2 = 0;
+               WARN(1, "failed to request optional IRQ %d; err=%d",
+                    data->irq2, ret);
+       } else {
+               disable_irq(data->irq2);
+       }
+
+       /* if 2d request fails, hide this; it is optional IRQ,
+        * and failure should not interrupt driver init sequence.
+        */
+       return 0;
+}
+
+static int nanohub_request_gpios(struct nanohub_data *data)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < ARRAY_SIZE(gconf); ++i) {
+               const struct gpio_config *cfg = &gconf[i];
+               unsigned int gpio = plat_gpio_get(data, cfg);
+               const char *label;
+               bool optional = gpio_is_optional(cfg);
+
+               ret = 0; /* clear errors on optional pins, if any */
+
+               if (!gpio_is_valid(gpio) && optional)
+                       continue;
+
+               label = cfg->label;
+               ret = gpio_request_one(gpio, cfg->flags, label);
+               if (ret && !optional) {
+                       pr_err("nanohub: gpio %d[%s] request failed;err=%d\n",
+                              gpio, label, ret);
+                       break;
+               }
+               if (gpio_has_irq(cfg)) {
+                       int irq = gpio_to_irq(gpio);
+                       if (irq > 0) {
+                               nanohub_set_irq_data(data, cfg, irq);
+                       } else if (!optional) {
+                               ret = -EINVAL;
+                               pr_err("nanohub: no irq; gpio %d[%s];err=%d\n",
+                                      gpio, label, irq);
+                               break;
+                       }
+               }
+       }
+       if (i < ARRAY_SIZE(gconf)) {
+               for (--i; i >= 0; --i)
+                       gpio_free(plat_gpio_get(data, &gconf[i]));
+       }
+
+       return ret;
+}
+
+static void nanohub_release_gpios_irqs(struct nanohub_data *data)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       if (data->irq2)
+               free_irq(data->irq2, data);
+       if (data->irq1)
+               free_irq(data->irq1, data);
+       if (gpio_is_valid(pdata->irq2_gpio))
+               gpio_free(pdata->irq2_gpio);
+       gpio_free(pdata->irq1_gpio);
+       gpio_set_value(pdata->nreset_gpio, 0);
+       gpio_free(pdata->nreset_gpio);
+       mcu_wakeup_gpio_set_value(data, 1);
+       gpio_free(pdata->wakeup_gpio);
+       gpio_set_value(pdata->boot0_gpio, 0);
+       gpio_free(pdata->boot0_gpio);
+}
+
+struct iio_dev *nanohub_probe(struct device *dev, struct iio_dev *iio_dev)
+{
+       int ret, i;
+       const struct nanohub_platform_data *pdata;
+       struct nanohub_data *data;
+       struct nanohub_buf *buf;
+       bool own_iio_dev = !iio_dev;
+
+       pdata = dev_get_platdata(dev);
+       if (!pdata) {
+               pdata = nanohub_parse_dt(dev);
+               if (IS_ERR(pdata))
+                       return ERR_PTR(PTR_ERR(pdata));
+       }
+
+       if (own_iio_dev) {
+               iio_dev = iio_device_alloc(sizeof(struct nanohub_data));
+               if (!iio_dev)
+                       return ERR_PTR(-ENOMEM);
+       }
+
+       iio_dev->name = "nanohub";
+       iio_dev->dev.parent = dev;
+       iio_dev->info = &nanohub_iio_info;
+       iio_dev->channels = NULL;
+       iio_dev->num_channels = 0;
+
+       data = iio_priv(iio_dev);
+       data->iio_dev = iio_dev;
+       data->pdata = pdata;
+
+       init_waitqueue_head(&data->kthread_wait);
+
+       nanohub_io_init(&data->free_pool, data, dev);
+
+       buf = vmalloc(sizeof(*buf) * READ_QUEUE_DEPTH);
+       data->vbuf = buf;
+       if (!buf) {
+               ret = -ENOMEM;
+               goto fail_vma;
+       }
+
+       for (i = 0; i < READ_QUEUE_DEPTH; i++)
+               nanohub_io_put_buf(&data->free_pool, &buf[i]);
+       atomic_set(&data->kthread_run, 0);
+       wake_lock_init(&data->wakelock_read, WAKE_LOCK_SUSPEND,
+                      "nanohub_wakelock_read");
+
+       atomic_set(&data->lock_mode, LOCK_MODE_NONE);
+       atomic_set(&data->wakeup_cnt, 0);
+       atomic_set(&data->wakeup_lock_cnt, 0);
+       atomic_set(&data->wakeup_acquired, 0);
+       init_waitqueue_head(&data->wakeup_wait);
+
+       ret = nanohub_request_gpios(data);
+       if (ret)
+               goto fail_gpio;
+
+       ret = nanohub_request_irqs(data);
+       if (ret)
+               goto fail_irq;
+
+       ret = iio_device_register(iio_dev);
+       if (ret) {
+               pr_err("nanohub: iio_device_register failed\n");
+               goto fail_irq;
+       }
+
+       ret = nanohub_create_devices(data);
+       if (ret)
+               goto fail_dev;
+
+       data->thread = kthread_run(nanohub_kthread, data, "nanohub");
+
+       udelay(30);
+
+       return iio_dev;
+
+fail_dev:
+       iio_device_unregister(iio_dev);
+fail_irq:
+       nanohub_release_gpios_irqs(data);
+fail_gpio:
+       wake_lock_destroy(&data->wakelock_read);
+       vfree(buf);
+fail_vma:
+       if (own_iio_dev)
+               iio_device_free(iio_dev);
+
+       return ERR_PTR(ret);
+}
+
+int nanohub_reset(struct nanohub_data *data)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       gpio_set_value(pdata->nreset_gpio, 1);
+       usleep_range(650000, 700000);
+       enable_irq(data->irq1);
+       if (data->irq2)
+               enable_irq(data->irq2);
+       else
+               nanohub_unmask_interrupt(data, 2);
+
+       return 0;
+}
+
+int nanohub_remove(struct iio_dev *iio_dev)
+{
+       struct nanohub_data *data = iio_priv(iio_dev);
+
+       nanohub_notify_thread(data);
+       kthread_stop(data->thread);
+
+       nanohub_destroy_devices(data);
+       iio_device_unregister(iio_dev);
+       nanohub_release_gpios_irqs(data);
+       wake_lock_destroy(&data->wakelock_read);
+       vfree(data->vbuf);
+       iio_device_free(iio_dev);
+
+       return 0;
+}
+
+int nanohub_suspend(struct iio_dev *iio_dev)
+{
+       struct nanohub_data *data = iio_priv(iio_dev);
+       int ret;
+
+       ret = nanohub_wakeup_lock(data, LOCK_MODE_SUSPEND_RESUME);
+       if (!ret) {
+               int cnt;
+               const int max_cnt = 10;
+
+               for (cnt = 0; cnt < max_cnt; ++cnt) {
+                       if (!nanohub_irq1_fired(data))
+                               break;
+                       usleep_range(10, 15);
+               }
+               if (cnt < max_cnt) {
+                       dev_dbg(&iio_dev->dev, "%s: cnt=%d\n", __func__, cnt);
+                       enable_irq_wake(data->irq1);
+                       return 0;
+               }
+               ret = -EBUSY;
+               dev_info(&iio_dev->dev,
+                        "%s: failed to suspend: IRQ1=%d, state=%d\n",
+                        __func__, nanohub_irq1_fired(data),
+                        nanohub_get_state(data));
+               nanohub_wakeup_unlock(data);
+       } else {
+               dev_info(&iio_dev->dev, "%s: could not take wakeup lock\n",
+                        __func__);
+       }
+
+       return ret;
+}
+
+int nanohub_resume(struct iio_dev *iio_dev)
+{
+       struct nanohub_data *data = iio_priv(iio_dev);
+
+       disable_irq_wake(data->irq1);
+       nanohub_wakeup_unlock(data);
+
+       return 0;
+}
+
+static int __init nanohub_init(void)
+{
+       int ret = 0;
+
+       sensor_class = class_create(THIS_MODULE, "nanohub");
+       if (IS_ERR(sensor_class)) {
+               ret = PTR_ERR(sensor_class);
+               pr_err("nanohub: class_create failed; err=%d\n", ret);
+       }
+       if (!ret)
+               major = __register_chrdev(0, 0, ID_NANOHUB_MAX, "nanohub",
+                                         &nanohub_fileops);
+
+       if (major < 0) {
+               ret = major;
+               major = 0;
+               pr_err("nanohub: can't register; err=%d\n", ret);
+       }
+
+#ifdef CONFIG_NANOHUB_I2C
+       if (ret == 0)
+               ret = nanohub_i2c_init();
+#endif
+#ifdef CONFIG_NANOHUB_SPI
+       if (ret == 0)
+               ret = nanohub_spi_init();
+#endif
+       pr_info("nanohub: loaded; ret=%d\n", ret);
+       return ret;
+}
+
+static void __exit nanohub_cleanup(void)
+{
+#ifdef CONFIG_NANOHUB_I2C
+       nanohub_i2c_cleanup();
+#endif
+#ifdef CONFIG_NANOHUB_SPI
+       nanohub_spi_cleanup();
+#endif
+       __unregister_chrdev(major, 0, ID_NANOHUB_MAX, "nanohub");
+       class_destroy(sensor_class);
+       major = 0;
+       sensor_class = 0;
+}
+
+module_init(nanohub_init);
+module_exit(nanohub_cleanup);
+
+MODULE_AUTHOR("Ben Fennema");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/nanohub/main.h b/drivers/staging/nanohub/main.h
new file mode 100644 (file)
index 0000000..9fd28ab
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _NANOHUB_MAIN_H
+#define _NANOHUB_MAIN_H
+
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/wakelock.h>
+
+#include "comms.h"
+#include "bl.h"
+
+#define NANOHUB_NAME "nanohub"
+
+struct nanohub_buf {
+       struct list_head list;
+       uint8_t buffer[255];
+       uint8_t length;
+};
+
+struct nanohub_data;
+
+struct nanohub_io {
+       struct device *dev;
+       struct nanohub_data *data;
+       wait_queue_head_t buf_wait;
+       struct list_head buf_list;
+};
+
+static inline struct nanohub_data *dev_get_nanohub_data(struct device *dev)
+{
+       struct nanohub_io *io = dev_get_drvdata(dev);
+
+       return io->data;
+}
+
+struct nanohub_data {
+       /* indices for io[] array */
+       #define ID_NANOHUB_SENSOR 0
+       #define ID_NANOHUB_COMMS 1
+       #define ID_NANOHUB_MAX 2
+
+       struct iio_dev *iio_dev;
+       struct nanohub_io io[ID_NANOHUB_MAX];
+
+       struct nanohub_comms comms;
+       struct nanohub_bl bl;
+       const struct nanohub_platform_data *pdata;
+       int irq1;
+       int irq2;
+
+       atomic_t kthread_run;
+       atomic_t thread_state;
+       wait_queue_head_t kthread_wait;
+
+       struct wake_lock wakelock_read;
+
+       struct nanohub_io free_pool;
+
+       atomic_t lock_mode;
+       /* these 3 vars should be accessed only with wakeup_wait.lock held */
+       atomic_t wakeup_cnt;
+       atomic_t wakeup_lock_cnt;
+       atomic_t wakeup_acquired;
+       wait_queue_head_t wakeup_wait;
+
+       uint32_t interrupts[8];
+
+       ktime_t wakeup_err_ktime;
+       int wakeup_err_cnt;
+
+       ktime_t kthread_err_ktime;
+       int kthread_err_cnt;
+
+       void *vbuf;
+       struct task_struct *thread;
+};
+
+enum {
+       KEY_WAKEUP_NONE,
+       KEY_WAKEUP,
+       KEY_WAKEUP_LOCK,
+};
+
+enum {
+       LOCK_MODE_NONE,
+       LOCK_MODE_NORMAL,
+       LOCK_MODE_IO,
+       LOCK_MODE_IO_BL,
+       LOCK_MODE_RESET,
+       LOCK_MODE_SUSPEND_RESUME,
+};
+
+int request_wakeup_ex(struct nanohub_data *data, long timeout,
+                     int key, int lock_mode);
+void release_wakeup_ex(struct nanohub_data *data, int key, int lock_mode);
+int nanohub_wait_for_interrupt(struct nanohub_data *data);
+int nanohub_wakeup_eom(struct nanohub_data *data, bool repeat);
+struct iio_dev *nanohub_probe(struct device *dev, struct iio_dev *iio_dev);
+int nanohub_reset(struct nanohub_data *data);
+int nanohub_remove(struct iio_dev *iio_dev);
+int nanohub_suspend(struct iio_dev *iio_dev);
+int nanohub_resume(struct iio_dev *iio_dev);
+
+static inline int nanohub_irq1_fired(struct nanohub_data *data)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       return !gpio_get_value(pdata->irq1_gpio);
+}
+
+static inline int nanohub_irq2_fired(struct nanohub_data *data)
+{
+       const struct nanohub_platform_data *pdata = data->pdata;
+
+       return data->irq2 && !gpio_get_value(pdata->irq2_gpio);
+}
+
+static inline int request_wakeup_timeout(struct nanohub_data *data, int timeout)
+{
+       return request_wakeup_ex(data, timeout, KEY_WAKEUP, LOCK_MODE_NORMAL);
+}
+
+static inline int request_wakeup(struct nanohub_data *data)
+{
+       return request_wakeup_ex(data, MAX_SCHEDULE_TIMEOUT, KEY_WAKEUP,
+                                LOCK_MODE_NORMAL);
+}
+
+static inline void release_wakeup(struct nanohub_data *data)
+{
+       release_wakeup_ex(data, KEY_WAKEUP, LOCK_MODE_NORMAL);
+}
+
+#endif
diff --git a/drivers/staging/nanohub/spi.c b/drivers/staging/nanohub/spi.c
new file mode 100644 (file)
index 0000000..a315327
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include "main.h"
+#include "bl.h"
+#include "comms.h"
+
+#define SPI_TIMEOUT            65535
+#define SPI_MIN_DMA            48
+
+struct nanohub_spi_data {
+       struct nanohub_data data;
+       struct spi_device *device;
+       struct semaphore spi_sem;
+       int cs;
+       uint16_t rx_length;
+       uint16_t rx_offset;
+};
+
+static uint8_t bl_checksum(const uint8_t *bytes, int length)
+{
+       int i;
+       uint8_t csum;
+
+       if (length == 1) {
+               csum = ~bytes[0];
+       } else if (length > 1) {
+               for (csum = 0, i = 0; i < length; i++)
+                       csum ^= bytes[i];
+       } else {
+               csum = 0xFF;
+       }
+
+       return csum;
+}
+
+static uint8_t spi_bl_write_data(const void *data, uint8_t *tx, int length)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_bl *bl = &spi_data->data.bl;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = length + 1,
+               .tx_buf = bl->tx_buffer,
+               .rx_buf = bl->rx_buffer,
+               .cs_change = 1,
+       };
+
+       tx[length] = bl_checksum(tx, length);
+       memcpy(bl->tx_buffer, tx, length + 1);
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       if (spi_sync_locked(spi_data->device, &msg) == 0)
+               return bl->rx_buffer[length];
+       else
+               return CMD_NACK;
+}
+
+static uint8_t spi_bl_write_cmd(const void *data, uint8_t cmd)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_bl *bl = &spi_data->data.bl;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 3,
+               .tx_buf = bl->tx_buffer,
+               .rx_buf = bl->rx_buffer,
+               .cs_change = 1,
+       };
+       bl->tx_buffer[0] = CMD_SOF;
+       bl->tx_buffer[1] = cmd;
+       bl->tx_buffer[2] = ~cmd;
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       if (spi_sync_locked(spi_data->device, &msg) == 0)
+               return CMD_ACK;
+       else
+               return CMD_NACK;
+}
+
+static uint8_t spi_bl_read_data(const void *data, uint8_t *rx, int length)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_bl *bl = &spi_data->data.bl;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = length + 1,
+               .tx_buf = bl->tx_buffer,
+               .rx_buf = bl->rx_buffer,
+               .cs_change = 1,
+       };
+       memset(&bl->tx_buffer[0], 0x00, length + 1);
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       if (spi_sync_locked(spi_data->device, &msg) == 0) {
+               memcpy(rx, &bl->rx_buffer[1], length);
+               return CMD_ACK;
+       } else {
+               return CMD_NACK;
+       }
+}
+
+static uint8_t spi_bl_read_ack(const void *data)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_bl *bl = &spi_data->data.bl;
+       int32_t timeout = SPI_TIMEOUT;
+       uint8_t ret;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 1,
+               .tx_buf = bl->tx_buffer,
+               .rx_buf = bl->rx_buffer,
+               .cs_change = 1,
+       };
+       bl->tx_buffer[0] = 0x00;
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       if (spi_sync_locked(spi_data->device, &msg) == 0) {
+               do {
+                       spi_sync_locked(spi_data->device, &msg);
+                       timeout--;
+                       if (bl->rx_buffer[0] != CMD_ACK
+                           && bl->rx_buffer[0] != CMD_NACK
+                           && timeout % 256 == 0)
+                               schedule();
+               } while (bl->rx_buffer[0] != CMD_ACK
+                        && bl->rx_buffer[0] != CMD_NACK && timeout > 0);
+
+               if (bl->rx_buffer[0] != CMD_ACK && bl->rx_buffer[0] != CMD_NACK
+                   && timeout == 0)
+                       ret = CMD_NACK;
+               else
+                       ret = bl->rx_buffer[0];
+
+               bl->tx_buffer[0] = CMD_ACK;
+               spi_sync_locked(spi_data->device, &msg);
+               return ret;
+       } else {
+               return CMD_NACK;
+       }
+}
+
+static int spi_bl_open(const void *data)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       int ret;
+
+       spi_bus_lock(spi_data->device->master);
+       spi_data->device->max_speed_hz = 1000000;
+       spi_data->device->mode = SPI_MODE_0;
+       spi_data->device->bits_per_word = 8;
+       ret = spi_setup(spi_data->device);
+       if (!ret)
+               gpio_set_value(spi_data->cs, 0);
+
+       return ret;
+}
+
+static void spi_bl_close(const void *data)
+{
+       const struct nanohub_spi_data *spi_data = data;
+
+       gpio_set_value(spi_data->cs, 1);
+       spi_bus_unlock(spi_data->device->master);
+}
+
+static uint8_t spi_bl_sync(const void *data)
+{
+       const struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_bl *bl = &spi_data->data.bl;
+       int32_t timeout = SPI_TIMEOUT;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 1,
+               .tx_buf = bl->tx_buffer,
+               .rx_buf = bl->rx_buffer,
+               .cs_change = 1,
+       };
+       bl->tx_buffer[0] = CMD_SOF;
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       do {
+               if (spi_sync_locked(spi_data->device, &msg) != 0)
+                       return CMD_NACK;
+               timeout--;
+               if (bl->rx_buffer[0] != CMD_SOF_ACK && timeout % 256 == 0)
+                       schedule();
+       } while (bl->rx_buffer[0] != CMD_SOF_ACK && timeout > 0);
+
+       if (bl->rx_buffer[0] == CMD_SOF_ACK)
+               return bl->read_ack(data);
+       else
+               return CMD_NACK;
+}
+
+void nanohub_spi_bl_init(struct nanohub_spi_data *spi_data)
+{
+       struct nanohub_bl *bl = &spi_data->data.bl;
+
+       bl->open = spi_bl_open;
+       bl->sync = spi_bl_sync;
+       bl->write_data = spi_bl_write_data;
+       bl->write_cmd = spi_bl_write_cmd;
+       bl->read_data = spi_bl_read_data;
+       bl->read_ack = spi_bl_read_ack;
+       bl->close = spi_bl_close;
+}
+
+int nanohub_spi_write(void *data, uint8_t *tx, int length, int timeout)
+{
+       struct nanohub_spi_data *spi_data = data;
+       const struct nanohub_comms *comms = &spi_data->data.comms;
+       int max_len = sizeof(struct nanohub_packet) + MAX_UINT8 +
+                     sizeof(struct nanohub_packet_crc);
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = max_len + timeout,
+               .tx_buf = comms->tx_buffer,
+               .rx_buf = comms->rx_buffer,
+               .cs_change = 1,
+       };
+       spi_data->rx_offset = max_len;
+       spi_data->rx_length = max_len + timeout;
+       memcpy(comms->tx_buffer, tx, length);
+       memset(comms->tx_buffer + length, 0xFF, max_len + timeout - length);
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       if (spi_sync_locked(spi_data->device, &msg) == 0)
+               return length;
+       else
+               return ERROR_NACK;
+}
+
+int nanohub_spi_read(void *data, uint8_t *rx, int max_length, int timeout)
+{
+       struct nanohub_spi_data *spi_data = data;
+       struct nanohub_comms *comms = &spi_data->data.comms;
+       const int min_size = sizeof(struct nanohub_packet) +
+           sizeof(struct nanohub_packet_crc);
+       int i, ret;
+       int offset = 0;
+       struct nanohub_packet *packet = NULL;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = timeout,
+               .tx_buf = comms->tx_buffer,
+               .rx_buf = comms->rx_buffer,
+               .cs_change = 1,
+       };
+
+       if (max_length < min_size)
+               return ERROR_NACK;
+
+       /* consume leftover bytes, if any */
+       if (spi_data->rx_offset < spi_data->rx_length) {
+               for (i = spi_data->rx_offset; i < spi_data->rx_length; i++) {
+                       if (comms->rx_buffer[i] != 0xFF) {
+                               offset = spi_data->rx_length - i;
+
+                               if (offset <
+                                   offsetof(struct nanohub_packet,
+                                            len) + sizeof(packet->len)) {
+                                       memcpy(rx, &comms->rx_buffer[i],
+                                              offset);
+                                       xfer.len =
+                                           min_size + MAX_UINT8 - offset;
+                                       break;
+                               } else {
+                                       packet =
+                                           (struct nanohub_packet *)&comms->
+                                           rx_buffer[i];
+                                       if (offset < min_size + packet->len) {
+                                               memcpy(rx, packet, offset);
+                                               xfer.len =
+                                                   min_size + packet->len -
+                                                   offset;
+                                               break;
+                                       } else {
+                                               memcpy(rx, packet,
+                                                      min_size + packet->len);
+                                               spi_data->rx_offset = i +
+                                                    min_size + packet->len;
+                                               return min_size + packet->len;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (xfer.len != 1 && xfer.len < SPI_MIN_DMA)
+               xfer.len = SPI_MIN_DMA;
+       memset(comms->tx_buffer, 0xFF, xfer.len);
+
+       spi_message_init_with_transfers(&msg, &xfer, 1);
+
+       ret = spi_sync_locked(spi_data->device, &msg);
+       if (ret == 0) {
+               if (offset > 0) {
+                       packet = (struct nanohub_packet *)rx;
+                       if (offset + xfer.len > max_length)
+                               memcpy(&rx[offset], comms->rx_buffer,
+                                       max_length - offset);
+                       else
+                               memcpy(&rx[offset], comms->rx_buffer, xfer.len);
+                       spi_data->rx_length = xfer.len;
+                       spi_data->rx_offset = min_size + packet->len - offset;
+               } else {
+                       for (i = 0; i < xfer.len; i++) {
+                               if (comms->rx_buffer[i] != 0xFF) {
+                                       spi_data->rx_length = xfer.len;
+
+                                       if (xfer.len - i < min_size) {
+                                               spi_data->rx_offset = i;
+                                               break;
+                                       } else {
+                                               packet =
+                                                   (struct nanohub_packet *)
+                                                   &comms->rx_buffer[i];
+                                               if (xfer.len - i <
+                                                   min_size + packet->len) {
+                                                       packet = NULL;
+                                                       spi_data->rx_offset = i;
+                                               } else {
+                                                       memcpy(rx, packet,
+                                                              min_size +
+                                                              packet->len);
+                                                       spi_data->rx_offset =
+                                                           i + min_size +
+                                                           packet->len;
+                                               }
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (ret < 0)
+               return ret;
+       else if (!packet)
+               return 0;
+       else
+               return min_size + packet->len;
+}
+
+static int nanohub_spi_open(void *data)
+{
+       struct nanohub_spi_data *spi_data = data;
+       int ret;
+
+       down(&spi_data->spi_sem);
+       spi_bus_lock(spi_data->device->master);
+       spi_data->device->max_speed_hz = 10000000;
+       spi_data->device->mode = SPI_MODE_0;
+       spi_data->device->bits_per_word = 8;
+       ret = spi_setup(spi_data->device);
+       if (!ret) {
+               udelay(40);
+               gpio_set_value(spi_data->cs, 0);
+               udelay(30);
+       }
+       return ret;
+}
+
+static void nanohub_spi_close(void *data)
+{
+       struct nanohub_spi_data *spi_data = data;
+
+       gpio_set_value(spi_data->cs, 1);
+       spi_bus_unlock(spi_data->device->master);
+       up(&spi_data->spi_sem);
+       udelay(60);
+}
+
+void nanohub_spi_comms_init(struct nanohub_spi_data *spi_data)
+{
+       struct nanohub_comms *comms = &spi_data->data.comms;
+       int max_len = sizeof(struct nanohub_packet) + MAX_UINT8 +
+                     sizeof(struct nanohub_packet_crc);
+
+       comms->seq = 1;
+       comms->timeout_write = 544;
+       comms->timeout_ack = 272;
+       comms->timeout_reply = 512;
+       comms->open = nanohub_spi_open;
+       comms->close = nanohub_spi_close;
+       comms->write = nanohub_spi_write;
+       comms->read = nanohub_spi_read;
+
+       max_len += comms->timeout_write;
+       max_len = max(max_len, comms->timeout_ack);
+       max_len = max(max_len, comms->timeout_reply);
+       comms->tx_buffer = kmalloc(max_len, GFP_KERNEL | GFP_DMA);
+       comms->rx_buffer = kmalloc(max_len, GFP_KERNEL | GFP_DMA);
+
+       spi_data->rx_length = 0;
+       spi_data->rx_offset = 0;
+
+       sema_init(&spi_data->spi_sem, 1);
+}
+
+static int nanohub_spi_probe(struct spi_device *spi)
+{
+       struct nanohub_spi_data *spi_data;
+       struct iio_dev *iio_dev;
+       int error;
+
+       iio_dev = iio_device_alloc(sizeof(struct nanohub_spi_data));
+
+       iio_dev = nanohub_probe(&spi->dev, iio_dev);
+
+       if (IS_ERR(iio_dev))
+               return PTR_ERR(iio_dev);
+
+       spi_data = iio_priv(iio_dev);
+
+       spi_set_drvdata(spi, iio_dev);
+
+       if (gpio_is_valid(spi_data->data.pdata->spi_cs_gpio)) {
+               error =
+                   gpio_request(spi_data->data.pdata->spi_cs_gpio,
+                                "nanohub_spi_cs");
+               if (error) {
+                       pr_err("nanohub: spi_cs_gpio request failed\n");
+               } else {
+                       spi_data->cs = spi_data->data.pdata->spi_cs_gpio;
+                       gpio_direction_output(spi_data->cs, 1);
+               }
+       } else {
+               pr_err("nanohub: spi_cs_gpio is not valid\n");
+       }
+
+       spi_data->device = spi;
+       nanohub_spi_comms_init(spi_data);
+
+       spi_data->data.bl.cmd_erase = CMD_ERASE;
+       spi_data->data.bl.cmd_read_memory = CMD_READ_MEMORY;
+       spi_data->data.bl.cmd_write_memory = CMD_WRITE_MEMORY;
+       spi_data->data.bl.cmd_get_version = CMD_GET_VERSION;
+       spi_data->data.bl.cmd_get_id = CMD_GET_ID;
+       spi_data->data.bl.cmd_readout_protect = CMD_READOUT_PROTECT;
+       spi_data->data.bl.cmd_readout_unprotect = CMD_READOUT_UNPROTECT;
+       spi_data->data.bl.cmd_update_finished = CMD_UPDATE_FINISHED;
+       nanohub_spi_bl_init(spi_data);
+
+       nanohub_reset(&spi_data->data);
+
+       return 0;
+}
+
+static int nanohub_spi_remove(struct spi_device *spi)
+{
+       struct nanohub_spi_data *spi_data;
+       struct iio_dev *iio_dev;
+
+       iio_dev = spi_get_drvdata(spi);
+       spi_data = iio_priv(iio_dev);
+
+       if (gpio_is_valid(spi_data->cs)) {
+               gpio_direction_output(spi_data->cs, 1);
+               gpio_free(spi_data->cs);
+       }
+
+       return nanohub_remove(iio_dev);
+}
+
+static int nanohub_spi_suspend(struct device *dev)
+{
+       struct iio_dev *iio_dev = spi_get_drvdata(to_spi_device(dev));
+       struct nanohub_spi_data *spi_data = iio_priv(iio_dev);
+       int ret;
+
+       ret = nanohub_suspend(iio_dev);
+
+       if (!ret) {
+               ret = down_interruptible(&spi_data->spi_sem);
+               if (ret)
+                       up(&spi_data->spi_sem);
+       }
+
+       return ret;
+}
+
+static int nanohub_spi_resume(struct device *dev)
+{
+       struct iio_dev *iio_dev = spi_get_drvdata(to_spi_device(dev));
+       struct nanohub_spi_data *spi_data = iio_priv(iio_dev);
+
+       up(&spi_data->spi_sem);
+
+       return nanohub_resume(iio_dev);
+}
+
+static struct spi_device_id nanohub_spi_id[] = {
+       {NANOHUB_NAME, 0},
+       {},
+};
+
+static const struct dev_pm_ops nanohub_spi_pm_ops = {
+       .suspend = nanohub_spi_suspend,
+       .resume = nanohub_spi_resume,
+};
+
+static struct spi_driver nanohub_spi_driver = {
+       .driver = {
+                  .name = NANOHUB_NAME,
+                  .owner = THIS_MODULE,
+                  .pm = &nanohub_spi_pm_ops,
+                  },
+       .probe = nanohub_spi_probe,
+       .remove = nanohub_spi_remove,
+       .id_table = nanohub_spi_id,
+};
+
+int __init nanohub_spi_init(void)
+{
+       return spi_register_driver(&nanohub_spi_driver);
+}
+
+void nanohub_spi_cleanup(void)
+{
+       spi_unregister_driver(&nanohub_spi_driver);
+}
+
+MODULE_DEVICE_TABLE(spi, nanohub_spi_id);
diff --git a/drivers/staging/nanohub/spi.h b/drivers/staging/nanohub/spi.h
new file mode 100644 (file)
index 0000000..0155c81
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _NANOHUB_SPI_H
+#define _NANOHUB_SPI_H
+
+int __init nanohub_spi_init(void);
+void nanohub_spi_cleanup(void);
+
+#endif
diff --git a/include/linux/platform_data/nanohub.h b/include/linux/platform_data/nanohub.h
new file mode 100644 (file)
index 0000000..f3050bd
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __LINUX_PLATFORM_DATA_NANOHUB_H
+#define __LINUX_PLATFORM_DATA_NANOHUB_H
+
+#include <linux/types.h>
+
+struct nanohub_flash_bank {
+       int bank;
+       u32 address;
+       size_t length;
+};
+
+struct nanohub_platform_data {
+       u32 wakeup_gpio;
+       u32 nreset_gpio;
+       u32 boot0_gpio;
+       u32 irq1_gpio;
+       u32 irq2_gpio;
+       u32 spi_cs_gpio;
+       u32 bl_addr;
+       u32 num_flash_banks;
+       struct nanohub_flash_bank *flash_banks;
+       u32 num_shared_flash_banks;
+       struct nanohub_flash_bank *shared_flash_banks;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_NANOHUB_H */