From 81a55464071d5079df38f81921ff6db2113d923d Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Thu, 18 Oct 2012 12:49:29 +0200 Subject: colibri_t20: nand: integrate NVIDIA partition table parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVIDIA's NAND layout includes a partition table that can be used to generically construct the mtdparts kernel boot argument. As an added benefit this is completely independent of the underlying NAND part used which differs with various module versions. It further allows our customer easy adoption to their own custom partition layout. Initial partition table parsing courtesy of Mitja Špes from LXNAV. --- arch/arm/include/asm/arch-tegra/tegra.h | 5 + arch/arm/lib/board.c | 8 + board/toradex/common/Makefile | 1 + board/toradex/common/tegra2_partitions.c | 342 +++++++++++++++++++++++++++++++ board/toradex/common/tegra2_partitions.h | 38 ++++ 5 files changed, 394 insertions(+) create mode 100644 board/toradex/common/tegra2_partitions.c create mode 100644 board/toradex/common/tegra2_partitions.h diff --git a/arch/arm/include/asm/arch-tegra/tegra.h b/arch/arm/include/asm/arch-tegra/tegra.h index c51a8014e01..db00ebf167d 100644 --- a/arch/arm/include/asm/arch-tegra/tegra.h +++ b/arch/arm/include/asm/arch-tegra/tegra.h @@ -78,6 +78,11 @@ struct timerus { unsigned int cntr_1us; }; +#define NVBOOTINFOTABLE_BCTSIZE 0x38 /* BCT size in BIT in IRAM */ +#define NVBOOTINFOTABLE_BCTPTR 0x3C /* BCT pointer in BIT in IRAM */ +#define BCT_PTINFO_OFFSET 3820 + +/* These are the available SKUs (product types) for Tegra */ enum { SKU_ID_T20 = 0x8, SKU_ID_T25SE = 0x14, diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c index 10ef2c81288..c6a12463e03 100644 --- a/arch/arm/lib/board.c +++ b/arch/arm/lib/board.c @@ -87,6 +87,10 @@ extern void rtl8019_get_enetaddr (uchar * addr); #include #endif +#ifdef CONFIG_COLIBRI_T20 +extern void tegra_partition_init(void); +#endif + /************************************************************************ * Coloured LED functionality @@ -594,6 +598,10 @@ void board_init_r (gd_t *id, ulong dest_addr) onenand_init(); #endif +#ifdef CONFIG_COLIBRI_T20 + tegra_partition_init(); +#endif + #ifdef CONFIG_GENERIC_MMC puts("MMC: "); mmc_initialize(bd); diff --git a/board/toradex/common/Makefile b/board/toradex/common/Makefile index e3a6dbf9382..8d160d50e90 100644 --- a/board/toradex/common/Makefile +++ b/board/toradex/common/Makefile @@ -27,6 +27,7 @@ LIB = $(obj)lib$(VENDOR).o COBJS-y += board.o COBJS-$(CONFIG_TEGRA2_NAND) += tegra2_nand.o +COBJS-$(CONFIG_TEGRA2_NAND) += tegra2_partitions.o COBJS-$(CONFIG_USB_EHCI_TEGRA) += ulpi_linux.o COBJS-$(CONFIG_USB_EHCI_TEGRA) += usb.o diff --git a/board/toradex/common/tegra2_partitions.c b/board/toradex/common/tegra2_partitions.c new file mode 100644 index 00000000000..b8d8bf29ba1 --- /dev/null +++ b/board/toradex/common/tegra2_partitions.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra2_nand.h" +#include "tegra2_partitions.h" + +#define DEBUG 0 + +DECLARE_GLOBAL_DATA_PTR; + +/** + * nvtegra_print_partition_table - prints partition table info + * @param pt: nvtegra_parttable_t structure + */ +void nvtegra_print_partition_table(nvtegra_parttable_t * pt) +{ + int i; + nvtegra_partinfo_t *p; + + // sanity check + if (!pt) { + printf("%s: Error! pt arg is NULL\n", __FUNCTION__); + return; + } + + p = &(pt->partinfo[0]); + printf("\n------ NVTEGRA Partitions ------\n"); + +#if DEBUG > 1 + printf("UNK:\n"); + for (i = 0; i < 18; i++) { + if (pt->_unknown[i] > 1000000) + printf("0x%08X\t", pt->_unknown[i]); + else + printf("%u\t", pt->_unknown[i]); + if ((i + 1) % 6 == 0) + printf("\n"); + } + printf("\n"); +#endif + + for (i = 0; (p->id < 128) && (i < TEGRA_MAX_PARTITIONS); i++) { + printf + ("\n[%u]\tPart:\tID=%u\tNAME=%s\tNAME2=%s\n\t\tTYPE=%u\tALLOC_POLICY=%u\tFS_TYPE=%u\n\t\tVIRTUAL START SEC=%u\tVIRTUAL SIZE=%u\n", + i, p->id, p->name, p->name2, p->type, p->allocation_policy, + p->filesystem_type, p->virtual_start_sector, + p->virtual_size); + printf("\t\tSTART SEC=%u\tEND SEC=%u\tTOTAL=%u\n", + p->start_sector, p->end_sector, + p->end_sector + 1 - p->start_sector); + +#if DEBUG > 1 + printf("\t\tUNK1=%u\t%u\n", p->_unknown1[0], p->_unknown1[1]); + printf("\t\tUNK2=%u\t%u\t0x%08x\n", p->_unknown2[0], + p->_unknown2[1], p->_unknown2[2]); + printf("\t\tUNK3=%u\n", p->_unknown3); + printf("\t\tUNK4=%u\n", p->_unknown4); + printf("\t\tUNK5=%u\n", p->_unknown5); + printf("\t\tUNK6=%u\t%u\n", p->_unknown6[0], p->_unknown6[1]); +#endif + + p++; + } + printf("--------------------------------\n"); +} + +/** + * nvtegra_read_partition_table - reads nvidia's partition table + * @return: + * 1 - Success + * 0 - Error + */ +int nvtegra_read_partition_table(nvtegra_parttable_t * pt) +{ + size_t size; + int i; + nvtegra_partinfo_t *p; + u32 bct_start, pt_logical, pt_offset; + + // sanity check + if (!pt) { + printf("%s: Error! pt arg is NULL\n", __FUNCTION__); + return 0; + } + + /* + * Partition table offset is stored in the BCT in IRAM by the BootROM. + * The BCT start and size are stored in the BIT in IRAM. + * Read the data @ bct_start + (bct_size - 260). This works + * on T20 and T30 BCTs, which are locked down. If this changes + * in new chips (T114, etc.), we can revisit this algorithm. + */ + bct_start = readl(AP20_BASE_PA_SRAM + NVBOOTINFOTABLE_BCTPTR); +#if DEBUG > 1 + printf("bct_start=0x%08x\n", bct_start); +#endif + pt_logical = readw(bct_start + BCT_PTINFO_OFFSET); + /* In case we are running with a recovery BCT missing the partition + table offset information */ + if (pt_logical == 0) { + /* BCT partition size is 3 M in our default layout */ + pt_logical = 3 * 1024 * 1024 / nand_info->writesize; + } + /* StartLogicalSector * PageSize + 4 * BlockSize */ + pt_offset = pt_logical * nand_info->writesize + 4 * nand_info->erasesize; +#if DEBUG > 1 + printf("logical=0x%08x writesize=0x%08x erasesize=0x%08x\n", readw(bct_start + BCT_PTINFO_OFFSET), nand_info->writesize, nand_info->erasesize); + printf("pt_offset=0x%08x\n", pt_offset); +#endif + + size = sizeof(nvtegra_parttable_t); + i = nand_read_skip_bad(&nand_info[0], pt_offset, &size, + (unsigned char *)pt); + if ((i != 0) || (size != sizeof(nvtegra_parttable_t))) { + printf + ("%s: Error! nand_read_skip_bad failed. nand_info->writesize=%d ret=%d\n", + __FUNCTION__, nand_info->writesize, i); + return 0; + } + + /* some heuristics */ + p = &(pt->partinfo[0]); + if ((p->id != 2) || memcmp(p->name, "BCT\0", 4) + || memcmp(p->name2, "BCT\0", 4) || (p->virtual_start_sector != 0)) { + printf + ("%s: Error! Partition table offset is probably incorrect. name='%s'\n", + __FUNCTION__, p->name); + return 0; + } + + return 1; +} + +/** + * nvtegra_find_partition - parse nvidia partition table + * @param pt: nvtegra_parttable_t structure + * @param name: partition name + * @param partinfo: output pointer to nvtegra_partinfo_t structure + * @return: + * 1 - Success, partinfo contains partition info + * 0 - Not found or error + */ +int nvtegra_find_partition(nvtegra_parttable_t * pt, const char *name, + nvtegra_partinfo_t ** partinfo) +{ + int i, l; + nvtegra_partinfo_t *p; + + // sanity checks + if (!pt) { + printf("%s: Error! pt arg is NULL\n", __FUNCTION__); + return 0; + } + if (!name) { + printf("%s: Error! name arg is NULL\n", __FUNCTION__); + return 0; + } + if (!partinfo) { + printf("%s: Error! partinfo arg is NULL\n", __FUNCTION__); + return 0; + } + + p = &(pt->partinfo[0]); + l = strlen(name) + 1; // string length + \0 + for (i = 0; (p->id < 128) && (i < TEGRA_MAX_PARTITIONS); i++) { + if (memcmp(p->name, name, l) == 0 + || memcmp(p->name2, name, l) == 0) { + *partinfo = p; + return 1; + } + p++; + } + + printf("%s: Error! Partition '%s' not found.\n", __FUNCTION__, name); + return 0; +} + +int nvtegra_mtdparts_string(char *output, int size) +{ + int i, j = 0; + nvtegra_parttable_t *pt; + nvtegra_partinfo_t *p, *usr; + char buffer[512]; + + // sanity checks + if (!output) { + printf("%s: Error! output arg is NULL.\n", __FUNCTION__); + return 0; + } + if (size > 256) { + printf + ("%s: Error! size is too large. Increase buffer size here.\n", + __FUNCTION__); + return 0; + } + // parse nvidia partition table + pt = malloc(sizeof(nvtegra_parttable_t)); + if (!pt) { + printf("%s: Error calling malloc(%d)\n", __FUNCTION__, + sizeof(nvtegra_parttable_t)); + return 0; + } + + if (!nvtegra_read_partition_table(pt)) { + free(pt); + return 0; + } + // make rootfs partition the first in the list + if (nvtegra_find_partition(pt, "USR", &p)) { + sprintf(buffer + j, "%uK@%uK(USR)", + p->virtual_size * nand_info->writesize / 1024, + p->start_sector * nand_info->writesize / 1024); + j += strlen(buffer + j); + } + usr = p; + + p = &(pt->partinfo[0]); + for (i = 0; (p->id < 128) && (i < TEGRA_MAX_PARTITIONS); i++) { + if (p != usr) { + if (strlen(p->name)) + sprintf(buffer + j, ",%uK@%uK(%s)", + p->virtual_size * + nand_info->writesize / 1024, + p->start_sector * + nand_info->writesize / 1024, p->name); + else + sprintf(buffer + j, ",%uK@%uK", + p->virtual_size * + nand_info->writesize / 1024, + p->start_sector * + nand_info->writesize / 1024); + + j += strlen(buffer + j); + } + if (strlen(buffer) >= size) + break; + p++; + } + + memcpy(output, buffer, (j < size ? j + 1 : size)); + free(pt); + return 1; +} + +void tegra_partition_init(void) +{ + nvtegra_parttable_t *pt; + nvtegra_partinfo_t *partinfo; + + // parse nvidia partition table + pt = malloc(sizeof(nvtegra_parttable_t)); + if (!pt) { + printf("%s: Error calling malloc(%d)\n", __FUNCTION__, + sizeof(nvtegra_parttable_t)); + return; + } + //copy partition information to global data + if (!nvtegra_read_partition_table(pt)) { + free(pt); + return; + } + + if (nvtegra_find_partition(pt, "ENV", &partinfo)) { + gd->env_offset = + partinfo->start_sector * nand_info->writesize; +#if DEBUG > 1 + printf + ("\nPart:\tID=%u\tNAME=%s\tNAME2=%s\n\t\tTYPE=%u\tALLOC_POLICY=%u\tFS_TYPE=%u\n\t\tVIRTUAL START SEC=%u\tVIRTUAL SIZE=%u\n", + partinfo->id, partinfo->name, partinfo->name2, + partinfo->type, partinfo->allocation_policy, + partinfo->filesystem_type, partinfo->virtual_start_sector, + partinfo->virtual_size); + printf("\t\tSTART SEC=%u\tEND SEC=%u\tTOTAL=%u\n", + partinfo->start_sector, partinfo->end_sector, + partinfo->end_sector + 1 - partinfo->start_sector); +#endif + } + + if (nvtegra_find_partition(pt, "ARG", &partinfo)) { + gd->conf_blk_offset = + partinfo->start_sector * nand_info->writesize; +#if DEBUG > 1 + printf + ("\nPart:\tID=%u\tNAME=%s\tNAME2=%s\n\t\tTYPE=%u\tALLOC_POLICY=%u\tFS_TYPE=%u\n\t\tVIRTUAL START SEC=%u\tVIRTUAL SIZE=%u\n", + partinfo->id, partinfo->name, partinfo->name2, + partinfo->type, partinfo->allocation_policy, + partinfo->filesystem_type, partinfo->virtual_start_sector, + partinfo->virtual_size); + printf("\t\tSTART SEC=%u\tEND SEC=%u\tTOTAL=%u\n", + partinfo->start_sector, partinfo->end_sector, + partinfo->end_sector + 1 - partinfo->start_sector); +#endif + } + + if (nvtegra_find_partition(pt, "LNX", &partinfo)) { + gd->kernel_offset = + partinfo->start_sector * nand_info->writesize; +#if DEBUG > 1 + printf + ("\nPart:\tID=%u\tNAME=%s\tNAME2=%s\n\t\tTYPE=%u\tALLOC_POLICY=%u\tFS_TYPE=%u\n\t\tVIRTUAL START SEC=%u\tVIRTUAL SIZE=%u\n", + partinfo->id, partinfo->name, partinfo->name2, + partinfo->type, partinfo->allocation_policy, + partinfo->filesystem_type, partinfo->virtual_start_sector, + partinfo->virtual_size); + printf("\t\tSTART SEC=%u\tEND SEC=%u\tTOTAL=%u\n", + partinfo->start_sector, partinfo->end_sector, + partinfo->end_sector + 1 - partinfo->start_sector); +#endif + } + + if (nvtegra_find_partition(pt, "USR", &partinfo)) { +#if DEBUG > 1 + printf + ("\nPart:\tID=%u\tNAME=%s\tNAME2=%s\n\t\tTYPE=%u\tALLOC_POLICY=%u\tFS_TYPE=%u\n\t\tVIRTUAL START SEC=%u\tVIRTUAL SIZE=%u\n", + partinfo->id, partinfo->name, partinfo->name2, + partinfo->type, partinfo->allocation_policy, + partinfo->filesystem_type, partinfo->virtual_start_sector, + partinfo->virtual_size); + printf("\t\tSTART SEC=%u\tEND SEC=%u\tTOTAL=%u\n", + partinfo->start_sector, partinfo->end_sector, + partinfo->end_sector + 1 - partinfo->start_sector); +#endif + } +#if DEBUG > 1 + printf("gd->env_offset=%u\n", gd->env_offset); + printf("gd->conf_blk_offset=%u\n", gd->conf_blk_offset); + printf("gd->kernel_offset=%u\n", gd->kernel_offset); +#endif + +#if DEBUG > 0 + nvtegra_print_partition_table(pt); +#endif + + free(pt); +} + diff --git a/board/toradex/common/tegra2_partitions.h b/board/toradex/common/tegra2_partitions.h new file mode 100644 index 00000000000..504cd1a5cf4 --- /dev/null +++ b/board/toradex/common/tegra2_partitions.h @@ -0,0 +1,38 @@ +#ifndef _TEGRA2_PARTITIONS_H_ +#define _TEGRA2_PARTITIONS_H_ + +#define TEGRA_MAX_PARTITIONS 24 + +typedef struct { + unsigned id; + char name[4]; + unsigned allocation_policy; + unsigned _unknown1[2]; + char name2[4]; + unsigned filesystem_type; + unsigned _unknown2[3]; + unsigned virtual_start_sector; + unsigned _unknown3; + unsigned virtual_size; + unsigned _unknown4; + unsigned start_sector; + unsigned _unknown5; + unsigned end_sector; + unsigned _unknown6[2]; + unsigned type; +} __attribute__ ((packed)) nvtegra_partinfo_t; + +typedef struct { + unsigned _unknown[18]; + nvtegra_partinfo_t partinfo[TEGRA_MAX_PARTITIONS]; +} __attribute__ ((packed)) nvtegra_parttable_t; + +void nvtegra_print_partition_table(nvtegra_parttable_t * pt); +int nvtegra_read_partition_table(nvtegra_parttable_t * pt); +int nvtegra_find_partition(nvtegra_parttable_t * pt, const char *name, + nvtegra_partinfo_t ** partinfo); +int nvtegra_mtdparts_string(char *output, int size); +void tegra_partition_init(void); + +#endif /* _TEGRA2_PARTITIONS_H_ */ + -- cgit v1.2.3