diff mbox series

[v3,7/7] ipq806x: Initial TP-Link and ASUS OnHub support

Message ID 20230111070652.1200657-7-computersforpeace@gmail.com
State Superseded
Headers show
Series [v3,1/7] base-files: Remove nand.sh dependency from emmc upgrade | expand

Commit Message

Brian Norris Jan. 11, 2023, 7:06 a.m. UTC
TP-Link and ASUS OnHub devices are very similar, sharing many of the
same characteristics and much of their Device Tree. They both run a
version of ChromeOS for their factory firmware, and so installation
instructions look very similar to Google Wifi [1].

Things I've tested, and are working:

 * Ethernet
 * WiFi (2.4 and 5 GHz)
 * LEDs
 * USB
 * eMMC
 * Serial console (if you wire it up yourself)
 * 2x CPU
 * Speaker

== Installation instructions summary ==

1. Flash *-factory.bin to a USB drive (e.g., with `dd`)
2. Insert USB drive, to boot OpenWrt from USB
3. Copy the same *-factory.bin over to device, and flash it to eMMC to
   make OpenWrt permanent

== Developer mode, booting from USB (Step 2) ==

To enter Developer Mode and boot OpenWrt from a USB stick:

1. Unplug power
2. Gain access to the "developer switch" through the bottom of the
   device
3. Hold down the "reset switch" (near the USB port / power plug)
4. Plug power back in
5. The LED on the device should turn white, then blink orange, then
   red. Release the reset switch.
6. Insert USB drive with OpenWrt factory.bin
7. Press the hidden developer switch under the device to boot to USB;
   you should see some activity lights (if you have any) on your USB
   drive
8. Depending on your configuration, the router's LED(s) should come on.
   You're now running OpenWrt off a USB stick.

These instructions are derived from:

https://www.exploitee.rs/index.php/Rooting_The_Google_OnHub#Enabling_%22Developer_Mode%22_on_the_OnHub
https://www.exploitee.rs/index.php/Asus_OnHub#Enabling_%22Developer_Mode%22_on_the_OnHub

~~Finding the developer switch:~~ for TP-Link, the developer switch is
on the bottom of the device, underneath some of the rubber padding and a
screw. For ASUS, remove the entire base, via 4 screws under the rubber
feet. See the Exploitee instructions for more info and photos.

== Making OpenWrt permanent (on eMMC) (Step 3) ==

Once you're running OpenWrt via USB:

1. Connect Ethernet to the LAN port; router's LAN address should be at
   192.168.1.1
2. Connect another system to the router's LAN, and copy the factory.bin
   image over, via SCP and SSH:

     scp -O openwrt-ipq806x-chromium-tplink_onhub-squashfs-factory.bin root@192.168.1.1:
     ssh root@192.168.1.1 -C "dd if=/dev/zero bs=512 seek=7552991 of=/dev/mmcblk0 count=33 && \
     dd if=/root/openwrt-ipq806x-chromium-tplink_onhub-squashfs-factory.bin of=/dev/mmcblk0"
3. Reboot and remove the USB drive.

== Developer mode beep ==

Note that every time you boot the OnHub in developer mode, the device
will play a loud "beep" after a few seconds. This is described in the
Chromium docs [2], and is intended to make it clear that the device is
not running Google software. It is nontrivial to completely disable this
beep, although it's possible to "acknowledge" developer mode (and skip
the beep) by using a USB keyboard to press CTRL+D every time you boot.

[1] https://openwrt.org/toh/google/wifi
[2] https://chromium.googlesource.com/chromiumos/docs/+/HEAD/developer_mode.md

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
---
 * There might be better ways to handle the multi-color LED support,
   but for now, each color is a separate LED
 * A variety of people have been interested in this work, and a few have
   tested versions of it already:
     https://forum.openwrt.org/t/onhub-tp-link-tgr1900-future-support/17899
 * This is dependent on an fstools change, to ensure it can find our
   'rootfs_data' properly:
     [PATCH fstools v2] partname: Ignore root=PARTUUID...
     https://patchwork.ozlabs.org/project/openwrt/patch/20230107020424.1703752-1-computersforpeace@gmail.com/

