--- /dev/null
+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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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, ¶m);
+ 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");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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 */