xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision cc0876a813fd31a8ff5fa42d3f96b57e946c1040)
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>
12661bcdfeSJason Zhu #include <image.h>
1322dccd11SJason Zhu #include <malloc.h>
14054229abSJason Zhu #include <nand.h>
1522dccd11SJason Zhu #include <part.h>
163fb7bf02SJason Zhu #include <spi.h>
17054229abSJason Zhu #include <dm/device-internal.h>
183fb7bf02SJason Zhu #include <linux/mtd/spi-nor.h>
196524556dSJon Lin #ifdef CONFIG_NAND
206524556dSJon Lin #include <linux/mtd/nand.h>
216524556dSJon Lin #endif
22054229abSJason Zhu 
2322dccd11SJason Zhu #define MTD_PART_NAND_HEAD		"mtdparts="
2404a8326aSJason Zhu #define MTD_ROOT_PART_NUM		"ubi.mtd="
25*cc0876a8SWeiwen Chen #define MTD_ROOT_PART_NAME_UBIFS	"root=ubi0:rootfs"
26*cc0876a8SWeiwen Chen #define MTD_ROOT_PART_NAME_SQUASHFS	"root=/dev/ubiblock0_0"
2722dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE		512
2822dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
2922dccd11SJason Zhu 
30c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
31c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
32c9e94690SJon Lin 
331f21bf61SJon Lin static int *mtd_map_blk_table;
341f21bf61SJon Lin 
351f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc,
361f21bf61SJon Lin 			   loff_t offset,
371f21bf61SJon Lin 			   size_t length)
381f21bf61SJon Lin {
391f21bf61SJon Lin 	u32 blk_total, blk_begin, blk_cnt;
401f21bf61SJon Lin 	struct mtd_info *mtd = NULL;
411f21bf61SJon Lin 	int i, j;
421f21bf61SJon Lin 
431f21bf61SJon Lin 	if (!desc)
441f21bf61SJon Lin 		return -ENODEV;
451f21bf61SJon Lin 
466524556dSJon Lin 	switch (desc->devnum) {
476524556dSJon Lin 	case BLK_MTD_NAND:
486524556dSJon Lin 	case BLK_MTD_SPI_NAND:
491f21bf61SJon Lin 		mtd = desc->bdev->priv;
506524556dSJon Lin 		break;
516524556dSJon Lin 	default:
526524556dSJon Lin 		break;
531f21bf61SJon Lin 	}
541f21bf61SJon Lin 
551f21bf61SJon Lin 	if (!mtd) {
561f21bf61SJon Lin 		return -ENODEV;
571f21bf61SJon Lin 	} else {
58b4e07918SJon Lin 		blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift;
591f21bf61SJon Lin 		if (!mtd_map_blk_table) {
600f1dc487SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int));
616524556dSJon Lin 			if (!mtd_map_blk_table)
626524556dSJon Lin 				return -ENOMEM;
630f1dc487SJon Lin 			for (i = 0; i < blk_total; i++)
640f1dc487SJon Lin 				mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN;
651f21bf61SJon Lin 		}
661f21bf61SJon Lin 
67b4e07918SJon Lin 		blk_begin = (u32)offset >> mtd->erasesize_shift;
68e091dc9dSJon Lin 		blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length + \
69e091dc9dSJon Lin 			mtd->erasesize - 1) >> mtd->erasesize_shift);
700f1dc487SJon Lin 		if (blk_begin >= blk_total) {
710f1dc487SJon Lin 			pr_err("map table blk begin[%d] overflow\n", blk_begin);
720f1dc487SJon Lin 			return -EINVAL;
730f1dc487SJon Lin 		}
74d6290238SJon Lin 		if ((blk_begin + blk_cnt) > blk_total)
75d6290238SJon Lin 			blk_cnt = blk_total - blk_begin;
76c9e94690SJon Lin 
77c9e94690SJon Lin 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
78c9e94690SJon Lin 			return 0;
79c9e94690SJon Lin 
801f21bf61SJon Lin 		j = 0;
811f21bf61SJon Lin 		 /* should not across blk_cnt */
821f21bf61SJon Lin 		for (i = 0; i < blk_cnt; i++) {
831f21bf61SJon Lin 			if (j >= blk_cnt)
84c9e94690SJon Lin 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
851f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
86b4e07918SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) {
871f21bf61SJon Lin 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
881f21bf61SJon Lin 					j++;
891f21bf61SJon Lin 					if (j == blk_cnt)
901f21bf61SJon Lin 						j++;
911f21bf61SJon Lin 					break;
921f21bf61SJon Lin 				}
931f21bf61SJon Lin 			}
941f21bf61SJon Lin 		}
951f21bf61SJon Lin 
961f21bf61SJon Lin 		return 0;
971f21bf61SJon Lin 	}
981f21bf61SJon Lin }
991f21bf61SJon Lin 
100c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off)
101c402731fSJon Lin {
102c402731fSJon Lin 	bool mapped;
103c402731fSJon Lin 	loff_t offset = *off;
104c402731fSJon Lin 	size_t block_offset = offset & (mtd->erasesize - 1);
105c402731fSJon Lin 
106c402731fSJon Lin 	mapped = false;
107c402731fSJon Lin 	if (!mtd_map_blk_table ||
108c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
109c402731fSJon Lin 	    MTD_BLK_TABLE_BLOCK_UNKNOWN ||
110c402731fSJon Lin 	    mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] ==
111c402731fSJon Lin 	    0xffffffff)
112c402731fSJon Lin 		return mapped;
113c402731fSJon Lin 
114c402731fSJon Lin 	mapped = true;
115c402731fSJon Lin 	*off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >>
116c402731fSJon Lin 		mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset);
117c402731fSJon Lin 
118c402731fSJon Lin 	return mapped;
119c402731fSJon Lin }
120c402731fSJon Lin 
121a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc)
122a07b97f2SJason Zhu {
123a07b97f2SJason Zhu 	disk_partition_t info;
124a07b97f2SJason Zhu 	int i, ret;
125a07b97f2SJason Zhu 
126a07b97f2SJason Zhu 	if (!desc)
127a07b97f2SJason Zhu 		return;
128a07b97f2SJason Zhu 
129a07b97f2SJason Zhu 	if (desc->if_type != IF_TYPE_MTD)
130a07b97f2SJason Zhu 		return;
131a07b97f2SJason Zhu 
132a07b97f2SJason Zhu 	for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
133a07b97f2SJason Zhu 		ret = part_get_info(desc, i, &info);
134a07b97f2SJason Zhu 		if (ret != 0)
135a07b97f2SJason Zhu 			continue;
136a07b97f2SJason Zhu 
137a07b97f2SJason Zhu 		if (mtd_blk_map_table_init(desc,
138a07b97f2SJason Zhu 					   info.start << 9,
139a07b97f2SJason Zhu 					   info.size << 9)) {
1409ee38883SJon Lin 			pr_debug("mtd block map table fail\n");
141a07b97f2SJason Zhu 		}
142a07b97f2SJason Zhu 	}
143a07b97f2SJason Zhu }
144a07b97f2SJason Zhu 
145661bcdfeSJason Zhu void mtd_blk_map_fit(struct blk_desc *desc, ulong sector, void *fit)
146661bcdfeSJason Zhu {
147661bcdfeSJason Zhu 	struct mtd_info *mtd = NULL;
148661bcdfeSJason Zhu 	int totalsize = 0;
149661bcdfeSJason Zhu 
150661bcdfeSJason Zhu 	if (desc->if_type != IF_TYPE_MTD)
151661bcdfeSJason Zhu 		return;
152661bcdfeSJason Zhu 
153661bcdfeSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
154661bcdfeSJason Zhu #if defined(CONFIG_NAND)
155661bcdfeSJason Zhu 		mtd = dev_get_priv(desc->bdev->parent);
156661bcdfeSJason Zhu #endif
157661bcdfeSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
158661bcdfeSJason Zhu #if defined(CONFIG_MTD_SPI_NAND)
159661bcdfeSJason Zhu 		mtd = desc->bdev->priv;
160661bcdfeSJason Zhu #endif
161661bcdfeSJason Zhu 	}
162661bcdfeSJason Zhu 
163661bcdfeSJason Zhu #ifdef CONFIG_SPL_FIT
164661bcdfeSJason Zhu 	if (fit_get_totalsize(fit, &totalsize))
165661bcdfeSJason Zhu 		debug("Can not find /totalsize node.\n");
166661bcdfeSJason Zhu #endif
167661bcdfeSJason Zhu 	if (mtd && totalsize) {
168661bcdfeSJason Zhu 		if (mtd_blk_map_table_init(desc, sector << 9, totalsize + (size_t)mtd->erasesize))
169661bcdfeSJason Zhu 			debug("Map block table fail.\n");
170661bcdfeSJason Zhu 	}
171661bcdfeSJason Zhu }
172661bcdfeSJason Zhu 
1731f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
1741f21bf61SJon Lin 				       size_t *length, size_t *actual,
1751f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
1761f21bf61SJon Lin {
1771f21bf61SJon Lin 	size_t left_to_read = *length;
1781f21bf61SJon Lin 	u_char *p_buffer = buffer;
1791f21bf61SJon Lin 	int rval;
1801f21bf61SJon Lin 
1811f21bf61SJon Lin 	while (left_to_read > 0) {
182360a2911SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
1831f21bf61SJon Lin 		size_t read_length;
184360a2911SJon Lin 		loff_t mapped_offset;
1851f21bf61SJon Lin 
1861f21bf61SJon Lin 		if (offset >= mtd->size)
1871f21bf61SJon Lin 			return 0;
1881f21bf61SJon Lin 
189360a2911SJon Lin 		mapped_offset = offset;
190360a2911SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
191360a2911SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
192360a2911SJon Lin 					    ~(mtd->erasesize - 1))) {
193360a2911SJon Lin 				printf("Skipping bad block 0x%08llx\n",
194360a2911SJon Lin 				       offset & ~(mtd->erasesize - 1));
195360a2911SJon Lin 				offset += mtd->erasesize - block_offset;
1961f21bf61SJon Lin 				continue;
1971f21bf61SJon Lin 			}
1981f21bf61SJon Lin 		}
1991f21bf61SJon Lin 
200360a2911SJon Lin 		if (left_to_read < (mtd->erasesize - block_offset))
2011f21bf61SJon Lin 			read_length = left_to_read;
2021f21bf61SJon Lin 		else
203360a2911SJon Lin 			read_length = mtd->erasesize - block_offset;
2041f21bf61SJon Lin 
205360a2911SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
2061f21bf61SJon Lin 				p_buffer);
2071f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
2081f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
209c402731fSJon Lin 			       offset, rval);
2101f21bf61SJon Lin 			*length -= left_to_read;
2111f21bf61SJon Lin 			return rval;
2121f21bf61SJon Lin 		}
2131f21bf61SJon Lin 
2141f21bf61SJon Lin 		left_to_read -= read_length;
2151f21bf61SJon Lin 		offset       += read_length;
2161f21bf61SJon Lin 		p_buffer     += read_length;
2171f21bf61SJon Lin 	}
2181f21bf61SJon Lin 
2191f21bf61SJon Lin 	return 0;
2201f21bf61SJon Lin }
2211f21bf61SJon Lin 
2229ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset,
2239ee38883SJon Lin 					size_t *length, size_t *actual,
2249ee38883SJon Lin 					loff_t lim, u_char *buffer, int flags)
2259ee38883SJon Lin {
2269ee38883SJon Lin 	int rval = 0, blocksize;
2279ee38883SJon Lin 	size_t left_to_write = *length;
2289ee38883SJon Lin 	u_char *p_buffer = buffer;
2299ee38883SJon Lin 	struct erase_info ei;
2309ee38883SJon Lin 
2319ee38883SJon Lin 	blocksize = mtd->erasesize;
2329ee38883SJon Lin 
2339ee38883SJon Lin 	/*
2349ee38883SJon Lin 	 * nand_write() handles unaligned, partial page writes.
2359ee38883SJon Lin 	 *
2369ee38883SJon Lin 	 * We allow length to be unaligned, for convenience in
2379ee38883SJon Lin 	 * using the $filesize variable.
2389ee38883SJon Lin 	 *
2399ee38883SJon Lin 	 * However, starting at an unaligned offset makes the
2409ee38883SJon Lin 	 * semantics of bad block skipping ambiguous (really,
2419ee38883SJon Lin 	 * you should only start a block skipping access at a
2429ee38883SJon Lin 	 * partition boundary).  So don't try to handle that.
2439ee38883SJon Lin 	 */
2449ee38883SJon Lin 	if ((offset & (mtd->writesize - 1)) != 0) {
2459ee38883SJon Lin 		printf("Attempt to write non page-aligned data\n");
2469ee38883SJon Lin 		*length = 0;
2479ee38883SJon Lin 		return -EINVAL;
2489ee38883SJon Lin 	}
2499ee38883SJon Lin 
2509ee38883SJon Lin 	while (left_to_write > 0) {
2519ee38883SJon Lin 		size_t block_offset = offset & (mtd->erasesize - 1);
2529ee38883SJon Lin 		size_t write_size, truncated_write_size;
2539ee38883SJon Lin 		loff_t mapped_offset;
2549ee38883SJon Lin 
2559ee38883SJon Lin 		if (offset >= mtd->size)
2569ee38883SJon Lin 			return 0;
2579ee38883SJon Lin 
2589ee38883SJon Lin 		mapped_offset = offset;
2599ee38883SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
2609ee38883SJon Lin 			if (mtd_block_isbad(mtd, mapped_offset &
2619ee38883SJon Lin 					    ~(mtd->erasesize - 1))) {
2629ee38883SJon Lin 				printf("Skipping bad block 0x%08llx\n",
2639ee38883SJon Lin 				       offset & ~(mtd->erasesize - 1));
2649ee38883SJon Lin 				offset += mtd->erasesize - block_offset;
2659ee38883SJon Lin 				continue;
2669ee38883SJon Lin 			}
2679ee38883SJon Lin 		}
2689ee38883SJon Lin 
2699ee38883SJon Lin 		if (!(mapped_offset & mtd->erasesize_mask)) {
2709ee38883SJon Lin 			memset(&ei, 0, sizeof(struct erase_info));
2719ee38883SJon Lin 			ei.addr = mapped_offset;
2729ee38883SJon Lin 			ei.len  = mtd->erasesize;
2739ee38883SJon Lin 			rval = mtd_erase(mtd, &ei);
2749ee38883SJon Lin 			if (rval) {
2759ee38883SJon Lin 				pr_info("error %d while erasing %llx\n", rval,
2769ee38883SJon Lin 					mapped_offset);
2779ee38883SJon Lin 				return rval;
2789ee38883SJon Lin 			}
2799ee38883SJon Lin 		}
2809ee38883SJon Lin 
2819ee38883SJon Lin 		if (left_to_write < (blocksize - block_offset))
2829ee38883SJon Lin 			write_size = left_to_write;
2839ee38883SJon Lin 		else
2849ee38883SJon Lin 			write_size = blocksize - block_offset;
2859ee38883SJon Lin 
2869ee38883SJon Lin 		truncated_write_size = write_size;
2879ee38883SJon Lin 		rval = mtd_write(mtd, mapped_offset, truncated_write_size,
2889ee38883SJon Lin 				 (size_t *)(&truncated_write_size), p_buffer);
2899ee38883SJon Lin 
2909ee38883SJon Lin 		offset += write_size;
2919ee38883SJon Lin 		p_buffer += write_size;
2929ee38883SJon Lin 
2939ee38883SJon Lin 		if (rval != 0) {
2949ee38883SJon Lin 			printf("NAND write to offset %llx failed %d\n",
2959ee38883SJon Lin 			       offset, rval);
2969ee38883SJon Lin 			*length -= left_to_write;
2979ee38883SJon Lin 			return rval;
2989ee38883SJon Lin 		}
2999ee38883SJon Lin 
3009ee38883SJon Lin 		left_to_write -= write_size;
3019ee38883SJon Lin 	}
3029ee38883SJon Lin 
3039ee38883SJon Lin 	return 0;
3049ee38883SJon Lin }
3059ee38883SJon Lin 
306338697c5SJon Lin static __maybe_unused int mtd_map_erase(struct mtd_info *mtd, loff_t offset,
307338697c5SJon Lin 					size_t length)
308338697c5SJon Lin {
309338697c5SJon Lin 	struct erase_info ei;
310338697c5SJon Lin 	loff_t pos, len;
311338697c5SJon Lin 	int ret;
312338697c5SJon Lin 
313338697c5SJon Lin 	pos = offset;
314338697c5SJon Lin 	len = length;
315338697c5SJon Lin 
316338697c5SJon Lin 	if ((pos & mtd->erasesize_mask) || (len & mtd->erasesize_mask)) {
317338697c5SJon Lin 		pr_err("Attempt to erase non block-aligned data, pos= %llx, len= %llx\n",
318338697c5SJon Lin 		       pos, len);
319338697c5SJon Lin 
320338697c5SJon Lin 		return -EINVAL;
321338697c5SJon Lin 	}
322338697c5SJon Lin 
323338697c5SJon Lin 	while (len) {
324bd676549SJon Lin 		loff_t mapped_offset;
325bd676549SJon Lin 
326bd676549SJon Lin 		mapped_offset = pos;
327bd676549SJon Lin 		if (!get_mtd_blk_map_address(mtd, &mapped_offset)) {
328338697c5SJon Lin 			if (mtd_block_isbad(mtd, pos) || mtd_block_isreserved(mtd, pos)) {
329338697c5SJon Lin 				pr_debug("attempt to erase a bad/reserved block @%llx\n",
330338697c5SJon Lin 					 pos);
331338697c5SJon Lin 				pos += mtd->erasesize;
332338697c5SJon Lin 				continue;
333338697c5SJon Lin 			}
334bd676549SJon Lin 		}
335338697c5SJon Lin 
336338697c5SJon Lin 		memset(&ei, 0, sizeof(struct erase_info));
337bd676549SJon Lin 		ei.addr = mapped_offset;
338338697c5SJon Lin 		ei.len  = mtd->erasesize;
339338697c5SJon Lin 		ret = mtd_erase(mtd, &ei);
340338697c5SJon Lin 		if (ret) {
341338697c5SJon Lin 			pr_err("map_erase error %d while erasing %llx\n", ret,
342338697c5SJon Lin 			       pos);
343338697c5SJon Lin 			return ret;
344338697c5SJon Lin 		}
345338697c5SJon Lin 
346338697c5SJon Lin 		pos += mtd->erasesize;
347338697c5SJon Lin 		len -= mtd->erasesize;
348338697c5SJon Lin 	}
349338697c5SJon Lin 
350338697c5SJon Lin 	return 0;
351338697c5SJon Lin }
352338697c5SJon Lin 
35322dccd11SJason Zhu char *mtd_part_parse(void)
35422dccd11SJason Zhu {
35522dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
35622dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
357*cc0876a8SWeiwen Chen 	char mtd_root_part_info[40] = {0};
35822dccd11SJason Zhu 	struct blk_desc *dev_desc;
35922dccd11SJason Zhu 	disk_partition_t info;
36022dccd11SJason Zhu 	char *mtd_part_info_p;
361c9289eddSJason Zhu 	struct mtd_info *mtd;
36222dccd11SJason Zhu 	char *mtd_part_info;
36322dccd11SJason Zhu 	int ret;
36422dccd11SJason Zhu 	int p;
36522dccd11SJason Zhu 
36622dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
36722dccd11SJason Zhu 	if (!dev_desc)
36822dccd11SJason Zhu 		return NULL;
36922dccd11SJason Zhu 
370c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
371ec6d4288SJason Zhu 	if (!mtd)
372ec6d4288SJason Zhu 		return NULL;
373ec6d4288SJason Zhu 
37404a8326aSJason Zhu 	p = part_get_info_by_name(dev_desc, PART_SYSTEM, &info);
37504a8326aSJason Zhu 	if (p > 0) {
376*cc0876a8SWeiwen Chen 		if (strstr(env_get("bootargs"), "rootfstype=squashfs"))
377*cc0876a8SWeiwen Chen 			snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s",
378*cc0876a8SWeiwen Chen 				 MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_SQUASHFS);
379*cc0876a8SWeiwen Chen 		else
380*cc0876a8SWeiwen Chen 			snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s",
381*cc0876a8SWeiwen Chen 				 MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_UBIFS);
38204a8326aSJason Zhu 		env_update("bootargs", mtd_root_part_info);
38304a8326aSJason Zhu 	}
38404a8326aSJason Zhu 
38522dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
38622dccd11SJason Zhu 	if (!mtd_part_info) {
38722dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
38822dccd11SJason Zhu 		return NULL;
38922dccd11SJason Zhu 	}
39022dccd11SJason Zhu 
39122dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
39222dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
39322dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
39422dccd11SJason Zhu 		 dev_desc->product);
39522dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
39622dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
39722dccd11SJason Zhu 
39822dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
39922dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
40022dccd11SJason Zhu 		if (ret)
40122dccd11SJason Zhu 			break;
40222dccd11SJason Zhu 
40322dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
40422dccd11SJason Zhu 		      (int)(size_t)info.start);
40522dccd11SJason Zhu 
40622dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
40722dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
40822dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
40922dccd11SJason Zhu 			 info.name);
41022dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
41122dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
41222dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
41322dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
41422dccd11SJason Zhu 			 info.name);
41522dccd11SJason Zhu 		strcat(mtd_part_info, ",");
41669bb6ffaSJon Lin 		if (part_get_info(dev_desc, p + 1, &info) &&
41769bb6ffaSJon Lin 		    (info.size + info.start + 33) == dev_desc->lba) {
418ce9d2743SJon Lin 			if (dev_desc->devnum == BLK_MTD_SPI_NOR) {
419ce9d2743SJon Lin 				/* Nor is 64KB erase block(kernel) and gpt table just
420ce9d2743SJon Lin 				* resserve 33 sectors for the last partition. This
421ce9d2743SJon Lin 				* will erase the backup gpt table by user program,
422ce9d2743SJon Lin 				* so reserve one block.
423ce9d2743SJon Lin 				*/
424ce9d2743SJon Lin 				snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
425ce9d2743SJon Lin 					(int)(size_t)(info.size -
426ce9d2743SJon Lin 					(info.size - 1) %
427ce9d2743SJon Lin 					(0x10000 >> 9) - 1) << 9,
428ce9d2743SJon Lin 					(int)(size_t)info.start << 9,
429ce9d2743SJon Lin 					info.name);
430ce9d2743SJon Lin 				break;
431ce9d2743SJon Lin 			} else {
432c9289eddSJason Zhu 				/* Nand flash is erased by block and gpt table just
433c9289eddSJason Zhu 				* resserve 33 sectors for the last partition. This
434c9289eddSJason Zhu 				* will erase the backup gpt table by user program,
435c9289eddSJason Zhu 				* so reserve one block.
436c9289eddSJason Zhu 				*/
437c9289eddSJason Zhu 				snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
438c9289eddSJason Zhu 					(int)(size_t)(info.size -
439c9289eddSJason Zhu 					(info.size - 1) %
440c9289eddSJason Zhu 					(mtd->erasesize >> 9) - 1) << 9,
44122dccd11SJason Zhu 					(int)(size_t)info.start << 9,
44222dccd11SJason Zhu 					info.name);
44322dccd11SJason Zhu 				break;
44422dccd11SJason Zhu 			}
445ce9d2743SJon Lin 		}
44622dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
44722dccd11SJason Zhu 		data_len -= length;
44822dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
44922dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
45022dccd11SJason Zhu 	}
45122dccd11SJason Zhu 
45222dccd11SJason Zhu 	return mtd_part_info;
45322dccd11SJason Zhu }
45422dccd11SJason Zhu 
455054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
456054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
457054229abSJason Zhu {
458054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
4590dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
4606e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
4616e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
4620dccd0d8SJason Zhu #endif
4636e8ac5a8SJason Zhu 	struct mtd_info *mtd;
464bbb83f58SJason Zhu 	int ret = 0;
465054229abSJason Zhu 
466054229abSJason Zhu 	if (!desc)
46739e38ab3SJason Zhu 		return ret;
468054229abSJason Zhu 
4696e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
4706e8ac5a8SJason Zhu 	if (!mtd)
4716e8ac5a8SJason Zhu 		return 0;
4726e8ac5a8SJason Zhu 
473054229abSJason Zhu 	if (blkcnt == 0)
474054229abSJason Zhu 		return 0;
475054229abSJason Zhu 
4769ee38883SJon Lin 	pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt);
4779ee38883SJon Lin 
478054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
4791f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
4801f21bf61SJon Lin 				   NULL, mtd->size,
4811f21bf61SJon Lin 				   (u_char *)(dst));
4826e8ac5a8SJason Zhu 		if (!ret)
483054229abSJason Zhu 			return blkcnt;
4846e8ac5a8SJason Zhu 		else
4856e8ac5a8SJason Zhu 			return 0;
486054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
4871f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
488bbb83f58SJason Zhu 				   NULL, mtd->size,
489bbb83f58SJason Zhu 				   (u_char *)(dst));
490bbb83f58SJason Zhu 		if (!ret)
4916e8ac5a8SJason Zhu 			return blkcnt;
4926e8ac5a8SJason Zhu 		else
493054229abSJason Zhu 			return 0;
494054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
4950dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
4963fb7bf02SJason Zhu 		struct spi_nor *nor = (struct spi_nor *)mtd->priv;
4973fb7bf02SJason Zhu 		struct spi_slave *spi = nor->spi;
4980dccd0d8SJason Zhu 		size_t retlen_nor;
4990dccd0d8SJason Zhu 
5003fb7bf02SJason Zhu 		if (desc->op_flag == BLK_PRE_RW)
5013fb7bf02SJason Zhu 			spi->mode |= SPI_DMA_PREPARE;
5020dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
5033fb7bf02SJason Zhu 		if (desc->op_flag == BLK_PRE_RW)
5043fb7bf02SJason Zhu 			spi->mode |= SPI_DMA_PREPARE;
5053fb7bf02SJason Zhu 
5060dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
5070dccd0d8SJason Zhu 			return blkcnt;
5080dccd0d8SJason Zhu 		else
5090dccd0d8SJason Zhu #endif
510054229abSJason Zhu 			return 0;
511054229abSJason Zhu 	} else {
512054229abSJason Zhu 		return 0;
513054229abSJason Zhu 	}
514054229abSJason Zhu }
515054229abSJason Zhu 
516a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE)
517054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
518054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
519054229abSJason Zhu {
5209ee38883SJon Lin 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
5219ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
5229ee38883SJon Lin 	loff_t off = (loff_t)(start * 512);
5239ee38883SJon Lin 	size_t rwsize = blkcnt * 512;
5249ee38883SJon Lin #endif
5259ee38883SJon Lin 	struct mtd_info *mtd;
5269ee38883SJon Lin 	int ret = 0;
5279ee38883SJon Lin 
5289ee38883SJon Lin 	if (!desc)
5299ee38883SJon Lin 		return ret;
5309ee38883SJon Lin 
5319ee38883SJon Lin 	mtd = desc->bdev->priv;
5329ee38883SJon Lin 	if (!mtd)
5339ee38883SJon Lin 		return 0;
5349ee38883SJon Lin 
5359ee38883SJon Lin 	pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt);
5369ee38883SJon Lin 
5379ee38883SJon Lin 	if (blkcnt == 0)
5389ee38883SJon Lin 		return 0;
5399ee38883SJon Lin 
5409ee38883SJon Lin 	if (desc->devnum == BLK_MTD_NAND ||
5419ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NAND ||
5429ee38883SJon Lin 	    desc->devnum == BLK_MTD_SPI_NOR) {
543853fc11fSJon Lin 		if (desc->op_flag == BLK_MTD_CONT_WRITE) {
544853fc11fSJon Lin 			ret = mtd_map_write(mtd, off, &rwsize,
545853fc11fSJon Lin 					    NULL, mtd->size,
546853fc11fSJon Lin 					    (u_char *)(src), 0);
547853fc11fSJon Lin 			if (!ret)
548853fc11fSJon Lin 				return blkcnt;
549853fc11fSJon Lin 			else
550853fc11fSJon Lin 				return 0;
551853fc11fSJon Lin 		} else {
55242439462SJon Lin 			lbaint_t off_aligned, alinged;
55342439462SJon Lin 			size_t rwsize_aligned;
55442439462SJon Lin 			u8 *p_buf;
55542439462SJon Lin 
55642439462SJon Lin 			alinged = off & mtd->erasesize_mask;
55742439462SJon Lin 			off_aligned = off - alinged;
55842439462SJon Lin 			rwsize_aligned = rwsize + alinged;
55942439462SJon Lin 			rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) &
56042439462SJon Lin 				~(mtd->erasesize - 1);
56142439462SJon Lin 
56242439462SJon Lin 			p_buf = malloc(rwsize_aligned);
56342439462SJon Lin 			if (!p_buf) {
56442439462SJon Lin 				printf("%s: Fail to malloc!", __func__);
56542439462SJon Lin 				return 0;
56642439462SJon Lin 			}
56742439462SJon Lin 
56842439462SJon Lin 			ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned,
56942439462SJon Lin 					   NULL, mtd->size,
57042439462SJon Lin 					   (u_char *)(p_buf));
57142439462SJon Lin 			if (ret) {
57242439462SJon Lin 				free(p_buf);
57342439462SJon Lin 				return 0;
57442439462SJon Lin 			}
57542439462SJon Lin 
57642439462SJon Lin 			memcpy(p_buf + alinged, src, rwsize);
57742439462SJon Lin 
57842439462SJon Lin 			ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned,
57942439462SJon Lin 					    NULL, mtd->size,
58042439462SJon Lin 					    (u_char *)(p_buf), 0);
58142439462SJon Lin 			free(p_buf);
58242439462SJon Lin 			if (!ret)
58342439462SJon Lin 				return blkcnt;
58442439462SJon Lin 			else
58542439462SJon Lin 				return 0;
58642439462SJon Lin 		}
5879ee38883SJon Lin 	} else {
5889ee38883SJon Lin 		return 0;
5899ee38883SJon Lin 	}
5909ee38883SJon Lin 
591054229abSJason Zhu 	return 0;
592054229abSJason Zhu }
593054229abSJason Zhu 
594054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
595054229abSJason Zhu 		 lbaint_t blkcnt)
596054229abSJason Zhu {
597338697c5SJon Lin 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
598338697c5SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
599338697c5SJon Lin 	loff_t off = (loff_t)(start * 512);
600338697c5SJon Lin 	size_t len = blkcnt * 512;
601338697c5SJon Lin #endif
602338697c5SJon Lin 	struct mtd_info *mtd;
603338697c5SJon Lin 	int ret = 0;
604338697c5SJon Lin 
605338697c5SJon Lin 	if (!desc)
606338697c5SJon Lin 		return ret;
607338697c5SJon Lin 
608338697c5SJon Lin 	mtd = desc->bdev->priv;
609338697c5SJon Lin 	if (!mtd)
610338697c5SJon Lin 		return 0;
611338697c5SJon Lin 
612338697c5SJon Lin 	pr_debug("mtd derase %s %lx %lx\n", mtd->name, start, blkcnt);
613338697c5SJon Lin 
614338697c5SJon Lin 	if (blkcnt == 0)
615338697c5SJon Lin 		return 0;
616338697c5SJon Lin 
617338697c5SJon Lin 	if (desc->devnum == BLK_MTD_NAND ||
6189148182dSJon Lin 	    desc->devnum == BLK_MTD_SPI_NAND ||
6199148182dSJon Lin 	    desc->devnum == BLK_MTD_SPI_NOR) {
620338697c5SJon Lin 		ret = mtd_map_erase(mtd, off, len);
621338697c5SJon Lin 		if (ret)
622338697c5SJon Lin 			return ret;
623338697c5SJon Lin 	} else {
624338697c5SJon Lin 		return 0;
625338697c5SJon Lin 	}
626338697c5SJon Lin 
627054229abSJason Zhu 	return 0;
628054229abSJason Zhu }
629a0166cc6SJason Zhu #endif
630054229abSJason Zhu 
631054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
632054229abSJason Zhu {
6336524556dSJon Lin 	struct mtd_info *mtd;
634054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
6356524556dSJon Lin 	int ret, i = 0;
6366524556dSJon Lin 
6376524556dSJon Lin 	mtd = dev_get_uclass_priv(udev->parent);
6386524556dSJon Lin 	if (mtd->type == MTD_NANDFLASH && desc->devnum == BLK_MTD_NAND) {
6396524556dSJon Lin #ifndef CONFIG_SPL_BUILD
6406524556dSJon Lin 		mtd = dev_get_priv(udev->parent);
6416524556dSJon Lin #endif
6426524556dSJon Lin 	}
643054229abSJason Zhu 
644c9289eddSJason Zhu 	desc->bdev->priv = mtd;
645054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
646a5c69560SJon Lin 	if (strncmp(mtd->name, "nand", 4) == 0)
647a5c69560SJon Lin 		memcpy(desc->product, "rk-nand", strlen("rk-nand"));
648a5c69560SJon Lin 	else
649e6482de4SJason Zhu 		memcpy(desc->product, mtd->name, strlen(mtd->name));
650054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
651f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
6526524556dSJon Lin #ifdef CONFIG_NAND
653f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
6546524556dSJon Lin 			i = NAND_BBT_SCAN_MAXBLOCKS;
6556524556dSJon Lin 		else if (desc->devnum == BLK_MTD_SPI_NAND)
6566524556dSJon Lin 			i = NANDDEV_BBT_SCAN_MAXBLOCKS;
6576524556dSJon Lin #endif
6586524556dSJon Lin 
6591d39542fSJason Zhu 		/*
6601d39542fSJason Zhu 		 * Find the first useful block in the end,
6611d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
6621d39542fSJason Zhu 		 */
6636524556dSJon Lin 		for (; i < (mtd->size / mtd->erasesize); i++) {
6641d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
6651d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
6661d39542fSJason Zhu 			if (!ret) {
6671d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
6681d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
6691d39542fSJason Zhu 				break;
6701d39542fSJason Zhu 			}
6711d39542fSJason Zhu 		}
672f1892190SJason Zhu 	} else {
673f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
674f1892190SJason Zhu 	}
675054229abSJason Zhu 
6761d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
6771d39542fSJason Zhu 
678054229abSJason Zhu 	return 0;
679054229abSJason Zhu }
680054229abSJason Zhu 
681054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
682054229abSJason Zhu 	.read	= mtd_dread,
683a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE)
684054229abSJason Zhu 	.write	= mtd_dwrite,
685054229abSJason Zhu 	.erase	= mtd_derase,
686054229abSJason Zhu #endif
687054229abSJason Zhu };
688054229abSJason Zhu 
689054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
690054229abSJason Zhu 	.name		= "mtd_blk",
691054229abSJason Zhu 	.id		= UCLASS_BLK,
692054229abSJason Zhu 	.ops		= &mtd_blk_ops,
693054229abSJason Zhu 	.probe		= mtd_blk_probe,
694054229abSJason Zhu };
695