xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 853fc11fcc1d87456eaa9ed149fad3ba4e01f06f)
1054229abSJason Zhu /*
2054229abSJason Zhu  * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3054229abSJason Zhu  *
4054229abSJason Zhu  * SPDX-License-Identifier:	GPL-2.0+
5054229abSJason Zhu  */
6054229abSJason Zhu 
7054229abSJason Zhu #include <common.h>
822dccd11SJason Zhu #include <blk.h>
922dccd11SJason Zhu #include <boot_rkimg.h>
10054229abSJason Zhu #include <dm.h>
11054229abSJason Zhu #include <errno.h>
1222dccd11SJason Zhu #include <malloc.h>
13054229abSJason Zhu #include <nand.h>
1422dccd11SJason Zhu #include <part.h>
153fb7bf02SJason Zhu #include <spi.h>
16054229abSJason Zhu #include <dm/device-internal.h>
173fb7bf02SJason Zhu #include <linux/mtd/spi-nor.h>
18054229abSJason Zhu 
1922dccd11SJason Zhu #define MTD_PART_NAND_HEAD		"mtdparts="
2004a8326aSJason Zhu #define MTD_ROOT_PART_NUM		"ubi.mtd="
2104a8326aSJason Zhu #define MTD_ROOT_PART_NAME		"root=ubi0:rootfs"
2222dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE		512
2322dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
2422dccd11SJason Zhu 
25c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
26c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
27c9e94690SJon Lin 
281f21bf61SJon Lin static int *mtd_map_blk_table;
291f21bf61SJon Lin 
301f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc,
311f21bf61SJon Lin 			   loff_t offset,
321f21bf61SJon Lin 			   size_t length)
331f21bf61SJon Lin {
341f21bf61SJon Lin 	u32 blk_total, blk_begin, blk_cnt;
351f21bf61SJon Lin 	struct mtd_info *mtd = NULL;
361f21bf61SJon Lin 	int i, j;
371f21bf61SJon Lin 
381f21bf61SJon Lin 	if (!desc)
391f21bf61SJon Lin 		return -ENODEV;
401f21bf61SJon Lin 
411f21bf61SJon Lin 	if (desc->devnum == BLK_MTD_NAND) {
421f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
431f21bf61SJon Lin 		mtd = dev_get_priv(desc->bdev->parent);
441f21bf61SJon Lin #endif
451f21bf61SJon Lin 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
461f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
471f21bf61SJon Lin 		mtd = desc->bdev->priv;
481f21bf61SJon Lin #endif
491f21bf61SJon Lin 	}
501f21bf61SJon Lin 
511f21bf61SJon Lin 	if (!mtd) {
521f21bf61SJon Lin 		return -ENODEV;
531f21bf61SJon Lin 	} else {
54b4e07918SJon Lin 		blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift;
551f21bf61SJon Lin 		if (!mtd_map_blk_table) {
560f1dc487SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int));
570f1dc487SJon Lin 			for (i = 0; i < blk_total; i++)
580f1dc487SJon Lin 				mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN;
591f21bf61SJon Lin 		}
601f21bf61SJon Lin 
61b4e07918SJon Lin 		blk_begin = (u32)offset >> mtd->erasesize_shift;
62b4e07918SJon Lin 		blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length) >> mtd->erasesize_shift);
630f1dc487SJon Lin 		if (blk_begin >= blk_total) {
640f1dc487SJon Lin 			pr_err("map table blk begin[%d] overflow\n", blk_begin);
650f1dc487SJon Lin 			return -EINVAL;
660f1dc487SJon Lin 		}
67d6290238SJon Lin 		if ((blk_begin + blk_cnt) > blk_total)
68d6290238SJon Lin 			blk_cnt = blk_total - blk_begin;
69c9e94690SJon Lin 
70c9e94690SJon Lin 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
71c9e94690SJon Lin 			return 0;
72c9e94690SJon Lin 
731f21bf61SJon Lin 		j = 0;
741f21bf61SJon Lin 		 /* should not across blk_cnt */
751f21bf61SJon Lin 		for (i = 0; i < blk_cnt; i++) {
761f21bf61SJon Lin 			if (j >= blk_cnt)
77c9e94690SJon Lin 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
781f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
79b4e07918SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) {
801f21bf61SJon Lin 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
811f21bf61SJon Lin 					j++;
821f21bf61SJon Lin 					if (j == blk_cnt)
831f21bf61SJon Lin 						j++;
841f21bf61SJon Lin 					break;
851f21bf61SJon Lin 				}
861f21bf61SJon Lin 			}
871f21bf61SJon Lin 		}
881f21bf61SJon Lin 
891f21bf61SJon Lin 		return 0;
901f21bf61SJon Lin 	}
911f21bf61SJon Lin }
921f21bf61SJon Lin 
93c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off)
94c402731fSJon Lin {
95c402731fSJon Lin 	bool mapped;
96c402731fSJon Lin 	loff_t offset = *off;
97c402731fSJon Lin 	size_t block_offset = offset & (mtd->erasesize - 1);
98c402731fSJon Lin 
99c402731fSJon Lin 	mapped = false;
100c402731fSJon Lin 	if (!mtd_map_blk_table ||
101c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
102c402731fSJon Lin 	    MTD_BLK_TABLE_BLOCK_UNKNOWN ||
103c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
104c402731fSJon Lin 	    0xffffffff)
105c402731fSJon Lin 		return mapped;
106c402731fSJon Lin 
107c402731fSJon Lin 	mapped = true;
108c402731fSJon Lin 	*off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >>
109c402731fSJon Lin 		mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset);
110c402731fSJon Lin 
111c402731fSJon Lin 	return mapped;
112c402731fSJon Lin }
113c402731fSJon Lin 
114a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc)
115a07b97f2SJason Zhu {
116a07b97f2SJason Zhu 	disk_partition_t info;
117a07b97f2SJason Zhu 	int i, ret;
118a07b97f2SJason Zhu 
119a07b97f2SJason Zhu 	if (!desc)
120a07b97f2SJason Zhu 		return;
121a07b97f2SJason Zhu 
122a07b97f2SJason Zhu 	if (desc->if_type != IF_TYPE_MTD)
123a07b97f2SJason Zhu 		return;
124a07b97f2SJason Zhu 
125a07b97f2SJason Zhu 	for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
126a07b97f2SJason Zhu 		ret = part_get_info(desc, i, &info);
127a07b97f2SJason Zhu 		if (ret != 0)
128a07b97f2SJason Zhu 			continue;
129a07b97f2SJason Zhu 
130a07b97f2SJason Zhu 		if (mtd_blk_map_table_init(desc,
131a07b97f2SJason Zhu 					   info.start << 9,
132a07b97f2SJason Zhu 					   info.size << 9)) {
1339ee38883SJon Lin 			pr_debug("mtd block map table fail\n");
134a07b97f2SJason Zhu 		}
135a07b97f2SJason Zhu 	}
136a07b97f2SJason Zhu }
137a07b97f2SJason Zhu 
1381f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
1391f21bf61SJon Lin 				       size_t *length, size_t *actual,
1401f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
1411f21bf61SJon Lin {
1421f21bf61SJon Lin 	size_t left_to_read = *length;
1431f21bf61SJon Lin 	u_char *p_buffer = buffer;
1441f21bf61SJon Lin 	int rval;
1451f21bf61SJon Lin 
1461f21bf61SJon Lin 	while (left_to_read > 0) {
147360a2911SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
1481f21bf61SJon Lin 		size_t read_length;
149360a2911SJon Lin 		loff_t mapped_offset;
1501f21bf61SJon Lin 
1511f21bf61SJon Lin 		if (offset >= mtd->size)
1521f21bf61SJon Lin 			return 0;
1531f21bf61SJon Lin 
154360a2911SJon Lin 		mapped_offset = offset;
155360a2911SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
156360a2911SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
157360a2911SJon Lin 					    ~(mtd->erasesize - 1))) {
158360a2911SJon Lin 				printf("Skipping bad block 0x%08llx\n",
159360a2911SJon Lin 				       offset & ~(mtd->erasesize - 1));
160360a2911SJon Lin 				offset += mtd->erasesize - block_offset;
1611f21bf61SJon Lin 				continue;
1621f21bf61SJon Lin 			}
1631f21bf61SJon Lin 		}
1641f21bf61SJon Lin 
165360a2911SJon Lin 		if (left_to_read < (mtd->erasesize - block_offset))
1661f21bf61SJon Lin 			read_length = left_to_read;
1671f21bf61SJon Lin 		else
168360a2911SJon Lin 			read_length = mtd->erasesize - block_offset;
1691f21bf61SJon Lin 
170360a2911SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
1711f21bf61SJon Lin 				p_buffer);
1721f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
1731f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
174c402731fSJon Lin 			       offset, rval);
1751f21bf61SJon Lin 			*length -= left_to_read;
1761f21bf61SJon Lin 			return rval;
1771f21bf61SJon Lin 		}
1781f21bf61SJon Lin 
1791f21bf61SJon Lin 		left_to_read -= read_length;
1801f21bf61SJon Lin 		offset       += read_length;
1811f21bf61SJon Lin 		p_buffer     += read_length;
1821f21bf61SJon Lin 	}
1831f21bf61SJon Lin 
1841f21bf61SJon Lin 	return 0;
1851f21bf61SJon Lin }
1861f21bf61SJon Lin 
1879ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset,
1889ee38883SJon Lin 					size_t *length, size_t *actual,
1899ee38883SJon Lin 					loff_t lim, u_char *buffer, int flags)
1909ee38883SJon Lin {
1919ee38883SJon Lin 	int rval = 0, blocksize;
1929ee38883SJon Lin 	size_t left_to_write = *length;
1939ee38883SJon Lin 	u_char *p_buffer = buffer;
1949ee38883SJon Lin 	struct erase_info ei;
1959ee38883SJon Lin 
1969ee38883SJon Lin 	blocksize = mtd->erasesize;
1979ee38883SJon Lin 
1989ee38883SJon Lin 	/*
1999ee38883SJon Lin 	 * nand_write() handles unaligned, partial page writes.
2009ee38883SJon Lin 	 *
2019ee38883SJon Lin 	 * We allow length to be unaligned, for convenience in
2029ee38883SJon Lin 	 * using the $filesize variable.
2039ee38883SJon Lin 	 *
2049ee38883SJon Lin 	 * However, starting at an unaligned offset makes the
2059ee38883SJon Lin 	 * semantics of bad block skipping ambiguous (really,
2069ee38883SJon Lin 	 * you should only start a block skipping access at a
2079ee38883SJon Lin 	 * partition boundary).  So don't try to handle that.
2089ee38883SJon Lin 	 */
2099ee38883SJon Lin 	if ((offset & (mtd->writesize - 1)) != 0) {
2109ee38883SJon Lin 		printf("Attempt to write non page-aligned data\n");
2119ee38883SJon Lin 		*length = 0;
2129ee38883SJon Lin 		return -EINVAL;
2139ee38883SJon Lin 	}
2149ee38883SJon Lin 
2159ee38883SJon Lin 	while (left_to_write > 0) {
2169ee38883SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
2179ee38883SJon Lin 		size_t write_size, truncated_write_size;
2189ee38883SJon Lin 		loff_t mapped_offset;
2199ee38883SJon Lin 
2209ee38883SJon Lin 		if (offset >= mtd->size)
2219ee38883SJon Lin 			return 0;
2229ee38883SJon Lin 
2239ee38883SJon Lin 		mapped_offset = offset;
2249ee38883SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
2259ee38883SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
2269ee38883SJon Lin 					    ~(mtd->erasesize - 1))) {
2279ee38883SJon Lin 				printf("Skipping bad block 0x%08llx\n",
2289ee38883SJon Lin 				       offset & ~(mtd->erasesize - 1));
2299ee38883SJon Lin 				offset += mtd->erasesize - block_offset;
2309ee38883SJon Lin 				continue;
2319ee38883SJon Lin 			}
2329ee38883SJon Lin 		}
2339ee38883SJon Lin 
2349ee38883SJon Lin 		if (!(mapped_offset & mtd->erasesize_mask)) {
2359ee38883SJon Lin 			memset(&ei, 0, sizeof(struct erase_info));
2369ee38883SJon Lin 			ei.addr = mapped_offset;
2379ee38883SJon Lin 			ei.len  = mtd->erasesize;
2389ee38883SJon Lin 			rval = mtd_erase(mtd, &ei);
2399ee38883SJon Lin 			if (rval) {
2409ee38883SJon Lin 				pr_info("error %d while erasing %llx\n", rval,
2419ee38883SJon Lin 					mapped_offset);
2429ee38883SJon Lin 				return rval;
2439ee38883SJon Lin 			}
2449ee38883SJon Lin 		}
2459ee38883SJon Lin 
2469ee38883SJon Lin 		if (left_to_write < (blocksize - block_offset))
2479ee38883SJon Lin 			write_size = left_to_write;
2489ee38883SJon Lin 		else
2499ee38883SJon Lin 			write_size = blocksize - block_offset;
2509ee38883SJon Lin 
2519ee38883SJon Lin 		truncated_write_size = write_size;
2529ee38883SJon Lin 		rval = mtd_write(mtd, mapped_offset, truncated_write_size,
2539ee38883SJon Lin 				 (size_t *)(&truncated_write_size), p_buffer);
2549ee38883SJon Lin 
2559ee38883SJon Lin 		offset += write_size;
2569ee38883SJon Lin 		p_buffer += write_size;
2579ee38883SJon Lin 
2589ee38883SJon Lin 		if (rval != 0) {
2599ee38883SJon Lin 			printf("NAND write to offset %llx failed %d\n",
2609ee38883SJon Lin 			       offset, rval);
2619ee38883SJon Lin 			*length -= left_to_write;
2629ee38883SJon Lin 			return rval;
2639ee38883SJon Lin 		}
2649ee38883SJon Lin 
2659ee38883SJon Lin 		left_to_write -= write_size;
2669ee38883SJon Lin 	}
2679ee38883SJon Lin 
2689ee38883SJon Lin 	return 0;
2699ee38883SJon Lin }
2709ee38883SJon Lin 
27122dccd11SJason Zhu char *mtd_part_parse(void)
27222dccd11SJason Zhu {
27322dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
27422dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
27504a8326aSJason Zhu 	char mtd_root_part_info[30] = {0};
27622dccd11SJason Zhu 	struct blk_desc *dev_desc;
27722dccd11SJason Zhu 	disk_partition_t info;
27822dccd11SJason Zhu 	char *mtd_part_info_p;
279c9289eddSJason Zhu 	struct mtd_info *mtd;
28022dccd11SJason Zhu 	char *mtd_part_info;
28122dccd11SJason Zhu 	int ret;
28222dccd11SJason Zhu 	int p;
28322dccd11SJason Zhu 
28422dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
28522dccd11SJason Zhu 	if (!dev_desc)
28622dccd11SJason Zhu 		return NULL;
28722dccd11SJason Zhu 
288c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
289ec6d4288SJason Zhu 	if (!mtd)
290ec6d4288SJason Zhu 		return NULL;
291ec6d4288SJason Zhu 
29204a8326aSJason Zhu 	p = part_get_info_by_name(dev_desc, PART_SYSTEM, &info);
29304a8326aSJason Zhu 	if (p > 0) {
29404a8326aSJason Zhu 		snprintf(mtd_root_part_info, 30, "%s%d %s", MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME);
29504a8326aSJason Zhu 		env_update("bootargs", mtd_root_part_info);
29604a8326aSJason Zhu 	}
29704a8326aSJason Zhu 
29822dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
29922dccd11SJason Zhu 	if (!mtd_part_info) {
30022dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
30122dccd11SJason Zhu 		return NULL;
30222dccd11SJason Zhu 	}
30322dccd11SJason Zhu 
30422dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
30522dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
30622dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
30722dccd11SJason Zhu 		 dev_desc->product);
30822dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
30922dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
31022dccd11SJason Zhu 
31122dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
31222dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
31322dccd11SJason Zhu 		if (ret)
31422dccd11SJason Zhu 			break;
31522dccd11SJason Zhu 
31622dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
31722dccd11SJason Zhu 		      (int)(size_t)info.start);
31822dccd11SJason Zhu 
31922dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
32022dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
32122dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
32222dccd11SJason Zhu 			 info.name);
32322dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
32422dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
32522dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
32622dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
32722dccd11SJason Zhu 			 info.name);
32822dccd11SJason Zhu 		strcat(mtd_part_info, ",");
32922dccd11SJason Zhu 		if (part_get_info(dev_desc, p + 1, &info)) {
330c9289eddSJason Zhu 			/* Nand flash is erased by block and gpt table just
331c9289eddSJason Zhu 			 * resserve 33 sectors for the last partition. This
332c9289eddSJason Zhu 			 * will erase the backup gpt table by user program,
333c9289eddSJason Zhu 			 * so reserve one block.
334c9289eddSJason Zhu 			 */
335c9289eddSJason Zhu 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
336c9289eddSJason Zhu 				 (int)(size_t)(info.size -
337c9289eddSJason Zhu 				 (info.size - 1) %
338c9289eddSJason Zhu 				 (mtd->erasesize >> 9) - 1) << 9,
33922dccd11SJason Zhu 				 (int)(size_t)info.start << 9,
34022dccd11SJason Zhu 				 info.name);
34122dccd11SJason Zhu 			break;
34222dccd11SJason Zhu 		}
34322dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
34422dccd11SJason Zhu 		data_len -= length;
34522dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
34622dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
34722dccd11SJason Zhu 	}
34822dccd11SJason Zhu 
34922dccd11SJason Zhu 	return mtd_part_info;
35022dccd11SJason Zhu }
35122dccd11SJason Zhu 
352054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
353054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
354054229abSJason Zhu {
355054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
3560dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
3576e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
3586e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
3590dccd0d8SJason Zhu #endif
3606e8ac5a8SJason Zhu 	struct mtd_info *mtd;
361bbb83f58SJason Zhu 	int ret = 0;
362054229abSJason Zhu 
363054229abSJason Zhu 	if (!desc)
36439e38ab3SJason Zhu 		return ret;
365054229abSJason Zhu 
3666e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
3676e8ac5a8SJason Zhu 	if (!mtd)
3686e8ac5a8SJason Zhu 		return 0;
3696e8ac5a8SJason Zhu 
370054229abSJason Zhu 	if (blkcnt == 0)
371054229abSJason Zhu 		return 0;
372054229abSJason Zhu 
3739ee38883SJon Lin 	pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt);
3749ee38883SJon Lin 
375054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
37639e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
3778cf6fca4SJason Zhu 		mtd = dev_get_priv(udev->parent);
3788cf6fca4SJason Zhu 		if (!mtd)
3798cf6fca4SJason Zhu 			return 0;
3808cf6fca4SJason Zhu 
3816e8ac5a8SJason Zhu 		ret = nand_read_skip_bad(mtd, off, &rwsize,
3826e8ac5a8SJason Zhu 					 NULL, mtd->size,
383054229abSJason Zhu 					 (u_char *)(dst));
3841f21bf61SJon Lin #else
3851f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
3861f21bf61SJon Lin 				   NULL, mtd->size,
3871f21bf61SJon Lin 				   (u_char *)(dst));
3881f21bf61SJon Lin #endif
3896e8ac5a8SJason Zhu 		if (!ret)
390054229abSJason Zhu 			return blkcnt;
3916e8ac5a8SJason Zhu 		else
3926e8ac5a8SJason Zhu 			return 0;
393054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
3941f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
395bbb83f58SJason Zhu 				   NULL, mtd->size,
396bbb83f58SJason Zhu 				   (u_char *)(dst));
397bbb83f58SJason Zhu 		if (!ret)
3986e8ac5a8SJason Zhu 			return blkcnt;
3996e8ac5a8SJason Zhu 		else
400054229abSJason Zhu 			return 0;
401054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
4020dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
4033fb7bf02SJason Zhu 		struct spi_nor *nor = (struct spi_nor *)mtd->priv;
4043fb7bf02SJason Zhu 		struct spi_slave *spi = nor->spi;
4050dccd0d8SJason Zhu 		size_t retlen_nor;
4060dccd0d8SJason Zhu 
4073fb7bf02SJason Zhu 		if (desc->op_flag == BLK_PRE_RW)
4083fb7bf02SJason Zhu 			spi->mode |= SPI_DMA_PREPARE;
4090dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
4103fb7bf02SJason Zhu 		if (desc->op_flag == BLK_PRE_RW)
4113fb7bf02SJason Zhu 			spi->mode |= SPI_DMA_PREPARE;
4123fb7bf02SJason Zhu 
4130dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
4140dccd0d8SJason Zhu 			return blkcnt;
4150dccd0d8SJason Zhu 		else
4160dccd0d8SJason Zhu #endif
417054229abSJason Zhu 			return 0;
418054229abSJason Zhu 	} else {
419054229abSJason Zhu 		return 0;
420054229abSJason Zhu 	}
421054229abSJason Zhu }
422054229abSJason Zhu 
423054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
424054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
425054229abSJason Zhu {
4269ee38883SJon Lin 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
4279ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
4289ee38883SJon Lin 	loff_t off = (loff_t)(start * 512);
4299ee38883SJon Lin 	size_t rwsize = blkcnt * 512;
4309ee38883SJon Lin #endif
4319ee38883SJon Lin 	struct mtd_info *mtd;
4329ee38883SJon Lin 	int ret = 0;
4339ee38883SJon Lin 
4349ee38883SJon Lin 	if (!desc)
4359ee38883SJon Lin 		return ret;
4369ee38883SJon Lin 
4379ee38883SJon Lin 	mtd = desc->bdev->priv;
4389ee38883SJon Lin 	if (!mtd)
4399ee38883SJon Lin 		return 0;
4409ee38883SJon Lin 
4419ee38883SJon Lin 	pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt);
4429ee38883SJon Lin 
4439ee38883SJon Lin 	if (blkcnt == 0)
4449ee38883SJon Lin 		return 0;
4459ee38883SJon Lin 
4469ee38883SJon Lin 	if (desc->devnum == BLK_MTD_NAND ||
4479ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NAND ||
4489ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NOR) {
449*853fc11fSJon Lin 		if (desc->op_flag == BLK_MTD_CONT_WRITE) {
450*853fc11fSJon Lin 			ret = mtd_map_write(mtd, off, &rwsize,
451*853fc11fSJon Lin 					    NULL, mtd->size,
452*853fc11fSJon Lin 					    (u_char *)(src), 0);
453*853fc11fSJon Lin 			if (!ret)
454*853fc11fSJon Lin 				return blkcnt;
455*853fc11fSJon Lin 			else
456*853fc11fSJon Lin 				return 0;
457*853fc11fSJon Lin 		} else {
45842439462SJon Lin 			lbaint_t off_aligned, alinged;
45942439462SJon Lin 			size_t rwsize_aligned;
46042439462SJon Lin 			u8 *p_buf;
46142439462SJon Lin 
46242439462SJon Lin 			alinged = off & mtd->erasesize_mask;
46342439462SJon Lin 			off_aligned = off - alinged;
46442439462SJon Lin 			rwsize_aligned = rwsize + alinged;
46542439462SJon Lin 			rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) &
46642439462SJon Lin 				~(mtd->erasesize - 1);
46742439462SJon Lin 
46842439462SJon Lin 			p_buf = malloc(rwsize_aligned);
46942439462SJon Lin 			if (!p_buf) {
47042439462SJon Lin 				printf("%s: Fail to malloc!", __func__);
47142439462SJon Lin 				return 0;
47242439462SJon Lin 			}
47342439462SJon Lin 
47442439462SJon Lin 			ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned,
47542439462SJon Lin 					   NULL, mtd->size,
47642439462SJon Lin 					   (u_char *)(p_buf));
47742439462SJon Lin 			if (ret) {
47842439462SJon Lin 				free(p_buf);
47942439462SJon Lin 				return 0;
48042439462SJon Lin 			}
48142439462SJon Lin 
48242439462SJon Lin 			memcpy(p_buf + alinged, src, rwsize);
48342439462SJon Lin 
48442439462SJon Lin 			ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned,
48542439462SJon Lin 					    NULL, mtd->size,
48642439462SJon Lin 					    (u_char *)(p_buf), 0);
48742439462SJon Lin 			free(p_buf);
48842439462SJon Lin 			if (!ret)
48942439462SJon Lin 				return blkcnt;
49042439462SJon Lin 			else
49142439462SJon Lin 				return 0;
49242439462SJon Lin 		}
4939ee38883SJon Lin 	} else {
4949ee38883SJon Lin 		return 0;
4959ee38883SJon Lin 	}
4969ee38883SJon Lin 
497054229abSJason Zhu 	return 0;
498054229abSJason Zhu }
499054229abSJason Zhu 
500054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
501054229abSJason Zhu 		 lbaint_t blkcnt)
502054229abSJason Zhu {
503054229abSJason Zhu 	/* Not implemented */
504054229abSJason Zhu 	return 0;
505054229abSJason Zhu }
506054229abSJason Zhu 
507054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
508054229abSJason Zhu {
5096e8ac5a8SJason Zhu 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
510054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
5111d39542fSJason Zhu 	int ret, i;
512054229abSJason Zhu 
513c9289eddSJason Zhu 	desc->bdev->priv = mtd;
514054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
515e6482de4SJason Zhu 	memcpy(desc->product, mtd->name, strlen(mtd->name));
516054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
517f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
518f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
519f3ba630bSJason Zhu 			mtd = dev_get_priv(udev->parent);
5201d39542fSJason Zhu 		/*
5211d39542fSJason Zhu 		 * Find the first useful block in the end,
5221d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
5231d39542fSJason Zhu 		 */
5241d39542fSJason Zhu 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
5251d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
5261d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
5271d39542fSJason Zhu 			if (!ret) {
5281d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
5291d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
5301d39542fSJason Zhu 				break;
5311d39542fSJason Zhu 			}
5321d39542fSJason Zhu 		}
533f1892190SJason Zhu 	} else {
534f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
535f1892190SJason Zhu 	}
536054229abSJason Zhu 
5371d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
5381d39542fSJason Zhu 
539054229abSJason Zhu 	return 0;
540054229abSJason Zhu }
541054229abSJason Zhu 
542054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
543054229abSJason Zhu 	.read	= mtd_dread,
544054229abSJason Zhu #ifndef CONFIG_SPL_BUILD
545054229abSJason Zhu 	.write	= mtd_dwrite,
546054229abSJason Zhu 	.erase	= mtd_derase,
547054229abSJason Zhu #endif
548054229abSJason Zhu };
549054229abSJason Zhu 
550054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
551054229abSJason Zhu 	.name		= "mtd_blk",
552054229abSJason Zhu 	.id		= UCLASS_BLK,
553054229abSJason Zhu 	.ops		= &mtd_blk_ops,
554054229abSJason Zhu 	.probe		= mtd_blk_probe,
555054229abSJason Zhu };
556