diff --git a/doc/devices/SWTG018AS_A_V2_0.md b/doc/devices/SWTG018AS_A_V2_0.md
index 0d12ecd..5165ebc 100644
--- a/doc/devices/SWTG018AS_A_V2_0.md
+++ b/doc/devices/SWTG018AS_A_V2_0.md
@@ -1,4 +1,10 @@
# SWTG018AS-A V2.0
+## Brands
+| Brand | Type |Managed| PCB | Flash | Chip RTL |
+|--------|------------------|-------|------------------|-----------------|---------------|
+| Ampcom | SWTG018AS-A V2.0 | No | SWTG018AS-A V2.0 | 2MB | 8273N + 8224N |
+| Horaco | HC-SWTGW218AS-A | Yes | SWTG018AS-A V2.0 | 2MB(25Q16JVSIQ) | 8273N + 8224N |
+
The following is a documentation for the unmanaged switch marked as `SWTG018AS-A V2.0`.
It is e.g. sold under the Ampcom brand, but no branh-markings are found on the device.
diff --git a/doc/devices/SWTG024AS-V2.0.md b/doc/devices/SWTG024AS-V2.0.md
new file mode 100644
index 0000000..c787428
--- /dev/null
+++ b/doc/devices/SWTG024AS-V2.0.md
@@ -0,0 +1,49 @@
+### SWTG024AS-V2.0
+
+## Brands
+|Brand|Type|Managed|PCB|Flash|Chip RTL|
+|---|---|---|---|---|---|
+| hongyavision | LG-SG5T1 | No | PCB-SWTG024AS-V2.0_16895 | 25Q40 | 8272 |
+
+### Label specifications
+
+- **Name**:
+- **Ports**:
+ - 5 × RJ45: 10/100/1000/2500 Mbps
+ - 1 × SFP+: 1000 / 2500 / 10000 Mbps
+- **Power**: 12V DC, 1A 5525 connector
+
+
+
+### What works
+The device is fully supported:
+- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
+- The SFP+ port supports 1G, 2.5G and 10G modules
+- LEDs work with the same indiciations as the OEM firmware
+- Online update does not work with 512KiB flash.
+### PCB overview
+
+**Board markings**
+- Top silkscreen: PCB-SWTG024AS-V2.0
+
+Top side
+
+
+
+Bottom
+
+
+
+### J1, serial console
+
+| `J1` pin | Signal |
+| -------- | ----------- |
+| 1 | GND |
+| 2 | RX (Input) |
+| 3 | TX (Output) |
+
+Note,`R52`、`R53`may not be installed.You need to bridge them using either solder or resistors.
+
+## Power supply
+
+Input power is delivered via barell plug, `12V 1A` adapter was provided.
\ No newline at end of file
diff --git a/doc/devices/SWTGW218AS.md b/doc/devices/SWTGW218AS.md
index c8777f2..4797125 100644
--- a/doc/devices/SWTGW218AS.md
+++ b/doc/devices/SWTGW218AS.md
@@ -6,6 +6,7 @@
| Mokerlink | ZX-SWTGW218AS | Yes| SWTG118AS-V2.0-16029 | 2MB (FM25Q16A)| 8273N + 8224N |
| Sodola | | | | | |
| Horaco | | | | | |
+| XikeStor | SKS3200-8E1X | Yes | SWTG118AS-V2.1-17462 | 2MB (25Q16JVSIQ) | |
## Photos
diff --git a/doc/devices/photos/SWTG024AS-V2.0/label.jpg b/doc/devices/photos/SWTG024AS-V2.0/label.jpg
new file mode 100644
index 0000000..f2c628a
Binary files /dev/null and b/doc/devices/photos/SWTG024AS-V2.0/label.jpg differ
diff --git a/doc/devices/photos/SWTG024AS-V2.0/pcb_bottom.jpg b/doc/devices/photos/SWTG024AS-V2.0/pcb_bottom.jpg
new file mode 100644
index 0000000..c1ac456
Binary files /dev/null and b/doc/devices/photos/SWTG024AS-V2.0/pcb_bottom.jpg differ
diff --git a/doc/devices/photos/SWTG024AS-V2.0/pcb_top.jpg b/doc/devices/photos/SWTG024AS-V2.0/pcb_top.jpg
new file mode 100644
index 0000000..496391b
Binary files /dev/null and b/doc/devices/photos/SWTG024AS-V2.0/pcb_top.jpg differ
diff --git a/doc/supported_devices.md b/doc/supported_devices.md
index c1b3f9a..b367c1e 100644
--- a/doc/supported_devices.md
+++ b/doc/supported_devices.md
@@ -1,18 +1,35 @@
# Supported Hardware
The following devices have been tested and are fully working:
-- Horaco ZX_SG4T2
-- keepLINK KP-9000-6XH-X2 / KP-9000-6XHML-X2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+);
- select the machine target by PCB revision, see [KP-9000-6XH-X2](devices/KP-9000-6XH-X2.md)
-- keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+)
-- keepLINK kp-9000-9xh-x-eu (1 x RTL8373 + RTL8224: 8x 2.5GBit + 1x 10GBit SFP+)
-- Lianguo LG-SWTGW218AS (RTL8373 + RTL8224 PHY: 8x 2.5GBit + 1x 10GBit SFP+)
-- No-Name ZX-SWTGW215AS, managed version of kp-9000-6hx-x, ordered on
- AliExpress as keepLINK 5+1 port managed
-- TrendNet TEG-S562 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
-- FNS-1200P (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
-Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide]
-(https://github.com/up-n-atom/SWTG118AS)
+| Brand | Type | Managed | PCB | Flash | Ports |
+|----------|-----------------|---------|------------------------------------------------------------------------------------------------------------------|-------|-------|
+| Ampcom | WAM902-SWTG018AS| No | [SWTG018AS-A V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
+| Davuaz | Da-K6501W | No | [PCB-K0501W-V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/K0501W_V2_0.md) | | 5 + 1 |
+| FOXNEO | FNS-1200P | No | [PCB-K0402W-U13-V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/FNS-1200P.md) | 2M | 4 + 2 |
+| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
+| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/HI-K0801WS.md) | | 8 + 1 |
+| hongyavision | LG-SG5T1 | No | [PCB-SWTG024AS-V2.0_16895](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS-V2.0.md) | 0.5M | 5 + 1 |
+| Horaco | HC-SWTGW218AS | Yes | [SWTG018AS-A V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
+| Horaco | ZX310S-4T2XH | Yes | [PCB-SL310S-4T1T1X-V1.0.1-24107](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/ZX310S-4T2XH.md) | 2M | 5 + 1 |
+| Horaco | ZX310S-4T2XT | Yes | [PCB-SL310S-4T2XT-V1.0.0-22273](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/ZX310S-4T2XT.md) | 2M | 6 |
+| Horaco | ZX-SWTG124AS | Yes | [SWTG024AS-v2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | | 4 + 2 |
+| Keeplink | KP-9000-6XH-X2 / KP-9000-6XHML-X2 | No/Yes | [2M-PCB43-V1.2 / V2.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/KP-9000-6XH-X2.md) | | 4 + 2 |
+| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 |
+| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
+| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | 0.5M | 4 + 2 |
+| Lianguo | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTGW215AS.md) | 2M | 5 + 1 |
+| Mokerlink| ZX-SWTGW218AS | Yes | [SWTG118AS-V2.0-16029](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTGW218AS.md) | 2M | 8 + 1 |
+| Sodola | SL-SWTG124AS-D | Yes | [SWTG024AS-v2.0-17452](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | 2M | 4 + 2 |
+| Steamemo | IG204-V1 | No | [PB-2131](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/STEAMEMO_IG204_V1.md) | | 4 + 2 |
+| TrendNet | TEG-S562 | No | [TEG-S563/EU H/W: V1.0R](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/TEG-S562.md) | 2M | 4 + 2 |
+| Xikestore| SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | | 4 + 2 |
+| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTGW218AS.md) | 2M | 8 + 1 |
+| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
+
+For KP-9000-6XH-X2 / KP-9000-6XHML-X2 devices, select the machine target by PCB
+revision. The managed/unmanaged label alone does not identify the wiring.
+
+Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide](https://github.com/up-n-atom/SWTG118AS)
Many of the RTL8272/3 devices come in versions with PoE support. The RTLPlayground usually also
works on these, however, no support for configuring PoE is provided, simply because these
diff --git a/html/config.js b/html/config.js
index dd8512b..6efa283 100644
--- a/html/config.js
+++ b/html/config.js
@@ -14,6 +14,7 @@ const conf_cmds = [
/^pvid\s+\d{1,2}\s+\d{1,4}$/,
/^ingress(\s+\d{1,2}[tua])+$/,
/^ingress\s+[tua]$/,
+ /^port\s+\d{1,2}\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
/^port\s+\d{1,2}\s+name\s+\S+$/,
/^eee(\s+\d{1,2})?\s+(on|off)$/,
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,
@@ -36,6 +37,7 @@ const conf_overwrite = [
/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
/^pvid\s+\d{1,2}\b/,
/^ingress\b/,
+ /^port\s+\d{1,2}(?!\s+name\b)/,
/^port\s+\d{1,2}\s+name\b/,
/^eee\s+\d{1,2}\b/,
/^eee\b/,
@@ -57,8 +59,7 @@ function parseConf(s){
const deleteMatch = line.match(/^vlan\s+(\d{1,4})\s+d$/);
if (deleteMatch) {
const prefix = "vlan " + deleteMatch[1] + " ";
- configuration = configuration.filter(c =>
- c !== "vlan " + deleteMatch[1] && !c.startsWith(prefix));
+ configuration = configuration.filter(c => !c.startsWith(prefix));
continue;
}
console.log(l + ' --> ' + line);
@@ -71,10 +72,13 @@ function parseConf(s){
let m = line.match(x);
let matchStr = m[0];
configuration = configuration.filter(item =>
- !(item === matchStr || item.startsWith(matchStr + " ")));
+ !(item === matchStr || (item.startsWith(matchStr + " ") && !item.endsWith(" mgmt") && !item.startsWith(matchStr + " name "))));
break;
}
}
+ // Only one management VLAN can be active, so drop any previous mgmt entry
+ if (/^vlan\s+\d{1,4}\s+mgmt$/.test(line))
+ configuration = configuration.filter(item => !/^vlan\s+\d{1,4}\s+mgmt$/.test(item));
configuration.push(line);
}
console.log("Configuration now:");
diff --git a/machine.c b/machine.c
index 96c6f0b..a25fe98 100644
--- a/machine.c
+++ b/machine.c
@@ -365,7 +365,7 @@ __code const struct machine machine = {
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
- .reset_pin = GPIO_NA,
+ .reset_pin = GPIO48_I2C_SCL1,
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
.led_sets = {
@@ -917,6 +917,68 @@ void machine_custom_init(void)
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
}
+#elif defined MACHINE_SWTG024AS_V2_0
+__code const struct machine machine = {
+ .machine_name = "SWTG024AS-V2.0",
+ .isRTL8373 = 0,
+ .min_port = 3,
+ .max_port = 8,
+ .n_sfp = 1,
+ .log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
+ .phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
+ .is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
+ .sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
+ .sfp_port[0].pin_los = GPIO37,
+ .sfp_port[0].pin_tx_disable = GPIO_NA,
+ .sfp_port[0].sds = 1,
+ .sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
+
+ .reset_pin = GPIO_NA,
+ .high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
+ .port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ /* Ports 1-5 RJ45 use set 0, port 9 SFP uses set 1
+ * Ports 1-5: Green: 2.5GBit, Amber: 10/100/1000MBit
+ * SFP-port: Blue: 10GBit, Amber: 100MBit-2.5GBit
+ */
+ .led_sets = {
+ {
+ LEDS_2G5 | LEDS_LINK | LEDS_ACT,
+ LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
+ LEDS_DUPLEX,
+ LEDS_2G5 | LEDS_LINK | LEDS_ACT
+ },
+ {
+ LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
+ LEDS_10G | LEDS_LINK | LEDS_ACT,
+ LEDS_2G5 | LEDS_LINK,
+ LEDS_COL | LEDS_DUPLEX
+ },
+ },
+ .led_mux_custom = 1,
+ .led_mux = {
+ 0x00,0x01,0x04,0x05,0x08,0x09,0x0c,0x3f,0x0d,0x10,0x11,0x0e,0x14,0x11,0x12,0x15,0x15,0x16,0x18,0x19,0x1a,0x19,0x1d,0x1e,0x1c,0x1d,0x20,0x21
+ },
+ };
+
+void machine_custom_init(void)
+{
+ uint16_t pval;
+
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+ reg_bit_set(RTL837X_REG_LED_MODE, 17);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 9);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 7);
+
+ // OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
+ sds_read(0, 0, 0);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 0, 0, pval | 0x100);
+
+ sds_read(0, 6, 2);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 6, 2, pval | 0x4000);
+}
+
#elif defined MACHINE_ZX310S_4T2XT
__code const struct machine machine = {
.machine_name = "ZX310S_4T2XT",
diff --git a/machine.h b/machine.h
index 3483f2e..5ae75c9 100644
--- a/machine.h
+++ b/machine.h
@@ -31,6 +31,7 @@
// #define MACHINE_HI_K0801WS
// #define MACHINE_FNS1200P
// #define MACHINE_PCB_SWTG024AS_A_2_0_1
+// #define MACHINE_SWTG024AS_V2_0
typedef struct {
// GPIO pins for SDA/SCL
diff --git a/tools/led_gpio_utils/README.md b/tools/led_gpio_utils/README.md
new file mode 100644
index 0000000..e0ac336
--- /dev/null
+++ b/tools/led_gpio_utils/README.md
@@ -0,0 +1,149 @@
+# LED GPIO Utilities
+
+This directory contains utility talking to RTL837x switch IC via I2C bus.
+These scripts are designed to help with monitoring, debugging GPIO, and identifying LED configurations.
+(Only tested in Linux)
+
+## Hardware requirements
+
+To use these utilities, you need:
+
+ - A hardware dongle that can communicate with I2C devices. One example is the [I2C-Pico-USB](https://github.com/dquadros/I2C-Pico-USB) which provides USB-to-I2C connectivity.
+ - Connection between the hardware dongle and the RTL837x's I2C communication port.
+
+## Scripts
+
+### 1. `i2c_read_rtl_gpio.py`
+
+This script reads RTL GPIO register values via I2C and displays changes in GPIO states.
+
+**Features:**
+
+ - Reads live RTL GPIO register values via I2C (default address 0x5C)
+ - Monitors GPIO changes with delta detection
+ - Allows specification of I2C bus, sleep interval, and ignored GPIO pins
+ - Shows changes in real-time with GPIO index display
+
+**Usage:**
+```bash
+# Basic usage (defaults to I2C bus 1, 2s sleep interval)
+python3 i2c_read_rtl_gpio.py
+
+# Specify I2C bus
+python3 i2c_read_rtl_gpio.py --i2c-bus 0
+
+# Specify sleep interval in seconds
+python3 i2c_read_rtl_gpio.py --sleep-interval 5
+
+# Ignore specific GPIO pins
+python3 i2c_read_rtl_gpio.py --ignored-ios 28 31 34 44
+
+# Combine options
+python3 i2c_read_rtl_gpio.py --i2c-bus 2 --sleep-interval 1 --ignored-ios 28 31
+```
+
+**Output Format:**
+
+ - Displays register address and data in hex format
+ - Shows GPIO pins that have changed since last read
+ - Example: `0044: 00 00 00 00 00 00 00 00`
+
+### 2. `i2c_dump_rtl_regs.py`
+
+This script dumps all register values from an RTL device via I2C.
+
+**Features:**
+
+ - Dumps registers sequentially from address 0x0000 to 0xFFFF
+ - Reads 16 bytes at a time for efficiency
+ - Configurable I2C bus number
+ - Provides complete register dump in hex format
+
+**Usage:**
+```bash
+# Basic usage (defaults to I2C bus 1)
+python3 i2c_dump_rtl_regs.py >reg_dump.txt
+
+# Specify I2C bus
+python3 i2c_dump_rtl_regs.py --bus 0 >reg_dump.txt
+
+# Or using short option
+python3 i2c_dump_rtl_regs.py -b 2 >reg_dump.txt
+```
+
+**Output Format:**
+
+ - Displays address and 16 bytes of data in hex format
+ - Example: `0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00`
+
+### 3. `dec_leds_from_dump.py`
+
+This script decodes LED configuration from a register dump file ( the output from `i2c_dump_rtl_regs.py`).
+
+**Features:**
+
+ - Parses register dump files (reg_dump.txt format)
+ - Decodes LED pad configurations and LED sets
+ - Maps LED types to bit positions based on configuration
+ - Outputs LED mux configuration and set mappings
+
+**Usage:**
+```bash
+# Must be run in same directory as reg_dump.txt
+python3 dec_leds_from_dump.py
+```
+
+**Output Format:**
+
+ - LED pad configuration (hex values for each pad)
+ - LED set configurations with descriptions of LED types
+ - Port selection mappings
+
+**Requirements:**
+
+ - Requires a `reg_dump.txt` file containing the register dump output
+
+## Requirements
+
+All scripts require:
+
+ - Python 3
+ - `smbus2` Python package
+
+(Depend upon Linux distribution) Install with:
+```bash
+apt install python3-smbus2
+```
+OR
+```bash
+pip3 install smbus2
+```
+
+
+## Common Use Cases
+
+Both `i2c_read_rtl_gpio.py` and `i2c_dump_rtl_regs.py` shall run with the **original** firmware, not RTLPlayground firmware.
+
+
+### Monitoring GPIO Changes
+```bash
+# Monitor GPIO changes on bus 1 with 1-second intervals
+python3 i2c_read_rtl_gpio.py --i2c-bus 1 --sleep-interval 1
+```
+
+### Register Analysis
+```bash
+# Dump all device registers
+python3 i2c_dump_rtl_regs.py --bus 2 > reg_dump.txt
+
+# Analyze the register dump to understand LED configuration
+python3 dec_leds_from_dump.py
+```
+
+## Configuration
+
+All scripts support command-line arguments for flexible configuration:
+
+ - `--i2c-bus` or `-b`: Specify I2C bus (default: 1)
+ - `--sleep-interval` or `-s`: Sleep between reads in seconds (default: 2)
+ - `--ignored-ios` or `-i`: GPIO pins to ignore (default: [28, 31, 34, 44])
diff --git a/tools/led_gpio_utils/dec_leds_from_dump.py b/tools/led_gpio_utils/dec_leds_from_dump.py
new file mode 100755
index 0000000..5885068
--- /dev/null
+++ b/tools/led_gpio_utils/dec_leds_from_dump.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+
+regs = list(range(65536))
+
+glb_mux = 0x65E0
+
+led3_0_set1 = 0x6528
+led1_0_set0 = 0x6548
+led_port_sel = 0x654C
+
+
+def get_word(i):
+ val = (regs[i + 3] << 24) + (regs[i + 2] << 16) + (regs[i + 1] << 8) + regs[i]
+ # print(f"READING {i:04x} : {val:08x}")
+ return val
+
+
+with open("reg_dump.txt", "r") as file:
+ for line in file:
+ addr, data = line.split(":")
+ data = [int(x, 16) for x in data.strip().split(" ")]
+ addr = int(addr, 16)
+ for x in range(len(data)):
+ regs[addr + x] = data[x]
+
+ledstr = []
+ledfmt = []
+print("LED pad Configuration:")
+for i in range(28):
+ j = i % 5
+ if j == 0:
+ idx = glb_mux + (i // 5) * 4
+ val = get_word(idx)
+ ledval = (val >> (6 * j)) & 0x3F
+ ledstr.append(f"{ledval:02x}")
+ ledfmt.append(f"{i:02x}")
+print(f"{' '.join(ledfmt)}")
+print(f"{' '.join(ledstr)}")
+print(f".led_mux = {{ 0x{', 0x'.join(ledstr)} }},")
+
+LED_TYPES = [
+ " 10G",
+ 16,
+ " TWO_5G",
+ 17,
+ " 5G",
+ 18,
+ " TWO_2G5",
+ 19,
+ " 2G5",
+ 0,
+ " TWO_1G",
+ 1,
+ " 1G",
+ 2,
+ " 500M",
+ 3,
+ " 100M",
+ 4,
+ " 10M",
+ 5,
+ " LINK",
+ 6,
+ " LINK_FLASH",
+ 7,
+ " ACT",
+ 8,
+ " RX",
+ 9,
+ " TX",
+ 10,
+ " COL",
+ 11,
+ " DUPLEX",
+ 12,
+ " TRAINING",
+ 13,
+ " MASTER",
+ 14,
+ "",
+ 15,
+]
+
+led_set = []
+led_set_str = []
+print("\nLED-set Configuration:")
+print("LED-ID 0 1 2 3")
+for i in range(4):
+ idval = []
+ idvalstr = []
+ for id in range(4):
+ val = 0xFFFF & (
+ get_word(led1_0_set0 - 8 * i - ((id >> 1) << 2)) >> (16 * (id & 1))
+ )
+ valhi = (
+ 0xF & (get_word(led3_0_set1 - 4 * (i >> 1)) >> (16 * (i & 1) + 4 * id))
+ ) << 16
+ val += valhi
+ idval.append(val)
+ valstr = "("
+ for bit in range(0, len(LED_TYPES), 2):
+ if val & (1 << (LED_TYPES[bit + 1])):
+ valstr += LED_TYPES[bit]
+ valstr += ")"
+ idvalstr.append(valstr)
+ led_set.append(idval)
+ led_set_str.append(idvalstr)
+ idstr = " ".join([f"{d:05x}" for d in idval])
+ print(f"SET {i}: {idstr}")
+ # print(f"{idvalstr}")
+
+portsel = get_word(led_port_sel)
+for i in range(3, 9):
+ sel = (portsel >> (i << 1)) & 3
+ print(f"Port {i}: SET {sel}: {', '.join(led_set_str[sel])}")
diff --git a/tools/led_gpio_utils/i2c_dump_rtl_regs.py b/tools/led_gpio_utils/i2c_dump_rtl_regs.py
new file mode 100755
index 0000000..e90ac00
--- /dev/null
+++ b/tools/led_gpio_utils/i2c_dump_rtl_regs.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+import argparse
+from smbus2 import SMBus, i2c_msg
+
+# Configuration
+DEVICE_ADDR = 0x5C # Replace with your device address
+NUM_BYTES = 16 # Bytes to read
+
+# Parse command line arguments
+parser = argparse.ArgumentParser(description="Dump RTL registers via I2C")
+parser.add_argument(
+ "-b",
+ "--i2c-bus",
+ type=int,
+ required=True,
+ help="I2C bus number (use i2cdetect -l to find out the bus number of the dongle)",
+)
+args = parser.parse_args()
+
+# Open I2C bus
+with SMBus(args.i2c_bus) as bus:
+
+ for addr in range(0, 65536, NUM_BYTES):
+ # Create write message (send register address)
+ write = i2c_msg.write(DEVICE_ADDR, [addr >> 8, addr & 0xFF])
+
+ # Create read message (read 2 bytes)
+ read = i2c_msg.read(DEVICE_ADDR, NUM_BYTES)
+
+ # Perform combined transaction
+ bus.i2c_rdwr(write, read)
+
+ new_data = list(read)
+
+ # Convert read message to string
+ datastr = " ".join([f"{x:02x}" for x in new_data])
+ print(f"{addr:04x}: {datastr}")
diff --git a/tools/led_gpio_utils/i2c_read_rtl_gpio.py b/tools/led_gpio_utils/i2c_read_rtl_gpio.py
new file mode 100755
index 0000000..12612ed
--- /dev/null
+++ b/tools/led_gpio_utils/i2c_read_rtl_gpio.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+
+import time
+import argparse
+from smbus2 import SMBus, i2c_msg
+
+# Configuration
+I2C_BUS = 1
+DEVICE_ADDR = 0x5C # Replace with your device address
+NUM_BYTES = 8 # Bytes to read
+SLEEP_INTERVAL = 1
+
+# Handle command line arguments for ignored IOs
+# default to ignore IOs related to SYS_LED, UART, SMI, SPI
+IGNORED_IOS = [28, 31, 32, 34, 35, 42, 43, 44, 45]
+
+# Setup argument parser
+parser = argparse.ArgumentParser(description="Read I2C data from RTL GPIO expander")
+parser.add_argument(
+ "-b",
+ "--i2c-bus",
+ type=int,
+ required=True,
+ help="I2C bus number (use i2cdetect -l to find out the bus number of the dongle)",
+)
+parser.add_argument(
+ "-s",
+ "--sleep-interval",
+ type=int,
+ default=SLEEP_INTERVAL,
+ help=f"Sleep interval in seconds (default: {SLEEP_INTERVAL})",
+)
+parser.add_argument(
+ "-i",
+ "--ignored-ios",
+ nargs="*",
+ type=int,
+ default=IGNORED_IOS,
+ help=f"List of GPIO pins to ignore (default: {IGNORED_IOS})",
+)
+args = parser.parse_args()
+I2C_BUS = args.i2c_bus
+
+first_read = True
+last_data = [0 for x in range(NUM_BYTES)]
+
+# Open I2C bus
+with SMBus(I2C_BUS) as bus:
+ addr = 0x44
+
+ while True:
+ # Create write message (send register address)
+ write = i2c_msg.write(DEVICE_ADDR, [addr >> 8, addr & 0xFF])
+
+ # Create read message (read NUM_BYTES bytes)
+ read = i2c_msg.read(DEVICE_ADDR, NUM_BYTES)
+
+ # Perform combined transaction
+ bus.i2c_rdwr(write, read)
+
+ new_data = list(read)
+ if first_read:
+ delta_data = last_data
+ first_read = False
+ else:
+ delta_data = [new_data[x] ^ last_data[x] for x in range(NUM_BYTES)]
+ last_data = new_data
+
+ # Convert read message to list
+ datastr = " ".join([f"{x:02x}" for x in new_data])
+ deltastr = ""
+ chg_list = []
+ for x in range(NUM_BYTES):
+ if delta_data[x] == 0:
+ continue
+ for y in range(8):
+ if delta_data[x] & (1 << y):
+ idx = x * 8 + y
+ if idx not in args.ignored_ios:
+ deltastr += f"\n{' ':8s}GPIO{idx}"
+ # deltastr = " ".join([ f"{x:02x}" for x in delta_data])
+ print(f"{addr:04x}: {datastr}{deltastr}")
+ time.sleep(args.sleep_interval)