[PATCH v2 3/3] rockchip: cmd: add rkmtd command

Johan Jonker jbx6244 at gmail.com
Thu Sep 28 20:51:18 CEST 2023


The command rkmtd creates a virtual block device to transfer
Rockchip boot block data to and from NAND with block orientated
tools like "ums" and "rockusb".

It uses the Rockchip MTD driver to scan for boot blocks and copies
data from the first block in a GPT formated virtual disk.
Data must be written in U-boot "idbloader.img" format and start at
partition "loader1" offset 64. The data header is parsed
for length and offset. When the last sector is received
it erases up to 5 erase blocks on NAND and writes bootblocks
in a pattern depending on the NAND ID. Data is then verified.
When a block turns out bad the block header is discarded.

Signed-off-by: Johan Jonker <jbx6244 at gmail.com>
---

Changed V2:
  rename to rkmtd
  change function call
  restyle
---
 cmd/Kconfig  |    7 +
 cmd/Makefile |    1 +
 cmd/rkmtd.c  | 1432 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1440 insertions(+)
 create mode 100644 cmd/rkmtd.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2d6e5f993f04..4ce6123b5017 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1553,6 +1553,13 @@ config CMD_USB_SDP
 	  Enables the command "sdp" which is used to have U-Boot emulating the
 	  Serial Download Protocol (SDP) via USB.

+config CMD_RKMTD
+	bool "rkmtd"
+	help
+	  Enable the command "rkmtd" to create a virtual block device to transfer
+	  Rockchip boot block data to and from NAND with block orientated tools
+	  like "ums" and "rockusb".
+
 config CMD_ROCKUSB
 	bool "rockusb"
 	depends on USB_FUNCTION_ROCKUSB
diff --git a/cmd/Makefile b/cmd/Makefile
index 9f8c0b058bea..dfada2306026 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_CMD_REISER) += reiser.o
 obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o
 obj-$(CONFIG_CMD_RNG) += rng.o
 obj-$(CONFIG_CMD_KASLRSEED) += kaslrseed.o
+obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
 obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o
 obj-$(CONFIG_CMD_RTC) += rtc.o
 obj-$(CONFIG_SANDBOX) += host.o
