1
0
Fork 0
forked from len0rd/rockbox

iPod Nano 2G NAND/ECC driver and FTL improvements (still polling)

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22958 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sparmann 2009-10-05 14:42:25 +00:00
parent 112bc15d65
commit 79bf2da1ef
2 changed files with 176 additions and 51 deletions

View file

@ -26,6 +26,35 @@
#include <nand-target.h> #include <nand-target.h>
#include <ftl-target.h> #include <ftl-target.h>
#include <string.h> #include <string.h>
#include "kernel.h"
#include "panic.h"
//#define FTL_FORCEMOUNT
#ifdef FTL_FORCEMOUNT
#ifndef FTL_READONLY
#define FTL_READONLY
#endif
#endif
#ifdef FTL_READONLY
uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
{
(void)sector;
(void)count;
(void)buffer;
return 1;
}
uint32_t ftl_sync(void)
{
return 0;
}
#endif
@ -371,6 +400,8 @@ uint8_t ftl_erasectr_dirt[8];
#endif #endif
static struct mutex ftl_mtx;
/* Finds a device info page for the specified bank and returns its number. /* Finds a device info page for the specified bank and returns its number.
@ -653,6 +684,7 @@ void ftl_vfl_schedule_block_for_remap(uint32_t bank, uint32_t block)
{ {
if (ftl_vfl_check_remap_scheduled(bank, block) == 1) if (ftl_vfl_check_remap_scheduled(bank, block) == 1)
return; return;
panicf("FTL: Scheduling bank %d block %d for remap!", bank, block);
if (ftl_vfl_cxt[bank].scheduledstart == ftl_vfl_cxt[bank].spareused) if (ftl_vfl_cxt[bank].scheduledstart == ftl_vfl_cxt[bank].spareused)
return; return;
ftl_vfl_cxt[bank].remaptable[--ftl_vfl_cxt[bank].scheduledstart] = block; ftl_vfl_cxt[bank].remaptable[--ftl_vfl_cxt[bank].scheduledstart] = block;
@ -738,6 +770,7 @@ uint32_t ftl_vfl_remap_block(uint32_t bank, uint32_t block)
{ {
uint32_t i; uint32_t i;
uint32_t newblock = 0, newidx; uint32_t newblock = 0, newidx;
panicf("FTL: Remapping bank %d block %d!", bank, block);
if (bank >= ftl_banks || block >= (*ftl_nand_type).blocks) return 0; if (bank >= ftl_banks || block >= (*ftl_nand_type).blocks) return 0;
for (i = 0; i < ftl_vfl_cxt[bank].sparecount; i++) for (i = 0; i < ftl_vfl_cxt[bank].sparecount; i++)
if (ftl_vfl_cxt[bank].remaptable[i] == 0) if (ftl_vfl_cxt[bank].remaptable[i] == 0)
@ -949,7 +982,9 @@ uint32_t ftl_open(void)
else else
{ {
/* This will trip if there was an unclean unmount before. */ /* This will trip if there was an unclean unmount before. */
#ifndef FTL_FORCEMOUNT
break; break;
#endif
} }
} }
@ -1030,6 +1065,8 @@ uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer)
if (count == 0) return 0; if (count == 0) return 0;
mutex_lock(&ftl_mtx);
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
uint32_t block = (sector + i) / ppb; uint32_t block = (sector + i) / ppb;
@ -1054,6 +1091,9 @@ uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer)
memset(&((uint8_t*)buffer)[i << 11], 0, 0x800); memset(&((uint8_t*)buffer)[i << 11], 0, 0x800);
} }
} }
mutex_unlock(&ftl_mtx);
return error; return error;
} }
@ -1609,11 +1649,17 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
if (count == 0) return 0; if (count == 0) return 0;
mutex_lock(&ftl_mtx);
if (ftl_cxt.clean_flag == 1) if (ftl_cxt.clean_flag == 1)
{ {
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
{ {
if (ftl_next_ctrl_pool_page() != 0) return 1; if (ftl_next_ctrl_pool_page() != 0)
{
mutex_unlock(&ftl_mtx);
return 1;
}
memset(ftl_buffer, 0xFF, 0x800); memset(ftl_buffer, 0xFF, 0x800);
memset(&ftl_sparebuffer, 0xFF, 0x40); memset(&ftl_sparebuffer, 0xFF, 0x40);
ftl_sparebuffer.meta.usn = ftl_cxt.usn; ftl_sparebuffer.meta.usn = ftl_cxt.usn;
@ -1622,7 +1668,11 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
&ftl_sparebuffer) == 0) &ftl_sparebuffer) == 0)
break; break;
} }
if (i == 3) return 1; if (i == 3)
{
mutex_unlock(&ftl_mtx);
return 1;
}
ftl_cxt.clean_flag = 0; ftl_cxt.clean_flag = 0;
} }
@ -1632,7 +1682,11 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
uint32_t page = (sector + i) % ppb; uint32_t page = (sector + i) % ppb;
struct ftl_log_type* logentry = ftl_allocate_log_entry(block); struct ftl_log_type* logentry = ftl_allocate_log_entry(block);
if (logentry == (struct ftl_log_type*)0) return 1; if (logentry == (struct ftl_log_type*)0)
{
mutex_unlock(&ftl_mtx);
return 1;
}
if (page == 0 && count - i >= ppb) if (page == 0 && count - i >= ppb)
{ {
uint32_t vblock = (*logentry).scatteredvblock; uint32_t vblock = (*logentry).scatteredvblock;
@ -1641,7 +1695,11 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
{ {
ftl_release_pool_block(vblock); ftl_release_pool_block(vblock);
vblock = ftl_allocate_pool_block(); vblock = ftl_allocate_pool_block();
if (vblock == 0) return 1; if (vblock == 0)
{
mutex_unlock(&ftl_mtx);
return 1;
}
} }
ftl_cxt.nextblockusn++; ftl_cxt.nextblockusn++;
for (j = 0; j < ppb; j++) for (j = 0; j < ppb; j++)
@ -1665,7 +1723,11 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
{ {
ftl_remove_scattered_block(logentry); ftl_remove_scattered_block(logentry);
logentry = ftl_allocate_log_entry(block); logentry = ftl_allocate_log_entry(block);
if (logentry == (struct ftl_log_type*)0) return 1; if (logentry == (struct ftl_log_type*)0)
{
mutex_unlock(&ftl_mtx);
return 1;
}
} }
memset(&ftl_sparebuffer, 0xFF, 0x40); memset(&ftl_sparebuffer, 0xFF, 0x40);
ftl_sparebuffer.user.lpn = sector + i; ftl_sparebuffer.user.lpn = sector + i;
@ -1699,6 +1761,7 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
ftl_save_erasectr_page(i); ftl_save_erasectr_page(i);
} }
} }
mutex_unlock(&ftl_mtx);
return 0; return 0;
} }
#endif #endif
@ -1745,6 +1808,7 @@ uint32_t ftl_sync(void)
which will just do nothing if everything was already clean. */ which will just do nothing if everything was already clean. */
uint32_t ftl_init(void) uint32_t ftl_init(void)
{ {
mutex_init(&ftl_mtx);
uint32_t i; uint32_t i;
uint32_t result = 0; uint32_t result = 0;
uint32_t foundsignature, founddevinfo, blockwiped, repaired, skip; uint32_t foundsignature, founddevinfo, blockwiped, repaired, skip;
@ -1755,6 +1819,7 @@ uint32_t ftl_init(void)
ftl_nand_type = nand_get_device_type(0); ftl_nand_type = nand_get_device_type(0);
foundsignature = 0; foundsignature = 0;
blockwiped = 1; blockwiped = 1;
mutex_unlock(&ftl_mtx);
for (i = 0; i < (*ftl_nand_type).pagesperblock; i++) for (i = 0; i < (*ftl_nand_type).pagesperblock; i++)
{ {
result = nand_read_page(0, i, ftl_buffer, (uint32_t*)0, 1, 1); result = nand_read_page(0, i, ftl_buffer, (uint32_t*)0, 1, 1);
@ -1772,10 +1837,23 @@ uint32_t ftl_init(void)
repaired = 0; repaired = 0;
skip = 0; skip = 0;
if (founddevinfo == 0) return 1; if (founddevinfo == 0)
if (foundsignature != 0 && (result & 0x11F) != 0) return 1; {
mutex_unlock(&ftl_mtx);
return 1;
}
if (foundsignature != 0 && (result & 0x11F) != 0)
{
mutex_unlock(&ftl_mtx);
return 1;
}
if (ftl_vfl_open() == 0) if (ftl_vfl_open() == 0)
if (ftl_open() == 0) return 0; if (ftl_open() == 0)
{
mutex_unlock(&ftl_mtx);
return 0;
}
/* Something went terribly wrong. We may want to allow the user to erase /* Something went terribly wrong. We may want to allow the user to erase
block zero in that condition, to make norboot reinitialize the FTL. block zero in that condition, to make norboot reinitialize the FTL.
@ -1785,5 +1863,7 @@ uint32_t ftl_init(void)
nand_block_erase(0, 0); nand_block_erase(0, 0);
*/ */
mutex_unlock(&ftl_mtx);
return 1; return 1;
} }

View file

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include "kernel.h"
#include "cpu.h" #include "cpu.h"
#include "inttypes.h" #include "inttypes.h"
#include "nand-target.h" #include "nand-target.h"
@ -84,6 +85,11 @@ uint8_t nand_tunk2[4];
uint8_t nand_tunk3[4]; uint8_t nand_tunk3[4];
uint32_t nand_type[4]; uint32_t nand_type[4];
static struct mutex nand_mtx;
static struct wakeup nand_wakeup;
static struct mutex ecc_mtx;
static struct wakeup ecc_wakeup;
static uint8_t nand_aligned_data[0x800] __attribute__((aligned(32))); static uint8_t nand_aligned_data[0x800] __attribute__((aligned(32)));
static uint8_t nand_aligned_ctrl[0x200] __attribute__((aligned(32))); static uint8_t nand_aligned_ctrl[0x200] __attribute__((aligned(32)));
static uint8_t nand_aligned_spare[0x40] __attribute__((aligned(32))); static uint8_t nand_aligned_spare[0x40] __attribute__((aligned(32)));
@ -98,35 +104,60 @@ static uint8_t nand_aligned_ecc[0x28] __attribute__((aligned(32)));
((uint8_t*)(((uint32_t)nand_aligned_ecc) | 0x40000000)) ((uint8_t*)(((uint32_t)nand_aligned_ecc) | 0x40000000))
uint32_t nand_unlock(uint32_t rc)
{
mutex_unlock(&nand_mtx);
return rc;
}
uint32_t ecc_unlock(uint32_t rc)
{
mutex_unlock(&ecc_mtx);
return rc;
}
uint32_t nand_timeout(long timeout)
{
if (TIME_AFTER(current_tick, timeout)) return 1;
else
{
yield();
return 0;
}
}
uint32_t nand_wait_rbbdone(void) uint32_t nand_wait_rbbdone(void)
{ {
uint32_t timeout = 0x40000; long timeout = current_tick + HZ / 1;
while ((FMCSTAT & FMCSTAT_RBBDONE) == 0) if (timeout-- == 0) return 1; while ((FMCSTAT & FMCSTAT_RBBDONE) == 0)
if (nand_timeout(timeout)) return 1;
FMCSTAT = FMCSTAT_RBBDONE; FMCSTAT = FMCSTAT_RBBDONE;
return 0; return 0;
} }
uint32_t nand_wait_cmddone(void) uint32_t nand_wait_cmddone(void)
{ {
uint32_t timeout = 0x40000; long timeout = current_tick + HZ / 1;
while ((FMCSTAT & FMCSTAT_CMDDONE) == 0) if (timeout-- == 0) return 1; while ((FMCSTAT & FMCSTAT_CMDDONE) == 0)
if (nand_timeout(timeout)) return 1;
FMCSTAT = FMCSTAT_CMDDONE; FMCSTAT = FMCSTAT_CMDDONE;
return 0; return 0;
} }
uint32_t nand_wait_addrdone(void) uint32_t nand_wait_addrdone(void)
{ {
uint32_t timeout = 0x40000; long timeout = current_tick + HZ / 1;
while ((FMCSTAT & FMCSTAT_ADDRDONE) == 0) if (timeout-- == 0) return 1; while ((FMCSTAT & FMCSTAT_ADDRDONE) == 0)
if (nand_timeout(timeout)) return 1;
FMCSTAT = FMCSTAT_ADDRDONE; FMCSTAT = FMCSTAT_ADDRDONE;
return 0; return 0;
} }
uint32_t nand_wait_chip_ready(uint32_t bank) uint32_t nand_wait_chip_ready(uint32_t bank)
{ {
uint32_t timeout = 0x40000; long timeout = current_tick + HZ / 1;
while ((FMCSTAT & (FMCSTAT_BANK0READY << bank)) == 0) while ((FMCSTAT & (FMCSTAT_BANK0READY << bank)) == 0)
if (timeout-- == 0) return 1; if (nand_timeout(timeout)) return 1;
FMCSTAT = (FMCSTAT_BANK0READY << bank); FMCSTAT = (FMCSTAT_BANK0READY << bank);
return 0; return 0;
} }
@ -163,7 +194,7 @@ uint32_t nand_reset(uint32_t bank)
uint32_t nand_wait_status_ready(uint32_t bank) uint32_t nand_wait_status_ready(uint32_t bank)
{ {
uint32_t timeout = 0x4000; long timeout = current_tick + HZ / 1;
nand_set_fmctrl0(bank, 0); nand_set_fmctrl0(bank, 0);
if ((FMCSTAT & (FMCSTAT_BANK0READY << bank)) != 0) if ((FMCSTAT & (FMCSTAT_BANK0READY << bank)) != 0)
FMCSTAT = (FMCSTAT_BANK0READY << bank); FMCSTAT = (FMCSTAT_BANK0READY << bank);
@ -171,7 +202,7 @@ uint32_t nand_wait_status_ready(uint32_t bank)
if (nand_send_cmd(NAND_CMD_GET_STATUS) != 0) return 1; if (nand_send_cmd(NAND_CMD_GET_STATUS) != 0) return 1;
while (1) while (1)
{ {
if (timeout-- == 0) return 1; if (nand_timeout(timeout)) return 1;
FMDNUM = 0; FMDNUM = 0;
FMCTRL1 = FMCTRL1_DOREADDATA; FMCTRL1 = FMCTRL1_DOREADDATA;
if (nand_wait_addrdone() != 0) return 1; if (nand_wait_addrdone() != 0) return 1;
@ -185,7 +216,7 @@ uint32_t nand_wait_status_ready(uint32_t bank)
uint32_t nand_transfer_data(uint32_t bank, uint32_t direction, uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
void* buffer, uint32_t size) void* buffer, uint32_t size)
{ {
uint32_t timeout = 0x40000; long timeout = current_tick + HZ / 1;
nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA); nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
FMDNUM = size - 1; FMDNUM = size - 1;
FMCTRL1 = FMCTRL1_DOREADDATA << direction; FMCTRL1 = FMCTRL1_DOREADDATA << direction;
@ -199,7 +230,7 @@ uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
DMATCNT3 = (size >> 4) - 1; DMATCNT3 = (size >> 4) - 1;
DMACOM3 = 4; DMACOM3 = 4;
while ((DMAALLST & DMAALLST_DMABUSY3) != 0) while ((DMAALLST & DMAALLST_DMABUSY3) != 0)
if (timeout-- == 0) return 1; if (nand_timeout(timeout)) return 1;
if (nand_wait_addrdone() != 0) return 1; if (nand_wait_addrdone() != 0) return 1;
if (direction == 0) FMCTRL1 = FMCTRL1_CLEARRFIFO | FMCTRL1_CLEARWFIFO; if (direction == 0) FMCTRL1 = FMCTRL1_CLEARRFIFO | FMCTRL1_CLEARWFIFO;
return 0; return 0;
@ -207,32 +238,36 @@ uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer) uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer)
{ {
uint32_t timeout = 0x40000; mutex_lock(&ecc_mtx);
long timeout = current_tick + HZ / 1;
ECC_INT_CLR = 1; ECC_INT_CLR = 1;
SRCPND = INTMSK_ECC; SRCPND = INTMSK_ECC;
ECC_UNK1 = size; ECC_UNK1 = size;
ECC_DATA_PTR = (uint32_t)databuffer; ECC_DATA_PTR = (uint32_t)databuffer;
ECC_SPARE_PTR = (uint32_t)sparebuffer; ECC_SPARE_PTR = (uint32_t)sparebuffer;
ECC_CTRL = ECCCTRL_STARTDECODING; ECC_CTRL = ECCCTRL_STARTDECODING;
while ((SRCPND & INTMSK_ECC) == 0) if (timeout-- == 0) return 1; while ((SRCPND & INTMSK_ECC) == 0)
if (nand_timeout(timeout)) return ecc_unlock(1);
ECC_INT_CLR = 1; ECC_INT_CLR = 1;
SRCPND = INTMSK_ECC; SRCPND = INTMSK_ECC;
return ECC_RESULT; return ecc_unlock(ECC_RESULT);
} }
uint32_t ecc_encode(uint32_t size, void* databuffer, void* sparebuffer) uint32_t ecc_encode(uint32_t size, void* databuffer, void* sparebuffer)
{ {
uint32_t timeout = 0x40000; mutex_lock(&ecc_mtx);
long timeout = current_tick + HZ / 1;
ECC_INT_CLR = 1; ECC_INT_CLR = 1;
SRCPND = INTMSK_ECC; SRCPND = INTMSK_ECC;
ECC_UNK1 = size; ECC_UNK1 = size;
ECC_DATA_PTR = (uint32_t)databuffer; ECC_DATA_PTR = (uint32_t)databuffer;
ECC_SPARE_PTR = (uint32_t)sparebuffer; ECC_SPARE_PTR = (uint32_t)sparebuffer;
ECC_CTRL = ECCCTRL_STARTENCODING; ECC_CTRL = ECCCTRL_STARTENCODING;
while ((SRCPND & INTMSK_ECC) == 0) if (timeout-- == 0) return 1; while ((SRCPND & INTMSK_ECC) == 0)
if (nand_timeout(timeout)) return ecc_unlock(1);
ECC_INT_CLR = 1; ECC_INT_CLR = 1;
SRCPND = INTMSK_ECC; SRCPND = INTMSK_ECC;
return 0; return ecc_unlock(0);
} }
uint32_t nand_check_empty(uint8_t* buffer) uint32_t nand_check_empty(uint8_t* buffer)
@ -246,51 +281,53 @@ uint32_t nand_check_empty(uint8_t* buffer)
uint32_t nand_get_chip_type(uint32_t bank) uint32_t nand_get_chip_type(uint32_t bank)
{ {
mutex_lock(&nand_mtx);
uint32_t result; uint32_t result;
if (nand_reset(bank) != 0) return 0xFFFFFFFF; if (nand_reset(bank) != 0) return nand_unlock(0xFFFFFFFF);
if (nand_send_cmd(0x90) != 0) return 0xFFFFFFFF; if (nand_send_cmd(0x90) != 0) return nand_unlock(0xFFFFFFFF);
FMANUM = 0; FMANUM = 0;
FMADDR0 = 0; FMADDR0 = 0;
FMCTRL1 = FMCTRL1_DOTRANSADDR; FMCTRL1 = FMCTRL1_DOTRANSADDR;
if (nand_wait_cmddone() != 0) return 0xFFFFFFFF; if (nand_wait_cmddone() != 0) return nand_unlock(0xFFFFFFFF);
FMDNUM = 4; FMDNUM = 4;
FMCTRL1 = FMCTRL1_DOREADDATA; FMCTRL1 = FMCTRL1_DOREADDATA;
if (nand_wait_addrdone() != 0) return 0xFFFFFFFF; if (nand_wait_addrdone() != 0) return nand_unlock(0xFFFFFFFF);
result = FMFIFO; result = FMFIFO;
FMCTRL1 = FMCTRL1_CLEARRFIFO | FMCTRL1_CLEARWFIFO; FMCTRL1 = FMCTRL1_CLEARRFIFO | FMCTRL1_CLEARWFIFO;
return result; return nand_unlock(result);
} }
uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer, uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc, void* sparebuffer, uint32_t doecc,
uint32_t checkempty) uint32_t checkempty)
{ {
mutex_lock(&nand_mtx);
uint32_t rc, eccresult; uint32_t rc, eccresult;
nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA); nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
if (nand_send_cmd(NAND_CMD_READ) != 0) return 1; if (nand_send_cmd(NAND_CMD_READ) != 0) return nand_unlock(1);
if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0) if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0)
return 1; return nand_unlock(1);
if (nand_send_cmd(NAND_CMD_READ2) != 0) return 1; if (nand_send_cmd(NAND_CMD_READ2) != 0) return nand_unlock(1);
if (nand_wait_status_ready(bank) != 0) return 1; if (nand_wait_status_ready(bank) != 0) return nand_unlock(1);
if (databuffer != 0) if (databuffer != 0)
if (nand_transfer_data(bank, 0, nand_uncached_data, 0x800) != 0) if (nand_transfer_data(bank, 0, nand_uncached_data, 0x800) != 0)
return 1; return nand_unlock(1);
if (doecc == 0) if (doecc == 0)
{ {
memcpy(databuffer, nand_uncached_data, 0x800); memcpy(databuffer, nand_uncached_data, 0x800);
if (sparebuffer != 0) if (sparebuffer != 0)
{ {
if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0) if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0)
return 1; return nand_unlock(1);
memcpy(sparebuffer, nand_uncached_spare, 0x800); memcpy(sparebuffer, nand_uncached_spare, 0x800);
if (checkempty != 0) if (checkempty != 0)
return nand_check_empty((uint8_t*)sparebuffer) << 1; return nand_check_empty((uint8_t*)sparebuffer) << 1;
} }
return 0; return nand_unlock(0);
} }
rc = 0; rc = 0;
if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0) if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0)
return 1; return nand_unlock(1);
memcpy(nand_uncached_ecc, &nand_uncached_spare[0xC], 0x28); memcpy(nand_uncached_ecc, &nand_uncached_spare[0xC], 0x28);
rc |= (ecc_decode(3, nand_uncached_data, nand_uncached_ecc) & 0xF) << 4; rc |= (ecc_decode(3, nand_uncached_data, nand_uncached_ecc) & 0xF) << 4;
if (databuffer != 0) memcpy(databuffer, nand_uncached_data, 0x800); if (databuffer != 0) memcpy(databuffer, nand_uncached_data, 0x800);
@ -307,51 +344,54 @@ uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer,
} }
if (checkempty != 0) rc |= nand_check_empty(nand_uncached_spare) << 1; if (checkempty != 0) rc |= nand_check_empty(nand_uncached_spare) << 1;
return rc; return nand_unlock(rc);
} }
uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer, uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc) void* sparebuffer, uint32_t doecc)
{ {
mutex_lock(&nand_mtx);
if (sparebuffer != 0) memcpy(nand_uncached_spare, sparebuffer, 0x40); if (sparebuffer != 0) memcpy(nand_uncached_spare, sparebuffer, 0x40);
else memset(nand_uncached_spare, 0xFF, 0x40); else memset(nand_uncached_spare, 0xFF, 0x40);
if (doecc != 0) if (doecc != 0)
{ {
memcpy(nand_uncached_data, databuffer, 0x800); memcpy(nand_uncached_data, databuffer, 0x800);
if (ecc_encode(3, nand_uncached_data, nand_uncached_ecc) != 0) if (ecc_encode(3, nand_uncached_data, nand_uncached_ecc) != 0)
return 1; return nand_unlock(1);
memcpy(&nand_uncached_spare[0xC], nand_uncached_ecc, 0x28); memcpy(&nand_uncached_spare[0xC], nand_uncached_ecc, 0x28);
memset(nand_uncached_ctrl, 0xFF, 0x200); memset(nand_uncached_ctrl, 0xFF, 0x200);
memcpy(nand_uncached_ctrl, nand_uncached_spare, 0xC); memcpy(nand_uncached_ctrl, nand_uncached_spare, 0xC);
if (ecc_encode(0, nand_uncached_ctrl, nand_uncached_ecc) != 0) if (ecc_encode(0, nand_uncached_ctrl, nand_uncached_ecc) != 0)
return 1; return nand_unlock(1);
memcpy(&nand_uncached_spare[0x34], nand_uncached_ecc, 0xC); memcpy(&nand_uncached_spare[0x34], nand_uncached_ecc, 0xC);
} }
nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA); nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
if (nand_send_cmd(NAND_CMD_PROGRAM) != 0) if (nand_send_cmd(NAND_CMD_PROGRAM) != 0)
return 1; return nand_unlock(1);
if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0) if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0)
return 1; return nand_unlock(1);
if (databuffer != 0) if (databuffer != 0)
if (nand_transfer_data(bank, 1, nand_uncached_data, 0x800) != 0) if (nand_transfer_data(bank, 1, nand_uncached_data, 0x800) != 0)
return 1; return nand_unlock(1);
if (sparebuffer != 0 || doecc != 0) if (sparebuffer != 0 || doecc != 0)
if (nand_transfer_data(bank, 1, nand_uncached_spare, 0x40) != 0) if (nand_transfer_data(bank, 1, nand_uncached_spare, 0x40) != 0)
return 1; return nand_unlock(1);
if (nand_send_cmd(NAND_CMD_PROGCNFRM) != 0) return 1; if (nand_send_cmd(NAND_CMD_PROGCNFRM) != 0) return nand_unlock(1);
return nand_wait_status_ready(bank); return nand_wait_status_ready(bank);
} }
uint32_t nand_block_erase(uint32_t bank, uint32_t page) uint32_t nand_block_erase(uint32_t bank, uint32_t page)
{ {
mutex_lock(&nand_mtx);
nand_set_fmctrl0(bank, 0); nand_set_fmctrl0(bank, 0);
if (nand_send_cmd(NAND_CMD_BLOCKERASE) != 0) return 1; if (nand_send_cmd(NAND_CMD_BLOCKERASE) != 0) return nand_unlock(1);
FMANUM = 2; FMANUM = 2;
FMADDR0 = page; FMADDR0 = page;
FMCTRL1 = FMCTRL1_DOTRANSADDR; FMCTRL1 = FMCTRL1_DOTRANSADDR;
if (nand_wait_cmddone() != 0) return 1; if (nand_wait_cmddone() != 0) return nand_unlock(1);
if (nand_send_cmd(NAND_CMD_ERASECNFRM) != 0) return 1; if (nand_send_cmd(NAND_CMD_ERASECNFRM) != 0) return nand_unlock(1);
return nand_wait_status_ready(bank); if (nand_wait_status_ready(bank) != 0) return nand_unlock(1);
return nand_unlock(0);
} }
const struct nand_device_info_type* nand_get_device_type(uint32_t bank) const struct nand_device_info_type* nand_get_device_type(uint32_t bank)
@ -363,6 +403,11 @@ const struct nand_device_info_type* nand_get_device_type(uint32_t bank)
uint32_t nand_device_init(void) uint32_t nand_device_init(void)
{ {
mutex_init(&nand_mtx);
wakeup_init(&nand_wakeup);
mutex_init(&ecc_mtx);
wakeup_init(&ecc_wakeup);
uint32_t type; uint32_t type;
uint32_t i, j; uint32_t i, j;
PCON2 = 0x33333333; PCON2 = 0x33333333;