Even more Nano2G FTL speedup. Now 8% faster than disk mode, 10% slower than the OFW. 4.5MB/s sustained (contiguous) write, 6.0MB/s read for me now.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25109 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sparmann 2010-03-11 00:59:17 +00:00
parent 029e74866a
commit 62c011c8fb
5 changed files with 286 additions and 285 deletions

View file

@ -220,4 +220,5 @@
//#define HAVE_SERIAL //#define HAVE_SERIAL
#define STORAGE_ALIGN_MASK 15 #define STORAGE_ALIGN_MASK 15
#define USB_WRITE_BUFFER_SIZE (1024*64)

View file

@ -31,6 +31,8 @@
#define FTL_COPYBUF_SIZE 32
#define FTL_WRITESPARE_SIZE 32
//#define FTL_FORCEMOUNT //#define FTL_FORCEMOUNT
@ -372,7 +374,7 @@ struct ftl_cxt_type ftl_cxt;
uint8_t ftl_buffer[0x800] __attribute__((aligned(16))); uint8_t ftl_buffer[0x800] __attribute__((aligned(16)));
/* Temporary spare byte buffer for internal use by the FTL */ /* Temporary spare byte buffer for internal use by the FTL */
union ftl_spare_data_type ftl_sparebuffer[4] __attribute__((aligned(16))); union ftl_spare_data_type ftl_sparebuffer[FTL_WRITESPARE_SIZE] __attribute__((aligned(16)));
#ifndef FTL_READONLY #ifndef FTL_READONLY
@ -402,8 +404,8 @@ uint8_t ftl_erasectr_dirt[8];
/* Buffer needed for copying pages around while moving or committing blocks. /* Buffer needed for copying pages around while moving or committing blocks.
This can't be shared with ftl_buffer, because this one could be overwritten This can't be shared with ftl_buffer, because this one could be overwritten
during the copying operation in order to e.g. commit a CXT. */ during the copying operation in order to e.g. commit a CXT. */
uint8_t ftl_copybuffer[4][0x800] __attribute__((aligned(16))); uint8_t ftl_copybuffer[FTL_COPYBUF_SIZE][0x800] __attribute__((aligned(16)));
union ftl_spare_data_type ftl_copyspare[4] __attribute__((aligned(16))); union ftl_spare_data_type ftl_copyspare[FTL_COPYBUF_SIZE] __attribute__((aligned(16)));
/* Needed to store the old scattered page offsets in order to be able to roll /* Needed to store the old scattered page offsets in order to be able to roll
back if something fails while compacting a scattered page block. */ back if something fails while compacting a scattered page block. */
@ -916,85 +918,72 @@ uint32_t ftl_vfl_read_fast(uint32_t vpage, void* buffer, void* sparebuffer,
#ifndef FTL_READONLY #ifndef FTL_READONLY
/* Writes the specified vPage, dealing with all kinds of trouble */ /* Writes the specified vPage, dealing with all kinds of trouble */
uint32_t ftl_vfl_write(uint32_t vpage, void* buffer, void* sparebuffer) uint32_t ftl_vfl_write(uint32_t vpage, uint32_t count,
void* buffer, void* sparebuffer)
{ {
uint32_t i, j;
uint32_t ppb = (*ftl_nand_type).pagesperblock * ftl_banks; uint32_t ppb = (*ftl_nand_type).pagesperblock * ftl_banks;
uint32_t syshyperblocks = (*ftl_nand_type).blocks uint32_t syshyperblocks = (*ftl_nand_type).blocks
- (*ftl_nand_type).userblocks - 0x17; - (*ftl_nand_type).userblocks - 0x17;
uint32_t abspage = vpage + ppb * syshyperblocks; uint32_t abspage = vpage + ppb * syshyperblocks;
if (abspage >= (*ftl_nand_type).blocks * ppb || abspage < ppb) if (abspage + count > (*ftl_nand_type).blocks * ppb || abspage < ppb)
panicf("FTL: Trying to write out-of-bounds vPage %u", panicf("FTL: Trying to write out-of-bounds vPage %u",
(unsigned)vpage); (unsigned)vpage);
//return 4; //return 4;
uint32_t bank = abspage % ftl_banks; uint32_t bank[5];
uint32_t block = abspage / ((*ftl_nand_type).pagesperblock * ftl_banks); uint32_t block[5];
uint32_t page = (abspage / ftl_banks) % (*ftl_nand_type).pagesperblock; uint32_t physpage[5];
uint32_t physblock = ftl_vfl_get_physical_block(bank, block);
uint32_t physpage = physblock * (*ftl_nand_type).pagesperblock + page;
if (nand_write_page(bank, physpage, buffer, sparebuffer, 1) == 0) for (i = 0; i < count; i++, abspage++)
return 0;
if ((nand_read_page(bank, physpage, ftl_buffer,
&ftl_sparebuffer[0], 1, 1) & 0x11F) == 0)
return 0;
panicf("FTL: write error on vPage %u, bank %u, pPage %u",
(unsigned)vpage, (unsigned)bank, (unsigned)physpage);
ftl_vfl_log_trouble(bank, block);
return 1;
}
#endif
#ifndef FTL_READONLY
/* Multi-bank version of ftl_vfl_write, will write ftl_banks pages in parallel */
uint32_t ftl_vfl_write_fast(uint32_t vpage, void* buffer, void* sparebuffer)
{
uint32_t i, rc = 0;
uint32_t ppb = (*ftl_nand_type).pagesperblock * ftl_banks;
uint32_t syshyperblocks = (*ftl_nand_type).blocks
- (*ftl_nand_type).userblocks - 0x17;
uint32_t abspage = vpage + ppb * syshyperblocks;
if (abspage + ftl_banks - 1 >= (*ftl_nand_type).blocks * ppb || abspage < ppb)
panicf("FTL: Trying to write out-of-bounds vPage %u",
(unsigned)vpage);
//return 4;
uint32_t bank = abspage % ftl_banks;
uint32_t block = abspage / ((*ftl_nand_type).pagesperblock * ftl_banks);
uint32_t page = (abspage / ftl_banks) % (*ftl_nand_type).pagesperblock;
if (bank)
{ {
for (i = 0; i < ftl_banks; i++) for (j = ftl_banks; j > 0; j--)
{ {
void* databuf = (void*)0; bank[j] = bank[j - 1];
void* sparebuf = (void*)0; block[j] = block[j - 1];
if (buffer) databuf = (void*)((uint32_t)buffer + 0x800 * i); physpage[j] = physpage[j - 1];
if (sparebuffer) sparebuf = (void*)((uint32_t)sparebuffer + 0x40 * i);
rc |= ftl_vfl_write(vpage + i, databuf, sparebuf) << i;
} }
return rc; bank[0] = abspage % ftl_banks;
block[0] = abspage / ((*ftl_nand_type).pagesperblock * ftl_banks);
uint32_t page = (abspage / ftl_banks) % (*ftl_nand_type).pagesperblock;
uint32_t physblock = ftl_vfl_get_physical_block(bank[0], block[0]);
physpage[0] = physblock * (*ftl_nand_type).pagesperblock + page;
if (i >= ftl_banks)
if (nand_write_page_collect(bank[ftl_banks]))
if (nand_read_page(bank[ftl_banks], physpage[ftl_banks],
ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F)
{
panicf("FTL: write error (2) on vPage %u, bank %u, pPage %u",
(unsigned)(vpage + i - ftl_banks),
(unsigned)bank[ftl_banks],
(unsigned)physpage[ftl_banks]);
ftl_vfl_log_trouble(bank[ftl_banks], block[ftl_banks]);
}
if (nand_write_page_start(bank[0], physpage[0],
(void*)((uint32_t)buffer + 0x800 * i),
(void*)((uint32_t)sparebuffer + 0x40 * i), 1))
if (nand_read_page(bank[0], physpage[0], ftl_buffer,
&ftl_sparebuffer[0], 1, 1) & 0x11F)
{
panicf("FTL: write error (1) on vPage %u, bank %u, pPage %u",
(unsigned)(vpage + i), (unsigned)bank[0], (unsigned)physpage[0]);
ftl_vfl_log_trouble(bank[0], block[0]);
}
} }
uint32_t physblock = ftl_vfl_get_physical_block(bank, block);
uint32_t physpage = physblock * (*ftl_nand_type).pagesperblock + page;
rc = nand_write_page_fast(physpage, buffer, sparebuffer, 1); for (i = count < ftl_banks ? count : ftl_banks; i > 0; i--)
if (!rc) return 0; if (nand_write_page_collect(bank[i - 1]))
if (nand_read_page(bank[i - 1], physpage[i - 1],
ftl_buffer, &ftl_sparebuffer[0], 1, 1) & 0x11F)
{
panicf("FTL: write error (2) on vPage %u, bank %u, pPage %u",
(unsigned)(vpage + count - i),
(unsigned)bank[i - 1], (unsigned)physpage[i - 1]);
ftl_vfl_log_trouble(bank[i - 1], block[i - 1]);
}
for (i = 0; i < ftl_banks; i++) return 0;
if (rc & (1 << i))
{
if (!(nand_read_page(i, physpage, ftl_buffer,
&ftl_sparebuffer[i], 1, 1) & 0x11F))
rc &= ~(1 << i);
panicf("FTL: write error on vPage %u, bank %u, pPage %u",
(unsigned)(vpage + i), (unsigned)i, (unsigned)physpage);
ftl_vfl_log_trouble(i, block);
}
return rc;
} }
#endif #endif
@ -1315,8 +1304,8 @@ uint32_t ftl_erase_block_internal(uint32_t block)
ftl_vfl_cxt[i].field_18++; ftl_vfl_cxt[i].field_18++;
if (ftl_vfl_remap_block(i, block) == 0) return 1; if (ftl_vfl_remap_block(i, block) == 0) return 1;
if (ftl_vfl_commit_cxt(i) != 0) return 1; if (ftl_vfl_commit_cxt(i) != 0) return 1;
memset(&ftl_sparebuffer[i], 0, 0x40); memset(&ftl_sparebuffer[0], 0, 0x40);
nand_write_page(i, pblock[i], &ftl_vfl_cxt[0], &ftl_sparebuffer[i], 1); nand_write_page(i, pblock[i], &ftl_vfl_cxt[0], &ftl_sparebuffer[0], 1);
} }
} }
return 0; return 0;
@ -1406,7 +1395,7 @@ uint32_t ftl_save_erasectr_page(uint32_t index)
ftl_sparebuffer[0].meta.usn = ftl_cxt.usn; ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
ftl_sparebuffer[0].meta.idx = index; ftl_sparebuffer[0].meta.idx = index;
ftl_sparebuffer[0].meta.type = 0x46; ftl_sparebuffer[0].meta.type = 0x46;
if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_erasectr[index << 10], if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_erasectr[index << 10],
&ftl_sparebuffer[0]) != 0) &ftl_sparebuffer[0]) != 0)
return 1; return 1;
if ((ftl_vfl_read(ftl_cxt.ftlctrlpage, ftl_buffer, if ((ftl_vfl_read(ftl_cxt.ftlctrlpage, ftl_buffer,
@ -1478,7 +1467,7 @@ uint32_t ftl_copy_page(uint32_t source, uint32_t destination,
else if (rc != 0) ftl_copyspare[0].user.eccmark = 0x55; else if (rc != 0) ftl_copyspare[0].user.eccmark = 0x55;
if (type == 1 && destination % ppb == ppb - 1) if (type == 1 && destination % ppb == ppb - 1)
ftl_copyspare[0].user.type = 0x41; ftl_copyspare[0].user.type = 0x41;
return ftl_vfl_write(destination, ftl_copybuffer[0], &ftl_copyspare[0]); return ftl_vfl_write(destination, 1, ftl_copybuffer[0], &ftl_copyspare[0]);
} }
#endif #endif
@ -1491,24 +1480,25 @@ uint32_t ftl_copy_block(uint32_t source, uint32_t destination)
uint32_t ppb = (*ftl_nand_type).pagesperblock * ftl_banks; uint32_t ppb = (*ftl_nand_type).pagesperblock * ftl_banks;
uint32_t error = 0; uint32_t error = 0;
ftl_cxt.nextblockusn++; ftl_cxt.nextblockusn++;
for (i = 0; i < (*ftl_nand_type).pagesperblock; i ++) for (i = 0; i < ppb; i += FTL_COPYBUF_SIZE)
{ {
uint32_t rc = ftl_read(source * ppb + i * ftl_banks, ftl_banks, ftl_copybuffer[0]); uint32_t rc = ftl_read(source * ppb + i,
memset(&ftl_copyspare[0], 0xFF, 0x100); FTL_COPYBUF_SIZE, ftl_copybuffer[0]);
for (j = 0; j < ftl_banks; j++) memset(&ftl_copyspare[0], 0xFF, 0x40 * FTL_COPYBUF_SIZE);
for (j = 0; j < FTL_COPYBUF_SIZE; j++)
{ {
ftl_copyspare[j].user.lpn = source * ppb + i * ftl_banks + j; ftl_copyspare[j].user.lpn = source * ppb + i + j;
ftl_copyspare[j].user.usn = ftl_cxt.nextblockusn; ftl_copyspare[j].user.usn = ftl_cxt.nextblockusn;
ftl_copyspare[j].user.type = 0x40; ftl_copyspare[j].user.type = 0x40;
if (rc) if (rc)
{ {
if (ftl_read(source * ppb + i * ftl_banks + j, 1, ftl_copybuffer[j])) if (ftl_read(source * ppb + i + j, 1, ftl_copybuffer[j]))
ftl_copyspare[j].user.eccmark = 0x55; ftl_copyspare[j].user.eccmark = 0x55;
} }
if (i + j == ppb - 1) ftl_copyspare[j].user.type = 0x41; if (i + j == ppb - 1) ftl_copyspare[j].user.type = 0x41;
} }
if (ftl_vfl_write_fast(destination * ppb + i * ftl_banks, if (ftl_vfl_write(destination * ppb + i, FTL_COPYBUF_SIZE,
ftl_copybuffer[0], &ftl_copyspare[0])) ftl_copybuffer[0], &ftl_copyspare[0]))
{ {
error = 1; error = 1;
break; break;
@ -1636,37 +1626,30 @@ uint32_t ftl_commit_sequential(struct ftl_log_type* entry)
|| (*entry).pagescurrent != (*entry).pagesused) || (*entry).pagescurrent != (*entry).pagesused)
return 1; return 1;
for (; (*entry).pagesused < ppb; (*entry).pagesused++) for (; (*entry).pagesused < ppb; )
{ {
uint32_t lpn = (*entry).logicalvblock * ppb + (*entry).pagesused; uint32_t lpn = (*entry).logicalvblock * ppb + (*entry).pagesused;
uint32_t newpage = (*entry).scatteredvblock * ppb uint32_t newpage = (*entry).scatteredvblock * ppb
+ (*entry).pagesused; + (*entry).pagesused;
uint32_t oldpage = ftl_map[(*entry).logicalvblock] * ppb uint32_t count = FTL_COPYBUF_SIZE < ppb - (*entry).pagesused
+ (*entry).pagesused; ? FTL_COPYBUF_SIZE : ppb - (*entry).pagesused;
if ((*entry).pageoffsets[(*entry).pagesused] != 0xFFFF) for (i = 0; i < count; i++)
return ftl_commit_scattered(entry); if ((*entry).pageoffsets[(*entry).pagesused + i] != 0xFFFF)
if (!((*entry).pagesused & (ftl_banks - 1)))
{
uint32_t rc = ftl_vfl_read_fast(oldpage, ftl_copybuffer[0],
&ftl_copyspare[0], 1, 1);
memset(&ftl_copyspare[0], 0xFF, 0x100);
for (i = 0; i < ftl_banks; i++)
{
ftl_copyspare[i].user.lpn = lpn + i;
ftl_copyspare[i].user.usn = ++ftl_cxt.nextblockusn;
ftl_copyspare[i].user.type = 0x40;
if (rc & (2 << (i << 2))) memset(ftl_copybuffer[i], 0, 0x800);
else if (rc & (0xd << (i << 2)))
ftl_copyspare[i].user.eccmark = 0x55;
if ((*entry).pagesused + i == ppb - 1)
ftl_copyspare[i].user.type = 0x41;
}
if (ftl_vfl_write_fast(newpage, ftl_copybuffer[0], &ftl_copyspare[0]))
return ftl_commit_scattered(entry); return ftl_commit_scattered(entry);
(*entry).pagesused += ftl_banks - 1; uint32_t rc = ftl_read(lpn, count, ftl_copybuffer[0]);
memset(&ftl_copyspare[0], 0xFF, 0x40 * FTL_COPYBUF_SIZE);
for (i = 0; i < count; i++)
{
ftl_copyspare[i].user.lpn = lpn + i;
ftl_copyspare[i].user.usn = ++ftl_cxt.nextblockusn;
ftl_copyspare[i].user.type = 0x40;
if (rc) ftl_copyspare[i].user.eccmark = 0x55;
if ((*entry).pagesused + i == ppb - 1)
ftl_copyspare[i].user.type = 0x41;
} }
else if (ftl_copy_page(oldpage, newpage, lpn, 1)) if (ftl_vfl_write(newpage, count, ftl_copybuffer[0], &ftl_copyspare[0]))
return ftl_commit_scattered(entry); return ftl_commit_scattered(entry);
(*entry).pagesused += count;
} }
ftl_release_pool_block(ftl_map[(*entry).logicalvblock]); ftl_release_pool_block(ftl_map[(*entry).logicalvblock]);
ftl_map[(*entry).logicalvblock] = (*entry).scatteredvblock; ftl_map[(*entry).logicalvblock] = (*entry).scatteredvblock;
@ -1733,6 +1716,7 @@ struct ftl_log_type* ftl_allocate_log_entry(uint32_t block)
{ {
uint32_t i; uint32_t i;
struct ftl_log_type* entry = ftl_get_log_entry(block); struct ftl_log_type* entry = ftl_get_log_entry(block);
(*entry).usn = ftl_cxt.nextblockusn - 1;
if (entry != (struct ftl_log_type*)0) return entry; if (entry != (struct ftl_log_type*)0) return entry;
for (i = 0; i < 0x11; i++) for (i = 0; i < 0x11; i++)
@ -1776,7 +1760,7 @@ uint32_t ftl_commit_cxt(void)
uint32_t mappages = ((*ftl_nand_type).userblocks + 0x3ff) >> 10; uint32_t mappages = ((*ftl_nand_type).userblocks + 0x3ff) >> 10;
uint32_t ctrpages = ((*ftl_nand_type).userblocks + 23 + 0x3ff) >> 10; uint32_t ctrpages = ((*ftl_nand_type).userblocks + 23 + 0x3ff) >> 10;
uint32_t endpage = ftl_cxt.ftlctrlpage + mappages + ctrpages + 1; uint32_t endpage = ftl_cxt.ftlctrlpage + mappages + ctrpages + 1;
if (endpage % ppb > ppb - 1) if (endpage >= (ftl_cxt.ftlctrlpage / ppb + 1) * ppb)
ftl_cxt.ftlctrlpage |= ppb - 1; ftl_cxt.ftlctrlpage |= ppb - 1;
for (i = 0; i < ctrpages; i++) for (i = 0; i < ctrpages; i++)
{ {
@ -1790,7 +1774,7 @@ uint32_t ftl_commit_cxt(void)
ftl_sparebuffer[0].meta.usn = ftl_cxt.usn; ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
ftl_sparebuffer[0].meta.idx = i; ftl_sparebuffer[0].meta.idx = i;
ftl_sparebuffer[0].meta.type = 0x44; ftl_sparebuffer[0].meta.type = 0x44;
if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_map[i << 10], if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_map[i << 10],
&ftl_sparebuffer[0]) != 0) &ftl_sparebuffer[0]) != 0)
return 1; return 1;
ftl_cxt.ftl_map_pages[i] = ftl_cxt.ftlctrlpage; ftl_cxt.ftl_map_pages[i] = ftl_cxt.ftlctrlpage;
@ -1800,7 +1784,7 @@ uint32_t ftl_commit_cxt(void)
memset(&ftl_sparebuffer[0], 0xFF, 0x40); memset(&ftl_sparebuffer[0], 0xFF, 0x40);
ftl_sparebuffer[0].meta.usn = ftl_cxt.usn; ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
ftl_sparebuffer[0].meta.type = 0x43; ftl_sparebuffer[0].meta.type = 0x43;
if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_cxt, &ftl_sparebuffer[0]) != 0) if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, &ftl_cxt, &ftl_sparebuffer[0]) != 0)
return 1; return 1;
return 0; return 0;
} }
@ -1877,7 +1861,7 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
memset(&ftl_sparebuffer[0], 0xFF, 0x40); memset(&ftl_sparebuffer[0], 0xFF, 0x40);
ftl_sparebuffer[0].meta.usn = ftl_cxt.usn; ftl_sparebuffer[0].meta.usn = ftl_cxt.usn;
ftl_sparebuffer[0].meta.type = 0x47; ftl_sparebuffer[0].meta.type = 0x47;
if (ftl_vfl_write(ftl_cxt.ftlctrlpage, ftl_buffer, if (ftl_vfl_write(ftl_cxt.ftlctrlpage, 1, ftl_buffer,
&ftl_sparebuffer[0]) == 0) &ftl_sparebuffer[0]) == 0)
break; break;
} }
@ -1915,24 +1899,24 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
} }
} }
ftl_cxt.nextblockusn++; ftl_cxt.nextblockusn++;
for (j = 0; j < ppb; j += ftl_banks) for (j = 0; j < ppb; j += FTL_WRITESPARE_SIZE)
{ {
memset(&ftl_sparebuffer[0], 0xFF, 0x100); memset(&ftl_sparebuffer[0], 0xFF, 0x40 * FTL_WRITESPARE_SIZE);
for (k = 0; k < ftl_banks; k++) for (k = 0; k < FTL_WRITESPARE_SIZE; k++)
{ {
ftl_sparebuffer[k].user.lpn = sector + i + j + k; ftl_sparebuffer[k].user.lpn = sector + i + j + k;
ftl_sparebuffer[k].user.usn = ftl_cxt.nextblockusn; ftl_sparebuffer[k].user.usn = ftl_cxt.nextblockusn;
ftl_sparebuffer[k].user.type = 0x40; ftl_sparebuffer[k].user.type = 0x40;
if (j == ppb - 1) ftl_sparebuffer[k].user.type = 0x41; if (j == ppb - 1) ftl_sparebuffer[k].user.type = 0x41;
} }
uint32_t rc = ftl_vfl_write_fast(vblock * ppb + j, uint32_t rc = ftl_vfl_write(vblock * ppb + j, FTL_WRITESPARE_SIZE,
&((uint8_t*)buffer)[(i + j) << 11], &((uint8_t*)buffer)[(i + j) << 11],
&ftl_sparebuffer[0]); &ftl_sparebuffer[0]);
if (rc) if (rc)
for (k = 0; k < ftl_banks; k++) for (k = 0; k < ftl_banks; k++)
if (rc & (1 << k)) if (rc & (1 << k))
{ {
while (ftl_vfl_write(vblock * ppb + j + k, while (ftl_vfl_write(vblock * ppb + j + k, 1,
&((uint8_t*)buffer)[(i + j + k) << 11], &((uint8_t*)buffer)[(i + j + k) << 11],
&ftl_sparebuffer[k])); &ftl_sparebuffer[k]));
} }
@ -1953,59 +1937,40 @@ uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
return 1; return 1;
} }
} }
if ((unsigned)((*logentry).pagesused + ftl_banks) <= ppb uint32_t cnt = FTL_WRITESPARE_SIZE;
&& i + ftl_banks <= count if (cnt > count - i) cnt = count - i;
&& !((*logentry).pagesused & (ftl_banks - 1)) if (cnt > ppb - (*logentry).pagesused) cnt = ppb - (*logentry).pagesused;
&& page + ftl_banks <= ppb) if (cnt > ppb - page) cnt = ppb - page;
memset(&ftl_sparebuffer[0], 0xFF, 0x40 * cnt);
for (j = 0; j < cnt; j++)
{ {
memset(&ftl_sparebuffer[0], 0xFF, 0x100); ftl_sparebuffer[j].user.lpn = sector + i + j;
for (j = 0; j < ftl_banks; j++) ftl_sparebuffer[j].user.usn = ++ftl_cxt.nextblockusn;
{ ftl_sparebuffer[j].user.type = 0x40;
ftl_sparebuffer[j].user.lpn = sector + i + j; if ((*logentry).pagesused + j == ppb - 1 && (*logentry).issequential)
ftl_sparebuffer[j].user.usn = ++ftl_cxt.nextblockusn; ftl_sparebuffer[j].user.type = 0x41;
ftl_sparebuffer[j].user.type = 0x40;
if ((*logentry).pagesused + j == ppb - 1 && (*logentry).issequential)
ftl_sparebuffer[j].user.type = 0x41;
}
uint32_t abspage = (*logentry).scatteredvblock * ppb
+ (*logentry).pagesused;
(*logentry).pagesused += ftl_banks;
if (ftl_vfl_write_fast(abspage, &((uint8_t*)buffer)[i << 11],
&ftl_sparebuffer[0]) == 0)
{
for (j = 0; j < ftl_banks; j++)
{
if ((*logentry).pageoffsets[page + j] == 0xFFFF)
(*logentry).pagescurrent++;
(*logentry).pageoffsets[page + j] = (*logentry).pagesused - ftl_banks + j;
if ((*logentry).pagesused - ftl_banks + j + 1 != (*logentry).pagescurrent
|| (*logentry).pageoffsets[page + j] != page + j)
(*logentry).issequential = 0;
}
i += ftl_banks;
}
} }
else uint32_t abspage = (*logentry).scatteredvblock * ppb
+ (*logentry).pagesused;
(*logentry).pagesused += cnt;
if (ftl_vfl_write(abspage, cnt, &((uint8_t*)buffer)[i << 11],
&ftl_sparebuffer[0]) == 0)
{ {
memset(&ftl_sparebuffer[0], 0xFF, 0x40); for (j = 0; j < cnt; j++)
ftl_sparebuffer[0].user.lpn = sector + i;
ftl_sparebuffer[0].user.usn = ++ftl_cxt.nextblockusn;
ftl_sparebuffer[0].user.type = 0x40;
if ((*logentry).pagesused == ppb - 1 && (*logentry).issequential)
ftl_sparebuffer[0].user.type = 0x41;
uint32_t abspage = (*logentry).scatteredvblock * ppb
+ (*logentry).pagesused++;
if (ftl_vfl_write(abspage, &((uint8_t*)buffer)[i << 11],
&ftl_sparebuffer[0]) == 0)
{ {
if ((*logentry).pageoffsets[page] == 0xFFFF) if ((*logentry).pageoffsets[page + j] == 0xFFFF)
(*logentry).pagescurrent++; (*logentry).pagescurrent++;
(*logentry).pageoffsets[page] = (*logentry).pagesused - 1; (*logentry).pageoffsets[page + j] = (*logentry).pagesused - cnt + j;
ftl_check_still_sequential(logentry, page); if ((*logentry).pagesused - cnt + j + 1 != (*logentry).pagescurrent
i++; || (*logentry).pageoffsets[page + j] != page + j)
(*logentry).issequential = 0;
} }
i += cnt;
} }
else panicf("FTL: Write error: %u %u %u!",
(unsigned)sector, (unsigned)count, (unsigned)i);
} }
if ((*logentry).pagesused == ppb) ftl_remove_scattered_block(logentry);
} }
if (ftl_cxt.swapcounter >= 300) if (ftl_cxt.swapcounter >= 300)
{ {

View file

@ -215,10 +215,9 @@ uint32_t nand_wait_status_ready(uint32_t bank)
return nand_send_cmd(NAND_CMD_READ); return nand_send_cmd(NAND_CMD_READ);
} }
uint32_t nand_transfer_data(uint32_t bank, uint32_t direction, void nand_transfer_data_start(uint32_t bank, uint32_t direction,
void* buffer, uint32_t size) void* buffer, uint32_t size)
{ {
long timeout = current_tick + HZ / 50;
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;
@ -232,6 +231,11 @@ uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
DMATCNT3 = (size >> 4) - 1; DMATCNT3 = (size >> 4) - 1;
clean_dcache(); clean_dcache();
DMACOM3 = 4; DMACOM3 = 4;
}
uint32_t nand_transfer_data_collect(uint32_t direction)
{
long timeout = current_tick + HZ / 50;
while ((DMAALLST & DMAALLST_DMABUSY3)) while ((DMAALLST & DMAALLST_DMABUSY3))
if (nand_timeout(timeout)) return 1; if (nand_timeout(timeout)) return 1;
if (!direction) invalidate_dcache(); if (!direction) invalidate_dcache();
@ -241,17 +245,29 @@ uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
return 0; return 0;
} }
uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer) uint32_t nand_transfer_data(uint32_t bank, uint32_t direction,
void* buffer, uint32_t size)
{
nand_transfer_data_start(bank, direction, buffer, size);
uint32_t rc = nand_transfer_data_collect(direction);
return rc;
}
void ecc_start(uint32_t size, void* databuffer, void* sparebuffer, uint32_t type)
{ {
mutex_lock(&ecc_mtx); mutex_lock(&ecc_mtx);
long timeout = current_tick + HZ / 50;
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;
clean_dcache(); clean_dcache();
ECC_CTRL = ECCCTRL_STARTDECODING; ECC_CTRL = type;
}
uint32_t ecc_collect(void)
{
long timeout = current_tick + HZ / 50;
while (!(SRCPND & INTMSK_ECC)) while (!(SRCPND & INTMSK_ECC))
if (nand_timeout(timeout)) return ecc_unlock(1); if (nand_timeout(timeout)) return ecc_unlock(1);
invalidate_dcache(); invalidate_dcache();
@ -260,23 +276,18 @@ uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer)
return ecc_unlock(ECC_RESULT); return ecc_unlock(ECC_RESULT);
} }
uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer)
{
ecc_start(size, databuffer, sparebuffer, ECCCTRL_STARTDECODING);
uint32_t rc = ecc_collect();
return rc;
}
uint32_t ecc_encode(uint32_t size, void* databuffer, void* sparebuffer) uint32_t ecc_encode(uint32_t size, void* databuffer, void* sparebuffer)
{ {
mutex_lock(&ecc_mtx); ecc_start(size, databuffer, sparebuffer, ECCCTRL_STARTENCODING);
long timeout = current_tick + HZ / 50; ecc_collect();
ECC_INT_CLR = 1; return 0;
SRCPND = INTMSK_ECC;
ECC_UNK1 = size;
ECC_DATA_PTR = (uint32_t)databuffer;
ECC_SPARE_PTR = (uint32_t)sparebuffer;
clean_dcache();
ECC_CTRL = ECCCTRL_STARTENCODING;
while (!(SRCPND & INTMSK_ECC))
if (nand_timeout(timeout)) return ecc_unlock(1);
invalidate_dcache();
ECC_INT_CLR = 1;
SRCPND = INTMSK_ECC;
return ecc_unlock(0);
} }
uint32_t nand_check_empty(uint8_t* buffer) uint32_t nand_check_empty(uint8_t* buffer)
@ -429,8 +440,8 @@ uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer,
return nand_unlock(rc); return nand_unlock(rc);
} }
uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer, uint32_t nand_write_page_int(uint32_t bank, uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc) void* sparebuffer, uint32_t doecc, uint32_t wait)
{ {
uint8_t* data = nand_data; uint8_t* data = nand_data;
uint8_t* spare = nand_spare[0]; uint8_t* spare = nand_spare[0];
@ -447,9 +458,14 @@ uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer,
if (spare != sparebuffer) memcpy(spare, sparebuffer, 0x40); if (spare != sparebuffer) memcpy(spare, sparebuffer, 0x40);
} }
else memset(spare, 0xFF, 0x40); else memset(spare, 0xFF, 0x40);
nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
if (nand_send_cmd(NAND_CMD_PROGRAM)) return nand_unlock(1);
if (nand_send_address(page, databuffer ? 0 : 0x800))
return nand_unlock(1);
if (databuffer && data != databuffer) memcpy(data, databuffer, 0x800);
if (databuffer) nand_transfer_data_start(bank, 1, data, 0x800);
if (doecc) if (doecc)
{ {
if (databuffer && data != databuffer) memcpy(data, databuffer, 0x800);
if (ecc_encode(3, data, nand_ecc)) return nand_unlock(1); if (ecc_encode(3, data, nand_ecc)) return nand_unlock(1);
memcpy(&spare[0xC], nand_ecc, 0x28); memcpy(&spare[0xC], nand_ecc, 0x28);
memset(nand_ctrl, 0xFF, 0x200); memset(nand_ctrl, 0xFF, 0x200);
@ -457,18 +473,15 @@ uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer,
if (ecc_encode(0, nand_ctrl, nand_ecc)) return nand_unlock(1); if (ecc_encode(0, nand_ctrl, nand_ecc)) return nand_unlock(1);
memcpy(&spare[0x34], nand_ecc, 0xC); memcpy(&spare[0x34], nand_ecc, 0xC);
} }
nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
if (nand_send_cmd(NAND_CMD_PROGRAM)) return nand_unlock(1);
if (nand_send_address(page, databuffer ? 0 : 0x800))
return nand_unlock(1);
if (databuffer) if (databuffer)
if (nand_transfer_data(bank, 1, data, 0x800)) if (nand_transfer_data_collect(1))
return nand_unlock(1); return nand_unlock(1);
if (sparebuffer || doecc) if (sparebuffer || doecc)
if (nand_transfer_data(bank, 1, spare, 0x40)) if (nand_transfer_data(bank, 1, spare, 0x40))
return nand_unlock(1); return nand_unlock(1);
if (nand_send_cmd(NAND_CMD_PROGCNFRM)) return nand_unlock(1); if (nand_send_cmd(NAND_CMD_PROGCNFRM)) return nand_unlock(1);
return nand_unlock(nand_wait_status_ready(bank)); if (wait) if (nand_wait_status_ready(bank)) return nand_unlock(1);
return nand_unlock(0);
} }
uint32_t nand_block_erase(uint32_t bank, uint32_t page) uint32_t nand_block_erase(uint32_t bank, uint32_t page)
@ -535,119 +548,132 @@ uint32_t nand_read_page_fast(uint32_t page, void* databuffer,
continue; continue;
} }
} }
for (i = 0; i < 4; i++) uint8_t status[4];
for (i = 0; i < 4; i++) status[i] = (nand_type[i] == 0xFFFFFFFF);
if (!status[0])
if (nand_wait_status_ready(0))
status[0] = 1;
if (!status[0])
if (nand_transfer_data(0, 0, databuffer, 0x800))
status[0] = 1;
if (!status[0])
if (nand_transfer_data(0, 0, sparebuffer, 0x40))
status[0] = 1;
for (i = 1; i < 4; i++)
{ {
if (nand_type[i] == 0xFFFFFFFF) continue; if (!status[i])
if (nand_wait_status_ready(i)) if (nand_wait_status_ready(i))
status[i] = 1;
if (!status[i])
nand_transfer_data_start(i, 0, (void*)((uint32_t)databuffer
+ 0x800 * i), 0x800);
if (!status[i - 1])
{ {
rc |= 1 << (i << 2); memcpy(nand_ecc, (void*)((uint32_t)sparebuffer + 0x40 * (i - 1) + 0xC), 0x28);
continue; ecc_start(3, (void*)((uint32_t)databuffer
+ 0x800 * (i - 1)), nand_ecc, ECCCTRL_STARTDECODING);
} }
if (nand_transfer_data(i, 0, (void*)((uint32_t)databuffer if (!status[i])
+ 0x800 * i), 0x800)) if (nand_transfer_data_collect(0))
status[i] = 1;
if (!status[i])
nand_transfer_data_start(i, 0, (void*)((uint32_t)sparebuffer
+ 0x40 * i), 0x40);
if (!status[i - 1])
if (ecc_collect() & 1)
status[i - 1] = 4;
if (!status[i - 1])
{ {
rc |= 1 << (i << 2); memset(nand_ctrl, 0xFF, 0x200);
continue; memcpy(nand_ctrl, (void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), 0xC);
memcpy(nand_ecc, (void*)((uint32_t)sparebuffer + 0x40 * (i - 1) + 0x34), 0xC);
ecc_start(0, nand_ctrl, nand_ecc, ECCCTRL_STARTDECODING);
} }
if (nand_transfer_data(i, 0, (void*)((uint32_t)sparebuffer if (!status[i])
+ 0x40 * i), 0x40)) if (nand_transfer_data_collect(0))
status[i] = 1;
if (!status[i - 1])
{ {
rc |= 1 << (i << 2); if (ecc_collect() & 1)
continue; {
status[i - 1] |= 8;
memset((void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), 0xFF, 0xC);
}
else memcpy((void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), nand_ctrl, 0xC);
if (checkempty)
status[i - 1] |= nand_check_empty((void*)((uint32_t)sparebuffer
+ 0x40 * (i - 1))) << 1;
} }
memcpy(nand_ecc, (void*)((uint32_t)sparebuffer + 0x40 * i + 0xC), 0x28);
if (ecc_decode(3, (void*)((uint32_t)databuffer + 0x800 * i), nand_ecc) & 1)
rc |= 4 << (i << 2);
memset(nand_ctrl, 0xFF, 0x200);
memcpy(nand_ctrl, (void*)((uint32_t)sparebuffer + 0x40 * i), 0xC);
memcpy(nand_ecc, (void*)((uint32_t)sparebuffer + 0x40 * i + 0x34), 0xC);
if (ecc_decode(0, nand_ctrl, nand_ecc))
{
rc |= 8 << (i << 2);
memset((void*)((uint32_t)sparebuffer + 0x40 * i), 0xFF, 0xC);
}
else memcpy((void*)((uint32_t)sparebuffer + 0x40 * i), nand_ctrl, 0xC);
if (checkempty)
rc |= nand_check_empty((void*)((uint32_t)sparebuffer
+ 0x40 * i)) << ((i << 2) + 1);
} }
if (!status[i - 1])
{
memcpy(nand_ecc,(void*)((uint32_t)sparebuffer + 0x40 * (i - 1) + 0xC), 0x28);
if (ecc_decode(3, (void*)((uint32_t)databuffer
+ 0x800 * (i - 1)), nand_ecc) & 1)
status[i - 1] = 4;
}
if (!status[i - 1])
{
memset(nand_ctrl, 0xFF, 0x200);
memcpy(nand_ctrl, (void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), 0xC);
memcpy(nand_ecc, (void*)((uint32_t)sparebuffer + 0x40 * (i - 1) + 0x34), 0xC);
if (ecc_decode(0, nand_ctrl, nand_ecc) & 1)
{
status[i - 1] |= 8;
memset((void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), 0xFF, 0xC);
}
else memcpy((void*)((uint32_t)sparebuffer + 0x40 * (i - 1)), nand_ctrl, 0xC);
if (checkempty)
status[i - 1] |= nand_check_empty((void*)((uint32_t)sparebuffer
+ 0x40 * (i - 1))) << 1;
}
for (i = 0; i < 4; i++)
if (nand_type[i] != 0xFFFFFFFF)
rc |= status[i] << (i << 2);
return nand_unlock(rc); return nand_unlock(rc);
} }
uint32_t nand_write_page_fast(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)
{
return nand_write_page_int(bank, page, databuffer, sparebuffer, doecc, 1);
}
uint32_t nand_write_page_start(uint32_t bank, uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc)
{ {
uint32_t i, rc = 0;
if (((uint32_t)databuffer & 0xf) || ((uint32_t)sparebuffer & 0xf) if (((uint32_t)databuffer & 0xf) || ((uint32_t)sparebuffer & 0xf)
|| !databuffer || !sparebuffer || !doecc) || !databuffer || !sparebuffer || !doecc)
{ return nand_write_page_int(bank, page, databuffer, sparebuffer, doecc, 0);
for (i = 0; i < 4; i++)
{
if (nand_type[i] == 0xFFFFFFFF) continue;
void* databuf = (void*)0;
void* sparebuf = (void*)0;
if (databuffer) databuf = (void*)((uint32_t)databuffer + 0x800 * i);
if (sparebuffer) sparebuf = (void*)((uint32_t)sparebuffer + 0x40 * i);
rc |= nand_write_page(i, page, databuf, sparebuf, doecc) << i;
}
return rc;
}
mutex_lock(&nand_mtx); mutex_lock(&nand_mtx);
nand_last_activity_value = current_tick; nand_last_activity_value = current_tick;
led(true); led(true);
if (!nand_powered) nand_power_up(); if (!nand_powered) nand_power_up();
for (i = 0; i < 4; i++) nand_set_fmctrl0(bank, FMCTRL0_ENABLEDMA);
{ if (nand_send_cmd(NAND_CMD_PROGRAM))
if (nand_type[i] == 0xFFFFFFFF) continue; return nand_unlock(1);
if (ecc_encode(3, (void*)((uint32_t)databuffer + 0x800 * i), nand_ecc)) if (nand_send_address(page, 0))
{ return nand_unlock(1);
rc |= 1 << i; nand_transfer_data_start(bank, 1, databuffer, 0x800);
continue; if (ecc_encode(3, databuffer, nand_ecc))
} return nand_unlock(1);
memcpy((void*)((uint32_t)sparebuffer + 0x40 * i + 0xC), nand_ecc, 0x28); memcpy((void*)((uint32_t)sparebuffer + 0xC), nand_ecc, 0x28);
memset(nand_ctrl, 0xFF, 0x200); memset(nand_ctrl, 0xFF, 0x200);
memcpy(nand_ctrl, (void*)((uint32_t)sparebuffer + 0x40 * i), 0xC); memcpy(nand_ctrl, sparebuffer, 0xC);
if (ecc_encode(0, nand_ctrl, nand_ecc)) if (ecc_encode(0, nand_ctrl, nand_ecc))
{ return nand_unlock(1);
rc |= 1 << i; memcpy((void*)((uint32_t)sparebuffer + 0x34), nand_ecc, 0xC);
continue; if (nand_transfer_data_collect(0))
} return nand_unlock(1);
memcpy((void*)((uint32_t)sparebuffer + 0x40 * i + 0x34), nand_ecc, 0xC); if (nand_transfer_data(bank, 1, sparebuffer, 0x40))
nand_set_fmctrl0(i, FMCTRL0_ENABLEDMA); return nand_unlock(1);
if (nand_send_cmd(NAND_CMD_PROGRAM)) return nand_unlock(nand_send_cmd(NAND_CMD_PROGCNFRM));
{ }
rc |= 1 << i;
continue; uint32_t nand_write_page_collect(uint32_t bank)
} {
if (nand_send_address(page, databuffer ? 0 : 0x800)) return nand_wait_status_ready(bank);
{
rc |= 1 << i;
continue;
}
if (nand_transfer_data(i, 1, (void*)((uint32_t)databuffer
+ 0x800 * i), 0x800))
{
rc |= 1 << i;
continue;
}
if (nand_transfer_data(i, 1, (void*)((uint32_t)sparebuffer
+ 0x40 * i), 0x40))
{
rc |= 1 << i;
continue;
}
if (nand_send_cmd(NAND_CMD_PROGCNFRM))
{
rc |= 1 << i;
continue;
}
}
for (i = 0; i < 4; i++)
{
if (nand_type[i] == 0xFFFFFFFF) continue;
rc |= nand_wait_status_ready(i) << i;
}
return nand_unlock(rc);
} }
uint32_t nand_block_erase_fast(uint32_t page) uint32_t nand_block_erase_fast(uint32_t page)

