OpenWRT for TP-Link Deco x55

Heyo Fellow Forum members!

For a while now I haven't been happy with the performance of my mesh system from TP-Link so I've decided to try to take on the task of porting OpenWRT to these devices.

I'm still very early stages, mostly meaning I have a UART connection to the device and I'm starting to create a profile. For anyone who wants to do the same, I figured I'd create this forum post and document how things are going.

First and foremost, the location of the UART solder points. They are located on the bottom side of the main board, just below the metal "heat sink".

I connected them up to a Raspberry pi using a baud rate of 115200 and I am getting an output!

Normally I wouldn't post so soon, especially because I don't have much completed yet... but there was one interesting call out I wanted to share here. When I went back through the text file of my output, it looks like this device runs OpenWRT under the hood already.

Is it a normal practice for a company like TP-Link to use OpenWRT under the hood? The good news is that it should make it fairly straightforward to port... right? lol!

(The forum won't let me post more than one photo yet so I'll see if I can respond in a comment with more)

Up close of the UART solder pads.

Serial output showing OpenWRT kernel.

Interestingly … my google led to wrong device pcb :-S

FCC photos say IPQ - https://fcc.report/FCC-ID/2AXJ4X50/5514196, and they seem to match OP's photo of the PCB.

1 Like

I see, I understand that Broadcom doesn't have much support, but the chip in mine is a Qualcom IPQ5018. It also has the Qualcom QCN6102 Radio

1 Like

I'm currently trying to understand the partition table layout. The usual steps in the serial console after interrupting U-Boot with "tpl" don't give me much information on the partition table.

printenv mtdparts
mtdparts=mtdparts=nand0:0x900000@0x5ac0000(fdata), 

nand info
Device 0: nand0, sector size 128 KiB
 Page size       2048 b
 OOB size          64 b
 Erase size    131072 b
 subpagesize        0 b
 options     0x       0
 bbt options 0x       0 

mtdparts
device nand0 <nand0>, # parts = 1
#: name                size            offset          mask_flags
0: fdata               0x00900000      0x05ac0000      0
active partition: nand0,0 - (fdata) 0x00900000 @ 0x05ac0000
defaults:
mtdids  : none
mtdparts: none
flinfo doesn't show any output. 

I don't have cat in this environment to check /proc/mtd

I'm trying to explore other ideas on how to read the partition table. Trying to read the data from NAND, and displaying it with md but I'm not finding anything so far that gives me any information.

Any ideas for how I can proceed? I'll keep trying options for now

mtd's aren't listed by the stock firmware while booting ?

you could try to RAM boot one of the initramfs:es in https://downloads.openwrt.org/snapshots/targets/qualcommax/ipq60xx/, by running tftpboot + bootm in u-boot.

Please do full printenv and help in uboot

I didn't see any in the output, but I'm not super familiar with what I'm looking for. Here is the first part of the boot:

Format: Log Type - Time(microsec) - Message - Optional Info
Log Type: B - Since Boot(Power On Reset),  D - Delta,  S - Statistic
S - QC_IMAGE_VERSION_STRING=BOOT.BF.3.3.1.1-00059
S - IMAGE_VARIANT_STRING=MAACANAZA
S - OEM_IMAGE_VERSION_STRING=WINCE-SDC-101
S - Boot Config, 0x000002c5
B -       127 - PBL, Start
B -      1559 - bootable_media_detect_entry, Start
B -      3352 - bootable_media_detect_success, Start
B -      3355 - elf_loader_entry, Start
B -      8411 - auth_hash_seg_entry, Start
B -      8771 - auth_hash_seg_exit, Start
B -     99926 - elf_segs_hash_verify_entry, Start
B -    169406 - PBL, End
B -    139507 - SBL1, Start
B -    200507 - GCC [RstStat:0x0, RstDbg:0x600000] WDog Stat : 0x4
B -    209870 - clock_init, Start
D -      7808 - clock_init, Delta
B -    218929 - boot_flash_init, Start
D -     15524 - boot_flash_init, Delta
B -    234514 - boot_config_data_table_init, Start
D -      4910 - boot_config_data_table_init, Delta - (575 Bytes)
B -    242505 - Boot Setting :  0x00030618
B -    248697 - CDT version:2,Platform ID:8,Major ID:4,Minor ID:0,Subtype:2
B -    255590 - sbl1_ddr_set_params, Start
B -    257206 - Pre_DDR_clock_init, Start
B -    262879 - Pre_DDR_clock_init, End
B -    905301 - do ddr sanity test, Start
D -        61 - do ddr sanity test, Delta
B -    909967 - Image Load, Start
D -    244122 - QSEE Image Loaded, Delta - (578956 Bytes)
B -   1154943 - Image Load, Start
D -     14244 - DEVCFG Image Loaded, Delta - (13592 Bytes)
B -   1169248 - Image Load, Start
D -    251045 - APPSBL Image Loaded, Delta - (606922 Bytes)
B -   1420354 - QSEE Execution, Start
D -        61 - QSEE Execution, Delta
B -   1426820 - SBL1, End
D -   1289967 - SBL1, Delta
S - Flash Throughput, 2462 KB/s  (1200717 Bytes,  487536 us)
S - DDR Frequency, 800 MHz
S - Core 0 Frequency, 800 MHz


