xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision d6290238894eff0376dcf9b0fdf0c7ca7449d6e7)
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 
211f21bf61SJon Lin static int *mtd_map_blk_table;
221f21bf61SJon Lin 
231f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc,
241f21bf61SJon Lin 			   loff_t offset,
251f21bf61SJon Lin 			   size_t length)
261f21bf61SJon Lin {
271f21bf61SJon Lin 	u32 blk_total, blk_begin, blk_cnt;
281f21bf61SJon Lin 	struct mtd_info *mtd = NULL;
291f21bf61SJon Lin 	int i, j;
301f21bf61SJon Lin 
311f21bf61SJon Lin 	if (!desc)
321f21bf61SJon Lin 		return -ENODEV;
331f21bf61SJon Lin 
341f21bf61SJon Lin 	if (desc->devnum == BLK_MTD_NAND) {
351f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
361f21bf61SJon Lin 		mtd = dev_get_priv(desc->bdev->parent);
371f21bf61SJon Lin #endif
381f21bf61SJon Lin 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
391f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
401f21bf61SJon Lin 		mtd = desc->bdev->priv;
411f21bf61SJon Lin #endif
421f21bf61SJon Lin 	}
431f21bf61SJon Lin 
441f21bf61SJon Lin 	if (!mtd) {
451f21bf61SJon Lin 		return -ENODEV;
461f21bf61SJon Lin 	} else {
47*d6290238SJon Lin 		blk_total = (mtd->size + mtd->erasesize - 1) / mtd->erasesize;
481f21bf61SJon Lin 		if (!mtd_map_blk_table) {
491f21bf61SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
501f21bf61SJon Lin 			for (i = 0; i < blk_total; i++)
511f21bf61SJon Lin 				mtd_map_blk_table[i] = i;
521f21bf61SJon Lin 		}
531f21bf61SJon Lin 
541f21bf61SJon Lin 		blk_begin = (u32)offset / mtd->erasesize;
55*d6290238SJon Lin 		blk_cnt = ((u32)(offset % mtd->erasesize + length) / mtd->erasesize);
56*d6290238SJon Lin 		if ((blk_begin + blk_cnt) > blk_total)
57*d6290238SJon Lin 			blk_cnt = blk_total - blk_begin;
581f21bf61SJon Lin 		j = 0;
591f21bf61SJon Lin 		 /* should not across blk_cnt */
601f21bf61SJon Lin 		for (i = 0; i < blk_cnt; i++) {
611f21bf61SJon Lin 			if (j >= blk_cnt)
621f21bf61SJon Lin 				mtd_map_blk_table[blk_begin + i] = -1;
631f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
641f21bf61SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) {
651f21bf61SJon Lin 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
661f21bf61SJon Lin 					j++;
671f21bf61SJon Lin 					if (j == blk_cnt)
681f21bf61SJon Lin 						j++;
691f21bf61SJon Lin 					break;
701f21bf61SJon Lin 				}
711f21bf61SJon Lin 			}
721f21bf61SJon Lin 		}
731f21bf61SJon Lin 
741f21bf61SJon Lin 		return 0;
751f21bf61SJon Lin 	}
761f21bf61SJon Lin }
771f21bf61SJon Lin 
781f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
791f21bf61SJon Lin 				       size_t *length, size_t *actual,
801f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
811f21bf61SJon Lin {
821f21bf61SJon Lin 	size_t left_to_read = *length;
831f21bf61SJon Lin 	u_char *p_buffer = buffer;
841f21bf61SJon Lin 	u32 erasesize = mtd->erasesize;
851f21bf61SJon Lin 	int rval;
861f21bf61SJon Lin 
871f21bf61SJon Lin 	while (left_to_read > 0) {
881f21bf61SJon Lin 		size_t block_offset = offset & (erasesize - 1);
891f21bf61SJon Lin 		size_t read_length;
901f21bf61SJon Lin 		loff_t mapped_offset;
91*d6290238SJon Lin 		bool mapped;
921f21bf61SJon Lin 
931f21bf61SJon Lin 		if (offset >= mtd->size)
941f21bf61SJon Lin 			return 0;
951f21bf61SJon Lin 
961f21bf61SJon Lin 		mapped_offset = offset;
97*d6290238SJon Lin 		mapped = false;
981f21bf61SJon Lin 		if (mtd_map_blk_table)  {
99*d6290238SJon Lin 			mapped = true;
1001f21bf61SJon Lin 			mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset /
1011f21bf61SJon Lin 				erasesize] * erasesize + block_offset);
102*d6290238SJon Lin 		}
103*d6290238SJon Lin 
104*d6290238SJon Lin 		if (!mapped) {
1051f21bf61SJon Lin 			if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) {
1061f21bf61SJon Lin 				printf("Skip bad block 0x%08llx\n",
1071f21bf61SJon Lin 				       offset & ~(erasesize - 1));
1081f21bf61SJon Lin 				offset += erasesize - block_offset;
1091f21bf61SJon Lin 				continue;
1101f21bf61SJon Lin 			}
1111f21bf61SJon Lin 		}
1121f21bf61SJon Lin 
1131f21bf61SJon Lin 		if (left_to_read < (erasesize - block_offset))
1141f21bf61SJon Lin 			read_length = left_to_read;
1151f21bf61SJon Lin 		else
1161f21bf61SJon Lin 			read_length = erasesize - block_offset;
1171f21bf61SJon Lin 
1181f21bf61SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
1191f21bf61SJon Lin 				p_buffer);
1201f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
1211f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
1221f21bf61SJon Lin 			       mapped_offset, rval);
1231f21bf61SJon Lin 			*length -= left_to_read;
1241f21bf61SJon Lin 			return rval;
1251f21bf61SJon Lin 		}
1261f21bf61SJon Lin 
1271f21bf61SJon Lin 		left_to_read -= read_length;
1281f21bf61SJon Lin 		offset       += read_length;
1291f21bf61SJon Lin 		p_buffer     += read_length;
1301f21bf61SJon Lin 	}
1311f21bf61SJon Lin 
1321f21bf61SJon Lin 	return 0;
1331f21bf61SJon Lin }
1341f21bf61SJon Lin 
13522dccd11SJason Zhu char *mtd_part_parse(void)
13622dccd11SJason Zhu {
13722dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
13822dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
13922dccd11SJason Zhu 	struct blk_desc *dev_desc;
14022dccd11SJason Zhu 	disk_partition_t info;
14122dccd11SJason Zhu 	char *mtd_part_info_p;
142c9289eddSJason Zhu 	struct mtd_info *mtd;
14322dccd11SJason Zhu 	char *mtd_part_info;
14422dccd11SJason Zhu 	int ret;
14522dccd11SJason Zhu 	int p;
14622dccd11SJason Zhu 
14722dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
14822dccd11SJason Zhu 	if (!dev_desc)
14922dccd11SJason Zhu 		return NULL;
15022dccd11SJason Zhu 
151c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
152ec6d4288SJason Zhu 	if (!mtd)
153ec6d4288SJason Zhu 		return NULL;
154ec6d4288SJason Zhu 
15522dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
15622dccd11SJason Zhu 	if (!mtd_part_info) {
15722dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
15822dccd11SJason Zhu 		return NULL;
15922dccd11SJason Zhu 	}
16022dccd11SJason Zhu 
16122dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
16222dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
16322dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
16422dccd11SJason Zhu 		 dev_desc->product);
16522dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
16622dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
16722dccd11SJason Zhu 
16822dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
16922dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
17022dccd11SJason Zhu 		if (ret)
17122dccd11SJason Zhu 			break;
17222dccd11SJason Zhu 
17322dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
17422dccd11SJason Zhu 		      (int)(size_t)info.start);
17522dccd11SJason Zhu 
17622dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
17722dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
17822dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
17922dccd11SJason Zhu 			 info.name);
18022dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
18122dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
18222dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
18322dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
18422dccd11SJason Zhu 			 info.name);
18522dccd11SJason Zhu 		strcat(mtd_part_info, ",");
18622dccd11SJason Zhu 		if (part_get_info(dev_desc, p + 1, &info)) {
187c9289eddSJason Zhu 			/* Nand flash is erased by block and gpt table just
188c9289eddSJason Zhu 			 * resserve 33 sectors for the last partition. This
189c9289eddSJason Zhu 			 * will erase the backup gpt table by user program,
190c9289eddSJason Zhu 			 * so reserve one block.
191c9289eddSJason Zhu 			 */
192c9289eddSJason Zhu 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
193c9289eddSJason Zhu 				 (int)(size_t)(info.size -
194c9289eddSJason Zhu 				 (info.size - 1) %
195c9289eddSJason Zhu 				 (mtd->erasesize >> 9) - 1) << 9,
19622dccd11SJason Zhu 				 (int)(size_t)info.start << 9,
19722dccd11SJason Zhu 				 info.name);
19822dccd11SJason Zhu 			break;
19922dccd11SJason Zhu 		}
20022dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
20122dccd11SJason Zhu 		data_len -= length;
20222dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
20322dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
20422dccd11SJason Zhu 	}
20522dccd11SJason Zhu 
20622dccd11SJason Zhu 	return mtd_part_info;
20722dccd11SJason Zhu }
20822dccd11SJason Zhu 
209054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
210054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
211054229abSJason Zhu {
212054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2130dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
2146e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
2156e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
2160dccd0d8SJason Zhu #endif
2176e8ac5a8SJason Zhu 	struct mtd_info *mtd;
218bbb83f58SJason Zhu 	int ret = 0;
219054229abSJason Zhu 
220054229abSJason Zhu 	if (!desc)
22139e38ab3SJason Zhu 		return ret;
222054229abSJason Zhu 
2236e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
2246e8ac5a8SJason Zhu 	if (!mtd)
2256e8ac5a8SJason Zhu 		return 0;
2266e8ac5a8SJason Zhu 
227054229abSJason Zhu 	if (blkcnt == 0)
228054229abSJason Zhu 		return 0;
229054229abSJason Zhu 
230054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
23139e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
2328cf6fca4SJason Zhu 		mtd = dev_get_priv(udev->parent);
2338cf6fca4SJason Zhu 		if (!mtd)
2348cf6fca4SJason Zhu 			return 0;
2358cf6fca4SJason Zhu 
2366e8ac5a8SJason Zhu 		ret = nand_read_skip_bad(mtd, off, &rwsize,
2376e8ac5a8SJason Zhu 					 NULL, mtd->size,
238054229abSJason Zhu 					 (u_char *)(dst));
2391f21bf61SJon Lin #else
2401f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
2411f21bf61SJon Lin 				   NULL, mtd->size,
2421f21bf61SJon Lin 				   (u_char *)(dst));
2431f21bf61SJon Lin #endif
2446e8ac5a8SJason Zhu 		if (!ret)
245054229abSJason Zhu 			return blkcnt;
2466e8ac5a8SJason Zhu 		else
2476e8ac5a8SJason Zhu 			return 0;
248054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
2491f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
250bbb83f58SJason Zhu 				   NULL, mtd->size,
251bbb83f58SJason Zhu 				   (u_char *)(dst));
252bbb83f58SJason Zhu 		if (!ret)
2536e8ac5a8SJason Zhu 			return blkcnt;
2546e8ac5a8SJason Zhu 		else
255054229abSJason Zhu 			return 0;
256054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
2570dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
2580dccd0d8SJason Zhu 		size_t retlen_nor;
2590dccd0d8SJason Zhu 
2600dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
2610dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
2620dccd0d8SJason Zhu 			return blkcnt;
2630dccd0d8SJason Zhu 		else
2640dccd0d8SJason Zhu #endif
265054229abSJason Zhu 			return 0;
266054229abSJason Zhu 	} else {
267054229abSJason Zhu 		return 0;
268054229abSJason Zhu 	}
269054229abSJason Zhu }
270054229abSJason Zhu 
271054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
272054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
273054229abSJason Zhu {
274054229abSJason Zhu 	/* Not implemented */
275054229abSJason Zhu 	return 0;
276054229abSJason Zhu }
277054229abSJason Zhu 
278054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
279054229abSJason Zhu 		 lbaint_t blkcnt)
280054229abSJason Zhu {
281054229abSJason Zhu 	/* Not implemented */
282054229abSJason Zhu 	return 0;
283054229abSJason Zhu }
284054229abSJason Zhu 
285054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
286054229abSJason Zhu {
2876e8ac5a8SJason Zhu 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
288054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2891d39542fSJason Zhu 	int ret, i;
290054229abSJason Zhu 
291c9289eddSJason Zhu 	desc->bdev->priv = mtd;
292054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
293e6482de4SJason Zhu 	memcpy(desc->product, mtd->name, strlen(mtd->name));
294054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
295f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
296f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
297f3ba630bSJason Zhu 			mtd = dev_get_priv(udev->parent);
2981d39542fSJason Zhu 		/*
2991d39542fSJason Zhu 		 * Find the first useful block in the end,
3001d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
3011d39542fSJason Zhu 		 */
3021d39542fSJason Zhu 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
3031d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
3041d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
3051d39542fSJason Zhu 			if (!ret) {
3061d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
3071d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
3081d39542fSJason Zhu 				break;
3091d39542fSJason Zhu 			}
3101d39542fSJason Zhu 		}
311f1892190SJason Zhu 	} else {
312f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
313f1892190SJason Zhu 	}
314054229abSJason Zhu 
3151d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
3161d39542fSJason Zhu 
317054229abSJason Zhu 	return 0;
318054229abSJason Zhu }
319054229abSJason Zhu 
320054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
321054229abSJason Zhu 	.read	= mtd_dread,
322054229abSJason Zhu #ifndef CONFIG_SPL_BUILD
323054229abSJason Zhu 	.write	= mtd_dwrite,
324054229abSJason Zhu 	.erase	= mtd_derase,
325054229abSJason Zhu #endif
326054229abSJason Zhu };
327054229abSJason Zhu 
328054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
329054229abSJason Zhu 	.name		= "mtd_blk",
330054229abSJason Zhu 	.id		= UCLASS_BLK,
331054229abSJason Zhu 	.ops		= &mtd_blk_ops,
332054229abSJason Zhu 	.probe		= mtd_blk_probe,
333054229abSJason Zhu };
334