diff --git a/arch/arm/boards/ebv-socrates/board.c b/arch/arm/boards/ebv-socrates/board.c index 5d2d619..f3207b8 100644 --- a/arch/arm/boards/ebv-socrates/board.c +++ b/arch/arm/boards/ebv-socrates/board.c @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -26,12 +29,45 @@ static int socrates_init(void) { + enum bootsource bootsource = bootsource_get(); + uint32_t flag_qspi = 0; + uint32_t flag_mmc = 0; + if (!of_machine_is_compatible("ebv,socrates")) return 0; if (IS_ENABLED(CONFIG_PHYLIB)) phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK, phy_fixup); + switch (bootsource) { + case BOOTSOURCE_MMC: + flag_mmc |= BBU_HANDLER_FLAG_DEFAULT; + break; + case BOOTSOURCE_SPI: + flag_qspi |= BBU_HANDLER_FLAG_DEFAULT; + break; + default: + break; + } + + bbu_register_std_file_update("qspi-xload0", flag_qspi, + "/dev/mtd0.prebootloader0", + filetype_socfpga_xload); + bbu_register_std_file_update("qspi-xload1", 0, + "/dev/mtd0.prebootloader1", + filetype_socfpga_xload); + bbu_register_std_file_update("qspi-xload2", 0, + "/dev/mtd0.prebootloader2", + filetype_socfpga_xload); + bbu_register_std_file_update("qspi-xload3", 0, + "/dev/mtd0.prebootloader3", + filetype_socfpga_xload); + bbu_register_std_file_update("qspi", 0, "/dev/mtd0.barebox", + filetype_arm_barebox); + + bbu_register_std_file_update("mmc-xload", flag_mmc, "/dev/mmc0.0", + filetype_socfpga_xload); + return 0; } postcore_initcall(socrates_init); diff --git a/arch/arm/dts/socfpga_cyclone5_socrates.dts b/arch/arm/dts/socfpga_cyclone5_socrates.dts index 95cdf5d..ea7e6cc 100644 --- a/arch/arm/dts/socfpga_cyclone5_socrates.dts +++ b/arch/arm/dts/socfpga_cyclone5_socrates.dts @@ -19,42 +19,12 @@ #include "socfpga.dtsi" / { - model = "EBV SoCrates"; - compatible = "ebv,socrates", "altr,socfpga"; - chosen { stdout-path = &uart0; }; - leds: gpio-leds { - }; -}; - -&gpio0 { - status = "okay"; -}; - -&gpio1 { - status = "okay"; -}; - -&leds { - compatible = "gpio-leds"; - - led@0 { - label = "0"; - gpios = <&porta 28 1>; - linux,default-trigger = "heartbeat"; - }; - - led@1 { - label = "1"; - gpios = <&portb 19 1>; - }; - - led@2 { - label = "2"; - gpios = <&portb 25 1>; + aliases { + ethernet0 = &gmac1; }; }; @@ -75,5 +45,35 @@ cdns,tsd2d-ns = <50>; cdns,tchsh-ns = <4>; cdns,tslch-ns = <4>; + + partition@0 { + label = "prebootloader0"; + reg = <0x00000 0x10000>; + }; + + partition@1 { + label = "prebootloader1"; + reg = <0x10000 0x10000>; + }; + + partition@2 { + label = "prebootloader2"; + reg = <0x20000 0x10000>; + }; + + partition@3 { + label = "prebootloader3"; + reg = <0x30000 0x10000>; + }; + + partition@4 { + label = "barebox"; + reg = <0x40000 0x80000>; + }; + + partition@5 { + label = "data"; + reg = <0xc0000 0x1f40000>; + }; }; }; diff --git a/arch/arm/mach-socfpga/xload.c b/arch/arm/mach-socfpga/xload.c index 7f8f032..9936269 100644 --- a/arch/arm/mach-socfpga/xload.c +++ b/arch/arm/mach-socfpga/xload.c @@ -20,13 +20,15 @@ #include #include - -static struct socfpga_barebox_part default_part = { - .nor_offset = SZ_256K, - .nor_size = SZ_1M, - .mmc_disk = "disk0.1", +static struct socfpga_barebox_part default_parts[] = { + { + .nor_offset = SZ_256K, + .nor_size = SZ_1M, + .mmc_disk = "disk0.1", + }, + { /* sentinel */ } }; -const struct socfpga_barebox_part *barebox_part = &default_part; +const struct socfpga_barebox_part *barebox_parts = &default_parts; enum socfpga_clks { timer, mmc, qspi_clk, uart, clk_max @@ -109,28 +111,52 @@ static __noreturn int socfpga_xload(void) { enum bootsource bootsource = bootsource_get(); - void *buf; + struct socfpga_barebox_part *part; + void *buf = NULL; switch (bootsource) { case BOOTSOURCE_MMC: socfpga_mmc_init(); - buf = bootstrap_read_disk(barebox_part->mmc_disk, "fat"); + + for (part = barebox_parts; part->mmc_disk; part++) { + buf = bootstrap_read_disk(barebox_parts->mmc_disk, "fat"); + if (!buf) { + pr_info("failed to load barebox from MMC %s\n", + part->mmc_disk); + continue; + } + } + if (!buf) { + pr_err("failed to load barebox.bin from MMC\n"); + hang(); + } break; case BOOTSOURCE_SPI: socfpga_qspi_init(); - buf = bootstrap_read_devfs("mtd0", false, barebox_part->nor_offset, - barebox_part->nor_size, SZ_1M); + + for (part = barebox_parts; part->nor_size; part++) { + buf = bootstrap_read_devfs("mtd0", false, + part->nor_offset, part->nor_size, SZ_1M); + if (!buf) { + pr_info("failed to load barebox from QSPI NOR flash at offset %#x\n", + part->nor_offset); + continue; + } + + break; + } + + if (!buf) { + pr_err("failed to load barebox from QSPI NOR flash\n"); + hang(); + } + break; default: pr_err("unknown bootsource %d\n", bootsource); hang(); } - if (!buf) { - pr_err("failed to load barebox.bin\n"); - hang(); - } - pr_info("starting bootloader...\n"); bootstrap_boot(buf, 0); diff --git a/common/filetype.c b/common/filetype.c index a8666a1..4728f87 100644 --- a/common/filetype.c +++ b/common/filetype.c @@ -61,6 +61,7 @@ [filetype_xz_compressed] = { "XZ compressed", "xz" }, [filetype_exe] = { "MS-DOS executable", "exe" }, [filetype_mxs_bootstream] = { "Freescale MXS bootstream", "mxsbs" }, + [filetype_socfpga_xload] = { "SoCFPGA prebootloader image", "socfpga-xload" }, }; const char *file_type_to_string(enum filetype f) @@ -294,6 +295,9 @@ if (le32_to_cpu(buf[5]) == 0x504d5453) return filetype_mxs_bootstream; + if (buf[16] == 0x31305341) + return filetype_socfpga_xload; + if (is_barebox_arm_head(_buf)) return filetype_arm_barebox; if (buf[9] == 0x016f2818 || buf[9] == 0x18286f01) diff --git a/drivers/clk/socfpga.c b/drivers/clk/socfpga.c index 37ed038..6af0632 100644 --- a/drivers/clk/socfpga.c +++ b/drivers/clk/socfpga.c @@ -116,18 +116,27 @@ const char *parent; unsigned regofs; unsigned int fixed_div; + void __iomem *div_reg; + unsigned int width; + unsigned int shift; }; static unsigned long clk_periph_recalc_rate(struct clk *clk, unsigned long parent_rate) { struct clk_periph *periph = container_of(clk, struct clk_periph, clk); - u32 div; + u32 div, val; - if (periph->fixed_div) + if (periph->fixed_div) { div = periph->fixed_div; - else + } else { + if (periph->div_reg) { + val = readl(periph->div_reg) >> periph->shift; + val &= div_mask(periph->width); + parent_rate /= (val + 1); + } div = ((readl(clk_mgr_base_addr + periph->regofs) & 0x1ff) + 1); + } return parent_rate / div; } @@ -140,6 +149,7 @@ { struct clk_periph *periph; int ret; + u32 div_reg[3]; periph = xzalloc(sizeof(*periph)); @@ -152,6 +162,15 @@ periph->clk.name = xstrdup(node->name); periph->clk.ops = &clk_periph_ops; + ret = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!ret) { + periph->div_reg = clk_mgr_base_addr + div_reg[0]; + periph->shift = div_reg[1]; + periph->width = div_reg[2]; + } else { + periph->div_reg = 0; + } + of_property_read_u32(node, "reg", &periph->regofs); of_property_read_u32(node, "fixed-divider", &periph->fixed_div); diff --git a/include/filetype.h b/include/filetype.h index e87ca17..cde73c1 100644 --- a/include/filetype.h +++ b/include/filetype.h @@ -36,6 +36,7 @@ filetype_exe, filetype_xz_compressed, filetype_mxs_bootstream, + filetype_socfpga_xload, filetype_max, }; diff --git a/scripts/socfpga_mkimage.c b/scripts/socfpga_mkimage.c index 4fbd5c7..d7fe1b1 100644 --- a/scripts/socfpga_mkimage.c +++ b/scripts/socfpga_mkimage.c @@ -14,7 +14,10 @@ #define BRANCH_INST 0xea /* ARM opcode for "b" (unconditional branch) */ -#define MAX_IMAGE_SIZE (60 * 1024 - 4) +#define MAX_V0IMAGE_SIZE (60 * 1024 - 4) +/* Max size without authentication is 224 KB, due to memory used by + * the ROM boot code as a workspace out of the 256 KB of OCRAM */ +#define MAX_V1IMAGE_SIZE (224 * 1024 - 4) static int add_barebox_header; @@ -22,10 +25,21 @@ uint8_t validation_word[4]; uint8_t version; uint8_t flags; - uint8_t program_length[2]; - uint8_t spare[2]; - uint8_t checksum[2]; - uint8_t start_vector[4]; + union { + struct { + uint8_t program_length[2]; + uint8_t spare[2]; + uint8_t checksum[2]; + uint8_t start_vector[4]; + } v0; + struct { + uint8_t header_length[2]; + uint8_t program_length[4]; + uint8_t entry_offset[4]; + uint8_t spare[2]; + uint8_t checksum[2]; + } v1; + }; }; static uint32_t bb_header[] = { @@ -87,7 +101,7 @@ return insize; } -static uint32_t crc_table[256] = { +static const uint32_t crc_table[256] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, @@ -143,47 +157,94 @@ return crc; } +/* Create an ARM relative branch instuction + * branch is where the instruction will be placed and dest points to where + * it should branch too. */ +static void branch(uint8_t *branch, uint8_t *dest) +{ + int offset = dest - branch - 8; /* PC is offset +8 bytes on ARM */ + + branch[0] = (offset >> 2) & 0xff; /* instruction uses offset/4 */ + branch[1] = (offset >> 10) & 0xff; + branch[2] = (offset >> 18) & 0xff; + branch[3] = BRANCH_INST; +} + /* start_addr is where the socfpga header's start instruction should branch to. * It should be relative to the start of buf */ -static int add_socfpga_header(void *buf, size_t size, unsigned start_addr) +static int add_socfpga_header(void *buf, size_t size, unsigned start_addr, unsigned version) { struct socfpga_header *header = buf + 0x40; - uint8_t *bufp; + void *entry; + uint8_t *bufp, *sumendp; uint32_t *crc; unsigned checksum; - size_t length = size >> 2; if (size & 0x3) { fprintf(stderr, "%s: size must be multiple of 4\n", __func__); return -EINVAL; } - /* Calculate relative address of requested start_addr from the - * start_vector's branch instuction PC (+8 bytes on arm). */ - start_addr = start_addr + (int)(buf - (void*)&header->start_vector[0]) - 8; + /* Absolute address of entry point in buf */ + entry = buf + start_addr; + if (version == 0) { + sumendp = &header->v0.checksum[0]; + } else { + sumendp = &header->v1.checksum[0]; + + /* The ROM loader can't handle a negative offset */ + if (entry < (void*)header) { + /* add a trampoline branch inst after end of the header */ + uint8_t *trampoline = (void*)(header + 1); + branch(trampoline, entry); + + /* and then make the trampoline the entry point */ + entry = trampoline; + } + /* Calculate start address as offset relative to start of header */ + start_addr = entry - (void*)header; + } header->validation_word[0] = VALIDATION_WORD & 0xff; header->validation_word[1] = (VALIDATION_WORD >> 8) & 0xff; header->validation_word[2] = (VALIDATION_WORD >> 16) & 0xff; header->validation_word[3] = (VALIDATION_WORD >> 24) & 0xff; - header->version = 0; + header->version = version; header->flags = 0; - header->program_length[0] = length & 0xff; - header->program_length[1] = (length >> 8) & 0xff; - header->spare[0] = 0; - header->spare[1] = 0; - header->start_vector[0] = (start_addr >> 2) & 0xff; /* instruction uses offset/4 */ - header->start_vector[1] = (start_addr >> 10) & 0xff; - header->start_vector[2] = (start_addr >> 18) & 0xff; - header->start_vector[3] = BRANCH_INST; + + if (version == 0) { + header->v0.program_length[0] = (size >> 2) & 0xff; /* length in words */ + header->v0.program_length[1] = (size >> 10) & 0xff; + header->v0.spare[0] = 0; + header->v0.spare[1] = 0; + branch(header->v0.start_vector, entry); + } else { + header->v1.header_length[0] = (sizeof(*header) >> 0) & 0xff; + header->v1.header_length[1] = (sizeof(*header) >> 8) & 0xff; + header->v1.program_length[0] = (size >> 0) & 0xff; + header->v1.program_length[1] = (size >> 8) & 0xff; + header->v1.program_length[2] = (size >> 16) & 0xff; + header->v1.program_length[3] = (size >> 24) & 0xff; + header->v1.entry_offset[0] = (start_addr >> 0) & 0xff; + header->v1.entry_offset[1] = (start_addr >> 8) & 0xff; + header->v1.entry_offset[2] = (start_addr >> 16) & 0xff; + header->v1.entry_offset[3] = (start_addr >> 24) & 0xff; + header->v1.spare[0] = 0; + header->v1.spare[1] = 0; + } /* Sum from beginning of header to start of checksum field */ checksum = 0; - for (bufp = (uint8_t*)header; bufp < &header->checksum[0]; bufp++) + for (bufp = (uint8_t*)header; bufp < sumendp; bufp++) checksum += *bufp; - header->checksum[0] = checksum & 0xff;; - header->checksum[1] = (checksum >> 8) & 0xff;; + if (version == 0) { + header->v0.checksum[0] = checksum & 0xff;; + header->v0.checksum[1] = (checksum >> 8) & 0xff;; + } else { + header->v1.checksum[0] = checksum & 0xff;; + header->v1.checksum[1] = (checksum >> 8) & 0xff;; + } crc = buf + size - sizeof(uint32_t); @@ -195,7 +256,7 @@ static void usage(const char *prgname) { - fprintf(stderr, "usage: %s [OPTIONS] \n", prgname); + fprintf(stderr, "usage: %s [-hb] [-v version] -o \n", prgname); } int main(int argc, char *argv[]) @@ -205,16 +266,23 @@ struct stat s; void *buf; int fd; - int min_image_size = 80; - int max_image_size = MAX_IMAGE_SIZE; + int max_image_size, min_image_size = 80; int addsize = 0, pad; + unsigned int version = 0; - while ((opt = getopt(argc, argv, "o:hb")) != -1) { + while ((opt = getopt(argc, argv, "o:hbv:")) != -1) { switch (opt) { + case 'v': + version = atoi(optarg); + if (version > 1) { + printf("Versions supported: 0 or 1\n"); + usage(argv[0]); + exit(1); + } + break; case 'b': add_barebox_header = 1; min_image_size = 0; - max_image_size = MAX_IMAGE_SIZE - 512; addsize = 512; break; case 'h': @@ -224,15 +292,21 @@ outfile = optarg; break; default: + usage(argv[0]); exit(1); } } + if (version == 0) { + max_image_size = MAX_V0IMAGE_SIZE; + } else { + max_image_size = MAX_V1IMAGE_SIZE; + } + max_image_size -= addsize; - if (optind == argc) { + if (optind == argc || !outfile) { usage(argv[0]); exit(1); } - infile = argv[optind]; ret = stat(infile, &s); @@ -242,7 +316,8 @@ } if (s.st_size < min_image_size) { - fprintf(stderr, "input image too small. Minimum is 80 bytes\n"); + fprintf(stderr, "input image too small. Minimum is %d bytes\n", + min_image_size); exit(1); } @@ -253,7 +328,7 @@ } fd = open(infile, O_RDONLY); - if (fd < 0) { + if (fd == -1) { perror("open infile"); exit(1); } @@ -280,7 +355,8 @@ memcpy(buf, bb_header, sizeof(bb_header)); } - ret = add_socfpga_header(buf, s.st_size + 4 + addsize + pad, addsize); + ret = add_socfpga_header(buf, s.st_size + 4 + addsize + pad, addsize, + version); if (ret) exit(1);