[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