U-Boot 2016.01 (Feb 24 2022 - 17:53:09 +0800)

DRAM:  smem ram ptable found: ver: 1 len: 4
512 MiB
NAND:  QPIC controller support serial NAND
ID = 7f7f11c8
Vendor = c8
Device = 11
Serial Nand Device Found With ID : 0xc8 0x11
Serial NAND device Manufacturer:F50D1G41LB(2M)
Device Size:128 MiB, Page size:2048, Spare Size:64, ECC:4-bit
SF: Unsupported flash IDs: manuf 00, jedec 0000, ext_jedec 0000
ipq_spi: SPI Flash not found (bus/cs/speed/mode) = (0/0/48000000/0)
128 MiB
MMC:   sdhci: Node Not found, skipping initialization

PCI Link Intialized
In:    serial@78AF000
Out:   serial@78AF000
Err:   serial@78AF000
machid: 8040002
eth0 MAC Address from ART is not valid
eth1 MAC Address from ART is not valid
ubi0: attaching mtd1
ubi0: scanning is finished
ubi0: attached mtd1 (name "mtd=0", size 9 MiB)
ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
ubi0: good PEBs: 72, bad PEBs: 0, corrupted PEBs: 0
ubi0: user volume: 1, internal volumes: 1, max. volumes count: 128
ubi0: max/mean erase counter: 7/4, WL threshold: 4096, image sequence number: 1695445936
ubi0: available PEBs: 0, total reserved PEBs: 72, PEBs reserved for bad PEB handling: 20
Find no boot alter flag! Disable CONFIG_QSPI_SERIAL_TRAINING
Enter magic string to stop autoboot in 1 seconds
Unmounting UBIFS volume ubi_factory_data!

I can add more if needed.

printenv:
baudrate=115200
eth1addr=00:11:22:33:44:56
ethact=eth0
ethaddr=00:11:22:33:44:55
fdt_high=0x4A400000
fdtcontroladdr=4a9d4004
flash_type=11
has_default_mac=1
machid=8040002
mtddevname=fdata
mtddevnum=0
mtdids=nand0=nand0
mtdparts=mtdparts=nand0:0x900000@0x5ac0000(fdata),
nand_erasesize=20000
nand_oobsize=40
nand_writesize=800
partition=nand0,0
soc_hw_version=20180101
soc_version_major=1
soc_version_minor=1
stderr=serial@78AF000
stdin=serial@78AF000
stdout=serial@78AF000

And help:

?       - alias for 'help'
ar8xxx_dump- Dump ar8xxx registers
base    - print or set address offset
bdinfo  - print Board Info structure
bootelf - Boot from an ELF image in memory
bootipq - bootipq from flash device
bootm   - boot application image from memory
bootp   - boot image via network using BOOTP/TFTP protocol
bootvx  - Boot vxWorks from an ELF image
bootz   - boot Linux zImage image from memory
canary  - test stack canary
chpart  - change active partition
cmp     - memory compare
coninfo - print console devices and information
cp      - memory copy
crc32   - checksum calculation
dhcp    - boot image via network using DHCP/TFTP protocol
dm      - Driver model low level access
echo    - echo args to console
editenv - edit environment variable
env     - environment handling commands
erase   - erase FLASH memory
exectzt - execute TZT

exit    - exit script
false   - do nothing, unsuccessfully
fdt     - flattened device tree utility commands
flash   - flash part_name 
        flash part_name load_addr file_size 

flasherase- flerase part_name 

flinfo  - print FLASH memory information
fuseipq - fuse QFPROM registers from memory

go      - start application at address 'addr'
help    - print command description/usage
httpd   - Start httpd server
i2c     - I2C sub-system
imxtract- extract a part of a multi-image
ipq5018_mdio- IPQ5018 mdio utility commands
ipq_mdio- IPQ mdio utility commands
is_sec_boot_enabled- check secure boot fuse is enabled or not

itest   - return true/false on integer compare
loop    - infinite loop on address range
md      - memory display
mii     - MII utility commands
mm      - memory modify (auto-incrementing address)
mmc     - MMC sub system
mmcinfo - display MMC info
mtdparts- define flash/nand partitions
mtest   - simple RAM read/write test
mw      - memory write (fill)
nand    - NAND sub-system
nboot   - boot from NAND device
nfs     - boot image via network using NFS protocol
nm      - memory modify (constant address)
part    - disk partition related commands
pci     - list and access PCI Configuration Space
ping    - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
reset   - Perform RESET of the CPU
run     - run commands in an environment variable
runmulticore- Enable and schedule secondary cores
saveenv - save environment variables to persistent storage
secure_authenticate- authenticate the signed image

setenv  - set environment variables
setexpr - set environment variable as the result of eval expression
sf      - SPI flash sub-system
showvar - print local hushshell variables
sleep   - delay execution for some time
smeminfo- print SMEM FLASH information
source  - run script from memory
test    - minimal test like /bin/sh
test_mode- set test mode
tftpboot- boot image via network using TFTP protocol
tftpput - TFTP put command, for uploading files to a server
true    - do nothing, successfully
tzt     - load and run tzt

uart    - UART sub-system
ubi     - ubi commands
ubifsload- load file from an UBIFS filesystem
ubifsls - list files in a directory
ubifsmount- mount UBIFS volume
ubifsumount- unmount UBIFS volume
version - print monitor, compiler and linker version
zip     - zip a memory region

Life is busy so I had to step away yesterday. I followed @frollic's instructions for boot into initramfs using tftpboot. I was able to run cat /proc/mtd and got an output!

root@OpenWrt:~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00080000 00020000 "0:SBL1"
mtd1: 00080000 00020000 "0:MIBIB"
mtd2: 00100000 00020000 "0:QSEE"
mtd3: 00040000 00020000 "0:DEVCFG"
mtd4: 00040000 00020000 "0:CDT"
mtd5: 00080000 00020000 "0:APPSBLENV"
mtd6: 00140000 00020000 "0:APPSBL"
mtd7: 00100000 00020000 "0:ART"
mtd8: 00080000 00020000 "0:TRAINING"
mtd9: 00080000 00020000 "u_env"
mtd10: 00040000 00020000 "s_env"
mtd11: 00040000 00020000 "devinfo"
mtd12: 05200000 00020000 "kernel"
mtd13: 04a00000 00020000 "rootfs"
mtd14: 02740000 00020000 "alt_kernel"
mtd15: 01f40000 00020000 "alt_rootfs"
mtd16: 00000000 00020000 "sysdiag"
mtd17: 00000000 00020000 "syscfg"

I think this is correct, so I'm going to full send with this information xD

Sadly I'm gonna have to put a pause on this today as I'm running out of time. I've been able to compile an openwrt binary using the following dts file. With that being said, when I try to boot the initramfs version it locks up and reboots... So something isn't right yet. I used Gemini to help me craft this so I'm sure it's wrong somewhere lol.

I have hit another snag though, the initramfs I used to boot into the device fails to bring up a physical eth connection. So I really need to get my initramfs up and running so I can flash the device.

/dts-v1/;

#include "ipq5018.dtsi"
#include "ipq5018-mx-base.dtsi"

/ {
    model = "TP-Link Deco X55 v1.6";
    compatible = "tplink,deco-x55-v1.6", "qcom,ipq5018";

    aliases {
        serial0 = &uart0;
        // Standard flash alias for OpenWrt build system
        mmc0 = &qpic_nand;
    };

    memory@40000000 {
        device_type = "memory";
        // 512MB RAM (0x20000000) starting at 0x40000000
        reg = <0x0 0x40000000 0x0 0x20000000>;
    };
};

// --- DEFINITIVE NAND PARTITION LAYOUT (Based on /proc/mtd) ---
&qpic_nand {
    status = "okay";
    label = "flash"; // Add a general label for the flash device

    partitions {
        compatible = "fixed-partitions";
        #address-cells = <1>;
        #size-cells = <1>;
        
        // MTD offsets: 0x4c0000 is the start of the firmware

        partition@0 {
            label = "sbl";
            reg = <0x00000000 0x00100000>; // Covers mtd0, mtd1, mtd2
        };

        partition@100000 {
            label = "appsbl";
            reg = <0x00100000 0x00140000>; // Covers mtd5, mtd6
        };
        
        partition@240000 {
            label = "config";
            reg = <0x00240000 0x00180000>; // Covers mtd3, mtd4, mtd8, mtd9, mtd10, mtd11
        };
        
        partition@3c0000 {
            label = "art";
            reg = <0x003c0000 0x00100000>; // Covers mtd7
            read-only;
        };
        
        // --- FIRMWARE SLOTS (A/B Structure) ---
        
        // Slot A: mtd12 (kernel) + mtd13 (rootfs)
        partition@4c0000 {
            label = "firmware_A";
            reg = <0x004c0000 0x9c00000>; 
            
            partitions {
                compatible = "fixed-partitions";
                #address-cells = <1>;
                #size-cells = <1>;
                
                partition@0 {
                    label = "kernel";
                    reg = <0x00000000 0x05200000>; // mtd12
                };

                partition@5200000 {
                    label = "rootfs";
                    reg = <0x05200000 0x04a00000>; // mtd13
                    compatible = "openwrt,ubi";
                };
            };
        };

        // Slot B: mtd14 (alt_kernel) + mtd15 (alt_rootfs)
        partition@a0c0000 {
            label = "firmware_B";
            reg = <0x0a0c0000 0x4680000>; 
            
            partitions {
                compatible = "fixed-partitions";
                #address-cells = <1>;
                #size-cells = <1>;

                partition@0 {
                    label = "alt_kernel";
                    reg = <0x00000000 0x02740000>; // mtd14
                };
                
                partition@2740000 {
                    label = "alt_rootfs";
                    reg = <0x02740000 0x01f40000>; // mtd15
                    compatible = "openwrt,ubi";
                };
            };
        };
        
        // Final Log and Config partitions (sysdiag, syscfg)
        // The remaining space up to 0x10000000 (256 MiB) for user data/logs
        partition@e740000 {
            label = "user_data";
            reg = <0x0e740000 0x018c0000>;
        };
    };
};

/*
 * ===============================================================
 * Network Configuration (RTL8367S Switch)
 * ===============================================================
 */

&switch {
    status = "disabled";
};

&dp2 {
    status = "okay";
    nvmem-cells = <&hw_mac_addr 0>;
    nvmem-cell-names = "mac-address";
};

&mdio1 {
    status = "okay";
    pinctrl-0 = <&mdio1_pins>;
    pinctrl-names = "default";
    reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>;

    // Realtek RTL8367S Switch
    rtl8367s_switch: switch@0 {
        compatible = "realtek,rtl8367s";
        reg = <0>;
        #address-cells = <1>;
        #size-cells = <0>;
        
        realtek,ext-switch-port = <5>;
        
        ports {
            #address-cells = <1>;
            #size-cells = <0>;

            port@0 { reg = <0>; label = "lan2"; };
            port@1 { reg = <1>; label = "lan1"; };
            port@2 { reg = <2>; label = "wan"; };
            
            // CPU (GMAC DP2)
            port@5 {
                reg = <5>;
                label = "cpu";
                ethernet = <&dp2>;
                phy-mode = "rgmii";
                fixed-link {
                    speed = <1000>;
                    full-duplex;
                };
            };
        };
    };
};

/*
 * ===============================================================
 * Wireless LAN Configuration
 * ===============================================================
 */

&q6v5_wcss {
    status = "okay";

    q6_wcss_pd1: remoteproc-pd1 {
        compatible = "qcom,ipq5018-q6v5-wcss-pd";
        reg = <0x0 0x48400000 0x0 0x400000>;
        interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
        qcom,msa-name = "wcss_wlan0";
        label = "wlan0";
        qcom,subsystem-name = "wcss_wlan0";
    };

    q6_wcss_pd2: remoteproc-pd2 {
        compatible = "qcom,ipq5018-q6v5-wcss-pd";
        reg = <0x0 0x48800000 0x0 0x400000>;
        interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>;
        qcom,msa-name = "wcss_wlan1";
        label = "wlan1";
        qcom,subsystem-name = "wcss_wlan1";
    };
};

&wifi0 {
    // IPQ5018 (Internal 2.4 GHz)
    qcom,rproc = <&q6_wcss_pd1>;
    qcom,ath11k-calibration-variant = "Default"; 
    qcom,ath11k-fw-memory-mode = <1>;
    qcom,bdf-addr = <0x4c400000>; 
    status = "okay";
};

&wifi1 {
    // QCN6102 (External 5 GHz via PCI-E)
    qcom,rproc = <&q6_wcss_pd2>;
    qcom,userpd-subsys-name = "q6v5_wcss_userpd2";
    qcom,ath11k-calibration-variant = "Default";
    qcom,ath11k-fw-memory-mode = <1>;
    qcom,bdf-addr = <0x4d100000>; 
    qcom,m3-dump-addr = <0x4df00000>;
    status = "okay";
};

/*
 * ===============================================================
 * Console Setup (UART0 & UART1) - FINAL SYNTAX FIX
 * ===============================================================
 */
&soc {
    // UART0 (Primary Console) - Confirmed 0x78b0000
    // Labeled as uart0: so the /aliases node can find it.
    uart0: serial@78b0000 {
        status = "okay";
    };

    // UART1 (Secondary Port) - Confirmed 0x78af000
    // Labeled as uart1: for completeness.
    uart1: serial@78af000 {
        status = "okay";
    };
};

One small update, I was able to stop the power cycling by switching the network modules:
kmod-qca-nss-dp, and kmod-qca-ssdkfrom

from built-in to module. I don't have time right this second, but I need to:

A. Confirm if those are even the right modules to be using.
B. Figure out the right modules to be using.

I lied, I didn't give up yet even though I was supposed to leave my office an hour ago :rofl:

I commented out my serial console @78b0000 and used the following bootargs:

setenv bootargs "console=ttyMSM0,115200 earlycon=msm_geni_serial,0x078af000 root=/dev/ram0 init=/sbin/init"

This gave me a shell on my compiled initramfs. This is a huge step forward for me as I can now play around with different kernel packages/settings and see if I can get something to work!

This time I am going offline for the evening xD

Careful!
Flash partitions are hardcoded for most devices, so what you see would be correct for the other device you nicked the initramfs from, but NOT for your device. As long as you're running purely from RAM and don't touch the flash, that's all fine - but don't think about taking those readings for granted or writing to flash (which would be fatal).

oh snap, that's really good to know. I'm not doing any flashing yet as I'm trying to make sure my physical ports and wireless work before I go flashing. (Struggling with this!)

Any idea how I can get the correct partitions?!

You need to get that information from the OEM firmware, there is no other way. /proc/mtd, dmesg, extracting the FDT, ubiinfo, …

I decided to give this part another go, and interrupted the boot and throw a couple bootenvs in before booting.

Turns out it outputted:

 [    0.840652] nand: GigaDevice F50D1G41LB(2M) SPI NAND 1G 1.8V
[    0.847020] nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
[    0.852980] 16 ofpart partitions found on MTD device qcom_nand.0
[    0.860045] Creating 16 MTD partitions on "qcom_nand.0":
[    0.866192] 0x000000000000-0x000000080000 : "0:SBL1"
[    0.873029] 0x000000080000-0x000000100000 : "0:MIBIB"
[    0.877898] 0x000000100000-0x000000140000 : "0:BOOTCONFIG"
[    0.882559] 0x000000140000-0x000000180000 : "0:BOOTCONFIG1"
[    0.888080] 0x000000180000-0x000000280000 : "0:QSEE"
[    0.894090] 0x000000280000-0x0000002c0000 : "0:DEVCFG"
[    0.898655] 0x0000002c0000-0x000000300000 : "0:CDT"
[    0.903566] 0x000000300000-0x000000380000 : "0:APPSBLENV"
[    0.908650] 0x000000380000-0x0000004c0000 : "0:APPSBL"
[    0.914727] 0x0000004c0000-0x0000005c0000 : "0:ART"
[    0.919634] 0x0000005c0000-0x000000640000 : "0:TRAINING"
[    0.923933] 0x000000640000-0x000003040000 : "rootfs"
[    0.962091] mtd: device 11 (rootfs) set to be root filesystem
[    0.962357] mtdsplit: no squashfs found in "rootfs"
[    0.966881] 0x000003040000-0x000005a40000 : "rootfs_1"
[    1.005206] 0x000005a40000-0x000005ac0000 : "0:ETHPHYFW"
[    1.006673] 0x000005ac0000-0x0000063c0000 : "factory_data"
[    1.017806] 0x0000063c0000-0x0000074c0000 : "runtime_data" 

Does this look more accurate? This was on boot with the factory firmware

It looks like TP-link allows you to download source from their GPL Code Center, but I'm struggling to find the .dts file they used.

If anyone can give some guidance on how to find this that would be great :smiley:

Small Update! I contacted TP-Link and they added the x55 GPL code.

I can't do anything with it tonight, but figured I'd put the link here in case anyone else has time to look before me :smiling_face: