summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-10-18 12:49:29 +0200
committerMax Krummenacher <max.krummenacher@toradex.com>2012-10-18 19:17:50 +0200
commitcac4ae7a91a4872dd67084cb244f68446a4b39b8 (patch)
tree90343c6b35b0d43f20b82026a5e29f57c702670e /board
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.
Diffstat (limited to 'board')
-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
3 files changed, 381 insertions, 0 deletions
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_ */
+