copy_sectors() copied one destination raw page at a time, so on a
two-plane part every page of a copy took two programs and two tPROG.
Its buffer already holds a page of every plane: copy that much at once,
and flash_program() programs the planes together.
Copies are most of the programs when the FTL closes blocks that random
writes left part written. Over USB mass storage on a Samsung YP-CP3 a
stress test that ran 66792 one-plane programs ran 692 now, with 43958
two-plane ones, and its program time fell from 85 s to 59 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icc60d250ecc75c83c2d817efb7678333bffe3137
Every sector of a read was transferred from the chip into a controller
slot and then copied out of it, the next transfer starting only after
the copy. Start it before: it goes into the next slot, not the one
being copied.
On the Samsung YP-CP3, same test: reads at 8.25 MB/s, the original
firmware's speed, every read verified, also after a power cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I53f77ea3c00bb449d772357e39602cca9c5cdba3
flash_init() sets FMWAIT to 0x1081, as the OF's FlashInit() does, and
nothing changed it after. But the OF, and the Samsung YP-CP3's NAND
bootloader, follow it with FlashTimingCfg(), at every bus clock
change: from chip 0's access time and the bus clock it derives a
timing that, for the YP-CP3's 25 ns Samsung part at 100 MHz, is 0x60
- by the RK28 controller's register layout, under half the bus cycles
per byte.
Do the same once chips are detected, for 100 MHz: the AHB runs at
CPUFREQ_MAX / 2 or slower, where the value only gains margin.
Over USB mass storage on the YP-CP3 the NAND read at 5.70 MB/s and
wrote at 3.26 MB/s; now 6.83 and 3.65 MB/s, every read verified, also
after a power cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I356214c5d10e65952abc3334f5fae5deb2e02cff
usb_drv_exit() masks the UDC interrupt in the interrupt controller at
every disconnect, but only usb_init_device(), once at boot, unmasked
it. After the first unplug the UDC raised no more interrupts: on the
next plug the charging icon showed - plug detection polls VBUS_STS -
but the host's reset and requests went unanswered, so the device never
enumerated and the USB screen never came up.
Unmask it in usb_drv_init(), which runs at every connect, so that it
pairs with the mask in usb_drv_exit(). The interrupt is now masked
while USB is off, at boot too; nothing needs it then.
Tested on a Samsung YP-CP3: it enumerates at every replug.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iab7b7e93c140990c39b0e6dfae23ffcc829f6ab2
Every sector of a write waited for the previous sector's transfer to
the chip before it was copied into a controller slot, so the copy and
the transfer never ran together. The slot the copy goes to is not the
one in transfer: copy first and wait only before the BCH engine and
the transfer restart.
On the Samsung YP-CP3, same test: 3.26 MB/s, every read verified, also
after a power cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I41f43658916891a2ded74bbe21b6970f7cc78068
On a two-plane part an FTL page spans the same page of two blocks, one
in each plane, and flash_program() programmed them one after the other:
two program busy times (tPROG) per page. The original firmware's
FlashProgEnhanced() programs both with one two-plane program - 80h, the
first page, 11h, a wait of tDBSY, 81h, the second page, 10h - so the
planes share one tPROG.
Do the same where the runs of a write cover the same page of both
planes, on parts that take 81h for the second page. The original
firmware sends 80h there on Toshiba and Micron parts, which address
the planes differently too; those still program a page at a time.
enum vendor_t moves to nand-target.h for the check.
Over USB mass storage on a Samsung YP-CP3 the NAND wrote at 2.22 MB/s,
against 4.35 MB/s in the original firmware; now 2.86 MB/s, every read
verified.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2f40e2ec426cf787b4ce452cbd0d0eb4b702e168
flash_read() latched the page afresh for every sector. The controller
streams a page from its first sector, so each sector also cost a
transfer of every sector before it in the page: reading an 8-sector
page sector by sector took 8 array loads and 36 sector transfers
instead of 1 and 8. Over USB mass storage on a Samsung YP-CP3 the
NAND read at 1.87 MB/s, against 8.25 MB/s in the original firmware.
Read each run of sectors that lie consecutively in one raw page with a
single latch. On a two-plane part a run of FTL sectors stays in one
page until it moves on to the other plane.
On the YP-CP3, same test: 5.64 MB/s, every read verified.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I9b08e56f4429ae432ccd2d2e23b9b34c18e56e97
Firmware builds reprogram the SDRAM controller twice. system_init()
sets burst 8, T_RP = T_RCD = 1 and the refresh, with CAS 3 since
a6538abd16 - CAS 2 on HM60X/HM801. And set_sdram_timing(), on every CPU
frequency change, rewrites the mode: CAS 2 whenever the AHB runs at
100 MHz or below, which is every clock this code sets.
On a generic rk2705 the firmware hangs in system_init() on those writes;
with them skipped it boots. Doing the same writes from IRAM, so that
nothing touches the SDRAM while the controller reprograms the chip,
hangs the same way: it is the settings themselves. The board's SDRAM
is an Elpida EDS1216AATA-75 (16 MB), a 133 MHz part at CAS 3 whose
minimum clock period at CAS 2 is 10 ns - exactly the 100 MHz it runs
at here, with no margin, next to minimal T_RP/T_RCD.
On a Samsung YP-CP3 the firmware boots but corrupts memory at random
once the clock first changes: data aborts on valid addresses in
unrelated code, undefined instruction exceptions on valid instructions,
glitches in the boot logo, crashes on USB plug and unplug. A memory
test over 15 MB passes with the boot's setup (CAS 3, burst 1,
T_RP = T_RCD = 2) and with system_init()'s values alike - it never
changes the clock - and skipping only the system_init() writes is not
enough, as set_sdram_timing() still selects CAS 2. With both removed
the YP-CP3 runs, passes a USB mass storage stress test and survives
USB unplug.
The boot ROM's and the bootloaders' setup works on every rk27xx target
seen, and nothing before Rockbox changes it (the NAND bootloader's
stage 1 and rk27load's s1 only probe the organisation). The gain
claimed for the tweak was a slight improvement in memory throughput.
So remove it for every rk27xx target, the HM60X/HM801 CAS 2 included,
and have set_sdram_timing() adjust only the refresh to the bus clock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icd063367783b0ae6eb80bad67d908269c07e704f
Every SD write failed. The card took the write command and went to
receive-data state, but never saw the block: CMD12 still found it there,
and the controller reported a missing CRC status after each block, 20
retries over. Reads worked. Stress-tested over USB mass storage on a
generic rk2705, with a SDHC card, in the normal firmware and in the
bootloader alike.
sd_init_card() switched the card to high-speed mode with CMD6. This host
is an SD 1.01 controller with a card clock of at most 25 MHz, per the
rk27xx datasheet; high speed and CMD6 came with SD 1.10, and a card
switched to it evidently does not take the data this host drives. The
original firmware never switches: after selecting the card it sets the
block length and a 1-bit bus and stays at default speed. A slower card
clock did not help; dropping the switch alone did.
Leave the card at default speed. Tested on a generic rk2705:
ums_stress.py over a 32 MiB window of the card - fill, verify, edge
sizes, a mixed read/write soak - passes at 2.3 MB/s writing and reading,
about 75% of what a 1-bit bus at 25 MHz carries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icfafab0bb460c8c0fda3dea455df9a36622bf764
config.h defines HAVE_STORAGE_FLUSH for the rk27xx Scheme A FTL when
CONFIG_STORAGE has STORAGE_NAND and CONFIG_NAND is NAND_RK27XX. sim.h
undefines CONFIG_NAND but keeps CONFIG_STORAGE, so a simulator for a
NAND target - the iPod nano 2G - evaluated the undefined macro:
"CONFIG_NAND" is not defined, evaluates to 0 [-Wundef]
Test that it is defined first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Id1e328f08c14d7c5528d29f110fdbb8be68ab2f5
CONFIG_RK27XX_FTL selects the FTL a target's NAND uses,
RK27XX_FTL_SCHEME_A or RK27XX_FTL_SCHEME_B. rk27generic and the YP-CP3
are Scheme A, the HM-60x Scheme B. ftl-rk27xx.c mounts the one named;
for Scheme B it maps the drives onto the volumes ID block 1 records:
the system disk from LBA 0, the user volume after the system data
area.
The other rk27xx targets with NAND - HM-801, MA8, MA8C, MA9, MA9C and
iHiFi 760, 770, 770C, 800, 960 - have no confirmed scheme. They drop
the NAND from storage and build only the FTL scheme finder, so users
can report what their device holds and the scheme can then be set.
Only Scheme A flushes at shutdown: Scheme B holds nothing in RAM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I484217b82c6de7316b90b354c012bfbaa263b3dd
Many rk27xx targets have NAND whose format nobody has examined. The
finder reads ID block 1 and the first page of the first 512 blocks and
says which FTL formatted them: Scheme A by its remap-log blocks,
Scheme B by its bad-block table and data headers, another Scheme B
generation by other 0xFxxx tags. The later ID block layout ('RK27' at
0x0a) records the FTL area's BCH strength at 0x1ed - 8 on the HM-601,
14 on the Archos Vision 28 - and the scan reads in that mode.
It is read-only, shown in the debug menu as "View FTL scheme", and
built for targets whose NAND is not storage - none yet.
Run on dumps of an HM-601 it reports Scheme B, a Samsung YP-CP3
Scheme A, and an Archos Vision 28 the other Scheme B generation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I0528f9d2a6996089b6488a77b43942f6fd1f2c16
Scheme B is the self-describing NAND format of the HiFiMAN HM-601 and
similar players: every block carries its logical number and a version
in its metadata, so the mapping is rebuilt by a scan at mount, and
small writes go through a 16-page RAM cache journalled to flash.
ftl-scheme-b.c is a reimplementation from reverse engineering. The
format and the behaviour were worked out by analysing the machine code
of the HM-601's NAND bootloader and of a compiled Rockchip FTL object
from the rk2808 platform, which handles the same format, and checked
against dumps of the media; no source code was used. Where the two
binaries differ the HM-601 is followed: 16-bit versions compared
across wrap, plain 0xF200/0xF100 tags, a copy that stamps one header
on every page. The number of open exchange blocks is configurable - 8
on the HM-601, whose mount recovers no more.
Checked by running the compiled object under qemu over a NAND
simulator, side by side with this code, on a 4 GiB HM-601 dump: the
same state after mount, identical reads of all 3958 logical blocks,
and flash programs and erases identical one for one - over 600 random
writes on each of three seeds and at every power-cut point of three
sweeps, 1435 points - with 0 wrong sectors.
Not built yet: no target selects it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Icb935ea78d716c1e51f1453fc3ab3f47f3467027
The controller has a second ECC mode, BCHCTL bit 13: t=14 instead of
t=8, over the same field and polynomial. Some firmware writes its FTL
area in it - the Archos Vision 28 does - while every boot area seen is
t=8.
flash_set_ecc() selects the mode for reads and copies of the FTL area;
flash_read_raw(), which reads the boot area, stays at t=8. Writing is
refused in t=14 mode: a t=14 sector is a 538-byte record on the media
(3 metadata and 23 parity bytes), the program path addresses 528-byte
records, and programming in that mode has not been tried.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iaf4ce9274b72838b7719232fc49cdce0cceecbac
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
The NAND holds two volumes, SYS and USER; the split is recorded in the
boot area, so they are separate drives rather than partitions of one.
SYS is hidden: nothing in it is the user's, and deleting or overfilling
it stops the device booting. HAVE_RK27XX_NAND_SYS, documented in the
config and off, brings it back as a drive of its own.
storage.c numbers drives by driver, SD first:
default HAVE_RK27XX_NAND_SYS
drive 0 SD SD
drive 1 NAND USER NAND SYS
drive 2 NAND USER
NUM_DRIVES was 1, which was already wrong for a target whose
CONFIG_STORAGE names both SD and NAND.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ic68921838faef0ff6fd296d43e86ec3a10a091c5
ftl-rk27xx.c has been four empty stubs since 2010. Fill them in with
the flash translation layer the rk2705/rk2706 original firmware uses,
called Scheme A here to tell it from the log-structured layout of later
firmware. It reads and writes that format exactly as the original
firmware does, so a device keeps working with its original firmware
after Rockbox has written to it.
- ftl-scheme-a.{c,h}: the FTL. The file opens with a description of
the on-flash format and how the FTL works: the SYS and USER volumes,
super-blocks and zones, the zone table, the remap log and its mirror,
the exchange record and the write protocol, power-loss recovery, bad
blocks. Oddities of the original firmware kept for compatibility are
marked where they are.
- Parameters that differ between firmware builds - the zone reserve
base, the system zone offset, the format flag - are recovered from
the media at mount and checked against its structure; a mount that
cannot confirm them is read-only. A mount that would have to repair
the remap log while not allowed to write fails rather than serve
wrong data.
- ftl-rk27xx.c: the storage glue. It finds the boot area's ID block,
which records where SYS ends, and mounts the FTL.
- ata-nand-rk27xx.c: a drive per volume. SYS holds the original
firmware - on a Rockbox device including the BASE.RKW that chainloads
the bootloader - and nothing of the user's, so it is a drive only when
the target defines HAVE_RK27XX_NAND_SYS. Capacity comes from the FTL's
tables, not from raw block geometry.
- config.h: HAVE_STORAGE_FLUSH for the rk27xx NAND. The FTL holds up to
three part-written pages in RAM; storage_flush() commits them at
shutdown and ROLO.
Writing is opt-in: without FTL_ALLOW_WRITE the FTL mounts read-only and
never writes the flash, not even a repair the mount could make.
Two bugs of the original firmware are not reproduced. A write starting
before a page held part-written in RAM and running through it left two
buffers holding that page, and the older one was later programmed over
the newer data; such a write now flushes the held page first. And its
bad-block marker took two of its three metadata bytes from the stack,
which can make a retired block look like a remap-log block; the marker
is now written in full.
Tested in a host simulator on NAND images of a Samsung YP-CP3 and a
generic rk2705, against the original firmware's FTL object run under
qemu-arm: identical traces of every read, program and erase, with a
hash of the data each program writes, over mounting and reading, random
writes with every sector verified, a power cut at every flash operation
of a write, and a program or erase failure at every one.
On a generic rk2705: the read-only mount reports the layout and
capacities the original firmware does and every file's MD5 matches; a
write test passes 8192/8192 across a remount and a power cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I573d944389cefb456ecf38c6c7b91bebfe0fe377
system_init() gates the clocks of modules a firmware build does not use,
and the list included the NAND controller's HCLK. Once the NAND is
storage, the first access to the controller - flash_init() writing
FMWAIT at 0x180e8004 - takes a data abort on the unclocked peripheral.
No rk27xx target stored to NAND before, which is why this never showed.
The LCDC clock in the same list stays gated: the firmware draws with it
gated, the MCU interface running from the LCDC HCLK, which is not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I52a4dc9e63fe5b89c85b60dc3ef77d74528dc06f
flash-rk27xx.c drives the NAND controller and its BCH engine for the
flash translation layer that follows. It knows the chip's geometry and
how to read, program, erase and copy sectors, but not what the sectors
mean.
- Addresses are 512-byte sectors in a linear view of the chip where a
block is a super-block: on a two-plane part, one physical block from
each plane, consecutive pages alternating between them. The layer
maps that view to the chip; the FTL never sees planes.
- Every sector carries 16 spare bytes: 13 of BCH code, generated and
checked by the hardware, and 3 for the FTL. Byte 1 is written 0x00 on
every program - the "page programmed" marker the original firmware's
FTL keys its mount and recovery on.
- The program sequence was read out of the original firmware's own
machine code. The write kick is the read kick plus FL_WR, and the BCH
engine needs BCH_WR to encode rather than decode.
- A copy is read through the ECC engine and programmed, never the
chip's internal data move, which on this MLC part would carry bit
errors forward.
- Writes are refused until the FTL enables them, and writes into the
boot area are dropped and reported successful, as the original
firmware does. The write-protect line is lifted only for the duration
of each program or erase.
- Failures and timeouts are counted: the FTL can act on few of them.
Only the first chip is handled; every device the FTL has been checked
on has one.
Tested on a generic rk2705 directly, on a free block: a program across
both planes, a single-sector program with metadata, a whole page, a
copy and an erase each read back byte-exact through the controller's
ECC decode, which also shows the code it generated is valid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: If6ffa9f9811172f4f8829c1e68d6de9466a022c8
flash_init() identifies a chip by looking its READ ID device code up in
device_code[] and taking the capacity from device_info[] at the same
index. device_code[] had seven entries and device_info[] eight: 0xd5
(16 Gbit) was dropped when the table was transcribed from the OF. Every
code after the gap picked up the capacity one row up, so a 0xd7
(32 Gbit, 4 GiB) part was sized at 2 GiB.
On a generic rk2705 that halved total_phy_sec to 4194304, and the FTL
looked for its tables in the wrong place and read erased flash. With the
entry restored it reports 8388608, matching the chip and the host-side
dump of the same unit.
The OF's own table, as it appears in its NAND bootloader:
76 79 f1 da dc d3 d5 d7 00 00 02 00 00 00 04 00 ...
A compile-time check now fails the build if the two tables differ in
length again. Also fixes two register addresses in the same loop that
were missing a digit (0x180E204/0x180E208 for 0x180E8204/0x180E8208);
they are stored for reference only and nothing reads them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ia70c249eea2e44ea34aef52d8ae20860b2b5db9d
nand-rk27xx.c held a complete transcription of the OF's NAND handling -
chip detection, geometry derivation, chip select, ECC reads - inside
"#if 0", written as documentation back when the FTL was still unknown.
It has therefore never been compiled.
Enabling it exposed three things nothing had ever caught:
- flash_init() looks up ManufactureIDTbl[] and DeviceCode[], but the
tables are named manufacture_id_tbl[] and device_code[]
- mlc_refresh_row, flash_pend_cmd and flash_read_status_cmd are
assigned but were never defined
- memcpy() was used without including string.h
struct flashspec_t moves to nand-target.h, with flash_spec[] and
total_phy_sec declared there, because the FTL's flash primitives need
the geometry flash_init() derives. The "_raw" fields describe one
physical plane and the others the multi-plane view the FTL addresses;
that distinction is load-bearing for the FTL's address mapping, so both
are kept.
flash_read_page() is renamed flash_read_page_raw(). It reads a whole
page unbuffered and without ECC, and the name is needed for the FTL
primitive that does the ECC read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I3eb2d6f41d72b3239a0f9493e49532c242c588fa
BCHCTL bit 13 and the whole of BCHST were marked unknown. Both are
needed by any driver that reads NAND through the controller's ECC
engine: a read has to be able to report an uncorrectable sector, and MLC
parts want a refresh once a sector needs enough correction.
BCHCTL bit 13 ECC strength, clear = t=8, set = t=14 (both m=13,
poly 0x25af). Established by decoding both modes
against real media until the stored ECC bytes
reproduced.
BCHST bit 0 result valid
bit 2 error - uncorrectable when set together with bit 0
bits 6:3 number of corrected bit errors
Taken from the rk2705 NAND bootloader's ECC read loop:
tst r0,#1 ; tst r0,#4 both set -> sector uncorrectable
lsl r0,#25 ; lsr r0,#28 -> (BCHST >> 3) & 0xf, corrected bits
cmp r0,#3 >= 3 triggers a block refresh
The threshold agrees with MlcRefreshHook in the Samsung OF's flash.o, so
two independent firmwares say the same thing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I1e356d5510827b46035f1d7d4a2cc69f4e83f43f
commit_discard_idcache() invalidated both cache ways while running from
cached SDRAM. When the loop polling for the invalidate to finish starts on
a cache line of its own, it is fetched through the ways being invalidated
and the CPU takes a data abort, reported at the loop's branch. Whether it
crashed thus depended on where the linker put the function.
usb_storage calls it on every USB connect. On rk27generic a jpeg change
that grew clip_jpeg_fd by 8 bytes moved the loop onto a new line, and the
firmware crashed as the USB screen came up, with an empty backtrace.
Turn the cache controller off around the invalidate, as crt0.S does at
start-up. The cache is write-through, so no data is lost.
Tested on a generic rk2705 with the function padded so the poll loop starts
a new cache line: without this change it crashes at the first USB connect,
with it the device enumerates as a mass storage device. The normally
linked build works too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I319b811fdef30717999b60132000e70f410001fc
The rk27xx UDC driver was fixed and stress-tested on a generic rk2705
against the USB core of early 2026 - including a port to the control
request API of that time. Meanwhile the core took over the EP0 state
machine (usb_core_setup_received()) and endpoint allocation, and the
driver was converted to both without the fixes. This carries them over.
Transfers and resets (a mass-storage stress test fell off the bus after
10,000 - 27,000 operations without these):
- Transfers are set up with interrupts disabled: the interrupt handler
advances buf/cnt of the same endpoint for the next packet.
- ep_write()'s wait for TXFULL to clear is bounded by an iteration count.
It also runs in the interrupt handler, where current_tick never
advances, so a tick timeout spun forever.
- A bus reset cancels transfers - usb_drv_cancel_all_transfers() was
empty - instead of re-initialising the completion semaphores, which
loses a thread blocked on one for good; blocked senders are woken with
an error and the enabled endpoints NAKed and flushed. The reset handler
also calls usb_core_bus_reset(), which it never did.
- Blocking sends time out after a second and report the error.
- An ACK with no transfer armed (one cancelled by a reset) is ignored.
Configuration:
- The configuration number is DEV_INFO [11:8]; it was read as bits 10:7,
bit 7 being DEV_EN, so configuration 1 was reported as 2.
- The UDC completes SET_ADDRESS and SET_CONFIGURATION itself, raising no
interrupt, so udc_helper() - which reports them from DEV_INFO - also
runs from a tick task while the device is unconfigured. After a bus
reset of a configured device the host re-sends SET_CONFIGURATION and
goes straight to a bulk command that NAKs without interrupting: without
the tick, the device never came back.
EP0, with the core now running the control state machine:
- Right after connect the UDC reports one SETUP with both registers zero;
no host sends that, and it is ignored.
- A SETUP clears a stall, and ends - reported to the core as failed -
any EP0 transfer still in flight, which belongs to a request the host
abandoned; otherwise the core would wait for it forever. EP0 stall uses
the EP0 registers, not endpoints[0], a stub without registers.
- Control reads are clipped to wLength and end with a zero length packet
when a short answer fills whole packets.
- The core arms status stages with no buffer; they land in a dummy one.
- A status OUT arriving while the data IN is still going - the host took
less than was offered - ends the data stage too.
- At a bus reset EP0 transfers are dropped silently: the core resets its
own EP0 state.
Tested on a generic rk2705. The transfer, reset and configuration fixes
first against the USB core of early 2026: RAM-disk, NAND and SD stress
tests over USB mass storage, and usbreset recovery. Then the driver as it
is here, on the current core: enumeration, and a mass-storage stress test
of three LUNs at once (RAM disk, NAND, SD) that also passes after an eject
and a cold power cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I1b2d03ca8f688f2a0e7b28ead64b7ab050a0a9e8
The UDC was only ever connected from the interrupt handler, on CONN_INTR.
That needs a cable-insert edge after the stack is up, and there is none
when the cable is already in - booting with it plugged, or taking the
controller over from the ROM loader or hwstub, which leave it enumerated
as a different device. The device then never enumerated.
Connecting from usb_drv_init() would not do either: usb_core_init() calls
it first, before the class drivers are set up and before the core sets its
own state, so a fast host enumerated against state that was then
overwritten and the descriptor read timed out - depending on timing.
Add usb_drv_connect(), which drops off the bus, resets the PHY and
reconnects, and call it from usb_enable() after usb_core_init() returns.
Tested on a generic rk2705, loaded over hwstub with the cable in: the
device drops off, comes back and enumerates as Rockbox.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I372bd6a49594c00909ada0cc43b046178140e755
sd_read_sectors() and sd_write_sectors() take sd_mtx and power the
controller, then check the requested range and return -1 on failure -
leaving the mutex held and the controller on. Check the range first.
With no card present numblocks is 0, so every request takes that path.
Rockbox mutexes are recursive for the owning thread, so the first thread
to touch the SD drive keeps working, and every other thread that does
blocks forever.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I16c2be3bde5c62946fdedaabe115b6c4bc330467
The FM screen had no actions for the rk27xx generic keypad; give it those
its keymap already maps (menu, play, stop, exit) in radio.c.
The board's tuner is an RDA5807P. It keeps being driven as a TEA5767, in
the chip's compatible mode, as the original firmware does: tuning, seek
and the stereo indicator work so, and the RDA mode would bring nothing
here - this variant has no RDS. Say so next to CONFIG_TUNER.
The tuner's audio is on the codec's line input 1: only that line is
powered and mixed in while the radio plays (RK27XX_CODEC_FM_LINE 1). With
line 2 instead the radio is silent.
Tested on the rk27generic board: manual tuning, seek, stereo indicator and
audio.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I2135ee484705ff0fce6d88e73288d8ed2aec6c2d
audiohw_set_monitor() switched the output mixer from the DAC to both line
bypasses: voice and beeps went silent while the radio played, and a line
nobody listened to was mixed in, with its noise. Both line inputs were
also powered from start-up on.
- the target names the line its tuner is on, RK27XX_CODEC_FM_LINE (1 or
2); both are used where it does not say
- monitoring adds that line's bypass to the DAC instead of replacing it
- the line inputs stay in standby until monitored, and go back after
Only rk27generic uses the internal codec - the other rk27xx targets
have external DACs or codecs.
Tested on rk27generic: the radio plays through the monitored line, key
clicks stay audible over it, and playback is unaffected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I177fdf605e4d997159898c868b2186c9584eb19a
Bootloaders with this change are now able to boot rockbox binaries over
1MB. (1.5MB buffer led to bootloader hanging)
Change-Id: I540bd4146aaa7236df93e74f6f2c69aa32e4a874
- config: HAVE_RECORDING, sources microphone and FM, 8-48 kHz
- wm8751.c: the YP-CP3's own audiohw_set_recsrc(). The rk27xx cannot
send received samples straight back out, so what is heard of an input
goes through the codec's analog bypass - the input PGA into the output
mixers: the radio always, the microphone never. As in the original
firmware, the radio passes the PGA at +12 dB when only listened to,
and the microphone - mono, on RINPUT2 - gets +13 dB boost and is
recorded by the right ADC onto both channels (the original firmware's
noise gate is left out). The HD300's version assumes the microphone on
INPUT3 and headphones on OUT1, and switches OUT2 - the YP-CP3's
headphones - off.
- wm8751.c: the playback-only FM monitor added for the YP-CP3 goes; the
radio is routed by audiohw_set_recsrc() now, as on the HD300
- wm8751.h: DATSEL, which ADC feeds each channel of the output data
Only partly tested on hardware: FM radio still plays, and the recording
screen's peak meter follows the microphone. Not yet tested: a recorded
file played back, recording FM, recording gain range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I3c25f4333f765d98dddc2fd722c111f8493bbe77
pcm-rk27xx.c had no recording at all. Add it for targets with
HAVE_RECORDING: HDMA channel 1 moves the I2S Rx FIFO into the recording
buffer - hdreq 7, fixed source, incrementing destination, 32-bit inc8
slices, the mirror of the playback channel and the setup the Samsung
YP-CP3's original firmware uses. Playback keeps channel 0, so the two can
run together.
- HDMA_ISR holds both channels' masks and flags; playback used to write
all of it, clearing whatever the other channel had. Each channel now
updates only its own bits, and INT_HDMA serves each channel's count
down flag. Both channels start masked and clear.
- Recording cannot mask the shared interrupt, so pcm_rec_lock() defers a
completed buffer to pcm_rec_unlock() instead.
- In master mode the I2S Rx side runs only while recording: started with
nothing reading its FIFO it upsets playback. I2S_RXCTL gets the Tx
frame format, plus bit 24 as the YP-CP3's original firmware sets it.
- audio-rk27xx.c routes inputs through audiohw_set_recsrc() on targets
that record.
Only partly tested, on a YP-CP3: playback and FM radio still work after
the interrupt changes, and the recording screen's peak meter follows the
microphone, so samples arrive through the DMA. Not yet tested: playing
back a recorded file, recording FM, long recordings, recording while
playing. A known risk: a flag set by the hardware in the few cycles of
the read-modify-write of HDMA_ISR would be lost and stall that channel.
Other rk27xx targets build; their playback was not retested on hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Idc64bce8075d0fc628c29767fc33beda4fff4342
The YP-CP3's tuner is a Silicon Labs Si470x at I2C address 0x20, an
Si4703 by the RDS the original firmware enables. The firmware drives it
the Si470x way - writes from register 2, reads from 0x0A - starts its
oscillator with TEST1 = 0x8100 and a 500 ms wait before ENABLE, and has
no power or reset line for it.
- config: CONFIG_TUNER SI4700 with RDS, polled (no interrupt line
needed), and FM radio as an input source
- si4700.c: the YP-CP3 starts the oscillator as the Sansas do
- power-ypcp3.c: tuner power stubs - there is nothing to switch
- the rk27xx tuner I2C glue, and "radio" in the target's configure entry
- wm8751.c: audiohw_set_monitor() for a WM8750 target that plays the
radio but cannot record (rk27xx has no recording yet). The tuner, on
LINPUT1/RINPUT1, goes through the input PGA at +12 dB - as in the
original firmware - into the output mixers, the DAC staying in the mix.
The existing monitor routing lives in the recording code and switches
OUT2 off, which is the YP-CP3's headphone output.
Tested on a YP-CP3: stations tune and play at a sane level.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ic097fc85dbd7fb71fcc28c97a9d378fa09599e79
The YP-CP3 uses Wolfson WM8750. Headphones are wired on OUT2 and
a headphone amplifier enabled by GPIO F2, active high -
all RE from the original firmware.
The original firmware runs the codec as I2S master in its 12 MHz "USB
mode", fed a fixed 12 MHz MCLK, which puts 44.1 kHz at 44.118. Rockbox
instead makes the rk27xx the master and clocks the codec from the codec
PLL at exactly 256 fs (CODEC_SLAVE, as every other rk27xx target with an
external codec), so the codec's CLOCKING register is its normal-mode
256 fs setting at every rate. 96 kHz is left out: the WM8750 cannot take
it at 256 fs.
- config: HAVE_WM8750, CODEC_SLAVE, rates 8-48 kHz; the WM8750 has
hardware tone controls, so HAVE_SW_TONE_CONTROLS goes
- ypcp3/wmcodec-ypcp3.c: register writes over the rk27xx I2C driver
- wm8751.c: on the YP-CP3, power on and drive OUT2 instead of OUT1, set
the volume there, and switch the amplifier with the outputs
- english.lang: the YP-CP3 gets the bass/treble cutoff settings the
WM8750 brings
Tested on a YP-CP3: playback at 44.1 and 48 kHz on headphones, pitch and
volume correct.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I870038fb6a21a9c26b025e3df7c220fd925f49f6
Found in the original firmware by its command-in-address protocol
and bit-reversed data; the original firmware leaves it
in 12-hour mode, which the driver now handles.
Change-Id: I9f4147d3057aaa6aa498a1750cb4e746d69b1be6
The S-35390A keeps the hour either as 0-23 or - its default after a
power-on reset - as 0-11 plus a p.m. flag, selected by the 12/24 bit of
status register 1. The driver assumed 24-hour mode and never set it: it
masked the p.m. flag off, so on a chip left in 12-hour mode every
afternoon read as morning, and writing an afternoon time stored an
out-of-range hour.
Read the mode in rtc_init() and convert the hour both ways. The chip's
mode is left alone, as another firmware on the same player may depend on
it - the Samsung YP-CP3's original firmware runs it in 12-hour mode and
never touches the bit. If the status register cannot be read, the driver
keeps assuming 24-hour mode, as before.
Upstream removed the driver with its only users, the Meizu M3/M6 and
Samsung YP-S3 ports (1a33d7990a); the Samsung YP-CP3 needs it, so it
comes back here, with RTC_S35390A and its SOURCES entry.
Also pick the I2C header by CONFIG_I2C, so rk27xx targets can use the
driver; i2c_read()/i2c_write() have the same shape there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I54d8c8cfaa1b88237f6c4b08d2a510ec9fce8ec5
set_codec_freq()'s table of codec PLL settings names every rate from 8 to
96 kHz by its HW_FREQ_ index, which exists only for rates in the target's
HW_SAMPR_CAPS. Every CODEC_SLAVE target so far has all of them; a codec
without 96 kHz at 256 fs - the WM8750 - leaves HW_FREQ_96 undefined and
the table no longer builds.
Wrap each entry in HW_HAVE_xx_(), as the codec drivers' tables do. For
the existing targets the table is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ifab688ffa6f83c5319d686fcf40fc2b887c75d24
Scaffolding for a port, starting from the rk27xx generic target: configure
entry 146 (target id 117, model number 132), config/samsungypcp3.h, and
firmware/target/arm/rk27xx/ypcp3/.
- LCD: the rk27xx LCD interface (lcdif-rk27xx.c) with this panel's init
sequence, from RE. See g#1050. Looks like SPFD5420A 18-bit bus,
400x240 landscape. It differs from the generic panel's sequence in the
gamma curve and in not writing VCOM_HV1.
- Backlight: PD4 / PWM0 with the generic board's timing, which the hwstub
work found to be the same.
- Storage: the microSD slot. Card detect is PC7, active low,
as on the generic board.
- Keys: the YP-R0's key set - five-way yog, Back, Menu, Rec/User, Power -
so the target uses SAMSUNG_YPR0_PAD and its keymaps. Eight keys sit on
two resistor ladders on the LSADC, found in the original firmware and
measured on a unit:
channel 1 up 158, down 295, select 435, left 561, right 687
channel 2 menu 155, back 292, user 431
each key owning half a step either side of its reading;
power is GPIO C1, active high.
Builds as firmware and as bootloader. Status 3 (unusable) in builds.pm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iff4984023415f341ca7507af6b5b2b551201e953
USB mass storage writes ran at 0.03 MB/s in the firmware - to the NAND
and to the SD card alike - while the bootloader, with the same driver,
wrote the same NAND at 2 MB/s. The firmware adds a HID interface, and
without it writes ran at full speed.
The UDC's endpoints come in groups of three - bulk OUT, bulk IN,
interrupt IN: 1-3, 4-6 and so on - and allocation handed out the first
free endpoint of each type, so HID's interrupt endpoint 3 landed in the
group of mass storage's bulk endpoints 1 and 2. The host polls it every
16 ms, the idle endpoint NAKs, and each poll costs the group's bulk
traffic: writes advanced about one packet per poll. Polled every 125 us
instead, they all but stopped; moved to endpoint 6, in a group of its
own, they ran at 2.36 MB/s, as without HID.
Never give an interrupt endpoint a group with bulk endpoints in use, or
the other way round, whichever class asks first. Since which endpoints
are available then depends on what is already allocated, the driver
tracks the allocation itself - option 2 of usb_drv.h, as usb-designware
does - with its context in usb-rk27xx.h, which usb_core.c includes before
usb_drv.h. HID keeps working.
Tested on a generic rk2705 with ums_stress.py over USB mass storage to
the SD card: 0.03 MB/s with HID on endpoint 3, 2.36 MB/s with it on
endpoint 6 - the allocation this change produces for mass storage plus
HID. (Measured with the driver's earlier allocator, before the USB core
took allocation over; this carries the same rule into the new scheme.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ib80ed206a3705f2c76fc8457b5c0a54d60a46402
delay_nop() in lcd-rk27generic.c and udelay() in system-rk27xx.c both
count a register down with subs, but pass it as an input operand only:
asm volatile ("1: subs %[n], %[n], #1\n bne 1b" : : [n] "r" (cycles));
That tells GCC the register is unchanged afterwards, so it is free to
load a constant once and reuse the register for every later call with the
same argument. Current GCC does exactly that in lcd_display_init():
ldr r4, =10000 @ first delay_nop(10000)
subs r4, r4, #1 @ ... counts r4 down to 0
bl lcd_write_reg
subs r4, r4, #1 @ next delay_nop(10000): r4 not reloaded,
@ 0 - 1 wraps, loop runs 2^32 times
At 4 cycles per iteration and 200 MHz that is 85.9 s per wrapped call.
Nine calls wrap, so lcd_init() took 9 x 85.9 = 773 s. Measured on a
generic rk2705 with a tick timestamp either side of lcd_display_init():
77320 ticks at HZ=100, i.e. 773.2 s. With this fix it is 9 ticks, clear
loop included.
That is why lcd_init() looked like a hang on current toolchains while it
worked when the port was written. It also explains why no LCDC clock,
divider, gating or strobe-timing change had any effect: the time was
never spent in the LCD controller.
udelay() happens to work today because its count is computed at run time
on each call, but it has the same undefined behaviour and gets the same
fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Iecdd6cca0701c35bce3427f359a9a638b21291b5
A host that is done with a mass-storage device - "safely remove", eject,
udisksctl power-off - sends START STOP UNIT with the start bit clear, and
may cut power right after. usb_storage only marked the LUN ejected.
Storage drivers can still hold data in RAM at that point: a flash
translation layer keeps part-written pages until a later write completes
them, and HAVE_STORAGE_FLUSH exists so they can be committed - but only
shutdown and ROLO called it. On a device unplugged after a proper eject
and then losing power, that data was lost although the host had done
everything right.
Call storage_flush() on stop and on eject, on targets that define
HAVE_STORAGE_FLUSH. The SCSI handler runs in the USB thread, as do the
reads and writes, so the flush cannot race them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: I5b5811858c75dfb3ee89535ab59f4a19663945df
Thanks for the SET_ADDRESS rework. I think it leaves one case uncovered:
a host whose first request is SET_ADDRESS ends up with a device whose
interfaces are all numbered 0.
Two things still depend on the core seeing a request before the address arrives:
1. usb_core assigns interfaces and endpoints only on the first control
request it handles in DEFAULT. When SET_ADDRESS comes first, the driver
completes it and usb_core_set_address() moves the state to ADDRESS. So
allocate_interfaces_and_endpoints() never runs.
2. Under USB_DETECT_BY_REQUEST, usb.c enables the class drivers only
on a USB_TRANSFER_COMPLETION event. The SET_ADDRESS status stage now
completes inside the driver, so the drivers are still disabled when the
address arrives.
Change-Id: Ifacb8b07cbdefb0dee3d414c2257a50a08759f3d
A host can replace an unfinished control transfer with a new SETUP. ARC
and DesignWare flush EP0 without reporting completion callbacks, leaving
the core waiting for a data/status packet that will never arrive.
Explicitly notify the core after both directions and stale completion
bits have been cancelled. Return an abandoned data/status stage to READY.
If a queued or running handler still owns the request and data buffer,
keep that ownership and suppress its response; dispatch only the latest
replacement when it returns. A driver-owned SET_ADDRESS can also cancel
a deferred request without passing its SETUP to the core.
Do not overwrite an arbitrary busy state in usb_core_setup_received().
ARC cancellation is a prerequisite in the preceding patch; DesignWare
cancels both EP0 directions before notifying and rearming reception.
Change-Id: Ia8e0a68fe47ccab952168da24a2f76e311ca2b15
Handle SET_ADDRESS directly when each controller receives SETUP, before
passing other requests to the core. Cancel transfers, send the status
response in the driver and queue usb_core_notify_set_address(); the core
only updates its address/configuration state. Remove usb_drv_set_address
and avoid a request callback from the core back into the controller.
Keep status completions for this driver-owned request out of the core EP0
state machine. ARC writes DEVICEADDR and notifies after successful status
IN completion; other controllers retain their existing register/status
ordering or automatic address handling. Use IRQ-local operations on
DesignWare rather than helpers which unconditionally re-enable its IRQ.
Take the low seven bits of wValue; do not impose new validation on the
unspecified wIndex/wLength fields. Convert all eleven firmware backends;
the separate hwstub API is unchanged.
Change-Id: I7b96275df90c14ef4a9954f04ac6fdc004ec8d6c
Advertise only the implemented 32, 44.1 and 48 kHz stereo formats. Add
128-byte packets at 32 kHz and wrap the 44.1 kHz cadence at ten packets;
a uint8_t wrap at 256 otherwise emits extra samples every cycle.
Use the serialized UAC header length (including baInterfaceNr), maintain
the active alternate setting, accept alt 0 on non-streaming interfaces,
and retain the last valid sample rate when a SET_CUR request is rejected.
Change-Id: I41dab55fc0c7d2e6474492f97062e9838bc3aebf
A host may send a command after SET_CONFIGURATION while filesystem clients are still acknowledging the storage handover. Previously TEST UNIT READY could report no medium, while other commands could reach storage before exclusive access was granted.
Retain the first CBW until exclusive access is available. Notify the mass-storage class when handover completes, then execute the retained command and keep the OUT endpoint unarmed until its CSW. On BOT reset, discard the retained CBW and accept a new command.
This changes command handling while waiting for ownership. The policy for requesting, preserving and releasing exclusive storage is handled by a separate preparatory patch.
Change-Id: If82fc87160e9d7da930a3217445fea92e58dd1ec
Do not release and reacquire the disk when a host repeats configuration
or resets the bus before selecting mass storage again. This avoids
remounting local filesystems while the host still owns the disk.
After reset the filesystems remain unmounted until the host selects a
configuration which releases storage, or the cable is unplugged. A host
normally reconfigures promptly; one that never does leaves the disk
unavailable locally until unplug, rather than risking simultaneous access.
Change-Id: I6e296247dd2227e549105d85760613c1a81554a1
The USB core can accept a replacement SETUP while the previous control transfer is unfinished. Before dispatching that SETUP, flush both EP0 directions and discard their old completion bits so old descriptors cannot be reported as the new transfer. Leave non-control endpoints alone.
The register-stub test verifies both flushes, EP0 completion clearing and release of old EP0 waiters. This complements the separate generic control-request state-machine fix.
Change-Id: If595c7c0cfade7d855f113587621847926143606
audio_hard_stop() should be called *prior* to rolo_load(), and indeed
already is at every call site.
The reason to remove it from inside rolo_load() as opposed to the call
sites is because those already show a feedback splash while the audio
path is being shut down.
Change-Id: Ib6b995f7172c6a92599ace75245909730b2e941a
Move the iriver-specific functions for detecting flashed
Rockbox/OF images into system-iriver.c and remove the
HAVE_FLASHED_ROCKBOX define which is now redundant (all
targets using system-iriver.c enable it).
Copyright attribution on the new system-iriver.h header
is a best guess from Git history.
Change-Id: If1933f881a63fd517162ab9ca8f4a3007b997739