xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision c17ccbf6fd302d07c7f5440e01ab8c0cfdbf9fb1)
1 /*
2  * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <blk.h>
9 #include <boot_rkimg.h>
10 #include <dm.h>
11 #include <errno.h>
12 #include <malloc.h>
13 #include <nand.h>
14 #include <part.h>
15 #include <dm/device-internal.h>
16 
17 #define MTD_PART_NAND_HEAD		"mtdparts="
18 #define MTD_PART_INFO_MAX_SIZE		512
19 #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
20 
21 #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
22 #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
23 
24 static int *mtd_map_blk_table;
25 
26 int mtd_blk_map_table_init(struct blk_desc *desc,
27 			   loff_t offset,
28 			   size_t length)
29 {
30 	u32 blk_total, blk_begin, blk_cnt;
31 	struct mtd_info *mtd = NULL;
32 	int i, j;
33 
34 	if (!desc)
35 		return -ENODEV;
36 
37 	if (desc->devnum == BLK_MTD_NAND) {
38 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
39 		mtd = dev_get_priv(desc->bdev->parent);
40 #endif
41 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
42 #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
43 		mtd = desc->bdev->priv;
44 #endif
45 	}
46 
47 	if (!mtd) {
48 		return -ENODEV;
49 	} else {
50 		blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift;
51 		if (!mtd_map_blk_table) {
52 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
53 			memset(mtd_map_blk_table, MTD_BLK_TABLE_BLOCK_UNKNOWN,
54 			       blk_total * 4);
55 		}
56 
57 		blk_begin = (u32)offset >> mtd->erasesize_shift;
58 		blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length) >> mtd->erasesize_shift);
59 		if ((blk_begin + blk_cnt) > blk_total)
60 			blk_cnt = blk_total - blk_begin;
61 
62 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
63 			return 0;
64 
65 		j = 0;
66 		 /* should not across blk_cnt */
67 		for (i = 0; i < blk_cnt; i++) {
68 			if (j >= blk_cnt)
69 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
70 			for (; j < blk_cnt; j++) {
71 				if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) {
72 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
73 					j++;
74 					if (j == blk_cnt)
75 						j++;
76 					break;
77 				}
78 			}
79 		}
80 
81 		return 0;
82 	}
83 }
84 
85 static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off)
86 {
87 	bool mapped;
88 	loff_t offset = *off;
89 	size_t block_offset = offset & (mtd->erasesize - 1);
90 
91 	mapped = false;
92 	if (!mtd_map_blk_table ||
93 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
94 	    MTD_BLK_TABLE_BLOCK_UNKNOWN ||
95 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
96 	    0xffffffff)
97 		return mapped;
98 
99 	mapped = true;
100 	*off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >>
101 		mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset);
102 
103 	return mapped;
104 }
105 
106 static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
107 				       size_t *length, size_t *actual,
108 				       loff_t lim, u_char *buffer)
109 {
110 	size_t left_to_read = *length;
111 	u_char *p_buffer = buffer;
112 	u32 erasesize = mtd->erasesize;
113 	int rval;
114 
115 	while (left_to_read > 0) {
116 		size_t block_offset = offset & (erasesize - 1);
117 		size_t read_length;
118 
119 		if (offset >= mtd->size)
120 			return 0;
121 
122 		if (!get_mtd_blk_map_address(mtd, &offset)) {
123 			if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) {
124 				printf("Skip bad block 0x%08llx\n",
125 				       offset & ~(erasesize - 1));
126 				offset += erasesize - block_offset;
127 				continue;
128 			}
129 		}
130 
131 		if (left_to_read < (erasesize - block_offset))
132 			read_length = left_to_read;
133 		else
134 			read_length = erasesize - block_offset;
135 
136 		rval = mtd_read(mtd, offset, read_length, &read_length,
137 				p_buffer);
138 		if (rval && rval != -EUCLEAN) {
139 			printf("NAND read from offset %llx failed %d\n",
140 			       offset, rval);
141 			*length -= left_to_read;
142 			return rval;
143 		}
144 
145 		left_to_read -= read_length;
146 		offset       += read_length;
147 		p_buffer     += read_length;
148 	}
149 
150 	return 0;
151 }
152 
153 char *mtd_part_parse(void)
154 {
155 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
156 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
157 	struct blk_desc *dev_desc;
158 	disk_partition_t info;
159 	char *mtd_part_info_p;
160 	struct mtd_info *mtd;
161 	char *mtd_part_info;
162 	int ret;
163 	int p;
164 
165 	dev_desc = rockchip_get_bootdev();
166 	if (!dev_desc)
167 		return NULL;
168 
169 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
170 	if (!mtd)
171 		return NULL;
172 
173 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
174 	if (!mtd_part_info) {
175 		printf("%s: Fail to malloc!", __func__);
176 		return NULL;
177 	}
178 
179 	mtd_part_info_p = mtd_part_info;
180 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
181 		 MTD_PART_NAND_HEAD,
182 		 dev_desc->product);
183 	data_len -= strlen(mtd_part_info_p);
184 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
185 
186 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
187 		ret = part_get_info(dev_desc, p, &info);
188 		if (ret)
189 			break;
190 
191 		debug("name is %s, start addr is %x\n", info.name,
192 		      (int)(size_t)info.start);
193 
194 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
195 			 (int)(size_t)info.size << 9,
196 			 (int)(size_t)info.start << 9,
197 			 info.name);
198 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
199 			 "0x%x@0x%x(%s)",
200 			 (int)(size_t)info.size << 9,
201 			 (int)(size_t)info.start << 9,
202 			 info.name);
203 		strcat(mtd_part_info, ",");
204 		if (part_get_info(dev_desc, p + 1, &info)) {
205 			/* Nand flash is erased by block and gpt table just
206 			 * resserve 33 sectors for the last partition. This
207 			 * will erase the backup gpt table by user program,
208 			 * so reserve one block.
209 			 */
210 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
211 				 (int)(size_t)(info.size -
212 				 (info.size - 1) %
213 				 (mtd->erasesize >> 9) - 1) << 9,
214 				 (int)(size_t)info.start << 9,
215 				 info.name);
216 			break;
217 		}
218 		length = strlen(mtd_part_info_temp);
219 		data_len -= length;
220 		mtd_part_info_p = mtd_part_info_p + length + 1;
221 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
222 	}
223 
224 	return mtd_part_info;
225 }
226 
227 ulong mtd_dread(struct udevice *udev, lbaint_t start,
228 		lbaint_t blkcnt, void *dst)
229 {
230 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
231 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
232 	loff_t off = (loff_t)(start * 512);
233 	size_t rwsize = blkcnt * 512;
234 #endif
235 	struct mtd_info *mtd;
236 	int ret = 0;
237 
238 	if (!desc)
239 		return ret;
240 
241 	mtd = desc->bdev->priv;
242 	if (!mtd)
243 		return 0;
244 
245 	if (blkcnt == 0)
246 		return 0;
247 
248 	if (desc->devnum == BLK_MTD_NAND) {
249 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
250 		mtd = dev_get_priv(udev->parent);
251 		if (!mtd)
252 			return 0;
253 
254 		ret = nand_read_skip_bad(mtd, off, &rwsize,
255 					 NULL, mtd->size,
256 					 (u_char *)(dst));
257 #else
258 		ret = mtd_map_read(mtd, off, &rwsize,
259 				   NULL, mtd->size,
260 				   (u_char *)(dst));
261 #endif
262 		if (!ret)
263 			return blkcnt;
264 		else
265 			return 0;
266 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
267 		ret = mtd_map_read(mtd, off, &rwsize,
268 				   NULL, mtd->size,
269 				   (u_char *)(dst));
270 		if (!ret)
271 			return blkcnt;
272 		else
273 			return 0;
274 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
275 #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
276 		size_t retlen_nor;
277 
278 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
279 		if (retlen_nor == rwsize)
280 			return blkcnt;
281 		else
282 #endif
283 			return 0;
284 	} else {
285 		return 0;
286 	}
287 }
288 
289 ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
290 		 lbaint_t blkcnt, const void *src)
291 {
292 	/* Not implemented */
293 	return 0;
294 }
295 
296 ulong mtd_derase(struct udevice *udev, lbaint_t start,
297 		 lbaint_t blkcnt)
298 {
299 	/* Not implemented */
300 	return 0;
301 }
302 
303 static int mtd_blk_probe(struct udevice *udev)
304 {
305 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
306 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
307 	int ret, i;
308 
309 	desc->bdev->priv = mtd;
310 	sprintf(desc->vendor, "0x%.4x", 0x2207);
311 	memcpy(desc->product, mtd->name, strlen(mtd->name));
312 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
313 	if (mtd->type == MTD_NANDFLASH) {
314 		if (desc->devnum == BLK_MTD_NAND)
315 			mtd = dev_get_priv(udev->parent);
316 		/*
317 		 * Find the first useful block in the end,
318 		 * and it is the end lba of the nand storage.
319 		 */
320 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
321 			ret =  mtd_block_isbad(mtd,
322 					       mtd->size - mtd->erasesize * (i + 1));
323 			if (!ret) {
324 				desc->lba = (mtd->size >> 9) -
325 					(mtd->erasesize >> 9) * i;
326 				break;
327 			}
328 		}
329 	} else {
330 		desc->lba = mtd->size >> 9;
331 	}
332 
333 	debug("MTD: desc->lba is %lx\n", desc->lba);
334 
335 	return 0;
336 }
337 
338 static const struct blk_ops mtd_blk_ops = {
339 	.read	= mtd_dread,
340 #ifndef CONFIG_SPL_BUILD
341 	.write	= mtd_dwrite,
342 	.erase	= mtd_derase,
343 #endif
344 };
345 
346 U_BOOT_DRIVER(mtd_blk) = {
347 	.name		= "mtd_blk",
348 	.id		= UCLASS_BLK,
349 	.ops		= &mtd_blk_ops,
350 	.probe		= mtd_blk_probe,
351 };
352