summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-10-18 12:49:29 +0200
committertrdx <trdx@vm_one.(none)>2012-10-18 18:24:15 +0200
commit81a55464071d5079df38f81921ff6db2113d923d (patch)
tree90343c6b35b0d43f20b82026a5e29f57c702670e
parent22e3ede7d034616be39c48656a16d96fbfb27f43 (diff)
colibri_t20: nand: integrate NVIDIA partition table parsing
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.
-rw-r--r--arch/arm/include/asm/arch-tegra/tegra.h5
-rw-r--r--arch/arm/lib/board.c8
-rw-r--r--board/toradex/common/Makefile1
-rw-r--r--board/toradex/common/tegra2_partitions.c342
-rw-r--r--board/toradex/common/tegra2_partitions.h38
5 files changed, 394 insertions, 0 deletions
diff --git a/arch/arm/include/asm/arch-tegra/tegra.h b/arch/arm/include/asm/arch-tegra/tegra.h
index c51a8014e0..db00ebf167 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 10ef2c8128..c6a12463e0 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 <i2c.h>
#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 e3a6dbf938..8d160d50e9 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 0000000000..b8d8bf29ba
--- /dev/null
+++ b/board/toradex/common/tegra2_partitions.c
@@ -0,0 +1,342 @@
+#include <common.h>
+#include <asm/io.h>
+#include <nand.h>
+#include <malloc.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch-tegra/ap20.h>
+#include <asm/errno.h>
+#include <fdt_decode.h>
+#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 0000000000..504cd1a5cf
--- /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_ */
+