TP-Link TL-SG2452P

Under Construction!
This page is currently under construction. You can edit the article to help completing it.

The TL-SG2452P, also known as T1600G-52PS, is a 48+4 port Gigabit PoE switch. Only v4 is supported by OpenWrt; earlier versions use a Broadcom SoC.

  • SFP ports: No PHY support
  • PoE: Startup script required (see below)
  • LEDs: Startup script required (see below)
  • Fan control: Binary slow/full only; temperature-based control requires workaround below

Do not upgrade to OpenWrt 25.12.x. Tested and confirmed: all Ethernet ports go dead after flashing (RTL839x DSA driver regression in kernel 6.12). Stay on 24.10.x.

The bootloader (TP-Link BOOTUTIL) supports TFTP recovery triggered by grounding the CLK pin of the SPI flash chip. Important: Use OpenWrt 23.05.0 as the recovery image - its kernel 5.15 correctly handles the flash write-protection bits that BOOTUTIL sets after recovery (in contrast to 24.10.x!). A direct sysupgrade to 24.10.x works without extra steps. The 23.05.0 sysupgrade file (~6.1 MB) also still fits into the file size limitation of the bootloader which is about 6MB. Newer images are too big for this approach.

wget -O uImage.img \
  https://downloads.openwrt.org/releases/23.05.0/targets/realtek/rtl839x/openwrt-23.05.0-realtek-rtl839x-tplink_sg2452p-v4-squashfs-sysupgrade.bin

Finding U6 and pin 15

The SPI flash chip U6 (Winbond W25Q256, wide SOIC-16) is located in the very centre of the PCB. Pin 1 is marked with a dot on the chip body.

           [dot = pin 1]
  pin 1  ┌──────────────┐  pin 16  VCC
  pin 2  │              │  pin 15  CLK  ← ground this pin to trigger recovery
  pin 3  │   W25Q256    │  pin 14  IO3
  pin 4  │  (SOIC-16)   │  pin 13  NC
  pin 5  │              │  pin 12  NC
  pin 6  │              │  pin 11  NC
  pin 7  │              │  pin 10  NC
  pin 8  └──────────────┘   pin 9  IO2
         GND

Pin 15 is the second pin from the top on the right side (opposite the dot). Ground it to any GND point (chassis, capacitor leg, or J32 pin 2).

Prepare TFTP Server

You need to prepare a TFTP server from which the switch can get the system image:

  1. TFTP server: IP 192.168.0.146, sysupgrade file named uImage.img
  2. Plug your PC into port 1 of the switch
  3. Set your PC's IP to 192.168.0.146/24

Blind method (no serial cable necessary)

  1. Start packet capture: tcpdump -i <iface> host 192.168.0.30
  2. Power on the switch
  3. After 2–3 seconds, briefly ground U6 pin 15
  4. Release when TFTP requests from 192.168.0.30 appear in the capture
  5. Switch flashes OpenWrt and reboots to 192.168.1.1

Serial console method

Header J32 (4 through-holes near board edge, arrowhead = pin 1):

Pin Signal
1 VCC — do not connect
2 GND
3 TX (SoC → adapter RX)
4 RX (SoC ← adapter TX)

38400 baud, 8N1. The bootloader drops to a TP-Link BOOTUTIL shell; there is no known way to exit to a standard U-Boot prompt.

To install using the serial console power on device, and stop boot by pressing any key.

Once the shell is active:

  1. Ground out the CLK (pin 15) of the ROM (U6)
  2. Select option “3. Start”
  3. Bootloader notes that “The kernel has been damaged!”
  4. Release CLK as soon as bootloader thinks image is corrupted.
  5. Bootloader enters automatic recovery -- details printed on console
  6. Switch flashes OpenWrt and reboots to 192.168.1.1

The PoE ICs are not initialised correctly at boot. Install i2c-tools and add to /etc/rc.local:

i2cset -y 0 0x30 0x12 0xff
for i in `seq 3 14`; do
  echo 1 > /sys/class/hwmon/hwmon$i/in0_enable
  echo 1 > /sys/class/hwmon/hwmon$i/in1_enable
  echo 1 > /sys/class/hwmon/hwmon$i/in2_enable
  echo 1 > /sys/class/hwmon/hwmon$i/in3_enable
done

The inX_enable files are write-only — cat returns “Permission denied”, which is normal.

Add to /etc/rc.local (note: path says rtl838x even on this RTL8393M — this is correct):

cd /sys/kernel/debug/rtl838x/led
echo 0x0060f568 > led_glb_ctrl
echo 0x00007dea > led_set_0_1
echo 0xffffffff > led_copr_pmask_ctrl_0
echo 0x000fffff > led_copr_pmask_ctrl_1
echo 0xffffffff > led_combo_ctrl_0
echo 0x000fffff > led_combo_ctrl_1

The fan runs at full speed by default — the DTS has no thermal zone wired to the fan cooling device. The control script polls all hwmon*/temp1_input sensors (primarily the 12 TPS23861 PoE controllers) and is fully functional without the SoC module below. The kernel module is optional: it adds the RTL8393M die temperature to the set of monitored sensors (CONFIG_DEVMEM is not set in the kernel, so a module is the only way to read SoC registers from userspace).

SoC temperature module (optional)

Build with the OpenWrt 24.10.2 SDK (realtek/rtl839x). Create rtl839x_thermal.c and Makefile:

obj-m := rtl839x_thermal.o
// SPDX-License-Identifier: GPL-2.0-only
// RTL839x SoC thermal sensor — exposes RTL8393M internal sensor via hwmon
// Register definitions from OpenWrt main branch realtek-thermal.c
// Copyright (C) 2025 Bjorn Mork <bjorn@mork.no>
 
#include <linux/bitfield.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/platform_device.h>
 
#define RTL839X_SWITCHCORE_BASE       0x1b000000UL
#define RTL839X_SWITCHCORE_SIZE       0x400
#define RTL8390_THERMAL_METER0_CTRL0  0x274  // bit 0 = enable
#define RTL8390_THERMAL_METER0_RESULT 0x280  // bit 8 = valid, bits[6:0] = raw C
#define RTL8390_TM_ENABLE             BIT(0)
#define RTL8390_TEMP_VALID            BIT(8)
#define RTL8390_TEMP_OUT_MASK         GENMASK(6, 0)
 
static void __iomem *sw_base;
static struct device *hwmon_dev;
static struct platform_device *pdev;
 
static int rtl839x_read_temp(struct device *dev, enum hwmon_sensor_types type,
                             u32 attr, int channel, long *val)
{
    u32 ctrl, result;
    int i;
    if (type != hwmon_temp || attr != hwmon_temp_input)
        return -EOPNOTSUPP;
    ctrl = readl(sw_base + RTL8390_THERMAL_METER0_CTRL0);
    if (!(ctrl & RTL8390_TM_ENABLE))
        writel(ctrl | RTL8390_TM_ENABLE, sw_base + RTL8390_THERMAL_METER0_CTRL0);
    for (i = 0; i < 10; i++) {
        result = readl(sw_base + RTL8390_THERMAL_METER0_RESULT);
        if (result & RTL8390_TEMP_VALID) {
            *val = (long)FIELD_GET(RTL8390_TEMP_OUT_MASK, result) * 1000;
            return 0;
        }
        msleep(10);
    }
    return -ETIMEDOUT;
}
 
static umode_t rtl839x_is_visible(const void *data, enum hwmon_sensor_types type,
                                  u32 attr, int channel)
{
    return (type == hwmon_temp && attr == hwmon_temp_input) ? 0444 : 0;
}
 
static const struct hwmon_channel_info * const rtl839x_hwmon_info[] = {
    HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), NULL
};
static const struct hwmon_ops rtl839x_hwmon_ops = {
    .is_visible = rtl839x_is_visible, .read = rtl839x_read_temp,
};
static const struct hwmon_chip_info rtl839x_chip_info = {
    .ops = &rtl839x_hwmon_ops, .info = rtl839x_hwmon_info,
};
 
static int __init rtl839x_thermal_init(void)
{
    int ret;
    pdev = platform_device_register_simple("rtl839x_thermal", -1, NULL, 0);
    if (IS_ERR(pdev)) return PTR_ERR(pdev);
    sw_base = ioremap(RTL839X_SWITCHCORE_BASE, RTL839X_SWITCHCORE_SIZE);
    if (!sw_base) { ret = -ENOMEM; goto err_pdev; }
    hwmon_dev = hwmon_device_register_with_info(&pdev->dev, "rtl839x",
                                                NULL, &rtl839x_chip_info, NULL);
    if (IS_ERR(hwmon_dev)) { ret = PTR_ERR(hwmon_dev); goto err_ioremap; }
    pr_info("rtl839x_thermal: SoC thermal sensor registered\n");
    return 0;
err_ioremap:
    iounmap(sw_base); sw_base = NULL;
err_pdev:
    platform_device_unregister(pdev);
    return ret;
}
 
static void __exit rtl839x_thermal_exit(void)
{
    if (!IS_ERR_OR_NULL(hwmon_dev)) hwmon_device_unregister(hwmon_dev);
    if (sw_base) iounmap(sw_base);
    if (!IS_ERR_OR_NULL(pdev)) platform_device_unregister(pdev);
}
 
module_init(rtl839x_thermal_init);
module_exit(rtl839x_thermal_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RTL839x SoC thermal sensor (hwmon)");

Build:

SDK=/path/to/openwrt-sdk-24.10.2-realtek-rtl839x
KERNEL=$SDK/build_dir/target-mips_24kc_musl/linux-realtek_rtl839x/linux-6.6.93
export STAGING_DIR=$SDK/staging_dir
make -C "$KERNEL" M=$(pwd) ARCH=mips \
  CROSS_COMPILE=$SDK/staging_dir/toolchain-mips_24kc_gcc-13.3.0_musl/bin/mips-openwrt-linux-musl- \
  modules

Install on the switch:

KVER=$(ssh root@192.168.1.1 uname -r)
scp -O rtl839x_thermal.ko root@192.168.1.1:/lib/modules/$KVER/
ssh root@192.168.1.1 '
  echo "rtl839x_thermal" > /etc/modules.d/rtl839x_thermal
  insmod /lib/modules/$(uname -r)/rtl839x_thermal.ko'

The module registers a hwmon device (name rtl839x) with temp1_input in millidegrees Celsius. Typical SoC idle temperature: ~35 °C.

Fan control script and service

Save as /usr/bin/fancontrol.sh (chmod +x):

#!/bin/sh
# Fan control for TL-SG2452P v4
# Scans all hwmon temp1_input files (12x TPS23861 PoE chips + RTL8393M SoC)
# cooling_device1 cur_state: 0=slow, 1=full speed
 
COOLING=/sys/class/thermal/cooling_device1/cur_state
TEMP_HIGH=62000   # full speed above 62 C (TPS23861 rated max: 85 C)
TEMP_LOW=52000    # slow below 52 C (10 C hysteresis)
INTERVAL=15       # seconds
 
echo 0 > "$COOLING"; cur_state=0
logger -t fancontrol "Started: fan SLOW"
 
while true; do
    max_temp=0
    for f in /sys/class/hwmon/hwmon*/temp1_input; do
        t=$(cat "$f" 2>/dev/null) || continue
        [ "$t" -gt "$max_temp" ] && max_temp=$t
    done
    if [ "$cur_state" -eq 0 ] && [ "$max_temp" -gt "$TEMP_HIGH" ]; then
        echo 1 > "$COOLING"; cur_state=1
        logger -t fancontrol "FULL speed (${max_temp}mC)"
    elif [ "$cur_state" -eq 1 ] && [ "$max_temp" -lt "$TEMP_LOW" ]; then
        echo 0 > "$COOLING"; cur_state=0
        logger -t fancontrol "SLOW speed (${max_temp}mC)"
    fi
    sleep $INTERVAL
done

Save as /etc/init.d/fancontrol (chmod +x), then enable and start it:

#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
STOP=10
 
start_service() {
    procd_open_instance
    procd_set_param command /usr/bin/fancontrol.sh
    procd_set_param respawn 3600 5 5
    procd_close_instance
}

At idle (fan slow), temperatures are ~35 °C SoC and 39–53 °C on the PoE chips. The fan kicks to full speed only above 62 °C and slows back below 52 °C. Speed changes are logged: logread | grep fancontrol

Architecture MIPS 34Kc (mips32r2, no hardware FPU)
Vendor Realtek
Bootloader TP-Link BOOTUTIL (crippled U-Boot)
System-On-Chip RTL8393M
CPU speed 500 MHz
Flash chip Winbond W25Q256 SOIC-16 (U6, centre of PCB)
Flash size 32 MiB
RAM 256 MiB DDR3
Wireless None
Ethernet 48× GbE (PoE), 4× SFP
USB None
Serial Yes — J32, 38400 baud 8N1
JTAG No
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2026/05/02 22:48
  • by j-4