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