diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile
index 862ad7da004b..5c89d413c0d3 100644
--- a/target/linux/ipq806x/Makefile
+++ b/target/linux/ipq806x/Makefile
@@ -5,10 +5,10 @@ include $(TOPDIR)/rules.mk
 ARCH:=arm
 BOARD:=ipq806x
 BOARDNAME:=Qualcomm Atheros IPQ806X
-FEATURES:=squashfs nand fpu ramdisk
+FEATURES:=squashfs fpu ramdisk
 CPU_TYPE:=cortex-a15
 CPU_SUBTYPE:=neon-vfpv4
-SUBTARGETS:=generic
+SUBTARGETS:=generic chromium
 
 KERNEL_PATCHVER:=5.15
 
diff --git a/target/linux/ipq806x/base-files/etc/board.d/01_leds b/target/linux/ipq806x/base-files/etc/board.d/01_leds
index 2b259b903614..80a337c6a4d4 100644
--- a/target/linux/ipq806x/base-files/etc/board.d/01_leds
+++ b/target/linux/ipq806x/base-files/etc/board.d/01_leds
@@ -9,6 +9,9 @@ board_config_update
 board=$(board_name)
 
 case "$board" in
+asus,onhub)
+	ucidef_set_led_default "status" "STATUS" "LED_Green" "1"
+	;;
 buffalo,wxr-2533dhp)
 	ucidef_set_led_wlan "wlan" "WLAN" "white:wireless" "phy0tpt"
 	ucidef_set_led_switch "wan" "WAN" "white:internet" "switch0" "0x20"
@@ -58,6 +61,14 @@ tplink,c2600)
 	ucidef_set_led_switch "wan" "wan" "white:wan" "switch0" "0x20"
 	ucidef_set_led_switch "lan" "lan" "white:lan" "switch0" "0x1e"
 	;;
+tplink,onhub)
+	ucidef_set_led_default "led0_red" "LED0_Red" "LED0_Red" "1"
+	ucidef_set_led_default "led1_green" "LED1_Green" "LED1_Green" "1"
+	ucidef_set_led_default "led2_blue" "LED2_Blue" "LED2_Blue" "1"
+	ucidef_set_led_default "led3_red" "LED3_Red" "LED3_Red" "1"
+	ucidef_set_led_default "led4_green" "LED4_Green" "LED4_Green" "1"
+	ucidef_set_led_default "led5_blue" "LED5_Blue" "LED5_Blue" "1"
+	;;
 tplink,vr2600v)
 	ucidef_set_led_usbport "usb" "USB" "white:usb" "usb1-port1" "usb2-port1" "usb3-port1" "usb4-port1"
 	ucidef_set_led_switch "lan" "lan" "white:lan" "switch0" "0x1e"
diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network b/target/linux/ipq806x/base-files/etc/board.d/02_network
index dbff854731a4..f38876b69f08 100644
--- a/target/linux/ipq806x/base-files/etc/board.d/02_network
+++ b/target/linux/ipq806x/base-files/etc/board.d/02_network
@@ -79,6 +79,12 @@ tplink,ad7200)
 	ucidef_add_switch "switch0" \
 		"2:lan:1" "3:lan:2" "4:lan:3" "5:lan:4" "6@eth1" "1:wan" "0@eth0"
 	;;
+asus,onhub |\
+tplink,onhub)
+	ucidef_set_interfaces_lan_wan "eth1" "eth0"
+	ucidef_add_switch "switch0" \
+		"1:lan" "6@eth1" "2:wan" "0@eth0"
+	;;
 ubnt,unifi-ac-hd)
 	ucidef_set_interface_lan "eth0 eth1"
 	;;
diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
index f9e592f4bd8f..ada895319b65 100644
--- a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
@@ -57,6 +57,14 @@ platform_do_upgrade() {
 		MTD_CONFIG_ARGS="-s 0x200000"
 		default_do_upgrade "$1"
 		;;
+	asus,onhub |\
+	tplink,onhub)
+		export_bootdevice
+		export_partdevice CI_ROOTDEV 0
+		CI_KERNPART="kernel"
+		CI_ROOTPART="rootfs"
+		emmc_do_upgrade "$1"
+		;;
 	tplink,vr2600v)
 		MTD_CONFIG_ARGS="-s 0x200000"
 		default_do_upgrade "$1"
@@ -69,3 +77,13 @@ platform_do_upgrade() {
 		;;
 	esac
 }
