xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 1f21bf610b77e407869c4efc5d02a312f10bf2c5)
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 
21*1f21bf61SJon Lin static int *mtd_map_blk_table;
22*1f21bf61SJon Lin 
23*1f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc,
24*1f21bf61SJon Lin 			   loff_t offset,
25*1f21bf61SJon Lin 			   size_t length)
26*1f21bf61SJon Lin {
27*1f21bf61SJon Lin 	u32 blk_total, blk_begin, blk_cnt;
28*1f21bf61SJon Lin 	struct mtd_info *mtd = NULL;
29*1f21bf61SJon Lin 	int i, j;
30*1f21bf61SJon Lin 
31*1f21bf61SJon Lin 	if (!desc)
32*1f21bf61SJon Lin 		return -ENODEV;
33*1f21bf61SJon Lin 
34*1f21bf61SJon Lin 	if (desc->devnum == BLK_MTD_NAND) {
35*1f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
36*1f21bf61SJon Lin 		mtd = dev_get_priv(desc->bdev->parent);
37*1f21bf61SJon Lin #endif
38*1f21bf61SJon Lin 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
39*1f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
40*1f21bf61SJon Lin 		mtd = desc->bdev->priv;
41*1f21bf61SJon Lin #endif
42*1f21bf61SJon Lin 	}
43*1f21bf61SJon Lin 
44*1f21bf61SJon Lin 	if (!mtd) {
45*1f21bf61SJon Lin 		return -ENODEV;
46*1f21bf61SJon Lin 	} else {
47*1f21bf61SJon Lin 		blk_total = mtd->size / mtd->erasesize;
48*1f21bf61SJon Lin 		if (!mtd_map_blk_table) {
49*1f21bf61SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
50*1f21bf61SJon Lin 			for (i = 0; i < blk_total; i++)
51*1f21bf61SJon Lin 				mtd_map_blk_table[i] = i;
52*1f21bf61SJon Lin 		}
53*1f21bf61SJon Lin 
54*1f21bf61SJon Lin 		blk_begin = (u32)offset / mtd->erasesize;
55*1f21bf61SJon Lin 		blk_cnt = (u32)length / mtd->erasesize;
56*1f21bf61SJon Lin 		j = 0;
57*1f21bf61SJon Lin 		 /* should not across blk_cnt */
58*1f21bf61SJon Lin 		for (i = 0; i < blk_cnt; i++) {
59*1f21bf61SJon Lin 			if (j >= blk_cnt)
60*1f21bf61SJon Lin 				mtd_map_blk_table[blk_begin + i] = -1;
61*1f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
62*1f21bf61SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) {
63*1f21bf61SJon Lin 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
64*1f21bf61SJon Lin 					j++;
65*1f21bf61SJon Lin 					if (j == blk_cnt)
66*1f21bf61SJon Lin 						j++;
67*1f21bf61SJon Lin 					break;
68*1f21bf61SJon Lin 				}
69*1f21bf61SJon Lin 			}
70*1f21bf61SJon Lin 		}
71*1f21bf61SJon Lin 
72*1f21bf61SJon Lin 		return 0;
73*1f21bf61SJon Lin 	}
74*1f21bf61SJon Lin }
75*1f21bf61SJon Lin 
76*1f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
77*1f21bf61SJon Lin 				       size_t *length, size_t *actual,
78*1f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
79*1f21bf61SJon Lin {
80*1f21bf61SJon Lin 	size_t left_to_read = *length;
81*1f21bf61SJon Lin 	u_char *p_buffer = buffer;
82*1f21bf61SJon Lin 	u32 erasesize = mtd->erasesize;
83*1f21bf61SJon Lin 	int rval;
84*1f21bf61SJon Lin 
85*1f21bf61SJon Lin 	while (left_to_read > 0) {
86*1f21bf61SJon Lin 		size_t block_offset = offset & (erasesize - 1);
87*1f21bf61SJon Lin 		size_t read_length;
88*1f21bf61SJon Lin 		loff_t mapped_offset;
89*1f21bf61SJon Lin 
90*1f21bf61SJon Lin 		if (offset >= mtd->size)
91*1f21bf61SJon Lin 			return 0;
92*1f21bf61SJon Lin 
93*1f21bf61SJon Lin 		mapped_offset = offset;
94*1f21bf61SJon Lin 		if (mtd_map_blk_table)  {
95*1f21bf61SJon Lin 			mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset /
96*1f21bf61SJon Lin 				erasesize] * erasesize + block_offset);
97*1f21bf61SJon Lin 		} else {
98*1f21bf61SJon Lin 			if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) {
99*1f21bf61SJon Lin 				printf("Skip bad block 0x%08llx\n",
100*1f21bf61SJon Lin 				       offset & ~(erasesize - 1));
101*1f21bf61SJon Lin 				offset += erasesize - block_offset;
102*1f21bf61SJon Lin 				continue;
103*1f21bf61SJon Lin 			}
104*1f21bf61SJon Lin 		}
105*1f21bf61SJon Lin 
106*1f21bf61SJon Lin 		if (left_to_read < (erasesize - block_offset))
107*1f21bf61SJon Lin 			read_length = left_to_read;
108*1f21bf61SJon Lin 		else
109*1f21bf61SJon Lin 			read_length = erasesize - block_offset;
110*1f21bf61SJon Lin 
111*1f21bf61SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
112*1f21bf61SJon Lin 				p_buffer);
113*1f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
114*1f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
115*1f21bf61SJon Lin 			       mapped_offset, rval);
116*1f21bf61SJon Lin 			*length -= left_to_read;
117*1f21bf61SJon Lin 			return rval;
118*1f21bf61SJon Lin 		}
119*1f21bf61SJon Lin 
120*1f21bf61SJon Lin 		left_to_read -= read_length;
121*1f21bf61SJon Lin 		offset       += read_length;
122*1f21bf61SJon Lin 		p_buffer     += read_length;
123*1f21bf61SJon Lin 	}
124*1f21bf61SJon Lin 
125*1f21bf61SJon Lin 	return 0;
126*1f21bf61SJon Lin }
127*1f21bf61SJon Lin 
12822dccd11SJason Zhu char *mtd_part_parse(void)
12922dccd11SJason Zhu {
13022dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
13122dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
13222dccd11SJason Zhu 	struct blk_desc *dev_desc;
13322dccd11SJason Zhu 	disk_partition_t info;
13422dccd11SJason Zhu 	char *mtd_part_info_p;
135c9289eddSJason Zhu 	struct mtd_info *mtd;
13622dccd11SJason Zhu 	char *mtd_part_info;
13722dccd11SJason Zhu 	int ret;
13822dccd11SJason Zhu 	int p;
13922dccd11SJason Zhu 
14022dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
14122dccd11SJason Zhu 	if (!dev_desc)
14222dccd11SJason Zhu 		return NULL;
14322dccd11SJason Zhu 
144c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
145ec6d4288SJason Zhu 	if (!mtd)
146ec6d4288SJason Zhu 		return NULL;
147ec6d4288SJason Zhu 
14822dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
14922dccd11SJason Zhu 	if (!mtd_part_info) {
15022dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
15122dccd11SJason Zhu 		return NULL;
15222dccd11SJason Zhu 	}
15322dccd11SJason Zhu 
15422dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
15522dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
15622dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
15722dccd11SJason Zhu 		 dev_desc->product);
15822dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
15922dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
16022dccd11SJason Zhu 
16122dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
16222dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
16322dccd11SJason Zhu 		if (ret)
16422dccd11SJason Zhu 			break;
16522dccd11SJason Zhu 
16622dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
16722dccd11SJason Zhu 		      (int)(size_t)info.start);
16822dccd11SJason Zhu 
16922dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
17022dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
17122dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
17222dccd11SJason Zhu 			 info.name);
17322dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
17422dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
17522dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
17622dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
17722dccd11SJason Zhu 			 info.name);
17822dccd11SJason Zhu 		strcat(mtd_part_info, ",");
17922dccd11SJason Zhu 		if (part_get_info(dev_desc, p + 1, &info)) {
180c9289eddSJason Zhu 			/* Nand flash is erased by block and gpt table just
181c9289eddSJason Zhu 			 * resserve 33 sectors for the last partition. This
182c9289eddSJason Zhu 			 * will erase the backup gpt table by user program,
183c9289eddSJason Zhu 			 * so reserve one block.
184c9289eddSJason Zhu 			 */
185c9289eddSJason Zhu 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
186c9289eddSJason Zhu 				 (int)(size_t)(info.size -
187c9289eddSJason Zhu 				 (info.size - 1) %
188c9289eddSJason Zhu 				 (mtd->erasesize >> 9) - 1) << 9,
18922dccd11SJason Zhu 				 (int)(size_t)info.start << 9,
19022dccd11SJason Zhu 				 info.name);
19122dccd11SJason Zhu 			break;
19222dccd11SJason Zhu 		}
19322dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
19422dccd11SJason Zhu 		data_len -= length;
19522dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
19622dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
19722dccd11SJason Zhu 	}
19822dccd11SJason Zhu 
19922dccd11SJason Zhu 	return mtd_part_info;
20022dccd11SJason Zhu }
20122dccd11SJason Zhu 
202054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
203054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
204054229abSJason Zhu {
205054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2060dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
2076e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
2086e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
2090dccd0d8SJason Zhu #endif
2106e8ac5a8SJason Zhu 	struct mtd_info *mtd;
211bbb83f58SJason Zhu 	int ret = 0;
212054229abSJason Zhu 
213054229abSJason Zhu 	if (!desc)
21439e38ab3SJason Zhu 		return ret;
215054229abSJason Zhu 
2166e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
2176e8ac5a8SJason Zhu 	if (!mtd)
2186e8ac5a8SJason Zhu 		return 0;
2196e8ac5a8SJason Zhu 
220054229abSJason Zhu 	if (blkcnt == 0)
221054229abSJason Zhu 		return 0;
222054229abSJason Zhu 
223054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
22439e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
2258cf6fca4SJason Zhu 		mtd = dev_get_priv(udev->parent);
2268cf6fca4SJason Zhu 		if (!mtd)
2278cf6fca4SJason Zhu 			return 0;
2288cf6fca4SJason Zhu 
2296e8ac5a8SJason Zhu 		ret = nand_read_skip_bad(mtd, off, &rwsize,
2306e8ac5a8SJason Zhu 					 NULL, mtd->size,
231054229abSJason Zhu 					 (u_char *)(dst));
232*1f21bf61SJon Lin #else
233*1f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
234*1f21bf61SJon Lin 				   NULL, mtd->size,
235*1f21bf61SJon Lin 				   (u_char *)(dst));
236*1f21bf61SJon Lin #endif
2376e8ac5a8SJason Zhu 		if (!ret)
238054229abSJason Zhu 			return blkcnt;
2396e8ac5a8SJason Zhu 		else
2406e8ac5a8SJason Zhu 			return 0;
241054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
242*1f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
243bbb83f58SJason Zhu 				   NULL, mtd->size,
244bbb83f58SJason Zhu 				   (u_char *)(dst));
245bbb83f58SJason Zhu 		if (!ret)
2466e8ac5a8SJason Zhu 			return blkcnt;
2476e8ac5a8SJason Zhu 		else
248054229abSJason Zhu 			return 0;
249054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
2500dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
2510dccd0d8SJason Zhu 		size_t retlen_nor;
2520dccd0d8SJason Zhu 
2530dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
2540dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
2550dccd0d8SJason Zhu 			return blkcnt;
2560dccd0d8SJason Zhu 		else
2570dccd0d8SJason Zhu #endif
258054229abSJason Zhu 			return 0;
259054229abSJason Zhu 	} else {
260054229abSJason Zhu 		return 0;
261054229abSJason Zhu 	}
262054229abSJason Zhu }
263054229abSJason Zhu 
264054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
265054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
266054229abSJason Zhu {
267054229abSJason Zhu 	/* Not implemented */
268054229abSJason Zhu 	return 0;
269054229abSJason Zhu }
270054229abSJason Zhu 
271054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
272054229abSJason Zhu 		 lbaint_t blkcnt)
273054229abSJason Zhu {
274054229abSJason Zhu 	/* Not implemented */
275054229abSJason Zhu 	return 0;
276054229abSJason Zhu }
277054229abSJason Zhu 
278054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
279054229abSJason Zhu {
2806e8ac5a8SJason Zhu 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
281054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2821d39542fSJason Zhu 	int ret, i;
283054229abSJason Zhu 
284c9289eddSJason Zhu 	desc->bdev->priv = mtd;
285054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
286e6482de4SJason Zhu 	memcpy(desc->product, mtd->name, strlen(mtd->name));
287054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
288f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
289f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
290f3ba630bSJason Zhu 			mtd = dev_get_priv(udev->parent);
2911d39542fSJason Zhu 		/*
2921d39542fSJason Zhu 		 * Find the first useful block in the end,
2931d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
2941d39542fSJason Zhu 		 */
2951d39542fSJason Zhu 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
2961d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
2971d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
2981d39542fSJason Zhu 			if (!ret) {
2991d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
3001d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
3011d39542fSJason Zhu 				break;
3021d39542fSJason Zhu 			}
3031d39542fSJason Zhu 		}
304f1892190SJason Zhu 	} else {
305f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
306f1892190SJason Zhu 	}
307054229abSJason Zhu 
3081d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
3091d39542fSJason Zhu 
310054229abSJason Zhu 	return 0;
311054229abSJason Zhu }
312054229abSJason Zhu 
313054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
314054229abSJason Zhu 	.read	= mtd_dread,
315054229abSJason Zhu #ifndef CONFIG_SPL_BUILD
316054229abSJason Zhu 	.write	= mtd_dwrite,
317054229abSJason Zhu 	.erase	= mtd_derase,
318054229abSJason Zhu #endif
319054229abSJason Zhu };
320054229abSJason Zhu 
321054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
322054229abSJason Zhu 	.name		= "mtd_blk",
323054229abSJason Zhu 	.id		= UCLASS_BLK,
324054229abSJason Zhu 	.ops		= &mtd_blk_ops,
325054229abSJason Zhu 	.probe		= mtd_blk_probe,
326054229abSJason Zhu };
327