Changes in v3:
 * use 'ucode' for base64, to reduce dependency complexity and avoid
   bringing in coreutils
 * simplify installation instructions
 * add back in second CPU / drop maxcpus=1 (I had apparently already
   fixed this, but kept the maxcpus=1)

Changes in v2:
 * Drop custom ath10k base64 property
 * Provide base64 caldata parsing via
   /etc/hotplug.d/firmware/11-ath10k-caldata instead
 * add coreutils-base64 dependency
 * add 3rd (rootfs_data) partition, to better handle sysupgrade and
   utilize the whole disk

 target/linux/ipq806x/Makefile                 |   4 +-
 .../ipq806x/base-files/etc/board.d/01_leds    |  11 +
 .../ipq806x/base-files/etc/board.d/02_network |   6 +
 .../etc/hotplug.d/firmware/11-ath10k-caldata  |  35 ++
 .../base-files/lib/upgrade/platform.sh        |  19 +
 .../base-files/usr/bin/base64decode.uc        |  23 +
 target/linux/ipq806x/chromium/config-default  |  13 +
 target/linux/ipq806x/chromium/target.mk       |   2 +
 .../arm/boot/dts/qcom-ipq8064-asus-onhub.dts  |  95 ++++
 .../arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi | 463 ++++++++++++++++++
 .../boot/dts/qcom-ipq8064-tplink-onhub.dts    | 207 ++++++++
 target/linux/ipq806x/generic/target.mk        |   1 +
 target/linux/ipq806x/image/chromium.mk        |  58 +++
 13 files changed, 935 insertions(+), 2 deletions(-)
 create mode 100755 target/linux/ipq806x/base-files/usr/bin/base64decode.uc
 create mode 100644 target/linux/ipq806x/chromium/config-default
 create mode 100644 target/linux/ipq806x/chromium/target.mk
 create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
 create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
 create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts
 create mode 100644 target/linux/ipq806x/image/chromium.mk

Comments

