Commit graph

12373 commits

Author SHA1 Message Date
Marcin Bukat
d8f099f937 rk27xx: restore the missing 0xd5 NAND device code
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
2026-10-01 10:28:03 +02:00
Marcin Bukat
c3356ac35f rk27xx: build the raw NAND layer
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
2026-10-01 10:28:03 +02:00
Marcin Bukat
53bc6abc97 rk27xx: fill in the NANDC BCH register definitions
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
2026-10-01 10:28:03 +02:00
Marcin Bukat
0e1952a4d1 rk27xx: invalidate the cache with the cache controller off
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
2026-09-30 16:47:48 +02:00
Marcin Bukat
70c546113d rk27xx: bring the hardware-tested UDC fixes to the current USB core
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
2026-09-30 15:28:54 +02:00
Marcin Bukat
475b4629b6 rk27xx: attach to the bus only once the USB core is set up
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
2026-09-30 15:23:08 +02:00
Marcin Bukat
79329c8f7c rk27xx: don't return from SD transfers with the lock held
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
2026-09-30 14:43:00 +02:00
Marcin Bukat
89e9ade855 rk27generic: FM radio screen keys, tuner noted as an RDA5807P
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
2026-09-30 08:07:47 -04:00
Marcin Bukat
19caf27567 rk27xx codec: mix in only the tuner's line, keep the DAC
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
2026-09-30 08:07:15 -04:00
Solomon Peachy
bbe37187d8 imx233: Increase bootloader's firmware buffer size to 2MB
Bootloaders with this change are now able to boot rockbox binaries over
1MB.  (1.5MB buffer led to bootloader hanging)

Change-Id: I540bd4146aaa7236df93e74f6f2c69aa32e4a874
2026-09-29 15:15:45 -04:00
Marcin Bukat
3689292b28 YP-CP3: recording from the microphone and FM radio
- 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
2026-09-29 09:52:13 +02:00
Marcin Bukat
a33b0c7360 rk27xx: recording over I2S and HDMA channel 1
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
2026-09-29 09:50:20 +02:00
Marcin Bukat
c922ef98c3 YP-CP3: Si4703 FM radio
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
2026-09-29 09:47:51 +02:00
Marcin Bukat
a35f344eef YP-CP3: WM8750 audio
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
2026-09-29 09:34:02 +02:00
Marcin Bukat
f7655e226c Samsung YP-CP3: Seiko S-35390A on I2C as RTC
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
2026-09-29 09:30:23 +02:00
Marcin Bukat
2190a31dac Samsung YP-CP3 define power hold pin
Change-Id: I9fbfa00b37d3f2194df36f808fa01e2a2eab0e59
2026-09-29 09:29:38 +02:00
Marcin Bukat
0dc08e53a9 S-35390A RTC: bring the driver back, handle 12-hour mode
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
2026-09-29 09:20:12 +02:00
Marcin Bukat
8a53c15d1c rk27xx: only list PLL settings for the rates a target has
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
2026-09-29 09:06:36 +02:00
Marcin Bukat
98f162b212 New target: Samsung YP-CP3 (rk27xx)
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
2026-09-29 08:53:27 +02:00
Marcin Bukat
d3893ead3a rk27xx: keep interrupt and bulk endpoints in separate groups
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
2026-09-28 16:55:21 +02:00
Marcin Bukat
4dcb5bae9d rk27xx: fix busy-wait asm that modifies an input-only operand
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
2026-09-28 16:22:13 +02:00
Marcin Bukat
9bcdd30293 usb_storage: flush storage when the host stops or ejects the unit
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
2026-09-28 08:39:24 +02:00
Solomon Peachy
2745e0b973 FS#14011: Handle usb hosts whose first request is SET_ADDRESS (Anthony Fletcher)
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
2026-09-27 19:36:21 -04:00
Paul Sauro
9aa2d7fe94 usb: accept replacement SETUP after an abandoned control transfer
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
2026-09-27 09:19:39 -04:00
Paul Sauro
841007dfa1 usb: let controller drivers handle SET_ADDRESS requests
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
2026-09-26 13:23:01 -04:00
Paul Sauro
3bd18f5a44 usb iap: correct sample rate descriptors and packet cadence
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
2026-09-25 13:49:30 -04:00
Paul Sauro
e2ee665cce usb storage: defer commands until exclusive storage handover completes
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
2026-09-25 07:49:36 -04:00
Paul Sauro
2b664d6025 usb: preserve exclusive storage ownership across repeated configuration and reset
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
2026-09-25 07:48:31 -04:00
Paul Sauro
d1fab121f8 usb arc: flush both EP0 directions when SETUP replaces a transfer
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
2026-09-24 09:55:26 -04:00
Solomon Peachy
9101f35519 ROLO: Get rid of redundant call to audio_hard_stop()
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
2026-09-23 16:52:01 -04:00
Aidan MacDonald
c6abf3382a firmware: move iriver flash helper functions into target tree
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
2026-09-23 13:44:45 -04:00
Aidan MacDonald
190822f261 firmware: limit system_memory_guard() to coldfire targets
Only Coldfire targets have ever implemented this. Gate it
behind CPU_COLDFIRE so the stub functions won't be needed
in other targets.

