mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-10 08:03:04 -04:00
rk27xx: let an FTL keep its own data in metadata byte 1
The flash layer writes 0x00 into metadata byte 1 of every page it programs, which is how Scheme A tells a programmed page from an erased one. Scheme B keeps a 16-bit field in bytes 0-1 of every sector - its block tags, versions and block numbers - so it needs the byte as written. Add flash_set_meta_passthrough() to turn the forcing off, and flash_copy_meta(), a copy that either keeps each sector's own metadata or programs a page of it given by the caller: Scheme B's copy stamps one header on every page it moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Change-Id: I49b2c24d812284af2c50d776f0942b5b1b989c98
This commit is contained in:
parent
0db035d231
commit
6bf3eaff41
2 changed files with 67 additions and 7 deletions
|
|
@ -76,6 +76,7 @@
|
|||
static struct flash_geometry geo;
|
||||
static bool ready;
|
||||
static bool writable;
|
||||
static bool meta_passthrough;
|
||||
static uint32_t boot_area = UINT32_MAX;
|
||||
static struct flash_stats stats;
|
||||
|
||||
|
|
@ -129,6 +130,11 @@ void flash_set_boot_area(uint32_t sectors)
|
|||
boot_area = sectors;
|
||||
}
|
||||
|
||||
void flash_set_meta_passthrough(bool on)
|
||||
{
|
||||
meta_passthrough = on;
|
||||
}
|
||||
|
||||
void flash_get_stats(struct flash_stats *out)
|
||||
{
|
||||
*out = stats;
|
||||
|
|
@ -393,7 +399,10 @@ static int prog_raw_run(uint32_t row, uint32_t first, uint32_t n,
|
|||
{
|
||||
memcpy(spare, meta + i * FLASH_META_SIZE, FLASH_META_SIZE);
|
||||
}
|
||||
if (!meta_passthrough)
|
||||
{
|
||||
spare[META_PROGRAMMED] = 0x00;
|
||||
}
|
||||
|
||||
/* a slot is reused every four sectors: its last transfer must be
|
||||
* done */
|
||||
|
|
@ -547,14 +556,24 @@ int flash_erase(uint32_t sec)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* What a copy programs as the destination's metadata */
|
||||
enum copy_meta
|
||||
{
|
||||
COPY_META_FRESH, /* as flash_program() with meta NULL */
|
||||
COPY_META_KEEP, /* each sector's own */
|
||||
COPY_META_PAGE, /* the caller's, by position in the page */
|
||||
};
|
||||
|
||||
/* Copy through the ECC engine, never with the chip's internal data move: on
|
||||
* this MLC part moved pages accumulate bit errors, and the data area's
|
||||
* t=8 is already below the chip's 12-bit minimum. A source sector that
|
||||
* fails ECC is copied as read, and counted. */
|
||||
int flash_copy(uint32_t src, uint32_t dst, unsigned n)
|
||||
static int copy_sectors(uint32_t src, uint32_t dst, unsigned n,
|
||||
enum copy_meta how, const uint8_t *page_meta)
|
||||
{
|
||||
static uint8_t buf[FLASH_MAX_SEC_PER_PAGE * FLASH_SECTOR_SIZE]
|
||||
__attribute__((aligned(4)));
|
||||
static uint8_t meta[FLASH_MAX_SEC_PER_PAGE * FLASH_META_SIZE];
|
||||
uint32_t k = 0;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -599,14 +618,30 @@ int flash_copy(uint32_t src, uint32_t dst, unsigned n)
|
|||
}
|
||||
|
||||
if (read_raw_run(raw / geo.sec_per_page_raw, first, run,
|
||||
buf + (size_t)i * FLASH_SECTOR_SIZE, NULL))
|
||||
buf + (size_t)i * FLASH_SECTOR_SIZE,
|
||||
how == COPY_META_KEEP ?
|
||||
meta + (size_t)i * FLASH_META_SIZE : NULL))
|
||||
{
|
||||
stats.copy_uncorrectable++;
|
||||
}
|
||||
i += run;
|
||||
}
|
||||
|
||||
if (ret == 0 && flash_program(dst + k, buf, NULL, len))
|
||||
if (how == COPY_META_PAGE)
|
||||
{
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
uint32_t at = (dst + k + i) % geo.sec_per_page;
|
||||
|
||||
memcpy(meta + i * FLASH_META_SIZE,
|
||||
page_meta + at * FLASH_META_SIZE,
|
||||
FLASH_META_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0
|
||||
&& flash_program(dst + k, buf,
|
||||
how == COPY_META_FRESH ? NULL : meta, len))
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
|
|
@ -615,3 +650,16 @@ int flash_copy(uint32_t src, uint32_t dst, unsigned n)
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int flash_copy(uint32_t src, uint32_t dst, unsigned n)
|
||||
{
|
||||
return copy_sectors(src, dst, n, COPY_META_FRESH, NULL);
|
||||
}
|
||||
|
||||
int flash_copy_meta(uint32_t src, uint32_t dst, unsigned n,
|
||||
const void *page_meta)
|
||||
{
|
||||
return copy_sectors(src, dst, n,
|
||||
page_meta ? COPY_META_PAGE : COPY_META_KEEP,
|
||||
page_meta);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@
|
|||
* METADATA: every sector carries 16 spare bytes. The last 13 are the BCH
|
||||
* code and belong to the hardware; the first three are the FTL's, and are
|
||||
* what the meta arguments below carry - FLASH_META_SIZE bytes per sector.
|
||||
* Byte 1 is special: this layer writes 0x00 there on every program, so that
|
||||
* the FTL can tell a programmed page from an erased one whatever it wrote.
|
||||
* Byte 1 is special: by default this layer writes 0x00 there on every
|
||||
* program, so that the FTL can tell a programmed page from an erased one
|
||||
* whatever it wrote. An FTL that keeps its own data in byte 1 turns that off
|
||||
* with flash_set_meta_passthrough().
|
||||
*
|
||||
* Only the first chip is supported; every device the FTL has been validated
|
||||
* on has one. */
|
||||
|
|
@ -86,7 +88,8 @@ const struct flash_geometry *flash_get_geometry(void);
|
|||
int flash_read(uint32_t sec, void *data, void *meta, unsigned n);
|
||||
|
||||
/* Program n sectors from sec, which must be erased. data NULL programs
|
||||
* 0xff; meta NULL programs {0xff, 0x00, 0xff}. Returns 0 or 1 on failure. */
|
||||
* 0xff; meta NULL programs {0xff, 0x00, 0xff} - {0xff, 0xff, 0xff} with
|
||||
* passthrough on. Returns 0 or 1 on failure. */
|
||||
int flash_program(uint32_t sec, const void *data, const void *meta, unsigned n);
|
||||
|
||||
/* Program the whole page (every plane) that contains sec. */
|
||||
|
|
@ -100,6 +103,15 @@ int flash_erase(uint32_t sec);
|
|||
* gets fresh metadata {0xff, 0x00, 0xff}. */
|
||||
int flash_copy(uint32_t src, uint32_t dst, unsigned n);
|
||||
|
||||
/* As flash_copy(), but with chosen metadata: page_meta NULL keeps each
|
||||
* sector's own; otherwise sector k of every destination page is programmed
|
||||
* with page_meta[k] (sec_per_page * FLASH_META_SIZE bytes). */
|
||||
int flash_copy_meta(uint32_t src, uint32_t dst, unsigned n,
|
||||
const void *page_meta);
|
||||
|
||||
/* Program metadata byte 1 as given rather than 0x00. */
|
||||
void flash_set_meta_passthrough(bool on);
|
||||
|
||||
/* Read one PHYSICAL sector, bypassing the super-block view: for the boot
|
||||
* area, which the boot ROM addresses physically. */
|
||||
int flash_read_raw(uint32_t raw_sec, void *data, void *meta);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue