Skip to content

Commit 0e853b3

Browse files
committed
sunxi-fel: use RX DMA for FEL writes
The newer 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 a parameterized RX DMA 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. Keep the common thunk code in fel.c and put the SoC-specific hook, translation-table and shadow-page addresses in the SoC table. Enable the path for SoCs whose BROM dumps contain the same FIFO-copy helper ABI and compatible MUSB register layout: A33, A64, H3, H5, A63, H6, H616, V853, V5, A523 and A133. 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 90002a3 commit 0e853b3

11 files changed

Lines changed: 713 additions & 28 deletions

File tree

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,10 @@ PROGRESS := progress.c progress.h
163163
SOC_INFO := soc_info.c soc_info.h
164164
FEL_LIB := fel_lib.c fel_lib.h
165165
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
166+
FEL_THUNKS := thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-smc-thunk.h
167+
FEL_THUNKS += thunks/fel-rx-dma-thunk.h
166168

167-
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h thunks/fel-to-secure-svc-smc-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
169+
sunxi-fel: fel.c fit_image.c $(FEL_THUNKS) $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
168170
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LIBFDT_CFLAGS) $(LDFLAGS) -o $@ \
169171
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) $(LIBFDT_LIBS)
170172

fel.c

Lines changed: 51 additions & 4 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
@@ -109,6 +110,12 @@ typedef struct image_header {
109110

110111
#define SID_STR_SIZE 36
111112

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

185192
double start = gettime();
186193

194+
if (fel_usb_high_speed_enabled && fel_dram_ready &&
195+
offset >= DRAM_BASE && offset < DRAM_END)
196+
aw_fel_install_rx_dma_thunk(dev);
197+
187198
aw_fel_write_buffer(dev, buf, offset, len, progress);
188199
return gettime() - start;
189200
}
@@ -410,8 +421,9 @@ static uint32_t fel_to_secure_svc_smc_thunk[] = {
410421
#include "thunks/fel-to-secure-svc-smc-thunk.h"
411422
};
412423

413-
#define DRAM_BASE 0x40000000
414-
#define DRAM_SIZE 0x80000000
424+
static uint32_t fel_rx_dma_thunk[] = {
425+
#include "thunks/fel-rx-dma-thunk.h"
426+
};
415427

416428
uint32_t aw_read_arm_cp_reg(feldev_handle *dev, soc_info_t *soc_info,
417429
uint32_t coproc, uint32_t opc1, uint32_t crn,
@@ -467,6 +479,40 @@ void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val)
467479
fel_writel_n(dev, addr, &val, 1);
468480
}
469481

