xref: /rk3399_rockchip-uboot/drivers/mtd/mtd_blk.c (revision 5033bc62feb0a935bf71dd6d960da9b184da7840)
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 static int *mtd_map_blk_table;
22 
23 int mtd_blk_map_table_init(struct blk_desc *desc,
24 			   loff_t offset,
25 			   size_t length)
26 {
27 	u32 blk_total, blk_begin, blk_cnt;
28 	struct mtd_info *mtd = NULL;
29 	int i, j;
30 
31 	if (!desc)
32 		return -ENODEV;
33 
34 	if (desc->devnum == BLK_MTD_NAND) {
35 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
36 		mtd = dev_get_priv(desc->bdev->parent);
37 #endif
38 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
39 #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD)
40 		mtd = desc->bdev->priv;
41 #endif
42 	}
43 
44 	if (!mtd) {
45 		return -ENODEV;
46 	} else {
47 		blk_total = (mtd->size + mtd->erasesize - 1) / mtd->erasesize;
48 		if (!mtd_map_blk_table) {
49 			mtd_map_blk_table = (int *)malloc(blk_total * 4);
50 			for (i = 0; i < blk_total; i++)
51 				mtd_map_blk_table[i] = i;
52 		}
53 
54 		blk_begin = (u32)offset / mtd->erasesize;
55 		blk_cnt = ((u32)(offset % mtd->erasesize + length) / mtd->erasesize);
56 		if ((blk_begin + blk_cnt) > blk_total)
57 			blk_cnt = blk_total - blk_begin;
58 		j = 0;
59 		 /* should not across blk_cnt */
60 		for (i = 0; i < blk_cnt; i++) {
61 			if (j >= blk_cnt)
62 				mtd_map_blk_table[blk_begin + i] = -1;
63 			for (; j < blk_cnt; j++) {
64 				if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) {
65 					mtd_map_blk_table[blk_begin + i] = blk_begin + j;
66 					j++;
67 					if (j == blk_cnt)
68 						j++;
69 					break;
70 				}
71 			}
72 		}
73 
74 		return 0;
75 	}
76 }
77 
78 static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset,
79 				       size_t *length, size_t *actual,
80 				       loff_t lim, u_char *buffer)
81 {
82 	size_t left_to_read = *length;
83 	u_char *p_buffer = buffer;
84 	u32 erasesize = mtd->erasesize;
85 	int rval;
86 
87 	while (left_to_read > 0) {
88 		size_t block_offset = offset & (erasesize - 1);
89 		size_t read_length;
90 		loff_t mapped_offset;
91 		bool mapped;
92 
93 		if (offset >= mtd->size)
94 			return 0;
95 
96 		mapped_offset = offset;
97 		mapped = false;
98 		if (mtd_map_blk_table)  {
99 			mapped = true;
100 			mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset /
101 				erasesize] * erasesize + block_offset);
102 		}
103 
104 		if (!mapped) {
105 			if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) {
106 				printf("Skip bad block 0x%08llx\n",
107 				       offset & ~(erasesize - 1));
108 				offset += erasesize - block_offset;
109 				continue;
110 			}
111 		}
112 
113 		if (left_to_read < (erasesize - block_offset))
114 			read_length = left_to_read;
115 		else
116 			read_length = erasesize - block_offset;
117 
118 		rval = mtd_read(mtd, mapped_offset, read_length, &read_length,
119 				p_buffer);
120 		if (rval && rval != -EUCLEAN) {
121 			printf("NAND read from offset %llx failed %d\n",
122 			       mapped_offset, rval);
123 			*length -= left_to_read;
124 			return rval;
125 		}
126 
127 		left_to_read -= read_length;
128 		offset       += read_length;
129 		p_buffer     += read_length;
130 	}
131 
132 	return 0;
133 }
134 
135 char *mtd_part_parse(void)
136 {
137 	char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0};
138 	u32 length, data_len = MTD_PART_INFO_MAX_SIZE;
139 	struct blk_desc *dev_desc;
140 	disk_partition_t info;
141 	char *mtd_part_info_p;
142 	struct mtd_info *mtd;
143 	char *mtd_part_info;
144 	int ret;
145 	int p;
146 
147 	dev_desc = rockchip_get_bootdev();
148 	if (!dev_desc)
149 		return NULL;
150 
151 	mtd = (struct mtd_info *)dev_desc->bdev->priv;
152 	if (!mtd)
153 		return NULL;
154 
155 	mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char));
156 	if (!mtd_part_info) {
157 		printf("%s: Fail to malloc!", __func__);
158 		return NULL;
159 	}
160 
161 	mtd_part_info_p = mtd_part_info;
162 	snprintf(mtd_part_info_p, data_len - 1, "%s%s:",
163 		 MTD_PART_NAND_HEAD,
164 		 dev_desc->product);
165 	data_len -= strlen(mtd_part_info_p);
166 	mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p);
167 
168 	for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
169 		ret = part_get_info(dev_desc, p, &info);
170 		if (ret)
171 			break;
172 
173 		debug("name is %s, start addr is %x\n", info.name,
174 		      (int)(size_t)info.start);
175 
176 		snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
177 			 (int)(size_t)info.size << 9,
178 			 (int)(size_t)info.start << 9,
179 			 info.name);
180 		snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1,
181 			 "0x%x@0x%x(%s)",
182 			 (int)(size_t)info.size << 9,
183 			 (int)(size_t)info.start << 9,
184 			 info.name);
185 		strcat(mtd_part_info, ",");
186 		if (part_get_info(dev_desc, p + 1, &info)) {
187 			/* Nand flash is erased by block and gpt table just
188 			 * resserve 33 sectors for the last partition. This
189 			 * will erase the backup gpt table by user program,
190 			 * so reserve one block.
191 			 */
192 			snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)",
193 				 (int)(size_t)(info.size -
194 				 (info.size - 1) %
195 				 (mtd->erasesize >> 9) - 1) << 9,
196 				 (int)(size_t)info.start << 9,
197 				 info.name);
198 			break;
199 		}
200 		length = strlen(mtd_part_info_temp);
201 		data_len -= length;
202 		mtd_part_info_p = mtd_part_info_p + length + 1;
203 		memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE);
204 	}
205 
206 	return mtd_part_info;
207 }
208 
209 ulong mtd_dread(struct udevice *udev, lbaint_t start,
210 		lbaint_t blkcnt, void *dst)
211 {
212 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
213 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD)
214 	loff_t off = (loff_t)(start * 512);
215 	size_t rwsize = blkcnt * 512;
216 #endif
217 	struct mtd_info *mtd;
218 	int ret = 0;
219 
220 	if (!desc)
221 		return ret;
222 
223 	mtd = desc->bdev->priv;
224 	if (!mtd)
225 		return 0;
226 
227 	if (blkcnt == 0)
228 		return 0;
229 
230 	if (desc->devnum == BLK_MTD_NAND) {
231 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD)
232 		mtd = dev_get_priv(udev->parent);
233 		if (!mtd)
234 			return 0;
235 
236 		ret = nand_read_skip_bad(mtd, off, &rwsize,
237 					 NULL, mtd->size,
238 					 (u_char *)(dst));
239 #else
240 		ret = mtd_map_read(mtd, off, &rwsize,
241 				   NULL, mtd->size,
242 				   (u_char *)(dst));
243 #endif
244 		if (!ret)
245 			return blkcnt;
246 		else
247 			return 0;
248 	} else if (desc->devnum == BLK_MTD_SPI_NAND) {
249 		ret = mtd_map_read(mtd, off, &rwsize,
250 				   NULL, mtd->size,
251 				   (u_char *)(dst));
252 		if (!ret)
253 			return blkcnt;
254 		else
255 			return 0;
256 	} else if (desc->devnum == BLK_MTD_SPI_NOR) {
257 #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD)
258 		size_t retlen_nor;
259 
260 		mtd_read(mtd, off, rwsize, &retlen_nor, dst);
261 		if (retlen_nor == rwsize)
262 			return blkcnt;
263 		else
264 #endif
265 			return 0;
266 	} else {
267 		return 0;
268 	}
269 }
270 
271 ulong mtd_dwrite(struct udevice *udev, lbaint_t start,
272 		 lbaint_t blkcnt, const void *src)
273 {
274 	/* Not implemented */
275 	return 0;
276 }
277 
278 ulong mtd_derase(struct udevice *udev, lbaint_t start,
279 		 lbaint_t blkcnt)
280 {
281 	/* Not implemented */
282 	return 0;
283 }
284 
285 static int mtd_blk_probe(struct udevice *udev)
286 {
287 	struct mtd_info *mtd = dev_get_uclass_priv(udev->parent);
288 	struct blk_desc *desc = dev_get_uclass_platdata(udev);
289 	int ret, i;
290 
291 	desc->bdev->priv = mtd;
292 	sprintf(desc->vendor, "0x%.4x", 0x2207);
293 	memcpy(desc->product, mtd->name, strlen(mtd->name));
294 	memcpy(desc->revision, "V1.00", sizeof("V1.00"));
295 	if (mtd->type == MTD_NANDFLASH) {
296 		if (desc->devnum == BLK_MTD_NAND)
297 			mtd = dev_get_priv(udev->parent);
298 		/*
299 		 * Find the first useful block in the end,
300 		 * and it is the end lba of the nand storage.
301 		 */
302 		for (i = 0; i < (mtd->size / mtd->erasesize); i++) {
303 			ret =  mtd_block_isbad(mtd,
304 					       mtd->size - mtd->erasesize * (i + 1));
305 			if (!ret) {
306 				desc->lba = (mtd->size >> 9) -
307 					(mtd->erasesize >> 9) * i;
308 				break;
309 			}
310 		}
311 	} else {
312 		desc->lba = mtd->size >> 9;
313 	}
314 
315 	debug("MTD: desc->lba is %lx\n", desc->lba);
316 
317 	return 0;
318 }
319 
320 static const struct blk_ops mtd_blk_ops = {
321 	.read	= mtd_dread,
322 #ifndef CONFIG_SPL_BUILD
323 	.write	= mtd_dwrite,
324 	.erase	= mtd_derase,
325 #endif
326 };
327 
328 U_BOOT_DRIVER(mtd_blk) = {
329 	.name		= "mtd_blk",
330 	.id		= UCLASS_BLK,
331 	.ops		= &mtd_blk_ops,
332 	.probe		= mtd_blk_probe,
333 };
334