Change-Id: I507952c40a04d813a40296142a6eba1df24b0a68
2026-09-23 13:12:48 -04:00
Aidan MacDonald
d27af08ff6 hw_h264: remove redundant ifdef guards
Change-Id: I5fa1abc1226ec5a35888713da83467a71a3c4caf
2026-09-23 16:22:18 +01:00
Aidan MacDonald
29eef25ac7 hw_h264: use standard rockbox copyright headers
Some files were not using the standard header with the
Rockbox logo. Add this and move any technical notes into
a separate comment.

Change-Id: Idaac932cd56154c7b785ba0e6c0231a21878f786
2026-09-23 16:22:18 +01:00
Paul Sauro
b1385d831e usb arc: reset endpoint data toggles when clearing halt
CLEAR_FEATURE(ENDPOINT_HALT) must reset the selected non-control endpoint data toggle as well as removing STALL. Set the corresponding ARC toggle-reset bit when clearing the halt, including when the endpoint is already unstalled. EP0 keeps its SETUP-controlled toggle handling.

The register-stub test checks IN/OUT independently and verifies that EP0 does not receive a non-control toggle reset.

Change-Id: I18bce488ec250336512bfda4db6ff369d21d9180
2026-09-23 08:37:39 -04:00
Paul Sauro
63978def70 usb arc: discard stale transfers and audio work on bus reset
If RESET and IOC/SOF are reported together, the old interrupt order can refill audio or report completions from descriptors invalidated by the reset. Handle RESET first and return without dispatching those stale events. Disable SOF refill and stop the batch ring when resetting the controller.

The register-stub regression exercises simultaneous RESET, IOC and SOF and checks that no old completion or refill is dispatched.

Change-Id: Iac98df59aea30dfee155302f7ad31c6ba9902975
2026-09-23 08:37:23 -04:00
Solomon Peachy
44e7c009ae as3525: Increase bootloader firmware buffer to 2MB
Modern rockbox builds are just larger than 1MB on the Sansa Fuze/Fuzev2,
which exceeds the reserved space to load the firmware.

The other AMS targets are a little under 1MB, so their time is likely
to come soon.  Bump the buffer to 2MB so we never have to worry about
this again.

