xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision c9e94690bae8a96b23658fb09c0c37d6f41e0cff)
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*c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN	(-2)
22*c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT	(-1)
23*c9e94690SJon 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 {
50d6290238SJon Lin 		blk_total = (mtd->size + mtd->erasesize - 1) / mtd->erasesize;
511f21bf61SJon Lin 		if (!mtd_map_blk_table) {
521f21bf61SJon Lin 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
53*c9e94690SJon Lin 			memset(mtd_map_blk_table, MTD_BLK_TABLE_BLOCK_UNKNOWN,
54*c9e94690SJon Lin 			       blk_total * 4);
551f21bf61SJon Lin 		}
561f21bf61SJon Lin 
571f21bf61SJon Lin 		blk_begin = (u32)offset / mtd->erasesize;
58d6290238SJon Lin 		blk_cnt = ((u32)(offset % mtd->erasesize + length) / mtd->erasesize);
59d6290238SJon Lin 		if ((blk_begin + blk_cnt) > blk_total)
60d6290238SJon Lin 			blk_cnt = blk_total - blk_begin;
61*c9e94690SJon Lin 
62*c9e94690SJon Lin 		if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN)
63*c9e94690SJon Lin 			return 0;
64*c9e94690SJon 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)
69*c9e94690SJon Lin 				mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT;
701f21bf61SJon Lin 			for (; j < blk_cnt; j++) {
711f21bf61SJon Lin 				if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) {
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 
851f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
861f21bf61SJon Lin 				       size_t *length, size_t *actual,
871f21bf61SJon Lin 				       loff_t lim, u_char *buffer)
881f21bf61SJon Lin {
891f21bf61SJon Lin 	size_t left_to_read = *length;
901f21bf61SJon Lin 	u_char *p_buffer = buffer;
911f21bf61SJon Lin 	u32 erasesize = mtd->erasesize;
921f21bf61SJon Lin 	int rval;
931f21bf61SJon Lin 
941f21bf61SJon Lin 	while (left_to_read > 0) {
951f21bf61SJon Lin 		size_t block_offset = offset & (erasesize - 1);
961f21bf61SJon Lin 		size_t read_length;
971f21bf61SJon Lin 		loff_t mapped_offset;
98d6290238SJon Lin 		bool mapped;
991f21bf61SJon Lin 
1001f21bf61SJon Lin 		if (offset >= mtd->size)
1011f21bf61SJon Lin 			return 0;
1021f21bf61SJon Lin 
1031f21bf61SJon Lin 		mapped_offset = offset;
104d6290238SJon Lin 		mapped = false;
105*c9e94690SJon Lin 		if (mtd_map_blk_table &&
106*c9e94690SJon Lin 		    mtd_map_blk_table[(u64)offset / erasesize] !=
107*c9e94690SJon Lin 		    MTD_BLK_TABLE_BLOCK_UNKNOWN)  {
108d6290238SJon Lin 			mapped = true;
1091f21bf61SJon Lin 			mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset /
1101f21bf61SJon Lin 				erasesize] * erasesize + block_offset);
111d6290238SJon Lin 		}
112d6290238SJon Lin 
113d6290238SJon Lin 		if (!mapped) {
1141f21bf61SJon Lin 			if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) {
1151f21bf61SJon Lin 				printf("Skip bad block 0x%08llx\n",
1161f21bf61SJon Lin 				       offset & ~(erasesize - 1));
1171f21bf61SJon Lin 				offset += erasesize - block_offset;
1181f21bf61SJon Lin 				continue;
1191f21bf61SJon Lin 			}
1201f21bf61SJon Lin 		}
1211f21bf61SJon Lin 
1221f21bf61SJon Lin 		if (left_to_read < (erasesize - block_offset))
1231f21bf61SJon Lin 			read_length = left_to_read;
1241f21bf61SJon Lin 		else
1251f21bf61SJon Lin 			read_length = erasesize - block_offset;
1261f21bf61SJon Lin 
1271f21bf61SJon Lin 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
1281f21bf61SJon Lin 				p_buffer);
1291f21bf61SJon Lin 		if (rval && rval != -EUCLEAN) {
1301f21bf61SJon Lin 			printf("NAND read from offset %llx failed %d\n",
1311f21bf61SJon Lin 			       mapped_offset, rval);
1321f21bf61SJon Lin 			*length -= left_to_read;
1331f21bf61SJon Lin 			return rval;
1341f21bf61SJon Lin 		}
1351f21bf61SJon Lin 
1361f21bf61SJon Lin 		left_to_read -= read_length;
1371f21bf61SJon Lin 		offset       += read_length;
1381f21bf61SJon Lin 		p_buffer     += read_length;
1391f21bf61SJon Lin 	}
1401f21bf61SJon Lin 
1411f21bf61SJon Lin 	return 0;
1421f21bf61SJon Lin }
1431f21bf61SJon Lin 
14422dccd11SJason Zhu char *mtd_part_parse(void)
14522dccd11SJason Zhu {
14622dccd11SJason Zhu 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
14722dccd11SJason Zhu 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
14822dccd11SJason Zhu 	struct blk_desc *dev_desc;
14922dccd11SJason Zhu 	disk_partition_t info;
15022dccd11SJason Zhu 	char *mtd_part_info_p;
151c9289eddSJason Zhu 	struct mtd_info *mtd;
15222dccd11SJason Zhu 	char *mtd_part_info;
15322dccd11SJason Zhu 	int ret;
15422dccd11SJason Zhu 	int p;
15522dccd11SJason Zhu 
15622dccd11SJason Zhu 	dev_desc = rockchip_get_bootdev();
15722dccd11SJason Zhu 	if (!dev_desc)
15822dccd11SJason Zhu 		return NULL;
15922dccd11SJason Zhu 
160c9289eddSJason Zhu 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
161ec6d4288SJason Zhu 	if (!mtd)
162ec6d4288SJason Zhu 		return NULL;
163ec6d4288SJason Zhu 
16422dccd11SJason Zhu 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
16522dccd11SJason Zhu 	if (!mtd_part_info) {
16622dccd11SJason Zhu 		printf("%s: Fail to malloc!", __func__);
16722dccd11SJason Zhu 		return NULL;
16822dccd11SJason Zhu 	}
16922dccd11SJason Zhu 
17022dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info;
17122dccd11SJason Zhu 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
17222dccd11SJason Zhu 		 MTD_PART_NAND_HEAD,
17322dccd11SJason Zhu 		 dev_desc->product);
17422dccd11SJason Zhu 	data_len -= strlen(mtd_part_info_p);
17522dccd11SJason Zhu 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
17622dccd11SJason Zhu 
17722dccd11SJason Zhu 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
17822dccd11SJason Zhu 		ret = part_get_info(dev_desc, p, &info);
17922dccd11SJason Zhu 		if (ret)
18022dccd11SJason Zhu 			break;
18122dccd11SJason Zhu 
18222dccd11SJason Zhu 		debug("name is %s, start addr is %x\n", info.name,
18322dccd11SJason Zhu 		      (int)(size_t)info.start);
18422dccd11SJason Zhu 
18522dccd11SJason Zhu 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
18622dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
18722dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
18822dccd11SJason Zhu 			 info.name);
18922dccd11SJason Zhu 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
19022dccd11SJason Zhu 			 "0x%x@0x%x(%s)",
19122dccd11SJason Zhu 			 (int)(size_t)info.size << 9,
19222dccd11SJason Zhu 			 (int)(size_t)info.start << 9,
19322dccd11SJason Zhu 			 info.name);
19422dccd11SJason Zhu 		strcat(mtd_part_info, ",");
19522dccd11SJason Zhu 		if (part_get_info(dev_desc, p + 1, &info)) {
196c9289eddSJason Zhu 			/* Nand flash is erased by block and gpt table just
197c9289eddSJason Zhu 			 * resserve 33 sectors for the last partition. This
198c9289eddSJason Zhu 			 * will erase the backup gpt table by user program,
199c9289eddSJason Zhu 			 * so reserve one block.
200c9289eddSJason Zhu 			 */
201c9289eddSJason Zhu 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
202c9289eddSJason Zhu 				 (int)(size_t)(info.size -
203c9289eddSJason Zhu 				 (info.size - 1) %
204c9289eddSJason Zhu 				 (mtd->erasesize >> 9) - 1) << 9,
20522dccd11SJason Zhu 				 (int)(size_t)info.start << 9,
20622dccd11SJason Zhu 				 info.name);
20722dccd11SJason Zhu 			break;
20822dccd11SJason Zhu 		}
20922dccd11SJason Zhu 		length = strlen(mtd_part_info_temp);
21022dccd11SJason Zhu 		data_len -= length;
21122dccd11SJason Zhu 		mtd_part_info_p = mtd_part_info_p + length + 1;
21222dccd11SJason Zhu 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
21322dccd11SJason Zhu 	}
21422dccd11SJason Zhu 
21522dccd11SJason Zhu 	return mtd_part_info;
21622dccd11SJason Zhu }
21722dccd11SJason Zhu 
218054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start,
219054229abSJason Zhu 		lbaint_t blkcnt, void *dst)
220054229abSJason Zhu {
221054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2220dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
2236e8ac5a8SJason Zhu 	loff_t off = (loff_t)(start * 512);
2246e8ac5a8SJason Zhu 	size_t rwsize = blkcnt * 512;
2250dccd0d8SJason Zhu #endif
2266e8ac5a8SJason Zhu 	struct mtd_info *mtd;
227bbb83f58SJason Zhu 	int ret = 0;
228054229abSJason Zhu 
229054229abSJason Zhu 	if (!desc)
23039e38ab3SJason Zhu 		return ret;
231054229abSJason Zhu 
2326e8ac5a8SJason Zhu 	mtd = desc->bdev->priv;
2336e8ac5a8SJason Zhu 	if (!mtd)
2346e8ac5a8SJason Zhu 		return 0;
2356e8ac5a8SJason Zhu 
236054229abSJason Zhu 	if (blkcnt == 0)
237054229abSJason Zhu 		return 0;
238054229abSJason Zhu 
239054229abSJason Zhu 	if (desc->devnum == BLK_MTD_NAND) {
24039e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
2418cf6fca4SJason Zhu 		mtd = dev_get_priv(udev->parent);
2428cf6fca4SJason Zhu 		if (!mtd)
2438cf6fca4SJason Zhu 			return 0;
2448cf6fca4SJason Zhu 
2456e8ac5a8SJason Zhu 		ret = nand_read_skip_bad(mtd, off, &rwsize,
2466e8ac5a8SJason Zhu 					 NULL, mtd->size,
247054229abSJason Zhu 					 (u_char *)(dst));
2481f21bf61SJon Lin #else
2491f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
2501f21bf61SJon Lin 				   NULL, mtd->size,
2511f21bf61SJon Lin 				   (u_char *)(dst));
2521f21bf61SJon Lin #endif
2536e8ac5a8SJason Zhu 		if (!ret)
254054229abSJason Zhu 			return blkcnt;
2556e8ac5a8SJason Zhu 		else
2566e8ac5a8SJason Zhu 			return 0;
257054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
2581f21bf61SJon Lin 		ret = mtd_map_read(mtd, off, &rwsize,
259bbb83f58SJason Zhu 				   NULL, mtd->size,
260bbb83f58SJason Zhu 				   (u_char *)(dst));
261bbb83f58SJason Zhu 		if (!ret)
2626e8ac5a8SJason Zhu 			return blkcnt;
2636e8ac5a8SJason Zhu 		else
264054229abSJason Zhu 			return 0;
265054229abSJason Zhu 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
2660dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
2670dccd0d8SJason Zhu 		size_t retlen_nor;
2680dccd0d8SJason Zhu 
2690dccd0d8SJason Zhu 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
2700dccd0d8SJason Zhu 		if (retlen_nor == rwsize)
2710dccd0d8SJason Zhu 			return blkcnt;
2720dccd0d8SJason Zhu 		else
2730dccd0d8SJason Zhu #endif
274054229abSJason Zhu 			return 0;
275054229abSJason Zhu 	} else {
276054229abSJason Zhu 		return 0;
277054229abSJason Zhu 	}
278054229abSJason Zhu }
279054229abSJason Zhu 
280054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
281054229abSJason Zhu 		 lbaint_t blkcnt, const void *src)
282054229abSJason Zhu {
283054229abSJason Zhu 	/* Not implemented */
284054229abSJason Zhu 	return 0;
285054229abSJason Zhu }
286054229abSJason Zhu 
287054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start,
288054229abSJason Zhu 		 lbaint_t blkcnt)
289054229abSJason Zhu {
290054229abSJason Zhu 	/* Not implemented */
291054229abSJason Zhu 	return 0;
292054229abSJason Zhu }
293054229abSJason Zhu 
294054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev)
295054229abSJason Zhu {
2966e8ac5a8SJason Zhu 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
297054229abSJason Zhu 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
2981d39542fSJason Zhu 	int ret, i;
299054229abSJason Zhu 
300c9289eddSJason Zhu 	desc->bdev->priv = mtd;
301054229abSJason Zhu 	sprintf(desc->vendor, "0x%.4x", 0x2207);
302e6482de4SJason Zhu 	memcpy(desc->product, mtd->name, strlen(mtd->name));
303054229abSJason Zhu 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
304f1892190SJason Zhu 	if (mtd->type == MTD_NANDFLASH) {
305f3ba630bSJason Zhu 		if (desc->devnum == BLK_MTD_NAND)
306f3ba630bSJason Zhu 			mtd = dev_get_priv(udev->parent);
3071d39542fSJason Zhu 		/*
3081d39542fSJason Zhu 		 * Find the first useful block in the end,
3091d39542fSJason Zhu 		 * and it is the end lba of the nand storage.
3101d39542fSJason Zhu 		 */
3111d39542fSJason Zhu 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
3121d39542fSJason Zhu 			ret =  mtd_block_isbad(mtd,
3131d39542fSJason Zhu 					       mtd->size - mtd->erasesize * (i + 1));
3141d39542fSJason Zhu 			if (!ret) {
3151d39542fSJason Zhu 				desc->lba = (mtd->size >> 9) -
3161d39542fSJason Zhu 					(mtd->erasesize >> 9) * i;
3171d39542fSJason Zhu 				break;
3181d39542fSJason Zhu 			}
3191d39542fSJason Zhu 		}
320f1892190SJason Zhu 	} else {
321f1892190SJason Zhu 		desc->lba = mtd->size >> 9;
322f1892190SJason Zhu 	}
323054229abSJason Zhu 
3241d39542fSJason Zhu 	debug("MTD: desc->lba is %lx\n", desc->lba);
3251d39542fSJason Zhu 
326054229abSJason Zhu 	return 0;
327054229abSJason Zhu }
328054229abSJason Zhu 
329054229abSJason Zhu static const struct blk_ops mtd_blk_ops = {
330054229abSJason Zhu 	.read	= mtd_dread,
331054229abSJason Zhu #ifndef CONFIG_SPL_BUILD
332054229abSJason Zhu 	.write	= mtd_dwrite,
333054229abSJason Zhu 	.erase	= mtd_derase,
334054229abSJason Zhu #endif
335054229abSJason Zhu };
336054229abSJason Zhu 
337054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = {
338054229abSJason Zhu 	.name		= "mtd_blk",
339054229abSJason Zhu 	.id		= UCLASS_BLK,
340054229abSJason Zhu 	.ops		= &mtd_blk_ops,
341054229abSJason Zhu 	.probe		= mtd_blk_probe,
342054229abSJason Zhu };
343