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
This commit is contained in:
Marcin Bukat 2026-09-30 16:42:20 +02:00
parent 70c546113d
commit 0e1952a4d1

View file

@ -208,11 +208,21 @@ static void cache_invalidate_way(int way)
void commit_discard_idcache(void)
{
int old_irq = disable_irq_save();
unsigned long devid = DEVID;
/* Invalidate with the cache off, as crt0 does. This code runs from
* cached SDRAM: invalidating the ways while fetching through them fails
* when the poll loop starts on a cache line of its own, depending on
* where the linker happened to put it. The cache is write-through, so
* nothing is lost by turning it off. */
DEVID = devid & ~(1UL << 31);
cache_invalidate_way(0);
cache_invalidate_way(1);
DEVID = devid;
restore_irq(old_irq);
}
void commit_discard_dcache (void) __attribute__((alias("commit_discard_idcache")));