rockbox/firmware/target/arm/s5l8702/ipodnano3g/nand-nano3g.c
Andrew Rice bd24fddb7e ipodnano3g: mark the 4-CE B614D5EC row checked
A contributor's check archive matches the row exactly and passes
test_crash clean. test_ftl disagrees with the oracle on two logical
pages in one block - a second, distinct false positive from the
A5D5D589 x2 case: a closed data block addressed purely by position,
with one stale leftover page. Confirmed against the decode notes
(_FTLRestore's "closed blocks -> map" step) and documented in
test_ftl.c alongside the existing false positive.

Testing evidence: utils/ipodnano3g/RESULTS.md.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: Ib24d2df18e2b0e60e000ee6ea43bef9c214f7f72
2026-09-21 09:16:33 -04:00

1214 lines
38 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2009 by Michael Sparmann
* Copyright (C) 2026 by Andrew Rice
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* NAND driver for the iPod Nano 3G (S5L8702 FMC controller).
*
* The register sequences were worked out from the BootROM and the retail
* firmware of the owner's own player: resetting a bank, waiting for it to
* go ready, and reading a page with its per-chunk ECC. nand_identify()
* follows wInd3x (freemyipod/wInd3x, pkg/exploit/wind3x_n3g.go).
*
* Limitations:
* - Only the first FMC controller is used. Banks (chip enables) 0 to
* NAND_MAX_BANKS-1 are probed at init; banks above 0 are reachable only
* because gpio_preinit() muxes all of PCON(9), which the BootROM does not.
* - Only chips validated on hardware are driven; nand_init() refuses any
* other chip (see nand_chip_table).
* - Storage goes through Apple's FTL (ftl-nano3g.c).
*
* A build with -DNAND_CHECK is the contributor check image: it serves the
* raw NAND read-only instead of the FTL's disk, so the storage API below is
* left to nand-check-nano3g.c and this file keeps only the chip.
*/
#include "config.h"
#include "cpu.h"
#include "system.h"
#include "kernel.h"
#include "mv.h"
#include "storage.h"
#include "clocking-s5l8702.h"
#include "logf.h"
#include "panic.h"
#include "nand-target.h"
#include "ftl-target.h"
#include <cpucache-arm.h>
#include <stdbool.h>
#define NAND_CMD_READ 0x00
#define NAND_CMD_READ2 0x30
#define NAND_CMD_READSTATUS 0x70
#define NAND_CMD_READID 0x90
#define NAND_CMD_RESET 0xFF
#define NAND_CMD_PROGRAM 0x80
#define NAND_CMD_PROGRAM_PLANE1 0x81 /* plane 1 of a two-plane program */
#define NAND_CMD_PROGRAM_QUEUE 0x11 /* plane 0 queued, plane 1 to come */
#define NAND_CMD_PROGRAM2 0x10
#define NAND_CMD_ERASE 0x60
#define NAND_CMD_ERASE2 0xD0
/* READ STATUS result */
#define NAND_STATUS_FAIL (1 << 0)
#define NAND_STATUS_READY (1 << 6)
/* The controller moves a page in units of 2KiB, each of four 512-byte
* chunks: one unit on 2KiB-page chips, two on 4KiB-page chips */
#define NAND_UNIT_SIZE 2048
#define NAND_CHUNK_SIZE 512
/* Bus timing: Apple's values, giving FMCTRL0 = 0x43803 for bank 0. Every
* legal combination reads the ID correctly, so these are not critical. */
#define NAND_TUNK1 4 /* 3-bit field at bit 16 */
#define NAND_TWP 3 /* 3-bit field at bit 12 */
/* Apple's poll helper (0x20000e50) gives up after 100000 reads. */
#define NAND_POLL_SPINS 1000000
struct nand_chip_info
{
uint32_t id; /* first four ID bytes, little endian */
uint8_t banks; /* chip enables holding this part */
uint8_t mode; /* Apple's layout and programming mode */
uint16_t blocks; /* per chip enable */
uint16_t pagesperblock;
uint16_t pagesize; /* bytes of data per page */
uint16_t userblocks; /* per chip enable, as Whimory counts them */
bool validated; /* read, written and remounted on hardware */
};
/* Apple's own chip table in the Nano 3G firmware (osos 1.1.3: 18 rows of 44
* bytes holding the id, chip enables, blocks per enable, pages per block,
* 512-byte sectors per page, spare bytes, timings, user blocks and mode).
* Apple picks the row matching both the id and the number of chip enables
* answering with it, and so does nand_init(): the same part can come in
* a different mode with a different bank count.
*
* The mode decides how the VFL groups physical blocks (nand_mode_planes()
* and nand_mode_layout()) and which program writes two planes at once. Only
* validated rows are driven; nand_init() refuses any other chip, except in
* the check image, which reads a chip without mounting it writable. */
static const struct nand_chip_info nand_chip_table[] =
{
/* Hynix: the 4GB unit this port was developed on, and the 8GB one */
{ 0xA514D3AD, 4, 8, 4096, 128, 2048, 3872, true },
{ 0xA555D5AD, 4, 8, 8192, 128, 2048, 7744, true },
/* Micron, 2 chip enables */
{ 0xA5D5D52C, 2, 4, 8192, 128, 2048, 7744, true },
#ifdef NAND_CHECK
/* The rest of the chips the original firmware knows. They are here for
* the check image alone, which identifies a chip and reads it to
* collect what validating it needs; a normal build does not carry them,
* so Rockbox cannot drive a chip nobody has tested. A row moves above
* this line, with its validated flag set, once someone has run the
* check and a write test on that chip. A row tagged "reported" is
* secondhand - someone's id and geometry, unconfirmed by us; one
* tagged "checked" has a check archive that mounts and replays
* correctly in the host FTL suite - real evidence, short only of the
* write test. */
/* Micronas (ITT Intermetall, acquired by TDK 2016): 0xEC, not Samsung */
{ 0xB614D5EC, 2, 8, 4096, 128, 4096, 3872, false }, /* checked */
{ 0xB614D5EC, 4, 1, 4096, 128, 4096, 3872, false }, /* checked */
{ 0x2555D5EC, 4, 9, 8192, 128, 2048, 7744, false },
/* Hynix */
{ 0xB614D5AD, 4, 1, 4096, 128, 4096, 3872, false },
/* Toshiba */
{ 0xA585D598, 2, 13, 8320, 128, 2048, 7744, false },
{ 0xA585D598, 4, 13, 8320, 128, 2048, 7744, false },
{ 0xBA94D598, 2, 12, 4096, 128, 4096, 3872, false },
{ 0xBA94D598, 4, 1, 4096, 128, 4096, 3872, false }, /* checked */
/* Intel */
{ 0xA5D5D589, 2, 4, 8192, 128, 2048, 7744, false }, /* checked */
{ 0xA5D5D589, 4, 2, 8192, 128, 2048, 7744, false }, /* checked */
{ 0x3E94D589, 2, 3, 4096, 128, 4096, 3872, false }, /* checked */
{ 0x3ED5D789, 2, 2, 8192, 128, 4096, 7744, false },
/* Micron */
{ 0xA5D5D52C, 4, 2, 8192, 128, 2048, 7744, false },
{ 0x3E94D52C, 2, 3, 4096, 128, 4096, 3872, false },
{ 0x3ED5D72C, 2, 2, 8192, 128, 4096, 7744, false },
#endif
};
/* Physical blocks per bank in one VFL block: the original firmware's mode
* switch uses 1 for mode 1, 4 for mode 4, and 2 for every other mode */
static unsigned int nand_mode_planes(unsigned int mode)
{
return mode == 1 ? 1 : mode == 4 ? 4 : 2;
}
/* How the planes of a VFL block are placed: a partner in the next block,
* the other half of the chip, or both */
static unsigned int nand_mode_layout(unsigned int mode)
{
switch (mode)
{
case 3: case 8: return NAND_LAYOUT_ADJACENT;
case 2: case 9: case 12: return NAND_LAYOUT_HALVES;
case 4: return NAND_LAYOUT_BOTH;
case 1: return NAND_LAYOUT_SINGLE;
case 13: return NAND_LAYOUT_SPLIT13;
default: return NAND_LAYOUT_UNKNOWN;
}
}
static const struct nand_chip_info *nand_chip;
static uint32_t nand_id; /* what bank 0 answered READ ID with */
static unsigned int nand_banks;
static bool nand_ready = false;
static long nand_last_activity_value = -1;
static struct nand_geometry nand_geo;
const struct nand_geometry *nand_get_geometry(void)
{
if (!nand_ready)
return NULL;
nand_geo.banks = nand_banks;
nand_geo.blocks = nand_chip->blocks;
nand_geo.pagesperblock = nand_chip->pagesperblock;
nand_geo.planes = nand_mode_planes(nand_chip->mode);
nand_geo.userblocks = nand_chip->userblocks;
/* Whimory reserves each bank's blocks beyond its user blocks, less 23
* superblocks: 3 FTL control blocks and a pool of 20. 89 was measured
* on the 4GB unit; the rest follow the same rule. */
nand_geo.vflspares = (nand_chip->blocks - nand_chip->userblocks)
/ nand_geo.planes - 23;
/* Mode 8 is the mode whose two-plane program nand_write_pages()
* reproduces */
nand_geo.twoplane = nand_chip->mode == 8;
nand_geo.pagesize = nand_chip->pagesize;
nand_geo.mode = nand_chip->mode;
nand_geo.layout = nand_mode_layout(nand_chip->mode);
nand_geo.validated = nand_chip->validated;
#ifdef NAND_WRITABLE_ID
/* Validation builds for testers: mount chips with this id writable,
* whatever their bank count, e.g. -DNAND_WRITABLE_ID=0xA555D5AD */
if (nand_chip->id == NAND_WRITABLE_ID)
nand_geo.validated = true;
#endif
#ifdef NAND_TEST_READONLY
/* Test builds: behave as on a chip that is not validated */
nand_geo.validated = false;
#endif
return &nand_geo;
}
/* ECC state gathered over the four chunks of a page, as the firmware's
* page read does */
struct nand_ecc_acc
{
bool flagged; /* FMCSTAT_UNK27 was set on some chunk */
uint32_t unk810; /* FMUNK810 after each correction, ORed */
uint32_t transstat; /* FMTRANSSTAT after each correction, ORed */
};
static void nand_touch(void)
{
nand_last_activity_value = current_tick;
}
static void nand_set_fmctrl0(uint32_t bank)
{
FMCTRL0 = (NAND_TUNK1 << 16) | (NAND_TWP << 12) | FMCTRL0_UNK1
| 1 | FMCTRL0_CE(bank);
}
/* Spin until bit is set in reg, then write 1 to clear it. Tight and
* bounded, like Apple's poll helper: some of these flags assert briefly. */
static int nand_wait_reg(volatile uint32_t *reg, uint32_t bit)
{
unsigned long spins = NAND_POLL_SPINS;
while (spins--)
{
if (*reg & bit)
{
*reg = bit;
return 0;
}
}
return -1;
}
static int nand_wait_stat(uint32_t bit)
{
return nand_wait_reg(&FMCSTAT, bit);
}
/* As nand_wait_stat(), but leaves the bit set */
static int nand_wait_stat_noclear(uint32_t bit)
{
unsigned long spins = NAND_POLL_SPINS;
while (spins--)
if (FMCSTAT & bit)
return 0;
return -1;
}
/* Clear every FMCSTAT bit. Needed after nand_identify() and after each page
* read: stale bits let the stage waits in nand_transfer_chunk() pass before
* the data has moved, which corrupts the next read. The original
* firmware's page read ends the same way. */
static void nand_clear_status(void)
{
FMCSTAT = 0xffffffff;
}
static int nand_send_cmd(uint32_t cmd)
{
FMCMD = cmd;
return nand_wait_stat(FMCSTAT_CMDDONE);
}
static int nand_send_addr_byte(uint32_t addr)
{
FMANUM = 0;
FMADDR0 = addr;
FMCTRL1 = FMCTRL1_DOTRANSADDR;
return nand_wait_stat(FMCSTAT_ADDRDONE);
}
static int nand_send_addr_page(uint32_t page)
{
FMANUM = 4;
FMADDR0 = page << 16;
FMADDR1 = (page >> 16) & 0xFF;
FMCTRL1 = FMCTRL1_DOTRANSADDR;
return nand_wait_stat(FMCSTAT_ADDRDONE);
}
/* Switch the NAND pins to their NAND function: Apple's 0x20001830 with
* argument 0 (first controller). It leaves the other PCON(9) pins alone. */
static void nand_gpio_config(void)
{
PCON(10) = (PCON(10) & 0xFFFF0000) | 0x00002222;
PCON(9) = (PCON(9) & ~0x000F000F) | 0x00020002;
PCON(8) = 0x22222222;
}
/* Wait for the chip to go ready, as the original firmware does after every
* operation: READ STATUS, then FMCTRL1 = 0xca has the controller read
* the status byte into FMSYND0. Apple writes FMADDR2 = 1 whatever the bank.
* Returns the status byte, or -1 on a timeout. It leaves the chip in status
* output mode, so a reader must re-issue READ. */
static int nand_read_status(void)
{
/* The microsecond timer, not current_tick: this runs at nand_init()
* and must be bounded with interrupts off too */
unsigned long timeout = USEC_TIMER + 200000;
unsigned long spins;
uint32_t status = 0;
FMADDR2 = 1;
if (nand_send_cmd(NAND_CMD_READSTATUS))
return -1;
do
{
FMCTRL1 = 0xca;
for (spins = 10000; spins; spins--)
{
status = FMSYND0;
if (status & NAND_STATUS_READY)
break;
}
} while (!(status & NAND_STATUS_READY)
&& TIME_BEFORE(USEC_TIMER, timeout));
if (!(status & NAND_STATUS_READY))
return -1;
FMCSTAT = FMCSTAT_STATUSREADY;
FMCTRL1 = 0x20;
return status & 0xff;
}
/* Wait for a read or reset to finish. This used to follow Apple's ROM
* (0x20009614): READ STATUS, FMCTRL1 = 0x2a, then wait for FMCSTAT bit 23.
* That bit does set (128 reads of 128, measured), but nothing in it says
* the chip is ready; nand_read_status() checks the ready bit, as the retail
* firmware does. Both take ~300 us per page read. */
static int nand_wait_ready(void)
{
int status = nand_read_status();
FMADDR2 = 0;
FMCTRL1 = 0xe0;
return status < 0 ? NAND_ERR_READY_CMD : 0;
}
int nand_reset(uint32_t bank)
{
clockgate_enable(CLOCKGATE_NAND, true);
clockgate_enable(CLOCKGATE_NANDECC, true);
nand_gpio_config();
nand_set_fmctrl0(bank);
if (nand_send_cmd(NAND_CMD_RESET))
return NAND_ERR_RESET_CMD;
/* Wait out tRST as well: the old ready wait could not tell when the
* chip had left reset. */
udelay(5000);
return nand_wait_ready();
}
/* Read the first four ID bytes of the bank selected by the last
* nand_reset(), following wInd3x. ext, if not NULL, gets
* the next four of the eight the controller is asked for. */
int nand_identify(uint32_t *id, uint32_t *ext)
{
if (nand_send_cmd(NAND_CMD_READID))
return -1;
if (nand_send_addr_byte(0))
return -1;
FMDNUM = 7;
FMADDR2 = 1;
FMCSTAT = FMCSTAT_TRANSDONE; /* nand_wait_ready() leaves it set */
FMCTRL1 = FMCTRL1_DOREADDATA | FMCTRL1_CLEARWFIFO | FMCTRL1_CLEARRFIFO
| (1 << 8);
/* wInd3x leaves TRANSDONE set here; nand_clear_status() clears it */
if (nand_wait_stat_noclear(FMCSTAT_TRANSDONE))
return -1;
FMADDR2 = 0x100;
FMCTRL1 = FMCTRL1_CLEARWFIFO | (1 << 8) | (1 << 9);
*id = FMFIFO;
if (ext)
*ext = FMFIFO;
nand_clear_status();
return 0;
}
/* Kick the controller's correction of the chunk just read and wait for it.
* The register writes Apple's 0x2000ac1c makes, in the same order. */
static int nand_kick_chunk(uint32_t half, uint32_t sub, uint32_t upper)
{
FMTRANSSTAT = 0x7f;
FMTRANS1 = 0x01000180;
FMTRANS0 = (1 << (sub + 8)) | (upper << 16) | (1 << (half + 8)) | 1;
return nand_wait_reg(&FMTRANSSTAT, 1 << 2);
}
/* Move one 512-byte quarter (chunk 0..3) of the current page to dst, as
* the firmware's page read does. Stages A and B read the chunk into the
* controller,
* which sets FMCSTAT_UNK27 if it finds ECC errors and corrects them when
* kicked. Stage C then has the controller write the chunk to dst. */
static int nand_transfer_chunk(uint32_t chunk, void *dst,
struct nand_ecc_acc *ecc)
{
uint32_t half = chunk & 1;
uint32_t upper = (chunk >> 1) & 1;
uint32_t sub = half + 4;
/* Stage A: 16 units */
FMADDR6 = upper ? 0x10 : 0;
FMADDR2 = 1 << sub;
FMDNUM = 0xf;
FMCTRL1 = 0x32;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
/* Stage B: 512 units */
FMDNUM = 0x1ff;
FMADDR2 = 1 << half;
FMADDR6 = 0;
FMCTRL1 = 0x22;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
if (FMCSTAT & FMCSTAT_UNK27)
{
if (nand_kick_chunk(half, sub, upper))
return -1;
ecc->flagged = true;
ecc->unk810 |= FMUNK810;
ecc->transstat |= FMTRANSSTAT;
}
/* Stage C: the controller writes dst behind the cache's back */
commit_discard_dcache_range(dst, NAND_CHUNK_SIZE);
FMDATAW0 = (uint32_t)(intptr_t)dst;
FMDATAW1 = 7;
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_ENABLEDMA) | FMCTRL0_AUTOXFER | 1;
FMADDR2 = 1 << (half + 8);
FMCTRL1 = 0x1a0;
if (nand_wait_stat(FMCSTAT_UNK20))
return -1;
discard_dcache_range(dst, NAND_CHUNK_SIZE);
return 0;
}
int nand_read_page(uint32_t bank, uint32_t page, void *databuf,
uint32_t *meta)
{
struct nand_ecc_acc ecc = { false, 0, 0 };
unsigned long spins;
uint32_t chunk;
/* That sequence only knows 2KiB pages. Larger pages take the firmware's
* read sequence, which loops over the page's units. */
if (nand_chip->pagesize != NAND_UNIT_SIZE)
{
struct nand_read r = { bank, page, databuf, meta, 0, 0 };
return nand_read_pages(&r, 1) ? -1 : r.ecc;
}
nand_set_fmctrl0(bank);
if (nand_send_cmd(NAND_CMD_READ))
return -1;
if (nand_send_addr_page(page))
return -1;
if (nand_send_cmd(NAND_CMD_READ2))
return -1;
if (nand_wait_ready())
return -1;
if (nand_send_cmd(NAND_CMD_READ)) /* back to data output */
return -1;
for (chunk = 0; chunk < 4; chunk++)
{
FMCSTAT = FMCSTAT_UNK27;
if (nand_transfer_chunk(chunk,
(uint8_t *)databuf + chunk * NAND_CHUNK_SIZE, &ecc))
return -1;
}
/* Have the controller decode the spare metadata, as the firmware does
* after its chunk loop. The result appears in FMSYND5..7. */
FMUNK78 = 0x5140;
FMUNK7C = 2;
spins = NAND_POLL_SPINS;
while (FMUNK7C & 2)
if (!--spins)
return -1;
if (meta)
{
meta[0] = FMSYND5;
meta[1] = FMSYND6;
meta[2] = FMSYND7;
}
nand_clear_status();
/* The same classification the firmware's page read returns */
if (!ecc.flagged)
return NAND_ECC_CLEAN;
if (ecc.unk810 & 1)
return NAND_ECC_FAILED;
if (ecc.transstat & 0x20)
return NAND_ECC_STATUS6;
return NAND_ECC_CORRECTED;
}
/* Add one chunk's correction result to the bitmap used by Apple's firmware
* read program: bit 30 is uncorrectable, and low bit n-1 says an n-bit
* correction occurred. */
static void nand_read_ecc_result(uint32_t *result)
{
uint32_t ecc = FMUNK810;
if (ecc & 1)
*result |= 1u << 30;
else
{
uint32_t count = (ecc >> 16) & 0xf;
if (count)
*result |= 1u << (count - 1);
}
}
/* Transfer one page after its NAND page load has completed. This is the
* data and ECC path of the firmware's read program: four chunks per
* 2KiB unit, the last chunk of every unit but the last going out as the
* next unit's buffer is loaded, and the page's final chunk and erased-page
* check once at the end. */
static int nand_read_loaded_page(struct nand_read *r, struct nand_read *next,
bool primed, uint32_t *result)
{
static const uint32_t bufsel[3] = { 0x102, 0x201, 0x102 };
static const uint32_t correct[4] = { 0x1101, 0x11201, 0x21101, 0x31201 };
uint32_t units = nand_chip->pagesize / NAND_UNIT_SIZE;
uint32_t unit, chunk, stat;
unsigned long spins;
if (!primed)
{
FMCTRL1 = 0x100e0;
if (nand_send_cmd(NAND_CMD_READ))
return -1;
/* Prime the first 16-byte and 512-byte controller stages. */
FMDNUM = 0xf;
FMADDR2 = 0x10;
FMADDR6 = 0;
FMCTRL1 = 0x32;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
FMDNUM = 0x1ff;
FMADDR2 = 1;
FMCTRL1 = 0xe2;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
FMCTRL0 |= FMCTRL0_AUTOXFER;
FMDATAW0 = (uint32_t)(intptr_t)r->buf;
}
for (unit = 0; unit < units; unit++)
{
if (unit)
{
/* The previous unit's last chunk, and this unit's buffer */
FMDNUM = 0xf;
FMADDR2 = 0x10;
FMADDR6 = 0;
FMCTRL1 = 0x32;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
FMADDR2 = 0x201;
FMDNUM = 0x1ff;
FMCTRL1 = 0xe2;
FMTRANSSTAT = 0x1ff;
FMTRANS1 = 0x180;
FMTRANS0 = correct[3];
if (nand_wait_reg(&FMTRANSSTAT, 1))
return -1;
nand_read_ecc_result(result);
FMCTRL1 = 0x100;
if (nand_wait_stat(FMCSTAT_UNK20))
return -1;
FMDATAW0 = (uint32_t)(intptr_t)r->buf + unit * NAND_UNIT_SIZE;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
}
for (chunk = 0; chunk < 3; chunk++)
{
FMDNUM = 0xf;
FMADDR2 = 0x10;
FMADDR6 = (chunk + 1) << 4;
FMCTRL1 = 0x32;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
FMADDR2 = bufsel[chunk];
FMDNUM = 0x1ff;
FMCTRL1 = 0xe2;
FMTRANSSTAT = 0x1ff;
FMTRANS1 = 0x180;
FMTRANS0 = correct[chunk];
if (nand_wait_reg(&FMTRANSSTAT, 1))
return -1;
nand_read_ecc_result(result);
FMCTRL1 = 0x100;
if (nand_wait_stat(FMCSTAT_UNK20)
|| nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
}
}
stat = FMCSTAT & FMCSTAT_UNK27;
FMCSTAT = FMCSTAT_UNK27;
if (!stat)
*result |= 1u << 29;
FMTRANSSTAT = 0x1ff;
FMTRANS1 = 0x180;
FMTRANS0 = correct[3];
if (next)
{
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK) | FMCTRL0_CE(next->bank);
FMADDR2 = 1;
if (nand_send_cmd(NAND_CMD_READSTATUS))
return -1;
FMCTRL1 = 0xca;
if (nand_wait_stat(FMCSTAT_STATUSREADY))
return -1;
FMCTRL1 = 0x20;
}
if (nand_wait_reg(&FMTRANSSTAT, 1))
return -1;
nand_read_ecc_result(result);
if (!next)
{
FMDATAW1 = 7;
FMADDR2 = 0x200;
FMDNUM = 0x1ff;
FMCTRL1 = 0x1e0;
if (nand_wait_stat(FMCSTAT_UNK20))
return -1;
}
FMUNK78 = 0x3210;
FMUNK7C = 2;
spins = NAND_POLL_SPINS;
while (FMUNK7C & 2)
if (!--spins)
return -1;
if (r->meta)
{
r->meta[0] = FMSYND5;
r->meta[1] = FMSYND6;
r->meta[2] = FMSYND7;
}
if (next)
{
FMCTRL1 = 0x100e0;
if (nand_send_cmd(NAND_CMD_READ))
return -1;
FMDNUM = 0xf;
FMADDR2 = 0x10;
FMADDR6 = 0;
FMCTRL1 = 0x32;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
FMADDR2 = 0x201;
FMDNUM = 0x1ff;
FMCTRL1 = 0xe2;
FMCTRL1 = 0x100;
if (nand_wait_stat(FMCSTAT_UNK20))
return -1;
FMDATAW0 = (uint32_t)(intptr_t)next->buf;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
return -1;
}
return 0;
}
int nand_read_pages(struct nand_read *r, unsigned int n)
{
unsigned int first, end, i;
uint32_t used;
int status;
if (!nand_ready || !n)
return -1;
for (i = 0; i < n; i++)
{
if (r[i].bank >= nand_banks
|| r[i].page >= (uint32_t)nand_chip->blocks
* nand_chip->pagesperblock)
return -1;
commit_discard_dcache_range(r[i].buf, nand_chip->pagesize);
r[i].ecc = NAND_ECC_FAILED;
}
FMCTRL1 = 0x0ff3f8e0;
FMCSTAT = 0x0ff00ffe;
FMCTRL0 = (NAND_TUNK1 << 16) | (NAND_TWP << 12) | FMCTRL0_UNK1 | 1;
/* The FMSS program consumes the largest prefix containing no repeated
* bank, starts every page load in it, transfers those pages, then repeats
* with the remainder. */
for (first = 0; first < n; first = end)
{
used = 0;
for (end = first; end < n && !(used & (1u << r[end].bank)); end++)
used |= 1u << r[end].bank;
for (i = first; i < end; i++)
{
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK) | FMCTRL0_CE(r[i].bank);
if (nand_send_cmd(NAND_CMD_READ)
|| nand_send_addr_page(r[i].page)
|| nand_send_cmd(NAND_CMD_READ2))
goto fail;
}
for (i = first; i < end; i++)
{
uint32_t result = 0;
if (i == first)
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK)
| FMCTRL0_CE(r[i].bank);
status = i == first ? nand_read_status() : NAND_STATUS_READY;
if (status < 0
|| nand_read_loaded_page(&r[i], i + 1 < end ? &r[i + 1] : NULL,
i != first, &result))
goto fail;
r[i].result = result;
r[i].ecc = (result & (1u << 30)) ? NAND_ECC_FAILED
: (result & 0xff) ? NAND_ECC_CORRECTED
: NAND_ECC_CLEAN;
discard_dcache_range(r[i].buf, nand_chip->pagesize);
}
FMCTRL0 &= ~FMCTRL0_AUTOXFER;
FMCTRL0 &= ~FMCTRL0_CE_MASK;
}
return 0;
fail:
FMCTRL0 &= ~FMCTRL0_AUTOXFER;
nand_clear_status();
return -1;
}
/*
* Programming and erasing. The original firmware does these with programs
* that the FMC's sequencer runs in hardware; this driver makes the register
* writes those programs do for a single page or block from the ARM. Tested
* on hardware: a page written, read back and erased again.
*/
static int nand_finish(int status)
{
nand_clear_status();
if (status < 0)
return -1;
return (status & NAND_STATUS_FAIL) ? NAND_OP_FAILED : 0;
}
int nand_erase_block(uint32_t bank, uint32_t block)
{
if (!nand_ready || bank >= nand_banks || block >= nand_chip->blocks)
return -1;
FMCTRL1 = 0x0ff3f8e0;
FMCSTAT = 0x0ff00ffe;
nand_set_fmctrl0(bank);
if (nand_send_cmd(NAND_CMD_ERASE))
return nand_finish(-1);
FMANUM = 2; /* row address only */
FMADDR0 = block * nand_chip->pagesperblock;
FMCTRL1 = FMCTRL1_DOTRANSADDR;
if (nand_wait_stat(FMCSTAT_ADDRDONE))
return nand_finish(-1);
if (nand_send_cmd(NAND_CMD_ERASE2))
return nand_finish(-1);
return nand_finish(nand_read_status());
}
/* Load a page's first chunk into the controller and encode its spare
* metadata. The rest of the page follows chunk by chunk as it goes out. */
static int nand_load_spare(const uint32_t *meta)
{
unsigned long spins = NAND_POLL_SPINS;
FMSYND5 = meta[0];
FMSYND6 = meta[1];
FMSYND7 = meta[2];
FMUNK78 = 0x3210;
FMUNK7C = 1;
while (FMUNK7C & 1)
if (!--spins)
return -1;
return nand_wait_stat(FMCSTAT_UNK20);
}
/* Start the ECC engine on a chunk */
static void nand_encode(uint32_t cmd)
{
FMTRANSSTAT = 0x1ff;
FMTRANS1 = 0x180;
FMTRANS0 = cmd;
}
/* READ STATUS on a bank of the current run: the chip enable is switched
* without touching the rest of FMCTRL0 */
static int nand_run_status(uint32_t bank)
{
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK) | FMCTRL0_CE(bank);
return nand_read_status();
}
/* Program a run of pages, each with its 12 bytes of spare metadata, as the
* firmware's sequencer programs do: one plane at a time, or with two_plane
* two planes per command, as the chip's mode selects. The controller loads
* each
* 512-byte chunk from memory while the previous one goes out, a 4KiB page's
* second 2KiB unit included, and loads a page's first chunk while the page
* before it finishes, so the run never
* waits for the host. A chip is not waited for until it is given its next
* page: the others program in the meantime, and every chip used is checked
* at the end. With two_plane the pages come in pairs on one bank, plane 0
* then plane 1. On failure *failbank names the bank that reported it. */
int nand_write_pages(const struct nand_write *w, unsigned int n,
bool two_plane, uint32_t *failbank)
{
/* Per chunk: the ECC engine's encode command, and which of the
* controller's buffers goes out while the next chunk loads */
static const uint32_t encode[4] = { 0x01102, 0x11202, 0x21102, 0x31202 };
static const uint32_t bufsel[4] = { 0x102, 0x201, 0x102, 0x201 };
uint32_t chunks = nand_chip->pagesize / NAND_CHUNK_SIZE;
uint32_t used, bank, i, chunk;
int status;
if (!nand_ready || !n || (two_plane && (n & 1)))
return -1;
for (i = 0; i < n; i++)
{
if (w[i].bank >= nand_banks
|| w[i].page >= (uint32_t)nand_chip->blocks
* nand_chip->pagesperblock)
return -1;
/* The controller reads the data behind the cache's back */
commit_dcache_range(w[i].buf, nand_chip->pagesize);
}
FMCTRL1 = 0x0ff3f8e0;
FMCSTAT = 0x0ff00ffe;
FMCTRL0 = (NAND_TUNK1 << 16) | (NAND_TWP << 12) | FMCTRL0_UNK1 | 1;
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK) | FMCTRL0_CE(w[0].bank);
used = 1 << w[0].bank;
/* Chunk 0 of the first page into the controller */
FMDATAW0 = (uint32_t)(intptr_t)w[0].buf;
FMCTRL0 |= FMCTRL0_AUTOXFER;
FMDNUM = 0x1ff;
FMADDR2 = 1;
FMCTRL1 = 0x2e0;
if (nand_load_spare(w[0].meta))
goto fail;
nand_encode(encode[0]);
for (i = 0; i < n; i++)
{
bool plane1 = two_plane && (i & 1);
bool last = i + 1 == n;
if (nand_send_cmd(plane1 ? NAND_CMD_PROGRAM_PLANE1 : NAND_CMD_PROGRAM)
|| nand_send_addr_page(w[i].page)
|| nand_wait_reg(&FMTRANSSTAT, 1 << 0))
goto fail;
for (chunk = 0; chunk < chunks; chunk++)
{
bool pageend = chunk + 1 == chunks;
if (pageend && last)
FMCTRL0 &= ~FMCTRL0_AUTOXFER;
/* 16 bytes of ECC and spare */
FMDNUM = 0xf;
FMADDR2 = 0x1000;
FMADDR7 = (chunk % 4) << 4;
FMCTRL1 = 0x34;
if (pageend && !last)
FMDATAW0 = (uint32_t)(intptr_t)w[i + 1].buf;
else if (chunk % 4 == 3 && !pageend) /* the next 2KiB unit */
FMDATAW0 = (uint32_t)(intptr_t)w[i].buf
+ (chunk + 1) * NAND_CHUNK_SIZE;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
goto fail;
/* 512 bytes of data, and the next chunk in behind them */
if (pageend && last)
{
FMDATAW1 = 7;
FMADDR2 = 0x200;
FMDNUM = 0x1ff;
FMCTRL1 = 0xe4;
if (nand_wait_stat(FMCSTAT_TRANSDONE))
goto fail;
break;
}
FMADDR2 = bufsel[chunk % 4];
FMDNUM = 0x1ff;
FMCTRL1 = 0x2e4;
if (pageend)
{
if (nand_load_spare(w[i + 1].meta))
goto fail;
nand_encode(encode[0]);
if (nand_wait_stat(FMCSTAT_TRANSDONE))
goto fail;
break;
}
if (nand_wait_stat(FMCSTAT_UNK20))
goto fail;
nand_encode(encode[(chunk + 1) % 4]);
if (nand_wait_reg(&FMTRANSSTAT, 1 << 0)
|| nand_wait_stat(FMCSTAT_TRANSDONE))
goto fail;
}
/* Plane 0 of a pair is queued; anything else starts programming */
if (nand_send_cmd(two_plane && !plane1 ? NAND_CMD_PROGRAM_QUEUE
: NAND_CMD_PROGRAM2))
goto fail;
if (last)
break;
/* On to the next page's chip, which must have finished whatever it
* was given before */
bank = w[i + 1].bank;
FMCTRL0 = (FMCTRL0 & ~FMCTRL0_CE_MASK) | FMCTRL0_CE(bank);
if (used & (1 << bank))
{
status = nand_read_status();
if (status < 0)
goto fail;
if (status & NAND_STATUS_FAIL)
goto failed;
}
used |= 1 << bank;
}
for (bank = 0; bank < NAND_MAX_BANKS; bank++)
{
if (!(used & (1 << bank)))
continue;
status = nand_run_status(bank);
if (status < 0)
goto fail;
if (status & NAND_STATUS_FAIL)
goto failed;
}
FMCTRL0 &= ~FMCTRL0_CE_MASK;
nand_clear_status();
return 0;
failed:
if (failbank)
*failbank = __builtin_ctz(FMCTRL0 & 0x1fe) - 1;
FMCTRL0 &= ~FMCTRL0_AUTOXFER;
nand_clear_status();
return NAND_OP_FAILED;
fail:
if (failbank)
*failbank = __builtin_ctz(FMCTRL0 & 0x1fe) - 1;
FMCTRL0 &= ~FMCTRL0_AUTOXFER;
return nand_finish(-1);
}
int nand_write_page(uint32_t bank, uint32_t page, const void *databuf,
const uint32_t *meta)
{
struct nand_write w = { bank, page, databuf, meta };
return nand_write_pages(&w, 1, false, NULL);
}
uint32_t nand_get_id(void)
{
return nand_id;
}
unsigned int nand_get_bank_count(void)
{
return nand_banks;
}
#ifdef NAND_CHECK
/* Hooks for the contributor check image (nand-check-nano3g.c), which reads
* the raw NAND itself and serves the storage API in its place */
int nand_get_chip_row(void)
{
return nand_chip ? (int)(nand_chip - nand_chip_table) : -1;
}
const struct nand_geometry *nand_check_use_chip(unsigned int pagesperblock)
{
/* Keep whatever was identified readable, mounted or not, but drop a
* chip whose blocks do not hold the pages the check's layout expects */
if (nand_chip && nand_chip->pagesperblock != pagesperblock)
nand_chip = NULL;
nand_ready = nand_chip != NULL;
return nand_get_geometry();
}
#endif /* NAND_CHECK */
/* ---- Rockbox storage API ---- */
static int nand_init_chip(void)
{
uint32_t bank, id;
unsigned int i;
int rc;
nand_ready = false;
nand_banks = 0;
nand_chip = NULL;
rc = nand_reset(0);
if (rc)
{
logf("nand: reset failed (%d)", rc);
return rc;
}
if (nand_identify(&id, NULL))
{
logf("nand: identify failed");
return NAND_ERR_IDENTIFY;
}
nand_id = id;
/* Further banks count if they hold the same chip, contiguously. Like
* Apple's FIL, refuse a unit where another chip enable answers with a
* different part, or where a chip follows a missing one: mounting it
* with fewer banks would read the wrong blocks. */
nand_banks = 1;
for (bank = 1; bank < NAND_MAX_BANKS; bank++)
{
if (nand_reset(bank) || nand_identify(&id, NULL)
|| !id || id == 0xffffffff)
continue;
if (id != nand_id || bank != nand_banks)
{
logf("nand: bank %lu answers %08lx", (unsigned long)bank,
(unsigned long)id);
return NAND_ERR_MIXED;
}
nand_banks++;
}
for (i = 0; i < ARRAYLEN(nand_chip_table); i++)
if (nand_chip_table[i].id == nand_id
&& nand_chip_table[i].banks == nand_banks)
nand_chip = &nand_chip_table[i];
if (!nand_chip)
{
/* A normal build carries only the chips Rockbox has been tested on,
* so a chip missing from the table is one nobody has validated */
logf("nand: chip %08lx x %u is not supported", (unsigned long)nand_id,
nand_banks);
return NAND_ERR_UNSUPPORTED;
}
nand_ready = true;
nand_touch();
#ifndef NAND_CHECK
/* The table can still hold a row that has not been through a write test
* (a NAND_TEST_READONLY build makes every row look that way): leave that
* chip alone too. The check image reads any chip it identifies. */
if (!nand_get_geometry()->validated)
{
logf("nand: chip %08lx x %u is not validated",
(unsigned long)nand_id, nand_banks);
nand_ready = false;
return NAND_ERR_UNSUPPORTED;
}
#endif
rc = ftl_init();
if (rc)
{
logf("nand: FTL mount failed (%d)", rc);
nand_ready = false;
return NAND_ERR_FTL + rc;
}
return 0;
}
int nand_init(void)
{
int rc = nand_init_chip();
#ifdef NAND_CHECK
/* The check image reports whatever happened and serves the raw NAND of
* any chip it identified, mounted or not, so init always succeeds */
nand_check_init(rc);
rc = 0;
#endif
return rc;
}
void nand_spindown(int seconds)
{
(void)seconds;
}
void nand_spin(void)
{
nand_touch();
}
#ifdef HAVE_STORAGE_FLUSH
int nand_flush(void)
{
int rc;
if (!nand_ready)
return -1;
rc = ftl_sync();
if (rc)
panicf("Failed to unmount flash: %d", rc);
return rc;
}
#endif
/* ---- the FTL's disk. A NAND_CHECK build serves the raw NAND instead, from
* nand-check-nano3g.c, which defines these five entry points in place of
* the ones below ---- */
#ifndef NAND_CHECK
#ifdef HAVE_STORAGE_READONLY
bool nand_readonly(IF_MD_NONVOID(int drive))
{
IF_MD((void)drive);
return ftl_readonly_mount();
}
#endif
int nand_read_sectors(IF_MD(int drive,) sector_t start, int incount,
void* inbuf)
{
IF_MD((void)drive);
int rc;
if (!nand_ready)
return -1;
rc = ftl_read((uint32_t)start, incount, inbuf);
if (!rc)
nand_touch();
return rc;
}
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count,
const void* outbuf)
{
IF_MD((void)drive);
int rc;
if (!nand_ready)
return -1;
rc = ftl_write((uint32_t)start, count, outbuf);
if (!rc)
nand_touch();
return rc;
}
int nand_event(long id, intptr_t data)
{
(void) id;
(void) data;
return 0;
}
#ifdef STORAGE_GET_INFO
void nand_get_info(IF_MD(int drive,) struct storage_info *info)
{
IF_MD((void)drive);
info->sector_size = SECTOR_SIZE;
info->num_sectors = ftl_num_sectors();
info->vendor = "Apple";
info->product = "iPod Nano 3G";
info->revision = "1.0";
}
#endif
#endif /* !NAND_CHECK */
long nand_last_disk_activity(void)
{
return nand_last_activity_value;
}