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
rkparm_param_parse(char * param,struct list_head * parts_head,struct blk_desc * dev_desc)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 #ifndef PARTS_RKPARM
92 if (dev_desc->if_type != IF_TYPE_RKNAND)
93 offset = RK_PARAM_OFFSET;
94 #endif
95 part->start = start + offset;
96 /* Last partition use all remain space */
97 if (size == (~0UL))
98 size = dev_desc->lba - part->start;
99 part->size = size;
100 strncpy(part->name, next, len);
101 part->name[len] = '\0';
102 list_add_tail(&part->node, parts_head);
103 next = strchr(next, ',');
104 }
105
106 dev_num = ((dev_desc->if_type << 8) + dev_desc->devnum);
107
108 return 0;
109 }
110
rkparm_init_param(struct blk_desc * dev_desc,struct list_head * parts_head)111 static int rkparm_init_param(struct blk_desc *dev_desc,
112 struct list_head *parts_head)
113 {
114 char *parts_list;
115
116 /*
117 * There are ways to get partition tables:
118 *
119 * 1. macro 'PARTS_RKPARM' string list (No RK_PARAM_OFFSET for every partition),
120 * 2. Legacy rk parameter in flash @RK_PARAM_OFFSET sectors offset
121 */
122 #ifdef PARTS_RKPARM
123 parts_list = PARTS_RKPARM;
124 #else
125 struct rkparm_param *param;
126 int offset = 0;
127 int ret;
128
129 param = memalign(ARCH_DMA_MINALIGN, MAX_PARAM_SIZE);
130 if (!param) {
131 printf("out of memory\n");
132 return -ENOMEM;
133 }
134
135 if (dev_desc->if_type != IF_TYPE_RKNAND)
136 offset = RK_PARAM_OFFSET;
137
138 ret = blk_dread(dev_desc, offset, MAX_PARAM_SIZE >> 9, (ulong *)param);
139 if (ret != (MAX_PARAM_SIZE >> 9)) {
140 printf("%s param read fail\n", __func__);
141 return -EINVAL;
142 }
143 parts_list = param->params;
144 #endif
145 return rkparm_param_parse(parts_list, parts_head, dev_desc);
146 }
147
part_print_rkparm(struct blk_desc * dev_desc)148 static void part_print_rkparm(struct blk_desc *dev_desc)
149 {
150 int ret = 0;
151 struct list_head *node;
152 struct rkparm_part *p = NULL;
153 int i = 0;
154
155 if (list_empty(&parts_head) ||
156 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum)))
157 ret = rkparm_init_param(dev_desc, &parts_head);
158
159 if (ret) {
160 printf("%s Invalid rkparm parameter\n", __func__);
161 return;
162 }
163
164 printf("Part\tStart LBA\tSize\t\tName\n");
165 list_for_each(node, &parts_head) {
166 p = list_entry(node, struct rkparm_part, node);
167 printf("%3d\t0x%08lx\t0x%08lx\t%s\n", (i++ + 1),
168 p->start, p->size, p->name);
169 }
170
171 return;
172 }
173
part_get_info_rkparm(struct blk_desc * dev_desc,int idx,disk_partition_t * info)174 static int part_get_info_rkparm(struct blk_desc *dev_desc, int idx,
175 disk_partition_t *info)
176 {
177 struct list_head *node;
178 struct rkparm_part *p = NULL;
179 int part_num = 1;
180 int ret = 0;
181
182 if (idx < 1) {
183 printf("%s Invalid partition no.%d\n", __func__, idx);
184 return -EINVAL;
185 }
186
187 if (list_empty(&parts_head) ||
188 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum))) {
189 ret = rkparm_init_param(dev_desc, &parts_head);
190 if (ret) {
191 printf("%s Invalid rkparm partition\n", __func__);
192 return -1;
193 }
194 }
195
196 list_for_each(node, &parts_head) {
197 p = list_entry(node, struct rkparm_part, node);
198 if (idx == part_num)
199 break;
200 part_num ++;
201 }
202
203 if (part_num < idx) {
204 debug("%s Invalid partition no.%d\n", __func__, idx);
205 return -EINVAL;
206 }
207
208 info->start = p->start;
209 info->size = p->size;
210 info->blksz = dev_desc->blksz;
211
212 sprintf((char *)info->name, "%s", p->name);
213 strcpy((char *)info->type, "U-Boot");
214 info->bootable = 0;
215
216 return 0;
217 }
218
part_test_rkparm(struct blk_desc * dev_desc)219 static int part_test_rkparm(struct blk_desc *dev_desc)
220 {
221 int ret = 0;
222
223 if (list_empty(&parts_head) ||
224 (dev_num != ((dev_desc->if_type << 8) + dev_desc->devnum)))
225 ret = rkparm_init_param(dev_desc, &parts_head);
226 if (ret)
227 ret = -1;
228
229 return ret;
230 }
231 /*
232 * Add an 'b_' prefix so it comes before 'dos' and after 'a_efi' in the linker
233 * list. We need to check EFI first, and then rkparm partition
234 */
235 U_BOOT_PART_TYPE(b_rkparm) = {
236 #ifdef PARTS_RKPARM
237 .name = "RKPARM(FIXED)",
238 #else
239 .name = "RKPARM",
240 #endif
241 .part_type = PART_TYPE_RKPARM,
242 .max_entries = RKPARM_ENTRY_NUMBERS,
243 .get_info = part_get_info_ptr(part_get_info_rkparm),
244 .print = part_print_ptr(part_print_rkparm),
245 .test = part_test_rkparm,
246 };
247 #endif
248