xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 9ee38883a7649a36beefc8a2b6c4e089afdef0a2)
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>
15054229abSJason Zhu #include <dm/device-internal.h>
16054229abSJason Zhu 
1722dccd11SJason Zhu #define MTD_PART_NAND_HEAD		"mtdparts="
1822dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE		512
1922dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
2022dccd11SJason Zhu 
21c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
22c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
23c9e94690SJon Lin 
241f21bf61SJon Lin static int *mtd_map_blk_table;
251f21bf61SJon Lin 
261f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc,
271f21bf61SJon Lin 			   loff_t offset,
281f21bf61SJon Lin 			   size_t length)
291f21bf61SJon Lin {
301f21bf61SJon Lin 	u32 blk_total, blk_begin, blk_cnt;
311f21bf61SJon Lin 	struct mtd_info *mtd = NULL;
321f21bf61SJon Lin 	int i, j;
331f21bf61SJon Lin 
341f21bf61SJon Lin 	if (!desc)
351f21bf61SJon Lin 		return -ENODEV;
361f21bf61SJon Lin 
371f21bf61SJon Lin 	if (desc->devnum == BLK_MTD_NAND) {
381f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
391f21bf61SJon Lin 		mtd = dev_get_priv(desc->bdev->parent);
401f21bf61SJon Lin #endif
411f21bf61SJon Lin 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
421f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
431f21bf61SJon Lin 		mtd = desc->bdev->priv;
441f21bf61SJon Lin #endif
451f21bf61SJon Lin 	}
461f21bf61SJon Lin 
471f21bf61SJon Lin 	if (!mtd) {
481f21bf61SJon Lin 		return -ENODEV;
491f21bf61SJon Lin 	} else {
50b4e07918SJon Lin 		blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift;
511f21bf61SJon Lin 		if (!mtd_map_blk_table) {
521f21bf61SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
53c9e94690SJon Lin 			memset(mtd_map_blk_table, MTD_BLK_TABLE_BLOCK_UNKNOWN,
54c9e94690SJon Lin 			       blk_total * 4);
551f21bf61SJon Lin 		}
561f21bf61SJon Lin 
57b4e07918SJon Lin 		blk_begin = (u32)offset >> mtd->erasesize_shift;
58b4e07918SJon Lin 		blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length) >> mtd->erasesize_shift);
59d6290238SJon Lin 		if ((blk_begin + blk_cnt) > blk_total)
60d6290238SJon Lin 			blk_cnt = blk_total - blk_begin;
61c9e94690SJon Lin 
62c9e94690SJon Lin 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
63c9e94690SJon Lin 			return 0;
64c9e94690SJon Lin 
651f21bf61SJon Lin 		j = 0;
661f21bf61SJon Lin 		 /* should not across blk_cnt */
671f21bf61SJon Lin 		for (i = 0; i < blk_cnt; i++) {
681f21bf61SJon Lin 			if (j >= blk_cnt)
69c9e94690SJon Lin 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
701f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
71b4e07918SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) {
721f21bf61SJon Lin 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
731f21bf61SJon Lin 					j++;
741f21bf61SJon Lin 					if (j == blk_cnt)
751f21bf61SJon Lin 						j++;
761f21bf61SJon Lin 					break;
771f21bf61SJon Lin 				}
781f21bf61SJon Lin 			}
791f21bf61SJon Lin 		}
801f21bf61SJon Lin 
811f21bf61SJon Lin 		return 0;
821f21bf61SJon Lin 	}
831f21bf61SJon Lin }
841f21bf61SJon Lin 
85c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off)
86c402731fSJon Lin {
87c402731fSJon Lin 	bool mapped;
88c402731fSJon Lin 	loff_t offset = *off;
89c402731fSJon Lin 	size_t block_offset = offset & (mtd->erasesize - 1);
90c402731fSJon Lin 
91c402731fSJon Lin 	mapped = false;
92c402731fSJon Lin 	if (!mtd_map_blk_table ||
93c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
94c402731fSJon Lin 	    MTD_BLK_TABLE_BLOCK_UNKNOWN ||
95c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
96c402731fSJon Lin 	    0xffffffff)
97c402731fSJon Lin 		return mapped;
98c402731fSJon Lin 
99c402731fSJon Lin 	mapped = true;
100c402731fSJon Lin 	*off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >>
101c402731fSJon Lin 		mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset);
102c402731fSJon Lin 
103c402731fSJon Lin 	return mapped;
104c402731fSJon Lin }
105c402731fSJon Lin 
106a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc)
107a07b97f2SJason Zhu {
108a07b97f2SJason Zhu 	disk_partition_t info;
109a07b97f2SJason Zhu 	int i, ret;
110a07b97f2SJason Zhu 
111a07b97f2SJason Zhu 	if (!desc)
112a07b97f2SJason Zhu 		return;
113a07b97f2SJason Zhu 
114a07b97f2SJason Zhu 	if (desc->if_type != IF_TYPE_MTD)
115a07b97f2SJason Zhu 		return;
116a07b97f2SJason Zhu 
117a07b97f2SJason Zhu 	for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
118a07b97f2SJason Zhu 		ret = part_get_info(desc, i, &info);
119a07b97f2SJason Zhu 		if (ret != 0)
120a07b97f2SJason Zhu 			continue;
121a07b97f2SJason Zhu 
122a07b97f2SJason Zhu 		if (mtd_blk_map_table_init(desc,
123a07b97f2SJason Zhu 					   info.start << 9,
124a07b97f2SJason Zhu 					   info.size << 9)) {
125*9ee38883SJon Lin 			pr_debug("mtd block map table fail\n");
126a07b97f2SJason Zhu 		}
127a07b97f2SJason Zhu 	}
128a07b97f2SJason Zhu }
129a07b97f2SJason Zhu 
1301f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
1311f21bf61SJon Lin 				       size_t *length, size_t *actual,
1321f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
1331f21bf61SJon Lin {
1341f21bf61SJon Lin 	size_t left_to_read = *length;
1351f21bf61SJon Lin 	u_char *p_buffer = buffer;
1361f21bf61SJon Lin 	int rval;
1371f21bf61SJon Lin 
1381f21bf61SJon Lin 	while (left_to_read > 0) {
139360a2911SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
1401f21bf61SJon Lin 		size_t read_length;
141360a2911SJon Lin 		loff_t mapped_offset;
1421f21bf61SJon Lin 
1431f21bf61SJon Lin 		if (offset >= mtd->size)
1441f21bf61SJon Lin 			return 0;
1451f21bf61SJon Lin 
146360a2911SJon Lin 		mapped_offset = offset;
147360a2911SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
148360a2911SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
149360a2911SJon Lin 					    ~(mtd->erasesize - 1))) {
150360a2911SJon Lin 				printf("Skipping bad block 0x%08llx\n",
151360a2911SJon Lin 				       offset & ~(mtd->erasesize - 1));
152360a2911SJon Lin 				offset += mtd->erasesize - block_offset;
1531f21bf61SJon Lin 				continue;
1541f21bf61SJon Lin 			}
1551f21bf61SJon Lin 		}
1561f21bf61SJon Lin 
157360a2911SJon Lin 		if (left_to_read < (mtd->erasesize - block_offset))
1581f21bf61SJon Lin 			read_length = left_to_read;
1591f21bf61SJon Lin 		else
160360a2911SJon Lin 			read_length = mtd->erasesize - block_offset;
1611f21bf61SJon Lin 
162360a2911SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
1631f21bf61SJon Lin 				p_buffer);
1641f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
1651f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
166c402731fSJon Lin 			       offset, rval);
1671f21bf61SJon Lin 			*length -= left_to_read;
1681f21bf61SJon Lin 			return rval;
1691f21bf61SJon Lin 		}
1701f21bf61SJon Lin 
1711f21bf61SJon Lin 		left_to_read -= read_length;
1721f21bf61SJon Lin 		offset       += read_length;
1731f21bf61SJon Lin 		p_buffer     += read_length;
1741f21bf61SJon Lin 	}
1751f21bf61SJon Lin 
1761f21bf61SJon Lin 	return 0;
1771f21bf61SJon Lin }
1781f21bf61SJon Lin 
179*9ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset,
180*9ee38883SJon Lin 					size_t *length, size_t *actual,
181*9ee38883SJon Lin 					loff_t lim, u_char *buffer, int flags)
182*9ee38883SJon Lin {
183*9ee38883SJon Lin 	int rval = 0, blocksize;
184*9ee38883SJon Lin 	size_t left_to_write = *length;
185*9ee38883SJon Lin 	u_char *p_buffer = buffer;
186*9ee38883SJon Lin 	struct erase_info ei;
187*9ee38883SJon Lin 
188*9ee38883SJon Lin 	blocksize = mtd->erasesize;
189*9ee38883SJon Lin 
190*9ee38883SJon Lin 	/*
191*9ee38883SJon Lin 	 * nand_write() handles unaligned, partial page writes.
192*9ee38883SJon Lin 	 *
193*9ee38883SJon Lin 	 * We allow length to be unaligned, for convenience in
194*9ee38883SJon Lin 	 * using the $filesize variable.
195*9ee38883SJon Lin 	 *
196*9ee38883SJon Lin 	 * However, starting at an unaligned offset makes the
197*9ee38883SJon Lin 	 * semantics of bad block skipping ambiguous (really,
198*9ee38883SJon Lin 	 * you should only start a block skipping access at a
199*9ee38883SJon Lin 	 * partition boundary).  So don't try to handle that.
200*9ee38883SJon Lin 	 */
201*9ee38883SJon Lin 	if ((offset & (mtd->writesize - 1)) != 0) {
202*9ee38883SJon Lin 		printf("Attempt to write non page-aligned data\n");
203*9ee38883SJon Lin 		*length = 0;
204*9ee38883SJon Lin 		return -EINVAL;
205*9ee38883SJon Lin 	}
206*9ee38883SJon Lin 
207*9ee38883SJon Lin 	while (left_to_write > 0) {
208*9ee38883SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
209*9ee38883SJon Lin 		size_t write_size, truncated_write_size;
210*9ee38883SJon Lin 		loff_t mapped_offset;
211*9ee38883SJon Lin 
212*9ee38883SJon Lin 		if (offset >= mtd->size)
213*9ee38883SJon Lin 			return 0;
214*9ee38883SJon Lin 
215*9ee38883SJon Lin 		mapped_offset = offset;
216*9ee38883SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
217*9ee38883SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
218*9ee38883SJon Lin 					    ~(mtd->erasesize - 1))) {
219*9ee38883SJon Lin 				printf("Skipping bad block 0x%08llx\n",
220*9ee38883SJon Lin 				       offset & ~(mtd->erasesize - 1));
221*9ee38883SJon Lin 				offset += mtd->erasesize - block_offset;
222*9ee38883SJon Lin 				continue;
223*9ee38883SJon Lin 			}
224*9ee38883SJon Lin 		}
225*9ee38883SJon Lin 
226*9ee38883SJon Lin 		if (!(mapped_offset & mtd->erasesize_mask)) {
227*9ee38883SJon Lin 			memset(&ei, 0, sizeof(struct erase_info));
228*9ee38883SJon Lin 			ei.addr = mapped_offset;
229*9ee38883SJon Lin 			ei.len  = mtd->erasesize;
230*9ee38883SJon Lin 			rval = mtd_erase(mtd, &ei);
231*9ee38883SJon Lin 			if (rval) {
232*9ee38883SJon Lin 				pr_info("error %d while erasing %llx\n", rval,
233*9ee38883SJon Lin 					mapped_offset);
234*9ee38883SJon Lin 				return rval;
235*9ee38883SJon Lin 			}
236*9ee38883SJon Lin 		}
237*9ee38883SJon Lin 
238*9ee38883SJon Lin 		if (left_to_write < (blocksize - block_offset))
239*9ee38883SJon Lin 			write_size = left_to_write;
240*9ee38883SJon Lin 		else
241*9ee38883SJon Lin 			write_size = blocksize - block_offset;
242*9ee38883SJon Lin 
243*9ee38883SJon Lin 		truncated_write_size = write_size;
244*9ee38883SJon Lin 		rval = mtd_write(mtd, mapped_offset, truncated_write_size,
245*9ee38883SJon Lin 				 (size_t *)(&truncated_write_size), p_buffer);
246*9ee38883SJon Lin 
247*9ee38883SJon Lin 		offset += write_size;
248*9ee38883SJon Lin 		p_buffer += write_size;
249*9ee38883SJon Lin 
250*9ee38883SJon Lin 		if (rval != 0) {
251*9ee38883SJon Lin 			printf("NAND write to offset %llx failed %d\n",
252*9ee38883SJon Lin 			       offset, rval);
253*9ee38883SJon Lin 			*length -= left_to_write;
254*9ee38883SJon Lin 			return rval;
255*9ee38883SJon Lin 		}
256*9ee38883SJon Lin 
257*9ee38883SJon Lin 		left_to_write -= write_size;
258*9ee38883SJon Lin 	}
259*9ee38883SJon Lin 
260*9ee38883SJon Lin 	return 0;
261*9ee38883SJon Lin }
262*9ee38883SJon Lin 
26322dccd11SJason Zhu char *mtd_part_parse(void)
26422dccd11SJason Zhu {
26522dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
26622dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
26722dccd11SJason Zhu 	struct blk_desc *dev_desc;
26822dccd11SJason Zhu 	disk_partition_t info;
26922dccd11SJason Zhu 	char *mtd_part_info_p;
270c9289eddSJason Zhu 	struct mtd_info *mtd;
27122dccd11SJason Zhu 	char *mtd_part_info;
27222dccd11SJason Zhu 	int ret;
27322dccd11SJason Zhu 	int p;
27422dccd11SJason Zhu 
27522dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
27622dccd11SJason Zhu 	if (!dev_desc)
27722dccd11SJason Zhu 		return NULL;
27822dccd11SJason Zhu 
279c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
280ec6d4288SJason Zhu 	if (!mtd)
281ec6d4288SJason Zhu 		return NULL;
282ec6d4288SJason Zhu 
28322dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
28422dccd11SJason Zhu 	if (!mtd_part_info) {
28522dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
28622dccd11SJason Zhu 		return NULL;
28722dccd11SJason Zhu 	}
28822dccd11SJason Zhu 
28922dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
29022dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
29122dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
29222dccd11SJason Zhu 		 dev_desc->product);
29322dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
29422dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
29522dccd11SJason Zhu 
29622dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
29722dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
29822dccd11SJason Zhu 		if (ret)
29922dccd11SJason Zhu 			break;
30022dccd11SJason Zhu 
30122dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
30222dccd11SJason Zhu 		      (int)(size_t)info.start);
30322dccd11SJason Zhu 
30422dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
30522dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
30622dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
30722dccd11SJason Zhu 			 info.name);
30822dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
30922dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
31022dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
31122dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
31222dccd11SJason Zhu 			 info.name);
31322dccd11SJason Zhu 		strcat(mtd_part_info, ",");
31422dccd11SJason Zhu 		if (part_get_info(dev_desc, p + 1, &info)) {
315c9289eddSJason Zhu 			/* Nand flash is erased by block and gpt table just
316c9289eddSJason Zhu 			 * resserve 33 sectors for the last partition. This
317c9289eddSJason Zhu 			 * will erase the backup gpt table by user program,
318c9289eddSJason Zhu 			 * so reserve one block.
319c9289eddSJason Zhu 			 */
320c9289eddSJason Zhu 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
321c9289eddSJason Zhu 				 (int)(size_t)(info.size -
322c9289eddSJason Zhu 				 (info.size - 1) %
323c9289eddSJason Zhu 				 (mtd->erasesize >> 9) - 1) << 9,
32422dccd11SJason Zhu 				 (int)(size_t)info.start << 9,
32522dccd11SJason Zhu 				 info.name);
32622dccd11SJason Zhu 			break;
32722dccd11SJason Zhu 		}
32822dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
32922dccd11SJason Zhu 		data_len -= length;
33022dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
33122dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
33222dccd11SJason Zhu 	}
33322dccd11SJason Zhu 
33422dccd11SJason Zhu 	return mtd_part_info;
33522dccd11SJason Zhu }
33622dccd11SJason Zhu 
337054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
338054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
339054229abSJason Zhu {
340054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
3410dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
3426e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
3436e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
3440dccd0d8SJason Zhu #endif
3456e8ac5a8SJason Zhu 	struct mtd_info *mtd;
346bbb83f58SJason Zhu 	int ret = 0;
347054229abSJason Zhu 
348054229abSJason Zhu 	if (!desc)
34939e38ab3SJason Zhu 		return ret;
350054229abSJason Zhu 
3516e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
3526e8ac5a8SJason Zhu 	if (!mtd)
3536e8ac5a8SJason Zhu 		return 0;
3546e8ac5a8SJason Zhu 
355054229abSJason Zhu 	if (blkcnt == 0)
356054229abSJason Zhu 		return 0;
357054229abSJason Zhu 
358*9ee38883SJon Lin 	pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt);
359*9ee38883SJon Lin 
360054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
36139e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
3628cf6fca4SJason Zhu 		mtd = dev_get_priv(udev->parent);
3638cf6fca4SJason Zhu 		if (!mtd)
3648cf6fca4SJason Zhu 			return 0;
3658cf6fca4SJason Zhu 
3666e8ac5a8SJason Zhu 		ret = nand_read_skip_bad(mtd, off, &rwsize,
3676e8ac5a8SJason Zhu 					 NULL, mtd->size,
368054229abSJason Zhu 					 (u_char *)(dst));
3691f21bf61SJon Lin #else
3701f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
3711f21bf61SJon Lin 				   NULL, mtd->size,
3721f21bf61SJon Lin 				   (u_char *)(dst));
3731f21bf61SJon Lin #endif
3746e8ac5a8SJason Zhu 		if (!ret)
375054229abSJason Zhu 			return blkcnt;
3766e8ac5a8SJason Zhu 		else
3776e8ac5a8SJason Zhu 			return 0;
378054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
3791f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
380bbb83f58SJason Zhu 				   NULL, mtd->size,
381bbb83f58SJason Zhu 				   (u_char *)(dst));
382bbb83f58SJason Zhu 		if (!ret)
3836e8ac5a8SJason Zhu 			return blkcnt;
3846e8ac5a8SJason Zhu 		else
385054229abSJason Zhu 			return 0;
386054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
3870dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
3880dccd0d8SJason Zhu 		size_t retlen_nor;
3890dccd0d8SJason Zhu 
3900dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
3910dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
3920dccd0d8SJason Zhu 			return blkcnt;
3930dccd0d8SJason Zhu 		else
3940dccd0d8SJason Zhu #endif
395054229abSJason Zhu 			return 0;
396054229abSJason Zhu 	} else {
397054229abSJason Zhu 		return 0;
398054229abSJason Zhu 	}
399054229abSJason Zhu }
400054229abSJason Zhu 
401054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
402054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
403054229abSJason Zhu {
404*9ee38883SJon Lin 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
405*9ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
406*9ee38883SJon Lin 	loff_t off = (loff_t)(start * 512);
407*9ee38883SJon Lin 	size_t rwsize = blkcnt * 512;
408*9ee38883SJon Lin #endif
409*9ee38883SJon Lin 	struct mtd_info *mtd;
410*9ee38883SJon Lin 	int ret = 0;
411*9ee38883SJon Lin 
412*9ee38883SJon Lin 	if (!desc)
413*9ee38883SJon Lin 		return ret;
414*9ee38883SJon Lin 
415*9ee38883SJon Lin 	mtd = desc->bdev->priv;
416*9ee38883SJon Lin 	if (!mtd)
417*9ee38883SJon Lin 		return 0;
418*9ee38883SJon Lin 
419*9ee38883SJon Lin 	pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt);
420*9ee38883SJon Lin 
421*9ee38883SJon Lin 	if (blkcnt == 0)
422*9ee38883SJon Lin 		return 0;
423*9ee38883SJon Lin 
424*9ee38883SJon Lin 	if (desc->devnum == BLK_MTD_NAND ||
425*9ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NAND ||
426*9ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NOR) {
427*9ee38883SJon Lin 		ret = mtd_map_write(mtd, off, &rwsize,
428*9ee38883SJon Lin 				    NULL, mtd->size,
429*9ee38883SJon Lin 				    (u_char *)(src), 0);
430*9ee38883SJon Lin 		if (!ret)
431*9ee38883SJon Lin 			return blkcnt;
432*9ee38883SJon Lin 		else
433*9ee38883SJon Lin 			return 0;
434*9ee38883SJon Lin 	} else {
435*9ee38883SJon Lin 		return 0;
436*9ee38883SJon Lin 	}
437*9ee38883SJon Lin 
438054229abSJason Zhu 	return 0;
439054229abSJason Zhu }
440054229abSJason Zhu 
441054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
442054229abSJason Zhu 		 lbaint_t blkcnt)
443054229abSJason Zhu {
444054229abSJason Zhu 	/* Not implemented */
445054229abSJason Zhu 	return 0;
446054229abSJason Zhu }
447054229abSJason Zhu 
448054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
449054229abSJason Zhu {
4506e8ac5a8SJason Zhu 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
451054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
4521d39542fSJason Zhu 	int ret, i;
453054229abSJason Zhu 
454c9289eddSJason Zhu 	desc->bdev->priv = mtd;
455054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
456e6482de4SJason Zhu 	memcpy(desc->product, mtd->name, strlen(mtd->name));
457054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
458f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
459f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
460f3ba630bSJason Zhu 			mtd = dev_get_priv(udev->parent);
4611d39542fSJason Zhu 		/*
4621d39542fSJason Zhu 		 * Find the first useful block in the end,
4631d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
4641d39542fSJason Zhu 		 */
4651d39542fSJason Zhu 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
4661d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
4671d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
4681d39542fSJason Zhu 			if (!ret) {
4691d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
4701d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
4711d39542fSJason Zhu 				break;
4721d39542fSJason Zhu 			}
4731d39542fSJason Zhu 		}
474f1892190SJason Zhu 	} else {
475f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
476f1892190SJason Zhu 	}
477054229abSJason Zhu 
4781d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
4791d39542fSJason Zhu 
480054229abSJason Zhu 	return 0;
481054229abSJason Zhu }
482054229abSJason Zhu 
483054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
484054229abSJason Zhu 	.read	= mtd_dread,
485054229abSJason Zhu #ifndef CONFIG_SPL_BUILD
486054229abSJason Zhu 	.write	= mtd_dwrite,
487054229abSJason Zhu 	.erase	= mtd_derase,
488054229abSJason Zhu #endif
489054229abSJason Zhu };
490054229abSJason Zhu 
491054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
492054229abSJason Zhu 	.name		= "mtd_blk",
493054229abSJason Zhu 	.id		= UCLASS_BLK,
494054229abSJason Zhu 	.ops		= &mtd_blk_ops,
495054229abSJason Zhu 	.probe		= mtd_blk_probe,
496054229abSJason Zhu };
497