241 lines
7.0 KiB
C
241 lines
7.0 KiB
C
/*
|
|
* Quantenna boot support
|
|
*/
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <environment.h>
|
|
|
|
#include "ruby.h"
|
|
#include "spi_flash.h"
|
|
|
|
#define BUFSIZE 256
|
|
|
|
/* get all of the pointers, vaguely validate them, 1 on failure, 0 on success */
|
|
static int get_qtnboot_envvars(unsigned long *safety_addr, unsigned long *live_addr,
|
|
unsigned long *safety_size, unsigned long *live_size)
|
|
{
|
|
char *safety_addr_str = getenv (SAFETY_IMG_ADDR_ARG);
|
|
char *safety_size_str = getenv (SAFETY_IMG_SIZE_ARG);
|
|
char *live_addr_str = getenv (LIVE_IMG_ADDR_ARG);
|
|
char *live_size_str = getenv (LIVE_IMG_SIZE_ARG);
|
|
|
|
if (safety_addr_str && safety_size_str && live_addr_str && live_size_str) {
|
|
*safety_addr = simple_strtoul(safety_addr_str, NULL, 0);
|
|
*safety_size = simple_strtoul(safety_size_str, NULL, 0);
|
|
*live_addr = simple_strtoul(live_addr_str, NULL, 0);
|
|
*live_size = simple_strtoul(live_size_str, NULL, 0);
|
|
if (!(*safety_size > 0
|
|
&& *live_addr >= *safety_addr + *safety_size
|
|
&& *live_size >= *safety_size)) {
|
|
printf("Variables: %s %s %s %s aren't valid\n",
|
|
SAFETY_IMG_ADDR_ARG,
|
|
SAFETY_IMG_SIZE_ARG,
|
|
LIVE_IMG_ADDR_ARG,
|
|
LIVE_IMG_SIZE_ARG);
|
|
return 1;
|
|
}
|
|
|
|
const unsigned sector_size = spi_flash_sector_size();
|
|
const unsigned flash_size = spi_flash_size();
|
|
|
|
if ((*safety_size % sector_size) || (*live_size % sector_size)) {
|
|
printf("partitions sizes not a multiple of sector size: %dk\n", sector_size / 1024);
|
|
return 1;
|
|
}
|
|
|
|
if ((*safety_size + *live_size +
|
|
RUBY_MIN_DATA_PARTITION_SIZE +
|
|
UBOOT_TEXT_PARTITION_SIZE +
|
|
UBOOT_ENV_PARTITION_SIZE) > flash_size) {
|
|
printf("Sizes exceed flash size: %dk, will not fit on device",
|
|
flash_size / 1024);
|
|
return 1;
|
|
}
|
|
} else {
|
|
printf("Variables: %s %s %s %s must be set\n",
|
|
SAFETY_IMG_ADDR_ARG,
|
|
SAFETY_IMG_SIZE_ARG,
|
|
LIVE_IMG_ADDR_ARG,
|
|
LIVE_IMG_SIZE_ARG);
|
|
return 1;
|
|
}
|
|
|
|
printf("%s: vars: %s 0x%lx %s 0x%lx %s 0x%lx %s 0x%lx\n",
|
|
__FUNCTION__,
|
|
SAFETY_IMG_ADDR_ARG, *safety_addr,
|
|
SAFETY_IMG_SIZE_ARG, *safety_size,
|
|
LIVE_IMG_ADDR_ARG, *live_addr,
|
|
LIVE_IMG_SIZE_ARG, *live_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* setup the mtdargs parameter to pass a partition table into linux */
|
|
static void set_mtdparts(unsigned long safety_size, unsigned long live_size)
|
|
{
|
|
char mtdparts[BUFSIZE];
|
|
|
|
sprintf(mtdparts, "spi_flash:%dk(uboot),%dk(uboot_env),%luk(linux_safety_image),%luk(linux_live_image),-(data)",
|
|
UBOOT_TEXT_PARTITION_SIZE / 1024,
|
|
UBOOT_ENV_PARTITION_SIZE / 1024,
|
|
safety_size / 1024,
|
|
live_size / 1024
|
|
);
|
|
|
|
setenv("mtdparts", mtdparts);
|
|
}
|
|
|
|
int do_qtn_setmtdparts (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
unsigned long safety_addr = 0;
|
|
unsigned long safety_size = 0;
|
|
unsigned long live_addr = 0;
|
|
unsigned long live_size = 0;
|
|
|
|
if (get_qtnboot_envvars(&safety_addr, &live_addr, &safety_size, &live_size))
|
|
return -1;
|
|
set_mtdparts(safety_size, live_size);
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(qtn_setmtdparts, CONFIG_SYS_MAXARGS, 0, do_qtn_setmtdparts,
|
|
"set the environment variable 'mtdparts'",
|
|
"sets mtdparts to a string appropriate for the mtdparts kernel \n"
|
|
"command line argument. Partitions are derived from the environment\n"
|
|
"variables: ${" LIVE_IMG_ADDR_ARG "}, ${" SAFETY_IMG_ADDR_ARG "},\n"
|
|
"${" SAFETY_IMG_SIZE_ARG "} and ${" LIVE_IMG_SIZE_ARG "}\n"
|
|
);
|
|
|
|
#define RUN(args...) _run(__FUNCTION__, args)
|
|
static int _run(const char* function_name, ...)
|
|
{
|
|
va_list args;
|
|
char cmdbuf[BUFSIZE];
|
|
sprintf(cmdbuf, args);
|
|
printf("%s: %s\n", function_name, cmdbuf);
|
|
return run_command(cmdbuf, 0);
|
|
}
|
|
|
|
int do_qtnboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
unsigned long safety_addr = 0;
|
|
unsigned long safety_size = 0;
|
|
unsigned long live_addr = 0;
|
|
unsigned long live_size = 0;
|
|
const unsigned long mem_addr = QTNBOOT_COPY_DRAM_ADDR;
|
|
|
|
if (get_qtnboot_envvars(&safety_addr, &live_addr, &safety_size, &live_size)) {
|
|
return -1;
|
|
}
|
|
|
|
set_mtdparts(safety_size, live_size);
|
|
RUN("setenv bootargs ${bootargs} mtdparts=${mtdparts}");
|
|
|
|
// attempt to load the live image into memory and boot it.
|
|
RUN("spi_flash read 0x%08lx 0x%08lx 0x%08lx", live_addr, mem_addr, live_size);
|
|
RUN("bootm 0x%08lx", mem_addr);
|
|
|
|
// if control returns, it failed
|
|
// load the safety image into memory, copy it over the live image, then boot/reset
|
|
RUN("spi_flash read 0x%08lx 0x%08lx 0x%08lx", safety_addr, mem_addr, safety_size);
|
|
|
|
// run_command returns -1 for errors, or repeatability, rather than return codes
|
|
if (RUN("imi 0x%08lx", mem_addr) < 0) {
|
|
printf("FATAL: safety image at 0x%08lx appears corrupt\n", safety_addr);
|
|
return -1;
|
|
}
|
|
|
|
RUN("spi_flash unlock");
|
|
RUN("spi_flash erase 0x%08lx 0x%08lx", live_addr, live_size);
|
|
RUN("spi_flash write 0x%08lx 0x%08lx 0x%08lx", live_addr, mem_addr, safety_size);
|
|
RUN("sleep 2");
|
|
RUN("reset");
|
|
|
|
// never gets to here
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(qtnboot, CONFIG_SYS_MAXARGS, 0, do_qtnboot,
|
|
"boot from live image, recover safety image if necessary",
|
|
"Quantenna dual boot with recovery. Attempts to boot the live image\n"
|
|
"found at address ${" LIVE_IMG_ADDR_ARG "}. If the checksum fails, \n"
|
|
"the safety image at ${" SAFETY_IMG_ADDR_ARG "} is copied over the \n"
|
|
"live image, then booted.\n"
|
|
);
|
|
|
|
int do_bootselect(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
unsigned long safety_addr = 0;
|
|
unsigned long safety_size = 0;
|
|
unsigned long live_addr = 0;
|
|
unsigned long live_size = 0;
|
|
unsigned long addr;
|
|
unsigned long size;
|
|
const unsigned long mem_addr = QTNBOOT_COPY_DRAM_ADDR;
|
|
unsigned long bootsel_val = 0;
|
|
const char* bootsel_str = NULL;
|
|
|
|
if (get_qtnboot_envvars(&safety_addr, &live_addr, &safety_size, &live_size)) {
|
|
return -1;
|
|
}
|
|
|
|
bootsel_str = getenv("bootselect");
|
|
if (bootsel_str) {
|
|
bootsel_val = simple_strtoul(bootsel_str, NULL, 0);
|
|
}
|
|
|
|
if (bootsel_val) {
|
|
addr = safety_addr;
|
|
size = safety_size;
|
|
} else {
|
|
addr = live_addr;
|
|
size = live_size;
|
|
}
|
|
|
|
RUN("spi_flash read 0x%08lx 0x%08lx 0x%08lx", addr, mem_addr, size);
|
|
RUN("bootm 0x%08lx", mem_addr);
|
|
}
|
|
|
|
U_BOOT_CMD(bootselect, CONFIG_SYS_MAXARGS, 0, do_bootselect,
|
|
"boot live/safety depending on value of 'bootselect'",
|
|
"If env variable 'bootselect' is 1, boot from image at safety address;\n"
|
|
"Otherwise boot from image at live address\n"
|
|
);
|
|
|
|
#define RUBY_SPI1_BASE_ADDR 0xF2000000
|
|
#define RUBY_SPI1_EXT 0xc
|
|
|
|
|
|
int do_setgpio(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
unsigned long ngpio;
|
|
unsigned long value;
|
|
|
|
if (argc < 2) {
|
|
cmd_usage(cmdtp);
|
|
return 1;
|
|
}
|
|
|
|
ngpio = simple_strtoul(argv[1], NULL, 10);
|
|
if (ngpio >= RUBY_GPIO_MAX)
|
|
return 1;
|
|
|
|
if (argc > 2) {
|
|
value = simple_strtoul(argv[2], NULL, 10);
|
|
value = !!value;
|
|
} else
|
|
value = 1;
|
|
|
|
gpio_output(ngpio, value);
|
|
gpio_config(ngpio, RUBY_GPIO_MODE_OUTPUT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_CMD(setgpio, CONFIG_SYS_MAXARGS, 2, do_setgpio,
|
|
"Configure gpio as output and set the output value",
|
|
"ngpio [value], drive the ngpio to 'value'\n"
|
|
"drive the ngpio to 1 if value is absent\n"
|
|
);
|
|
|