Support for Zyxel GS1920 series (GS1920-24HP)

I've rewrote the remaining u-boot mac init logic into the rtl8390_init_mac function you started. (Parts of it I have no clue yet of what it's about.) I've left out a big chunk that seems to be about led control, which I assume I won't need initially?

No traffic is passing through the switch though. Not from port to port, nor from port to cpu. So I'm guessing I'm still missing stuff?

Also a question on "the MAC isn't fully set up". Do you mean the switch "MAC" or the CPU "MAC"?

Also I've studied the partition map I found. It seems my GS2210 has a extra ROM section. But I think there's no overlap with the GS1920 layout from what I can see.

Yeah, probably. Unfortunately, I do not know more than you at the moment. I do know, however, that the U-Boot code is the key, since I had a working U-Boot port that allowed booting into OpenWrt with working Ethernet.

Tagging @svanheule and @olliver here, maybe they can shed some more light onto this?

I had a short look at it again today:

  1. I'm pretty sure your patch doesn't compile, you are missing the RTL839X_MAC_EFUSE_CTRL constant. Did you select kernel 5.15 during compilation?
  2. In rtl83xx-phy.c, the function rtl8390_configure_serdes look suspiciously short. I'm pretty sure we are missing the SerDes init parameters, as can be found in rtl8390_init.c U-Boot code (function rtl8390_serdes_config_init)

I don't have time to work on it, but that should give you some more pointers.

Actually, it does compile. RTL839X_MAC_EFUSE_CTRL is defined in target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h. To be sure my code was actually compiled, I had added some pr_info("Start/End %s\n", __func__); at the beginning and end and checked it was printed to dmesg. (But did not commit that)

I have been looking through u-boot's rtl8390_config function to see what it all does. And found it configures following items

  • GPIO
  • MDIO
  • Phy
  • MAC
  • SerDes
  • Misc ?
  • Custom board stuff (which is empty)

Since I added the MAC code and the Phy stuff works, I sort of came to the same thoughts that it might have to do with the SerDes stuff? Also after looking through the RTL8218B-CG datasheet and finding chapter 3.3. 24+4 Combo-Port Gigabit Ethernet Switch which looks suspiciously similar to the hardware PCB layout. In that design the SerDes component seem to play mayor a role in communication between the RTL8392 and RTL8218/RTL8214 chips via QSGMII links.

Thanks to your tip, I now also know where to look for and add that SerDes stuff in the OpenWRT codebase and also am a bit more confident that it's actually the missing part. It will take me a bit more time to try and write that though as it seems to be a bit more complicated code. (Also since I'm away for the weekend with my family).

1 Like

Hi all,
Been a while. I've been bashing my head against the phy / serdes code the last few weeks. And I've come to the conclusion that I'm completely clueless on how the paged (register) read/writes work. Mainly I think because I can't find any info on the "pages" (adresses / what they contain). I only found a reference to page 0x0A42 in the RTL8218B-CG datasheet register descriptions. I did dive into svanheule's online register documentation, but failed to see the light.

What I did figure out is that there's no active code path in drivers/net/phy/rtl83xx-phy.c for the rtl8390. The device matches the function rtl8218b_ext_phy_probe via the rtl83xx_phy_driver struct. But never reaches rtl8380_configure_ext_rtl8218b to actually configure stuff as it is gated with soc_info.family == RTL8380_FAMILY_ID

As a test I'm also calling rtl8380_configure_ext_rtl8218b for the rtl8390 and included the firmwares from files/firmware/rtl838x_phy in the initrd . As it looks very similar to rtl8218b_rtl8390_config from the uboot tree. But no luck yet. It fails at line 890

	for (int i = 0; i < 8; i++) {
		int l;rtl8380_configure_ext_rtl8218b

		for (l = 0; l < 100; l++) {
			val = phy_package_port_read_paged(phydev, i, RTL821X_PAGE_STATE, 0x10);
			if (val & 0x40)
				break;
		}
		if (l >= 100) {
			phydev_err(phydev, "Could not patch PHY\n");
			return -1;
		}
}

And as state before I can't figure out what this does. As I'm lost on all the "paged" stuff. Any pointer would be greatly appreciated. As well if someone can tell me if I'm completely barking up the wrong tree here...

Thnx, Harm

To make my life a bit easier I wrote an expect script to upload / test a fresh initrd.

#!/usr/bin/expect -f

set unlock "<bootloader unlock code>"
set initramfs "bin/targets/realtek/rtl839x/openwrt-realtek-rtl839x-zyxel_gs1920-24hp-initramfs-kernel.bin"

set filesize [file size $initramfs]
set timeout -1
set date [timestamp -format {%Y%m%d%H%M}]

set port [open /dev/ttyUSB0 r+]
fconfigure $port -blocking 0 -buffering none
fconfigure $port -mode "9600,n,8,1"
spawn -open $port
system curl "http://blitzwolf07.local/switch/blitzwolf07_relay/turn_on"

# Interrupt boot
expect -ex "Press any key to enter debug mode within 1 second."
after 100
send -- "\r"

