ANDROID: nanohub: Add nanohub kernel driver

Adds nanohub kernel driver files only from patch stack:
b4cbbe1aa08b nanohub: fix out of bounds write in nanohub_spi_read
b1476b2471b2 nanohub: detect wakeup signal stuck high and reset
e8faaee106b0 ANDROID: fix uninitilized variable
e9199f79cc9d nanohub: error reset time calculation should use monotonic time
10d17fa5d6fa nanohub: issue a reset to nanohub after a certain number of errors.
35effaa474fd nanohub: fix premature timeout and handling of split acks
c11e59628846 nanohub: increase wakeup sensor sample wakelock from 10ms to 250ms
1dd48648788f nanohub: fix build on non arm64 targets
9ceaa6d90e41 ANDROID: nanohub: force correct state of wakeup signal
06df86d16113 nanohub: fix non-wakeup sensors when using irq2
905edb4cb668 nanohub: send one large spi transaction for cmd + response
1d3e0d98e114 ANDROID: nanohub: fix suspend interaction with IRQ2
3c4a069a03ce ANDROID: nanohub: fix error in IRQ management
bb9d5a148313 nanohub: fix nanohub_request_gpios return value
9ed81dd96459 ANDROID: nanohub: fix FW upload on older versions
60f57f4d2444 ANDROID: nanohub: fix error handling on probe path
00903df14481 nanohub: add missing spin_lock_init
285eecf712c2 nanohub: add context_hub hal support
990695be3da6 nanohub: time out messages via elapsed time and not transactions
1637ec34beeb nanohub: add timeout to suspend
ef4355f12d2e nanohub: add spinlock around wakeup gpio changes in request and release wakeup
b16570f4a009 nanohub: add nanohub kernel driver

Note: CONFIG_NANOHUB_I2C was removed

