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:
committed by
Dmitry Shmidt
parent
8853995868
commit
33615a41e4
@@ -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>;
|
||||
@@ -106,4 +106,6 @@ source "drivers/staging/greybus/Kconfig"
|
||||
|
||||
source "drivers/staging/vc04_services/Kconfig"
|
||||
|
||||
source "drivers/staging/nanohub/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user