diff --git a/scripts/cfe-tplink-crcfix.py b/scripts/cfe-tplink-crcfix.py
new file mode 100755
index 0000000000..2db6d6cce9
--- /dev/null
+++ b/scripts/cfe-tplink-crcfix.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2022 OpenWrt.org, based on cameo-tag.py
+#
+# this tool fixes the CRC32 (kernel+rootfs) found in CFE headers from
+# Tp-Link bcm63xx devices. It doesn't recalculate header checksums
+# since CFE doesn't make any header integrity verification to boot OpenWrt.
+
+import argparse
+import os
+import struct
+import zlib
+
+READ_UNTIL_EOF = -1
+CFE_HEADER_SIZE = 512
+
+def read_buffer(offset, count):
+    args.cfeimage_file.seek(offset)
+    return bytearray(args.cfeimage_file.read(count))
+
+def write_buffer(whence, buf):
+    args.cfeimage_file.seek(0, whence)
+    args.cfeimage_file.write(buf)
+
+def invertcrc(buf):
+    return (zlib.crc32(buf) ^ 0xffffffff).to_bytes(4, 'big')
+
+def checksum_header(buf):
+    BINCRC32 = args.crc_offset
+    ROOTFSADDRESS = buf[124:128]
+    ROOTFSLEN = buf[128:132]
+    IMLEN = struct.unpack('>i', ROOTFSADDRESS)[0] + struct.unpack('>i', ROOTFSLEN)[0] + CFE_HEADER_SIZE
+    buf[BINCRC32:BINCRC32+4] = invertcrc(buf[CFE_HEADER_SIZE:IMLEN])
+    return buf
+
+parser = argparse.ArgumentParser(description='Insert CRC in tplink CFE firmware tags.')
+parser.add_argument('cfeimage_file', type=argparse.FileType('r+b'))
+# crc_offset should be 148 or 152
+parser.add_argument('crc_offset', type=int)
+args = parser.parse_args()
+
+args.cfeimage_file.seek(0, os.SEEK_END)
+if args.cfeimage_file.tell() <= CFE_HEADER_SIZE:
+    raise ValueError(f"CFE image must be larger than {CFE_HEADER_SIZE} bytes")
+
+buf = checksum_header(read_buffer(0, READ_UNTIL_EOF))
+write_buffer(os.SEEK_SET, buf)
diff --git a/target/linux/bcm63xx/image/Makefile b/target/linux/bcm63xx/image/Makefile
index f35358173c..019596e004 100644
--- a/target/linux/bcm63xx/image/Makefile
+++ b/target/linux/bcm63xx/image/Makefile
@@ -143,6 +143,15 @@ define Build/cfe-jffs2-cferam
 	rm -f $@.kernel
 endef
 
+define Build/cfe-kernel-header
+	$(TOPDIR)/scripts/cfe-bin-header.py \
+		--input-file $@ \
+		--output-file $@-tmp \
+		--load-addr $(if $(DEVICE_LOADADDR),$(DEVICE_LOADADDR),$(LOADER_ENTRY)) \
+		--entry-addr $(if $(DEVICE_LOADADDR),$(DEVICE_LOADADDR),$(LOADER_ENTRY))
+	mv $@-tmp $@
+endef
+
 define Build/cfe-jffs2-kernel
 	rm -rf $@-kernel
 	mkdir -p $@-kernel
@@ -274,6 +283,20 @@ define Build/zyxel-bin
 	mv $@.zyxel $@
 endef
 
+define Build/tplink-prepend-cfe
+	dd if=$(KDIR)/bcm63xx-cfe/$(CFE_BIN_FILE) bs=128k conv=sync of=$@.cfeprepend
+	dd if=$@ >> $@.cfeprepend
+	mv $@.cfeprepend $@
+endef
+
+define Build/tplink-sysupgrade
+	# append 512 bytes to avoid computing CRC for data beyond jffs2 EOF mark
+	dd if=/dev/null bs=512 count=1 >> $(IMAGE_ROOTFS)
+	$(call Build/tplink-v2-image, -v 0.9.1 -s)
+	# insert ~CRC32 (rootfs+kernel) to allow CFE booting Openwrt
+	$(TOPDIR)/scripts/cfe-tplink-crcfix.py $@ $(TPLINK_CRC_OFFSET)
+endef
+
 define Build/redboot-bin
 	# Prepare kernel and rootfs
 	dd if=$(IMAGE_KERNEL) of=$(BIN_DIR)/$(REDBOOT_PREFIX)-vmlinux.gz bs=65536 conv=sync
diff --git a/target/linux/bcm63xx/image/bcm63xx.mk b/target/linux/bcm63xx/image/bcm63xx.mk
index 97959d7819..bbf4da6505 100644
--- a/target/linux/bcm63xx/image/bcm63xx.mk
+++ b/target/linux/bcm63xx/image/bcm63xx.mk
@@ -5,9 +5,12 @@
 
 DEVICE_VARS += HCS_MAGIC_BYTES HCS_REV_MIN HCS_REV_MAJ
 DEVICE_VARS += BLOCK_SIZE FLASH_MB IMAGE_OFFSET
+DEVICE_VARS += CFE_BIN_FILE
 DEVICE_VARS += CFE_BOARD_ID CFE_EXTRAS
 DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_REGION
 DEVICE_VARS += REDBOOT_PREFIX
+DEVICE_VARS += TPLINK_HWID TPLINK_HWREV TPLINK_FLASHLAYOUT
+DEVICE_VARS += TPLINK_HWREVADD TPLINK_HVERSION TPLINK_CRC_OFFSET
 
 define Device/bcm33xx
   KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | loader-lzma bin | hcs-initramfs
@@ -59,6 +62,22 @@ define Device/bcm63xx_redboot
   REDBOOT_PREFIX := $$(DEVICE_IMG_PREFIX)
 endef
 
+define Device/bcm63xx_tplink
+  FILESYSTEMS := squashfs
+  DEVICE_VENDOR := TP-Link
+  TPLINK_HWID := 0x0
+  TPLINK_HWREV := 0x1
+  TPLINK_HWREVADD := 0x0
+  TPLINK_HVERSION := 3
+  TPLINK_FLASHLAYOUT :=
+  TPLINK_CRC_OFFSET := 152
+  CFE_BIN_FILE :=
+  KERNEL := kernel-bin | append-dtb | relocate-kernel | lzma | cfe-kernel-header
+  IMAGES := factory.bin sysupgrade.bin
+  IMAGE/sysupgrade.bin := tplink-sysupgrade
+  IMAGE/factory.bin := tplink-sysupgrade | tplink-prepend-cfe
+endef
+
 ### Generic ###
 define Device/brcm_bcm963281tan
   $(Device/bcm63xx)
