xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 18ed0023c913331f7eea8aff7c271b86672c7bf4)
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 <image.h>
13 #include <linux/log2.h>
14 #include <malloc.h>
15 #include <nand.h>
16 #include <part.h>
17 #include <spi.h>
18 #include <dm/device-internal.h>
19 #include <linux/mtd/spi-nor.h>
20 #ifdef CONFIG_NAND
21 #include <linux/mtd/nand.h>
22 #endif
23 
24 #define MTD_PART_NAND_HEAD		"mtdparts="
25 #define MTD_ROOT_PART_NUM		"ubi.mtd="
26 #define MTD_ROOT_PART_NAME_UBIFS	"root=ubi0:rootfs"
27 #define MTD_ROOT_PART_NAME_SQUASHFS	"root=/dev/ubiblock0_0"
28 #define MTD_PART_INFO_MAX_SIZE		512
29 #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
30 
31 #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
32 #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
33 
34 static int *mtd_map_blk_table;
35 
36 int mtd_blk_map_table_init(struct blk_desc *desc,
37 			   loff_t offset,
38 			   size_t length)
39 {
40 	u32 blk_total, blk_begin, blk_cnt;
41 	struct mtd_info *mtd = NULL;
42 	int i, j;
43 
44 	if (!desc)
45 		return -ENODEV;
46 
47 	switch (desc->devnum) {
48 	case BLK_MTD_NAND:
49 	case BLK_MTD_SPI_NAND:
50 		mtd = desc->bdev->priv;
51 		break;
52 	default:
53 		break;
54 	}
55 
56 	if (!mtd) {
57 		return -ENODEV;
58 	} else {
59 		blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift;
60 		if (!mtd_map_blk_table) {
61 			mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int));
62 			if (!mtd_map_blk_table)
63 				return -ENOMEM;
64 			for (i = 0; i < blk_total; i++)
65 				mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN;
66 		}
67 
68 		blk_begin = (u32)offset >> mtd->erasesize_shift;
69 		blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length + \
70 			mtd->erasesize - 1) >> mtd->erasesize_shift);
71 		if (blk_begin >= blk_total) {
72 			pr_err("map table blk begin[%d] overflow\n", blk_begin);
73 			return -EINVAL;
74 		}
75 		if ((blk_begin + blk_cnt) > blk_total)
76 			blk_cnt = blk_total - blk_begin;
77 
78 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
79 			return 0;
80 
81 		j = 0;
82 		 /* should not across blk_cnt */
83 		for (i = 0; i < blk_cnt; i++) {
84 			if (j >= blk_cnt)
85 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
86 			for (; j < blk_cnt; j++) {
87 				if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) {
88 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
89 					j++;
90 					if (j == blk_cnt)
91 						j++;
92 					break;
93 				}
94 			}
95 		}
96 
97 		return 0;
98 	}
99 }
100 
101 static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off)
102 {
103 	bool mapped;
104 	loff_t offset = *off;
105 	size_t block_offset = offset & (mtd->erasesize - 1);
106 
107 	mapped = false;
108 	if (!mtd_map_blk_table ||
109 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
110 	    MTD_BLK_TABLE_BLOCK_UNKNOWN ||
111 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
112 	    0xffffffff)
113 		return mapped;
114 
115 	mapped = true;
116 	*off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >>
117 		mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset);
118 
119 	return mapped;
120 }
121 
122 void mtd_blk_map_partitions(struct blk_desc *desc)
123 {
124 	disk_partition_t info;
125 	int i, ret;
126 
127 	if (!desc)
128 		return;
129 
130 	if (desc->if_type != IF_TYPE_MTD)
131 		return;
132 
133 	for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
134 		ret = part_get_info(desc, i, &info);
135 		if (ret != 0)
136 			continue;
137 
138 		if (mtd_blk_map_table_init(desc,
139 					   info.start << 9,
140 					   info.size << 9)) {
141 			pr_debug("mtd block map table fail\n");
142 		}
143 	}
144 }
145 
146 void mtd_blk_map_fit(struct blk_desc *desc, ulong sector, void *fit)
147 {
148 	struct mtd_info *mtd = NULL;
149 	int totalsize = 0;
150 
151 	if (desc->if_type != IF_TYPE_MTD)
152 		return;
153 
154 	if (desc->devnum == BLK_MTD_NAND) {
155 #if defined(CONFIG_NAND)
156 		mtd = dev_get_priv(desc->bdev->parent);
157 #endif
158 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
159 #if defined(CONFIG_MTD_SPI_NAND)
160 		mtd = desc->bdev->priv;
161 #endif
162 	}
163 
164 #ifdef CONFIG_SPL_FIT
165 	if (fit_get_totalsize(fit, &totalsize))
166 		debug("Can not find /totalsize node.\n");
167 #endif
168 	if (mtd && totalsize) {
169 		if (mtd_blk_map_table_init(desc, sector << 9, totalsize + (size_t)mtd->erasesize))
170 			debug("Map block table fail.\n");
171 	}
172 }
173 
174 static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
175 				       size_t *length, size_t *actual,
176 				       loff_t lim, u_char *buffer)
177 {
178 	size_t left_to_read = *length;
179 	u_char *p_buffer = buffer;
180 	int rval;
181 
182 	while (left_to_read > 0) {
183 		size_t block_offset = offset & (mtd->erasesize - 1);
184 		size_t read_length;
185 		loff_t mapped_offset;
186 
187 		if (offset >= mtd->size)
188 			return 0;
189 
190 		mapped_offset = offset;
191 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
192 			if (mtd_block_isbad(mtd, mapped_offset &
193 					    ~(mtd->erasesize - 1))) {
194 				printf("Skipping bad block 0x%08llx\n",
195 				       offset & ~(mtd->erasesize - 1));
196 				offset += mtd->erasesize - block_offset;
197 				continue;
198 			}
199 		}
200 
201 		if (left_to_read < (mtd->erasesize - block_offset))
202 			read_length = left_to_read;
203 		else
204 			read_length = mtd->erasesize - block_offset;
205 
206 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
207 				p_buffer);
208 		if (rval && rval != -EUCLEAN) {
209 			printf("NAND read from offset %llx failed %d\n",
210 			       offset, rval);
211 			*length -= left_to_read;
212 			return rval;
213 		}
214 
215 		left_to_read -= read_length;
216 		offset       += read_length;
217 		p_buffer     += read_length;
218 	}
219 
220 	return 0;
221 }
222 
223 static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset,
224 					size_t *length, size_t *actual,
225 					loff_t lim, u_char *buffer, int flags)
226 {
227 	int rval = 0, blocksize;
228 	size_t left_to_write = *length;
229 	u_char *p_buffer = buffer;
230 	struct erase_info ei;
231 
232 	blocksize = mtd->erasesize;
233 
234 	/*
235 	 * nand_write() handles unaligned, partial page writes.
236 	 *
237 	 * We allow length to be unaligned, for convenience in
238 	 * using the $filesize variable.
239 	 *
240 	 * However, starting at an unaligned offset makes the
241 	 * semantics of bad block skipping ambiguous (really,
242 	 * you should only start a block skipping access at a
243 	 * partition boundary).  So don't try to handle that.
244 	 */
245 	if ((offset & (mtd->writesize - 1)) != 0) {
246 		printf("Attempt to write non page-aligned data\n");
247 		*length = 0;
248 		return -EINVAL;
249 	}
250 
251 	while (left_to_write > 0) {
252 		size_t block_offset = offset & (mtd->erasesize - 1);
253 		size_t write_size, truncated_write_size;
254 		loff_t mapped_offset;
255 
256 		if (offset >= mtd->size)
257 			return 0;
258 
259 		mapped_offset = offset;
260 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
261 			if (mtd_block_isbad(mtd, mapped_offset &
262 					    ~(mtd->erasesize - 1))) {
263 				printf("Skipping bad block 0x%08llx\n",
264 				       offset & ~(mtd->erasesize - 1));
265 				offset += mtd->erasesize - block_offset;
266 				continue;
267 			}
268 		}
269 
270 		if (!(mapped_offset & mtd->erasesize_mask)) {
271 			memset(&ei, 0, sizeof(struct erase_info));
272 			ei.addr = mapped_offset;
273 			ei.len  = mtd->erasesize;
274 			rval = mtd_erase(mtd, &ei);
275 			if (rval) {
276 				pr_info("error %d while erasing %llx\n", rval,
277 					mapped_offset);
278 				return rval;
279 			}
280 		}
281 
282 		if (left_to_write < (blocksize - block_offset))
283 			write_size = left_to_write;
284 		else
285 			write_size = blocksize - block_offset;
286 
287 		truncated_write_size = write_size;
288 		rval = mtd_write(mtd, mapped_offset, truncated_write_size,
289 				 (size_t *)(&truncated_write_size), p_buffer);
290 
291 		offset += write_size;
292 		p_buffer += write_size;
293 
294 		if (rval != 0) {
295 			printf("NAND write to offset %llx failed %d\n",
296 			       offset, rval);
297 			*length -= left_to_write;
298 			return rval;
299 		}
300 
301 		left_to_write -= write_size;
302 	}
303 
304 	return 0;
305 }
306 
307 static __maybe_unused int mtd_map_erase(struct mtd_info *mtd, loff_t offset,
308 					size_t length)
309 {
310 	struct erase_info ei;
311 	loff_t pos, len;
312 	int ret;
313 
314 	pos = offset;
315 	len = length;
316 
317 	if ((pos & mtd->erasesize_mask) || (len & mtd->erasesize_mask)) {
318 		pr_err("Attempt to erase non block-aligned data, pos= %llx, len= %llx\n",
319 		       pos, len);
320 
321 		return -EINVAL;
322 	}
323 
324 	while (len) {
325 		loff_t mapped_offset;
326 
327 		mapped_offset = pos;
328 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
329 			if (mtd_block_isbad(mtd, pos) || mtd_block_isreserved(mtd, pos)) {
330 				pr_debug("attempt to erase a bad/reserved block @%llx\n",
331 					 pos);
332 				pos += mtd->erasesize;
333 				continue;
334 			}
335 		}
336 
337 		memset(&ei, 0, sizeof(struct erase_info));
338 		ei.addr = mapped_offset;
339 		ei.len  = mtd->erasesize;
340 		ret = mtd_erase(mtd, &ei);
341 		if (ret) {
342 			pr_err("map_erase error %d while erasing %llx\n", ret,
343 			       pos);
344 			return ret;
345 		}
346 
347 		pos += mtd->erasesize;
348 		len -= mtd->erasesize;
349 	}
350 
351 	return 0;
352 }
353 
354 char *mtd_part_parse(void)
355 {
356 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
357 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
358 	char mtd_root_part_info[40] = {0};
359 	struct blk_desc *dev_desc;
360 	disk_partition_t info;
361 	char *mtd_part_info_p;
362 	struct mtd_info *mtd;
363 	char *mtd_part_info;
364 	int ret;
365 	int p;
366 
367 	dev_desc = rockchip_get_bootdev();
368 	if (!dev_desc)
369 		return NULL;
370 
371 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
372 	if (!mtd)
373 		return NULL;
374 
375 	p = part_get_info_by_name(dev_desc, PART_SYSTEM, &info);
376 	if (p > 0) {
377 		if (strstr(env_get("bootargs"), "rootfstype=squashfs"))
378 			snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s",
379 				 MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_SQUASHFS);
380 		else
381 			snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s",
382 				 MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_UBIFS);
383 		env_update("bootargs", mtd_root_part_info);
384 	}
385 
386 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
387 	if (!mtd_part_info) {
388 		printf("%s: Fail to malloc!", __func__);
389 		return NULL;
390 	}
391 
392 	mtd_part_info_p = mtd_part_info;
393 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
394 		 MTD_PART_NAND_HEAD,
395 		 dev_desc->product);
396 	data_len -= strlen(mtd_part_info_p);
397 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
398 
399 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
400 		ret = part_get_info(dev_desc, p, &info);
401 		if (ret)
402 			break;
403 
404 		debug("name is %s, start addr is %x\n", info.name,
405 		      (int)(size_t)info.start);
406 
407 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
408 			 (int)(size_t)info.size << 9,
409 			 (int)(size_t)info.start << 9,
410 			 info.name);
411 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
412 			 "0x%x@0x%x(%s)",
413 			 (int)(size_t)info.size << 9,
414 			 (int)(size_t)info.start << 9,
415 			 info.name);
416 		strcat(mtd_part_info, ",");
417 		if (part_get_info(dev_desc, p + 1, &info)) {
418 			/* Partition with grow tag in parameter will be resized */
419 			if ((info.size + info.start + 64) >= dev_desc->lba) {
420 				if (dev_desc->devnum == BLK_MTD_SPI_NOR) {
421 					/* Nor is 64KB erase block(kernel) and gpt table just
422 					 * resserve 33 sectors for the last partition. This
423 					 * will erase the backup gpt table by user program,
424 					 * so reserve one block.
425 					 */
426 					snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
427 						 (int)(size_t)(info.size -
428 						 (info.size - 1) %
429 						 (0x10000 >> 9) - 1) << 9,
430 						 (int)(size_t)info.start << 9,
431 						 info.name);
432 					break;
433 				} else {
434 					/* Nand flash is erased by block and gpt table just
435 					 * resserve 33 sectors for the last partition. This
436 					 * will erase the backup gpt table by user program,
437 					 * so reserve one block.
438 					 */
439 					snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
440 						 (int)(size_t)(info.size -
441 						 (info.size - 1) %
442 						 (mtd->erasesize >> 9) - 1) << 9,
443 						 (int)(size_t)info.start << 9,
444 						 info.name);
445 					break;
446 				}
447 			} else {
448 				snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
449 					 "0x%x@0x%x(%s)",
450 					 (int)(size_t)info.size << 9,
451 					 (int)(size_t)info.start << 9,
452 					 info.name);
453 				break;
454 			}
455 		}
456 		length = strlen(mtd_part_info_temp);
457 		data_len -= length;
458 		mtd_part_info_p = mtd_part_info_p + length + 1;
459 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
460 	}
461 
462 	return mtd_part_info;
463 }
464 
465 ulong mtd_dread(struct udevice *udev, lbaint_t start,
466 		lbaint_t blkcnt, void *dst)
467 {
468 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
469 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
470 	loff_t off = (loff_t)(start * 512);
471 	size_t rwsize = blkcnt * 512;
472 #endif
473 	struct mtd_info *mtd;
474 	int ret = 0;
475 
476 	if (!desc)
477 		return ret;
478 
479 	mtd = desc->bdev->priv;
480 	if (!mtd)
481 		return 0;
482 
483 	if (blkcnt == 0)
484 		return 0;
485 
486 	pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt);
487 
488 	if (desc->devnum == BLK_MTD_NAND) {
489 		ret = mtd_map_read(mtd, off, &rwsize,
490 				   NULL, mtd->size,
491 				   (u_char *)(dst));
492 		if (!ret)
493 			return blkcnt;
494 		else
495 			return 0;
496 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
497 		ret = mtd_map_read(mtd, off, &rwsize,
498 				   NULL, mtd->size,
499 				   (u_char *)(dst));
500 		if (!ret)
501 			return blkcnt;
502 		else
503 			return 0;
504 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
505 #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
506 		struct spi_nor *nor = (struct spi_nor *)mtd->priv;
507 		struct spi_slave *spi = nor->spi;
508 		size_t retlen_nor;
509 
510 		if (desc->op_flag == BLK_PRE_RW)
511 			spi->mode |= SPI_DMA_PREPARE;
512 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
513 		if (desc->op_flag == BLK_PRE_RW)
514 			spi->mode |= SPI_DMA_PREPARE;
515 
516 		if (retlen_nor == rwsize)
517 			return blkcnt;
518 		else
519 #endif
520 			return 0;
521 	} else {
522 		return 0;
523 	}
524 }
525 
526 #if CONFIG_IS_ENABLED(MTD_WRITE)
527 ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
528 		 lbaint_t blkcnt, const void *src)
529 {
530 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
531 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
532 	loff_t off = (loff_t)(start * 512);
533 	size_t rwsize = blkcnt * 512;
534 #endif
535 	struct mtd_info *mtd;
536 	int ret = 0;
537 
538 	if (!desc)
539 		return ret;
540 
541 	mtd = desc->bdev->priv;
542 	if (!mtd)
543 		return 0;
544 
545 	pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt);
546 
547 	if (blkcnt == 0)
548 		return 0;
549 
550 	if (desc->devnum == BLK_MTD_NAND ||
551 	    desc->devnum == BLK_MTD_SPI_NAND ||
552 	    desc->devnum == BLK_MTD_SPI_NOR) {
553 		if (desc->op_flag == BLK_MTD_CONT_WRITE) {
554 			ret = mtd_map_write(mtd, off, &rwsize,
555 					    NULL, mtd->size,
556 					    (u_char *)(src), 0);
557 			if (!ret)
558 				return blkcnt;
559 			else
560 				return 0;
561 		} else {
562 			lbaint_t off_aligned, alinged;
563 			size_t rwsize_aligned;
564 			u8 *p_buf;
565 
566 			alinged = off & mtd->erasesize_mask;
567 			off_aligned = off - alinged;
568 			rwsize_aligned = rwsize + alinged;
569 			rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) &
570 				~(mtd->erasesize - 1);
571 
572 			p_buf = malloc(rwsize_aligned);
573 			if (!p_buf) {
574 				printf("%s: Fail to malloc!", __func__);
575 				return 0;
576 			}
577 
578 			ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned,
579 					   NULL, mtd->size,
580 					   (u_char *)(p_buf));
581 			if (ret) {
582 				free(p_buf);
583 				return 0;
584 			}
585 
586 			memcpy(p_buf + alinged, src, rwsize);
587 
588 			ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned,
589 					    NULL, mtd->size,
590 					    (u_char *)(p_buf), 0);
591 			free(p_buf);
592 			if (!ret)
593 				return blkcnt;
594 			else
595 				return 0;
596 		}
597 	} else {
598 		return 0;
599 	}
600 
601 	return 0;
602 }
603 
604 ulong mtd_derase(struct udevice *udev, lbaint_t start,
605 		 lbaint_t blkcnt)
606 {
607 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
608 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
609 	loff_t off = (loff_t)(start * 512);
610 	size_t len = blkcnt * 512;
611 #endif
612 	struct mtd_info *mtd;
613 	int ret = 0;
614 
615 	if (!desc)
616 		return ret;
617 
618 	mtd = desc->bdev->priv;
619 	if (!mtd)
620 		return 0;
621 
622 	pr_debug("mtd derase %s %lx %lx\n", mtd->name, start, blkcnt);
623 
624 	if (blkcnt == 0)
625 		return 0;
626 
627 	if (desc->devnum == BLK_MTD_NAND ||
628 	    desc->devnum == BLK_MTD_SPI_NAND ||
629 	    desc->devnum == BLK_MTD_SPI_NOR) {
630 		ret = mtd_map_erase(mtd, off, len);
631 		if (ret)
632 			return ret;
633 	} else {
634 		return 0;
635 	}
636 
637 	return 0;
638 }
639 #endif
640 
641 static int mtd_blk_probe(struct udevice *udev)
642 {
643 	struct mtd_info *mtd;
644 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
645 	int ret, i = 0;
646 
647 	mtd = dev_get_uclass_priv(udev->parent);
648 	if (mtd->type == MTD_NANDFLASH && desc->devnum == BLK_MTD_NAND) {
649 #ifndef CONFIG_SPL_BUILD
650 		mtd = dev_get_priv(udev->parent);
651 #endif
652 	}
653 
654 	/* Fill mtd devices information */
655 	if (is_power_of_2(mtd->erasesize))
656 		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
657 	else
658 		mtd->erasesize_shift = 0;
659 
660 	if (is_power_of_2(mtd->writesize))
661 		mtd->writesize_shift = ffs(mtd->writesize) - 1;
662 	else
663 		mtd->writesize_shift = 0;
664 
665 	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
666 	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
667 
668 	desc->bdev->priv = mtd;
669 	sprintf(desc->vendor, "0x%.4x", 0x2207);
670 	if (strncmp(mtd->name, "nand", 4) == 0)
671 		memcpy(desc->product, "rk-nand", strlen("rk-nand"));
672 	else
673 		memcpy(desc->product, mtd->name, strlen(mtd->name));
674 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
675 	if (mtd->type == MTD_NANDFLASH) {
676 #ifdef CONFIG_NAND
677 		if (desc->devnum == BLK_MTD_NAND)
678 			i = NAND_BBT_SCAN_MAXBLOCKS;
679 		else if (desc->devnum == BLK_MTD_SPI_NAND)
680 			i = NANDDEV_BBT_SCAN_MAXBLOCKS;
681 #endif
682 
683 		/*
684 		 * Find the first useful block in the end,
685 		 * and it is the end lba of the nand storage.
686 		 */
687 		for (; i < (mtd->size / mtd->erasesize); i++) {
688 			ret =  mtd_block_isbad(mtd,
689 					       mtd->size - mtd->erasesize * (i + 1));
690 			if (!ret) {
691 				desc->lba = (mtd->size >> 9) -
692 					(mtd->erasesize >> 9) * i;
693 				break;
694 			}
695 		}
696 	} else {
697 		desc->lba = mtd->size >> 9;
698 	}
699 
700 	debug("MTD: desc->lba is %lx\n", desc->lba);
701 
702 	return 0;
703 }
704 
705 static const struct blk_ops mtd_blk_ops = {
706 	.read	= mtd_dread,
707 #if CONFIG_IS_ENABLED(MTD_WRITE)
708 	.write	= mtd_dwrite,
709 	.erase	= mtd_derase,
710 #endif
711 };
712 
713 U_BOOT_DRIVER(mtd_blk) = {
714 	.name		= "mtd_blk",
715 	.id		= UCLASS_BLK,
716 	.ops		= &mtd_blk_ops,
717 	.probe		= mtd_blk_probe,
718 };
719