summaryrefslogtreecommitdiff
path: root/lib/x1000-installer/src/xf_nandio.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/x1000-installer/src/xf_nandio.c')
-rw-r--r--lib/x1000-installer/src/xf_nandio.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/lib/x1000-installer/src/xf_nandio.c b/lib/x1000-installer/src/xf_nandio.c
new file mode 100644
index 0000000000..ba79cbbcbf
--- /dev/null
+++ b/lib/x1000-installer/src/xf_nandio.c
@@ -0,0 +1,295 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_nandio.h"
23#include "xf_error.h"
24#include "core_alloc.h"
25#include "system.h"
26#include <string.h>
27#include <stdbool.h>
28
29int xf_nandio_init(struct xf_nandio* nio)
30{
31 int rc;
32
33 memset(nio, 0, sizeof(*nio));
34
35 /* open NAND */
36 nio->ndrv = nand_init();
37 nand_lock(nio->ndrv);
38 rc = nand_open(nio->ndrv);
39 if(rc != NAND_SUCCESS) {
40 nio->nand_err = rc;
41 rc = XF_E_NAND;
42 goto out;
43 }
44
45 /* read chip parameters */
46 nio->page_size = nio->ndrv->chip->page_size;
47 nio->block_size = nio->page_size << nio->ndrv->chip->log2_ppb;
48
49 /* allocate memory */
50 size_t alloc_size = 0;
51 alloc_size += CACHEALIGN_SIZE - 1;
52 alloc_size += nio->block_size * 2;
53
54 nio->alloc_handle = core_alloc("xf_nandio", alloc_size);
55 if(nio->alloc_handle < 0) {
56 rc = XF_E_OUT_OF_MEMORY;
57 goto out_nclose;
58 }
59
60 uint8_t* buffer = core_get_data(nio->alloc_handle);
61 CACHEALIGN_BUFFER(buffer, alloc_size);
62
63 nio->old_buf = buffer;
64 nio->new_buf = &buffer[nio->block_size];
65
66 rc = XF_E_SUCCESS;
67 goto out;
68
69 out_nclose:
70 nand_close(nio->ndrv);
71 out:
72 nand_unlock(nio->ndrv);
73 return rc;
74}
75
76void xf_nandio_destroy(struct xf_nandio* nio)
77{
78 if(nio->alloc_handle > 0) {
79 core_free(nio->alloc_handle);
80 nio->alloc_handle = 0;
81 }
82
83 if(nio->ndrv) {
84 nand_lock(nio->ndrv);
85 nand_close(nio->ndrv);
86 nand_unlock(nio->ndrv);
87 nio->ndrv = NULL;
88 }
89}
90
91static bool is_page_blank(const uint8_t* buf, uint32_t length)
92{
93 for(uint32_t i = 0; i < length; ++i)
94 if(buf[i] != 0xff)
95 return false;
96
97 return true;
98}
99
100static int flush_block(struct xf_nandio* nio, bool invalidate)
101{
102 /* no block, or only reading - flush is a no-op */
103 if(!nio->block_valid || nio->mode == XF_NANDIO_READ)
104 return XF_E_SUCCESS;
105
106 /* nothing to do if new data is same as old data */
107 if(!memcmp(nio->old_buf, nio->new_buf, nio->block_size))
108 return XF_E_SUCCESS;
109
110 /* data mismatch during verification - report the error */
111 if(nio->mode == XF_NANDIO_VERIFY)
112 return XF_E_VERIFY_FAILED;
113
114 /* erase the block */
115 int rc = nand_block_erase(nio->ndrv, nio->cur_block);
116 if(rc != NAND_SUCCESS) {
117 nio->block_valid = false;
118 nio->nand_err = rc;
119 return XF_E_NAND;
120 }
121
122 size_t oob_size = nio->ndrv->chip->oob_size;
123
124 unsigned page = 0;
125 nand_page_t page_addr = nio->cur_block;
126 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
127 /* skip programming blank pages to go faster & reduce wear */
128 uint8_t* page_data = &nio->new_buf[page * nio->page_size];
129 if(is_page_blank(page_data, nio->page_size))
130 continue;
131
132 /* copy page and write blank OOB data */
133 memcpy(nio->ndrv->page_buf, page_data, nio->page_size);
134 memset(&nio->ndrv->page_buf[nio->page_size], 0xff, oob_size);
135
136 /* program the page */
137 rc = nand_page_program(nio->ndrv, page_addr, nio->ndrv->page_buf);
138 if(rc != NAND_SUCCESS) {
139 nio->block_valid = false;
140 nio->nand_err = rc;
141 return XF_E_NAND;
142 }
143 }
144
145 if(invalidate)
146 nio->block_valid = false;
147 else {
148 /* update our 'old' buffer so a subsequent flush
149 * will not reprogram the same block */
150 memcpy(nio->old_buf, nio->new_buf, nio->block_size);
151 }
152
153 return XF_E_SUCCESS;
154}
155
156static int seek_to_block(struct xf_nandio* nio, nand_block_t block_addr,
157 size_t offset_in_block)
158{
159 /* already on this block? */
160 if(nio->block_valid && block_addr == nio->cur_block) {
161 nio->offset_in_block = offset_in_block;
162 return XF_E_SUCCESS;
163 }
164
165 /* ensure new block is within range */
166 if(block_addr >= (nio->ndrv->chip->nr_blocks << nio->ndrv->chip->log2_ppb))
167 return XF_E_OUT_OF_RANGE;
168
169 /* flush old block */
170 int rc = flush_block(nio, true);
171 if(rc)
172 return rc;
173
174 nio->block_valid = false;
175
176 /* read the new block */
177 unsigned page = 0;
178 nand_page_t page_addr = block_addr;
179 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
180 rc = nand_page_read(nio->ndrv, page_addr, nio->ndrv->page_buf);
181 if(rc != NAND_SUCCESS) {
182 nio->nand_err = rc;
183 return XF_E_NAND;
184 }
185
186 memcpy(&nio->old_buf[page * nio->page_size], nio->ndrv->page_buf, nio->page_size);
187 }
188
189 /* copy to 2nd buffer */
190 memcpy(nio->new_buf, nio->old_buf, nio->block_size);
191
192 /* update position */
193 nio->cur_block = block_addr;
194 nio->offset_in_block = offset_in_block;
195 nio->block_valid = true;
196 return XF_E_SUCCESS;
197}
198
199int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode)
200{
201 nand_lock(nio->ndrv);
202
203 /* flush the current block before switching to the new mode,
204 * to ensure consistency */
205 int rc = flush_block(nio, false);
206 if(rc)
207 goto err;
208
209 nio->mode = mode;
210 rc = XF_E_SUCCESS;
211
212 err:
213 nand_unlock(nio->ndrv);
214 return rc;
215}
216
217static int nandio_rdwr(struct xf_nandio* nio, void* buf, size_t count, bool write)
218{
219 while(count > 0) {
220 void* ptr;
221 size_t amount = count;
222 int rc = xf_nandio_get_buffer(nio, &ptr, &amount);
223 if(rc)
224 return rc;
225
226 if(write)
227 memcpy(ptr, buf, amount);
228 else
229 memcpy(buf, ptr, amount);
230
231 count -= amount;
232 }
233
234 return XF_E_SUCCESS;
235}
236
237int xf_nandio_seek(struct xf_nandio* nio, size_t offset)
238{
239 uint32_t block_nr = offset / nio->block_size;
240 size_t offset_in_block = offset % nio->block_size;
241 nand_block_t block_addr = block_nr << nio->ndrv->chip->log2_ppb;
242
243 nand_lock(nio->ndrv);
244 int rc = seek_to_block(nio, block_addr, offset_in_block);
245 nand_unlock(nio->ndrv);
246
247 return rc;
248}
249
250int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count)
251{
252 return nandio_rdwr(nio, buf, count, false);
253}
254
255int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count)
256{
257 return nandio_rdwr(nio, (void*)buf, count, true);
258}
259
260int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count)
261{
262 nand_lock(nio->ndrv);
263
264 /* make sure the current block data is read in */
265 int rc = seek_to_block(nio, nio->cur_block, nio->offset_in_block);
266 if(rc)
267 goto err;
268
269 size_t amount_left = nio->block_size - nio->offset_in_block;
270 if(amount_left == 0) {
271 amount_left = nio->block_size;
272 rc = seek_to_block(nio, nio->cur_block + nio->ndrv->ppb, 0);
273 if(rc)
274 goto err;
275 }
276
277 *buf = &nio->new_buf[nio->offset_in_block];
278 *count = MIN(*count, amount_left);
279
280 nio->offset_in_block += *count;
281 rc = XF_E_SUCCESS;
282
283 err:
284 nand_unlock(nio->ndrv);
285 return rc;
286}
287
288int xf_nandio_flush(struct xf_nandio* nio)
289{
290 nand_lock(nio->ndrv);
291 int rc = flush_block(nio, false);
292 nand_unlock(nio->ndrv);
293
294 return rc;
295}