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