blob: e9e802c6a03a098d5ee029ba2bce63f8c43ce262 [file] [log] [blame]
/*
* Quantenna boot support
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include "ruby.h"
#include "spi_flash.h"
#define BUFSIZE 256
#ifndef GFRG240
static int qtnboot_check(char *buf,
unsigned long flash_size, unsigned long sector_size,
unsigned long safety_addr, unsigned long safety_size,
unsigned long live_addr, unsigned long live_size)
{
/* check partition sizes */
if ((safety_size == 0) || (live_size == 0)) {
sprintf(buf, "linux partition sizes must be non-zero");
return -1;
}
/* check that partition addresses and sizes are sector aligned */
if ((safety_addr % sector_size) ||
(live_addr % sector_size) ||
(safety_size % sector_size) ||
(live_size % sector_size)) {
sprintf(buf, "all values must fit sector alignment: 0x%lx", sector_size);
return -1;
}
/* check that safety partition start clears the u-boot partitions */
if (safety_addr < IMAGES_START_ADDR) {
sprintf(buf, "%s 0x%lx overlaps u-boot partitions (ending 0x%x)",
SAFETY_IMG_ADDR_ARG, safety_addr, IMAGES_START_ADDR);
return -1;
}
/* check that live is at least as large as safety, so safety can be copied over live */
if (live_size < safety_size) {
sprintf(buf, "safety image size exceeds live image size");
return -1;
}
/* check that safety end doesn't overlap live start */
if (live_addr < safety_addr + safety_size) {
sprintf(buf, "live_addr 0x%lx not after safety end 0x%lx",
live_addr, safety_addr + safety_size);
return -1;
}
/* check that live end doesn't overrun flash end */
if (live_addr + live_size > flash_size) {
sprintf(buf, "live end 0x%lx exceeds flash size 0x%lx",
live_addr + live_size, flash_size);
return -1;
}
return 0;
}
/* 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)
{
const unsigned long sector_size = spi_flash_sector_size();
const unsigned long flash_size = spi_flash_size();
const char *safety_addr_str = getenv(SAFETY_IMG_ADDR_ARG);
const char *safety_size_str = getenv(SAFETY_IMG_SIZE_ARG);
const char *live_addr_str = getenv(LIVE_IMG_ADDR_ARG);
const char *live_size_str = getenv(LIVE_IMG_SIZE_ARG);
char errbuf[BUFSIZE] = {0};
int error;
*safety_addr = 0;
*safety_size = 0;
*live_addr = 0;
*live_size = 0;
/*
* Look for environment variables for each parameter,
* or provide sensible defaults based on flash size.
* No defaults for 8MB, which isn't officially supported
*/
if (safety_addr_str) {
*safety_addr = simple_strtoul(safety_addr_str, NULL, 0);
} else {
*safety_addr = IMAGES_START_ADDR;
}
if (safety_size_str) {
*safety_size = simple_strtoul(safety_size_str, NULL, 0);
} else if (flash_size == FLASH_16MB) {
*safety_size = IMG_SIZE_16M_FLASH_2_IMG;
}
if (live_addr_str) {
*live_addr = simple_strtoul(live_addr_str, NULL, 0);
} else {
*live_addr = *safety_addr + *safety_size;
}
if (live_size_str) {
*live_size = simple_strtoul(live_size_str, NULL, 0);
} else {
*live_size = *safety_size;
}
error = qtnboot_check(errbuf, flash_size, sector_size,
*safety_addr, *safety_size, *live_addr, *live_size);
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);
if (error) {
printf("%s: vars invalid: %s\n", __FUNCTION__, errbuf);
}
return error;
}
/* 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(" MTD_PARTNAME_UBOOT_BIN "),"
"%dk(" MTD_PARTNAME_UBOOT_ENV "),"
"%dk(" MTD_PARTNAME_UBOOT_ENV_BAK "),"
"%luk(" MTD_PARTNAME_LINUX_SAFETY "),"
"%luk(" MTD_PARTNAME_LINUX_LIVE "),"
"-(" MTD_PARTNAME_DATA ")",
UBOOT_TEXT_PARTITION_SIZE / 1024,
UBOOT_ENV_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"
);
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;
int num_of_image = 2;
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);
}
while (num_of_image--) {
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);
//if control return,switch to boot another image
bootsel_val = !bootsel_val;
RUN("setenv bootselect %d", bootsel_val);
saveenv();
}
return -1;
}
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"
);
#endif /* ifndef GFRG240 */
int _run(const char *function_name, const char *fmt, ...)
{
va_list args;
char cmdbuf[BUFSIZE];
va_start(args, fmt);
vsprintf(cmdbuf, fmt, args);
va_end(args);
printf("%s: %s\n", function_name, cmdbuf);
return run_command(cmdbuf, 0);
}
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"
);