View file

@ -49,8 +49,9 @@ uint32_t nand_block_erase(uint32_t bank, uint32_t page);
uint32_t nand_read_page_fast(uint32_t page, void* databuffer, uint32_t nand_read_page_fast(uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc, void* sparebuffer, uint32_t doecc,
uint32_t checkempty); uint32_t checkempty);
uint32_t nand_write_page_fast(uint32_t page, void* databuffer, uint32_t nand_write_page_start(uint32_t bank, uint32_t page, void* databuffer,
void* sparebuffer, uint32_t doecc); void* sparebuffer, uint32_t doecc);
uint32_t nand_write_page_collect(uint32_t bank);
uint32_t nand_block_erase_fast(uint32_t page); uint32_t nand_block_erase_fast(uint32_t page);
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);

View file

@ -57,13 +57,21 @@
* optimal, except for sd devices that apparently don't gain anything from * optimal, except for sd devices that apparently don't gain anything from
* double-buffering * double-buffering
*/ */
#ifdef USB_READ_BUFFER_SIZE
#define READ_BUFFER_SIZE USB_READ_BUFFER_SIZE
#else
#define READ_BUFFER_SIZE (1024*64) #define READ_BUFFER_SIZE (1024*64)
#endif
#ifdef USB_WRITE_BUFFER_SIZE
#define WRITE_BUFFER_SIZE USB_WRITE_BUFFER_SIZE
#else
#if (CONFIG_STORAGE & STORAGE_SD) #if (CONFIG_STORAGE & STORAGE_SD)
#define WRITE_BUFFER_SIZE (1024*64) #define WRITE_BUFFER_SIZE (1024*64)
#else #else
#define WRITE_BUFFER_SIZE (1024*24) #define WRITE_BUFFER_SIZE (1024*24)
#endif #endif
#endif
#define ALLOCATE_BUFFER_SIZE (2*MAX(READ_BUFFER_SIZE,WRITE_BUFFER_SIZE)) #define ALLOCATE_BUFFER_SIZE (2*MAX(READ_BUFFER_SIZE,WRITE_BUFFER_SIZE))