Christian Marangi Jan. 12, 2023, 2:34 p.m. UTC | #1
On Tue, Jan 10, 2023 at 11:06:52PM -0800, Brian Norris wrote:
> TP-Link and ASUS OnHub devices are very similar, sharing many of the
> same characteristics and much of their Device Tree. They both run a
> version of ChromeOS for their factory firmware, and so installation
> instructions look very similar to Google Wifi [1].
> 
> Things I've tested, and are working:
> 
>  * Ethernet
>  * WiFi (2.4 and 5 GHz)
>  * LEDs
>  * USB
>  * eMMC
>  * Serial console (if you wire it up yourself)
>  * 2x CPU
>  * Speaker
> 
> == Installation instructions summary ==
> 
> 1. Flash *-factory.bin to a USB drive (e.g., with `dd`)
> 2. Insert USB drive, to boot OpenWrt from USB
> 3. Copy the same *-factory.bin over to device, and flash it to eMMC to
>    make OpenWrt permanent
> 
> == Developer mode, booting from USB (Step 2) ==
> 
> To enter Developer Mode and boot OpenWrt from a USB stick:
> 
> 1. Unplug power
> 2. Gain access to the "developer switch" through the bottom of the
>    device
> 3. Hold down the "reset switch" (near the USB port / power plug)
> 4. Plug power back in
> 5. The LED on the device should turn white, then blink orange, then
>    red. Release the reset switch.
> 6. Insert USB drive with OpenWrt factory.bin
> 7. Press the hidden developer switch under the device to boot to USB;
>    you should see some activity lights (if you have any) on your USB
>    drive
> 8. Depending on your configuration, the router's LED(s) should come on.
>    You're now running OpenWrt off a USB stick.
> 
> These instructions are derived from:
> 
> https://www.exploitee.rs/index.php/Rooting_The_Google_OnHub#Enabling_%22Developer_Mode%22_on_the_OnHub
> https://www.exploitee.rs/index.php/Asus_OnHub#Enabling_%22Developer_Mode%22_on_the_OnHub
> 
> ~~Finding the developer switch:~~ for TP-Link, the developer switch is
> on the bottom of the device, underneath some of the rubber padding and a
> screw. For ASUS, remove the entire base, via 4 screws under the rubber
> feet. See the Exploitee instructions for more info and photos.
> 
> == Making OpenWrt permanent (on eMMC) (Step 3) ==
> 
> Once you're running OpenWrt via USB:
> 
> 1. Connect Ethernet to the LAN port; router's LAN address should be at
>    192.168.1.1
> 2. Connect another system to the router's LAN, and copy the factory.bin
>    image over, via SCP and SSH:
> 
>      scp -O openwrt-ipq806x-chromium-tplink_onhub-squashfs-factory.bin root@192.168.1.1:
>      ssh root@192.168.1.1 -C "dd if=/dev/zero bs=512 seek=7552991 of=/dev/mmcblk0 count=33 && \
>      dd if=/root/openwrt-ipq806x-chromium-tplink_onhub-squashfs-factory.bin of=/dev/mmcblk0"
> 3. Reboot and remove the USB drive.
> 
> == Developer mode beep ==
> 
> Note that every time you boot the OnHub in developer mode, the device
> will play a loud "beep" after a few seconds. This is described in the
> Chromium docs [2], and is intended to make it clear that the device is
> not running Google software. It is nontrivial to completely disable this
> beep, although it's possible to "acknowledge" developer mode (and skip
> the beep) by using a USB keyboard to press CTRL+D every time you boot.
> 
> [1] https://openwrt.org/toh/google/wifi
> [2] https://chromium.googlesource.com/chromiumos/docs/+/HEAD/developer_mode.md
> 
> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
> ---
>  * There might be better ways to handle the multi-color LED support,
>    but for now, each color is a separate LED
>  * A variety of people have been interested in this work, and a few have
>    tested versions of it already:
>      https://forum.openwrt.org/t/onhub-tp-link-tgr1900-future-support/17899
>  * This is dependent on an fstools change, to ensure it can find our
>    'rootfs_data' properly:
>      [PATCH fstools v2] partname: Ignore root=PARTUUID...
>      https://patchwork.ozlabs.org/project/openwrt/patch/20230107020424.1703752-1-computersforpeace@gmail.com/
> 
> Changes in v3:
>  * use 'ucode' for base64, to reduce dependency complexity and avoid
>    bringing in coreutils
>  * simplify installation instructions
>  * add back in second CPU / drop maxcpus=1 (I had apparently already
>    fixed this, but kept the maxcpus=1)
> 
> Changes in v2:
>  * Drop custom ath10k base64 property
>  * Provide base64 caldata parsing via
>    /etc/hotplug.d/firmware/11-ath10k-caldata instead
>  * add coreutils-base64 dependency
>  * add 3rd (rootfs_data) partition, to better handle sysupgrade and
>    utilize the whole disk
> 
>  target/linux/ipq806x/Makefile                 |   4 +-
>  .../ipq806x/base-files/etc/board.d/01_leds    |  11 +
>  .../ipq806x/base-files/etc/board.d/02_network |   6 +
>  .../etc/hotplug.d/firmware/11-ath10k-caldata  |  35 ++
>  .../base-files/lib/upgrade/platform.sh        |  19 +
>  .../base-files/usr/bin/base64decode.uc        |  23 +
>  target/linux/ipq806x/chromium/config-default  |  13 +
>  target/linux/ipq806x/chromium/target.mk       |   2 +
>  .../arm/boot/dts/qcom-ipq8064-asus-onhub.dts  |  95 ++++
>  .../arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi | 463 ++++++++++++++++++
>  .../boot/dts/qcom-ipq8064-tplink-onhub.dts    | 207 ++++++++
>  target/linux/ipq806x/generic/target.mk        |   1 +
>  target/linux/ipq806x/image/chromium.mk        |  58 +++
>  13 files changed, 935 insertions(+), 2 deletions(-)
>  create mode 100755 target/linux/ipq806x/base-files/usr/bin/base64decode.uc
>  create mode 100644 target/linux/ipq806x/chromium/config-default
>  create mode 100644 target/linux/ipq806x/chromium/target.mk
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts
>  create mode 100644 target/linux/ipq806x/image/chromium.mk
> 
> 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"

