summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/nand-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/nand-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c665
1 files changed, 321 insertions, 344 deletions
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index fc8d471559..1770324fb3 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -20,253 +20,350 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "nand-x1000.h" 22#include "nand-x1000.h"
23#include "nand-target.h"
24#include "sfc-x1000.h" 23#include "sfc-x1000.h"
25#include "system.h" 24#include "system.h"
26#include <string.h> 25#include <stddef.h>
27 26
28#if !defined(NAND_MAX_PAGE_SIZE) || \ 27/* NAND command numbers */
29 !defined(NAND_INIT_SFC_DEV_CONF) || \ 28#define NAND_CMD_READ_ID 0x9f
30 !defined(NAND_INIT_CLOCK_SPEED) 29#define NAND_CMD_WRITE_ENABLE 0x06
31# error "Target needs to specify NAND driver parameters" 30#define NAND_CMD_GET_FEATURE 0x0f
32#endif 31#define NAND_CMD_SET_FEATURE 0x1f
33 32#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
34/* Must be at least as big as a cacheline */ 33#define NAND_CMD_READ_FROM_CACHE 0x0b
35#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE 34#define NAND_CMD_READ_FROM_CACHEx4 0x6b
36 35#define NAND_CMD_PROGRAM_LOAD 0x02
37/* Writes have been enabled */ 36#define NAND_CMD_PROGRAM_LOADx4 0x32
38#define NAND_DRV_FLAG_WRITEABLE 0x01 37#define NAND_CMD_PROGRAM_EXECUTE 0x10
39 38#define NAND_CMD_BLOCK_ERASE 0xd8
40/* Defined by target */ 39
41extern const nand_chip_desc target_nand_chip_descs[]; 40/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
42 41#define NAND_FREG_PROTECTION 0xa0
43#ifdef BOOTLOADER_SPL 42#define NAND_FREG_FEATURE 0xb0
44# define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR 43#define NAND_FREG_STATUS 0xc0
44
45/* Protection register bits */
46#define NAND_FREG_PROTECTION_BRWD 0x80
47#define NAND_FREG_PROTECTION_BP2 0x20
48#define NAND_FREG_PROTECTION_BP1 0x10
49#define NAND_FREG_PROTECTION_BP0 0x80
50/* Mask of BP bits 0-2 */
51#define NAND_FREG_PROTECTION_ALLBP (0x38)
52
53/* Feature register bits */
54#define NAND_FREG_FEATURE_QE 0x01
55
56/* Status register bits */
57#define NAND_FREG_STATUS_OIP 0x01
58#define NAND_FREG_STATUS_WEL 0x02
59#define NAND_FREG_STATUS_E_FAIL 0x04
60#define NAND_FREG_STATUS_P_FAIL 0x08
61
62/* NAND chip config */
63const nand_chip_data target_nand_chip_data[] = {
64#ifdef FIIO_M3K
65 {
66 /* ATO25D1GA */
67 .mf_id = 0x9b,
68 .dev_id = 0x12,
69 .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1),
70 CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0),
71 STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)),
72 .clock_freq = 150000000,
73 .log2_page_size = 11, /* = 2048 bytes */
74 .log2_block_size = 6, /* = 64 pages */
75 .rowaddr_width = 3,
76 .coladdr_width = 2,
77 .flags = NANDCHIP_FLAG_QUAD,
78 }
45#else 79#else
46# define NANDBUFFER_ATTR CACHEALIGN_ATTR 80 /* Nobody will use this anyway if the device has no NAND flash */
81 { 0 },
47#endif 82#endif
83};
84
85const size_t target_nand_chip_count =
86 sizeof(target_nand_chip_data) / sizeof(nand_chip_data);
87
88/* NAND ops -- high level primitives used by the driver */
89static int nandop_wait_status(int errbit);
90static int nandop_read_page(uint32_t row_addr, uint8_t* buf);
91static int nandop_write_page(uint32_t row_addr, const uint8_t* buf);
92static int nandop_erase_block(uint32_t block_addr);
93static int nandop_set_write_protect(bool en);
94
95/* NAND commands -- 1-to-1 mapping between chip commands and functions */
96static int nandcmd_read_id(int* mf_id, int* dev_id);
97static int nandcmd_write_enable(void);
98static int nandcmd_get_feature(uint8_t reg);
99static int nandcmd_set_feature(uint8_t reg, uint8_t val);
100static int nandcmd_page_read_to_cache(uint32_t row_addr);
101static int nandcmd_read_from_cache(uint8_t* buf);
102static int nandcmd_program_load(const uint8_t* buf);
103static int nandcmd_program_execute(uint32_t row_addr);
104static int nandcmd_block_erase(uint32_t block_addr);
105
106struct nand_drv {
107 const nand_chip_data* chip_data;
108 bool write_enabled;
109};
48 110
49/* Globals for the driver */ 111static struct nand_drv nand_drv;
50static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; 112static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR;
51static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR;
52static nand_drv nand_driver;
53 113
54static void nand_drv_reset(nand_drv* d) 114static void nand_drv_reset(void)
55{ 115{
56 d->chip_ops = NULL; 116 nand_drv.chip_data = NULL;
57 d->chip_data = NULL; 117 nand_drv.write_enabled = false;
58 d->pagebuf = &pagebuffer[0];
59 d->auxbuf = &auxbuffer[0];
60 d->raw_page_size = 0;
61 d->flags = 0;
62} 118}
63 119
64/* Driver code */
65int nand_open(void) 120int nand_open(void)
66{ 121{
67 sfc_init(); 122 sfc_init();
68 sfc_lock(); 123 sfc_lock();
69 124
70 /* Reset driver state */ 125 nand_drv_reset();
71 nand_drv_reset(&nand_driver);
72
73 /* Init hardware */
74 sfc_open(); 126 sfc_open();
75 sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF);
76 sfc_set_clock(NAND_INIT_CLOCK_SPEED);
77
78 /* Identify NAND chip */
79 int status = 0;
80 int nandid = nandcmd_read_id(&nand_driver);
81 if(nandid < 0) {
82 status = NANDERR_CHIP_UNSUPPORTED;
83 goto _err;
84 }
85 127
86 unsigned char mf_id = nandid >> 8; 128 const nand_chip_data* chip_data = &target_nand_chip_data[0];
87 unsigned char dev_id = nandid & 0xff; 129 sfc_set_dev_conf(chip_data->dev_conf);
88 const nand_chip_desc* desc = &target_nand_chip_descs[0]; 130 sfc_set_clock(chip_data->clock_freq);
89 while(1) {
90 if(desc->data == NULL || desc->ops == NULL) {
91 status = NANDERR_CHIP_UNSUPPORTED;
92 goto _err;
93 }
94
95 if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id)
96 break;
97 }
98
99 /* Fill driver parameters */
100 nand_driver.chip_ops = desc->ops;
101 nand_driver.chip_data = desc->data;
102 nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size;
103
104 /* Configure hardware and run init op */
105 sfc_set_dev_conf(desc->data->dev_conf);
106 sfc_set_clock(desc->data->clock_freq);
107 131
108 if((status = desc->ops->open(&nand_driver)) < 0) 132 return NAND_SUCCESS;
109 goto _err;
110
111 _exit:
112 sfc_unlock();
113 return status;
114 _err:
115 nand_drv_reset(&nand_driver);
116 sfc_close();
117 goto _exit;
118} 133}
119 134
120void nand_close(void) 135void nand_close(void)
121{ 136{
122 sfc_lock(); 137 sfc_lock();
123 nand_driver.chip_ops->close(&nand_driver);
124 nand_drv_reset(&nand_driver);
125 sfc_close(); 138 sfc_close();
139 nand_drv_reset();
126 sfc_unlock(); 140 sfc_unlock();
127} 141}
128 142
129int nand_enable_writes(bool en) 143int nand_identify(int* mf_id, int* dev_id)
130{ 144{
131 sfc_lock(); 145 sfc_lock();
132 146
133 int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en); 147 int status = nandcmd_read_id(mf_id, dev_id);
134 if(st >= 0) { 148 if(status < 0)
135 if(en) 149 goto error;
136 nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE; 150
137 else 151 for(size_t i = 0; i < target_nand_chip_count; ++i) {
138 nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE; 152 const nand_chip_data* data = &target_nand_chip_data[i];
153 if(data->mf_id == *mf_id && data->dev_id == *dev_id) {
154 nand_drv.chip_data = data;
155 break;
156 }
157 }
158
159 if(!nand_drv.chip_data) {
160 status = NAND_ERR_UNKNOWN_CHIP;
161 goto error;
139 } 162 }
140 163
164 /* Set parameters according to new chip data */
165 sfc_set_dev_conf(nand_drv.chip_data->dev_conf);
166 sfc_set_clock(nand_drv.chip_data->clock_freq);
167 status = NAND_SUCCESS;
168
169 error:
141 sfc_unlock(); 170 sfc_unlock();
142 return st; 171 return status;
143} 172}
144 173
145extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf) 174const nand_chip_data* nand_get_chip_data(void)
146{ 175{
147 if(count <= 0) 176 return nand_drv.chip_data;
148 return 0; 177}
149 178
150 nand_drv* d = &nand_driver; 179extern int nand_enable_writes(bool en)
151 uint32_t rowaddr = byteaddr / d->chip_data->page_size; 180{
152 uint32_t coladdr = byteaddr % d->chip_data->page_size; 181 if(en == nand_drv.write_enabled)
153 unsigned char* dstbuf = (unsigned char*)buf; 182 return NAND_SUCCESS;
154 int status = 0;
155 183
156 sfc_lock(); 184 int rc = nandop_set_write_protect(!en);
157 do { 185 if(rc == NAND_SUCCESS)
158 if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) { 186 nand_drv.write_enabled = en;
159 status = NANDERR_READ_FAILED;
160 goto _end;
161 }
162 187
163 if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) { 188 return rc;
164 status = NANDERR_ECC_FAILED; 189}
165 goto _end;
166 }
167 190
168 int amount = d->chip_data->page_size - coladdr; 191static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf)
169 if(amount > count) 192{
170 amount = count; 193 const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size);
194
195 if(addr & (page_size - 1))
196 return NAND_ERR_UNALIGNED;
197 if(size & (page_size - 1))
198 return NAND_ERR_UNALIGNED;
199 if(size <= 0)
200 return NAND_SUCCESS;
201 if(write && !nand_drv.write_enabled)
202 return NAND_ERR_WRITE_PROTECT;
203 /* FIXME: re-enable this check after merging new SPL+bootloader.
204 * It's only necessary for DMA, which is currently not used, but it's a
205 * good practice anyway. Disable for now due to SPL complications. */
206 /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1))
207 return NAND_ERR_UNALIGNED;*/
208
209 addr >>= nand_drv.chip_data->log2_page_size;
210 size >>= nand_drv.chip_data->log2_page_size;
211
212 int rc = NAND_SUCCESS;
213 sfc_lock();
171 214
172 memcpy(dstbuf, d->pagebuf, amount); 215 for(; size > 0; --size, ++addr, buf += page_size) {
173 dstbuf += amount; 216 if(write)
174 count -= amount; 217 rc = nandop_write_page(addr, buf);
175 rowaddr += 1; 218 else
176 coladdr = 0; 219 rc = nandop_read_page(addr, buf);
177 } while(count > 0); 220
221 if(rc)
222 break;
223 }
178 224
179 _end:
180 sfc_unlock(); 225 sfc_unlock();
181 return status; 226 return rc;
182} 227}
183 228
184int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) 229int nand_read(uint32_t addr, uint32_t size, uint8_t* buf)
185{ 230{
186 nand_drv* d = &nand_driver; 231 return nand_rdwr(false, addr, size, buf);
232}
187 233
188 if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) 234int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf)
189 return NANDERR_WRITE_PROTECTED; 235{
236 return nand_rdwr(true, addr, size, (uint8_t*)buf);
237}
190 238
191 if(count <= 0) 239int nand_erase(uint32_t addr, uint32_t size)
192 return 0; 240{
241 const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size;
242 const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size;
243 const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_page_size;
244
245 if(addr & (block_size - 1))
246 return NAND_ERR_UNALIGNED;
247 if(size & (block_size - 1))
248 return NAND_ERR_UNALIGNED;
249 if(size <= 0)
250 return NAND_SUCCESS;
251 if(!nand_drv.write_enabled)
252 return NAND_ERR_WRITE_PROTECT;
253
254 addr >>= nand_drv.chip_data->log2_page_size;
255 size >>= nand_drv.chip_data->log2_page_size;
256 size >>= nand_drv.chip_data->log2_block_size;
257
258 int rc = NAND_SUCCESS;
259 sfc_lock();
193 260
194 uint32_t rowaddr = byteaddr / d->chip_data->page_size; 261 for(; size > 0; --size, addr += pages_per_block)
195 uint32_t coladdr = byteaddr % d->chip_data->page_size; 262 if((rc = nandop_erase_block(addr)))
263 break;
196 264
197 /* Only support whole page writes right now */ 265 sfc_unlock();
198 if(coladdr != 0) 266 return rc;
199 return NANDERR_UNALIGNED_ADDRESS; 267}
200 if(count % d->chip_data->page_size)
201 return NANDERR_UNALIGNED_LENGTH;
202 268
203 const unsigned char* srcbuf = (const unsigned char*)buf; 269/*
204 int status = 0; 270 * NAND ops
271 */
205 272
206 sfc_lock(); 273static int nandop_wait_status(int errbit)
274{
275 int reg;
207 do { 276 do {
208 memcpy(d->pagebuf, srcbuf, d->chip_data->page_size); 277 reg = nandcmd_get_feature(NAND_FREG_STATUS);
209 d->chip_ops->ecc_write(d, d->pagebuf); 278 if(reg < 0)
210 279 return reg;
211 if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) { 280 } while(reg & NAND_FREG_STATUS_OIP);
212 status = NANDERR_PROGRAM_FAILED;
213 goto _end;
214 }
215 281
216 rowaddr += 1; 282 if(reg & errbit)
217 srcbuf += d->chip_data->page_size; 283 return NAND_ERR_COMMAND;
218 count -= d->chip_data->page_size;
219 } while(count > 0);
220 284
221 _end: 285 return reg;
222 sfc_unlock();
223 return status;
224} 286}
225 287
226int nand_erase_bytes(uint32_t byteaddr, int count) 288static int nandop_read_page(uint32_t row_addr, uint8_t* buf)
227{ 289{
228 nand_drv* d = &nand_driver; 290 int status;
229 291
230 if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) 292 if((status = nandcmd_page_read_to_cache(row_addr)) < 0)
231 return NANDERR_WRITE_PROTECTED; 293 return status;
294 if((status = nandop_wait_status(0)) < 0)
295 return status;
296 if((status = nandcmd_read_from_cache(buf)) < 0)
297 return status;
232 298
233 /* Ensure address is aligned to a block boundary */ 299 return NAND_SUCCESS;
234 if(byteaddr % d->chip_data->page_size) 300}
235 return NANDERR_UNALIGNED_ADDRESS;
236 301
237 uint32_t blockaddr = byteaddr / d->chip_data->page_size; 302static int nandop_write_page(uint32_t row_addr, const uint8_t* buf)
238 if(blockaddr % d->chip_data->block_size) 303{
239 return NANDERR_UNALIGNED_ADDRESS; 304 int status;
240 305
241 /* Ensure length is also aligned to the size of a block */ 306 if((status = nandcmd_write_enable()) < 0)
242 if(count % d->chip_data->page_size) 307 return status;
243 return NANDERR_UNALIGNED_LENGTH; 308 if((status = nandcmd_program_load(buf)) < 0)
309 return status;
310 if((status = nandcmd_program_execute(row_addr)) < 0)
311 return status;
312 if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0)
313 return status;
244 314
245 count /= d->chip_data->page_size; 315 return NAND_SUCCESS;
246 if(count % d->chip_data->block_size) 316}
247 return NANDERR_UNALIGNED_LENGTH; 317
318static int nandop_erase_block(uint32_t block_addr)
319{
320 int status;
248 321
249 count /= d->chip_data->block_size; 322 if((status = nandcmd_write_enable()) < 0)
323 return status;
324 if((status = nandcmd_block_erase(block_addr)) < 0)
325 return status;
326 if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0)
327 return status;
250 328
251 int status = 0; 329 return NAND_SUCCESS;
252 sfc_lock(); 330}
253 331
254 for(int i = 0; i < count; ++i) { 332static int nandop_set_write_protect(bool en)
255 if(d->chip_ops->erase_block(d, blockaddr)) { 333{
256 status = NANDERR_ERASE_FAILED; 334 int val = nandcmd_get_feature(NAND_FREG_PROTECTION);
257 goto _end; 335 if(val < 0)
258 } 336 return val;
259 337
260 /* Advance to next block */ 338 if(en) {
261 blockaddr += d->chip_data->block_size; 339 val &= ~NAND_FREG_PROTECTION_ALLBP;
340 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
341 val &= ~NAND_FREG_PROTECTION_BRWD;
342 } else {
343 val |= NAND_FREG_PROTECTION_ALLBP;
344 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
345 val |= NAND_FREG_PROTECTION_BRWD;
262 } 346 }
263 347
264 _end: 348 /* NOTE: The WP pin typically only protects changes to the protection
265 sfc_unlock(); 349 * register -- it doesn't actually prevent writing to the chip. That's
266 return status; 350 * why it should be re-enabled after setting the new protection status.
351 */
352 sfc_set_wp_enable(false);
353 int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val);
354 sfc_set_wp_enable(true);
355
356 if(status < 0)
357 return status;
358
359 return NAND_SUCCESS;
267} 360}
268 361
269int nandcmd_read_id(nand_drv* d) 362/*
363 * Low-level NAND commands
364 */
365
366static int nandcmd_read_id(int* mf_id, int* dev_id)
270{ 367{
271 sfc_op op = {0}; 368 sfc_op op = {0};
272 op.command = NAND_CMD_READ_ID; 369 op.command = NAND_CMD_READ_ID;
@@ -274,72 +371,72 @@ int nandcmd_read_id(nand_drv* d)
274 op.addr_bytes = 1; 371 op.addr_bytes = 1;
275 op.addr_lo = 0; 372 op.addr_lo = 0;
276 op.data_bytes = 2; 373 op.data_bytes = 2;
277 op.buffer = d->auxbuf; 374 op.buffer = nand_auxbuf;
278 if(sfc_exec(&op)) 375 if(sfc_exec(&op))
279 return NANDERR_COMMAND_FAILED; 376 return NAND_ERR_CONTROLLER;
280 377
281 return (d->auxbuf[0] << 8) | d->auxbuf[1]; 378 *mf_id = nand_auxbuf[0];
379 *dev_id = nand_auxbuf[1];
380 return NAND_SUCCESS;
282} 381}
283 382
284int nandcmd_write_enable(nand_drv* d) 383static int nandcmd_write_enable(void)
285{ 384{
286 (void)d;
287
288 sfc_op op = {0}; 385 sfc_op op = {0};
289 op.command = NAND_CMD_WRITE_ENABLE; 386 op.command = NAND_CMD_WRITE_ENABLE;
290 if(sfc_exec(&op)) 387 if(sfc_exec(&op))
291 return NANDERR_COMMAND_FAILED; 388 return NAND_ERR_CONTROLLER;
292 389
293 return 0; 390 return NAND_SUCCESS;
294} 391}
295 392
296int nandcmd_get_feature(nand_drv* d, int reg) 393static int nandcmd_get_feature(uint8_t reg)
297{ 394{
298 sfc_op op = {0}; 395 sfc_op op = {0};
299 op.command = NAND_CMD_GET_FEATURE; 396 op.command = NAND_CMD_GET_FEATURE;
300 op.flags = SFC_FLAG_READ; 397 op.flags = SFC_FLAG_READ;
301 op.addr_bytes = 1; 398 op.addr_bytes = 1;
302 op.addr_lo = reg & 0xff; 399 op.addr_lo = reg;
303 op.data_bytes = 1; 400 op.data_bytes = 1;
304 op.buffer = d->auxbuf; 401 op.buffer = nand_auxbuf;
305 if(sfc_exec(&op)) 402 if(sfc_exec(&op))
306 return NANDERR_COMMAND_FAILED; 403 return NAND_ERR_CONTROLLER;
307 404
308 return d->auxbuf[0]; 405 return nand_auxbuf[0];
309} 406}
310 407
311int nandcmd_set_feature(nand_drv* d, int reg, int val) 408static int nandcmd_set_feature(uint8_t reg, uint8_t val)
312{ 409{
313 sfc_op op = {0}; 410 sfc_op op = {0};
314 op.command = NAND_CMD_SET_FEATURE; 411 op.command = NAND_CMD_SET_FEATURE;
315 op.flags = SFC_FLAG_READ; 412 op.flags = SFC_FLAG_READ;
316 op.addr_bytes = 1; 413 op.addr_bytes = 1;
317 op.addr_lo = reg & 0xff; 414 op.addr_lo = reg;
318 op.data_bytes = 1; 415 op.data_bytes = 1;
319 op.buffer = d->auxbuf; 416 op.buffer = nand_auxbuf;
320 d->auxbuf[0] = val & 0xff; 417 nand_auxbuf[0] = val;
321 if(sfc_exec(&op)) 418 if(sfc_exec(&op))
322 return NANDERR_COMMAND_FAILED; 419 return NAND_ERR_CONTROLLER;
323 420
324 return 0; 421 return NAND_SUCCESS;
325} 422}
326 423
327int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr) 424static int nandcmd_page_read_to_cache(uint32_t row_addr)
328{ 425{
329 sfc_op op = {0}; 426 sfc_op op = {0};
330 op.command = NAND_CMD_PAGE_READ_TO_CACHE; 427 op.command = NAND_CMD_PAGE_READ_TO_CACHE;
331 op.addr_bytes = d->chip_data->rowaddr_width; 428 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
332 op.addr_lo = rowaddr; 429 op.addr_lo = row_addr;
333 if(sfc_exec(&op)) 430 if(sfc_exec(&op))
334 return NANDERR_COMMAND_FAILED; 431 return NAND_ERR_CONTROLLER;
335 432
336 return 0; 433 return NAND_SUCCESS;
337} 434}
338 435
339int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) 436static int nandcmd_read_from_cache(uint8_t* buf)
340{ 437{
341 sfc_op op = {0}; 438 sfc_op op = {0};
342 if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { 439 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) {
343 op.command = NAND_CMD_READ_FROM_CACHEx4; 440 op.command = NAND_CMD_READ_FROM_CACHEx4;
344 op.mode = SFC_MODE_QUAD_IO; 441 op.mode = SFC_MODE_QUAD_IO;
345 } else { 442 } else {
@@ -348,21 +445,21 @@ int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf)
348 } 445 }
349 446
350 op.flags = SFC_FLAG_READ; 447 op.flags = SFC_FLAG_READ;
351 op.addr_bytes = d->chip_data->coladdr_width; 448 op.addr_bytes = nand_drv.chip_data->coladdr_width;
352 op.addr_lo = 0; 449 op.addr_lo = 0;
353 op.dummy_bits = 8; 450 op.dummy_bits = 8; // NOTE: this may need a chip_data parameter
354 op.data_bytes = d->raw_page_size; 451 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
355 op.buffer = buf; 452 op.buffer = buf;
356 if(sfc_exec(&op)) 453 if(sfc_exec(&op))
357 return NANDERR_COMMAND_FAILED; 454 return NAND_ERR_CONTROLLER;
358 455
359 return 0; 456 return NAND_SUCCESS;
360} 457}
361 458
362int nandcmd_program_load(nand_drv* d, const unsigned char* buf) 459static int nandcmd_program_load(const uint8_t* buf)
363{ 460{
364 sfc_op op = {0}; 461 sfc_op op = {0};
365 if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { 462 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) {
366 op.command = NAND_CMD_PROGRAM_LOADx4; 463 op.command = NAND_CMD_PROGRAM_LOADx4;
367 op.mode = SFC_MODE_QUAD_IO; 464 op.mode = SFC_MODE_QUAD_IO;
368 } else { 465 } else {
@@ -371,156 +468,36 @@ int nandcmd_program_load(nand_drv* d, const unsigned char* buf)
371 } 468 }
372 469
373 op.flags = SFC_FLAG_WRITE; 470 op.flags = SFC_FLAG_WRITE;
374 op.addr_bytes = d->chip_data->coladdr_width; 471 op.addr_bytes = nand_drv.chip_data->coladdr_width;
375 op.addr_lo = 0; 472 op.addr_lo = 0;
376 op.data_bytes = d->raw_page_size; 473 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
377 op.buffer = (void*)buf; 474 op.buffer = (void*)buf;
378 if(sfc_exec(&op)) 475 if(sfc_exec(&op))
379 return NANDERR_COMMAND_FAILED; 476 return NAND_ERR_CONTROLLER;
380 477
381 return 0; 478 return NAND_SUCCESS;
382} 479}
383 480
384int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr) 481static int nandcmd_program_execute(uint32_t row_addr)
385{ 482{
386 sfc_op op = {0}; 483 sfc_op op = {0};
387 op.command = NAND_CMD_PROGRAM_EXECUTE; 484 op.command = NAND_CMD_PROGRAM_EXECUTE;
388 op.addr_bytes = d->chip_data->rowaddr_width; 485 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
389 op.addr_lo = rowaddr; 486 op.addr_lo = row_addr;
390 if(sfc_exec(&op)) 487 if(sfc_exec(&op))
391 return NANDERR_COMMAND_FAILED; 488 return NAND_ERR_CONTROLLER;
392 489
393 return 0; 490 return NAND_SUCCESS;
394} 491}
395 492
396int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr) 493static int nandcmd_block_erase(uint32_t block_addr)
397{ 494{
398 sfc_op op = {0}; 495 sfc_op op = {0};
399 op.command = NAND_CMD_BLOCK_ERASE; 496 op.command = NAND_CMD_BLOCK_ERASE;
400 op.addr_bytes = d->chip_data->rowaddr_width; 497 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
401 op.addr_lo = blockaddr; 498 op.addr_lo = block_addr;
402 if(sfc_exec(&op)) 499 if(sfc_exec(&op))
403 return NANDERR_COMMAND_FAILED; 500 return NAND_ERR_CONTROLLER;
404 501
405 return 0; 502 return NAND_SUCCESS;
406}
407
408const nand_chip_ops nand_chip_ops_std = {
409 .open = nandop_std_open,
410 .close = nandop_std_close,
411 .read_page = nandop_std_read_page,
412 .write_page = nandop_std_write_page,
413 .erase_block = nandop_std_erase_block,
414 .set_wp_enable = nandop_std_set_wp_enable,
415 .ecc_read = nandop_ecc_none_read,
416 .ecc_write = nandop_ecc_none_write,
417};
418
419/* Helper needed by other ops */
420static int nandop_std_wait_status(nand_drv* d, int errbit)
421{
422 int reg;
423 do {
424 reg = nandcmd_get_feature(d, NAND_FREG_STATUS);
425 if(reg < 0)
426 return reg;
427 } while(reg & NAND_FREG_STATUS_OIP);
428
429 if(reg & errbit)
430 return NANDERR_OTHER;
431
432 return reg;
433}
434
435int nandop_std_open(nand_drv* d)
436{
437 (void)d;
438 return 0;
439}
440
441void nandop_std_close(nand_drv* d)
442{
443 (void)d;
444}
445
446int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf)
447{
448 int status;
449
450 if((status = nandcmd_page_read_to_cache(d, rowaddr)) < 0)
451 return status;
452 if((status = nandop_std_wait_status(d, 0)) < 0)
453 return status;
454 if((status = nandcmd_read_from_cache(d, buf)) < 0)
455 return status;
456
457 return 0;
458}
459
460int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf)
461{
462 int status;
463
464 if((status = nandcmd_write_enable(d)) < 0)
465 return status;
466 if((status = nandcmd_program_load(d, buf)) < 0)
467 return status;
468 if((status = nandcmd_program_execute(d, rowaddr)) < 0)
469 return status;
470 if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL)) < 0)
471 return status;
472
473 return 0;
474}
475
476int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr)
477{
478 int status;
479
480 if((status = nandcmd_write_enable(d)) < 0)
481 return status;
482 if((status = nandcmd_block_erase(d, blockaddr)) < 0)
483 return status;
484 if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0))
485 return status;
486
487 return 0;
488}
489
490int nandop_std_set_wp_enable(nand_drv* d, bool en)
491{
492 int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION);
493 if(val < 0)
494 return val;
495
496 if(en) {
497 val &= ~NAND_FREG_PROTECTION_ALLBP;
498 if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
499 val &= ~NAND_FREG_PROTECTION_BRWD;
500 } else {
501 val |= NAND_FREG_PROTECTION_ALLBP;
502 if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
503 val |= NAND_FREG_PROTECTION_BRWD;
504 }
505
506 sfc_set_wp_enable(false);
507 int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val);
508 sfc_set_wp_enable(true);
509
510 if(status < 0)
511 return status;
512
513 return 0;
514}
515
516int nandop_ecc_none_read(nand_drv* d, unsigned char* buf)
517{
518 (void)d;
519 (void)buf;
520 return 0;
521}
522
523void nandop_ecc_none_write(nand_drv* d, unsigned char* buf)
524{
525 memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size);
526} 503}