summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Rawling <mwr@google.com>2022-03-25 11:47:20 -0700
committerAndrew Evans <andrewevans@google.com>2022-04-19 10:16:03 -0700
commit4936dfbddc45391716f119fff5aba55089e65a4d (patch)
tree91958ce27b3fc53e6b2bdaec95a16be32e861da1
parentc5c7e44b5aeeda5ea1a1560d01c37c3f72d1606f (diff)
downloadnanohub-4936dfbddc45391716f119fff5aba55089e65a4d.tar.gz
MCU fast boot via NXP bootloader over SPI
The Rohan MCU currently takes 30s to boot over UART. This is unacceptable because: - it adds 30s to the visible boot, ie, until the watch face is visible - MCU restarts are so slow that sensor behaviour is impacted We cannot use blhost over SPI because it requires either SPIDEV, or a custom interface, precise timing control, and workarounds for bootloader bugs. None of these things are viable in Rohan. This CL adds support for a subset of the NXP bootloader commands, enough to enable booting the MCU over SPI. Other commands can be easily added on top of the basic interface. - MCU boot time is improved from 30s to 2s - bootloader sync and timing bugs are handled - bootloader data aborts are detected and retried - downloads run at elevated priority for speed and stability - signed firmware is supported See go/rh-mcu-boot-improvements Bug: 206044802 Change-Id: I79b9d9d7ab670e1e758824dfaf212b46e41120c2
-rw-r--r--Kbuild3
-rw-r--r--Makefile2
-rw-r--r--bl_nxp.c200
-rw-r--r--bl_nxp.h97
-rw-r--r--bl_st.c (renamed from bl.c)2
-rw-r--r--bl_st.h (renamed from bl.h)0
-rw-r--r--main.c152
-rw-r--r--main.h32
-rw-r--r--nanohub.h2
-rw-r--r--spi.c335
10 files changed, 784 insertions, 41 deletions
diff --git a/Kbuild b/Kbuild
index f048e5d..be92528 100644
--- a/Kbuild
+++ b/Kbuild
@@ -5,4 +5,5 @@
obj-$(CONFIG_NANOHUB) += nanohub.o
nanohub-y := main.o comms.o
nanohub-$(CONFIG_NANOHUB_SPI) += spi.o
-nanohub-$(CONFIG_NANOHUB_BL) += bl.o
+nanohub-$(CONFIG_NANOHUB_BL_NXP) += bl_nxp.o
+nanohub-$(CONFIG_NANOHUB_BL_ST) += bl_st.o
diff --git a/Makefile b/Makefile
index cc7d517..a35b455 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,12 @@
default: all
KBUILD_OPTIONS := CONFIG_NANOHUB=m
+KBUILD_OPTIONS += CONFIG_NANOHUB_BL_NXP=y
KBUILD_OPTIONS += CONFIG_NANOHUB_SPI=y
# Needed flags that aren't parsed in Kbuild
KBUILD_FLAGS := -DCONFIG_NANOHUB
+KBUILD_FLAGS += -DCONFIG_NANOHUB_BL_NXP
KBUILD_FLAGS += -DCONFIG_NANOHUB_SPI
all:
diff --git a/bl_nxp.c b/bl_nxp.c
new file mode 100644
index 0000000..b6433d2
--- /dev/null
+++ b/bl_nxp.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 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/vmalloc.h>
+
+#include "nanohub.h"
+#include "main.h"
+#include "bl_nxp.h"
+
+#define MAX_BUFFER_SIZE 1024
+
+int nanohub_bl_open(struct nanohub_data *data)
+{
+ int ret;
+
+ data->bl.tx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!data->bl.tx_buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ data->bl.rx_buffer = kmalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!data->bl.rx_buffer) {
+ ret = -ENOMEM;
+ goto free_tx;
+ }
+
+ ret = data->bl.open(data);
+ if (ret == 0)
+ return ret;
+
+ 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 int nanohub_bl_read_generic_response(struct nanohub_data *data,
+ uint8_t expected_command_tag)
+{
+ struct nanohub_bl *bl = &data->bl;
+ int ret, status;
+
+ ret = bl->read_response(data);
+ if (ret < 0)
+ return ret;
+ if (bl->rx_buffer[1] != BL_FRAME_COMMAND ||
+ bl->rx_buffer[6] != BL_RESPONSE_GENERIC ||
+ bl->rx_buffer[14] != expected_command_tag)
+ return -BL_ERR_BAD_RESPONSE;
+
+ status = get_le32(bl->rx_buffer + 10);
+ return -status;
+}
+
+int nanohub_bl_set_property(struct nanohub_data *data, uint32_t tag,
+ uint32_t value)
+{
+ int ret = data->bl.write_cmd(data, BL_COMMAND_SET_PROPERTY, 0, 2, tag,
+ value);
+ if (ret < 0)
+ return ret;
+ return nanohub_bl_read_generic_response(data, BL_COMMAND_SET_PROPERTY);
+}
+
+int nanohub_bl_write_memory(struct nanohub_data *data, const uint8_t *buffer,
+ uint32_t size, uint32_t address,
+ uint32_t *data_abort_offset)
+{
+ int sched = 0, chunk = BL_DATA_CHUNK_SIZE, offset;
+ int ret;
+
+ // command
+ ret = data->bl.write_cmd(data, BL_COMMAND_WRITE_MEMORY,
+ BL_FLAG_HAS_DATA, 3, address, size, 0);
+ if (ret < 0)
+ return ret;
+
+ // initial response
+ ret = nanohub_bl_read_generic_response(data, BL_COMMAND_WRITE_MEMORY);
+ if (ret < 0)
+ return ret;
+
+ // data phase
+ for (offset = 0; offset < size; offset += BL_DATA_CHUNK_SIZE) {
+ if (size - offset < BL_DATA_CHUNK_SIZE)
+ chunk = size - offset;
+ ret = data->bl.write_data(data, buffer + offset, chunk);
+ if (ret == -BL_FRAME_ABORT) {
+ *data_abort_offset = offset;
+ break;
+ }
+ if (ret < 0)
+ return ret;
+ if (++sched % 20 == 0)
+ schedule(); // ~10-20ms
+ }
+
+ // final response
+ return nanohub_bl_read_generic_response(data, BL_COMMAND_WRITE_MEMORY);
+}
+
+static int nanohub_bl_write_memory_retry(struct nanohub_data *data,
+ const uint8_t *buffer, uint32_t size,
+ uint32_t address, int max_attempts)
+{
+ int attempt, offset = 0, data_abort_offset = 0;
+ int ret;
+ for (attempt = 0; attempt < max_attempts; attempt++) {
+ ret = nanohub_bl_write_memory(data, buffer + offset,
+ size - offset, address + offset,
+ &data_abort_offset);
+ if (!data_abort_offset)
+ break;
+ offset += data_abort_offset;
+ data_abort_offset = 0;
+ pr_warn("nanohub: %s data abort offset=%d\n", __func__, offset);
+ }
+ return ret;
+}
+
+int nanohub_bl_execute(struct nanohub_data *data, uint32_t prgm_counter,
+ uint32_t ram_addr, uint32_t stack_ptr)
+{
+ int ret = data->bl.write_cmd(data, BL_COMMAND_EXECUTE, 0, 3,
+ prgm_counter, ram_addr, stack_ptr);
+ if (ret < 0)
+ return ret;
+ return nanohub_bl_read_generic_response(data, BL_COMMAND_EXECUTE);
+}
+
+int nanohub_bl_download_firmware(struct nanohub_data *data,
+ const uint8_t *buffer, uint32_t size)
+{
+ const uint32_t kSpOffset = 0;
+ const uint32_t kPcOffset = 1;
+ const uint32_t kLoadOffset = 13;
+ const uint32_t kSignedOffset = 0x1000;
+ const uint32_t kMagicOffset = 0x400;
+ const uint8_t kMagic[] = { 'C', 'H', 'R', 'E' };
+ uint32_t *firmware;
+ uint32_t ram_addr, stack_ptr, prgm_counter;
+ int ret = 0;
+
+ // Handle signed firmware
+ if (size > kSignedOffset &&
+ memcmp(buffer + kMagicOffset, kMagic, sizeof(kMagic)) == 0) {
+ buffer += kSignedOffset;
+ size -= kSignedOffset;
+ }
+
+ if (size < 64) {
+ pr_err("nanohub: %s invalid size\n", __func__);
+ return -ETOOSMALL;
+ }
+
+ firmware = (uint32_t *)buffer;
+ ram_addr = firmware[kLoadOffset];
+ stack_ptr = firmware[kSpOffset];
+ prgm_counter = firmware[kPcOffset];
+
+ // Enable GPIO mode using pin 4,29, shared with mcu_ap_wake_int.
+ ret = nanohub_bl_set_property(data, 0x1c, 0x8000041d);
+ if (ret < 0) {
+ pr_err("nanohub: %s set_property ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = nanohub_bl_write_memory_retry(data, buffer, size, ram_addr, 10);
+ if (ret < 0) {
+ pr_err("nanohub: %s write_memory ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = nanohub_bl_execute(data, prgm_counter, ram_addr, stack_ptr);
+ if (ret < 0) {
+ pr_err("nanohub: %s execute ret=%d\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
diff --git a/bl_nxp.h b/bl_nxp.h
new file mode 100644
index 0000000..35b733c
--- /dev/null
+++ b/bl_nxp.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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_NXP_H
+#define _NANOHUB_BL_NXP_H
+
+#include "nanohub.h"
+
+struct nanohub_data;
+
+struct nanohub_bl {
+ int (*open)(const void *);
+ void (*close)(const void *);
+ int (*read_ack)(const void *);
+ int (*read_response)(const void *);
+ int (*write_ack)(const void *);
+ int (*write_cmd)(const void *, uint8_t cmd, uint8_t flags,
+ int n_args, ...);
+ int (*write_data)(const void *, const uint8_t *buf, int size);
+
+ uint8_t *tx_buffer;
+ uint8_t *rx_buffer;
+};
+
+int nanohub_bl_open(struct nanohub_data *);
+void nanohub_bl_close(struct nanohub_data *);
+
+int nanohub_bl_set_property(struct nanohub_data *data, uint32_t tag,
+ uint32_t value);
+int nanohub_bl_write_memory(struct nanohub_data *data, const uint8_t *firmware,
+ uint32_t size, uint32_t address,
+ uint32_t *data_abort_offset);
+int nanohub_bl_execute(struct nanohub_data *data, uint32_t prgm_counter,
+ uint32_t ram_addr, uint32_t stack_ptr);
+
+int nanohub_bl_download_firmware(struct nanohub_data *, const uint8_t *firmware,
+ uint32_t size);
+
+#define BL_COMMAND_WRITE_MEMORY 0x04
+#define BL_COMMAND_EXECUTE 0x09
+#define BL_COMMAND_SET_PROPERTY 0x0C
+
+#define BL_RESPONSE_GENERIC 0xA0
+
+#define BL_FLAG_HAS_DATA 0x01
+
+#define BL_FRAME_SYNC 0x5A
+#define BL_FRAME_ACK 0xA1
+#define BL_FRAME_NACK 0xA2
+#define BL_FRAME_ABORT 0xA3
+#define BL_FRAME_COMMAND 0xA4
+#define BL_FRAME_DATA 0xA5
+#define BL_FRAME_PING 0xA6
+#define BL_FRAME_PING_RESPONSE 0xA7
+
+// XXX find system errors for these, will still clash with nxp status though
+// so maybe these should be in a unique scope?
+#define BL_ERR_TIMEOUT 52
+#define BL_ERR_CRC 53
+#define BL_ERR_SYNC 54
+#define BL_ERR_BAD_RESPONSE 55
+
+// Optimized for burst transfers (total packet multiple of 4)
+#define BL_DATA_CHUNK_SIZE 510
+
+static inline void put_le16(uint8_t *buf, uint16_t i) {
+ buf[0] = i & 0xff;
+ buf[1] = (i >> 8) & 0xff;
+}
+
+static inline void put_le32(uint8_t *buf, uint32_t i) {
+ buf[0] = i & 0xff;
+ buf[1] = (i >> 8) & 0xff;
+ buf[2] = (i >> 16) & 0xff;
+ buf[3] = (i >> 24) & 0xff;
+}
+
+static inline uint16_t get_le16(uint8_t *buf) {
+ return buf[0] | buf[1] << 8;
+}
+
+static inline uint32_t get_le32(uint8_t *buf) {
+ return buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
+}
+
+#endif
diff --git a/bl.c b/bl_st.c
index 255eb75..9ab3451 100644
--- a/bl.c
+++ b/bl_st.c
@@ -16,7 +16,7 @@
#include "nanohub.h"
#include "main.h"
-#include "bl.h"
+#include "bl_st.h"
#define MAX_BUFFER_SIZE 1024
#define MAX_FLASH_BANKS 16
diff --git a/bl.h b/bl_st.h
index e6a1e42..e6a1e42 100644
--- a/bl.h
+++ b/bl_st.h
diff --git a/main.c b/main.c
index 0d30387..55a6b5c 100644
--- a/main.c
+++ b/main.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/firmware.h>
@@ -41,9 +42,16 @@
#include "nanohub_exports.h"
#include "main.h"
#include "comms.h"
-#include "bl.h"
#include "spi.h"
+#ifdef CONFIG_NANOHUB_BL_ST
+#include "bl_st.h"
+#endif
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+#include "bl_nxp.h"
+#endif
+
#define READ_QUEUE_DEPTH 20
#define APP_FROM_HOST_EVENTID 0x000000F8
#define FIRST_SENSOR_EVENTID 0x00000200
@@ -722,7 +730,7 @@ static inline int nanohub_wakeup_lock(struct nanohub_data *data, int mode)
return ret;
}
-#ifdef CONFIG_NANOHUB_BL
+#if defined(CONFIG_NANOHUB_BL_ST) || defined(CONFIG_NANOHUB_BL_NXP)
if (mode == LOCK_MODE_IO || mode == LOCK_MODE_IO_BL)
ret = nanohub_bl_open(data);
if (ret < 0) {
@@ -747,7 +755,7 @@ static inline int nanohub_wakeup_unlock(struct nanohub_data *data)
atomic_set(&data->lock_mode, LOCK_MODE_NONE);
if (mode != LOCK_MODE_SUSPEND_RESUME)
enable_irq(data->irq1);
-#ifdef CONFIG_NANOHUB_BL
+#if defined(CONFIG_NANOHUB_BL_ST) || defined(CONFIG_NANOHUB_BL_NXP)
if (mode == LOCK_MODE_IO || mode == LOCK_MODE_IO_BL)
nanohub_bl_close(data);
#endif
@@ -763,16 +771,11 @@ static inline int nanohub_wakeup_unlock(struct nanohub_data *data)
static void __nanohub_hw_reset(struct nanohub_data *data, int boot0)
{
- //const struct nanohub_platform_data *pdata = data->pdata;
-
- //gpio_set_value(pdata->nreset_gpio, 0);
- //gpio_set_value(pdata->boot0_gpio, boot0 > 0);
- //usleep_range(30, 40);
- //gpio_set_value(pdata->nreset_gpio, 1);
- //if (boot0 > 0)
- // usleep_range(70000, 75000);
- //else if (!boot0)
- // usleep_range(750000, 800000);
+ const struct nanohub_platform_data *pdata = data->pdata;
+ gpio_set_value(pdata->nreset_gpio, 0);
+ usleep_range(1000, 2000);
+ gpio_set_value(pdata->nreset_gpio, 1);
+ usleep_range(10000, 20000);
nanohub_clear_err_cnt(data);
}
@@ -801,7 +804,7 @@ static ssize_t nanohub_try_hw_reset(struct device *dev,
return ret < 0 ? ret : count;
}
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
static ssize_t nanohub_erase_shared(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -906,7 +909,7 @@ static ssize_t nanohub_download_kernel(struct device *dev,
}
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
static ssize_t nanohub_download_kernel_bl(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -952,6 +955,92 @@ static ssize_t nanohub_download_kernel_bl(struct device *dev,
}
#endif
+#ifdef CONFIG_NANOHUB_BL_NXP
+
+static ssize_t nanohub_download_firmware_request(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nanohub_data *data = dev_get_drvdata(dev);
+ struct firmware_request *fw_request = &data->firmware_request;
+ char *fw_name = fw_request->fw_name;
+ int ret;
+
+ if (atomic_cmpxchg(&fw_request->state, FW_IDLE, FW_PENDING) !=
+ FW_IDLE) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (count == 0 || count >= FW_NAME_SIZE) {
+ ret = -ENAMETOOLONG;
+ goto out;
+ }
+
+ // Handle \n or unterminated name.
+ memcpy(fw_name, buf, count);
+ if (fw_name[count - 1] == '\n')
+ fw_name[count - 1] = 0;
+ fw_name[count] = 0;
+
+ atomic_set(&fw_request->state, FW_REQUESTED);
+ nanohub_notify_thread(data);
+ wait_for_completion(&fw_request->fw_complete);
+ ret = fw_request->result;
+
+out:
+ if (ret < 0)
+ pr_err("nanohub: firmware request failed: %s err=%d\n", fw_name,
+ ret);
+
+ atomic_set(&fw_request->state, FW_IDLE);
+ return ret < 0 ? ret : count;
+}
+
+static void nanohub_download_firmware(struct device *dev)
+{
+ struct nanohub_data *data = dev_get_drvdata(dev);
+ struct firmware_request *fw_request = &data->firmware_request;
+ const struct firmware *fw_entry;
+ int ret;
+
+ pr_info("nanohub: firmware download: %s\n", fw_request->fw_name);
+
+ atomic_set(&fw_request->state, FW_IN_PROGRESS);
+ ret = request_firmware(&fw_entry, fw_request->fw_name, dev);
+ if (ret < 0)
+ goto out;
+
+ ret = nanohub_wakeup_lock(data, LOCK_MODE_IO_BL);
+ if (ret < 0)
+ goto release_firmware;
+
+ // Disable irq1 as its GPIO is used by the bootloader.
+ if (data->irq1)
+ disable_irq(data->irq1);
+
+ __nanohub_hw_reset(data, -1);
+
+ ret = nanohub_bl_download_firmware(data, fw_entry->data,
+ fw_entry->size);
+
+ mcu_wakeup_gpio_set_value(data, 1);
+ if (data->irq1)
+ enable_irq(data->irq1);
+ nanohub_wakeup_unlock(data);
+
+release_firmware:
+ release_firmware(fw_entry);
+out:
+ pr_info("nanohub: firmware download complete: %s err=%d\n",
+ fw_request->fw_name, ret);
+ fw_request->result = ret;
+ atomic_set(&fw_request->state, FW_COMPLETE);
+ complete(&fw_request->fw_complete);
+}
+
+#endif
+
static ssize_t nanohub_download_app(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1040,7 +1129,7 @@ static ssize_t nanohub_download_app(struct device *dev,
return count;
}
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
static ssize_t nanohub_lock_bl(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1119,25 +1208,28 @@ static struct device_attribute attributes[] = {
__ATTR(firmware_version, 0440, nanohub_firmware_query, NULL),
__ATTR(time_sync, 0440, nanohub_time_sync, NULL),
__ATTR(wake_lock, 0220, NULL, nanohub_mcu_wake_lock),
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
__ATTR(download_bl, 0220, NULL, nanohub_download_bl),
#endif
__ATTR(download_kernel, 0220, NULL, nanohub_download_kernel),
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
__ATTR(download_kernel_bl, 0220, NULL, nanohub_download_kernel_bl),
#endif
__ATTR(download_app, 0220, NULL, nanohub_download_app),
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
__ATTR(erase_shared, 0220, NULL, nanohub_erase_shared),
__ATTR(erase_shared_bl, 0220, NULL, nanohub_erase_shared_bl),
#endif
__ATTR(hw_reset, 0220, NULL, nanohub_try_hw_reset),
__ATTR(nreset, 0660, nanohub_pin_nreset_get, nanohub_pin_nreset_set),
__ATTR(boot0, 0660, nanohub_pin_boot0_get, nanohub_pin_boot0_set),
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
__ATTR(lock, 0220, NULL, nanohub_lock_bl),
__ATTR(unlock, 0220, NULL, nanohub_unlock_bl),
#endif
+#ifdef CONFIG_NANOHUB_BL_NXP
+ __ATTR(download_firmware, 0220, NULL, nanohub_download_firmware_request),
+#endif
__ATTR(wakeup_event_msec, 0660, nanohub_wakeup_event_msec_get, nanohub_wakeup_event_msec_set),
};
@@ -1508,6 +1600,15 @@ static int nanohub_kthread(void *arg)
break;
}
atomic_set(&data->kthread_run, 0);
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+ if (atomic_read(&data->firmware_request.state) == FW_REQUESTED) {
+ nanohub_download_firmware(dev);
+ nanohub_set_state(data, ST_IDLE);
+ continue;
+ }
+#endif
+
if (!buf)
buf = nanohub_io_get_buf(&data->free_pool,
false);
@@ -1589,7 +1690,7 @@ static struct nanohub_platform_data *nanohub_parse_dt(struct device *dev)
{
struct nanohub_platform_data *pdata;
struct device_node *dt = dev->of_node;
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
const uint32_t *tmp;
struct property *prop;
uint32_t u, i;
@@ -1635,7 +1736,7 @@ static struct nanohub_platform_data *nanohub_parse_dt(struct device *dev)
/* optional (spi) */
pdata->spi_cs_gpio = of_get_named_gpio(dt, "sensorhub,spi-cs-gpio", 0);
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
/* optional (bl-max-frequency) */
pdata->bl_max_speed_hz = BL_MAX_SPEED_HZ;
ret = of_property_read_u32(dt, "sensorhub,bl-max-frequency", &u);
@@ -1714,7 +1815,7 @@ static struct nanohub_platform_data *nanohub_parse_dt(struct device *dev)
return pdata;
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
no_mem_shared:
devm_kfree(dev, pdata->flash_banks);
no_mem:
@@ -1907,6 +2008,11 @@ struct iio_dev *nanohub_probe(struct device *dev, struct iio_dev *iio_dev)
if (ret)
goto fail_dev;
+#ifdef CONFIG_NANOHUB_BL_NXP
+ atomic_set(&data->firmware_request.state, FW_IDLE);
+ init_completion(&data->firmware_request.fw_complete);
+#endif
+
data->thread = kthread_create(nanohub_kthread, data, "nanohub");
if (!IS_ERR(data->thread)) {
priv_nanohub_data = data;
diff --git a/main.h b/main.h
index 4e70a25..aef303e 100644
--- a/main.h
+++ b/main.h
@@ -23,7 +23,15 @@
#include <linux/semaphore.h>
#include "comms.h"
-#include "bl.h"
+
+#ifdef CONFIG_NANOHUB_BL_ST
+#include "bl_st.h"
+#endif
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+#include "bl_nxp.h"
+#endif
+
#define NANOHUB_NAME "nanohub"
@@ -44,6 +52,24 @@ struct nanohub_io {
uint8_t id;
};
+#ifdef CONFIG_NANOHUB_BL_NXP
+enum fw_state {
+ FW_IDLE,
+ FW_PENDING,
+ FW_REQUESTED,
+ FW_IN_PROGRESS,
+ FW_COMPLETE,
+};
+
+#define FW_NAME_SIZE 64
+struct firmware_request {
+ char fw_name[FW_NAME_SIZE];
+ atomic_t state;
+ int result;
+ struct completion fw_complete;
+};
+#endif
+
struct nanohub_data {
/* indices for io[] array */
#define ID_NANOHUB_COMMS 0
@@ -97,6 +123,10 @@ struct nanohub_data {
struct task_struct *thread;
int wakeup_event_msec;
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+ struct firmware_request firmware_request;
+#endif
};
enum {
diff --git a/nanohub.h b/nanohub.h
index 30628b0..ef29965 100644
--- a/nanohub.h
+++ b/nanohub.h
@@ -13,7 +13,7 @@ struct nanohub_platform_data {
u32 irq1_gpio;
u32 irq2_gpio;
u32 spi_cs_gpio;
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
u32 bl_max_speed_hz;
u32 bl_addr;
u32 num_flash_banks;
diff --git a/spi.c b/spi.c
index a1383aa..9e96500 100644
--- a/spi.c
+++ b/spi.c
@@ -19,9 +19,16 @@
#include <linux/module.h>
#include "main.h"
-#include "bl.h"
#include "comms.h"
+#ifdef CONFIG_NANOHUB_BL_ST
+#include "bl_st.h"
+#endif
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+#include "bl_nxp.h"
+#endif
+
#define SPI_TIMEOUT 65535
#define SPI_MIN_DMA 48
#define SPI_MAX_SPEED_HZ 10000000
@@ -38,7 +45,7 @@ struct nanohub_spi_data {
uint16_t rx_offset;
};
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
static uint8_t bl_checksum(const uint8_t *bytes, int length)
{
int i;
@@ -236,6 +243,294 @@ void nanohub_spi_bl_init(struct nanohub_spi_data *spi_data)
#endif
+
+#ifdef CONFIG_NANOHUB_BL_NXP
+
+#define CRC_POLY 0x1021
+
+static uint16_t crc_update(uint16_t crc_in, int incr)
+{
+ uint16_t xor = crc_in >> 15;
+ uint16_t out = crc_in << 1;
+ if (incr)
+ out++;
+ if (xor)
+ out ^= CRC_POLY;
+ return out;
+}
+
+// Calculate a packet CRC, ignoring the CRC bytes at offset 4 & 5.
+static uint16_t bl_crc16(const uint8_t *data, uint16_t size)
+{
+ uint16_t crc, i, j;
+ for (crc = 0, j = 0; j < size; j++, data++) {
+ if (j == 4 || j == 5)
+ continue; // skip the CRC word
+ for (i = 0x80; i; i >>= 1)
+ crc = crc_update(crc, *data & i);
+ }
+ for (i = 0; i < 16; i++)
+ crc = crc_update(crc, 0);
+ return crc;
+}
+
+enum bl_readiness {
+ BL_READY_READ = 0,
+ BL_READY_WRITE = 1,
+ BL_READY_NONE = 2,
+};
+
+static int spi_bl_wait(const void *data, enum bl_readiness bl_ready_state)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_platform_data *pdata = spi_data->data.pdata;
+ int retry, delay;
+
+ if (bl_ready_state == BL_READY_NONE)
+ return 0;
+ for (retry = 0, delay = 0; retry < 20; retry++) {
+ if (gpio_get_value(pdata->irq1_gpio) == bl_ready_state)
+ return 0;
+ delay += 10;
+ udelay(delay);
+ }
+ pr_warn("nanohub: %s timed out waiting for bootloader\n", __func__);
+ return -BL_ERR_TIMEOUT;
+}
+
+static int spi_bl_tx_rx(const void *data, struct spi_transfer *xfer,
+ enum bl_readiness bl_ready_state)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ struct spi_message msg;
+ int ret;
+
+ spi_message_init_with_transfers(&msg, xfer, 1);
+ ret = spi_bl_wait(data, bl_ready_state);
+ if (ret < 0)
+ return ret;
+ gpio_set_value(spi_data->cs, 0);
+ ret = spi_sync_locked(spi_data->device, &msg);
+ gpio_set_value(spi_data->cs, 1);
+ return ret;
+}
+
+static int spi_bl_write_ack(const void *data)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = bl->tx_buffer,
+ .rx_buf = NULL,
+ .cs_change = 1,
+ };
+
+ bl->tx_buffer[0] = BL_FRAME_SYNC;
+ bl->tx_buffer[1] = BL_FRAME_ACK;
+
+ return spi_bl_tx_rx(data, &xfer, BL_READY_WRITE);
+}
+
+// Read a packet beginning with a sync byte, eg, ACK and Frame packets.
+// Works around some bootloader issues that can cause random errors.
+static int spi_bl_read_sync_packet(const void *data, int length)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ struct spi_transfer xfer = {
+ .len = length,
+ .tx_buf = NULL,
+ .rx_buf = bl->rx_buffer,
+ .cs_change = 1,
+ };
+ int ret;
+
+ ret = spi_bl_tx_rx(data, &xfer, BL_READY_READ);
+ if (ret < 0)
+ return ret;
+
+ if (bl->rx_buffer[0] != BL_FRAME_SYNC)
+ return -BL_ERR_SYNC;
+
+ if (bl->rx_buffer[1] == BL_FRAME_SYNC) {
+ // The NXP bootloader can sometimes return an extra sync byte.
+ // Discard the extra sync and refetch the last content byte.
+ // This also removes the need to wait 50us b/w sync and ack.
+ // See ref manual 18.7.2.6.7
+ memmove(bl->rx_buffer, bl->rx_buffer + 1, length - 1);
+ xfer.len = 1;
+ xfer.rx_buf = bl->rx_buffer + length - 1;
+ ret = spi_bl_tx_rx(data, &xfer, BL_READY_NONE);
+ }
+
+ return ret;
+}
+
+static int spi_bl_read_ack(const void *data)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ uint8_t ack;
+ int ret = spi_bl_read_sync_packet(data, 2);
+ if (ret < 0)
+ return ret;
+ ack = bl->rx_buffer[1];
+ return ack == BL_FRAME_ACK ? 0 : -ack;
+}
+
+static int spi_bl_read_frame(const void *data)
+{
+ return spi_bl_read_sync_packet(data, 6);
+}
+
+// NXP bootloader command and response packet format
+// Frame
+// 0 sync 5A
+// 1 type A4
+// 2 length 2 bytes, length of cmd packet, little endian
+// 4 crc 2 bytes, crc of entire packet, not including crc, little endian
+// Command or Response
+// 6 cmd/resp 1 byte
+// 7 flags 1 byte
+// 8 reserved 00
+// 9 n_args 1 byte
+// 10 arg1 4 bytes, little endian
+// 14 ...
+static int spi_bl_write_cmd(const void *data, uint8_t cmd, uint8_t flags,
+ int n_args, ...)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ struct spi_transfer xfer = {
+ .len = 10 + 4 * n_args,
+ .tx_buf = bl->tx_buffer,
+ .rx_buf = NULL,
+ .cs_change = 1,
+ };
+ int i, ret;
+ uint16_t crc = 0;
+ va_list args;
+
+ bl->tx_buffer[0] = BL_FRAME_SYNC;
+ bl->tx_buffer[1] = BL_FRAME_COMMAND;
+ put_le16(bl->tx_buffer + 2, 4 + 4 * n_args);
+ bl->tx_buffer[6] = cmd;
+ bl->tx_buffer[7] = flags;
+ bl->tx_buffer[8] = 0;
+ bl->tx_buffer[9] = n_args;
+ va_start(args, n_args);
+ for (i = 0; i < n_args; i++) {
+ put_le32(bl->tx_buffer + 10 + i * 4, va_arg(args, uint32_t));
+ }
+ va_end(args);
+ crc = bl_crc16(bl->tx_buffer, 10 + 4 * n_args);
+ put_le16(bl->tx_buffer + 4, crc);
+
+ ret = spi_bl_tx_rx(data, &xfer, BL_READY_WRITE);
+ if (ret < 0)
+ return ret;
+
+ return spi_bl_read_ack(data);
+}
+
+static int spi_bl_write_data(const void *data, const uint8_t *buf, int length)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ struct spi_transfer xfer = {
+ .len = 6 + length,
+ .tx_buf = bl->tx_buffer,
+ .rx_buf = NULL,
+ .cs_change = 1,
+ };
+ uint16_t crc = 0;
+ int ret;
+
+ bl->tx_buffer[0] = BL_FRAME_SYNC;
+ bl->tx_buffer[1] = BL_FRAME_DATA;
+ put_le16(bl->tx_buffer + 2, length);
+ memcpy(bl->tx_buffer + 6, buf, length);
+ crc = bl_crc16(bl->tx_buffer, 6 + length);
+ put_le16(bl->tx_buffer + 4, crc);
+
+ ret = spi_bl_tx_rx(data, &xfer, BL_READY_WRITE);
+ if (ret < 0)
+ return ret;
+
+ return spi_bl_read_ack(data);
+}
+
+static int spi_bl_read_response(const void *data)
+{
+ const struct nanohub_spi_data *spi_data = data;
+ const struct nanohub_bl *bl = &spi_data->data.bl;
+ struct spi_transfer xfer = {
+ .tx_buf = NULL,
+ .cs_change = 1,
+ };
+ int ret;
+ uint16_t length, expected_crc, actual_crc;
+
+ // Read the frame
+ // This delay allows the MCU to begin a new data ready notification.
+ // It could be removed if we changed to an edge-driven interrupt.
+ udelay(60);
+ ret = spi_bl_read_frame(data);
+ if (ret < 0)
+ return ret;
+
+ length = get_le16(bl->rx_buffer + 2);
+ expected_crc = get_le16(bl->rx_buffer + 4);
+
+ if (length > 32)
+ return -BL_ERR_BAD_RESPONSE;
+
+ // Read the response
+ xfer.len = length;
+ xfer.rx_buf = bl->rx_buffer + 6;
+
+ ret = spi_bl_tx_rx(data, &xfer, BL_READY_NONE);
+ if (ret < 0)
+ return ret;
+
+ actual_crc = bl_crc16(bl->rx_buffer, 6 + length);
+ if (actual_crc != expected_crc)
+ return -BL_ERR_CRC;
+
+ return spi_bl_write_ack(data);
+}
+
+static int spi_bl_open(const void *data)
+{
+ const struct nanohub_spi_data *spi_data = data;
+
+ spi_bus_lock(spi_data->device->master);
+ return 0;
+}
+
+static void spi_bl_close(const void *data)
+{
+ const struct nanohub_spi_data *spi_data = data;
+
+ spi_bus_unlock(spi_data->device->master);
+}
+
+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->write_ack = spi_bl_write_ack;
+ bl->write_cmd = spi_bl_write_cmd;
+ bl->write_data = spi_bl_write_data;
+ bl->read_ack = spi_bl_read_ack;
+ bl->read_response = spi_bl_read_response;
+ bl->close = spi_bl_close;
+}
+
+#endif
+
int nanohub_spi_write(void *data, uint8_t *tx, int length, int timeout)
{
struct nanohub_spi_data *spi_data = data;
@@ -379,20 +674,13 @@ int nanohub_spi_read(void *data, uint8_t *rx, int max_length, int timeout)
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 = spi_data->data.max_speed_hz;
- spi_data->device->mode = SPI_MODE_0;
- spi_data->device->bits_per_word = SPI_BITS_PER_WORD;
- ret = spi_setup(spi_data->device);
- if (!ret) {
- udelay(40);
- gpio_set_value(spi_data->cs, 0);
- udelay(30);
- }
- return ret;
+ udelay(40);
+ gpio_set_value(spi_data->cs, 0);
+ udelay(30);
+ return 0;
}
static void nanohub_spi_close(void *data)
@@ -437,6 +725,12 @@ static int nanohub_spi_probe(struct spi_device *spi)
{
struct nanohub_spi_data *spi_data;
struct iio_dev *iio_dev;
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 0,
+ .tx_buf = NULL,
+ .rx_buf = NULL
+ };
int error;
iio_dev = iio_device_alloc(sizeof(struct nanohub_spi_data));
@@ -468,7 +762,7 @@ static int nanohub_spi_probe(struct spi_device *spi)
spi_data->data.max_speed_hz = spi->max_speed_hz ? : SPI_MAX_SPEED_HZ;
nanohub_spi_comms_init(spi_data);
-#ifdef CONFIG_NANOHUB_BL
+#ifdef CONFIG_NANOHUB_BL_ST
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;
@@ -480,6 +774,19 @@ static int nanohub_spi_probe(struct spi_device *spi)
nanohub_spi_bl_init(spi_data);
#endif
+#ifdef CONFIG_NANOHUB_BL_NXP
+ nanohub_spi_bl_init(spi_data);
+#endif
+
+ // Set up the SPI bus and run an empty message to set the idle state.
+ // This setup can overridden in nanohub_spi_open and spi_bl_open.
+ spi_data->device->max_speed_hz = spi_data->data.max_speed_hz;
+ spi_data->device->mode = SPI_MODE_3;
+ spi_data->device->bits_per_word = SPI_BITS_PER_WORD;
+ spi_setup(spi_data->device);
+ spi_message_init_with_transfers(&msg, &xfer, 1);
+ spi_sync_locked(spi_data->device, &msg);
+
nanohub_start(&spi_data->data);
return 0;