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)