blob: b6433d2be5739bf2759a35b23f4ce7ff6a45553f [file] [log] [blame]
/*
* 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;
}