1 /* 2 * (C) Copyright 2017 rkparm Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <malloc.h> 9 #ifdef CONFIG_ROCKCHIP_PRELOADER_ATAGS 10 #include <asm/arch/rk_atags.h> 11 #endif 12 13 #ifdef HAVE_BLOCK_DEVICE 14 #define RK_PARAM_OFFSET 0x2000 15 #define MAX_PARAM_SIZE (1024 * 64) 16 17 struct rkparm_param { 18 u32 tag; 19 u32 length; 20 char params[1]; 21 u32 crc; 22 }; 23 24 struct rkparm_part { 25 char name[PART_NAME_LEN]; 26 unsigned long start; 27 unsigned long size; 28 struct list_head node; 29 }; 30 31 32 static LIST_HEAD(parts_head); 33 34 /* 35 * What's this? 36 * 37 * There maybe two different storage media need to use this partition driver, 38 * e.g. rkand with SD card. So we need a flag info to recognize it. 39 */ 40 static int dev_num = -1; 41 42 static int rkparm_param_parse(char *param, struct list_head *parts_head, 43 struct blk_desc *dev_desc) 44 { 45 struct rkparm_part *part; 46 const char *cmdline = strstr(param, "CMDLINE:"); 47 const char *blkdev_parts; 48 char *cmdline_end, *next, *pend; 49 int len, offset = 0; 50 unsigned long size, start; 51 52 if (!cmdline) { 53 debug("RKPARM: Invalid parameter part table from storage\n"); 54 return -EINVAL; 55 } 56 57 blkdev_parts = strstr(cmdline, "mtdparts"); 58 next = strchr(blkdev_parts, ':'); 59 cmdline_end = strstr(cmdline, "\n"); /* end by '\n' */ 60 *cmdline_end = '\0'; 61 /* 62 * 1. skip "CMDLINE:" 63 * 2. Initrd fixup: remove unused "initrd=0x...,0x...", this for 64 * compatible with legacy parameter.txt 65 */ 66 env_update_filter("bootargs", cmdline + strlen("CMDLINE:"), "initrd="); 67 68 INIT_LIST_HEAD(parts_head); 69 while (next) { 70 /* Skip ':' and ',' */ 71 next++; 72 if (*next == '-') { 73 size = (~0UL); 74 next++; 75 } else { 76 size = simple_strtoul(next, &next, 16); 77 } 78 /* Skip '@' */ 79 next++; 80 start = simple_strtoul(next, &next, 16); 81 next++; 82 pend = strchr(next, ')'); 83 if (!pend) 84 break; 85 len = min_t(int, pend - next, PART_NAME_LEN); 86 part = malloc(sizeof(*part)); 87 if (!part) { 88 printf("out of memory\n"); 89 break; 90 } 91 if (dev_desc->if_type != IF_TYPE_RKNAND) 92 offset = RK_PARAM_OFFSET; 93 part->start = start + offset; 94 /* Last partition use all remain space */ 95 if (size == (~0UL)) 96 size = dev_desc->lba - part->start; 97 part->size = size; 98 strncpy(part->name, next, len); 99 part->name[len] = '\0'; 100 list_add_tail(&part->node, parts_head); 101 next = strchr(next, ','); 102 } 103 104 dev_num = ((dev_desc->if_type << 8) + dev_desc->devnum); 105 106 return 0; 107 } 108 109 static int rkparm_init_param(struct blk_desc *dev_desc, 110 struct list_head *parts_head) 111 { 112 struct rkparm_param *param; 113 int offset = 0; 114 int ret; 115 116 param = memalign(ARCH_DMA_MINALIGN, MAX_PARAM_SIZE); 117 if (!param) { 118 printf("out of memory\n"); 119 return -ENOMEM; 120 } 121 122 if (dev_desc->if_type != IF_TYPE_RKNAND) 123 offset = RK_PARAM_OFFSET; 124 125 ret = blk_dread(dev_desc, offset, MAX_PARAM_SIZE >> 9, (ulong *)param); 126 if (ret != (MAX_PARAM_SIZE >> 9)) { 127 printf("%s param read fail\n", __func__); 128 return -EINVAL; 129 } 130 131 return rkparm_param_parse(param->params, parts_head, dev_desc); 132 } 133 134 static void part_print_rkparm(struct blk_desc *dev_desc) 135 { 136 int ret = 0; 137 struct list_head *node; 138 struct rkparm_part *p = NULL; 139 int i = 0; 140 141 if (list_empty(&parts_head) || 142 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) 143 ret = rkparm_init_param(dev_desc, &parts_head); 144 145 if (ret) { 146 printf("%s Invalid rkparm parameter\n", __func__); 147 return; 148 } 149 150 printf("Part\tStart LBA\tSize\t\tName\n"); 151 list_for_each(node, &parts_head) { 152 p = list_entry(node, struct rkparm_part, node); 153 printf("%3d\t0x%08lx\t0x%08lx\t%s\n", (i++ + 1), 154 p->start, p->size, p->name); 155 } 156 157 return; 158 } 159 160 static int part_get_info_rkparm(struct blk_desc *dev_desc, int idx, 161 disk_partition_t *info) 162 { 163 struct list_head *node; 164 struct rkparm_part *p = NULL; 165 int part_num = 1; 166 int ret = 0; 167 168 if (idx < 1) { 169 printf("%s Invalid partition no.%d\n", __func__, idx); 170 return -EINVAL; 171 } 172 173 if (list_empty(&parts_head) || 174 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) { 175 ret = rkparm_init_param(dev_desc, &parts_head); 176 if (ret) { 177 printf("%s Invalid rkparm partition\n", __func__); 178 return -1; 179 } 180 } 181 182 list_for_each(node, &parts_head) { 183 p = list_entry(node, struct rkparm_part, node); 184 if (idx == part_num) 185 break; 186 part_num ++; 187 } 188 189 if (part_num < idx) { 190 debug("%s Invalid partition no.%d\n", __func__, idx); 191 return -EINVAL; 192 } 193 194 info->start = p->start; 195 info->size = p->size; 196 info->blksz = dev_desc->blksz; 197 198 sprintf((char *)info->name, "%s", p->name); 199 strcpy((char *)info->type, "U-Boot"); 200 info->bootable = 0; 201 202 return 0; 203 } 204 205 static int part_test_rkparm(struct blk_desc *dev_desc) 206 { 207 int ret = 0; 208 209 if (list_empty(&parts_head) || 210 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) 211 ret = rkparm_init_param(dev_desc, &parts_head); 212 if (ret) 213 ret = -1; 214 215 return ret; 216 } 217 /* 218 * Add an 'b_' prefix so it comes before 'dos' and after 'a_efi' in the linker 219 * list. We need to check EFI first, and then rkparm partition 220 */ 221 U_BOOT_PART_TYPE(b_rkparm) = { 222 .name = "RKPARM", 223 .part_type = PART_TYPE_RKPARM, 224 .max_entries = RKPARM_ENTRY_NUMBERS, 225 .get_info = part_get_info_ptr(part_get_info_rkparm), 226 .print = part_print_ptr(part_print_rkparm), 227 .test = part_test_rkparm, 228 }; 229 #endif 230