Was getting crazy about the slow XModem Transfers and wrote a small preloader that can change the baudrate of the console. At least tested up to 576000 baud.
#include <stdio.h>
#include <stdarg.h>
#define UART_BASE_ADDR (0xb8002000)
#define LCR_BKSE 0x80 /* Bank select enable */
#define LCRVAL LCR_8N1 /* 8 data, 1 stop, no parity */
#define LCRLOOPBACK
#define MCRVAL (MCR_DTR | MCR_RTS) /* RTS/DTR */
#define FCRVAL 0xC1 /* Clear & enable FIFOs */
#define LCR_8N1 0x03
#define MCR_DTR 0x01
#define MCR_RTS 0x02
void go();
int main()
{
go();
return 0;
}
char console_getc(void) {
while ((*((volatile unsigned int *)(UART_BASE_ADDR+0x14)) & 0x01000000) == 0);
return *((volatile unsigned char *)UART_BASE_ADDR);
}
void console_putc(char c) {
while ((*((volatile unsigned int *)(UART_BASE_ADDR+0x14)) & 0x20000000) == 0);
*((volatile unsigned char *)UART_BASE_ADDR) = c;
return;
}
void xprintf(const char* format, ...)
{
char buffer[512];
char *p;
va_list args;
va_start (args, format);
vsnprintf (buffer, 511, format, args);
va_end (args);
p = buffer;
while (*p) {
console_putc(*p);
p++;
}
}
void set_uart_divisor(unsigned int divisor)
{
*((volatile unsigned int *)(UART_BASE_ADDR+0xc)) = ((LCR_BKSE | LCRVAL) << 24);
*((volatile unsigned int *)(UART_BASE_ADDR)) = (divisor) << 24;
*((volatile unsigned int *)(UART_BASE_ADDR+0x4)) = (divisor >> 8) << 24;
*((volatile unsigned int *)(UART_BASE_ADDR+0xc)) = ((LCRVAL) << 24);
}
unsigned int calc_uart_divisor(unsigned int baudrate)
{
unsigned int config_sys_hz = 200000000; // LXB frequency
unsigned int mode_x_div = 16; // see UBoot stuff ...
unsigned int uart_divisor_mod = 1;
return ((config_sys_hz + (baudrate * (mode_x_div/2))) /
(mode_x_div * baudrate)) - uart_divisor_mod;
}
void go()
{
unsigned int baudrate = 230400;
unsigned int divisor = calc_uart_divisor(baudrate);
xprintf("Changing to %d baud (divisor=%d)\n", baudrate, divisor);
set_uart_divisor(divisor);
}
Compile this with
#!/bin/sh
export STAGING_DIR=<your OpenWrt staging dir>
${STAGING_DIR}/toolchain-mips_24kc_gcc-13.3.0_musl/bin/mips-openwrt-linux-musl-gcc -o baudset -static baudset.c -Ttext 81700100 -Wl,--section-start=.init=81700000
dd if=baudset of=baudset.bin bs=65536 skip=1
Upload with ATUP command and sx
openwrt# picocom -b115200 /dev/ttyUSB0
GS1920-24> ATUP81700000,<size of baudset.bin>
Starting XMODEM upload (CRC mode)....
<leave with ALT-Q-A>
Terminating...
Skipping tty reset...
Thanks for using picocom
openwrt# sx -k -vv baudset.bin < /dev/ttyUSB0 > /dev/ttyUSB0
Sending baudset.bin, 1598 blocks: Give your local XMODEM receive command now.
Bytes Sent: 204672 BPS:4472
Transfer complete
Finally change the baudrate on the switch. Address 0x81700240 should be the entry point of the program main() function.
openwrt# picocom -b115200 /dev/ttyUSB0
GS1920-24> ATGO81700240
Changing to 230400 baud (divisor=53)
<leave with ALT-Q-A>
Terminating...
Skipping tty reset...
Thanks for using picocom
openwrt# picocom -b230400 /dev/ttyUSB0
GS1920-24>
Multiple ATUP/ATGO sequences and small files seem to have issues with CPU caches. So uploads should go to different addresses. E.g. this baudrate changer to 0x81700000 the kernel to 0x81800000. To reach maximum kernel transmission performance
- set baud rate to 576000
- call sx with parameter -k to send 1KB data frames
- in case you use an FTDI USB to serial converter reduce the the time to send incomplete packets to the host from 16ms to 1ms (improves ACK response times)
echo 1 > /sys/devices/pci0000:00/.../usb1/1-3/1-3:1.0/ttyUSB0/latency_timer
That applied a completely automated LZMA kernel (see repo from above) initrams upload with an expect script will finish in less than 3 minutes and transfer data at a maximum speed of 50KByte/sec.