Change-Id: Ie6bf998596f5e55f5cc8e576a2a22639344306a4
2026-09-22 11:46:28 -04:00
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
Andrew Rice
413f17b8ce ipodnano3g: mark the 4-CE Toshiba BA94D598 row checked
A contributor's check archive matches the row exactly and passes both
host tests clean: test_ftl agrees with the oracle on all 3,964,928
sectors, test_crash survives 100 power cuts with 0 sectors wrong.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I7c651c94332e0a04686ce8855a274735ae39546c
2026-09-20 21:02:05 -04:00
Andrew Rice
00829f2258 ipodnano3g: mark four contributors' chips checked
Four check archives (B614D5EC x2, A5D5D589 x2, A5D5D589 x4, 3E94D589 x2)
all match their table rows and pass test_crash clean. Three pass test_ftl
outright; A5D5D589 x2 disagrees with the oracle on two logical pages,
traced to _FTLRestore's own tie-break between two competing logs
(verified instruction-for-instruction against osos 1.1.3) - Apple's own
firmware would resolve the same medium the same way, so this is not a
defect. test_ftl.c gets a note explaining the false positive for future
archives that hit it.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: If8de0d6b1175981a292c5c45fbc092d5a6e0891a
2026-09-20 21:01:48 -04:00
Andrew Rice
01925dd5d0 ipodnano3g: enable the 2-CE Micron A5D5D52C NAND
A contributor's check archive for a 2-chip-enable A5D5D52C unit matches
the row exactly and replays clean against the host FTL suite. The row
moves in with the validated chips on this evidence alone - no on-device
write test has been run.

Testing evidence: utils/ipodnano3g/RESULTS.md.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: Ia9e00c0e880b10785da3ab52bc98dc93e94acfaf
2026-09-20 21:01:09 -04:00
Paul Sauro
404e2c7626 usb: validate configuration descriptor indices and failed drivers
Reject the first out-of-range configuration index and omit class descriptors for drivers whose initialization failed.

Change-Id: I1f74dcceb69f19b52650aa6ae3f38331e320b151
2026-09-20 18:14:54 -04:00
Paul Sauro
93b49594d5 usb storage: fix BOT residue, rejected commands and ATA IDENTIFY
Account only completed data-stage bytes in the CSW residue. Halt the requested data pipe before returning failed status for an unsupported command with a data phase.

Send ATA IDENTIFY from the initialized transfer buffer rather than a stale READ/WRITE buffer pointer, and reject unsupported WRITE BUFFER variants.

Change-Id: I9855f56b3555c2a69724681cb028e57f733dae0e
2026-09-20 18:14:11 -04:00
Andrew Rice
76f8925d23 ipodnano3g: power, RTC, backlight, battery and audio
Fills in the stubs the Nano 3G port was left with, taking each from how
the original firmware drives the same hardware.

Testing evidence: firmware/target/arm/s5l8702/ipodnano3g/TESTING.md.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: Iea5314769502f5941a176600869591756c5e3233
2026-09-20 08:53:53 -04:00
Andrew Rice
e4c010be98 ipodnano3g: NAND check image for validating other chips
Rockbox only drives NAND parts proven on hardware, and this tree has one
model to prove them on. This is the image that lets an owner of another
Nano 3G supply what validating theirs takes: a bootloader built with
-DNAND_CHECK and run from DFU, which never touches the NOR flash or
writes to the NAND.

Testing evidence: firmware/target/arm/s5l8702/ipodnano3g/TESTING.md.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I46328b69f8011337790f35a195de97b5bcf347a5
2026-09-19 21:26:45 -04:00
Andrew Rice
34a18e7616 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
2026-09-19 20:08:03 -04:00
Aidan MacDonald
3a57f2f721 Add "rbfs" prefix to native filesystem functions
Most of the churn here occurs because 'filesize' is one of
the redefined filesystem functions, but some of the structs
used by the native FS code also include a 'filesize' member
variable which gets renamed by the macro in some but not all
source files.

It's easier to rename 'filesize()' to 'ffilesize()' rather
than try to clean up the macro mess or renaming the struct
members.

There is weirdness with root_realpath() which now breaks on
native builds because it was assumed to be unprefixed there.
dir_get_info() was unprefixed everywhere but this just seems
inconsistent; make it follow the FS_PREFIX() convention too.