# Unlock bootloader and switch to 115200 baud
expect -ex "GS2210-24LP>"
send -- "ATEN1,$unlock\r"
expect "OK"
expect "GS2210-24LP>"
send -- "ATBA5\r"
after 100

# Start Xmodem receive
fconfigure $port -mode "115200,n,8,1"
send -- "\r"
expect -ex "GS2210-24LP>"
send -- "\r"
expect -ex "GS2210-24LP>"
send -- "ATUP81800000,$filesize\r"
expect -ex "Starting XMODEM upload (CRC mode)..."
close

# Send initramfs
system sx -vv $initramfs < /dev/ttyUSB0 > /dev/ttyUSB0
after 100

# Boot
set port [open /dev/ttyUSB0 r+]
spawn -open $port
send -- "\r"
expect -ex "GS2210-24LP>"
send -- "ATGO81800000\r"
close

# Observe and interact once openwrt is booting
system picocom -b115200 /dev/ttyUSB0 -g picocom.log
#system curl "http://blitzwolf07.local/switch/blitzwolf07_relay/turn_off"

Note 1: The system curl commands are to a wifi smart socket, running esphome to turn the switch power on/off.
Note 2: The expect "GS2210-24LP>" lines will need to be adjusted for a GS1920

2 Likes

I just want to say that I think that's awesome that you're giving this a try :+1:

I have a GS1920-24 and thanks to this thread I learned about ZyNOS and then found the ZyNOS CI Command List and thanks to that I could actually execute some commands on the prompt that my SSH connection gave me :smiley:

Hi!

I just acquired this exact same switch (Zyxel GS1920-24HP), with the intention of running OpenWrt on it. As a (embedded) developer myself, I'd like to know if there's anything I can do to help move this forward? And is there a wiki with a TL;DR of the current state?

The documentation is this thread. If you read a few posts back, you can easily spot that the Ethernet driver is the culprit.

Hi @vhdirk ,

I've been trying give a go at porting the uboot init code to the openwrt driver. As stated a few posts above, I'm stuck trying to figure out how all the register pages work. As I haven't been able to find any relevant docs on it. (And I'd really like to understand what it actually all does)

Grtz, Harm

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.

2 Likes

While trying to fix the initialization issues I came around a bug in the existing code base. See https://github.com/openwrt/openwrt/pull/16123

Independent from the current realtek situation I would be thankful if you can test and promote it for inclusion.

1 Like

Awesome! I've updated my expect script accordingly:

Hi, how far are we from a snapshot being ready for testing?

Without working LAN, don't expect a build anytime soon.

Does it differ much from same SOC based switches?

So now I'm expecting a snapshot soon ...

2 Likes

That's great news! I'll test this soon and try to bring up the remaining pieces - PoE requires work and so does probably some other stuff as well. Let's see what other surprises are there ...

This is awesome! I only wish I understood half of what you did here.

I was able to copy the relevant parts from @andyboeh to your tree and compiled. Now I can ping my router :smiley:

root@OpenWrt:/# cat /etc/os-release 
NAME="OpenWrt"
VERSION="SNAPSHOT"
ID="openwrt"
ID_LIKE="lede openwrt"
PRETTY_NAME="OpenWrt SNAPSHOT"
VERSION_ID="snapshot"
HOME_URL="https://openwrt.org/"
BUG_URL="https://bugs.openwrt.org/"
SUPPORT_URL="https://forum.openwrt.org/"
BUILD_ID="r27054-1a6cb6004d"
OPENWRT_BOARD="realtek/rtl839x"
OPENWRT_ARCH="mips_24kc"
OPENWRT_TAINTS="no-all"
OPENWRT_DEVICE_MANUFACTURER="OpenWrt"
OPENWRT_DEVICE_MANUFACTURER_URL="https://openwrt.org/"
OPENWRT_DEVICE_PRODUCT="Generic"
OPENWRT_DEVICE_REVISION="v0"
OPENWRT_RELEASE="OpenWrt SNAPSHOT r27054-1a6cb6004d"
root@OpenWrt:/# ip a show dev switch.1
28: switch.1@switch: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 80:00:3b:60:00:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global switch.1
       valid_lft forever preferred_lft forever
    inet 192.168.42.123/24 scope global switch.1
       valid_lft forever preferred_lft forever
    inet6 fd5e:2b9a:7835::1/60 scope global noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 fe80::8200:3bff:fe60:0/64 scope link 
       valid_lft forever preferred_lft forever
root@OpenWrt:/# ping 192.168.42.1
PING 192.168.42.1 (192.168.42.1): 56 data bytes
64 bytes from 192.168.42.1: seq=0 ttl=64 time=0.957 ms
64 bytes from 192.168.42.1: seq=1 ttl=64 time=0.889 ms
^C
--- 192.168.42.1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.889/0.923/0.957 ms

Great - I had to recompile everything as I rebased my branch. I also cleaned up zynsig, this will be the first PR for firmware-utils once I'm confident it works.

Next steps:

  • flash layout - it might be possible to use both firmware partitions while still allowing stock recovery
  • PoE - we need the GPIOs for i2c
  • GPIOs - I think some are missing
  • fan control/thermal monitoring - I think there is an IC
1 Like