[PATCH v2 03/11] rockchip: idb: add IDB block device

Johan Jonker jbx6244 at gmail.com
Sat Jul 9 11:49:26 PDT 2022


The Rockchip SoCs with a NAND as boot device need
a special Rockchip IDB block device to transfer the data
from the rockusb gadget to the NAND driver.

Signed-off-by: Johan Jonker <jbx6244 at gmail.com>
---
 arch/arm/mach-rockchip/rockchip_idb.c | 402 ++++++++++++++++++++++++++
 1 file changed, 402 insertions(+)

diff --git a/arch/arm/mach-rockchip/rockchip_idb.c b/arch/arm/mach-rockchip/rockchip_idb.c
index 6a2af329..832b3251 100644
--- a/arch/arm/mach-rockchip/rockchip_idb.c
+++ b/arch/arm/mach-rockchip/rockchip_idb.c
@@ -3,10 +3,16 @@
  * Copyright (C) 2022 Johan Jonker <jbx6244 at gmail.com>
  */
 
+#include <blk.h>
 #include <clk.h>
+#include <command.h>
 #include <dm.h>
 #include <memalign.h>
+#include <part.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
 #include <linux/iopoll.h>
+#include <u-boot/crc.h>
 
 #define NFC_DEF_TIMEOUT		20000
 #define NFC_ECC_MAX_MODES	4
@@ -152,10 +158,18 @@ struct rk_idb {
 	u32 pages_per_blk;
 	struct idb idblock[5];
 	u32 blk_counter;
+	u32 idb_need_write_back;
 	u32 sectors;
 	u16 page_table[512];
+	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];
 };
 
 struct nand_para_info nand_para_tbl[] = {
@@ -816,6 +830,171 @@ void rk_idb_write_block(struct rk_idb *plat, u32 idb, u32 sectors, u32 *data, u3
 	}
 }
 
+unsigned long rk_idb_read(struct udevice *dev,
+			  unsigned long start, lbaint_t blkcnt,
+			  void *buffer)
+{
+	struct rk_idb *plat = dev_get_plat(dev->parent);
+	struct blk_desc *block_dev = dev_get_uclass_plat(dev);
+	char *buf = buffer;
+	int i;
+
+	debug("start   : %lu blkcnt: %lu\n", start, blkcnt);
+
+	if (start > (block_dev->lba - 1) || (start + blkcnt) > block_dev->lba) {
+		debug("invalid block\n");
+		return -1;
+	}
+
+	memset(buffer, 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("idb rd  : %d\n", i);
+
+			memcpy(&buf[(i - start) * block_dev->blksz],
+			       &plat->idb[(i - 64) * block_dev->blksz], block_dev->blksz);
+		}
+	}
+
+	return blkcnt;
+}
+
+unsigned long rk_idb_write(struct udevice *dev,
+			   unsigned long start, lbaint_t blkcnt,
+			   const void *buffer)
+{
+	struct rk_idb *plat = dev_get_plat(dev->parent);
+	struct blk_desc *block_dev = dev_get_uclass_plat(dev);
+	u32 data[512];
+	u32 spare[2];
+	int i, j;
+
+	if (start > (block_dev->lba - 1) || (start + blkcnt) > block_dev->lba) {
+		debug("invalid block\n");
+		return -1;
+	}
+
+	debug("start   : %lu blkcnt: %lu\n", start, blkcnt);
+
+	for (i = start; i < (start + blkcnt); i++) {
+		debug("idb 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 *)buffer;
+
+				memcpy(&plat->idb[(i - 64) * block_dev->blksz],
+				       &buf[(i - start) * block_dev->blksz],
+				       block_dev->blksz);
+
+				if (i == 64) {
+					memcpy(data, plat->idb, 512);
+
+					if (*data == RK_MAGIC) {
+						u32 boot_size;
+
+						rk_idb_rc4((char *)data, 512);
+
+						struct sector0 *sec0 = (struct sector0 *)data;
+
+						if ((sec0->flash_boot_size -
+						     sec0->flash_data_size) != 1024) {
+							boot_size = sec0->flash_boot_size -
+								    sec0->flash_data_size;
+						} else {
+							boot_size = 0;
+						}
+
+						plat->sectors = sec0->boot_code1_offset +
+								sec0->flash_data_size +
+								boot_size;
+
+						if (plat->sectors > 512) {
+							debug("max sector 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->sectors - 1)) {
+					debug("last block\n");
+
+					plat->idb_need_write_back = 0;
+
+					if (!plat->blk_counter) {
+						plat->idblock[0].blk = 2;
+						plat->idblock[0].ecc = plat->boot_ecc;
+						plat->idblock[1].blk = 3;
+						plat->idblock[1].ecc = plat->boot_ecc;
+						plat->idblock[2].blk = 4;
+						plat->idblock[2].ecc = plat->boot_ecc;
+						plat->idblock[3].blk = 5;
+						plat->idblock[3].ecc = plat->boot_ecc;
+						plat->idblock[4].blk = 6;
+						plat->idblock[4].ecc = plat->boot_ecc;
+						plat->blk_counter = 5;
+					}
+
+					for (j = 0; j < plat->blk_counter; j++) {
+						if (plat->idblock[j].blk < plat->boot_blks)
+							rk_idb_write_block(plat, j, plat->sectors,
+									   (u32 *)plat->idb, spare);
+					}
+
+					rk_idb_scan_block(plat, data, spare);
+
+					memset(plat->idb, 0xff, 512 * 512);
+
+					if (plat->blk_counter)
+						rk_idb_read_block(plat, 0, plat->idblock[0].sectors,
+								  (u32 *)plat->idb, spare);
+				}
+			}
+		} else if (plat->idb_need_write_back) {
+			plat->idb_need_write_back = 0;
+
+			memset(plat->idb, 0xff, 512 * 512);
+
+			if (plat->blk_counter)
+				rk_idb_read_block(plat, 0, plat->idblock[0].sectors,
+						  (u32 *)plat->idb, spare);
+		}
+	}
+
+	return blkcnt;
+}
+
 void rk_idb_block_align(struct rk_idb *plat, u32 pages_per_blk)
 {
 	plat->pages_per_blk = pages_per_blk;
@@ -827,9 +1006,25 @@ void rk_idb_block_align(struct rk_idb *plat, u32 pages_per_blk)
 		plat->pages_per_blk = 256;
 }
 
+static inline u32 efi_crc32(const void *buf, u32 len)
+{
+	return crc32(0, buf, len);
+}
+
 int rk_idb_init_plat(struct udevice *dev)
 {
+	static const efi_guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
 	struct rk_idb *plat = dev_get_plat(dev);
+	size_t efiname_len, dosname_len;
+	uchar name[] = "loader1";
+	u32 calc_crc32;
+	int k;
+
+	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);
 
@@ -845,6 +1040,127 @@ int rk_idb_init_plat(struct udevice *dev)
 		return -1;
 	}
 
+	plat->mbr = malloc_cache_aligned(sizeof(legacy_mbr));
+
+	if (!plat->mbr) {
+		debug("mbr malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		return -1;
+	}
+
+	plat->gpt_e = malloc_cache_aligned(sizeof(gpt_entry));
+
+	if (!plat->gpt_e) {
+		debug("gpt_e malloc failed\n");
+		free(plat->idb);
+		free(plat->check);
+		free(plat->mbr);
+		return -1;
+	}
+
+	plat->gpt_h = malloc_cache_aligned(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 -1;
+	}
+
+	plat->gpt_h2 = malloc_cache_aligned(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 -1;
+	}
+
+	/* 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);
+
 	return 0;
 }
 
@@ -853,6 +1169,8 @@ int rk_idb_probe(struct udevice *dev)
 	struct rk_idb *plat = dev_get_plat(dev);
 	const char *node_name;
 	ofnode subnode;
+	u32 data[512];
+	u32 spare[2];
 	u8 id[8];
 	int ret;
 	int i;
@@ -935,6 +1253,23 @@ int rk_idb_probe(struct udevice *dev)
 		return -ENOENT;
 	}
 
+	rk_idb_scan_block(plat, data, spare);
+
+	if (plat->blk_counter)
+		rk_idb_read_block(plat, 0, plat->idblock[0].sectors, (u32 *)plat->idb, spare);
+
+	return 0;
+}
+
+static int rk_idb_blk_probe(struct udevice *udev)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(udev);
+
+	desc->removable = true;
+	snprintf(desc->vendor, BLK_VEN_SIZE, "Rockchip");
+	snprintf(desc->product, BLK_PRD_SIZE, "IDB");
+	snprintf(desc->revision, BLK_REV_SIZE, "1.0");
+
 	return 0;
 }
 
@@ -1065,6 +1400,24 @@ static const struct udevice_id rk_idb_ids[] = {
 	{ /* sentinel */ }
 };
 
