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