diff --git a/cmd/rkmtd.c b/cmd/rkmtd.c
new file mode 100644
index 000000000000..d6f9dd3d0952
--- /dev/null
+++ b/cmd/rkmtd.c
@@ -0,0 +1,1432 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) 2023 Johan Jonker <jbx6244 at gmail.com>
+ */
+
+#include <blk.h>
+#include <command.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+#include <efi_api.h>
+#include <nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <memalign.h>
+#include <part.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <u-boot/crc.h>
+
+#define LBA			64 + 512 + 33
+
+#define RK_TAG			0xFCDC8C3B
+#define NFC_SYS_DATA_SIZE	4
+
+struct nand_para_info {
+	u8 id_bytes;
+	u8 nand_id[6];
+	u8 vendor;
+	u8 die_per_chip;
+	u8 sec_per_page;
+	u16 page_per_blk;
+	u8 cell;
+	u8 plane_per_die;
+	u16 blk_per_plane;
+	u16 operation_opt;
+	u8 lsb_mode;
+	u8 read_retry_mode;
+	u8 ecc_bits;
+	u8 access_freq;
+	u8 opt_mode;
+	u8 die_gap;
+	u8 bad_block_mode;
+	u8 multi_plane_mode;
+	u8 slc_mode;
+	u8 reserved[5];
+};
+
+struct bootblk {
+	int blk;
+	int boot_size;
+	int offset;
+};
+
+struct rkmtd_dev {
+	struct blk_desc *desc;
+	char *label;
+	legacy_mbr *mbr;
+	gpt_header *gpt_h;
+	gpt_header *gpt_h2;
+	gpt_entry *gpt_e;
+	char *check;
+	char *idb;
+	char *str;
+	char uuid_part_str[UUID_STR_LEN + 1];
+	char uuid_disk_str[UUID_STR_LEN + 1];
+	char *datbuf;
+	char *oobbuf;
+	struct mtd_info *mtd;
+	struct nand_para_info *info;
+	u16 page_table[512];
+	u32 idb_need_write_back;
+	struct bootblk idblock[5];
+	u32 blk_counter;
+	u32 boot_blks;
+	u32 offset;
+	u32 boot_size;
+};
+
+struct sector0 {
+	u32 magic;
+	u8  reserved[4];
+	u32 rc4_flag;
+	u16 boot_code1_offset;
+	u16 boot_code2_offset;
+	u8  reserved1[490];
+	u16 flash_data_size;
+	u16 flash_boot_size;
+	u8  reserved2[2];
+} __packed;
+
+struct rk_nfc_nand_chip {
+	struct nand_chip chip;
+
+	u16 boot_blks;
+	u16 metadata_size;
+	u32 boot_ecc;
+	u32 timing;
+
+	u8 nsels;
+	u8 sels[0];
+	/* Nothing after this field. */
+};
+
+struct nand_para_info nand_para_tbl[] = {
+	{6, {0x2c, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16,  256, 2, 2, 2048, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x44, 0x44, 0x4b, 0xa9, 0x00}, 4, 1, 16,  256, 2, 2, 1064, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 4, 1,  8,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x88, 0x04, 0x4b, 0xa9, 0x00}, 4, 1, 16,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0xa8, 0x05, 0xcb, 0xa9, 0x00}, 4, 2, 16,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x68, 0x04, 0x46, 0x89, 0x00}, 4, 1,  8,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x48, 0x04, 0x4a, 0xa5, 0x00}, 4, 1,  8,  256, 2, 2, 1024, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32,  512, 2, 2, 1024, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x64, 0x54, 0xa9, 0x00}, 4, 1, 32,  512, 2, 2, 1024, 0x01df,  4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 4, 1,  8,  128, 2, 2, 4096, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x48, 0x04, 0x46, 0x85, 0x00}, 4, 1,  8,  256, 2, 2, 1024, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x88, 0x05, 0xc6, 0x89, 0x00}, 4, 2,  8,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 4, 1, 16,  256, 2, 2, 2048, 0x011f,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x68, 0x00, 0x27, 0xa9, 0x00}, 4, 1, 16,  128, 1, 2, 2048, 0x011f,  0,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x64, 0x64, 0x56, 0xa5, 0x00}, 4, 1, 24,  512, 2, 2,  700, 0x01df,  4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0x84, 0xc5, 0x4b, 0xa9, 0x00}, 4, 2, 16,  256, 2, 2, 2048, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0xd5, 0xd1, 0xa6, 0x68, 0x00}, 4, 2,  8,   64, 1, 2, 2048, 0x0117,  0,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0xdc, 0x90, 0xa6, 0x54, 0x00}, 4, 1,  8,   64, 1, 2, 1024, 0x0117,  0,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x64, 0x64, 0x54, 0xa4, 0x00}, 4, 1, 32,  512, 2, 1, 1024, 0x01df,  4, 18, 60, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32,  512, 2, 1, 2184, 0x05c7,  5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x64, 0x44, 0x32, 0xa5, 0x00}, 4, 1, 32,  512, 2, 1, 1048, 0x05c7,  5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x64, 0x64, 0x3c, 0xa5, 0x00}, 4, 1, 32,  512, 2, 1, 1044, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x44, 0x32, 0xaa, 0x00}, 4, 1, 32,  512, 2, 1, 2184, 0x05c7,  5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x44, 0x34, 0xaa, 0x00}, 4, 1, 32,  512, 2, 1, 2184, 0x05c7,  5, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0xc4, 0x34, 0xaa, 0x00}, 4, 1, 32,  512, 2, 1, 2184, 0x05c7,  5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x44, 0x34, 0xa4, 0x00}, 4, 1, 32,  512, 2, 1, 2184, 0x05c7,  5, 19, 60, 32, 1, 0, 1, 0, 1, {0, 0, 0, 0, 0}},
+	{5, {0x2c, 0x84, 0x64, 0x3c, 0xa9, 0x00}, 4, 1, 32,  512, 2, 2, 1024, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x2c, 0xa4, 0x64, 0x32, 0xaa, 0x04}, 4, 1, 32, 1024, 2, 1, 2192, 0x05c7, 10, 19, 60, 32, 1, 0, 4, 0, 1, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x94, 0xd2, 0x04, 0x43}, 2, 1, 16,  256, 2, 2, 2048, 0x01d9,  1,  1, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3}, 2, 1, 16,  256, 2, 2, 1024, 0x01d9,  1,  2, 40, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd7, 0x94, 0x91, 0x60, 0x44}, 2, 1, 16,  256, 2, 2, 1046, 0x01d9,  1,  3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16,  256, 2, 2, 2090, 0x01d9,  1,  4, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x94, 0xeb, 0x74, 0x44}, 2, 1, 32,  256, 2, 2, 1066, 0x01d9,  1,  7, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd5, 0x94, 0xda, 0x74, 0xc4}, 2, 1, 16,  256, 2, 2,  530, 0x01d9,  1,  3, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd7, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16,  256, 2, 2, 1024, 0x0119,  1,  0, 24, 32, 4, 0, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a}, 2, 1, 32,  256, 2, 2, 1060, 0x01d9,  2,  5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd7, 0x14, 0x9e, 0x34, 0x4a}, 2, 1, 16,  256, 2, 2, 1056, 0x01d9,  2,  5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x94, 0xa7, 0x42, 0x48}, 2, 1, 32,  256, 2, 2, 1060, 0x01d9,  2,  5, 40, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xde, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32,  256, 2, 2, 1056, 0x01d9,  2,  6, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0x3a, 0x14, 0xab, 0x42, 0x4a}, 2, 1, 32,  256, 2, 2, 2092, 0x01d9,  2,  5, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0xd5, 0x94, 0x9a, 0x74, 0x42}, 2, 1, 16,  256, 2, 1, 1024, 0x0111,  1,  0, 24, 32, 4, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xad, 0x3a, 0x14, 0x03, 0x08, 0x50}, 2, 1, 32,  388, 2, 2, 1362, 0x01d9,  9,  8, 40, 32, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x64, 0x44, 0x4b, 0xa9, 0x00}, 7, 1, 16,  256, 2, 2, 2048, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x84}, 7, 1, 16,  256, 2, 2, 2048, 0x01df,  3, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x88, 0x24, 0x4b, 0xa9, 0x00}, 7, 1, 16,  256, 2, 2, 2048, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x68, 0x24, 0x4a, 0xa9, 0x00}, 7, 1,  8,  256, 2, 2, 2048, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x68, 0x04, 0x4a, 0xa9, 0x00}, 7, 1,  8,  256, 2, 2, 2048, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0xd7, 0x94, 0x3e, 0x84, 0x00}, 7, 1,  8,  256, 2, 2, 2048, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x68, 0x04, 0x46, 0xa9, 0x00}, 7, 1,  8,  256, 2, 2, 2048, 0x0117,  1,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x89, 0x64, 0x64, 0x3c, 0xa1, 0x00}, 7, 1, 32,  512, 2, 1, 1024, 0x01c7,  4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{5, {0x89, 0x84, 0x64, 0x3c, 0xa5, 0x00}, 7, 1, 32,  512, 2, 2, 1024, 0x01c7,  4, 17, 40, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x89, 0x88, 0x24, 0x3b, 0xa9, 0x00}, 7, 1, 16,  192, 2, 2, 2048, 0x0117, 12,  0, 24, 32, 1, 0, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32,  256, 2, 1, 1060, 0x05c1,  2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x84, 0x93, 0x72, 0x57}, 1, 1, 32,  256, 2, 1, 2092, 0x05c1,  2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0x3a, 0x85, 0x93, 0x76, 0x57}, 1, 2, 32,  256, 2, 1, 2092, 0x05e1,  2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd5, 0x84, 0x32, 0x72, 0x56}, 1, 1, 16,  128, 2, 1, 2056, 0x05c1,  2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56}, 1, 1, 16,  128, 2, 2, 2058, 0x05d1,  2, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x94, 0x82, 0x76, 0x56}, 1, 1, 16,  256, 2, 2, 2062, 0x05d1,  1, 33, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x50}, 1, 1, 32,  256, 2, 2, 1066, 0x05d9,  2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0x3a, 0x95, 0x93, 0x7a, 0x50}, 1, 2, 32,  256, 2, 2, 1066, 0x05d9,  2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd7, 0x94, 0x32, 0x76, 0x55}, 1, 1, 16,  128, 2, 2, 2050, 0x0191,  2,  0, 24, 32, 1, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x57}, 1, 1, 32,  256, 2, 2, 1058, 0x05d9,  2, 33, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x50}, 1, 1, 32,  256, 2, 1, 1060, 0x05c1,  2, 34, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32,  256, 2, 2, 1074, 0x05d9,  2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0x3a, 0x94, 0x93, 0x76, 0x51}, 1, 1, 32,  256, 2, 2, 2106, 0x05d9,  2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xd7, 0x84, 0x93, 0x72, 0x51}, 1, 1, 32,  256, 2, 1, 1056, 0x05d9,  2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x98, 0xde, 0x94, 0x93, 0x76, 0xd1}, 1, 1, 32,  256, 2, 2, 1074, 0x05d9,  2, 35, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x57}, 8, 1, 32,  256, 2, 2, 1058, 0x05d9,  2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32,  256, 2, 1, 1060, 0x05c1,  2, 66, 40, 32, 2, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0x56}, 8, 1, 16,  256, 2, 2, 2082, 0x01d9,  1, 65, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x50}, 8, 1, 32,  256, 2, 2, 1066, 0x05d9,  2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xd7, 0x84, 0x93, 0x72, 0x50}, 8, 1, 32,  256, 2, 1, 1060, 0x05c1,  2, 67, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0xa4, 0x82, 0x76, 0xd7}, 8, 1, 16,  256, 2, 2, 2090, 0x04d9,  1, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x84, 0x93, 0x72, 0x57}, 8, 1, 32,  256, 2, 1, 2092, 0x05c1,  2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0x3a, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32,  256, 2, 2, 2106, 0x01d9,  2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x94, 0x93, 0x76, 0x51}, 8, 1, 32,  256, 2, 2, 1074, 0x01d9,  2, 68, 40, 32, 3, 1, 4, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0x3a, 0xa4, 0x93, 0x7a, 0x50}, 8, 1, 32,  256, 2, 2, 2138, 0x05d9,  2,  0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x94, 0x82, 0x76, 0x56}, 8, 1, 16,  256, 2, 2, 2062, 0x01d9,  1,  0, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0x45, 0xde, 0x94, 0x93, 0x76, 0xd7}, 8, 1, 32,  256, 2, 2, 1058, 0x05d9,  2, 66, 40, 32, 3, 1, 1, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xd7, 0x94, 0x7e, 0x64, 0x44}, 0, 1, 16,  128, 2, 2, 2048, 0x01d9,  2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xde, 0xd5, 0x7e, 0x68, 0x44}, 0, 2, 16,  128, 2, 2, 2048, 0x01f9,  2, 49, 60, 36, 3, 0, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xd7, 0x94, 0x7a, 0x54, 0x43}, 0, 1, 16,  128, 2, 2, 2076, 0x0199,  2,  0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xde, 0xd5, 0x7a, 0x58, 0x43}, 0, 2, 16,  128, 2, 2, 2076, 0x01b9,  2,  0, 40, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xd5, 0x94, 0x76, 0x54, 0x43}, 0, 1, 16,  128, 2, 2, 1038, 0x0119,  2,  0, 24, 36, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xd7, 0x14, 0x76, 0x54, 0xc2}, 0, 1, 16,  128, 2, 2, 2076, 0x0491,  2,  0, 24, 40, 3, 1, 3, 0, 0, {0, 0, 0, 0, 0}},
+	{6, {0xec, 0xde, 0x94, 0xc3, 0xa4, 0xca}, 0, 1, 32,  792, 2, 1,  688, 0x04c1, 11, 50, 40, 32, 3, 1, 1, 0, 1, {0, 0, 0, 0, 0}},
+};
+
+void rkmtd_rc4(u8 *buf, u32 len)
+{
+	u8 S[256], K[256], temp;
+	u32 i, j, t, x;
+	u8 key[16] = { 124, 78, 3, 4, 85, 5, 9, 7, 45, 44, 123, 56, 23, 13, 23, 17};
+
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		S[i] = (u8)i;
+		j &= 0x0f;
+		K[i] = key[j];
+		j++;
+	}
+
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + K[i]) % 256;
+		temp = S[i];
+		S[i] = S[j];
+		S[j] = temp;
+	}
+
+	i = 0;
+	j = 0;
+	for (x = 0; x < len; x++) {
+		i = (i + 1) % 256;
+		j = (j + S[i]) % 256;
+		temp = S[i];
+		S[i] = S[j];
+		S[j] = temp;
+		t = (S[i] + (S[j] % 256)) % 256;
+		buf[x] = buf[x] ^ S[t];
+	}
+}
+
+static int rkmtd_write_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf)
+{
+	struct mtd_info *mtd = plat->mtd;
+	off &= ~(mtd->writesize - 1);
+	loff_t addr = (loff_t)off;
+	struct mtd_oob_ops ops;
+	int ret;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.datbuf = datbuf;
+	ops.oobbuf = oobbuf;
+	ops.len = mtd->writesize;
+	ops.ooblen = mtd->oobsize;
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ret = mtd_write_oob(mtd, addr, &ops);
+	if (ret < 0) {
+		debug("Error (%d) writing page %08lx\n", ret, off);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int rkmtd_read_oob(struct rkmtd_dev *plat, ulong off, u_char *datbuf, u_char *oobbuf)
+{
+	struct mtd_info *mtd = plat->mtd;
+	off &= ~(mtd->writesize - 1);
+	loff_t addr = (loff_t)off;
+	struct mtd_oob_ops ops;
+	int ret;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.datbuf = datbuf;
+	ops.oobbuf = oobbuf;
+	ops.len = mtd->writesize;
+	ops.ooblen = mtd->oobsize;
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ret = mtd_read_oob(mtd, addr, &ops);
+	if (ret < 0) {
+		debug("Error (%d) reading page %08lx\n", ret, off);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int rkmtd_erase(struct rkmtd_dev *plat, ulong off)
+{
+	struct mtd_info *mtd = plat->mtd;
+	off &= ~(mtd->writesize - 1);
+	loff_t addr = (loff_t)off;
+	struct erase_info info;
+	int ret;
+
+	memset(&info, 0, sizeof(info));
+	info.mtd = mtd;
+	info.addr = addr;
+	info.len = mtd->erasesize;
+	info.scrub = 1;
+	ret = mtd_erase(mtd, &info);
+	if (ret) {
+		debug("Error (%d) erasing page %08lx\n", ret, off);
+		return 1;
+	}
+
+	return 0;
+}
+
+void rkmtd_scan_block(struct rkmtd_dev *plat)
+{
+	u32 blk;
+
+	plat->blk_counter = 0;
+
+	for (blk = 0; blk < plat->boot_blks; blk++) {
+		rkmtd_read_oob(plat, blk * plat->mtd->erasesize, plat->datbuf, plat->oobbuf);
+		if (*(u32 *)plat->datbuf == RK_TAG) {
+			struct sector0 *sec0 = (struct sector0 *)plat->datbuf;
+
+			rkmtd_rc4(plat->datbuf, 512);
+
+			plat->idblock[plat->blk_counter].blk = blk;
+			plat->idblock[plat->blk_counter].offset = sec0->boot_code1_offset;
+			plat->idblock[plat->blk_counter].boot_size = sec0->flash_boot_size;
+
+			debug("\nblk       : %d\n", plat->idblock[plat->blk_counter].blk);
+			debug("offset    : %d\n", plat->idblock[plat->blk_counter].offset);
+			debug("boot_size : %d\n", plat->idblock[plat->blk_counter].boot_size);
+
+			plat->blk_counter += 1;
+
+			if (plat->blk_counter >= ARRAY_SIZE(plat->idblock))
+				return;
+		}
+	}
+}
+
+void rkmtd_read_block(struct rkmtd_dev *plat, u32 idx, u8 *buf)
+{
+	ulong off = plat->idblock[idx].blk * plat->mtd->erasesize;
+	struct nand_chip *chip = mtd_to_nand(plat->mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int counter = 0;
+	u32 spare0 = 0;
+	u32 *p_spare;
+	int sector;
+	int page;
+
+	rkmtd_read_oob(plat, off,
+		       plat->datbuf, plat->oobbuf);
+
+	memcpy(buf, plat->datbuf, 2048);
+
+	while (counter < plat->idblock[idx].boot_size) {
+		if (spare0)
+			page = (plat->idblock[idx].offset + spare0) / 4;
+		else
+			page = (plat->idblock[idx].offset + counter) / 4;
+
+		rkmtd_read_oob(plat,
+			       off + page * plat->mtd->writesize,
+			       plat->datbuf, plat->oobbuf);
+
+		sector = plat->idblock[idx].offset + counter;
+
+		memcpy(&buf[(sector / 4) * 2048], plat->datbuf, 2048);
+
+		p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE];
+
+		spare0 = *p_spare;
+		if (spare0 == -1)
+			break;
+
+		counter += 4;
+	}
+}
+
+void rkmtd_write_block(struct rkmtd_dev *plat, u32 idx, u8 *buf)
+{
+	ulong off = plat->idblock[idx].blk * plat->mtd->erasesize;
+	struct nand_chip *chip = mtd_to_nand(plat->mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	int counter = 0;
+	u32 *p_spare;
+	int sector;
+	int page;
+	int j, w, r;
+
+	rkmtd_erase(plat, off);
+
+	memset(plat->datbuf, 0xff, plat->mtd->writesize);
+	memcpy(plat->datbuf, buf, 2048);
+	memset(plat->oobbuf, 0xff, plat->mtd->oobsize);
+
+	rkmtd_write_oob(plat, off,
+			plat->datbuf, plat->oobbuf);
+
+	while (counter < plat->idblock[idx].boot_size) {
+		sector = plat->idblock[idx].offset + counter;
+
+		memset(plat->datbuf, 0xff, plat->mtd->writesize);
+		memcpy(plat->datbuf, &buf[(sector / 4) * 2048], 2048);
+		memset(plat->oobbuf, 0xff, plat->mtd->oobsize);
+
+		p_spare = (u32 *)&plat->oobbuf[(ecc->steps - 1) * NFC_SYS_DATA_SIZE];
+
+		*p_spare = (plat->page_table[sector / 4 + 1] - 1) * 4;
+
+		page = plat->page_table[sector / 4];
+
+		rkmtd_write_oob(plat,
+				off + page * plat->mtd->writesize,
+				plat->datbuf, plat->oobbuf);
+
+		counter += 4;
+	}
+
+	memset(plat->check, 0xff, 512 * 512);
+	rkmtd_read_block(plat, idx, plat->check);
+
+	for (j = 0; j < 2048; j++) {
+		w = *(buf + j);
+		r = *(plat->check + j);
+
+		if (r != w)
+			goto dumpblock;
+	}
+
+	for (j = 0; j < (plat->idblock[idx].boot_size * 512); j++) {
+		w = *(buf + plat->idblock[idx].offset * 512 + j);
+		r = *(plat->check + plat->idblock[idx].offset * 512  + j);
+
+		if (r != w)
+			goto dumpblock;
+	}
+
+	debug("write OK\n");
+	return;
+
+dumpblock:
+	debug("write and check error:%x r=%x w=%x\n", j, r, w);
+
+	plat->idblock[idx].offset = 0;
+	plat->idblock[idx].boot_size = 0;
+
+	memset(plat->datbuf, 0xff, plat->mtd->writesize);
+	memset(plat->datbuf, 0, 2048);
+	memset(plat->oobbuf, 0xff, plat->mtd->oobsize);
+
+	rkmtd_write_oob(plat, off, plat->datbuf, plat->oobbuf);
+}
+
+ulong rkmtd_bread(struct udevice *udev, lbaint_t start,
+		  lbaint_t blkcnt, void *dst)
+{
+	struct blk_desc *block_dev = dev_get_uclass_plat(udev);
+	struct udevice *parent_dev = dev_get_parent(udev);
+	struct rkmtd_dev *plat = dev_get_plat(parent_dev);
+	char *buf = dst;
+	int i;
+
+	if (blkcnt == 0)
+		return 0;
+
+	if (start > (block_dev->lba - 1) ||
+	    (start + blkcnt) > block_dev->lba)
+		return 0;
+
+	memset(dst, 0xff, blkcnt * block_dev->blksz);
+
+	for (i = start; i < (start + blkcnt); i++) {
+		if (i == 0)  {
+			debug("mbr     : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       plat->mbr, sizeof(legacy_mbr));
+		} else if (i == 1) {
+			debug("gpt_h   : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       plat->gpt_h, sizeof(gpt_header));
+		} else if (i == (block_dev->lba - 1)) {
+			debug("gpt_h2  : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       plat->gpt_h2, sizeof(gpt_header));
+		} else if (i == 2 || i == (block_dev->lba - 33)) {
+			debug("gpt_e   : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       plat->gpt_e, sizeof(gpt_entry));
+		} else if (i >= 64 && i < (block_dev->lba - 33)) {
+			debug("rd      : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       &plat->idb[(i - 64) * block_dev->blksz], block_dev->blksz);
+		}
+	}
+
+	return blkcnt;
+}
+
+ulong rkmtd_bwrite(struct udevice *udev, lbaint_t start,
+		   lbaint_t blkcnt, const void *src)
+{
+	struct blk_desc *block_dev = dev_get_uclass_plat(udev);
+	struct udevice *parent_dev = dev_get_parent(udev);
+	struct rkmtd_dev *plat = dev_get_plat(parent_dev);
+	int i, j;
+
+	if (blkcnt == 0)
+		return 0;
+
+	if (start > (block_dev->lba - 1) ||
+	    (start + blkcnt) > block_dev->lba)
+		return 0;
+
+	for (i = start; i < (start + blkcnt); i++) {
+		debug("wr      : %d\n", i);
+
+		if (i >= 64 && i < (block_dev->lba - 33)) {
+			if (i == 64) {
+				debug("first block\n");
+
+				plat->idb_need_write_back = 1;
+				memset(plat->idb, 0xff, 512 * 512);
+			}
+
+			if (plat->idb_need_write_back) {
+				char *buf = (char *)src;
+
+				memcpy(&plat->idb[(i - 64) * block_dev->blksz],
+				       &buf[(i - start) * block_dev->blksz],
+				       block_dev->blksz);
+
+				if (i == 64) {
+					memcpy(plat->check, plat->idb, 512);
+
+					if (*(u32 *)plat->check == RK_TAG) {
+						struct sector0 *sec0 = (struct sector0 *)plat->check;
+
+						rkmtd_rc4(plat->check, 512);
+
+						plat->offset = sec0->boot_code1_offset;
+						plat->boot_size = sec0->flash_boot_size;
+
+						if (plat->offset + plat->boot_size > 512) {
+							debug("max size limit\n");
+							plat->idb_need_write_back = 0;
+						}
+					} else {
+						debug("no IDB block found\n");
+						plat->idb_need_write_back = 0;
+					}
+				}
+
+				if (i == (64 + plat->offset + plat->boot_size - 1)) {
+					debug("last block\n");
+
+					plat->idb_need_write_back = 0;
+
+					if (!plat->blk_counter) {
+						plat->idblock[0].blk = 2;
+						plat->idblock[1].blk = 3;
+						plat->idblock[2].blk = 4;
+						plat->idblock[3].blk = 5;
+						plat->idblock[4].blk = 6;
+						plat->blk_counter = 5;
+					}
+
+					for (j = 0; j < plat->blk_counter; j++) {
+						if (plat->idblock[j].blk < plat->boot_blks) {
+							plat->idblock[j].offset = plat->offset;
+							plat->idblock[j].boot_size = plat->boot_size;
+							rkmtd_write_block(plat, j, plat->idb);
+						}
+					}
+
+					rkmtd_scan_block(plat);
+
+					memset(plat->idb, 0xff, 512 * 512);
+
+					if (plat->blk_counter)
+						rkmtd_read_block(plat, 0, plat->idb);
+				}
+			}
+		} else if (plat->idb_need_write_back) {
+			plat->idb_need_write_back = 0;
+
+			memset(plat->idb, 0xff, 512 * 512);
+
+			if (plat->blk_counter)
+				rkmtd_read_block(plat, 0, plat->idb);
+		}
+	}
+
+	return blkcnt;
+}
+
+static const struct blk_ops rkmtd_blk_ops = {
+	.read	= rkmtd_bread,
+	.write	= rkmtd_bwrite,
+};
+
+U_BOOT_DRIVER(rkmtd_blk) = {
+	.name		= "rkmtd_blk",
+	.id		= UCLASS_BLK,
+	.ops		= &rkmtd_blk_ops,
+};
+
+void rkmtd_build_page_table(struct rkmtd_dev *plat, u32 lsb_mode)
+{
+	u32 counter;
+	u32 counter2;
+
+	switch (lsb_mode) {
+	case 0:
+		counter = 0;
+		do {
+			u16 val = counter;
+
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 1:
+		counter = 0;
+		do {
+			u16 val = counter;
+
+			if (counter > 3) {
+				u16 offset;
+
+				if (counter & 1)
+					offset = 3;
+				else
+					offset = 2;
+				val = 2 * counter - offset;
+			}
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 2:
+		counter = 0;
+		do {
+			u16 val = counter;
+
+			if (counter > 1)
+				val = 2 * counter - 1;
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 3:
+		counter = 0;
+		do {
+			u16 val = counter;
+
+			if (counter > 5) {
+				u16 offset;
+
+				if (counter & 1)
+					offset = 5;
+				else
+					offset = 4;
+				val = 2 * counter - offset;
+			}
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 4:
+		counter = 8;
+		plat->page_table[0] = 0;
+		plat->page_table[1] = 1;
+		plat->page_table[2] = 2;
+		plat->page_table[3] = 3;
+		plat->page_table[4] = 4;
+		plat->page_table[5] = 5;
+		plat->page_table[6] = 7;
+		plat->page_table[7] = 8;
+		do {
+			u32 offset;
+			u32 val;
+
+			if (counter & 1)
+				offset = 7;
+			else
+				offset = 6;
+			val = 2 * counter - offset;
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 5:
+		counter = 0;
+		counter2 = 16;
+		do {
+			u16 val = counter;
+
+			plat->page_table[counter++] = val;
+		} while (counter != 16);
+		do {
+			plat->page_table[counter++] = counter2;
+			counter2 = counter2 + 2;
+		} while (counter != 512);
+		break;
+	case 6:
+		counter = 0;
+		counter2 = 0;
+		do {
+			u16 val = counter;
+
+			if (counter > 5) {
+				u16 offset;
+
+				if (counter & 1)
+					offset = 12;
+				else
+					offset = 10;
+				val = counter2 - offset;
+			}
+			plat->page_table[counter++] = val;
+			counter2 = counter2 + 3;
+		} while (counter != 512);
+		break;
+	case 9:
+		counter = 3;
+		counter2 = 3;
+		plat->page_table[0] = 0;
+		plat->page_table[1] = 1;
+		plat->page_table[2] = 2;
+		do {
+			plat->page_table[counter++] = counter2;
+			counter2 = counter2 + 2;
+		} while (counter != 512);
+		break;
+	case 10:
+		counter = 0;
+		counter2 = 63;
+		do {
+			u16 val = counter;
+
+			plat->page_table[counter++] = val;
+		} while (counter != 63);
+		do {
+			plat->page_table[counter++] = counter2;
+			counter2 = counter2 + 2;
+		} while (counter != 512);
+		break;
+	case 11:
+		counter = 0;
+		do {
+			u16 val = counter;
+
+			plat->page_table[counter++] = val;
+		} while (counter != 8);
+		do {
+			u32 offset;
+			u32 val;
+
+			if (counter & 1)
+				offset = 7;
+			else
+				offset = 6;
+			val = 2 * counter - offset;
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	case 12:
+		counter = 4;
+		plat->page_table[0] = 0;
+		plat->page_table[1] = 1;
+		plat->page_table[2] = 2;
+		plat->page_table[3] = 3;
+		do {
+			u32 val = counter - 1 + (counter >> 1);
+
+			plat->page_table[counter++] = val;
+		} while (counter != 512);
+		break;
+	}
+}
+
+static inline u32 efi_crc32(const void *buf, u32 len)
+{
+	return crc32(0, buf, len);
+}
+
+int rkmtd_init_plat(struct udevice *dev)
+{
+	static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
+	struct rkmtd_dev *plat = dev_get_plat(dev);
+	size_t efiname_len, dosname_len;
+	uchar name[] = "loader1";
+	u32 calc_crc32;
+	int k;
+
+	if (!plat)
+		debug("no plat ptr!\n");
+
+	gen_rand_uuid_str(plat->uuid_disk_str, UUID_STR_FORMAT_GUID);
+	gen_rand_uuid_str(plat->uuid_part_str, UUID_STR_FORMAT_GUID);
+
+	debug("uuid_part_str          : %s\n", plat->uuid_part_str);
+	debug("uuid_disk_str          : %s\n", plat->uuid_disk_str);
+
+	plat->idb = malloc_cache_aligned(512 * 512);
+
+	if (!plat->idb) {
+		debug("idb malloc failed\n");
+		return -ENOMEM;
+	}
+
+	plat->check = malloc_cache_aligned(512 * 512);
+
+	if (!plat->check) {
+		debug("check malloc failed\n");
+		free(plat->idb);
+		return -ENOMEM;
+	}
+
+	plat->mbr = malloc(sizeof(legacy_mbr));
+
+	if (!plat->mbr) {
+		debug("mbr malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		return -ENOMEM;
+	}
+
+	plat->gpt_e = malloc(sizeof(gpt_entry));
+
+	if (!plat->gpt_e) {
+		debug("gpt_e malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		free(plat->mbr);
+		return -ENOMEM;
+	}
+
+	plat->gpt_h = malloc(sizeof(gpt_header));
+
+	if (!plat->gpt_h) {
+		debug("gpt_h malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		free(plat->mbr);
+		free(plat->gpt_e);
+		return -ENOMEM;
+	}
+
+	plat->gpt_h2 = malloc(sizeof(gpt_header));
+
+	if (!plat->gpt_h2) {
+		debug("gpt_h2 malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		free(plat->mbr);
+		free(plat->gpt_e);
+		free(plat->gpt_h);
+		return -ENOMEM;
+	}
+
+	/* Init idb */
+	memset(plat->idb, 0xff, 512 * 512);
+
+	/* Init mbr */
+	memset((char *)plat->mbr, 0, sizeof(legacy_mbr));
+
+	plat->mbr->signature = MSDOS_MBR_SIGNATURE;
+	plat->mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT;
+	plat->mbr->partition_record[0].start_sect = 1;
+	plat->mbr->partition_record[0].nr_sects = LBA - 1;
+
+	/* Init gpt_e */
+	memset(plat->gpt_e, 0, sizeof(gpt_entry));
+
+	plat->gpt_e->starting_lba = cpu_to_le64(64);
+	plat->gpt_e->ending_lba = cpu_to_le64(LBA - 34);
+
+	debug("starting_lba           : %llu\n", le64_to_cpu(plat->gpt_e->starting_lba));
+	debug("ending_lba             : %llu\n", le64_to_cpu(plat->gpt_e->ending_lba));
+
+	memcpy(plat->gpt_e->partition_type_guid.b, &partition_basic_data_guid, 16);
+
+	uuid_str_to_bin(plat->uuid_part_str, plat->gpt_e->unique_partition_guid.b,
+			UUID_STR_FORMAT_GUID);
+
+	efiname_len = sizeof(plat->gpt_e->partition_name) / sizeof(efi_char16_t);
+	dosname_len = sizeof(name);
+
+	for (k = 0; k < min(dosname_len, efiname_len); k++)
+		plat->gpt_e->partition_name[k] = (efi_char16_t)(name[k]);
+
+	/* Init gpt_h */
+	memset(plat->gpt_h, 0, sizeof(gpt_header));
+
+	plat->gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE_UBOOT);
+	plat->gpt_h->revision = cpu_to_le32(GPT_HEADER_REVISION_V1);
+	plat->gpt_h->header_size = cpu_to_le32(sizeof(gpt_header));
+	plat->gpt_h->first_usable_lba = cpu_to_le64(64);
+	plat->gpt_h->last_usable_lba = cpu_to_le64(LBA - 34);
+	plat->gpt_h->num_partition_entries = cpu_to_le32(1);
+	plat->gpt_h->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry));
+
+	uuid_str_to_bin(plat->uuid_disk_str, plat->gpt_h->disk_guid.b,
+			UUID_STR_FORMAT_GUID);
+
+	plat->gpt_h->partition_entry_array_crc32 = 0;
+	calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_e,
+			       le32_to_cpu(plat->gpt_h->num_partition_entries) *
+			       le32_to_cpu(plat->gpt_h->sizeof_partition_entry));
+	plat->gpt_h->partition_entry_array_crc32 = cpu_to_le32(calc_crc32);
+
+	debug("partition crc32        : 0x%08x\n", calc_crc32);
+
+	plat->gpt_h->my_lba = cpu_to_le64(1);
+	plat->gpt_h->partition_entry_lba = cpu_to_le64(2);
+	plat->gpt_h->alternate_lba = cpu_to_le64(LBA - 1);
+
+	plat->gpt_h->header_crc32 = 0;
+	calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h,
+			       le32_to_cpu(plat->gpt_h->header_size));
+	plat->gpt_h->header_crc32 = cpu_to_le32(calc_crc32);
+
+	debug("header h1 crc32        : 0x%08x\n", calc_crc32);
+
+	/* Init gpt_h2 */
+	memcpy(plat->gpt_h2, plat->gpt_h, sizeof(gpt_header));
+
+	plat->gpt_h2->my_lba = cpu_to_le64(LBA - 1);
+	plat->gpt_h2->partition_entry_lba =
+		cpu_to_le64(le64_to_cpu(plat->gpt_h2->last_usable_lba) + 1);
+	plat->gpt_h2->alternate_lba = cpu_to_le64(1);
+
+	plat->gpt_h2->header_crc32 = 0;
+	calc_crc32 = efi_crc32((const unsigned char *)plat->gpt_h2,
+			       le32_to_cpu(plat->gpt_h2->header_size));
+	plat->gpt_h2->header_crc32 = cpu_to_le32(calc_crc32);
+
+	debug("header h2 crc32        : 0x%08x\n", calc_crc32);
+
+	part_init(plat->desc);
+
+	return 0;
+}
+
+static int rkmtd_bind(struct udevice *dev)
+{
+	struct rkmtd_dev *plat = dev_get_plat(dev);
+	struct udevice *bdev;
+	struct blk_desc *desc;
+	int ret;
+
+	ret = blk_create_devicef(dev, "rkmtd_blk", "blk", UCLASS_RKMTD,
+				 -1, 512, 0, &bdev);
+	if (ret)
+		return log_msg_ret("blk", ret);
+
+	desc = dev_get_uclass_plat(bdev);
+	desc->lba = LBA;
+	sprintf(desc->vendor, "0x%.4x", 0x2207);
+	memcpy(desc->product, "RKMTD", sizeof("RKMTD"));
+	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
+	plat->desc = desc;
+
+	return 0;
+}
+
+static inline struct rk_nfc_nand_chip *rk_nfc_to_rknand(struct nand_chip *chip)
+{
+	return container_of(chip, struct rk_nfc_nand_chip, chip);
+}
+
+static int rkmtd_attach_mtd(struct udevice *dev)
+{
+	struct rkmtd_dev *plat = dev_get_plat(dev);
+	struct rk_nfc_nand_chip *rknand;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	struct udevice *blk;
+	int ret;
+	u8 id[6];
+	int i, j;
+
+	/* Sanity check that rkmtd_bind() has been used */
+	ret = blk_find_from_parent(dev, &blk);
+	if (ret)
+		return ret;
+
+	mtd = get_nand_dev_by_index(0);
+	if (!mtd)
+		return -ENOSYS;
+
+	chip = mtd_to_nand(mtd);
+	rknand = rk_nfc_to_rknand(chip);
+	plat->boot_blks = rknand->boot_blks;
+	plat->mtd = mtd;
+
+	if (chip->select_chip)
+		chip->select_chip(mtd, 0);
+
+	nand_readid_op(chip, 0, id, 6);
+
+	if (chip->select_chip)
+		chip->select_chip(mtd, -1);
+
+	for (i = 0; i < ARRAY_SIZE(nand_para_tbl); i++) {
+		plat->info = (struct nand_para_info *)&nand_para_tbl[i];
+		for (j = 0; j < plat->info->id_bytes; j++) {
+			if (plat->info->nand_id[j] != id[j])
+				break;
+			if (j == plat->info->id_bytes - 1)
+				goto valid;
+		}
+	}
+
+	debug("no nand_para_info found\n");
+	return -ENODEV;
+valid:
+	debug("FLASH ID :");
+
+	for (j = 0; j < plat->info->id_bytes; j++)
+		debug(" %x", id[j]);
+
+	debug("\n");
+
+	rkmtd_build_page_table(plat, plat->info->lsb_mode);
+
+	plat->datbuf = malloc(mtd->writesize);
+	if (!plat->datbuf) {
+		debug("No memory for page buffer\n");
+		return -ENOMEM;
+	}
+
+	plat->oobbuf = malloc(mtd->oobsize);
+	if (!plat->oobbuf) {
+		debug("No memory for page buffer\n");
+		free(plat->datbuf);
+		return -ENOMEM;
+	}
+
+	debug("erasesize     %8d\n", mtd->erasesize);
+	debug("writesize     %8d\n", mtd->writesize);
+	debug("oobsize       %8d\n", mtd->oobsize);
+	debug("boot_blks     %8d\n", rknand->boot_blks);
+	debug("lsb_mode      %8d\n", plat->info->lsb_mode);
+
+	ret = rkmtd_init_plat(dev);
+	if (ret) {
+		debug("rkmtd_init_plat failed\n");
+		free(plat->datbuf);
+		free(plat->oobbuf);
+		return -ENOENT;
+	}
+
+	rkmtd_scan_block(plat);
+
+	memset(plat->idb, 0xff, 512 * 512);
+
+	if (plat->blk_counter)
+		rkmtd_read_block(plat, 0, plat->idb);
+
+	return 0;
+}
+
+int rkmtd_detach_mtd(struct udevice *dev)
+{
+	struct rkmtd_dev *plat = dev_get_plat(dev);
+	int ret;
+
+	free(plat->idb);
+	free(plat->check);
+	free(plat->mbr);
+	free(plat->gpt_e);
+	free(plat->gpt_h);
+	free(plat->gpt_h2);
+	free(plat->datbuf);
+	free(plat->oobbuf);
+
+	ret = device_remove(dev, DM_REMOVE_NORMAL);
+	if (ret)
+		return log_msg_ret("rem", ret);
+
+	ret = device_chld_unbind(dev, NULL);
+	if (ret)
+		return log_msg_ret("unb", ret);
+
+	return 0;
+}
+
+struct rkmtd_ops {
+	int (*attach_mtd)(struct udevice *dev);
+	int (*detach_mtd)(struct udevice *dev);
+};
+
+#define rkmtd_get_ops(dev)        ((struct rkmtd_ops *)(dev)->driver->ops)
+
+struct rkmtd_ops rkmtd_ops = {
+	.attach_mtd	= rkmtd_attach_mtd,
+	.detach_mtd	= rkmtd_detach_mtd,
+};
+
+U_BOOT_DRIVER(rkmtd_drv) = {
+	.name		= "rkmtd_drv",
+	.id		= UCLASS_RKMTD,
+	.ops		= &rkmtd_ops,
+	.bind		= rkmtd_bind,
+	.plat_auto	= sizeof(struct rkmtd_dev),
+};
+
+struct rkmtd_priv {
+	struct udevice *cur_dev;
+};
+
+struct udevice *rkmtd_get_cur_dev(void)
+{
+	struct uclass *uc = uclass_find(UCLASS_RKMTD);
+
+	if (uc) {
+		struct rkmtd_priv *priv = uclass_get_priv(uc);
+
+		return priv->cur_dev;
+	}
+
+	return NULL;
+}
+
+void rkmtd_set_cur_dev(struct udevice *dev)
+{
+	struct uclass *uc = uclass_find(UCLASS_RKMTD);
+
+	if (uc) {
+		struct rkmtd_priv *priv = uclass_get_priv(uc);
+
+		priv->cur_dev = dev;
+	}
+}
+
+struct udevice *rkmtd_find_by_label(const char *label)
+{
+	struct udevice *dev;
+	struct uclass *uc;
+
+	uclass_id_foreach_dev(UCLASS_RKMTD, dev, uc) {
+		struct rkmtd_dev *plat = dev_get_plat(dev);
+
+		if (plat->label && !strcmp(label, plat->label))
+			return dev;
+	}
+
+	return NULL;
+}
+
+int rkmtd_attach(struct udevice *dev)
+{
+	struct rkmtd_ops *ops = rkmtd_get_ops(dev);
+
+	if (!ops->attach_mtd)
+		return -ENOSYS;
+
+	return ops->attach_mtd(dev);
+}
+
+int rkmtd_detach(struct udevice *dev)
+{
+	struct rkmtd_ops *ops = rkmtd_get_ops(dev);
+
+	if (!ops->detach_mtd)
+		return -ENOSYS;
+
+	if (dev == rkmtd_get_cur_dev())
+		rkmtd_set_cur_dev(NULL);
+
+	return ops->detach_mtd(dev);
+}
+
+int rkmtd_create_device(const char *label, struct udevice **devp)
+{
+	char dev_name[30], *str, *label_new;
+	struct rkmtd_dev *plat;
+	struct udevice *dev, *blk;
+	int ret;
+
+	/* unbind any existing device with this label */
+	dev = rkmtd_find_by_label(label);
+	if (dev) {
+		ret = rkmtd_detach(dev);
+		if (ret)
+			return log_msg_ret("det", ret);
+
+		ret = device_unbind(dev);
+		if (ret)
+			return log_msg_ret("unb", ret);
+	}
+
+	snprintf(dev_name, sizeof(dev_name), "rkmtd-%s", label);
+	str = strdup(dev_name);
+	if (!str)
+		return -ENOMEM;
+
+	label_new = strdup(label);
+	if (!label_new) {
+		ret = -ENOMEM;
+		goto no_label;
+	}
+
+	ret = device_bind_driver(dm_root(), "rkmtd_drv", str, &dev);
+	if (ret)
+		goto no_dev;
+
+	device_set_name_alloced(dev);
+
+	if (!blk_find_from_parent(dev, &blk)) {
+		struct blk_desc *desc = dev_get_uclass_plat(blk);
+
+		desc->removable = true;
+	}
+
+	plat = dev_get_plat(dev);
+	plat->label = label_new;
+	*devp = dev;
+
+	return 0;
+
+no_dev:
+	free(label_new);
+no_label:
+	free(str);
+
+	return ret;
+}
+
+int rkmtd_create_attach_mtd(const char *label, struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = rkmtd_create_device(label, &dev);
+	if (ret)
+		return log_msg_ret("cre", ret);
+
+	ret = rkmtd_attach(dev);
+	if (ret) {
+		device_unbind(dev);
+		return log_msg_ret("att", ret);
+	}
+	*devp = dev;
+
+	return 0;
+}
+
+UCLASS_DRIVER(rkmtd) = {
+	.name		= "rkmtd",
+	.id		= UCLASS_RKMTD,
+	.post_bind	= dm_scan_fdt_dev,
+	.priv_auto	= sizeof(struct rkmtd_priv),
+};
+
+static int do_rkmtd_bind(struct cmd_tbl *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	struct udevice *dev;
+	const char *label;
+	int ret;
+
+	argc--;
+	argv++;
+
+	if (argc < 1)
+		return CMD_RET_USAGE;
+
+	if (argc > 1)
+		return CMD_RET_USAGE;
+
+	label = argv[0];
+	ret = rkmtd_create_attach_mtd(label, &dev);
+	if (ret) {
+		printf("Cannot create device / bind mtd\n");
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
+static struct udevice *parse_rkmtd_label(const char *label)
+{
+	struct udevice *dev;
+
+	dev = rkmtd_find_by_label(label);
+	if (!dev) {
+		int devnum;
+		char *ep;
+
+		devnum = hextoul(label, &ep);
+		if (*ep ||
+		    uclass_find_device_by_seq(UCLASS_RKMTD, devnum, &dev)) {
+			printf("No such device '%s'\n", label);
+			return NULL;
+		}
+	}
+
+	return dev;
+}
+
+static int do_rkmtd_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	struct udevice *dev;
+	const char *label;
+	int ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	label = argv[1];
+	dev = parse_rkmtd_label(label);
+	if (!dev)
+		return CMD_RET_FAILURE;
+
+	ret = rkmtd_detach(dev);
+	if (ret) {
+		printf("Cannot detach mtd\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = device_unbind(dev);
+	if (ret) {
+		printf("Cannot unbind device '%s'\n", dev->name);
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
+static void show_rkmtd_dev(struct udevice *dev)
+{
+	struct rkmtd_dev *plat = dev_get_plat(dev);
+	struct blk_desc *desc;
+	struct udevice *blk;
+	int ret;
+
+	printf("%3d ", dev_seq(dev));
+
+	ret = blk_get_from_parent(dev, &blk);
+	if (ret)
+		return;
+
+	desc = dev_get_uclass_plat(blk);
+	printf("%12lu %-15s\n", (unsigned long)desc->lba, plat->label);
+}
+
+static int do_rkmtd_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	struct udevice *dev;
+
+	if (argc < 1)
+		return CMD_RET_USAGE;
+
+	dev = NULL;
+	if (argc >= 2) {
+		dev = parse_rkmtd_label(argv[1]);
+		if (!dev)
+			return CMD_RET_FAILURE;
+	}
+
+	printf("%3s %12s %-15s\n", "dev", "blocks", "label");
+	if (dev) {
+		show_rkmtd_dev(dev);
+	} else {
+		struct uclass *uc;
+
+		uclass_id_foreach_dev(UCLASS_RKMTD, dev, uc)
+			show_rkmtd_dev(dev);
+	}
+
+	return 0;
+}
+
+static int do_rkmtd_dev(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	struct udevice *dev;
+	const char *label;
+
+	if (argc < 1 || argc > 3)
+		return CMD_RET_USAGE;
+
+	if (argc == 1) {
+		struct rkmtd_dev *plat;
+
+		dev = rkmtd_get_cur_dev();
+		if (!dev) {
+			printf("No current rkmtd device\n");
+			return CMD_RET_FAILURE;
+		}
+		plat = dev_get_plat(dev);
+		printf("Current rkmtd device: %d: %s\n", dev_seq(dev),
+		       plat->label);
+		return 0;
+	}
+
+	label = argv[1];
+	dev = parse_rkmtd_label(argv[1]);
+	if (!dev)
+		return CMD_RET_FAILURE;
+
+	rkmtd_set_cur_dev(dev);
+
+	return 0;
+}
+
+static struct cmd_tbl cmd_rkmtd_sub[] = {
+	U_BOOT_CMD_MKENT(bind, 4, 0, do_rkmtd_bind, "", ""),
+	U_BOOT_CMD_MKENT(unbind, 4, 0, do_rkmtd_unbind, "", ""),
+	U_BOOT_CMD_MKENT(info, 3, 0, do_rkmtd_info, "", ""),
+	U_BOOT_CMD_MKENT(dev, 0, 1, do_rkmtd_dev, "", ""),
+};
+
+static int do_rkmtd(struct cmd_tbl *cmdtp, int flag, int argc,
+		    char *const argv[])
+{
+	struct cmd_tbl *c;
+
+	argc--;
+	argv++;
+
+	c = find_cmd_tbl(argv[0], cmd_rkmtd_sub, ARRAY_SIZE(cmd_rkmtd_sub));
+
+	if (c)
+		return c->cmd(cmdtp, flag, argc, argv);
+	else
+		return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+	rkmtd, 8, 1, do_rkmtd,
+	"Rockchip MTD sub-system",
+	"bind <label>      - bind RKMTD device\n"
+	"rkmtd unbind <label>    - unbind RKMTD device\n"
+	"rkmtd info [<label>]    - show all available RKMTD devices\n"
+	"rkmtd dev [<label>]     - show or set current RKMTD device\n"
+);
--
2.39.2



More information about the U-Boot mailing list