Change-Id: I3cd512ac5c5720aeb4b4088944726fb09f7eb7fd
This commit is contained in:
Ben Fennema
2016-01-11 15:41:01 -08:00
committed by Dmitry Shmidt
parent 8853995868
commit 33615a41e4
14 changed files with 3914 additions and 0 deletions
@@ -0,0 +1,35 @@
GOOGLE nanohub sensorhub
Required properties:
- compatible : should be "nanohub"
Example:
&spi_7 {
sensorhub@0 {
compatible = "nanohub";
reg = <0>;
spi-max-frequency = <25000000>;
spi-cpol;
spi-cpha;
interrupt-parent = <&msm_gpio>;
interrupts = <66 0x2>;
sensorhub,nreset-gpio = <&msm_gpio 59 0>;
sensorhub,boot0-gpio = <&msm_gpio 60 0>;
sensorhub,wakeup-gpio = <&msm_gpio 65 0>;
sensorhub,irq1-gpio = <&msm_gpio 66 0>;
sensorhub,spi-cs-gpio = <&msm_gpio 43 0>;
sensorhub,bl-addr = <0x08000000>;
sensorhub,kernel-addr = <0x0800C000>;
sensorhub,shared-addr = <0x08020000>;
sensorhub,flash-banks = <0 0x08000000 0x04000>,
<3 0x0800C000 0x04000>,
<4 0x08010000 0x10000>,
<5 0x08020000 0x20000>;
sensorhub,num-flash-banks = <4>;
pinctrl-names = "default";
pinctrl-0 = <&sensorhub_ctrl_active
&sensorhub_int1_active
&sensorhub_int2_active>;
+2
View File
@@ -106,4 +106,6 @@ source "drivers/staging/greybus/Kconfig"
source "drivers/staging/vc04_services/Kconfig"
source "drivers/staging/nanohub/Kconfig"
endif # STAGING
+1
View File
@@ -42,3 +42,4 @@ obj-$(CONFIG_ISDN_I4L) += i4l/
obj-$(CONFIG_KS7010) += ks7010/
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/
obj-$(CONFIG_NANOHUB) += nanohub/
+22
View File
@@ -0,0 +1,22 @@
config NANOHUB
tristate "Nanohub"
select IIO
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.
If in doubt, say Y here.
endif # NANOHUB
+7
View File
@@ -0,0 +1,7 @@
#
# Makefile for nanohub
#
obj-$(CONFIG_NANOHUB) += nanohub.o
nanohub-y := main.o bl.o comms.o
nanohub-$(CONFIG_NANOHUB_SPI) += spi.o
+500
View File
@@ -0,0 +1,500 @@
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/platform_data/nanohub.h>
#include <linux/vmalloc.h>
#include "main.h"
#include "bl.h"
#define MAX_BUFFER_SIZE 1024
#define MAX_FLASH_BANKS 16
#define READ_ACK_TIMEOUT 100000
static uint8_t write_len(struct nanohub_data *data, int len)
{
uint8_t buffer[sizeof(uint8_t) + 1];
buffer[0] = len - 1;
return data->bl.write_data(data, buffer, sizeof(uint8_t));
}
static uint8_t write_cnt(struct nanohub_data *data, uint16_t cnt)
{
uint8_t buffer[sizeof(uint16_t) + 1];
buffer[0] = (cnt >> 8) & 0xFF;
buffer[1] = (cnt >> 0) & 0xFF;
return data->bl.write_data(data, buffer, sizeof(uint16_t));
}
static uint8_t write_addr(struct nanohub_data *data, uint32_t addr)
{
uint8_t buffer[sizeof(uint32_t) + 1];
buffer[0] = (addr >> 24) & 0xFF;
buffer[1] = (addr >> 16) & 0xFF;
buffer[2] = (addr >> 8) & 0xFF;
buffer[3] = addr & 0xFF;
return data->bl.write_data(data, buffer, sizeof(uint32_t));
}
/* write length followed by the data */
static uint8_t write_len_data(struct nanohub_data *data, int len,
const uint8_t *buf)
{
uint8_t buffer[sizeof(uint8_t) + 256 + sizeof(uint8_t)];
buffer[0] = len - 1;
memcpy(&buffer[1], buf, len);
return data->bl.write_data(data, buffer, sizeof(uint8_t) + len);
}
/* keep checking for ack until we receive a ack or nack */
static uint8_t read_ack_loop(struct nanohub_data *data)
{
uint8_t ret;
int32_t timeout = READ_ACK_TIMEOUT;
do {
ret = data->bl.read_ack(data);
if (ret != CMD_ACK && ret != CMD_NACK)
schedule();
} while (ret != CMD_ACK && ret != CMD_NACK && timeout-- > 0);
return ret;
}
uint8_t nanohub_bl_sync(struct nanohub_data *data)
{
return data->bl.sync(data);
}
int nanohub_bl_open(struct nanohub_data *data)
{
int ret = -1;
data->bl.tx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
if (!data->bl.tx_buffer)
goto out;
data->bl.rx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
if (!data->bl.rx_buffer)
goto free_tx;
ret = data->bl.open(data);
if (!ret)
goto out;
kfree(data->bl.rx_buffer);
free_tx:
kfree(data->bl.tx_buffer);
out:
return ret;
}
void nanohub_bl_close(struct nanohub_data *data)
{
data->bl.close(data);
kfree(data->bl.tx_buffer);
kfree(data->bl.rx_buffer);
}
static uint8_t write_bank(struct nanohub_data *data, int bank, uint32_t addr,
const uint8_t *buf, size_t length)
{
const struct nanohub_platform_data *pdata = data->pdata;
uint8_t status = CMD_ACK;
uint32_t offset;
if (addr <= pdata->flash_banks[bank].address) {
offset = pdata->flash_banks[bank].address - addr;
if (addr + length >
pdata->flash_banks[bank].address +
pdata->flash_banks[bank].length)
status =
nanohub_bl_write_memory(data,
pdata->flash_banks[bank].
address,
pdata->flash_banks[bank].
length, buf + offset);
else
status =
nanohub_bl_write_memory(data,
pdata->flash_banks[bank].
address, length - offset,
buf + offset);
} else {
if (addr + length >
pdata->flash_banks[bank].address +
pdata->flash_banks[bank].length)
status =
nanohub_bl_write_memory(data, addr,
pdata->flash_banks[bank].
address +
pdata->flash_banks[bank].
length - addr, buf);
else
status =
nanohub_bl_write_memory(data, addr, length, buf);
}
return status;
}
uint8_t nanohub_bl_download(struct nanohub_data *data, uint32_t addr,
const uint8_t *image, size_t length)
{
const struct nanohub_platform_data *pdata = data->pdata;
uint8_t *ptr;
int i, j;
uint8_t status;
uint32_t offset;
uint8_t erase_mask[MAX_FLASH_BANKS] = { 0 };
uint8_t erase_write_mask[MAX_FLASH_BANKS] = { 0 };
uint8_t write_mask[MAX_FLASH_BANKS] = { 0 };
if (pdata->num_flash_banks > MAX_FLASH_BANKS) {
status = CMD_NACK;
goto out;
}
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_download: sync=%02x\n", status);
goto out;
}
ptr = vmalloc(length);
if (!ptr) {
status = CMD_NACK;
goto out;
}
status = nanohub_bl_read_memory(data, addr, length, ptr);
pr_info(
"nanohub: nanohub_bl_read_memory: status=%02x, addr=%08x, length=%zd\n",
status, addr, length);
for (i = 0; i < pdata->num_flash_banks; i++) {
if (addr >= pdata->flash_banks[i].address &&
addr <
pdata->flash_banks[i].address +
pdata->flash_banks[i].length) {
break;
}
}
offset = (uint32_t) (addr - pdata->flash_banks[i].address);
j = 0;
while (j < length && i < pdata->num_flash_banks) {
if (image[j] != 0xFF)
erase_write_mask[i] = true;
if ((ptr[j] & image[j]) != image[j]) {
erase_mask[i] = true;
if (erase_write_mask[i]) {
j += pdata->flash_banks[i].length - offset;
offset = pdata->flash_banks[i].length;
} else {
j++;
offset++;
}
} else {
if (ptr[j] != image[j])
write_mask[i] = true;
j++;
offset++;
}
if (offset == pdata->flash_banks[i].length) {
i++;
offset = 0;
if (i < pdata->num_flash_banks)
j += (pdata->flash_banks[i].address -
pdata->flash_banks[i - 1].address -
pdata->flash_banks[i - 1].length);
else
j = length;
}
}
for (i = 0; status == CMD_ACK && i < pdata->num_flash_banks; i++) {
pr_info("nanohub: i=%d, erase=%d, erase_write=%d, write=%d\n",
i, erase_mask[i], erase_write_mask[i], write_mask[i]);
if (erase_mask[i]) {
status =
nanohub_bl_erase_sector(data,
pdata->flash_banks[i].bank);
if (status == CMD_ACK && erase_write_mask[i])
status =
write_bank(data, i, addr, image, length);
} else if (write_mask[i]) {
status = write_bank(data, i, addr, image, length);
}
}
vfree(ptr);
out:
return status;
}
uint8_t nanohub_bl_erase_shared(struct nanohub_data *data)
{
const struct nanohub_platform_data *pdata = data->pdata;
int i;
uint8_t status;
if (pdata->num_shared_flash_banks > MAX_FLASH_BANKS) {
status = CMD_NACK;
goto out;
}
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_erase_shared: sync=%02x\n", status);
goto out;
}
for (i = 0;
status == CMD_ACK && i < pdata->num_shared_flash_banks;
i++) {
status = nanohub_bl_erase_sector(data,
pdata->shared_flash_banks[i].bank);
}
out:
return status;
}
uint8_t nanohub_bl_erase_shared_bl(struct nanohub_data *data)
{
uint8_t status;
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_erase_shared_bl: sync=%02x\n", status);
goto out;
}
status = nanohub_bl_erase_special(data, 0xFFF0);
out:
return status;
}
/* erase a single sector */
uint8_t nanohub_bl_erase_sector(struct nanohub_data *data, uint16_t sector)
{
uint8_t ret;
data->bl.write_cmd(data, data->bl.cmd_erase);
ret = data->bl.read_ack(data);
if (ret == CMD_ACK)
ret = write_cnt(data, 0x0000);
if (ret != CMD_NACK)
ret = read_ack_loop(data);
if (ret == CMD_ACK)
ret = write_cnt(data, sector);
if (ret != CMD_NACK)
ret = read_ack_loop(data);
return ret;
}
/* erase special */
uint8_t nanohub_bl_erase_special(struct nanohub_data *data, uint16_t special)
{
uint8_t ret;
data->bl.write_cmd(data, data->bl.cmd_erase);
ret = data->bl.read_ack(data);
if (ret == CMD_ACK)
ret = write_cnt(data, special);
if (ret != CMD_NACK)
ret = read_ack_loop(data);
return ret;
}
/* read memory - this will chop the request into 256 byte reads */
uint8_t nanohub_bl_read_memory(struct nanohub_data *data, uint32_t addr,
uint32_t length, uint8_t *buffer)
{
uint8_t ret = CMD_ACK;
uint32_t offset = 0;
while (ret == CMD_ACK && length > offset) {
data->bl.write_cmd(data, data->bl.cmd_read_memory);
ret = data->bl.read_ack(data);
if (ret == CMD_ACK) {
write_addr(data, addr + offset);
ret = read_ack_loop(data);
if (ret == CMD_ACK) {
if (length - offset >= 256) {
write_len(data, 256);
ret = read_ack_loop(data);
if (ret == CMD_ACK) {
data->bl.read_data(data,
&buffer
[offset],
256);
offset += 256;
}
} else {
write_len(data, length - offset);
ret = read_ack_loop(data);
if (ret == CMD_ACK) {
data->bl.read_data(data,
&buffer
[offset],
length -
offset);
offset = length;
}
}
}
}
}
return ret;
}
/* write memory - this will chop the request into 256 byte writes */
uint8_t nanohub_bl_write_memory(struct nanohub_data *data, uint32_t addr,
uint32_t length, const uint8_t *buffer)
{
uint8_t ret = CMD_ACK;
uint32_t offset = 0;
while (ret == CMD_ACK && length > offset) {
data->bl.write_cmd(data, data->bl.cmd_write_memory);
ret = data->bl.read_ack(data);
if (ret == CMD_ACK) {
write_addr(data, addr + offset);
ret = read_ack_loop(data);
if (ret == CMD_ACK) {
if (length - offset >= 256) {
write_len_data(data, 256,
&buffer[offset]);
offset += 256;
} else {
write_len_data(data, length - offset,
&buffer[offset]);
offset = length;
}
ret = read_ack_loop(data);
}
}
}
return ret;
}
uint8_t nanohub_bl_get_version(struct nanohub_data *data, uint8_t *version)
{
uint8_t status;
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_get_version: sync=%02x\n", status);
goto out;
}
data->bl.write_cmd(data, data->bl.cmd_get_version);
status = data->bl.read_ack(data);
if (status == CMD_ACK)
data->bl.read_data(data, version, 1);
status = data->bl.read_ack(data);
out:
return status;
}
uint8_t nanohub_bl_get_id(struct nanohub_data *data, uint16_t *id)
{
uint8_t status;
uint8_t len;
uint8_t buffer[256];
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_get_id: sync=%02x\n", status);
goto out;
}
data->bl.write_cmd(data, data->bl.cmd_get_id);
status = data->bl.read_ack(data);
if (status == CMD_ACK) {
data->bl.read_data(data, &len, 1);
data->bl.read_data(data, buffer, len+1);
*id = (buffer[0] << 8) | buffer[1];
}
status = data->bl.read_ack(data);
out:
return status;
}
uint8_t nanohub_bl_lock(struct nanohub_data *data)
{
uint8_t status;
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_lock: sync=%02x\n", status);
goto out;
}
data->bl.write_cmd(data, data->bl.cmd_readout_protect);
status = data->bl.read_ack(data);
if (status == CMD_ACK)
status = read_ack_loop(data);
out:
return status;
}
uint8_t nanohub_bl_unlock(struct nanohub_data *data)
{
uint8_t status;
status = nanohub_bl_sync(data);
if (status != CMD_ACK) {
pr_err("nanohub_bl_lock: sync=%02x\n", status);
goto out;
}
data->bl.write_cmd(data, data->bl.cmd_readout_unprotect);
status = data->bl.read_ack(data);
if (status == CMD_ACK)
status = read_ack_loop(data);
out:
return status;
}
uint8_t nanohub_bl_update_finished(struct nanohub_data *data)
{
uint8_t ret;
data->bl.write_cmd(data, data->bl.cmd_update_finished);
ret = read_ack_loop(data);
return ret;
}
+95
View File
@@ -0,0 +1,95 @@
/*
* 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/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
+582
View File
@@ -0,0 +1,582 @@
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include "main.h"
#include "comms.h"
#define READ_ACK_TIMEOUT_MS 10
#define READ_MSG_TIMEOUT_MS 70
#define RESEND_SHORT_DELAY_US 500 /* 500us - 1ms */
#define RESEND_LONG_DELAY_US 100000 /* 100ms - 200ms */
static const uint32_t crc_table[] = {
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
};
static uint32_t crc32_word(uint32_t crc, uint32_t data, int cnt)
{
int i;
crc = crc ^ data;
for (i = 0; i < cnt; i++)
crc = (crc << 4) ^ crc_table[crc >> 28];
return crc;
}
uint32_t crc32(const uint8_t *buffer, int length, uint32_t crc)
{
uint32_t *data = (uint32_t *)buffer;
uint32_t word;
int i;
/* word by word crc32 */
for (i = 0; i < (length >> 2); i++)
crc = crc32_word(crc, data[i], 8);
/* zero pad last word if required */
if (length & 0x3) {
for (i *= 4, word = 0; i < length; i++)
word |= buffer[i] << ((i & 0x3) * 8);
crc = crc32_word(crc, word, 8);
}
return crc;
}
static inline size_t pad(size_t length)
{
return (length + 3) & ~3;
}
static inline size_t tot_len(size_t length)
{
/* [TYPE:1] [LENGTH:3] [DATA] [PAD:0-3] [CRC:4] */
return sizeof(uint32_t) + pad(length) + sizeof(uint32_t);
}
static struct nanohub_packet_pad *packet_alloc(int flags)
{
int len =
sizeof(struct nanohub_packet_pad) + MAX_UINT8 +
sizeof(struct nanohub_packet_crc);
uint8_t *packet = kmalloc(len, flags);
memset(packet, 0xFF, len);
return (struct nanohub_packet_pad *)packet;
}
static int packet_create(struct nanohub_packet *packet, uint32_t seq,
uint32_t reason, uint8_t len, const uint8_t *data,
bool user)
{
struct nanohub_packet_crc crc;
int ret = sizeof(struct nanohub_packet) + len +
sizeof(struct nanohub_packet_crc);
if (packet) {
packet->sync = COMMS_SYNC;
packet->seq = seq;
packet->reason = reason;
packet->len = len;
if (len > 0) {
if (user) {
if (copy_from_user(packet->data, data, len) !=
0)
ret = ERROR_NACK;
} else {
memcpy(packet->data, data, len);
}
}
crc.crc =
crc32((uint8_t *) packet,
sizeof(struct nanohub_packet) + len, ~0);
memcpy(&packet->data[len], &crc.crc,
sizeof(struct nanohub_packet_crc));
} else {
ret = ERROR_NACK;
}
return ret;
}
static int packet_verify(struct nanohub_packet *packet)
{
struct nanohub_packet_crc crc;
int cmp;
crc.crc =
crc32((uint8_t *) packet,
sizeof(struct nanohub_packet) + packet->len, ~0);
cmp =
memcmp(&crc.crc, &packet->data[packet->len],
sizeof(struct nanohub_packet_crc));
if (cmp != 0) {
uint8_t *ptr = (uint8_t *)packet;
pr_debug("nanohub: gen crc: %08x, got crc: %08x\n", crc.crc,
*(uint32_t *)&packet->data[packet->len]);
pr_debug(
"nanohub: %02x [%02x %02x %02x %02x] [%02x %02x %02x %02x] [%02x] [%02x %02x %02x %02x\n",
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12],
ptr[13]);
}
return cmp;
}
static void packet_free(struct nanohub_packet_pad *packet)
{
kfree(packet);
}
static int read_ack(struct nanohub_data *data, struct nanohub_packet *response,
int timeout)
{
int ret, i;
const int max_size = sizeof(struct nanohub_packet) + MAX_UINT8 +
sizeof(struct nanohub_packet_crc);
unsigned long end = jiffies + msecs_to_jiffies(READ_ACK_TIMEOUT_MS);
for (i = 0; time_before_eq(jiffies, end); i++) {
ret =
data->comms.read(data, (uint8_t *) response, max_size,
timeout);
if (ret == 0) {
pr_debug("nanohub: read_ack: %d: empty packet\n", i);
ret = ERROR_NACK;
continue;
} else if (ret < sizeof(struct nanohub_packet)) {
pr_debug("nanohub: read_ack: %d: too small\n", i);
ret = ERROR_NACK;
continue;
} else if (ret <
sizeof(struct nanohub_packet) + response->len +
sizeof(struct nanohub_packet_crc)) {
pr_debug("nanohub: read_ack: %d: too small length\n",
i);
ret = ERROR_NACK;
continue;
} else if (ret !=
sizeof(struct nanohub_packet) + response->len +
sizeof(struct nanohub_packet_crc)) {
pr_debug("nanohub: read_ack: %d: wrong length\n", i);
ret = ERROR_NACK;
break;
} else if (packet_verify(response) != 0) {
pr_debug("nanohub: read_ack: %d: invalid crc\n", i);
ret = ERROR_NACK;
break;
} else {
break;
}
}
return ret;
}
static int read_msg(struct nanohub_data *data, struct nanohub_packet *response,
int timeout)
{
int ret, i;
const int max_size = sizeof(struct nanohub_packet) + MAX_UINT8 +
sizeof(struct nanohub_packet_crc);
unsigned long end = jiffies + msecs_to_jiffies(READ_MSG_TIMEOUT_MS);
for (i = 0; time_before_eq(jiffies, end); i++) {
ret =
data->comms.read(data, (uint8_t *) response, max_size,
timeout);
if (ret == 0) {
pr_debug("nanohub: read_msg: %d: empty packet\n", i);
ret = ERROR_NACK;
continue;
} else if (ret < sizeof(struct nanohub_packet)) {
pr_debug("nanohub: read_msg: %d: too small\n", i);
ret = ERROR_NACK;
continue;
} else if (ret <
sizeof(struct nanohub_packet) + response->len +
sizeof(struct nanohub_packet_crc)) {
pr_debug("nanohub: read_msg: %d: too small length\n",
i);
ret = ERROR_NACK;
continue;
} else if (ret !=
sizeof(struct nanohub_packet) + response->len +
sizeof(struct nanohub_packet_crc)) {
pr_debug("nanohub: read_msg: %d: wrong length\n", i);
ret = ERROR_NACK;
break;
} else if (packet_verify(response) != 0) {
pr_debug("nanohub: read_msg: %d: invalid crc\n", i);
ret = ERROR_NACK;
break;
} else {
break;
}
}
return ret;
}
static int get_reply(struct nanohub_data *data, struct nanohub_packet *response,
uint32_t seq)
{
int ret;
ret = read_ack(data, response, data->comms.timeout_ack);
if (ret >= 0 && response->seq == seq) {
if (response->reason == CMD_COMMS_ACK) {
if (response->len == sizeof(data->interrupts))
memcpy(data->interrupts, response->data,
response->len);
ret =
read_msg(data, response, data->comms.timeout_reply);
if (ret < 0)
ret = ERROR_NACK;
} else {
int i;
uint8_t *b = (uint8_t *) response;
for (i = 0; i < ret; i += 25)
pr_debug(
"nanohub: %d: %d: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ret, i, b[i], b[i + 1], b[i + 2], b[i + 3],
b[i + 4], b[i + 5], b[i + 6], b[i + 7],
b[i + 8], b[i + 9], b[i + 10], b[i + 11],
b[i + 12], b[i + 13], b[i + 14], b[i + 15],
b[i + 16], b[i + 17], b[i + 18], b[i + 19],
b[i + 20], b[i + 21], b[i + 22], b[i + 23],
b[i + 24]);
if (response->reason == CMD_COMMS_NACK)
ret = ERROR_NACK;
else if (response->reason == CMD_COMMS_BUSY)
ret = ERROR_BUSY;
}
if (response->seq != seq)
ret = ERROR_NACK;
} else {
if (ret >= 0) {
int i;
uint8_t *b = (uint8_t *) response;
for (i = 0; i < ret; i += 25)
pr_debug(
"nanohub: %d: %d: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
ret, i, b[i], b[i + 1], b[i + 2], b[i + 3],
b[i + 4], b[i + 5], b[i + 6], b[i + 7],
b[i + 8], b[i + 9], b[i + 10], b[i + 11],
b[i + 12], b[i + 13], b[i + 14], b[i + 15],
b[i + 16], b[i + 17], b[i + 18], b[i + 19],
b[i + 20], b[i + 21], b[i + 22], b[i + 23],
b[i + 24]);
}
ret = ERROR_NACK;
}
return ret;
}
static int nanohub_comms_tx_rx(struct nanohub_data *data,
struct nanohub_packet_pad *pad, int packet_size,
uint32_t seq, uint8_t *rx, size_t rx_len)
{
int ret;
ret = data->comms.write(data, (uint8_t *)&pad->packet, packet_size,
data->comms.timeout_write);
if (ret == packet_size) {
ret = get_reply(data, &pad->packet, seq);
if (ret >= 0) {
if (pad->packet.len > 0) {
if (pad->packet.len > rx_len) {
memcpy(rx, pad->packet.data, rx_len);
ret = rx_len;
} else {
memcpy(rx, pad->packet.data,
pad->packet.len);
ret = pad->packet.len;
}
} else {
ret = 0;
}
}
} else {
ret = ERROR_NACK;
}
return ret;
}
int nanohub_comms_rx_retrans_boottime(struct nanohub_data *data, uint32_t cmd,
uint8_t *rx, size_t rx_len,
int retrans_cnt, int retrans_delay)
{
int packet_size = 0;
struct nanohub_packet_pad *pad = packet_alloc(GFP_KERNEL);
int delay = 0;
int ret;
uint32_t seq;
struct timespec ts;
s64 boottime;
if (pad == NULL)
return ERROR_NACK;
seq = data->comms.seq++;
do {
data->comms.open(data);
get_monotonic_boottime(&ts);
boottime = timespec_to_ns(&ts);
packet_size =
packet_create(&pad->packet, seq, cmd, sizeof(boottime),
(uint8_t *)&boottime, false);
ret =
nanohub_comms_tx_rx(data, pad, packet_size, seq, rx,
rx_len);
if (nanohub_wakeup_eom(data,
(ret == ERROR_BUSY) ||
(ret == ERROR_NACK && retrans_cnt >= 0)))
ret = -EFAULT;
data->comms.close(data);
if (ret == ERROR_NACK) {
retrans_cnt--;
delay += retrans_delay;
if (retrans_cnt >= 0)
udelay(retrans_delay);
} else if (ret == ERROR_BUSY) {
usleep_range(RESEND_LONG_DELAY_US,
RESEND_LONG_DELAY_US * 2);
}
} while ((ret == ERROR_BUSY)
|| (ret == ERROR_NACK && retrans_cnt >= 0));
packet_free(pad);
return ret;
}
int nanohub_comms_tx_rx_retrans(struct nanohub_data *data, uint32_t cmd,
const uint8_t *tx, uint8_t tx_len,
uint8_t *rx, size_t rx_len, bool user,
int retrans_cnt, int retrans_delay)
{
int packet_size = 0;
struct nanohub_packet_pad *pad = packet_alloc(GFP_KERNEL);
int delay = 0;
int ret;
uint32_t seq;
if (pad == NULL)
return ERROR_NACK;
seq = data->comms.seq++;
do {
packet_size =
packet_create(&pad->packet, seq, cmd, tx_len, tx, user);
data->comms.open(data);
ret =
nanohub_comms_tx_rx(data, pad, packet_size, seq, rx,
rx_len);
if (nanohub_wakeup_eom(data,
(ret == ERROR_BUSY) ||
(ret == ERROR_NACK && retrans_cnt >= 0)))
ret = -EFAULT;
data->comms.close(data);
if (ret == ERROR_NACK) {
retrans_cnt--;
delay += retrans_delay;
if (retrans_cnt >= 0)
udelay(retrans_delay);
} else if (ret == ERROR_BUSY) {
usleep_range(RESEND_LONG_DELAY_US,
RESEND_LONG_DELAY_US * 2);
}
} while ((ret == ERROR_BUSY)
|| (ret == ERROR_NACK && retrans_cnt >= 0));
packet_free(pad);
return ret;
}
struct firmware_header {
uint32_t size;
uint32_t crc;
uint8_t type;
} __packed;
struct firmware_chunk {
uint32_t offset;
uint8_t data[128];
}
__packed;
static int nanohub_comms_download(struct nanohub_data *data,
const uint8_t *image, size_t length,
uint8_t type)
{
uint8_t accepted;
struct firmware_header header;
struct firmware_chunk chunk;
int max_chunk_size = sizeof(chunk.data);
int chunk_size;
uint32_t offset = 0;
int ret;
uint8_t chunk_reply, upload_reply = 0, last_reply = 0;
uint32_t clear_interrupts[8] = { 0x00000008 };
uint32_t delay;
header.type = type;
header.size = cpu_to_le32(length);
header.crc = cpu_to_le32(~crc32(image, length, ~0));
if (request_wakeup(data))
return -ERESTARTSYS;
ret = nanohub_comms_tx_rx_retrans(data, CMD_COMMS_START_KERNEL_UPLOAD,
(const uint8_t *)&header,
sizeof(header), &accepted,
sizeof(accepted), false, 10, 10);
release_wakeup(data);
if (ret == 1 && accepted == 1) {
do {
if (request_wakeup(data))
continue;
delay = 0;
chunk.offset = cpu_to_le32(offset);
if (offset + max_chunk_size > length)
chunk_size = length - offset;
else
chunk_size = max_chunk_size;
memcpy(chunk.data, image + offset, chunk_size);
ret =
nanohub_comms_tx_rx_retrans(data,
CMD_COMMS_KERNEL_CHUNK,
(const uint8_t *)&chunk,
sizeof(uint32_t) +
chunk_size,
&chunk_reply,
sizeof(chunk_reply),
false, 10, 10);
pr_debug("nanohub: ret=%d, chunk_reply=%d, offset=%d\n",
ret, chunk_reply, offset);
if (ret == sizeof(chunk_reply)) {
if (chunk_reply == CHUNK_REPLY_ACCEPTED) {
offset += chunk_size;
} else if (chunk_reply == CHUNK_REPLY_WAIT) {
ret = nanohub_wait_for_interrupt(data);
if (ret < 0) {
release_wakeup(data);
continue;
}
nanohub_comms_tx_rx_retrans(data,
CMD_COMMS_CLR_GET_INTR,
(uint8_t *)clear_interrupts,
sizeof(clear_interrupts),
(uint8_t *)data->interrupts,
sizeof(data->interrupts),
false, 10, 0);
} else if (chunk_reply == CHUNK_REPLY_RESEND) {
if (last_reply == CHUNK_REPLY_RESEND)
delay = RESEND_LONG_DELAY_US;
else
delay = RESEND_SHORT_DELAY_US;
} else if (chunk_reply == CHUNK_REPLY_RESTART)
offset = 0;
else if (chunk_reply == CHUNK_REPLY_CANCEL ||
chunk_reply ==
CHUNK_REPLY_CANCEL_NO_RETRY) {
release_wakeup(data);
break;
}
last_reply = chunk_reply;
} else if (ret <= 0) {
release_wakeup(data);
break;
}
release_wakeup(data);
if (delay > 0)
usleep_range(delay, delay * 2);
} while (offset < length);
}
do {
if (upload_reply == UPLOAD_REPLY_PROCESSING)
usleep_range(RESEND_LONG_DELAY_US,
RESEND_LONG_DELAY_US * 2);
if (request_wakeup(data)) {
ret = sizeof(upload_reply);
upload_reply = UPLOAD_REPLY_PROCESSING;
continue;
}
ret = nanohub_comms_tx_rx_retrans(data,
CMD_COMMS_FINISH_KERNEL_UPLOAD,
NULL, 0,
&upload_reply, sizeof(upload_reply),
false, 10, 10);
release_wakeup(data);
} while (ret == sizeof(upload_reply) &&
upload_reply == UPLOAD_REPLY_PROCESSING);
pr_info("nanohub: nanohub_comms_download: ret=%d, upload_reply=%d\n",
ret, upload_reply);
return 0;
}
int nanohub_comms_kernel_download(struct nanohub_data *data,
const uint8_t *image, size_t length)
{
return nanohub_comms_download(data, image, length,
COMMS_FLASH_KERNEL_ID);
}
int nanohub_comms_app_download(struct nanohub_data *data, const uint8_t *image,
size_t length)
{
return nanohub_comms_download(data, image, length, COMMS_FLASH_APP_ID);
}
+129
View File
@@ -0,0 +1,129 @@
/*
* 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 {
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);
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
File diff suppressed because it is too large Load Diff
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _NANOHUB_MAIN_H
#define _NANOHUB_MAIN_H
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/wakelock.h>
#include "comms.h"
#include "bl.h"
#define NANOHUB_NAME "nanohub"
struct nanohub_buf {
struct list_head list;
uint8_t buffer[255];
uint8_t length;
};
struct nanohub_data;
struct nanohub_io {
struct device *dev;
struct nanohub_data *data;
wait_queue_head_t buf_wait;
struct list_head buf_list;
};
static inline struct nanohub_data *dev_get_nanohub_data(struct device *dev)
{
struct nanohub_io *io = dev_get_drvdata(dev);
return io->data;
}
struct nanohub_data {
/* indices for io[] array */
#define ID_NANOHUB_SENSOR 0
#define ID_NANOHUB_COMMS 1
#define ID_NANOHUB_MAX 2
struct iio_dev *iio_dev;
struct nanohub_io io[ID_NANOHUB_MAX];
struct nanohub_comms comms;
struct nanohub_bl bl;
const struct nanohub_platform_data *pdata;
int irq1;
int irq2;
atomic_t kthread_run;
atomic_t thread_state;
wait_queue_head_t kthread_wait;
struct wake_lock wakelock_read;
struct nanohub_io free_pool;
atomic_t lock_mode;
/* these 3 vars should be accessed only with wakeup_wait.lock held */
atomic_t wakeup_cnt;
atomic_t wakeup_lock_cnt;
atomic_t wakeup_acquired;
wait_queue_head_t wakeup_wait;
uint32_t interrupts[8];
ktime_t wakeup_err_ktime;
int wakeup_err_cnt;
ktime_t kthread_err_ktime;
int kthread_err_cnt;
void *vbuf;
struct task_struct *thread;
};
enum {
KEY_WAKEUP_NONE,
KEY_WAKEUP,
KEY_WAKEUP_LOCK,
};
enum {
LOCK_MODE_NONE,
LOCK_MODE_NORMAL,
LOCK_MODE_IO,
LOCK_MODE_IO_BL,
LOCK_MODE_RESET,
LOCK_MODE_SUSPEND_RESUME,
};
int request_wakeup_ex(struct nanohub_data *data, long timeout,
int key, int lock_mode);
void release_wakeup_ex(struct nanohub_data *data, int key, int lock_mode);
int nanohub_wait_for_interrupt(struct nanohub_data *data);
int nanohub_wakeup_eom(struct nanohub_data *data, bool repeat);
struct iio_dev *nanohub_probe(struct device *dev, struct iio_dev *iio_dev);
int nanohub_reset(struct nanohub_data *data);
int nanohub_remove(struct iio_dev *iio_dev);
int nanohub_suspend(struct iio_dev *iio_dev);
int nanohub_resume(struct iio_dev *iio_dev);
static inline int nanohub_irq1_fired(struct nanohub_data *data)
{
const struct nanohub_platform_data *pdata = data->pdata;
return !gpio_get_value(pdata->irq1_gpio);
}
static inline int nanohub_irq2_fired(struct nanohub_data *data)
{
const struct nanohub_platform_data *pdata = data->pdata;
return data->irq2 && !gpio_get_value(pdata->irq2_gpio);
}
static inline int request_wakeup_timeout(struct nanohub_data *data, int timeout)
{
return request_wakeup_ex(data, timeout, KEY_WAKEUP, LOCK_MODE_NORMAL);
}
static inline int request_wakeup(struct nanohub_data *data)
{
return request_wakeup_ex(data, MAX_SCHEDULE_TIMEOUT, KEY_WAKEUP,
LOCK_MODE_NORMAL);
}
static inline void release_wakeup(struct nanohub_data *data)
{
release_wakeup_ex(data, KEY_WAKEUP, LOCK_MODE_NORMAL);
}
#endif
+549
View File
@@ -0,0 +1,549 @@
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include "main.h"
#include "bl.h"
#include "comms.h"
#define SPI_TIMEOUT 65535
#define SPI_MIN_DMA 48
struct nanohub_spi_data {
struct nanohub_data data;
struct spi_device *device;
struct semaphore spi_sem;
int cs;
uint16_t rx_length;
uint16_t rx_offset;
};
static uint8_t bl_checksum(const uint8_t *bytes, int length)
{
int i;
uint8_t csum;
if (length == 1) {
csum = ~bytes[0];
} else if (length > 1) {
for (csum = 0, i = 0; i < length; i++)
csum ^= bytes[i];
} else {
csum = 0xFF;
}
return csum;
}
static uint8_t spi_bl_write_data(const void *data, uint8_t *tx, int length)
{
const struct nanohub_spi_data *spi_data = data;
const struct nanohub_bl *bl = &spi_data->data.bl;
struct spi_message msg;
struct spi_transfer xfer = {
.len = length + 1,
.tx_buf = bl->tx_buffer,
.rx_buf = bl->rx_buffer,
.cs_change = 1,
};
tx[length] = bl_checksum(tx, length);
memcpy(bl->tx_buffer, tx, length + 1);
spi_message_init_with_transfers(&msg, &xfer, 1);
if (spi_sync_locked(spi_data->device, &msg) == 0)
return bl->rx_buffer[length];
else
return CMD_NACK;
}
static uint8_t spi_bl_write_cmd(const void *data, uint8_t cmd)
{
const struct nanohub_spi_data *spi_data = data;
const struct nanohub_bl *bl = &spi_data->data.bl;
struct spi_message msg;
struct spi_transfer xfer = {
.len = 3,
.tx_buf = bl->tx_buffer,
.rx_buf = bl->rx_buffer,
.cs_change = 1,
};
bl->tx_buffer[0] = CMD_SOF;
bl->tx_buffer[1] = cmd;
bl->tx_buffer[2] = ~cmd;
spi_message_init_with_transfers(&msg, &xfer, 1);
if (spi_sync_locked(spi_data->device, &msg) == 0)
return CMD_ACK;
else
return CMD_NACK;
}
static uint8_t spi_bl_read_data(const void *data, uint8_t *rx, int length)
{
const struct nanohub_spi_data *spi_data = data;
const struct nanohub_bl *bl = &spi_data->data.bl;
struct spi_message msg;
struct spi_transfer xfer = {
.len = length + 1,
.tx_buf = bl->tx_buffer,
.rx_buf = bl->rx_buffer,
.cs_change = 1,
};
memset(&bl->tx_buffer[0], 0x00, length + 1);
spi_message_init_with_transfers(&msg, &xfer, 1);
if (spi_sync_locked(spi_data->device, &msg) == 0) {
memcpy(rx, &bl->rx_buffer[1], length);
return CMD_ACK;
} else {
return CMD_NACK;
}
}
static uint8_t spi_bl_read_ack(const void *data)
{
const struct nanohub_spi_data *spi_data = data;
const struct nanohub_bl *bl = &spi_data->data.bl;
int32_t timeout = SPI_TIMEOUT;
uint8_t ret;
struct spi_message msg;
struct spi_transfer xfer = {
.len = 1,
.tx_buf = bl->tx_buffer,
.rx_buf = bl->rx_buffer,
.cs_change = 1,
};
bl->tx_buffer[0] = 0x00;
spi_message_init_with_transfers(&msg, &xfer, 1);
if (spi_sync_locked(spi_data->device, &msg) == 0) {
do {
spi_sync_locked(spi_data->device, &msg);
timeout--;
if (bl->rx_buffer[0] != CMD_ACK
&& bl->rx_buffer[0] != CMD_NACK
&& timeout % 256 == 0)
schedule();
} while (bl->rx_buffer[0] != CMD_ACK
&& bl->rx_buffer[0] != CMD_NACK && timeout > 0);
if (bl->rx_buffer[0] != CMD_ACK && bl->rx_buffer[0] != CMD_NACK
&& timeout == 0)
ret = CMD_NACK;
else
ret = bl->rx_buffer[0];
bl->tx_buffer[0] = CMD_ACK;
spi_sync_locked(spi_data->device, &msg);
return ret;
} else {
return CMD_NACK;
}
}
static int spi_bl_open(const void *data)
{
const struct nanohub_spi_data *spi_data = data;
int ret;
spi_bus_lock(spi_data->device->master);
spi_data->device->max_speed_hz = 1000000;
spi_data->device->mode = SPI_MODE_0;
spi_data->device->bits_per_word = 8;
ret = spi_setup(spi_data->device);
if (!ret)
gpio_set_value(spi_data->cs, 0);
return ret;
}
static void spi_bl_close(const void *data)
{
const struct nanohub_spi_data *spi_data = data;
gpio_set_value(spi_data->cs, 1);
spi_bus_unlock(spi_data->device->master);
}
static uint8_t spi_bl_sync(const void *data)
{
const struct nanohub_spi_data *spi_data = data;
const struct nanohub_bl *bl = &spi_data->data.bl;
int32_t timeout = SPI_TIMEOUT;
struct spi_message msg;
struct spi_transfer xfer = {
.len = 1,
.tx_buf = bl->tx_buffer,
.rx_buf = bl->rx_buffer,
.cs_change = 1,
};
bl->tx_buffer[0] = CMD_SOF;
spi_message_init_with_transfers(&msg, &xfer, 1);
do {
if (spi_sync_locked(spi_data->device, &msg) != 0)
return CMD_NACK;
timeout--;
if (bl->rx_buffer[0] != CMD_SOF_ACK && timeout % 256 == 0)
schedule();
} while (bl->rx_buffer[0] != CMD_SOF_ACK && timeout > 0);
if (bl->rx_buffer[0] == CMD_SOF_ACK)
return bl->read_ack(data);
else
return CMD_NACK;
}
void nanohub_spi_bl_init(struct nanohub_spi_data *spi_data)
{
struct nanohub_bl *bl = &spi_data->data.bl;
bl->open = spi_bl_open;
bl->sync = spi_bl_sync;
bl->write_data = spi_bl_write_data;
bl->write_cmd = spi_bl_write_cmd;
bl->read_data = spi_bl_read_data;
bl->read_ack = spi_bl_read_ack;
bl->close = spi_bl_close;
}
int nanohub_spi_write(void *data, uint8_t *tx, int length, int timeout)
{
struct nanohub_spi_data *spi_data = data;
const struct nanohub_comms *comms = &spi_data->data.comms;
int max_len = sizeof(struct nanohub_packet) + MAX_UINT8 +
sizeof(struct nanohub_packet_crc);
struct spi_message msg;
struct spi_transfer xfer = {
.len = max_len + timeout,
.tx_buf = comms->tx_buffer,
.rx_buf = comms->rx_buffer,
.cs_change = 1,
};
spi_data->rx_offset = max_len;
spi_data->rx_length = max_len + timeout;
memcpy(comms->tx_buffer, tx, length);
memset(comms->tx_buffer + length, 0xFF, max_len + timeout - length);
spi_message_init_with_transfers(&msg, &xfer, 1);
if (spi_sync_locked(spi_data->device, &msg) == 0)
return length;
else
return ERROR_NACK;
}
int nanohub_spi_read(void *data, uint8_t *rx, int max_length, int timeout)
{
struct nanohub_spi_data *spi_data = data;
struct nanohub_comms *comms = &spi_data->data.comms;
const int min_size = sizeof(struct nanohub_packet) +
sizeof(struct nanohub_packet_crc);
int i, ret;
int offset = 0;
struct nanohub_packet *packet = NULL;
struct spi_message msg;
struct spi_transfer xfer = {
.len = timeout,
.tx_buf = comms->tx_buffer,
.rx_buf = comms->rx_buffer,
.cs_change = 1,
};
if (max_length < min_size)
return ERROR_NACK;
/* consume leftover bytes, if any */
if (spi_data->rx_offset < spi_data->rx_length) {
for (i = spi_data->rx_offset; i < spi_data->rx_length; i++) {
if (comms->rx_buffer[i] != 0xFF) {
offset = spi_data->rx_length - i;
if (offset <
offsetof(struct nanohub_packet,
len) + sizeof(packet->len)) {
memcpy(rx, &comms->rx_buffer[i],
offset);
xfer.len =
min_size + MAX_UINT8 - offset;
break;
} else {
packet =
(struct nanohub_packet *)&comms->
rx_buffer[i];
if (offset < min_size + packet->len) {
memcpy(rx, packet, offset);
xfer.len =
min_size + packet->len -
offset;
break;
} else {
memcpy(rx, packet,
min_size + packet->len);
spi_data->rx_offset = i +
min_size + packet->len;
return min_size + packet->len;
}
}
}
}
}
if (xfer.len != 1 && xfer.len < SPI_MIN_DMA)
xfer.len = SPI_MIN_DMA;
memset(comms->tx_buffer, 0xFF, xfer.len);
spi_message_init_with_transfers(&msg, &xfer, 1);
ret = spi_sync_locked(spi_data->device, &msg);
if (ret == 0) {
if (offset > 0) {
packet = (struct nanohub_packet *)rx;
if (offset + xfer.len > max_length)
memcpy(&rx[offset], comms->rx_buffer,
max_length - offset);
else
memcpy(&rx[offset], comms->rx_buffer, xfer.len);
spi_data->rx_length = xfer.len;
spi_data->rx_offset = min_size + packet->len - offset;
} else {
for (i = 0; i < xfer.len; i++) {
if (comms->rx_buffer[i] != 0xFF) {
spi_data->rx_length = xfer.len;
if (xfer.len - i < min_size) {
spi_data->rx_offset = i;
break;
} else {
packet =
(struct nanohub_packet *)
&comms->rx_buffer[i];
if (xfer.len - i <
min_size + packet->len) {
packet = NULL;
spi_data->rx_offset = i;
} else {
memcpy(rx, packet,
min_size +
packet->len);
spi_data->rx_offset =
i + min_size +
packet->len;
}
}
break;
}
}
}
}
if (ret < 0)
return ret;
else if (!packet)
return 0;
else
return min_size + packet->len;
}
static int nanohub_spi_open(void *data)
{
struct nanohub_spi_data *spi_data = data;
int ret;
down(&spi_data->spi_sem);
spi_bus_lock(spi_data->device->master);
spi_data->device->max_speed_hz = 10000000;
spi_data->device->mode = SPI_MODE_0;
spi_data->device->bits_per_word = 8;
ret = spi_setup(spi_data->device);
if (!ret) {
udelay(40);
gpio_set_value(spi_data->cs, 0);
udelay(30);
}
return ret;
}
static void nanohub_spi_close(void *data)
{
struct nanohub_spi_data *spi_data = data;
gpio_set_value(spi_data->cs, 1);
spi_bus_unlock(spi_data->device->master);
up(&spi_data->spi_sem);
udelay(60);
}
void nanohub_spi_comms_init(struct nanohub_spi_data *spi_data)
{
struct nanohub_comms *comms = &spi_data->data.comms;
int max_len = sizeof(struct nanohub_packet) + MAX_UINT8 +
sizeof(struct nanohub_packet_crc);
comms->seq = 1;
comms->timeout_write = 544;
comms->timeout_ack = 272;
comms->timeout_reply = 512;
comms->open = nanohub_spi_open;
comms->close = nanohub_spi_close;
comms->write = nanohub_spi_write;
comms->read = nanohub_spi_read;
max_len += comms->timeout_write;
max_len = max(max_len, comms->timeout_ack);
max_len = max(max_len, comms->timeout_reply);
comms->tx_buffer = kmalloc(max_len, GFP_KERNEL | GFP_DMA);
comms->rx_buffer = kmalloc(max_len, GFP_KERNEL | GFP_DMA);
spi_data->rx_length = 0;
spi_data->rx_offset = 0;
sema_init(&spi_data->spi_sem, 1);
}
static int nanohub_spi_probe(struct spi_device *spi)
{
struct nanohub_spi_data *spi_data;
struct iio_dev *iio_dev;
int error;
iio_dev = iio_device_alloc(sizeof(struct nanohub_spi_data));
iio_dev = nanohub_probe(&spi->dev, iio_dev);
if (IS_ERR(iio_dev))
return PTR_ERR(iio_dev);
spi_data = iio_priv(iio_dev);
spi_set_drvdata(spi, iio_dev);
if (gpio_is_valid(spi_data->data.pdata->spi_cs_gpio)) {
error =
gpio_request(spi_data->data.pdata->spi_cs_gpio,
"nanohub_spi_cs");
if (error) {
pr_err("nanohub: spi_cs_gpio request failed\n");
} else {
spi_data->cs = spi_data->data.pdata->spi_cs_gpio;
gpio_direction_output(spi_data->cs, 1);
}
} else {
pr_err("nanohub: spi_cs_gpio is not valid\n");
}
spi_data->device = spi;
nanohub_spi_comms_init(spi_data);
spi_data->data.bl.cmd_erase = CMD_ERASE;
spi_data->data.bl.cmd_read_memory = CMD_READ_MEMORY;
spi_data->data.bl.cmd_write_memory = CMD_WRITE_MEMORY;
spi_data->data.bl.cmd_get_version = CMD_GET_VERSION;
spi_data->data.bl.cmd_get_id = CMD_GET_ID;
spi_data->data.bl.cmd_readout_protect = CMD_READOUT_PROTECT;
spi_data->data.bl.cmd_readout_unprotect = CMD_READOUT_UNPROTECT;
spi_data->data.bl.cmd_update_finished = CMD_UPDATE_FINISHED;
nanohub_spi_bl_init(spi_data);
nanohub_reset(&spi_data->data);
return 0;
}
static int nanohub_spi_remove(struct spi_device *spi)
{
struct nanohub_spi_data *spi_data;
struct iio_dev *iio_dev;
iio_dev = spi_get_drvdata(spi);
spi_data = iio_priv(iio_dev);
if (gpio_is_valid(spi_data->cs)) {
gpio_direction_output(spi_data->cs, 1);
gpio_free(spi_data->cs);
}
return nanohub_remove(iio_dev);
}
static int nanohub_spi_suspend(struct device *dev)
{
struct iio_dev *iio_dev = spi_get_drvdata(to_spi_device(dev));
struct nanohub_spi_data *spi_data = iio_priv(iio_dev);
int ret;
ret = nanohub_suspend(iio_dev);
if (!ret) {
ret = down_interruptible(&spi_data->spi_sem);
if (ret)
up(&spi_data->spi_sem);
}
return ret;
}
static int nanohub_spi_resume(struct device *dev)
{
struct iio_dev *iio_dev = spi_get_drvdata(to_spi_device(dev));
struct nanohub_spi_data *spi_data = iio_priv(iio_dev);
up(&spi_data->spi_sem);
return nanohub_resume(iio_dev);
}
static struct spi_device_id nanohub_spi_id[] = {
{NANOHUB_NAME, 0},
{},
};
static const struct dev_pm_ops nanohub_spi_pm_ops = {
.suspend = nanohub_spi_suspend,
.resume = nanohub_spi_resume,
};
static struct spi_driver nanohub_spi_driver = {
.driver = {
.name = NANOHUB_NAME,
.owner = THIS_MODULE,
.pm = &nanohub_spi_pm_ops,
},
.probe = nanohub_spi_probe,
.remove = nanohub_spi_remove,
.id_table = nanohub_spi_id,
};
int __init nanohub_spi_init(void)
{
return spi_register_driver(&nanohub_spi_driver);
}
void nanohub_spi_cleanup(void)
{
spi_unregister_driver(&nanohub_spi_driver);
}
MODULE_DEVICE_TABLE(spi, nanohub_spi_id);
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _NANOHUB_SPI_H
#define _NANOHUB_SPI_H
int __init nanohub_spi_init(void);
void nanohub_spi_cleanup(void);
#endif
+26
View File
@@ -0,0 +1,26 @@
#ifndef __LINUX_PLATFORM_DATA_NANOHUB_H
#define __LINUX_PLATFORM_DATA_NANOHUB_H
#include <linux/types.h>
struct nanohub_flash_bank {
int bank;
u32 address;
size_t length;
};
struct nanohub_platform_data {
u32 wakeup_gpio;
u32 nreset_gpio;
u32 boot0_gpio;
u32 irq1_gpio;
u32 irq2_gpio;
u32 spi_cs_gpio;
u32 bl_addr;
u32 num_flash_banks;
struct nanohub_flash_bank *flash_banks;
u32 num_shared_flash_banks;
struct nanohub_flash_bank *shared_flash_banks;
};
#endif /* __LINUX_PLATFORM_DATA_NANOHUB_H */