Can't we set this directly in the dt? Also I think we should use a more
descriptive name.

> +	;;
>  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"
> +	;;

Same here.

Aside from these leds problem rest of the series looks OK. I have just
some concern about the changed name in patch 1 for downstream project
but we can live with that.

>  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/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> index 398ddf9a290a..a0e2e9d1237b 100644
> --- a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> +++ b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> @@ -6,9 +6,32 @@
>  
>  board=$(board_name)
>  
> +dt_base64_extract() {
> +	local target_dir="/sys$DEVPATH"
> +	local source="$target_dir/../../of_node/qcom,ath10k-calibration-data-base64"
> +
> +	[ -e "$source" ] || caldata_die "cannot find base64 calibration data: $source"
> +	[ -d "$target_dir" ] || \
> +		caldata_die "no sysfs dir to write: $target"
> +
> +	echo 1 > "$target_dir/loading"
> +	base64decode.uc "$source" > "$target_dir/data"
> +	if [ $? != 0 ]; then
> +		echo 1 > "$target_dir/loading"
> +		caldata_die \
> +			"failed to write calibration data to $target_dir/data"
> +	else
> +		echo 0 > "$target_dir/loading"
> +	fi
> +}
> +
>  case "$FIRMWARE" in
>  "ath10k/cal-pci-0000:01:00.0.bin")
>  	case "$board" in
> +	asus,onhub |\
> +	tplink,onhub)
> +		dt_base64_extract
> +		;;
>  	meraki,mr52)
>  		CI_UBIPART=art
>  		caldata_extract_ubi "ART" 0x1000 0x844
> @@ -35,6 +58,14 @@ case "$FIRMWARE" in
>  		;;
>  	esac
>  	;;
> +"ath10k/cal-pci-0001:01:00.0.bin")
> +	case "$board" in
> +	asus,onhub |\
> +	tplink,onhub)
> +		dt_base64_extract
> +		;;
> +	esac
> +	;;
>  "ath10k/pre-cal-pci-0001:01:00.0.bin")
>  	case $board in
>  	asrock,g10)
> @@ -61,6 +92,10 @@ case "$FIRMWARE" in
>  	;;
>  "ath10k/cal-pci-0002:01:00.0.bin")
>  	case "$board" in
> +	asus,onhub |\
> +	tplink,onhub)
> +		dt_base64_extract
> +		;;
>  	meraki,mr42)
>  		CI_UBIPART=art
>  		caldata_extract_ubi "ART" 0x9000 0x844
> diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
> index f9e592f4bd8f..67ceaab24fb5 100644
> --- a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
> +++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
> @@ -57,6 +57,15 @@ 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"
> +		CI_DATAPART="rootfs_data"
> +		emmc_do_upgrade "$1"
> +		;;
>  	tplink,vr2600v)
>  		MTD_CONFIG_ARGS="-s 0x200000"
>  		default_do_upgrade "$1"
> @@ -69,3 +78,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/base-files/usr/bin/base64decode.uc b/target/linux/ipq806x/base-files/usr/bin/base64decode.uc
> new file mode 100755
> index 000000000000..7dcf30986c04
> --- /dev/null
> +++ b/target/linux/ipq806x/base-files/usr/bin/base64decode.uc
> @@ -0,0 +1,23 @@
> +#!/usr/bin/ucode
> +
> +import { stdin, open, error } from 'fs';
> +
> +if (length(ARGV) == 0 && stdin.isatty()) {
> +	warn("usage: b64decode [stdin|path]\n");
> +	exit(1);
> +}
> +
> +let fp = stdin;
> +let source = ARGV[0];
> +
> +if (source) {
> +	fp = open(source);
> +	if (!fp) {
> +		warn('b64decode: unable to open ${source}: ${error()}\n');
> +		exit(1);
> +	}
> +}
> +
> +print(b64dec(fp.read("all")));
> +fp.close();
> +exit(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..de0cf978c3e3
> --- /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 = " 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..25ba71da00ef
> --- /dev/null
> +++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
> @@ -0,0 +1,463 @@
> +// 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-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-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";
> +		};
> +	};
> +};
> +
> +&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..493c348c229b
> --- /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 = " 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/chromium.mk b/target/linux/ipq806x/image/chromium.mk
> new file mode 100644
> index 000000000000..16af6b95ba6c
> --- /dev/null
> +++ b/target/linux/ipq806x/image/chromium.mk
> @@ -0,0 +1,58 @@
> +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 \
> +				-N rootfs_data -p \
> +				$$((3687-$(CONFIG_TARGET_ROOTFS_PARTSIZE)-\
> +				         $(CONFIG_TARGET_KERNEL_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 \
> +			   ucode 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
> -- 
> 2.39.0
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Brian Norris Jan. 12, 2023, 5:35 p.m. UTC | #2
Hi Christian,

On Thu, Jan 12, 2023 at 6:34 AM Christian Marangi <ansuelsmth@gmail.com> wrote:
> On Tue, Jan 10, 2023 at 11:06:52PM -0800, Brian Norris wrote:
> > 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"
>
> Can't we set this directly in the dt?

I suppose. I guess that's "linux,default-trigger"? Confusingly,
there's also "default-state" in the common bindings, but it's only
supported for a handful of drivers.

Downside: IIUC, this will no longer reflect in the UCI configuration,
and so it's less obvious how to override things. Especially when, like
the TP-Link version, you have 18 (!!) LEDs (or, 6 x 3-color LEDs), and
you have to guess as to which ones to override. But I can live with
that if that's deemed better.

> Also I think we should use a more
> descriptive name.

Sure. What do you think would be better? For ASUS, it's only a single
(set of) LEDs, so maybe I could go with "{red,green,blue}:status"
naming like I see on some others.

But how about TP-Link? There are 6 x 3-color LEDs strung in a ring
around the top. There's not really any particular function assigned to
them, and there isn't much visual separation between them (they
overlap in a sort of gradient). So, "{red,green,blue}:status{0..5}"?
Or "{red,green,blue}:ring{0..5}"?

There are some photos here, if you need inspiration:
https://openwrt.org/inbox/toh/google/onhub_tp-link_tgr1900
https://www.exploitee.rs/index.php/Rooting_The_Google_OnHub

> > +     ;;
> >  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"
> > +     ;;
>
> Same here.
>
> Aside from these leds problem rest of the series looks OK. I have just
> some concern about the changed name in patch 1 for downstream project
> but we can live with that.

Thanks,
Brian
Christian Marangi Jan. 12, 2023, 5:48 p.m. UTC | #3
On Thu, Jan 12, 2023 at 09:35:03AM -0800, Brian Norris wrote:
> Hi Christian,
> 
> On Thu, Jan 12, 2023 at 6:34 AM Christian Marangi <ansuelsmth@gmail.com> wrote:
> > On Tue, Jan 10, 2023 at 11:06:52PM -0800, Brian Norris wrote:
> > > 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"
> >
> > Can't we set this directly in the dt?
> 
> I suppose. I guess that's "linux,default-trigger"? Confusingly,
> there's also "default-state" in the common bindings, but it's only
> supported for a handful of drivers.

If the idea is to keep them turned on, default-trigger and set it to
always on but strange that default-state is not supported...

> 
> Downside: IIUC, this will no longer reflect in the UCI configuration,
> and so it's less obvious how to override things. Especially when, like
> the TP-Link version, you have 18 (!!) LEDs (or, 6 x 3-color LEDs), and
> you have to guess as to which ones to override. But I can live with
> that if that's deemed better.

It's not needed to have an UCI conf reflecting the state... If someone
needs to configure the led the current configuration is ignored anyway
as it will be changed to what the user wants.

> 
> > Also I think we should use a more
> > descriptive name.
> 
> Sure. What do you think would be better? For ASUS, it's only a single
> (set of) LEDs, so maybe I could go with "{red,green,blue}:status"
> naming like I see on some others.

Yes the pattern is color:function... Ideally everything should be
handled with standard linux binding instead of declaring a label...

So using function-enumerator, color and function.
If something is not here [1] then label are required

[1] https://elixir.bootlin.com/linux/latest/source/include/dt-bindings/leds/common.h

> 
> But how about TP-Link? There are 6 x 3-color LEDs strung in a ring
> around the top. There's not really any particular function assigned to
> them, and there isn't much visual separation between them (they
> overlap in a sort of gradient). So, "{red,green,blue}:status{0..5}"?
> Or "{red,green,blue}:ring{0..5}"?
> 
> There are some photos here, if you need inspiration:
> https://openwrt.org/inbox/toh/google/onhub_tp-link_tgr1900
> https://www.exploitee.rs/index.php/Rooting_The_Google_OnHub
> 

I would use function_status for everything... function enumerator and
color... 

> > > +     ;;
> > >  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"
> > > +     ;;
> >
> > Same here.
> >
> > Aside from these leds problem rest of the series looks OK. I have just
> > some concern about the changed name in patch 1 for downstream project
> > but we can live with that.
> 
> Thanks,
> Brian
Brian Norris Jan. 12, 2023, 6:15 p.m. UTC | #4
On Thu, Jan 12, 2023 at 9:48 AM Christian Marangi <ansuelsmth@gmail.com> wrote:
> On Thu, Jan 12, 2023 at 09:35:03AM -0800, Brian Norris wrote:
> > On Thu, Jan 12, 2023 at 6:34 AM Christian Marangi <ansuelsmth@gmail.com> wrote:
> > Downside: IIUC, this will no longer reflect in the UCI configuration,
> > and so it's less obvious how to override things. Especially when, like
> > the TP-Link version, you have 18 (!!) LEDs (or, 6 x 3-color LEDs), and
> > you have to guess as to which ones to override. But I can live with
> > that if that's deemed better.
>
> It's not needed to have an UCI conf reflecting the state... If someone
> needs to configure the led the current configuration is ignored anyway
> as it will be changed to what the user wants.

Right, but with the ucidef approach, the default state will be in the
UCI configuration already, and so the user only has to delete (or
rewrite) entries to turn off (or change) LEDs. With defaults in the
DTS, there's no default UCI entry, and the user has to learn which of
the 18 LEDs they want to override. For the TP-Link ring especially,
this could be non-obvious. Maybe that'd get better if (L)UCI did a
better job with multi-color LEDs, so there'd only be 6 things to
configure instead of 18.

> >
> > > Also I think we should use a more
> > > descriptive name.
> >
> > Sure. What do you think would be better? For ASUS, it's only a single
> > (set of) LEDs, so maybe I could go with "{red,green,blue}:status"
> > naming like I see on some others.
>
> Yes the pattern is color:function... Ideally everything should be
> handled with standard linux binding instead of declaring a label...
>
> So using function-enumerator, color and function.
> If something is not here [1] then label are required
>
> [1] https://elixir.bootlin.com/linux/latest/source/include/dt-bindings/leds/common.h

I'll play around with this. Last I checked, the lp5523 driver didn't
end up with very nice names without specifying a "chan-name" and/or
"label" (again, individual drivers seem to have a lot of leeway
despite the "common" bindings), but I'll check again later today.

Brian
Linus Walleij Jan. 13, 2023, 1:40 p.m. UTC | #5
Hi Brian!

I have this device, so as soon as I manage to find time for opening it up
and mounting a UART I will test your patch set!

On Wed, Jan 11, 2023 at 8:13 AM Brian Norris
<computersforpeace@gmail.com> wrote:

> TP-Link and ASUS OnHub devices are very similar, sharing many of the
> same characteristics and much of their Device Tree. They both run a
> version of ChromeOS for their factory firmware, and so installation
> instructions look very similar to Google Wifi [1].
(...)
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
>  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts

Could you please submit these device trees upstream to the
Linux kernel as well?

I don't know your immediate plans, if your idea is to put it into
OpenWrt first, but I'm sure the Qualcomm maintainers
Bjorn and Krzysztof would be delighted to take a look.
A small patch to Documentation/devicetree/bindings/arm/qcom.yaml
adding the new compatibles is needed too.

If you don't have time for this I understand, I can volunteer
to push it upstream and iron out any snags in that case.

Yours,
Linus Walleij
Brian Norris Jan. 13, 2023, 6:09 p.m. UTC | #6
On Fri, Jan 13, 2023 at 5:40 AM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> Hi Brian!
>
> I have this device, so as soon as I manage to find time for opening it up
> and mounting a UART I will test your patch set!

Hi Linus! Glad to see you're interested.

> On Wed, Jan 11, 2023 at 8:13 AM Brian Norris
> <computersforpeace@gmail.com> wrote:
>
> > TP-Link and ASUS OnHub devices are very similar, sharing many of the
> > same characteristics and much of their Device Tree. They both run a
> > version of ChromeOS for their factory firmware, and so installation
> > instructions look very similar to Google Wifi [1].
> (...)
> >  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-asus-onhub.dts
> >  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
> >  create mode 100644 target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-tplink-onhub.dts
>
> Could you please submit these device trees upstream to the
> Linux kernel as well?
>
> I don't know your immediate plans, if your idea is to put it into
> OpenWrt first, but I'm sure the Qualcomm maintainers
> Bjorn and Krzysztof would be delighted to take a look.
> A small patch to Documentation/devicetree/bindings/arm/qcom.yaml
> adding the new compatibles is needed too.
>
> If you don't have time for this I understand, I can volunteer
> to push it upstream and iron out any snags in that case.

That's not currently my interest. My current motivation is to get this
into OpenWrt, since these devices are no longer supported by Google as
of January 11, and a lot of people are interested in taking back
control of their hardware.

And, I think there are at least a handful of important things missing
from upstream such that either the device tree would not fit
upstream's typical review process (non-upstream bindings), or else
would be missing major features (USB, and possibly more), which
wouldn't serve the primary goal (of making these devices usable again
as quickly as possible).