Change-Id: Ic3700c6234ea45f32679c1a8429d70fdb8f4088a
2026-09-17 16:22:31 -04:00
Andrew Rice
6958f6638e usb_storage: report read-only drives as write protected
A target that defines HAVE_STORAGE_READONLY provides storage_readonly(),
and USB mass storage reports such a drive write protected: MODE SENSE
sets the WP bit, and WRITE(10) and WRITE(16) fail with DATA PROTECT /
WRITE PROTECTED before taking data, so a host mounts it read-only
instead of failing each write as a medium error. Only NAND storage wires
it up so far.

The iPod Nano 3G, added in the changes that follow, defines it: its FTL
mounts read-only in the bootloader and after a failed commit, and the
NAND check image is always read-only.

Built for the Nano 3G (bootloader, check image, firmware) without new
warnings. On hardware, Linux and Windows hosts have read the
always-read-only check image over USB; no host write has been made to a
read-only drive.

AI provenance: developed with Claude Opus 5 (Anthropic), used through
Claude Code. The model wrote most of the code and this message under
Andrew Rice's direction. Any hardware testing described above was
carried out by Andrew Rice, who is responsible for this change.

Change-Id: Idb34def2d588047e5dced9bac1521c00b37dd5e5
2026-09-17 13:18:58 +10:00
Andrew Rice
66bc0728d5 ipodnano3g: preserve PMU register 0x10 bit 2, which the NAND needs
pmu_preinit() masked register 0x10 with 0xdb, which clears bits 2 and
5. With bit 2 clear the NAND chip does not answer and nand_init() fails.
Masking with 0xdf clears only bit 5 and leaves bit 2 alone.

Measured on a 4GB Nano 3G. Every build that had worked carried debug
PMU reads, i.e. extra I2C delay, so delay was the confound to rule out
and each mask was tried with and without it:

  0xdb, no extra delay     dead
  0xdb, extra delay        dead
  0xdf, extra delay        reads (3 runs)
  0xdf, no extra delay     reads

One early 0xdf build without instrumentation failed once (rc=-1). That
has not been explained; the final validation run is the same
configuration and reads correctly.

Reading the register back after pmu_preinit() gives 0xdf. The new mask
keeps bit 2 and "| 0x8" only sets bit 3, so bit 2 is set as the BootROM
left it - the old mask was clearing it. Bit 5 is hidden by the mask, so
what the ROM leaves there is not known.

What bit 2 does is not established. The old comment guessed "bit4 is
related to NAND, LDO_0x15 on/off"; the register does sit among the NAND
supply settings (0x15 reads back the value commented as Vnand), but no
datasheet was consulted, so the new comment states only the observed
effect.

AI provenance: developed with Claude Opus 5 (Anthropic), used through
Claude Code. The model wrote most of the code and this message under
Andrew Rice's direction. Any hardware testing described above was
carried out by Andrew Rice, who is responsible for this change.

Change-Id: Iee73f876b7b6a5c85e09f8bf1c5ac174a49409e0
2026-09-16 08:14:24 -04:00
Andrew Rice
2adcfa08cf s5l8702: add the Nano 3G's flash controller registers
The S5L8702 has two flash memory controllers; the BootROM uses the first
at 0x38a00000 and addresses the second at +0x400. FMC_BASE was defined
for the S5L8700 and S5L8701 only.

Also add the registers Apple's NANDReadPage (BootROM 0x20009910) uses
that are not in the existing S5L8700 set, and three FMCSTAT bits. Their
real names are unknown, so they are named for what they were observed to
do and the comments say which are inferred rather than measured.

No functional change: this header only adds definitions.

AI provenance: developed with Claude Opus 5 (Anthropic), used through
Claude Code. The model wrote most of the code and this message under
Andrew Rice's direction. Any hardware testing described above was
carried out by Andrew Rice, who is responsible for this change.

Change-Id: Ib8c808128abc9a117cb811fe1fe47bffa4ec0abe
2026-09-16 08:14:24 -04:00