Skip to content

Commit f8c3e57

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. Put the common thunk code in fel_lib.c and install it from the shared aw_fel_write_buffer() path. This makes normal writes and FIT image loading use the same post-SPL DRAM write preparation path. 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, R528/T113-S3/T528, V5, A523 and A133. Coalesce full high-speed packets into bounded RX DMA requests. Keep each host write as a single AW_FEL_1_WRITE request, and have the thunk copy the final short packet with PIO after stopping DMA. This keeps arbitrary post-SPL DRAM write sizes working without changing the FEL wire protocol. Use a 192 KiB DMA request cap on most supported SoCs; 256 KiB requests can time out while the host is writing. The R528/T113-S3/T528 path uses a smaller 16 KiB cap based on tested hardware behavior. 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 59d234a commit f8c3e57

11 files changed

Lines changed: 883 additions & 31 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: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ bool verbose = false; /* If set, makes the 'fel' tool more talkative */
3636
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;
39-
static bool fel_usb_high_speed_enabled = false;
4039

4140
#define FEL_REENUM_TIMEOUT_MS 5000
4241
#define FEL_REENUM_RETRY_MS 100
@@ -1118,6 +1117,7 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
11181117
/* re-enable the MMU if it was enabled by BROM */
11191118
if (tt != NULL)
11201119
aw_restore_and_enable_mmu(dev, soc_info, tt);
1120+
dev->dram_ready = true;
11211121

11221122
return spl_len;
11231123
}
@@ -1650,7 +1650,7 @@ void usage(const char *cmd) {
16501650
" -h, --help Print this usage summary and exit\n"
16511651
" -v, --verbose Verbose logging\n"
16521652
" -p, --progress \"write\" transfers show a progress bar\n"
1653-
" --no-high-speed Disable high-speed USB\n"
1653+
" --no-high-speed Disable high-speed USB and fast writes\n"
16541654
" -l, --list Enumerate all (USB) FEL devices and exit\n"
16551655
" -d, --dev bus:devnum Use specific USB bus and device number\n"
16561656
" --sid SID Select device by SID key (exact match)\n"
@@ -1790,10 +1790,11 @@ int main(int argc, char **argv)
17901790
*/
17911791
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
17921792

1793-
fel_usb_high_speed_enabled = high_speed &&
1794-
handle->soc_info->usb_musb_base != 0;
1793+
handle->verbose = verbose;
1794+
handle->usb_high_speed = high_speed &&
1795+
handle->soc_info->usb_musb_base != 0;
17951796
reenum_sid_arg = sid_arg;
1796-
if (fel_usb_high_speed_enabled && !reenum_sid_arg &&
1797+
if (handle->usb_high_speed && !reenum_sid_arg &&
17971798
read_device_sid(handle, reenum_sid)) {
17981799
reenum_sid_arg = reenum_sid;
17991800
pr_info("Tracking FEL device by SID %s\n", reenum_sid_arg);
@@ -1820,8 +1821,9 @@ int main(int argc, char **argv)
18201821

18211822
handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID,
18221823
AW_USB_PRODUCT_ID);
1823-
fel_usb_high_speed_enabled = high_speed &&
1824-
handle->soc_info->usb_musb_base != 0;
1824+
handle->verbose = verbose;
1825+
handle->usb_high_speed = high_speed &&
1826+
handle->soc_info->usb_musb_base != 0;
18251827
}
18261828

18271829
/* Some SoCs need the SMC workaround to enter secure state. */

fel_lib.c

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
**********************************************************************/
2323

2424
#include "portable_endian.h"
25+
#include "common.h"
2526
#include "fel_lib.h"
2627
#include <libusb.h>
2728

@@ -70,19 +71,25 @@ static void usb_error(int rc, const char *caption, int exitcode)
7071
* timeout, and "slow" transfers take place at approx. 64 KiB/sec - so we can
7172
* expect the maximum chunk being transmitted within 8 seconds or less.
7273
*/
73-
static const int AW_USB_MAX_BULK_SEND = 512 * 1024; /* 512 KiB per bulk request */
74+
static const int AW_USB_MAX_BULK_SEND = 512 * 1024;
75+
static const int AW_USB_PROGRESS_BULK_SEND = 128 * 1024;
76+
static const int AW_USB_FAST_PROGRESS_BULK_SEND = 1024 * 1024;
7477

78+
#define DRAM_BASE 0x40000000
79+
#define DRAM_SIZE 0x80000000
80+
#define DRAM_END (DRAM_BASE + DRAM_SIZE)
81+
82+
static uint32_t fel_rx_dma_thunk[] = {
83+
#include "thunks/fel-rx-dma-thunk.h"
84+
};
85+
86+
/*
87+
* Keep progress updates frequent enough for slow BROM PIO transfers. Once an
88+
* RX-DMA thunk is installed, 1 MiB chunks avoid host-side progress overhead.
89+
*/
7590
static void usb_bulk_send(libusb_device_handle *usb, int ep, const void *data,
76-
size_t length, bool progress)
91+
size_t length, size_t max_chunk, bool progress)
7792
{
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-
8693
size_t chunk;
8794
int rc, sent;
8895
while (length > 0) {
@@ -187,7 +194,7 @@ static void aw_send_usb_request(feldev_handle *dev, int type, int length)
187194
};
188195
req.length2 = req.length;
189196
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
190-
&req, sizeof(req), false);
197+
&req, sizeof(req), AW_USB_MAX_BULK_SEND, false);
191198
}
192199