+
+platform_copy_config() {
+	case "${board_name}" in
+	asus,onhub |\
+	tplink,onhub)
+		emmc_copy_config
+		;;
+	esac
+	return 0
+}
diff --git a/target/linux/ipq806x/chromium/config-default b/target/linux/ipq806x/chromium/config-default
new file mode 100644
index 000000000000..d7db9f7db35a
--- /dev/null
+++ b/target/linux/ipq806x/chromium/config-default
@@ -0,0 +1,13 @@
+CONFIG_BLK_DEV_SD=y
+CONFIG_LEDS_LP5523=y
+CONFIG_LEDS_LP55XX_COMMON=y
+CONFIG_PHY_QCOM_IPQ806X_USB=y
+CONFIG_SCSI=y
+CONFIG_SCSI_COMMON=y
+CONFIG_SG_POOL=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_HOST=y
+CONFIG_USB_DWC3_QCOM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_PLATFORM=y
diff --git a/target/linux/ipq806x/chromium/target.mk b/target/linux/ipq806x/chromium/target.mk
new file mode 100644
index 000000000000..3983a9281a5d
--- /dev/null
+++ b/target/linux/ipq806x/chromium/target.mk
@@ -0,0 +1,2 @@
+BOARDNAME:=Google Chromium
+FEATURES += emmc boot-part rootfs-part
diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
new file mode 100644
index 000000000000..7ce41bc92b80
--- /dev/null
+++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014 The ChromiumOS Authors
+ */
+
+#include "qcom-ipq8064-onhub.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/soc/qcom,gsbi.h>
+
+/ {
+	model = "ASUS OnHub";
+	compatible = "asus,onhub", "google,arkham", "qcom,ipq8064";
+
+	chosen {
+		bootargs-append = " maxcpus=1 rootwait";
+	};
+};
+
+&qcom_pinmux {
+	ap3223_pins: ap3223_pinmux {
+		pins = "gpio22";
+		function = "gpio";
+		bias-none;
+	};
+
+	i2c7_pins: i2c7_pinmux {
+		mux {
+			pins = "gpio8", "gpio9";
+			function = "gsbi7";
+		};
+		data {
+			pins = "gpio8";
+			bias-disable;
+		};
+		clk {
+			pins = "gpio9";
+			bias-disable;
+		};
+	};
+};
+
+&gsbi7 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_I2C_UART>;
+};
+
+&gsbi7_i2c {
+	status = "okay";
+	clock-frequency = <100000>;
+	pinctrl-0 = <&i2c7_pins>;
+	pinctrl-names = "default";
+
+	ap3223@1c {
+		compatible = "dynaimage,ap3223";
+		reg = <0x1c>;
+
+		pinctrl-0 = <&ap3223_pins>;
+		pinctrl-names = "default";
+
+		int-gpio = <&qcom_pinmux 22 GPIO_ACTIVE_LOW>;
+	};
+
+	led-controller@32 {
+		compatible = "national,lp5523";
+		reg = <0x32>;
+		clock-mode = /bits/ 8 <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		led@4 {
+			reg = <4>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED_Green";
+			led-cur = /bits/ 8 <0xfa>;
+			max-cur = /bits/ 8 <0xff>;
+		};
+
+		led@5 {
+			reg = <5>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED_Blue";
+			led-cur = /bits/ 8 <0xfa>;
+			max-cur = /bits/ 8 <0xff>;
+		};
+
+		led@8 {
+			reg = <8>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED_Red";
+			led-cur = /bits/ 8 <0xfa>;
+			max-cur = /bits/ 8 <0xff>;
+		};
+	};
+};
diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
new file mode 100644
index 000000000000..7b56e6ff4f5a
--- /dev/null
+++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014 The ChromiumOS Authors
+ */
+
+#include "qcom-ipq8064-smb208.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/soc/qcom,tcsr.h>
+
+/ {
+	aliases {
+		ethernet0 = &gmac0;
+		ethernet1 = &gmac2;
+		mdio-gpio0 = &mdio;
+		serial0 = &gsbi4_serial;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		rsvd@41200000 {
+			reg = <0x41200000 0x300000>;
+			no-map;
+		};
+	};
+
+	mdio: mdio {
+		compatible = "virtual,mdio-gpio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		gpios = <&qcom_pinmux 1 GPIO_ACTIVE_HIGH>,
+			<&qcom_pinmux 0 GPIO_ACTIVE_HIGH>;
+		pinctrl-0 = <&mdio_pins>;
+		pinctrl-names = "default";
+
+		phy0: ethernet-phy@0 {
+			reg = <0>;
+			qca,ar8327-initvals = <
+				0x00004 0x7600000   /* PAD0_MODE */
+				0x00008 0x1000000   /* PAD5_MODE */
+				0x0000c 0x80        /* PAD6_MODE */
+				0x000e4 0xaa545     /* MAC_POWER_SEL */
+				0x000e0 0xc74164de  /* SGMII_CTRL */
+				0x0007c 0x4e        /* PORT0_STATUS */
+				0x00094 0x4e        /* PORT6_STATUS */
+				>;
+		};
+
+		phy1: ethernet-phy@1 {
+			reg = <1>;
+		};
+	};
+
+	soc {
+		rng@1a500000 {
+			status = "disabled";
+		};
+
+		sound {
+			compatible = "google,storm-audio";
+			qcom,model = "ipq806x-storm";
+			cpu = <&lpass>;
+			codec = <&max98357a>;
+		};
+
+		lpass: lpass@28100000 {
+			status = "okay";
+			pinctrl-names = "default", "idle";
+			pinctrl-0 = <&mi2s_default>;
+			pinctrl-1 = <&mi2s_idle>;
+		};
+
+		max98357a: max98357a {
+			compatible = "maxim,max98357a";
+			#sound-dai-cells = <1>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sdmode_pins>;
+			sdmode-gpios = <&qcom_pinmux 25 GPIO_ACTIVE_HIGH>;
+		};
+	};
+};
+
+&qcom_pinmux {
+	rgmii0_pins: rgmii0_pins {
+		mux {
+			pins = "gpio2", "gpio66";
+			drive-strength = <8>;
+			bias-disable;
+		};
+	};
+	mi2s_pins {
+		mi2s_default: mi2s_default {
+			dout {
+				pins = "gpio32";
+				function = "mi2s";
+				drive-strength = <16>;
+				bias-disable;
+			};
+			sync {
+				pins = "gpio27";
+				function = "mi2s";
+				drive-strength = <16>;
+				bias-disable;
+			};
+			clk {
+				pins = "gpio28";
+				function = "mi2s";
+				drive-strength = <16>;
+				bias-disable;
+			};
+		};
+		mi2s_idle: mi2s_idle {
+			dout {
+				pins = "gpio32";
+				function = "mi2s";
+				drive-strength = <2>;
+				bias-pull-down;
+			};
+			sync {
+				pins = "gpio27";
+				function = "mi2s";
+				drive-strength = <2>;
+				bias-pull-down;
+			};
+			clk {
+				pins = "gpio28";
+				function = "mi2s";
+				drive-strength = <2>;
+				bias-pull-down;
+			};
+		};
+	};
+
+	mdio_pins: mdio_pins {
+		mux {
+			pins = "gpio0", "gpio1";
+			function = "gpio";
+			drive-strength = <8>;
+			bias-disable;
+		};
+		rst {
+			pins = "gpio26";
+			output-low;
+		};
+	};
+
+	sdmode_pins: sdmode_pinmux {
+		pins = "gpio25";
+		function = "gpio";
+		drive-strength = <16>;
+		bias-disable;
+	};
+
+	sdcc1_pins: sdcc1_pinmux {
+		mux {
+			pins = "gpio38", "gpio39", "gpio40",
+			       "gpio41", "gpio42", "gpio43",
+			       "gpio44", "gpio45", "gpio46",
+			       "gpio47";
+			function = "sdc1";
+		};
+		cmd {
+			pins = "gpio45";
+			drive-strength = <10>;
+			bias-pull-up;
+		};
+		data {
+			pins = "gpio38", "gpio39", "gpio40",
+			       "gpio41", "gpio43", "gpio44",
+			       "gpio46", "gpio47";
+			drive-strength = <10>;
+			bias-pull-up;
+		};
+		clk {
+			pins = "gpio42";
+			drive-strength = <16>;
+			bias-pull-down;
+		};
+	};
+
+	i2c1_pins: i2c1_pinmux {
+		pins = "gpio53", "gpio54";
+		function = "gsbi1";
+		bias-disable;
+	};
+
+	rpm_i2c_pinmux: rpm_i2c_pinmux {
+		mux {
+			pins = "gpio12", "gpio13";
+			function = "gsbi4";
+			drive-strength = <12>;
+			bias-disable;
+		};
+	};
+
+	spi_pins: spi_pins {
+		mux {
+			pins = "gpio18", "gpio19", "gpio21";
+			function = "gsbi5";
+			bias-pull-down;
+			/delete-property/ bias-none;
+			/delete-property/ drive-strength;
+		};
+		data {
+			pins = "gpio18", "gpio19";
+			drive-strength = <10>;
+		};
+		cs {
+			pins = "gpio20";
+			drive-strength = <10>;
+			bias-pull-up;
+		};
+		clk {
+			pins = "gpio21";
+			drive-strength = <12>;
+		};
+	};
+
+	fw_pinmux {
+		wp {
+			pins = "gpio17";
+			output-low;
+		};
+		recovery {
+			pins = "gpio16";
+			bias-none;
+		};
+		developer {
+			pins = "gpio15";
+			bias-none;
+		};
+	};
+
+	spi6_pins: spi6_pins {
+		mux {
+			pins = "gpio55", "gpio56", "gpio58";
+			function = "gsbi6";
+			bias-pull-down;
+		};
+		data {
+			pins = "gpio55", "gpio56";
+			drive-strength = <10>;
+		};
+		cs {
+			pins = "gpio57";
+			drive-strength = <10>;
+			bias-pull-up;
+			output-high;
+		};
+		clk {
+			pins = "gpio58";
+			drive-strength = <12>;
+		};
+	};
+};
+
+&gmac0 {
+	status = "okay";
+	phy-mode = "rgmii";
+	qcom,id = <0>;
+	phy-handle = <&phy1>;
+
+	pinctrl-0 = <&rgmii0_pins>;
+	pinctrl-names = "default";
+
+	fixed-link {
+		speed = <1000>;
+		full-duplex;
+	};
+};
+
+&gmac2 {
+	status = "okay";
+	phy-mode = "sgmii";
+	qcom,id = <2>;
+	phy-handle = <&phy0>;
+
+	fixed-link {
+		speed = <1000>;
+		full-duplex;
+	};
+};
+
+&gsbi1 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_I2C_UART>;
+};
+
+&gsbi1_i2c {
+	status = "okay";
+
+	clock-frequency = <100000>;
+
+	pinctrl-0 = <&i2c1_pins>;
+	pinctrl-names = "default";
+
+	tpm@20 {
+		compatible = "infineon,slb9645tt";
+		reg = <0x20>;
+		powered-while-suspended;
+	};
+};
+
+&gsbi4 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_I2C_UART>;
+};
+
+&gsbi4_serial {
+	status = "okay";
+};
+
+&gsbi5 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_SPI>;
+
+	spi4: spi@1a280000 {
+		status = "okay";
+		spi-max-frequency = <50000000>;
+		pinctrl-0 = <&spi_pins>;
+		pinctrl-names = "default";
+
+		cs-gpios = <&qcom_pinmux 20 0>;
+
+		flash: flash@0 {
+			compatible = "jedec,spi-nor";
+			spi-max-frequency = <50000000>;
+			reg = <0>;
+		};
+	};
+};
+
+&gsbi6 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_SPI>;
+};
+
+&gsbi6_spi {
+	status = "okay";
+	spi-max-frequency = <25000000>;
+
+	pinctrl-0 = <&spi6_pins>;
+	pinctrl-names = "default";
+
+	cs-gpios = <&qcom_pinmux 57 GPIO_ACTIVE_HIGH>;
+
+	dmas = <&adm_dma 8 0xb>,
+	       <&adm_dma 7 0x14>;
+	dma-names = "rx", "tx";
+
+	/*
+	 * This "spidev" was included in the manufacturer device tree. I suspect
+	 * it's the (unused) Zigbee radio -- SiliconLabs EM3581 Zigbee? There's
+	 * no driver or binding for this at the moment.
+	 */
+	spidev@0 {
+		compatible = "spidev";
+		reg = <0>;
+		spi-max-frequency = <25000000>;
+	};
+};
+
+&pcie0 {
+	status = "okay";
+
+	pcie@0 {
+		reg = <0 0 0 0 0>;
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		device_type = "pci";
+
+		ath10k@0,0 {
+			reg = <0 0 0 0 0>;
+			device_type = "pci";
+			qcom,ath10k-calibration-data = [00];
+			qcom,ath10k-sa-gpio = <2 3 4 0>;
+			qcom,ath10k-sa-gpio-func = <5 5 5 0>;
+		};
+	};
+};
+
+&pcie1 {
+	status = "okay";
+
+	pcie@0 {
+		reg = <0 0 0 0 0>;
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		device_type = "pci";
+
+		ath10k@0,0 {
+			reg = <0 0 0 0 0>;
+			device_type = "pci";
+			qcom,ath10k-calibration-data = [00];
+			qcom,ath10k-sa-gpio = <2 3 4 0>;
+			qcom,ath10k-sa-gpio-func = <5 5 5 0>;
+		};
+	};
+};
+
+&pcie2 {
+	status = "okay";
+
+	pcie@0 {
+		reg = <0 0 0 0 0>;
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		device_type = "pci";
+
+		ath10k@0,0 {
+			reg = <0 0 0 0 0>;
+			device_type = "pci";
+			qcom,ath10k-calibration-data = [00];
+		};
+	};
+};
+
+&rpm {
+	pinctrl-0 = <&rpm_i2c_pinmux>;
+	pinctrl-names = "default";
+};
+
+&sdcc1 {
+	status = "okay";
+	pinctrl-0 = <&sdcc1_pins>;
+	pinctrl-names = "default";
+	/delete-property/ mmc-ddr-1_8v;
+};
+
+&tcsr {
+	compatible = "qcom,tcsr-ipq8064", "qcom,tcsr", "syscon";
+	qcom,usb-ctrl-select = <TCSR_USB_SELECT_USB3_DUAL>;
+};
+
+&hs_phy_0 {
+	status = "okay";
+};
+
+&ss_phy_0 {
+	status = "okay";
+};
+
+&usb3_0 {
+	status = "okay";
+};
+
+&hs_phy_1 {
+	status = "okay";
+};
+
+&ss_phy_1 {
+	status = "okay";
+};
+
+&usb3_1 {
+	status = "okay";
+};
diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts
new file mode 100644
index 000000000000..112de6769f29
--- /dev/null
+++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014 The ChromiumOS Authors
+ */
+
+#include "qcom-ipq8064-onhub.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/soc/qcom,gsbi.h>
+
+/ {
+	model = "TP-Link OnHub";
+	compatible = "tplink,onhub", "google,whirlwind-sp5", "qcom,ipq8064";
+
+	chosen {
+		bootargs-append = " maxcpus=1 rootwait";
+	};
+};
+
+&qcom_pinmux {
+	i2c7_pins: i2c7_pinmux {
+		mux {
+			pins = "gpio8", "gpio9";
+			function = "gsbi7";
+		};
+		data {
+			pins = "gpio8";
+			bias-disable;
+		};
+		clk {
+			pins = "gpio9";
+			bias-disable;
+		};
+	};
+};
+
+&gsbi7 {
+	status = "okay";
+	qcom,mode = <GSBI_PROT_I2C_UART>;
+};
+
+&gsbi7_i2c {
+	status = "okay";
+	clock-frequency = <100000>;
+	pinctrl-0 = <&i2c7_pins>;
+	pinctrl-names = "default";
+
+	led-controller@32 {
+		compatible = "national,lp5523";
+		reg = <0x32>;
+		clock-mode = /bits/ 8 <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		led@0 {
+			reg = <0>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED0_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@1 {
+			reg = <1>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED0_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@2 {
+			reg = <2>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED0_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@3 {
+			reg = <3>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED1_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@4 {
+			reg = <4>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED1_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@5 {
+			reg = <5>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED1_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@6 {
+			reg = <6>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED2_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@7 {
+			reg = <7>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED2_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@8 {
+			reg = <8>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED2_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+	};
+
+	led-controller@33 {
+		compatible = "national,lp5523";
+		reg = <0x33>;
+		clock-mode = /bits/ 8 <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		led@0 {
+			reg = <0>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED3_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@1 {
+			reg = <1>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED3_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@2 {
+			reg = <2>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED3_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@3 {
+			reg = <3>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED4_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@4 {
+			reg = <4>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED4_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@5 {
+			reg = <5>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED4_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@6 {
+			reg = <6>;
+			color = <LED_COLOR_ID_RED>;
+			chan-name = "LED5_Red";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@7 {
+			reg = <7>;
+			color = <LED_COLOR_ID_GREEN>;
+			chan-name = "LED5_Green";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+
+		led@8 {
+			reg = <8>;
+			color = <LED_COLOR_ID_BLUE>;
+			chan-name = "LED5_Blue";
+			led-cur = /bits/ 8 <0x64>;
+			max-cur = /bits/ 8 <0x78>;
+		};
+	};
+};
diff --git a/target/linux/ipq806x/generic/target.mk b/target/linux/ipq806x/generic/target.mk
index f5cb1fb19b94..4de636f0e0ae 100644
--- a/target/linux/ipq806x/generic/target.mk
+++ b/target/linux/ipq806x/generic/target.mk
@@ -1 +1,2 @@
 BOARDNAME:=Generic
+FEATURES += nand
diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
index 8c1fc88010cd..c5c23eb9f72e 100644
--- a/target/linux/ipq806x/image/Makefile
+++ b/target/linux/ipq806x/image/Makefile
@@ -5,6 +5,7 @@ include $(INCLUDE_DIR)/image.mk
 
 define Device/Default
 	PROFILES := Default
+	KERNEL_DEPENDS = $$(wildcard $(DTS_DIR)/$$(DEVICE_DTS).dts)
 	KERNEL_LOADADDR = 0x42208000
 	DEVICE_DTS = $$(SOC)-$(lastword $(subst _, ,$(1)))
 	DEVICE_DTS_CONFIG := config@1
diff --git a/target/linux/ipq806x/image/chromium.mk b/target/linux/ipq806x/image/chromium.mk
new file mode 100644
index 000000000000..a11faa38df88
--- /dev/null
+++ b/target/linux/ipq806x/image/chromium.mk
@@ -0,0 +1,55 @@
+define Build/cros-gpt
+	cp $@ $@.tmp 2>/dev/null || true
+	ptgen -o $@.tmp -g \
+		-T cros_kernel	-N kernel -p $(CONFIG_TARGET_KERNEL_PARTSIZE)m \
+				-N rootfs -p $(CONFIG_TARGET_ROOTFS_PARTSIZE)m
+	cat $@.tmp >> $@
+	rm $@.tmp
+endef
+
+define Build/append-kernel-part
+	dd if=$(IMAGE_KERNEL) bs=$(CONFIG_TARGET_KERNEL_PARTSIZE)M conv=sync >> $@
+endef
+
+# NB: Chrome OS bootloaders replace the '%U' in command lines with the UUID of
+# the kernel partition it chooses to boot from. This gives a flexible way to
+# consistently build and sign kernels that always use the subsequent
+# (PARTNROFF=1) partition as their rootfs.
+define Build/cros-vboot
+	$(STAGING_DIR_HOST)/bin/cros-vbutil \
+		-k $@ -c "root=PARTUUID=%U/PARTNROFF=1" -o $@.new
+	@mv $@.new $@
+endef
+
+define Device/OnhubImage
+	KERNEL_LOADADDR = 0x44208000
+	SOC := qcom-ipq8064
+	KERNEL_SUFFIX := -fit-zImage.itb.vboot
+	KERNEL_NAME := zImage
+	KERNEL = kernel-bin | fit none $$(KDIR)/image-$$(DEVICE_DTS).dtb | cros-vboot
+	IMAGES := factory.bin sysupgrade.bin
+	IMAGE/factory.bin := cros-gpt | append-kernel-part | append-rootfs
+	IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
+	DEVICE_PACKAGES := ath10k-firmware-qca988x-ct e2fsprogs kmod-fs-ext4 losetup \
+			   partx-utils mkf2fs kmod-fs-f2fs \
+			   kmod-google-firmware kmod-tpm-i2c-infineon \
+			   kmod-sound-soc-ipq8064-storm kmod-usb-storage
+endef
+
+define Device/asus_onhub
+	$(call Device/OnhubImage)
+	DEVICE_VENDOR := ASUS
+	DEVICE_MODEL := OnHub SRT-AC1900
+	DEVICE_DTS := $$(SOC)-asus-onhub
+	BOARD_NAME := asus-onhub
+endef
+TARGET_DEVICES += asus_onhub
+
+define Device/tplink_onhub
+	$(call Device/OnhubImage)
+	DEVICE_VENDOR := TP-Link
+	DEVICE_MODEL := OnHub AC1900 Cloud Router
+	DEVICE_DTS := $$(SOC)-tplink-onhub
+	BOARD_NAME := tplink-onhub
+endef
+TARGET_DEVICES += tplink_onhub