So, maybe I could take a stab at things eventually, but not
immediately. In case you want to help (which would be very welcome!),
here's one missing piece:
target/linux/ipq806x/patches-5.15/850-soc-add-qualcomm-syscon.patch
I'd bet that needs to get reworked into some kind of integration with
the USB and/or PHY driver(s). And I'm sure there's more if you look
through target/linux/ipq806x/patches-5.15/. But hey, getting it
running yourself to play with would be a good start, and I'd love to
have some company on this potentially-upstreaming journey ;)

BTW, if you're interested in this sort of thing (and perhaps have
other Google-related hardware?), I took some notes on my last
(failed/stalled)-upstreaming efforts, for Google Wifi:
https://openwrt.org/toh/google/wifi#upstreaming_notes
That's another can of worms, since I think the upstream maintainers
would prefer to ignore "legacy" firmware that doesn't work exactly the
same as whatever modern chips they're playing with...
...so you're left with upstream not really supporting most Qualcomm
hardware. *shrug*

Brian
diff mbox series

Patch

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/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
index 398ddf9a290a..a0e2e9d1237b 100644
--- a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
+++ b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
@@ -6,9 +6,32 @@ 
 
 board=$(board_name)
 
+dt_base64_extract() {
+	local target_dir="/sys$DEVPATH"
+	local source="$target_dir/../../of_node/qcom,ath10k-calibration-data-base64"
+
+	[ -e "$source" ] || caldata_die "cannot find base64 calibration data: $source"
+	[ -d "$target_dir" ] || \
+		caldata_die "no sysfs dir to write: $target"
+
+	echo 1 > "$target_dir/loading"
+	base64decode.uc "$source" > "$target_dir/data"
+	if [ $? != 0 ]; then
+		echo 1 > "$target_dir/loading"
+		caldata_die \
+			"failed to write calibration data to $target_dir/data"
+	else
+		echo 0 > "$target_dir/loading"
+	fi
+}
+
 case "$FIRMWARE" in
 "ath10k/cal-pci-0000:01:00.0.bin")
 	case "$board" in