+static const struct blk_ops rk_idb_ops = {
+	.read	= rk_idb_read,
+	.write	= rk_idb_write,
+};
+
+U_BOOT_DRIVER(idb_blk) = {
+	.name		= "idb_blk",
+	.id		= UCLASS_BLK,
+	.ops		= &rk_idb_ops,
+	.probe		= rk_idb_blk_probe,
+};
+
+UCLASS_DRIVER(idb) = {
+	.id		= UCLASS_RK_IDB,
+	.name		= "idb",
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+};
+
 U_BOOT_DRIVER(rockchip_idb) = {
 	.name		= "rockchip_idb",
 	.id		= UCLASS_RK_IDB,
@@ -1072,3 +1425,52 @@ U_BOOT_DRIVER(rockchip_idb) = {
 	.probe		= rk_idb_probe,
 	.plat_auto	= sizeof(struct rk_idb),
 };
+
+int rk_idb_start(void)
+{
+	struct udevice *dev;
+	struct udevice *bdev;
+	int ret;
+
+	ret = uclass_get_device(UCLASS_RK_IDB, 0, &dev);
+	if (ret) {
+		printf("no IDB device found\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = blk_get_device(IF_TYPE_RK_IDB, 0, &bdev);
+	if (ret) {
+		ret = blk_create_device(dev, "idb_blk", "blk",
+					IF_TYPE_RK_IDB, 0, 512,
+					LBA, &bdev);
+		if (ret)
+			return ret;
+
+		ret = blk_probe_or_unbind(bdev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+#if !defined(CONFIG_SPL_BUILD)
+
+static int rk_idb_do(struct cmd_tbl *cmdtp, int flag, int argc,
+		     char *const argv[])
+{
+	if (argc == 2) {
+		if (!strcmp(argv[1], "start"))
+			return rk_idb_start();
+	}
+
+	return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+	idb, 5, 1, rk_idb_do,
+	"Rockchip IDB block device",
+	"start - start IDB device\n"
+);
+
+#endif
-- 
2.20.1




More information about the Linux-rockchip mailing list