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