+	asus,onhub |\
+	tplink,onhub)
+		dt_base64_extract
+		;;
 	meraki,mr52)
 		CI_UBIPART=art
 		caldata_extract_ubi "ART" 0x1000 0x844
@@ -35,6 +58,14 @@  case "$FIRMWARE" in
 		;;
 	esac
 	;;
+"ath10k/cal-pci-0001:01:00.0.bin")
+	case "$board" in
+	asus,onhub |\
+	tplink,onhub)
+		dt_base64_extract
+		;;
+	esac
+	;;
 "ath10k/pre-cal-pci-0001:01:00.0.bin")
 	case $board in
 	asrock,g10)
@@ -61,6 +92,10 @@  case "$FIRMWARE" in
 	;;
 "ath10k/cal-pci-0002:01:00.0.bin")
 	case "$board" in
+	asus,onhub |\
+	tplink,onhub)
+		dt_base64_extract
+		;;
 	meraki,mr42)
 		CI_UBIPART=art
 		caldata_extract_ubi "ART" 0x9000 0x844
diff --git a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
index f9e592f4bd8f..67ceaab24fb5 100644
--- a/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ipq806x/base-files/lib/upgrade/platform.sh
@@ -57,6 +57,15 @@  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"
+		CI_DATAPART="rootfs_data"
+		emmc_do_upgrade "$1"
+		;;
 	tplink,vr2600v)
 		MTD_CONFIG_ARGS="-s 0x200000"
 		default_do_upgrade "$1"