482+
static void aw_fel_install_rx_dma_thunk(feldev_handle *dev)
483+
{
484+
const fel_rx_dma_info *dma = &dev->soc_info->fel_rx_dma;
485+
uint32_t params[6], param_offset;
486+
size_t i;
487+
488+
if (dev->rx_dma_patched || !dma->thunk_addr)
489+
return;
490+
491+
param_offset = fel_rx_dma_thunk[1];
492+
if (param_offset % sizeof(*fel_rx_dma_thunk) ||
493+
param_offset + sizeof(params) > sizeof(fel_rx_dma_thunk))
494+
pr_fatal("RX DMA thunk: bad parameter offset 0x%x\n",
495+
param_offset);
496+
497+
uint32_t thunk_buf[ARRAY_SIZE(fel_rx_dma_thunk)];
498+
499+
pr_info("Patching BROM RX FIFO copy to use DMA.\n");
500+
for (i = 0; i < sizeof(thunk_buf) / sizeof(*thunk_buf); i++)
501+
thunk_buf[i] = htole32(fel_rx_dma_thunk[i]);
502+
params[0] = htole32(dma->l1_tt_addr);
503+
params[1] = htole32(dma->l2_tt_addr);
504+
params[2] = htole32(dma->brom_hook_addr);
505+
params[3] = htole32(dma->brom_hook_shadow_addr);
506+
params[4] = htole32(dev->soc_info->usb_musb_base);
507+
params[5] = htole32(dma->dma_max_len);
508+
memcpy((uint8_t *)thunk_buf + param_offset, params, sizeof(params));
509+
510+
aw_fel_write(dev, thunk_buf, dma->thunk_addr, sizeof(thunk_buf));
511+
512+
aw_fel_execute(dev, dma->thunk_addr + fel_rx_dma_thunk[0]);
513+
dev->rx_dma_patched = true;
514+
}
515+
470516
static bool aw_fel_switch_usb_high_speed(feldev_handle *dev)
471517
{
472518
soc_info_t *soc_info = dev->soc_info;
@@ -952,7 +998,7 @@ void aw_restore_and_enable_mmu(feldev_handle *dev,
952998
};
953999

9541000
pr_info("Setting write-combine mapping for DRAM.\n");
955-
for (i = (DRAM_BASE >> 20); i < ((DRAM_BASE + DRAM_SIZE) >> 20); i++) {
1001+
for (i = (DRAM_BASE >> 20); i < (DRAM_END >> 20); i++) {
9561002
/* Clear TEXCB bits */
9571003
tt[i] &= ~((7 << 12) | (1 << 3) | (1 << 2));
9581004
/* Set TEXCB to 00100 (Normal uncached mapping) */
@@ -1119,6 +1165,7 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
11191165
/* re-enable the MMU if it was enabled by BROM */
11201166
if (tt != NULL)
11211167
aw_restore_and_enable_mmu(dev, soc_info, tt);
1168+
fel_dram_ready = true;
11221169

11231170
return spl_len;
11241171
}
@@ -1651,7 +1698,7 @@ void usage(const char *cmd) {
16511698
" -h, --help Print this usage summary and exit\n"
16521699
" -v, --verbose Verbose logging\n"
16531700
" -p, --progress \"write\" transfers show a progress bar\n"
1654-
" --no-high-speed Disable high-speed USB\n"
1701+
" --no-high-speed Disable high-speed USB and fast writes\n"
16551702
" -l, --list Enumerate all (USB) FEL devices and exit\n"
16561703
" -d, --dev bus:devnum Use specific USB bus and device number\n"
16571704
" --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: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <stdio.h>
2626
#include <string.h>
2727

28+
#define FEL_RX_DMA_MAX_LEN 0x00030000
29+
2830
/*
2931
* The FEL code from BROM in A10/A13/A20 sets up two stacks for itself. One
3032
* at 0x2000 (and growing down) for the IRQ handler. And another one at 0x7000
@@ -390,6 +392,14 @@ soc_info_t soc_info_table[] = {
390392
.sid_sections = generic_2k_sid_maps,
391393
.usb_musb_base= 0x01c19000,
392394
.fel_endpoint_table_addr = 0x00006ed8,
395+
.fel_rx_dma = {
396+
.thunk_addr = 0x0004e000,
397+
.l1_tt_addr = 0x00048000,
398+
.l2_tt_addr = 0x0004c000,
399+
.brom_hook_addr = 0xffff2588,
400+
.brom_hook_shadow_addr = 0x0004d000,
401+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
402+
},
393403
.watchdog = &wd_h3_compat,
394404
},{
395405
.soc_id = 0x1689, /* Allwinner A64 */
@@ -405,6 +415,14 @@ soc_info_t soc_info_table[] = {
405415
.rvbar_reg = 0x017000A0,
406416
.usb_musb_base= 0x01c19000,
407417
.fel_endpoint_table_addr = 0x00016ecc,
418+
.fel_rx_dma = {
419+
.thunk_addr = 0x00022000,
420+
.l1_tt_addr = 0x0002c000,
421+
.l2_tt_addr = 0x00031000,
422+
.brom_hook_addr = 0x00002670,
423+
.brom_hook_shadow_addr = 0x00030000,
424+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
425+
},
408426
/* Check L.NOP in the OpenRISC reset vector */
409427
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
410428
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
@@ -456,6 +474,14 @@ soc_info_t soc_info_table[] = {
456474
.sid_sections = h3_sid_maps,
457475
.usb_musb_base= 0x01c19000,
458476
.fel_endpoint_table_addr = 0x00006ecc,
477+
.fel_rx_dma = {
478+
.thunk_addr = 0x0000e000,
479+
.l1_tt_addr = 0x00008000,
480+
.l2_tt_addr = 0x0000c000,
481+
.brom_hook_addr = 0xffff2680,
482+
.brom_hook_shadow_addr = 0x0000d000,
483+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
484+
},
459485
/* Check L.NOP in the OpenRISC reset vector */
460486
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
461487
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
@@ -497,6 +523,14 @@ soc_info_t soc_info_table[] = {
497523
.rvbar_reg = 0x017000A0,
498524
.usb_musb_base= 0x01c19000,
499525
.fel_endpoint_table_addr = 0x00016ecc,
526+
.fel_rx_dma = {
527+
.thunk_addr = 0x00022000,
528+
.l1_tt_addr = 0x0002c000,
529+
.l2_tt_addr = 0x00031000,
530+
.brom_hook_addr = 0x0000205c,
531+
.brom_hook_shadow_addr = 0x00030000,
532+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
533+
},
500534
/* Check L.NOP in the OpenRISC reset vector */
501535
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
502536
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
@@ -526,6 +560,14 @@ soc_info_t soc_info_table[] = {
526560
.rvbar_reg = 0x09010040,
527561
.usb_musb_base= 0x05100000,
528562
.fel_endpoint_table_addr = 0x00026ecc,
563+
.fel_rx_dma = {
564+
.thunk_addr = 0x00022000,
565+
.l1_tt_addr = 0x00038000,
566+
.l2_tt_addr = 0x0003d000,
567+
.brom_hook_addr = 0x000021ac,
568+
.brom_hook_shadow_addr = 0x0003c000,
569+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
570+
},
529571
.watchdog = &wd_h6_compat,
530572
},{
531573
.soc_id = 0x1728, /* Allwinner H6 */
@@ -541,6 +583,14 @@ soc_info_t soc_info_table[] = {
541583
.rvbar_reg = 0x09010040,
542584
.usb_musb_base= 0x05100000,
543585
.fel_endpoint_table_addr = 0x00026ecc,
586+
.fel_rx_dma = {
587+
.thunk_addr = 0x00022000,
588+
.l1_tt_addr = 0x00038000,
589+
.l2_tt_addr = 0x0003d000,
590+
.brom_hook_addr = 0x00002300,
591+
.brom_hook_shadow_addr = 0x0003c000,
592+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
593+
},
544594
/* Check L.NOP in the OpenRISC reset vector */
545595
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
546596
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
@@ -585,6 +635,14 @@ soc_info_t soc_info_table[] = {
585635
.ver_reg = 0x03000024,
586636
.usb_musb_base= 0x05100000,
587637
.fel_endpoint_table_addr = 0x000550f0,
638+
.fel_rx_dma = {
639+
.thunk_addr = 0x00022000,
640+
.l1_tt_addr = 0x0004c000,
641+
.l2_tt_addr = 0x00051000,
642+
.brom_hook_addr = 0x0000a17c,
643+
.brom_hook_shadow_addr = 0x00050000,
644+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
645+
},
588646
.needs_smc_workaround_if_zero_word_at_addr = 0x03006240,
589647
.secure_boot_fuse_offset = 0xa0,
590648
.smc_workaround = SMC_WORKAROUND_SECURE_SVC_SMC_THUNK,
@@ -617,6 +675,14 @@ soc_info_t soc_info_table[] = {
617675
.sid_sections = generic_2k_sid_maps,
618676
.usb_musb_base= 0x04100000,
619677
.fel_endpoint_table_addr = 0x000402e8,
678+
.fel_rx_dma = {
679+
.thunk_addr = 0x00022000,
680+
.l1_tt_addr = 0x00038000,
681+
.l2_tt_addr = 0x0003d000,
682+
.brom_hook_addr = 0x0000aff0,
683+
.brom_hook_shadow_addr = 0x0003c000,
684+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
685+
},
620686
.icache_fix = true,
621687
.watchdog = &wd_v853_compat,
622688
},{
@@ -645,6 +711,14 @@ soc_info_t soc_info_table[] = {
645711
.sid_sections = generic_2k_sid_maps,
646712
.usb_musb_base= 0x05100000,
647713
.fel_endpoint_table_addr = 0x00026efc,
714+
.fel_rx_dma = {
715+
.thunk_addr = 0x00022000,
716+
.l1_tt_addr = 0x00038000,
717+
.l2_tt_addr = 0x0003d000,
718+
.brom_hook_addr = 0x000036ec,
719+
.brom_hook_shadow_addr = 0x0003c000,
720+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
721+
},
648722
.watchdog = &wd_h6_compat,
649723
},{
650724
.soc_id = 0x1890, /* Allwinner A523 */
@@ -660,6 +734,14 @@ soc_info_t soc_info_table[] = {
660734
.rvbar_reg = 0x08000040,
661735
.usb_musb_base= 0x04100000,
662736
.fel_endpoint_table_addr = 0x0005e6ec,
737+
.fel_rx_dma = {
738+
.thunk_addr = 0x00041000,
739+
.l1_tt_addr = 0x00050000,
740+
.l2_tt_addr = 0x00055000,
741+
.brom_hook_addr = 0x00017574,
742+
.brom_hook_shadow_addr = 0x00054000,
743+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
744+
},
663745
.icache_fix = true,
664746
.watchdog = &wd_a523_compat,
665747
},{
@@ -676,6 +758,14 @@ soc_info_t soc_info_table[] = {
676758
.rvbar_reg = 0x08100040,
677759
.usb_musb_base= 0x05100000,
678760
.fel_endpoint_table_addr = 0x00041ff0,
761+
.fel_rx_dma = {
762+
.thunk_addr = 0x00022000,
763+
.l1_tt_addr = 0x00038000,
764+
.l2_tt_addr = 0x0003d000,
765+
.brom_hook_addr = 0x0000b698,
766+
.brom_hook_shadow_addr = 0x0003c000,
767+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
768+
},
679769
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
680770
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
681771
.watchdog = &wd_h6_compat,

soc_info.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ typedef struct {
7070
uint32_t size_bits;
7171
} sid_section;
7272

73+
typedef struct {
74+
uint32_t thunk_addr;
75+
uint32_t l1_tt_addr;
76+
uint32_t l2_tt_addr;
77+
uint32_t brom_hook_addr;
78+
uint32_t brom_hook_shadow_addr;
79+
uint32_t dma_max_len;
80+
} fel_rx_dma_info;
81+
7382
#define SID_SECTION(_name, _offset, _size_bits) { \
7483
.name = _name, \
7584
.offset = _offset, \
@@ -149,6 +158,7 @@ typedef struct {
149158
uint32_t ver_reg; /* MMIO address of "Version Register" */
150159
uint32_t usb_musb_base;/* base address of the USB OTG controller */
151160
uint32_t fel_endpoint_table_addr; /* BROM FEL endpoint table */
161+
fel_rx_dma_info fel_rx_dma; /* BROM RX FIFO copy DMA patch */
152162
const watchdog_info *watchdog; /* Used for reset */
153163
bool sid_fix; /* Use SID workaround (read via register) */
154164
/* 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

0 commit comments

Comments
 (0)