mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
ipodnano3g: NAND driver, FTL and storage
The Nano 3G keeps everything behind the S5L8702 flash controller and
Apple's FTL ("Whimory"), and nand-nano3g.c was a stub, so the port had
no storage at all. This is the flash driver, the FTL and the wiring that
makes the NAND Rockbox's disk. The three are one change because neither
half is usable without the other: the driver alone cannot see a
filesystem, and the FTL alone has nothing to drive.
Testing evidence: firmware/target/arm/s5l8702/ipodnano3g/TESTING.md.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: Ib04a991489ff3a1a63cd55552e4a8d7ec7a5fc8c
This commit is contained in:
parent
d23a19dc2d
commit
34a18e7616
12 changed files with 4065 additions and 33 deletions
|
|
@ -45,6 +45,9 @@
|
|||
#include "version.h"
|
||||
#include "powermgmt.h"
|
||||
#include "usb.h"
|
||||
#ifdef IPOD_NANO3G
|
||||
#include "nand-target.h"
|
||||
#endif
|
||||
#ifdef HAVE_SERIAL
|
||||
#include "serial.h"
|
||||
#endif
|
||||
|
|
@ -878,6 +881,30 @@ void main(void)
|
|||
#endif
|
||||
|
||||
rc = storage_init();
|
||||
#ifdef IPOD_NANO3G
|
||||
if (rc == NAND_ERR_UNSUPPORTED) {
|
||||
/* Rockbox only drives NAND chips proven on hardware. Say which
|
||||
* one this is and how to get it validated, then leave the unit
|
||||
* to Apple's firmware, which is untouched. */
|
||||
lcd_set_foreground(LCD_RBYELLOW);
|
||||
printf("NAND not supported yet");
|
||||
lcd_set_foreground(LCD_WHITE);
|
||||
printf("chip %08lx x %u", (unsigned long)nand_get_id(),
|
||||
nand_get_bank_count());
|
||||
printf("Rockbox does not write to");
|
||||
printf("chips it has not been");
|
||||
printf("tested on.");
|
||||
printf("Starting Apple firmware...");
|
||||
sleep(8 * HZ); /* long enough to read the message */
|
||||
rc = kernel_launch_onb();
|
||||
/* Only reached if the ONB could not be read from NOR */
|
||||
printf("Apple firmware failed: %d", rc);
|
||||
printf("Hold MENU+SELECT to reboot,");
|
||||
printf("then SELECT+PLAY for disk mode");
|
||||
while (1)
|
||||
sleep(HZ);
|
||||
}
|
||||
#endif
|
||||
if (rc != 0) {
|
||||
printf("Storage error: %d", rc);
|
||||
fatal_error(ERR_STORAGE);
|
||||
|
|
@ -964,6 +991,12 @@ void main(void)
|
|||
printf("P%d T%02x S%llx",
|
||||
i, pinfo.type, (unsigned long long)pinfo.size);
|
||||
}
|
||||
#if defined(IPOD_NANO3G) && defined(DEFAULT_VIRT_SECTOR_SIZE)
|
||||
/* Nothing mounted: show USB hosts Apple's 4096-byte sectors, which
|
||||
* its partition table counts in */
|
||||
disk_set_sector_multiplier(IF_MD(0,)
|
||||
DEFAULT_VIRT_SECTOR_SIZE / SECTOR_SIZE);
|
||||
#endif
|
||||
fatal_error(ERR_RB);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1635,6 +1635,7 @@ target/arm/s5l8702/ipodnano3g/adc-nano3g.c
|
|||
target/arm/s5l8702/ipodnano3g/powermgmt-nano3g.c
|
||||
target/arm/s5l8702/ipodnano3g/power-nano3g.c
|
||||
target/arm/s5l8702/ipodnano3g/nand-nano3g.c
|
||||
target/arm/s5l8702/ipodnano3g/ftl-nano3g.c
|
||||
#if (CONFIG_RTC == RTC_NANO3G)
|
||||
target/arm/s5l8702/ipodnano3g/rtc-nano3g.c // TODO: -> drivers/rtc/rtc_xxx.c ???
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -80,6 +80,12 @@
|
|||
|
||||
#define CONFIG_STORAGE STORAGE_NAND
|
||||
|
||||
/* The flash controller DMAs straight into and out of the caller's buffer
|
||||
* and only handles word-aligned ones: a read into a buffer 1-3 bytes off
|
||||
* returns wrong data and writes past the buffer (measured). The FAT code
|
||||
* bounces unaligned transfers, as on the Classic 6G. */
|
||||
#define STORAGE_NEEDS_BOUNCE_BUFFER
|
||||
|
||||
// TODO
|
||||
//#define CONFIG_NAND NAND_SAMSUNG
|
||||
#define CONFIG_NAND 0
|
||||
|
|
@ -88,9 +94,18 @@
|
|||
needs to do cleanup on shutdown */
|
||||
#define HAVE_STORAGE_FLUSH
|
||||
|
||||
/* Chips not validated are mounted read-only; USB hosts are told so */
|
||||
#define HAVE_STORAGE_READONLY
|
||||
|
||||
/* The NAND flash has 2048-byte sectors, and is our only storage */
|
||||
#define SECTOR_SIZE 2048
|
||||
|
||||
/* Apple's partition table on the NAND counts 4096-byte sectors */
|
||||
#define MAX_VIRT_SECTOR_SIZE 4096
|
||||
|
||||
/* If we have no valid partitions, advertise this as our sector size */
|
||||
#define DEFAULT_VIRT_SECTOR_SIZE 4096
|
||||
|
||||
/* LCD dimensions */
|
||||
#define LCD_WIDTH 320
|
||||
#define LCD_HEIGHT 240
|
||||
|
|
@ -122,8 +137,8 @@
|
|||
/* Define this to enable morse code input */
|
||||
#define HAVE_MORSE_INPUT
|
||||
|
||||
// TODO
|
||||
/* define this if you have a real-time clock */
|
||||
// TODO
|
||||
//#define CONFIG_RTC RTC_NANO3G
|
||||
#define CONFIG_RTC 0
|
||||
|
||||
|
|
@ -135,7 +150,7 @@
|
|||
// TODO
|
||||
#if 0
|
||||
/* Define the type of audio codec */
|
||||
#define HAVE_WM1870
|
||||
#define HAVE_WM8975
|
||||
#endif
|
||||
// XXX: dummy for preliminary build, WRONG CODEC!!!
|
||||
#define HAVE_CS42L55
|
||||
|
|
@ -238,8 +253,7 @@
|
|||
#define USB_VENDOR_ID 0x05AC
|
||||
#define USB_PRODUCT_ID 0x1262
|
||||
#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
|
||||
// TODO
|
||||
//#define HAVE_BOOTLOADER_USB_MODE
|
||||
#define HAVE_BOOTLOADER_USB_MODE
|
||||
#ifdef BOOTLOADER
|
||||
#define USBPOWER_BTN_IGNORE (~0)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ int nand_spinup_time(void); /* ticks */
|
|||
#ifdef STORAGE_GET_INFO
|
||||
void nand_get_info(IF_MD(int drive,) struct storage_info *info);
|
||||
#endif
|
||||
#ifdef HAVE_STORAGE_READONLY
|
||||
bool nand_readonly(IF_MD_NONVOID(int drive));
|
||||
#endif
|
||||
|
||||
long nand_last_disk_activity(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -929,7 +929,7 @@
|
|||
#define RSSYND3_2 (*(REG32_PTR_T)(FMC_BASE + 0x0188)) /* On-the-fly Synd Register3[71:64] */
|
||||
#define FLAGSYND (*(REG32_PTR_T)(FMC_BASE + 0x0190)) /* On-the-fly ECC Result Flag */
|
||||
|
||||
/* S5L8702 registers used by Apple's NANDReadPage (BootROM 0x20009910).
|
||||
/* S5L8702 registers the BootROM's page read uses.
|
||||
* The real names are unknown. */
|
||||
#define FMUNK78 (*(REG32_PTR_T)(FMC_BASE + 0x0078)) /* spare decode setup, 0x5140 */
|
||||
#define FMUNK7C (*(REG32_PTR_T)(FMC_BASE + 0x007C)) /* bit 1 start/busy; FMSYND5..7 */
|
||||
|
|
@ -938,6 +938,8 @@
|
|||
#define FMTRANS1 (*(REG32_PTR_T)(FMC_BASE + 0x0814)) /* correction mode, always 0x01000180 */
|
||||
#define FMTRANSSTAT (*(REG32_PTR_T)(FMC_BASE + 0x0840)) /* correction status, w1c; bit 2 done */
|
||||
|
||||
#define FMCTRL0_CE(bank) (1 << ((bank) + 1)) /* chip enable bits 1-8 */
|
||||
#define FMCTRL0_CE_MASK (0xff << 1)
|
||||
#define FMCTRL0_ENABLEDMA (1 << 10)
|
||||
#define FMCTRL0_UNK1 (1 << 11)
|
||||
#define FMCTRL0_AUTOXFER (1 << 24) /* S5L8702: write the chunk to the address in FMDATAW0 */
|
||||
|
|
|
|||
|
|
@ -266,6 +266,9 @@ static inline void storage_sleep(void) {};
|
|||
#define storage_removable(drive) nand_removable(IF_MD(drive))
|
||||
#define storage_present(drive) nand_present(IF_MD(drive))
|
||||
#endif
|
||||
#ifdef HAVE_STORAGE_READONLY
|
||||
#define storage_readonly(drive) nand_readonly(IF_MD(drive))
|
||||
#endif
|
||||
#define storage_driver_type(drive) (STORAGE_NAND_NUM)
|
||||
#elif (CONFIG_STORAGE & STORAGE_RAMDISK)
|
||||
#define STORAGE_FUNCTION(NAME) (ramdisk_## NAME)
|
||||
|
|
|
|||
16
firmware/target/arm/s5l8702/ipodnano3g/TESTING.md
Normal file
16
firmware/target/arm/s5l8702/ipodnano3g/TESTING.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# iPod Nano 3G testing notes
|
||||
|
||||
Evidence for the driver, FTL, storage and hardware support in this
|
||||
directory.
|
||||
|
||||
## NAND driver, FTL and storage
|
||||
|
||||
Since the earlier host and hardware pass: `test_disk` passes at every
|
||||
buffer alignment - it's what found and confirmed the fix for a DMA
|
||||
alignment bug (a misaligned buffer returned wrong data and wrote past
|
||||
itself). The storage thread holds the FTL for at most 492 ms during a
|
||||
merge, measured with a timing build - within the 3 s PCM buffer, so no
|
||||
audio starvation.
|
||||
|
||||
The 8GB Hynix A555D5AD row's evidence is Robin Tschirschnitz's (delacor):
|
||||
measured and run on their own unit, not this one.
|
||||
2692
firmware/target/arm/s5l8702/ipodnano3g/ftl-nano3g.c
Normal file
2692
firmware/target/arm/s5l8702/ipodnano3g/ftl-nano3g.c
Normal file
File diff suppressed because it is too large
Load diff
60
firmware/target/arm/s5l8702/ipodnano3g/ftl-target.h
Normal file
60
firmware/target/arm/s5l8702/ipodnano3g/ftl-target.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2009 by Michael Sparmann
|
||||
*
|
||||
* Interface modelled on
|
||||
* firmware/target/arm/s5l8700/ipodnano2g/ftl-target.h.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __FTL_TARGET_H__
|
||||
#define __FTL_TARGET_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Mount Apple's FTL. nand_init() must have succeeded. Returns 0, or a
|
||||
* negative number saying which stage failed: -4 if the chip's layout or
|
||||
* page size is not supported, -5 if its VFL context does not fit the chip
|
||||
* table row. The FTL is mounted read-only, so that writes fail and
|
||||
* ftl_sync() does nothing, in the bootloader, on a chip whose row is not
|
||||
* validated, and if anything a commit must reproduce could not be
|
||||
* loaded. */
|
||||
int ftl_init(void);
|
||||
|
||||
/* Read count logical sectors of NAND_PAGE_SIZE bytes, one or two to a NAND
|
||||
* page. Returns 0 or a negative number. */
|
||||
int ftl_read(uint32_t sector, uint32_t count, void *buffer);
|
||||
|
||||
/* Write count logical sectors of NAND_PAGE_SIZE bytes. The data is on the
|
||||
* medium when this returns, but the block map that finds it is not: call
|
||||
* ftl_sync() to commit that. Returns 0 or a negative number. */
|
||||
int ftl_write(uint32_t sector, uint32_t count, const void *buffer);
|
||||
|
||||
/* Commit the block map, so the writes above survive a remount. Returns 0 or
|
||||
* a negative number; after a failure the FTL refuses further writes. */
|
||||
int ftl_sync(void);
|
||||
|
||||
/* Number of logical sectors, 0 if the FTL is not mounted */
|
||||
uint32_t ftl_num_sectors(void);
|
||||
|
||||
/* Whether the mounted FTL refuses writes */
|
||||
bool ftl_readonly_mount(void);
|
||||
|
||||
#endif /* __FTL_TARGET_H__ */
|
||||
File diff suppressed because it is too large
Load diff
161
firmware/target/arm/s5l8702/ipodnano3g/nand-target.h
Normal file
161
firmware/target/arm/s5l8702/ipodnano3g/nand-target.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2009 by Michael Sparmann
|
||||
*
|
||||
* Interface modelled on
|
||||
* firmware/target/arm/s5l8700/ipodnano2g/nand-target.h.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __NAND_TARGET_H__
|
||||
#define __NAND_TARGET_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* The smallest page, which is also the storage layer's sector: 4KiB-page
|
||||
* chips hold two sectors per page */
|
||||
#define NAND_PAGE_SIZE 2048
|
||||
#define NAND_MAX_PAGE_SIZE 4096
|
||||
|
||||
/* The on-flash VFL suggests four banks on the 4GB model. FMCTRL0 has chip
|
||||
* enable bits for eight. */
|
||||
#define NAND_MAX_BANKS 4
|
||||
|
||||
/* Non-negative results of nand_read_page(): the ECC verdict of Apple's
|
||||
* the firmware's page read. Every written page reads as CORRECTED and every
|
||||
* erased page
|
||||
* as CLEAN (measured over a whole 4GB unit), so CORRECTED is the normal
|
||||
* result for data. FAILED and STATUS6 are named from how the ROM derives
|
||||
* them and have not been seen. */
|
||||
#define NAND_ECC_CLEAN 0
|
||||
#define NAND_ECC_CORRECTED 1
|
||||
#define NAND_ECC_FAILED 3
|
||||
#define NAND_ECC_STATUS6 6
|
||||
|
||||
/* Size of the spare metadata nand_read_page() returns, in 32-bit words.
|
||||
* This is the 12-byte Whimory spare header (lpn/usn/type/eccmark). */
|
||||
#define NAND_META_WORDS 3
|
||||
|
||||
/* How a VFL block's planes sit on a bank (Apple's mode functions for the
|
||||
* FIL, which the original firmware installs per chip mode) */
|
||||
#define NAND_LAYOUT_UNKNOWN 0
|
||||
#define NAND_LAYOUT_SINGLE 1 /* one block: mode 1 */
|
||||
#define NAND_LAYOUT_ADJACENT 2 /* block 2b + plane: modes 3, 8 */
|
||||
#define NAND_LAYOUT_HALVES 3 /* b + plane * blocks / 2: 2, 9, 12 */
|
||||
#define NAND_LAYOUT_BOTH 4 /* both of the above: mode 4 */
|
||||
#define NAND_LAYOUT_SPLIT13 5 /* Toshiba's 8320 blocks: mode 13 */
|
||||
|
||||
struct nand_geometry
|
||||
{
|
||||
unsigned int banks;
|
||||
unsigned int blocks; /* per bank */
|
||||
unsigned int pagesperblock;
|
||||
unsigned int planes; /* physical blocks per bank in a VFL block */
|
||||
unsigned int userblocks; /* per bank, as Whimory counts them */
|
||||
unsigned int vflspares; /* VFL reserved blocks per plane */
|
||||
bool twoplane; /* whole rows may be programmed two-plane */
|
||||
unsigned int pagesize; /* bytes of data per page */
|
||||
unsigned int mode; /* Apple's chip table mode */
|
||||
unsigned int layout; /* NAND_LAYOUT_* */
|
||||
bool validated; /* may be mounted writable */
|
||||
};
|
||||
|
||||
/* Reset one bank (chip enable) and select it. Returns 0 or a NAND_ERR_*. */
|
||||
int nand_reset(uint32_t bank);
|
||||
|
||||
/* Read the first four ID bytes of the bank the last nand_reset() selected.
|
||||
* ext, if not NULL, gets the next four. Returns 0, or -1 on a timeout. */
|
||||
int nand_identify(uint32_t *id, uint32_t *ext);
|
||||
|
||||
/* Read one page. meta may be NULL. Returns a negative number if the
|
||||
* controller timed out, else one of NAND_ECC_*. */
|
||||
int nand_read_page(uint32_t bank, uint32_t page, void *databuf,
|
||||
uint32_t *meta);
|
||||
|
||||
/* One page of a read run. nand_read_pages() fills ecc with one of
|
||||
* NAND_ECC_* when the controller completed the page. */
|
||||
struct nand_read
|
||||
{
|
||||
uint32_t bank;
|
||||
uint32_t page;
|
||||
void *buf;
|
||||
uint32_t *meta;
|
||||
int ecc;
|
||||
uint32_t result; /* the read program's result bitmap: bit 30
|
||||
* uncorrectable, bit 29 erased, bit n-1 an n-bit
|
||||
* correction in some chunk */
|
||||
};
|
||||
|
||||
/* Read a run of pages with Apple's firmware read sequence, overlapping the
|
||||
* NAND page-load time on the chip enables in the run. Returns 0 if every
|
||||
* page completed or -1 on a controller timeout; inspect each page's ecc for
|
||||
* its ECC verdict. */
|
||||
int nand_read_pages(struct nand_read *r, unsigned int n);
|
||||
|
||||
/* Erase one block, or write one page with its NAND_META_WORDS words of spare
|
||||
* metadata. Return 0, -1 if the controller timed out, or NAND_OP_FAILED if
|
||||
* the chip reports a failure (the block should be treated as bad). */
|
||||
#define NAND_OP_FAILED (-2)
|
||||
int nand_erase_block(uint32_t bank, uint32_t block);
|
||||
int nand_write_page(uint32_t bank, uint32_t page, const void *databuf,
|
||||
const uint32_t *meta);
|
||||
|
||||
/* One page of a run: where it goes, its data and its spare metadata */
|
||||
struct nand_write
|
||||
{
|
||||
uint32_t bank;
|
||||
uint32_t page;
|
||||
const void *buf;
|
||||
const uint32_t *meta;
|
||||
};
|
||||
|
||||
/* Program a run of pages at once, as Apple's firmware does: the chips
|
||||
* program in parallel, each waited for only when it is given its next
|
||||
* page, and with two_plane the pages come in pairs - plane 0 then plane 1
|
||||
* of one bank - programmed by one command each. Returns as
|
||||
* nand_write_page(). After a failure *failbank (if not NULL) is the bank
|
||||
* that was being programmed or reported it, and any page of the run may not
|
||||
* have been written. */
|
||||
int nand_write_pages(const struct nand_write *w, unsigned int n,
|
||||
bool two_plane, uint32_t *failbank);
|
||||
|
||||
/* nand_init() results. -4 is retired: early builds used it for a ready
|
||||
* bit timeout that turned out not to be an error. */
|
||||
enum nand_init_result
|
||||
{
|
||||
NAND_ERR_RESET_CMD = -1, /* RESET never completed */
|
||||
NAND_ERR_IDENTIFY = -2, /* READ ID failed */
|
||||
NAND_ERR_READY_CMD = -3, /* READ STATUS never completed */
|
||||
NAND_ERR_UNSUPPORTED = -5, /* not a chip Rockbox has been tested on */
|
||||
NAND_ERR_MIXED = -6, /* chip enables answer with different ids */
|
||||
NAND_ERR_FTL = -100, /* plus ftl_init()'s code */
|
||||
};
|
||||
|
||||
/* The READ ID bytes bank 0 answered with, 0 before nand_init() */
|
||||
uint32_t nand_get_id(void);
|
||||
|
||||
/* Number of banks (chip enables) nand_init() found holding the same chip
|
||||
* as bank 0 */
|
||||
unsigned int nand_get_bank_count(void);
|
||||
|
||||
/* Geometry of the attached chips, or NULL before nand_init() succeeded */
|
||||
const struct nand_geometry *nand_get_geometry(void);
|
||||
|
||||
#endif /* __NAND_TARGET_H__ */
|
||||
|
|
@ -201,7 +201,8 @@ int pmu_firewire_present(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(BOOTLOADER) // XXX: from usb-s5l8702.c
|
||||
/* XXX: from usb-s5l8702.c */
|
||||
#if defined(BOOTLOADER) && !defined(HAVE_BOOTLOADER_USB_MODE)
|
||||
#include "usb.h"
|
||||
static int usb_status = USB_EXTRACTED;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue