Porting OpenWrt to TP-Link Deco M9 Plus v2: A Comprehensive Guide

Introduction

This guide documents the process of adapting OpenWrt firmware for the TP-Link Deco M9 Plus version 2.0 router, which is based on the Qualcomm IPQ4019 chipset. While OpenWrt already supports the IPQ4019 platform, adding support for a specific device requires careful configuration of device-specific parameters including memory layout, flash partitioning, network interfaces, and GPIO assignments.

Hardware Overview

The target device features the following specifications:

Software Adaptation

Device Profile Creation

Working within the IPQ40xx target directory, we establish a new device profile following OpenWrt's naming conventions. Based on existing Deco series entries, the identifeir tplink,deco-m9-plus-v2 is adopted.

The integration involves updating several key files in the base system:

Network Configuration

## File: target/linux/ipq40xx/base-files/etc/board.d/02_network
## Add the new device to the interface mapping

case "$board" in
tplink,deco-m9-plus-v2|\
asus,map-ac2200|\
edgecore,ecw5211|\
...)
    ucidef_set_interfaces_lan_wan "eth1" "eth2"
    ;;
esac

Wireless Calibration Data

## File: target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
## Extract calibration data from ART partition

"ath10k/pre-cal-pci-0000:01:00.0.bin")
    case "$board" in
    tplink,deco-m9-plus-v2|\
    asus,map-ac2200)
        caldata_extract_ubi "art" 0x9000 0x2f20
        ;;
    esac
    ;;

"ath10k/pre-cal-ahb-a000000.wifi.bin")
    case "$board" in
    tplink,deco-m9-plus-v2|\
    asus,map-ac2200|\
    asus,rt-ac58u)
        caldata_extract_ubi "art" 0x1000 0x2f20
        ;;
    esac
    ;;

MAC Address Assignment

## File: target/linux/ipq40xx/base-files/lib/preinit/05_set_iface_mac_ipq40xx.sh
## Configure interface MAC addresses based on factory data

preinit_configure_mac_addresses() {
    case $(board_name) in
    tplink,deco-m9-plus-v2)
        base_mac=$(mtd_get_mac_binary_ubi Factory 0x1006)
        ip link set dev eth1 address $(macaddr_add "$base_mac" 1)
        ip link set dev eth2 address $(macaddr_add "$base_mac" 3)
        ;;
    esac
}

Image Build Configuration

## File: target/linux/ipq40xx/image/generic.mk
## Define build parameters for the new device

define Device/tplink_deco-m9-plus-v2
    $(call Device/FitzImage)
    $(call Device/UbiFit)
    DEVICE_VENDOR := TP-Link
    DEVICE_MODEL := Deco M9 Plus
    DEVICE_VARIANT := v2
    SOC := qcom-ipq4019
    DEVICE_DTS_CONFIG := config@ap.dk07.1-c1
    KERNEL_INITRAMFS_SUFFIX := -recovery.itb
    IMAGE/factory.ubi := append-ubi
    PAGESIZE := 2048
    BLOCKSIZE := 128k
    KERNEL_SIZE := 4096k
    IMAGE_SIZE := 31232k
    KERNEL_IN_UBI := 1
    DEVICE_PACKAGES := ath10k-firmware-qca9888-ct \
        kmod-fs-ext4 kmod-mmc kmod-spi-dev mkf2fs e2fsprogs kmod-fs-f2fs
endef
TARGET_DEVICES += tplink_deco-m9-plus-v2

Device Tree Configuration

The Device Tree Source (DTS) file is the most critical component. Create qcom-ipq4019-deco-m9-plus-v2.dts in the appropriate directory. Many elements can be inherited from the common IPQ4019 DTSI file.

Memory Layout

Configure the 512MB memory mapping:

memory {
    device_type = "memory";
    reg = <0x80000000 0x20000000>;
};

Flash Partitioning

Based on the bootloader output, define the NAND partitions. The ART partition contains calibration data and MAC addresses:

&nand {
    status = "okay";

    partitions {
        compatible = "fixed-partitions";
        #address-cells = <1>;
        #size-cells = <1>;

        partition@0 {
            label = "sbl1";
            reg = <0x0 0x100000>;
            read-only;
        };

        partition@100000 {
            label = "mibib";
            reg = <0x100000 0x100000>;
            read-only;
        };

        /* ... additional bootloader partitions ... */

        partition@880000 {
            label = "art";
            reg = <0x880000 0x80000>;
            read-only;
            compatible = "nvmem-cells";
            #address-cells = <1>;
            #size-cells = <1>;

            wifi_cal_2g: caldata@1000 {
                reg = <0x1000 0x2f20>;
            };

            wifi_cal_5g_primary: caldata@5000 {
                reg = <0x5000 0x2f20>;
            };

            wifi_cal_5g_secondary: caldata@9000 {
                reg = <0x9000 0x2f20>;
            };

            mac_base: macaddr@0 {
                reg = <0x0 0x6>;
            };
        };

        partition@900000 {
            label = "ubi";
            reg = <0x900000 0x7680000>;
        };
    };
};

Wireless Radios Configuration

Configure the three wireless interfaces (2.4GHz, 5GHz primary, and 5GHz secondary):

&wifi0 {
    status = "okay";
    qcom,ath10k-calibration-variant = "Deco-M9-Plus-v2";
    nvmem-cell-names = "pre-calibration", "mac-address";
    nvmem-cells = <&wifi_cal_2g>, <&mac_base>;
    mac-address-increment = <1>;
};

&wifi1 {
    status = "okay";
    qcom,ath10k-calibration-variant = "Deco-M9-Plus-v2";
    nvmem-cell-names = "pre-calibration", "mac-address";
    nvmem-cells = <&wifi_cal_5g_primary>, <&mac_base>;
    mac-address-increment = <2>;
};

&pcie0 {
    status = "okay";
    perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
    wake-gpios = <&tlmm 40 GPIO_ACTIVE_LOW>;

    bridge@0,0 {
        reg = <0x00000000 0 0 0 0>;
        #address-cells = <3>;
        #size-cells = <2>;
        ranges;

        wifi2: wifi@1,0 {
            compatible = "qcom,ath10k";
            reg = <0x00010000 0 0 0 0>;
            qcom,ath10k-calibration-variant = "Deco-M9-Plus-v2";
            nvmem-cell-names = "pre-calibration", "mac-address";
            nvmem-cells = <&wifi_cal_5g_secondary>, <&mac_base>;
        };
    };
};

Ethernet Ports

&swport4 {
    status = "okay";
    label = "eth2";
    nvmem-cell-names = "mac-address";
    nvmem-cells = <&mac_base>;
};

&swport5 {
    status = "okay";
    label = "eth1";
    nvmem-cell-names = "mac-address";
    nvmem-cells = <&mac_base>;
};

GPIO Assignments

LED indicators and reset button configuration:

leds {
    compatible = "gpio-leds";
    system_red: led-0 {
        label = "red:system";
        gpios = <&tlmm 0 GPIO_ACTIVE_LOW>;
    };

    system_green: led-1 {
        label = "green:system";
        gpios = <&tlmm 1 GPIO_ACTIVE_LOW>;
    };

    system_blue: led-2 {
        label = "blue:system";
        gpios = <&tlmm 2 GPIO_ACTIVE_LOW>;
    };
};

keys {
    compatible = "gpio-keys";
    reset {
        label = "reset";
        gpios = <&tlmm 18 GPIO_ACTIVE_LOW>;
        linux,code = <key_restart>;
    };
};
</key_restart>

Build Process

After completing the configuration files, build the firmware:

make menuconfig
# Select Target System: Qualcomm Atheros IPQ40xx
# Select Target Profile: TP-Link Deco M9 Plus v2

make -j$(nproc) V=s

Successful compilation produces these artifacts:

openwrt-ipq40xx-generic-tplink_deco-m9-plus-v2-initramfs-recovery.itb
openwrt-ipq40xx-generic-tplink_deco-m9-plus-v2-squashfs-factory.ubi
openwrt-ipq40xx-generic-tplink_deco-m9-plus-v2-squashfs-sysupgrade.bin

Installation

Flash via U-Boot console:

# Erase UBI partition
nand erase 0x900000 0x7680000

# Load factory image via TFTP
setenv serverip 192.168.0.66
setenv ipaddr 192.168.0.171
tftpboot openwrt-ipq40xx-generic-tplink_deco-m9-plus-v2-squashfs-factory.ubi

# Write to NAND
nand write 0x84000000 0x900000 $filesize

# Configure boot environment
setenv bootargs 'ubi.mtd=ubi root=/dev/ubiblock0_1 rootfstype=squashfs,ubi'
setenv mtdids 'nand0=nand0'
setenv mtdparts 'mtdparts=nand0:0x900000@0x0(boot),0x7680000@0x900000(fs)'
setenv bootkernel 'ubi read 0x84000000 kernel && bootm'
setenv bootcmd 'run bootkernel'

# Save and boot
saveenv
boot

Troubleshooting

If the QCA9886 radio fails to initialize with firmware errors, ensure the correct packages are included in the build configuration:

CONFIG_PACKAGE_ath10k-firmware-qca9888=y
CONFIG_PACKAGE_ath10k-board-qca9888=y

These packages provide the necessary firmware files for the secondary 5GHz radio.

Tags: openwrt ipq4019 deco-m9-plus device-tree firmware-porting

Posted on Sun, 17 May 2026 00:00:51 +0000 by mr.echo