Skip to content

Commit f712eac

Browse files
committed
sunxi-fel: use RX DMA for H616 FEL writes
The H616 BROM FEL path copies received USB data byte-by-byte from the MUSB FIFO. Even after switching the controller to high-speed mode, this leaves large post-SPL DRAM writes far slower than the USB link can support. After SPL has initialized DRAM, install an H616-specific thunk for writes into the known DRAM window. The thunk builds the MMU remap it needs, hooks the BROM RX FIFO copy helper, and replaces it with a MUSB/USBC endpoint-DMA receive path. It routes the active RX endpoint DRQ through VEND0 before starting the internal DMA channel, so the FEL wire protocol stays unchanged. Coalesce full high-speed packets into bounded RX DMA requests and use larger progress chunks once the RX DMA thunk is installed. This reduces device-side FIFO-copy overhead and host-side progress overhead for large DRAM writes. Use a 192 KiB DMA request cap; 256 KiB requests can time out while the host is writing. Add the checked-in DMA thunk header to the sunxi-fel prerequisites and thunk documentation. This makes the binary rebuild when the generated header changes without making the sunxi-fel target regenerate thunk headers. Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
1 parent 972309b commit f712eac

11 files changed

Lines changed: 573 additions & 29 deletions

File tree

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,10 @@ PROGRESS := progress.c progress.h
140140
SOC_INFO := soc_info.c soc_info.h
141141
FEL_LIB := fel_lib.c fel_lib.h
142142
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
143+
FEL_THUNKS := thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-return-thunk.h
144+
FEL_THUNKS += thunks/h616-fel-rx-dma-thunk.h
143145

144-
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-return-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
146+
sunxi-fel: fel.c fit_image.c $(FEL_THUNKS) $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
145147
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
146148
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) -lfdt
147149

fel.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */
3737
static uint32_t uboot_size = 0; /* size of U-Boot binary */
3838
static bool enter_in_aarch64 = false;
3939
static bool fel_usb_high_speed_enabled = false;
40+
static bool fel_dram_ready = false;
4041

4142
#define FEL_REENUM_TIMEOUT_MS 5000
4243
#define FEL_REENUM_RETRY_MS 100
@@ -108,6 +109,12 @@ typedef struct image_header {
108109

109110
#define SID_STR_SIZE 36
110111

112+
#define DRAM_BASE 0x40000000
113+
#define DRAM_SIZE 0x80000000
114+
#define DRAM_END (DRAM_BASE + DRAM_SIZE)
115+
116+
static void aw_fel_install_rx_dma_thunk(feldev_handle *dev);
117+
111118
static uint32_t get_le32(const void *ptr)
112119
{
113120
uint32_t val;
@@ -183,6 +190,10 @@ double aw_write_buffer(feldev_handle *dev, void *buf, uint32_t offset,
183190

184191
double start = gettime();
185192

193+
if (fel_usb_high_speed_enabled && fel_dram_ready &&
194+
offset >= DRAM_BASE && offset < DRAM_END)
195+
aw_fel_install_rx_dma_thunk(dev);
196+
186197
aw_fel_write_buffer(dev, buf, offset, len, progress);
187198
return gettime() - start;
188199
}
@@ -409,9 +420,6 @@ static uint32_t fel_to_secure_svc_return_thunk[] = {
409420
#include "thunks/fel-to-secure-svc-return-thunk.h"
410421
};
411422

412-
#define DRAM_BASE 0x40000000
413-
#define DRAM_SIZE 0x80000000
414-
415423
uint32_t aw_read_arm_cp_reg(feldev_handle *dev, soc_info_t *soc_info,
416424
uint32_t coproc, uint32_t opc1, uint32_t crn,
417425
uint32_t crm, uint32_t opc2)
@@ -466,6 +474,25 @@ void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val)
466474
fel_writel_n(dev, addr, &val, 1);
467475
}
468476

477+
static void aw_fel_install_rx_dma_thunk(feldev_handle *dev)
478+
{
479+
const fel_rx_dma_thunk_info *thunk = dev->soc_info->fel_rx_dma_thunk;
480+
size_t i;
481+
482+
if (dev->rx_dma_patched || !thunk)
483+
return;
484+
485+
uint32_t thunk_buf[thunk->size / sizeof(*thunk->code)];
486+
487+
pr_info("Patching BROM RX FIFO copy to use DMA.\n");
488+
for (i = 0; i < sizeof(thunk_buf) / sizeof(*thunk_buf); i++)
489+
thunk_buf[i] = htole32(thunk->code[i]);
490+
aw_fel_write(dev, thunk_buf, thunk->addr, sizeof(thunk_buf));
491+
492+
aw_fel_execute(dev, thunk->addr + thunk->code[0]);
493+
dev->rx_dma_patched = true;
494+
}
495+
469496
static bool aw_fel_switch_usb_high_speed(feldev_handle *dev)
470497
{
471498
soc_info_t *soc_info = dev->soc_info;
@@ -941,7 +968,7 @@ void aw_restore_and_enable_mmu(feldev_handle *dev,
941968
};
942969

943970
pr_info("Setting write-combine mapping for DRAM.\n");
944-
for (i = (DRAM_BASE >> 20); i < ((DRAM_BASE + DRAM_SIZE) >> 20); i++) {
971+
for (i = (DRAM_BASE >> 20); i < (DRAM_END >> 20); i++) {
945972
/* Clear TEXCB bits */
946973
tt[i] &= ~((7 << 12) | (1 << 3) | (1 << 2));
947974
/* Set TEXCB to 00100 (Normal uncached mapping) */
@@ -1108,6 +1135,7 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
11081135
/* re-enable the MMU if it was enabled by BROM */
11091136
if (tt != NULL)
11101137
aw_restore_and_enable_mmu(dev, soc_info, tt);
1138+
fel_dram_ready = true;
11111139

11121140
return spl_len;
11131141
}
@@ -1640,7 +1668,7 @@ void usage(const char *cmd) {
16401668
" -h, --help Print this usage summary and exit\n"
16411669
" -v, --verbose Verbose logging\n"
16421670
" -p, --progress \"write\" transfers show a progress bar\n"
1643-
" --no-high-speed Disable high-speed USB\n"
1671+
" --no-high-speed Disable high-speed USB and fast writes\n"
16441672
" -l, --list Enumerate all (USB) FEL devices and exit\n"
16451673
" -d, --dev bus:devnum Use specific USB bus and device number\n"
16461674
" --sid SID Select device by SID key (exact match)\n"

fel_lib.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,17 @@ static void usb_error(int rc, const char *caption, int exitcode)
7070
* timeout, and "slow" transfers take place at approx. 64 KiB/sec - so we can
7171
* expect the maximum chunk being transmitted within 8 seconds or less.
7272
*/
73-
static const int AW_USB_MAX_BULK_SEND = 512 * 1024; /* 512 KiB per bulk request */
73+
static const int AW_USB_MAX_BULK_SEND = 512 * 1024;
74+
static const int AW_USB_PROGRESS_BULK_SEND = 128 * 1024;
75+
static const int AW_USB_FAST_PROGRESS_BULK_SEND = 1024 * 1024;
7476

77+
/*
78+
* Keep progress updates frequent enough for slow BROM PIO transfers. Once an
79+
* RX-DMA thunk is installed, 1 MiB chunks avoid host-side progress overhead.
80+
*/
7581
static void usb_bulk_send(libusb_device_handle *usb, int ep, const void *data,
76-
size_t length, bool progress)
82+
size_t length, size_t max_chunk, bool progress)
7783
{
78-
/*
79-
* With no progress notifications, we'll use the maximum chunk size.
80-
* Otherwise, it's useful to lower the size (have more chunks) to get
81-
* more frequent status updates. 128 KiB per request seem suitable.
82-
* (Worst case of "slow" transfers -> one update every two seconds.)
83-
*/
84-
size_t max_chunk = progress ? 128 * 1024 : AW_USB_MAX_BULK_SEND;
85-
8684
size_t chunk;
8785
int rc, sent;
8886
while (length > 0) {
@@ -187,7 +185,7 @@ static void aw_send_usb_request(feldev_handle *dev, int type, int length)
187185
};
188186
req.length2 = req.length;
189187
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
190-
&req, sizeof(req), false);
188+
&req, sizeof(req), AW_USB_MAX_BULK_SEND, false);
191189
}
192190

193191
static bool aw_send_usb_request_may_disconnect(feldev_handle *dev, int type,
@@ -239,9 +237,18 @@ static bool aw_usb_read_may_disconnect(feldev_handle *dev, void *data,
239237
static void aw_usb_write(feldev_handle *dev, const void *data, size_t len,
240238
bool progress)
241239
{
240+
size_t max_chunk = AW_USB_MAX_BULK_SEND;
241+
242+
if (progress) {
243+
if (dev->rx_dma_patched)
244+
max_chunk = AW_USB_FAST_PROGRESS_BULK_SEND;
245+
else
246+
max_chunk = AW_USB_PROGRESS_BULK_SEND;
247+
}
248+
242249
aw_send_usb_request(dev, AW_USB_WRITE, len);
243250
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
244-
data, len, progress);
251+
data, len, max_chunk, progress);
245252
aw_read_usb_response(dev);
246253
}
247254

fel_lib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ typedef struct {
3535
soc_name_t soc_name;
3636
soc_info_t *soc_info;
3737
bool disconnected;
38+
bool rx_dma_patched;
3839
} feldev_handle;
3940

4041
/* list_fel_devices() will return an array of this type */

soc_info.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@
2525
#include <stdio.h>
2626
#include <string.h>
2727

28+
static const uint32_t h616_fel_rx_dma_thunk_code[] = {
29+
#include "thunks/h616-fel-rx-dma-thunk.h"
30+
};
31+
32+
static const fel_rx_dma_thunk_info h616_fel_rx_dma_thunk = {
33+
.addr = 0x00022000,
34+
.code = h616_fel_rx_dma_thunk_code,
35+
.size = sizeof(h616_fel_rx_dma_thunk_code),
36+
};
37+
2838
/*
2939
* The FEL code from BROM in A10/A13/A20 sets up two stacks for itself. One
3040
* at 0x2000 (and growing down) for the IRQ handler. And another one at 0x7000
@@ -567,6 +577,7 @@ soc_info_t soc_info_table[] = {
567577
.ver_reg = 0x03000024,
568578
.usb_musb_base= 0x05100000,
569579
.fel_bulk_ep_addr = 0x00055100,
580+
.fel_rx_dma_thunk = &h616_fel_rx_dma_thunk,
570581
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
571582
.secure_boot_fuse_addr = 0x030060a0,
572583
.secure_boot_fuse_mask = 0xf,

soc_info.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define _SUNXI_TOOLS_SOC_INFO_H
2020

2121
#include <stdbool.h>
22+
#include <stddef.h>
2223
#include <stdint.h>
2324

2425
/* SoC version information, as retrieved by the FEL protocol */
@@ -70,6 +71,12 @@ typedef struct {
7071
uint32_t size_bits;
7172
} sid_section;
7273

74+
typedef struct {
75+
uint32_t addr;
76+
const uint32_t *code;
77+
size_t size;
78+
} fel_rx_dma_thunk_info;
79+
7380
#define SID_SECTION(_name, _offset, _size_bits) { \
7481
.name = _name, \
7582
.offset = _offset, \
@@ -142,6 +149,7 @@ typedef struct {
142149
uint32_t ver_reg; /* MMIO address of "Version Register" */
143150
uint32_t usb_musb_base;/* base address of the USB OTG controller */
144151
uint32_t fel_bulk_ep_addr; /* BROM FEL bulk endpoint state */
152+
const fel_rx_dma_thunk_info *fel_rx_dma_thunk;
145153
const watchdog_info *watchdog; /* Used for reset */
146154
bool sid_fix; /* Use SID workaround (read via register) */
147155
/* Use I$ workaround (disable I$ before first write to prevent stale thunk */

sunxi-fel.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Enable verbose logging.
4242
.sp
4343
.B \-\-no-high-speed
4444
.RS 4
45-
Disable automatic high-speed USB switching.
45+
Disable automatic high-speed USB switching and post-SPL fast DRAM writes.
4646
.RE
4747
.sp
4848
.B \-l, \-\-list

thunks/Makefile

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
# build "preprocessed" .h files for inclusion of ARM scratch code
33
#
44

5-
SPL_THUNK := fel-to-spl-thunk.h
6-
SPL_THUNK += fel-to-secure-svc-return-thunk.h
5+
FEL_EXEC_THUNKS := fel-to-spl-thunk.h
6+
FEL_EXEC_THUNKS += fel-to-secure-svc-return-thunk.h
7+
FEL_EXEC_THUNKS += h616-fel-rx-dma-thunk.h
78
THUNKS := clrsetbits.h
89
THUNKS += memcpy.h
910
THUNKS += readl_writel.h
1011
THUNKS += rmr-thunk.h
1112
THUNKS += sid_read_root.h
1213

13-
all: $(SPL_THUNK) $(THUNKS)
14+
all: $(FEL_EXEC_THUNKS) $(THUNKS)
1415
# clean up object files afterwards
1516
rm -f *.o
1617

@@ -25,9 +26,10 @@ OBJDUMP := $(CROSS_COMPILE)objdump
2526

2627
AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk
2728

28-
# These thunks require the old output format. The "style" variable for
29-
# awk controls this, and causes the htole32() conversion to be omitted.
30-
$(SPL_THUNK): %.h: %.S FORCE
29+
# These thunks are copied into a uint32_t buffer and byte-swapped by fel.c.
30+
# The "style" variable for awk controls this, and causes the htole32()
31+
# conversion to be omitted.
32+
$(FEL_EXEC_THUNKS): %.h: %.S FORCE
3133
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
3234
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@
3335

thunks/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ usually via `sunxi-fel`.
88

99
Normally you don't need to change or (re)build anything within this folder.
1010
Currently our main build process (via the parent directory's _Makefile_)
11-
includes `fel-to-spl-thunk.h` and `fel-to-secure-svc-return-thunk.h` directly.
12-
Other _.h_ files are provided **just for reference**. The main purpose of this
13-
folder is simply keeping track of _.S_ sources, to help with possible future
14-
maintenance of the various code snippets.
11+
includes `fel-to-spl-thunk.h`, `fel-to-secure-svc-return-thunk.h`, and
12+
`h616-fel-rx-dma-thunk.h` directly. Other _.h_ files are provided **just for
13+
reference**. The main purpose of this folder is simply keeping track of _.S_
14+
sources, to help with possible future maintenance of the various code snippets.
1515

1616
Please note that any files lacking explicit license information are intended
1717
to be covered by the project's [overall license](../LICENSE.md) (GPLv2).

0 commit comments

Comments
 (0)