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