@@ -69,3 +78,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/base-files/usr/bin/base64decode.uc b/target/linux/ipq806x/base-files/usr/bin/base64decode.uc
new file mode 100755
index 000000000000..7dcf30986c04
--- /dev/null
+++ b/target/linux/ipq806x/base-files/usr/bin/base64decode.uc
@@ -0,0 +1,23 @@ 
+#!/usr/bin/ucode
+
+import { stdin, open, error } from 'fs';
+
+if (length(ARGV) == 0 && stdin.isatty()) {
+	warn("usage: b64decode [stdin|path]\n");
+	exit(1);
+}
+
+let fp = stdin;
+let source = ARGV[0];
+
+if (source) {
+	fp = open(source);
+	if (!fp) {
+		warn('b64decode: unable to open ${source}: ${error()}\n');
+		exit(1);
+	}
+}
+
+print(b64dec(fp.read("all")));
+fp.close();
+exit(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..de0cf978c3e3
--- /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 = " 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..25ba71da00ef
--- /dev/null
+++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-onhub.dtsi
@@ -0,0 +1,463 @@ 
+// 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-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-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";
+		};
+	};
+};
+
+&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..493c348c229b
--- /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 = " 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/chromium.mk b/target/linux/ipq806x/image/chromium.mk
new file mode 100644
index 000000000000..16af6b95ba6c
--- /dev/null
+++ b/target/linux/ipq806x/image/chromium.mk
@@ -0,0 +1,58 @@ 
+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 \
+				-N rootfs_data -p \
+				$$((3687-$(CONFIG_TARGET_ROOTFS_PARTSIZE)-\
+				         $(CONFIG_TARGET_KERNEL_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 \
+			   ucode 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