diff --git a/arch/arm/boards/pcm043/pcm043.c b/arch/arm/boards/pcm043/pcm043.c index d4c3f5c..b2726df 100644 --- a/arch/arm/boards/pcm043/pcm043.c +++ b/arch/arm/boards/pcm043/pcm043.c @@ -45,6 +45,7 @@ #include #include #include +#include static struct fec_platform_data fec_info = { .xcv_type = PHY_INTERFACE_MODE_MII, @@ -116,6 +117,7 @@ { uint32_t reg; char *envstr; + unsigned long bbu_nand_flags = 0; /* CS0: Nor Flash */ imx35_setup_weimcs(5, 0x22C0CF00, 0x75000D01, 0x00000900); @@ -147,6 +149,7 @@ devfs_add_partition("nand0", SZ_512K, SZ_256K, DEVFS_PARTITION_FIXED, "env_raw"); dev_add_bb_dev("env_raw", "env0"); envstr = "NAND"; + bbu_nand_flags = BBU_HANDLER_FLAG_DEFAULT; break; case BOOTSOURCE_NOR: default: @@ -164,6 +167,9 @@ armlinux_set_bootparams((void *)0x80000100); armlinux_set_architecture(MACH_TYPE_PCM043); + imx_bbu_external_nand_register_handler("nand", "/dev/nand0.barebox", + bbu_nand_flags); + return 0; } diff --git a/arch/arm/include/asm/barebox-arm-head.h b/arch/arm/include/asm/barebox-arm-head.h index 9d9b854..9385415 100644 --- a/arch/arm/include/asm/barebox-arm-head.h +++ b/arch/arm/include/asm/barebox-arm-head.h @@ -33,6 +33,13 @@ set_cr(r); } +/* + * 32 bytes at this offset is reserved in the barebox head for board/SoC + * usage + */ +#define ARM_HEAD_SPARE_OFS 0x30 +#define ARM_HEAD_SPARE_MARKER 0x55555555 + #ifdef CONFIG_HAVE_MACH_ARM_HEAD #include #else @@ -64,6 +71,9 @@ * barebox can skip relocation */ ".word _barebox_image_size\n" /* image size to copy */ + ".rept 8\n" + ".word 0x55555555\n" + ".endr\n" ); } #endif diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 1308f3c..7703127 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -149,6 +149,12 @@ endchoice +config BAREBOX_UPDATE_IMX_EXTERNAL_NAND + bool + depends on ARCH_IMX_EXTERNAL_BOOT_NAND + depends on BAREBOX_UPDATE + default y + comment "Freescale i.MX System-on-Chip" choice diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 4adf522..dd58c62 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -16,4 +16,5 @@ obj-y += devices.o imx.o esdctl.o obj-y += boot.o obj-$(CONFIG_BAREBOX_UPDATE) += imx-bbu-internal.o +obj-$(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND) += imx-bbu-external-nand.o pbl-y += esdctl.o diff --git a/arch/arm/mach-imx/external-nand-boot.c b/arch/arm/mach-imx/external-nand-boot.c index 73c4ccd..636f974 100644 --- a/arch/arm/mach-imx/external-nand-boot.c +++ b/arch/arm/mach-imx/external-nand-boot.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -154,10 +155,18 @@ #endif } +static noinline void __bare_init imx_nandboot_get_page(void *regs, + u32 offs, int pagesize_2k) +{ + imx_nandboot_send_cmd(regs, NAND_CMD_READ0); + imx_nandboot_nfc_addr(regs, offs, pagesize_2k); + imx_nandboot_send_page(regs, NFC_OUTPUT, pagesize_2k); +} + void __bare_init imx_nand_load_image(void *dest, int size) { - u32 tmp, page, block, blocksize, pagesize; - int pagesize_2k = 1; + u32 tmp, page, block, blocksize, pagesize, badblocks; + int pagesize_2k = 1, bbt = 0; void *regs, *base, *spare0; #if defined(CONFIG_NAND_IMX_BOOT_512) @@ -223,30 +232,52 @@ writew(NFC_V2_SPAS_SPARESIZE(16), regs + NFC_V2_SPAS); } + /* + * Check if this image has a bad block table embedded. See + * imx_bbu_external_nand_register_handler for more information + */ + badblocks = *(uint32_t *)(base + ARM_HEAD_SPARE_OFS); + if (badblocks == IMX_NAND_BBT_MAGIC) { + bbt = 1; + badblocks = *(uint32_t *)(base + ARM_HEAD_SPARE_OFS + 4); + } + block = page = 0; while (1) { page = 0; + + imx_nandboot_get_page(regs, block * blocksize + + page * pagesize, pagesize_2k); + + if (bbt) { + if (badblocks & (1 << block)) { + block++; + continue; + } + } else if (pagesize_2k) { + if ((readw(spare0) & 0xff) != 0xff) { + block++; + continue; + } + } else { + if ((readw(spare0 + 4) & 0xff00) != 0xff00) { + block++; + continue; + } + } + while (page * pagesize < blocksize) { debug("page: %d block: %d dest: %p src " "0x%08x\n", page, block, dest, block * blocksize + page * pagesize); - - imx_nandboot_send_cmd(regs, NAND_CMD_READ0); - imx_nandboot_nfc_addr(regs, block * blocksize + + if (page) + imx_nandboot_get_page(regs, block * blocksize + page * pagesize, pagesize_2k); - imx_nandboot_send_page(regs, NFC_OUTPUT, pagesize_2k); - page++; - if (pagesize_2k) { - if ((readw(spare0) & 0xff) != 0xff) - continue; - } else { - if ((readw(spare0 + 4) & 0xff00) != 0xff00) - continue; - } + page++; __memcpy32(dest, base, pagesize); dest += pagesize; diff --git a/arch/arm/mach-imx/imx-bbu-external-nand.c b/arch/arm/mach-imx/imx-bbu-external-nand.c new file mode 100644 index 0000000..f0e9dd1 --- /dev/null +++ b/arch/arm/mach-imx/imx-bbu-external-nand.c @@ -0,0 +1,210 @@ +/* + * imx-bbu-external-nand.c - i.MX specific update functions for external + * nand boot + * + * Copyright (c) 2013 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int imx_bbu_external_nand_update(struct bbu_handler *handler, struct bbu_data *data) +{ + struct mtd_info_user meminfo; + int fd; + struct stat s; + int size_available, size_need; + int ret; + uint32_t num_bb = 0, bbt = 0; + uint64_t offset; + int block = 0, len, now, blocksize; + void *image = data->image; + + ret = stat(data->devicefile, &s); + if (ret) + return ret; + + size_available = s.st_size; + + fd = open(data->devicefile, O_RDWR); + if (fd < 0) + return fd; + + ret = ioctl(fd, MEMGETINFO, &meminfo); + if (ret) + goto out; + + blocksize = meminfo.erasesize; + + size_need = data->len; + + /* + * Collect bad blocks and construct BBT + */ + while (size_need > 0) { + ret = ioctl(fd, MEMGETBADBLOCK, &offset); + if (ret < 0) + goto out; + + if (ret) { + if (!offset) { + printf("1st block is bad. This is not supported\n"); + ret = -EINVAL; + goto out; + } + + debug("bad block at 0x%08llx\n", offset); + num_bb++; + bbt |= (1 << block); + offset += blocksize; + block++; + continue; + } + size_need -= blocksize; + size_available -= blocksize; + offset += blocksize; + block++; + + if (size_available < 0) { + printf("device is too small.\n" + "raw partition size: 0x%08llx\n" + "partition size w/o bad blocks: 0x%08llx\n" + "size needed: 0x%08x\n", + s.st_size, + s.st_size - num_bb * blocksize, + data->len); + ret = -ENOSPC; + goto out; + } + } + + debug("total image size: 0x%08x. Space needed including bad blocks: 0x%08x\n", + data->len, data->len + num_bb * blocksize); + + if (meminfo.writesize >= 2048) { + uint32_t *image_bbt = image + ARM_HEAD_SPARE_OFS; + + debug(">= 2k nand detected. Creating in-image bbt\n"); + + if (*image_bbt != ARM_HEAD_SPARE_MARKER) { + if (!bbu_force(data, "Cannot find space for the BBT")) { + ret = -EINVAL; + goto out; + } + } else { + *image_bbt++ = IMX_NAND_BBT_MAGIC; + *image_bbt++ = bbt; + } + } + + if (data->len + num_bb * blocksize > s.st_size) { + printf("needed space (0x%08x) exceeds partition space (0x%08llx)\n", + data->len + num_bb * blocksize, s.st_size); + ret = -ENOSPC; + goto out; + } + + len = data->len; + offset = 0; + + /* last chance before erasing the flash */ + ret = bbu_confirm(data); + if (ret) + return ret; + + /* + * Write image to NAND skipping bad blocks + */ + while (len > 0) { + now = min(len, blocksize); + + ret = ioctl(fd, MEMGETBADBLOCK, &offset); + if (ret < 0) + goto out; + + if (ret) { + ret = lseek(fd, offset + blocksize, SEEK_SET); + if (ret < 0) + goto out; + offset += blocksize; + continue; + } + + debug("writing %d bytes at 0x%08llx\n", now, offset); + + ret = erase(fd, blocksize, offset); + if (ret) + goto out; + + ret = write(fd, image, now); + if (ret < 0) + goto out; + + len -= now; + image += now; + offset += now; + } + + ret = 0; + +out: + close(fd); + + return ret; +} + +/* + * Register a i.MX external nand boot update handler. + * + * For 512b page NANDs this handler simply writes the image to NAND skipping + * bad blocks. + * + * For 2K page NANDs this handler embeds a bad block table in the flashed image. + * This is necessary since we rely on an on-flash BBT for these flashes, but the + * regular mtd BBT is too complex to be handled in the 2k the i.MX is able to + * initially load from NAND. The BBT consists of a single 32bit value in which + * each bit represents a single block. With 2k NAND flashes this is enough for + * 4MiB size including bad blocks. + */ +int imx_bbu_external_nand_register_handler(const char *name, char *devicefile, + unsigned long flags) +{ + struct bbu_handler *handler; + int ret; + + handler = xzalloc(sizeof(*handler)); + handler->devicefile = devicefile; + handler->name = name; + handler->flags = flags; + handler->handler = imx_bbu_external_nand_update; + + ret = bbu_register_handler(handler); + if (ret) + free(handler); + + return ret; +} diff --git a/arch/arm/mach-imx/include/mach/bbu.h b/arch/arm/mach-imx/include/mach/bbu.h index ad7c754..077133a 100644 --- a/arch/arm/mach-imx/include/mach/bbu.h +++ b/arch/arm/mach-imx/include/mach/bbu.h @@ -78,6 +78,17 @@ #endif +#if defined(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND) +int imx_bbu_external_nand_register_handler(const char *name, char *devicefile, + unsigned long flags); +#else +static inline int imx_bbu_external_nand_register_handler(const char *name, char *devicefile, + unsigned long flags) +{ + return -ENOSYS; +} +#endif + struct dcd_table { void *data; unsigned int size; diff --git a/arch/arm/mach-imx/include/mach/imx-nand.h b/arch/arm/mach-imx/include/mach/imx-nand.h index 8352d79..9e6416e 100644 --- a/arch/arm/mach-imx/include/mach/imx-nand.h +++ b/arch/arm/mach-imx/include/mach/imx-nand.h @@ -70,4 +70,10 @@ #define NFC_ID (1 << 4) #define NFC_STATUS (1 << 5) +/* + * For external NAND boot this defines the magic value for the bad block table + * This is found at offset ARM_HEAD_SPARE_OFS in the image on NAND. + */ +#define IMX_NAND_BBT_MAGIC 0xbadb10c0 + #endif /* __ASM_ARCH_NAND_H */