193200
static bool aw_send_usb_request_may_disconnect(feldev_handle *dev, int type,
@@ -239,9 +246,18 @@ static bool aw_usb_read_may_disconnect(feldev_handle *dev, void *data,
239246
static void aw_usb_write(feldev_handle *dev, const void *data, size_t len,
240247
bool progress)
241248
{
249+
size_t max_chunk = AW_USB_MAX_BULK_SEND;
250+
251+
if (progress) {
252+
if (dev->rx_dma_patched)
253+
max_chunk = AW_USB_FAST_PROGRESS_BULK_SEND;
254+
else
255+
max_chunk = AW_USB_PROGRESS_BULK_SEND;
256+
}
257+
242258
aw_send_usb_request(dev, AW_USB_WRITE, len);
243259
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
244-
data, len, progress);
260+
data, len, max_chunk, progress);
245261
aw_read_usb_response(dev);
246262
}
247263

@@ -363,6 +379,49 @@ void aw_fel_execute_may_disconnect(feldev_handle *dev, uint32_t offset)
363379
(void)aw_read_fel_status_may_disconnect(dev);
364380
}
365381

382+
static void aw_fel_install_rx_dma_thunk(feldev_handle *dev)
383+
{
384+
const fel_rx_dma_info *dma = &dev->soc_info->fel_rx_dma;
385+
uint32_t params[6], param_offset;
386+
size_t i;
387+
388+
if (dev->rx_dma_patched || !dma->thunk_addr)
389+
return;
390+
391+
param_offset = fel_rx_dma_thunk[1];
392+
if (param_offset % sizeof(*fel_rx_dma_thunk) ||
393+
param_offset + sizeof(params) > sizeof(fel_rx_dma_thunk))
394+
pr_fatal("RX DMA thunk: bad parameter offset 0x%x\n",
395+
param_offset);
396+
397+
uint32_t thunk_buf[ARRAY_SIZE(fel_rx_dma_thunk)];
398+
399+
if (dev->verbose)
400+
printf("Patching BROM RX FIFO copy to use DMA.\n");
401+
for (i = 0; i < sizeof(thunk_buf) / sizeof(*thunk_buf); i++)
402+
thunk_buf[i] = htole32(fel_rx_dma_thunk[i]);
403+
params[0] = htole32(dma->l1_tt_addr);
404+
params[1] = htole32(dma->l2_tt_addr);
405+
params[2] = htole32(dma->brom_hook_addr);
406+
params[3] = htole32(dma->brom_hook_shadow_addr);
407+
params[4] = htole32(dev->soc_info->usb_musb_base);
408+
params[5] = htole32(dma->dma_max_len);
409+
memcpy((uint8_t *)thunk_buf + param_offset, params, sizeof(params));
410+
411+
aw_fel_write(dev, thunk_buf, dma->thunk_addr, sizeof(thunk_buf));
412+
413+
aw_fel_execute(dev, dma->thunk_addr + fel_rx_dma_thunk[0]);
414+
dev->rx_dma_patched = true;
415+
}
416+
417+
static void aw_fel_prepare_fast_dram_write(feldev_handle *dev,
418+
uint32_t offset)
419+
{
420+
if (dev->usb_high_speed && dev->dram_ready &&
421+
offset >= DRAM_BASE && offset < DRAM_END)
422+
aw_fel_install_rx_dma_thunk(dev);
423+
}
424+
366425
static void aw_disable_icache(feldev_handle *dev)
367426
{
368427
soc_info_t *soc_info = dev->soc_info;
@@ -401,6 +460,7 @@ void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
401460
if (len == 0)
402461
return;
403462

463+
aw_fel_prepare_fast_dram_write(dev, offset);
404464
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
405465
aw_usb_write(dev, buf, len, progress);
406466
aw_read_fel_status(dev);

fel_lib.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ typedef struct {
3535
soc_name_t soc_name;
3636
soc_info_t *soc_info;
3737
bool disconnected;
38+
bool verbose;
39+
bool usb_high_speed;
40+
bool dram_ready;
41+
bool rx_dma_patched;
3842
} feldev_handle;
3943

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

soc_info.c

Lines changed: 98 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
},{
@@ -632,6 +698,14 @@ soc_info_t soc_info_table[] = {
632698
.sid_sections = generic_2k_sid_maps,
633699
.usb_musb_base= 0x04100000,
634700
.fel_endpoint_table_addr = 0x00044ea8,
701+
.fel_rx_dma = {
702+
.thunk_addr = 0x00022000,
703+
.l1_tt_addr = 0x00030000,
704+
.l2_tt_addr = 0x00035000,
705+
.brom_hook_addr = 0x000094c8,
706+
.brom_hook_shadow_addr = 0x00034000,
707+
.dma_max_len = 0x4000,
708+
},
635709
.icache_fix = true,
636710
.watchdog = &wd_v853_compat,
637711
},{
@@ -647,6 +721,14 @@ soc_info_t soc_info_table[] = {
647721
.sid_sections = generic_2k_sid_maps,
648722
.usb_musb_base= 0x05100000,
649723
.fel_endpoint_table_addr = 0x00026efc,
724+
.fel_rx_dma = {
725+
.thunk_addr = 0x00022000,
726+
.l1_tt_addr = 0x00038000,
727+
.l2_tt_addr = 0x0003d000,
728+
.brom_hook_addr = 0x000036ec,
729+
.brom_hook_shadow_addr = 0x0003c000,
730+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
731+
},
650732
.watchdog = &wd_h6_compat,
651733
},{
652734
.soc_id = 0x1890, /* Allwinner A523 */
@@ -662,6 +744,14 @@ soc_info_t soc_info_table[] = {
662744
.rvbar_reg = 0x08000040,
663745
.usb_musb_base= 0x04100000,
664746
.fel_endpoint_table_addr = 0x0005e6ec,
747+
.fel_rx_dma = {
748+
.thunk_addr = 0x00041000,
749+
.l1_tt_addr = 0x00050000,
750+
.l2_tt_addr = 0x00055000,
751+
.brom_hook_addr = 0x00017574,
752+
.brom_hook_shadow_addr = 0x00054000,
753+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
754+
},
665755
.icache_fix = true,
666756
.watchdog = &wd_a523_compat,
667757
},{
@@ -678,6 +768,14 @@ soc_info_t soc_info_table[] = {
678768
.rvbar_reg = 0x08100040,
679769
.usb_musb_base= 0x05100000,
680770
.fel_endpoint_table_addr = 0x00041ff0,
771+
.fel_rx_dma = {
772+
.thunk_addr = 0x00022000,
773+
.l1_tt_addr = 0x00038000,
774+
.l2_tt_addr = 0x0003d000,
775+
.brom_hook_addr = 0x0000b698,
776+
.brom_hook_shadow_addr = 0x0003c000,
777+
.dma_max_len = FEL_RX_DMA_MAX_LEN,
778+
},
681779
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
682780
.smc_workaround = SMC_WORKAROUND_DIRECT_SMC,
683781
.watchdog = &wd_h6_compat,

0 commit comments

Comments
 (0)