xref: /rk3399_ARM-atf/drivers/mtd/nand/core.c (revision f29c0702d2e7a67327b67766f91793d8ae6d0f73)
1b114abb6SLionel Debieve /*
2*f29c0702SLionel Debieve  * Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved
3b114abb6SLionel Debieve  *
4b114abb6SLionel Debieve  * SPDX-License-Identifier: BSD-3-Clause
5b114abb6SLionel Debieve  */
6b114abb6SLionel Debieve 
7b114abb6SLionel Debieve #include <assert.h>
8b114abb6SLionel Debieve #include <errno.h>
9b114abb6SLionel Debieve #include <stddef.h>
10b114abb6SLionel Debieve 
11b114abb6SLionel Debieve #include <common/debug.h>
12b114abb6SLionel Debieve #include <drivers/delay_timer.h>
13b114abb6SLionel Debieve #include <drivers/nand.h>
14b114abb6SLionel Debieve #include <lib/utils.h>
15b114abb6SLionel Debieve 
16*f29c0702SLionel Debieve #include <platform_def.h>
17*f29c0702SLionel Debieve 
18b114abb6SLionel Debieve /*
19b114abb6SLionel Debieve  * Define a single nand_device used by specific NAND frameworks.
20b114abb6SLionel Debieve  */
21b114abb6SLionel Debieve static struct nand_device nand_dev;
22*f29c0702SLionel Debieve 
23*f29c0702SLionel Debieve #pragma weak plat_get_scratch_buffer
24*f29c0702SLionel Debieve void plat_get_scratch_buffer(void **buffer_addr, size_t *buf_size)
25*f29c0702SLionel Debieve {
26b114abb6SLionel Debieve 	static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE];
27b114abb6SLionel Debieve 
28*f29c0702SLionel Debieve 	assert(buffer_addr != NULL);
29*f29c0702SLionel Debieve 	assert(buf_size != NULL);
30*f29c0702SLionel Debieve 
31*f29c0702SLionel Debieve 	*buffer_addr = (void *)scratch_buff;
32*f29c0702SLionel Debieve 	*buf_size = sizeof(scratch_buff);
33*f29c0702SLionel Debieve }
34*f29c0702SLionel Debieve 
35b114abb6SLionel Debieve int nand_read(unsigned int offset, uintptr_t buffer, size_t length,
36b114abb6SLionel Debieve 	      size_t *length_read)
37b114abb6SLionel Debieve {
38b114abb6SLionel Debieve 	unsigned int block = offset / nand_dev.block_size;
39b114abb6SLionel Debieve 	unsigned int end_block = (offset + length - 1U) / nand_dev.block_size;
40b114abb6SLionel Debieve 	unsigned int page_start =
41b114abb6SLionel Debieve 		(offset % nand_dev.block_size) / nand_dev.page_size;
42b114abb6SLionel Debieve 	unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size;
43b114abb6SLionel Debieve 	unsigned int start_offset = offset % nand_dev.page_size;
44b114abb6SLionel Debieve 	unsigned int page;
45b114abb6SLionel Debieve 	unsigned int bytes_read;
46b114abb6SLionel Debieve 	int is_bad;
47b114abb6SLionel Debieve 	int ret;
48*f29c0702SLionel Debieve 	uint8_t *scratch_buff;
49*f29c0702SLionel Debieve 	size_t scratch_buff_size;
50*f29c0702SLionel Debieve 
51*f29c0702SLionel Debieve 	plat_get_scratch_buffer((void **)&scratch_buff, &scratch_buff_size);
52*f29c0702SLionel Debieve 
53*f29c0702SLionel Debieve 	assert(scratch_buff != NULL);
54b114abb6SLionel Debieve 
55b114abb6SLionel Debieve 	VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n",
56b114abb6SLionel Debieve 		block, end_block, page_start, nb_pages, length, offset);
57b114abb6SLionel Debieve 
58b114abb6SLionel Debieve 	*length_read = 0UL;
59b114abb6SLionel Debieve 
60b114abb6SLionel Debieve 	if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) &&
61*f29c0702SLionel Debieve 	    (scratch_buff_size < nand_dev.page_size)) {
62b114abb6SLionel Debieve 		return -EINVAL;
63b114abb6SLionel Debieve 	}
64b114abb6SLionel Debieve 
65b114abb6SLionel Debieve 	while (block <= end_block) {
66b114abb6SLionel Debieve 		is_bad = nand_dev.mtd_block_is_bad(block);
67b114abb6SLionel Debieve 		if (is_bad < 0) {
68b114abb6SLionel Debieve 			return is_bad;
69b114abb6SLionel Debieve 		}
70b114abb6SLionel Debieve 
71b114abb6SLionel Debieve 		if (is_bad == 1) {
72b114abb6SLionel Debieve 			/* Skip the block */
73b114abb6SLionel Debieve 			uint32_t max_block =
74b114abb6SLionel Debieve 				nand_dev.size / nand_dev.block_size;
75b114abb6SLionel Debieve 
76b114abb6SLionel Debieve 			block++;
77b114abb6SLionel Debieve 			end_block++;
78b114abb6SLionel Debieve 			if ((block < max_block) && (end_block < max_block)) {
79b114abb6SLionel Debieve 				continue;
80b114abb6SLionel Debieve 			}
81b114abb6SLionel Debieve 
82b114abb6SLionel Debieve 			return -EIO;
83b114abb6SLionel Debieve 		}
84b114abb6SLionel Debieve 
85b114abb6SLionel Debieve 		for (page = page_start; page < nb_pages; page++) {
86b114abb6SLionel Debieve 			if ((start_offset != 0U) ||
87b114abb6SLionel Debieve 			    (length < nand_dev.page_size)) {
88b114abb6SLionel Debieve 				ret = nand_dev.mtd_read_page(
89b114abb6SLionel Debieve 						&nand_dev,
90b114abb6SLionel Debieve 						(block * nb_pages) + page,
91b114abb6SLionel Debieve 						(uintptr_t)scratch_buff);
92b114abb6SLionel Debieve 				if (ret != 0) {
93b114abb6SLionel Debieve 					return ret;
94b114abb6SLionel Debieve 				}
95b114abb6SLionel Debieve 
96b114abb6SLionel Debieve 				bytes_read = MIN((size_t)(nand_dev.page_size -
97b114abb6SLionel Debieve 							  start_offset),
98b114abb6SLionel Debieve 						 length);
99b114abb6SLionel Debieve 
100b114abb6SLionel Debieve 				memcpy((uint8_t *)buffer,
101b114abb6SLionel Debieve 				       scratch_buff + start_offset,
102b114abb6SLionel Debieve 				       bytes_read);
103b114abb6SLionel Debieve 
104b114abb6SLionel Debieve 				start_offset = 0U;
105b114abb6SLionel Debieve 			} else {
106b114abb6SLionel Debieve 				ret = nand_dev.mtd_read_page(&nand_dev,
107b114abb6SLionel Debieve 						(block * nb_pages) + page,
108b114abb6SLionel Debieve 						buffer);
109b114abb6SLionel Debieve 				if (ret != 0) {
110b114abb6SLionel Debieve 					return ret;
111b114abb6SLionel Debieve 				}
112b114abb6SLionel Debieve 
113b114abb6SLionel Debieve 				bytes_read = nand_dev.page_size;
114b114abb6SLionel Debieve 			}
115b114abb6SLionel Debieve 
116b114abb6SLionel Debieve 			length -= bytes_read;
117b114abb6SLionel Debieve 			buffer += bytes_read;
118b114abb6SLionel Debieve 			*length_read += bytes_read;
119b114abb6SLionel Debieve 
120b114abb6SLionel Debieve 			if (length == 0U) {
121b114abb6SLionel Debieve 				break;
122b114abb6SLionel Debieve 			}
123b114abb6SLionel Debieve 		}
124b114abb6SLionel Debieve 
125b114abb6SLionel Debieve 		page_start = 0U;
126b114abb6SLionel Debieve 		block++;
127b114abb6SLionel Debieve 	}
128b114abb6SLionel Debieve 
129b114abb6SLionel Debieve 	return 0;
130b114abb6SLionel Debieve }
131b114abb6SLionel Debieve 
132bc3eebb2SYann Gautier int nand_seek_bb(uintptr_t base, unsigned int offset, size_t *extra_offset)
133bc3eebb2SYann Gautier {
134bc3eebb2SYann Gautier 	unsigned int block;
135bc3eebb2SYann Gautier 	unsigned int offset_block;
136bc3eebb2SYann Gautier 	unsigned int max_block;
137bc3eebb2SYann Gautier 	int is_bad;
138bc3eebb2SYann Gautier 	size_t count_bb = 0U;
139bc3eebb2SYann Gautier 
140bc3eebb2SYann Gautier 	block = base / nand_dev.block_size;
141bc3eebb2SYann Gautier 
142bc3eebb2SYann Gautier 	if (offset != 0U) {
143bc3eebb2SYann Gautier 		offset_block = (base + offset - 1U) / nand_dev.block_size;
144bc3eebb2SYann Gautier 	} else {
145bc3eebb2SYann Gautier 		offset_block = block;
146bc3eebb2SYann Gautier 	}
147bc3eebb2SYann Gautier 
148bc3eebb2SYann Gautier 	max_block = nand_dev.size / nand_dev.block_size;
149bc3eebb2SYann Gautier 
150bc3eebb2SYann Gautier 	while (block <= offset_block) {
151bc3eebb2SYann Gautier 		if (offset_block >= max_block) {
152bc3eebb2SYann Gautier 			return -EIO;
153bc3eebb2SYann Gautier 		}
154bc3eebb2SYann Gautier 
155bc3eebb2SYann Gautier 		is_bad = nand_dev.mtd_block_is_bad(block);
156bc3eebb2SYann Gautier 		if (is_bad < 0) {
157bc3eebb2SYann Gautier 			return is_bad;
158bc3eebb2SYann Gautier 		}
159bc3eebb2SYann Gautier 
160bc3eebb2SYann Gautier 		if (is_bad == 1) {
161bc3eebb2SYann Gautier 			count_bb++;
162bc3eebb2SYann Gautier 			offset_block++;
163bc3eebb2SYann Gautier 		}
164bc3eebb2SYann Gautier 
165bc3eebb2SYann Gautier 		block++;
166bc3eebb2SYann Gautier 	}
167bc3eebb2SYann Gautier 
168bc3eebb2SYann Gautier 	*extra_offset = count_bb * nand_dev.block_size;
169bc3eebb2SYann Gautier 
170bc3eebb2SYann Gautier 	return 0;
171bc3eebb2SYann Gautier }
172bc3eebb2SYann Gautier 
173b114abb6SLionel Debieve struct nand_device *get_nand_device(void)
174b114abb6SLionel Debieve {
175b114abb6SLionel Debieve 	return &nand_dev;
176b114abb6SLionel Debieve }
177