xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 473221da5a5a767b650f97c0a6e63b0854c2221a)
1 /*
2  * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <blk.h>
9 #include <boot_rkimg.h>
10 #include <dm.h>
11 #include <errno.h>
12 #include <malloc.h>
13 #include <nand.h>
14 #include <part.h>
15 #include <dm/device-internal.h>
16 
17 #define MTD_PART_NAND_HEAD		"mtdparts="
18 #define MTD_PART_INFO_MAX_SIZE		512
19 #define MTD_SINGLE_PART_INFO_MAX_SIZE	40
20 
21 char *mtd_part_parse(void)
22 {
23 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
24 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
25 	struct blk_desc *dev_desc;
26 	disk_partition_t info;
27 	char *mtd_part_info_p;
28 	struct mtd_info *mtd;
29 	char *mtd_part_info;
30 	int ret;
31 	int p;
32 
33 	dev_desc = rockchip_get_bootdev();
34 	if (!dev_desc)
35 		return NULL;
36 
37 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
38 	if (!mtd)
39 		return NULL;
40 
41 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
42 	if (!mtd_part_info) {
43 		printf("%s: Fail to malloc!", __func__);
44 		return NULL;
45 	}
46 
47 	mtd_part_info_p = mtd_part_info;
48 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
49 		 MTD_PART_NAND_HEAD,
50 		 dev_desc->product);
51 	data_len -= strlen(mtd_part_info_p);
52 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
53 
54 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
55 		ret = part_get_info(dev_desc, p, &info);
56 		if (ret)
57 			break;
58 
59 		debug("name is %s, start addr is %x\n", info.name,
60 		      (int)(size_t)info.start);
61 
62 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
63 			 (int)(size_t)info.size << 9,
64 			 (int)(size_t)info.start << 9,
65 			 info.name);
66 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
67 			 "0x%x@0x%x(%s)",
68 			 (int)(size_t)info.size << 9,
69 			 (int)(size_t)info.start << 9,
70 			 info.name);
71 		strcat(mtd_part_info, ",");
72 		if (part_get_info(dev_desc, p + 1, &info)) {
73 			/* Nand flash is erased by block and gpt table just
74 			 * resserve 33 sectors for the last partition. This
75 			 * will erase the backup gpt table by user program,
76 			 * so reserve one block.
77 			 */
78 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
79 				 (int)(size_t)(info.size -
80 				 (info.size - 1) %
81 				 (mtd->erasesize >> 9) - 1) << 9,
82 				 (int)(size_t)info.start << 9,
83 				 info.name);
84 			break;
85 		}
86 		length = strlen(mtd_part_info_temp);
87 		data_len -= length;
88 		mtd_part_info_p = mtd_part_info_p + length + 1;
89 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
90 	}
91 
92 	return mtd_part_info;
93 }
94 
95 ulong mtd_dread(struct udevice *udev, lbaint_t start,
96 		lbaint_t blkcnt, void *dst)
97 {
98 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
99 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
100 	loff_t off = (loff_t)(start * 512);
101 	size_t rwsize = blkcnt * 512;
102 #endif
103 	struct mtd_info *mtd;
104 	int ret = 0;
105 
106 	if (!desc)
107 		return ret;
108 
109 	mtd = desc->bdev->priv;
110 	if (!mtd)
111 		return 0;
112 
113 	if (blkcnt == 0)
114 		return 0;
115 
116 	if (desc->devnum == BLK_MTD_NAND) {
117 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
118 		mtd = dev_get_priv(udev->parent);
119 		if (!mtd)
120 			return 0;
121 
122 		ret = nand_read_skip_bad(mtd, off, &rwsize,
123 					 NULL, mtd->size,
124 					 (u_char *)(dst));
125 		if (!ret)
126 			return blkcnt;
127 		else
128 #endif
129 			return 0;
130 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
131 #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
132 		ret = nand_read_skip_bad(mtd, off, &rwsize,
133 					 NULL, mtd->size,
134 					 (u_char *)(dst));
135 		if (!ret)
136 			return blkcnt;
137 		else
138 #elif defined(CONFIG_SPL_BUILD)
139 		size_t retlen;
140 
141 		mtd_read(mtd, off, rwsize, &retlen, dst);
142 		if (retlen == rwsize)
143 			return blkcnt;
144 		else
145 #endif
146 			return 0;
147 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
148 #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
149 		size_t retlen_nor;
150 
151 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
152 		if (retlen_nor == rwsize)
153 			return blkcnt;
154 		else
155 #endif
156 			return 0;
157 	} else {
158 		return 0;
159 	}
160 }
161 
162 ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
163 		 lbaint_t blkcnt, const void *src)
164 {
165 	/* Not implemented */
166 	return 0;
167 }
168 
169 ulong mtd_derase(struct udevice *udev, lbaint_t start,
170 		 lbaint_t blkcnt)
171 {
172 	/* Not implemented */
173 	return 0;
174 }
175 
176 static int mtd_blk_probe(struct udevice *udev)
177 {
178 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
179 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
180 	int ret, i;
181 
182 	desc->bdev->priv = mtd;
183 	sprintf(desc->vendor, "0x%.4x", 0x2207);
184 	memcpy(desc->product, mtd->name, strlen(mtd->name));
185 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
186 	if (mtd->type == MTD_NANDFLASH) {
187 		if (desc->devnum == BLK_MTD_NAND)
188 			mtd = dev_get_priv(udev->parent);
189 		/*
190 		 * Find the first useful block in the end,
191 		 * and it is the end lba of the nand storage.
192 		 */
193 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
194 			ret =  mtd_block_isbad(mtd,
195 					       mtd->size - mtd->erasesize * (i + 1));
196 			if (!ret) {
197 				desc->lba = (mtd->size >> 9) -
198 					(mtd->erasesize >> 9) * i;
199 				break;
200 			}
201 		}
202 	} else {
203 		desc->lba = mtd->size >> 9;
204 	}
205 
206 	debug("MTD: desc->lba is %lx\n", desc->lba);
207 
208 	return 0;
209 }
210 
211 static const struct blk_ops mtd_blk_ops = {
212 	.read	= mtd_dread,
213 #ifndef CONFIG_SPL_BUILD
214 	.write	= mtd_dwrite,
215 	.erase	= mtd_derase,
216 #endif
217 };
218 
219 U_BOOT_DRIVER(mtd_blk) = {
220 	.name		= "mtd_blk",
221 	.id		= UCLASS_BLK,
222 	.ops		= &mtd_blk_ops,
223 	.probe		= mtd_blk_probe,
224 };
225