diff mbox series

[2/2] ipq40xx: add support for eero Cento (J010001)

Message ID 20230517174700.21083-2-contact@cnorthway.com
State Handled Elsewhere
Headers show
Series [1/2] ipq-wifi: bump to latest git HEAD | expand

Commit Message

Connor Northway May 17, 2023, 5:47 p.m. UTC
development/internal name: Cento
public name: eero (2nd-gen)
model number: J010001

Hardware Info:

    SoC                 : Qualcomm IPQ4019
    RAM                 : 512 MB (Hynix NT5CC256M16ER-EK)
    SPI Flash           : 8 MB (Winbond W25Q64JVSS)
    eMMC Flash          : 4 GB (Kingston EMMC04G-M627)
    Wi-Fi               : 2.4 Ghz 2x2 802.11n (IPQ4019)
    Wi-Fi               : 5 Ghz 2x2 802.11ac (IPQ4019)
    Ethernet            : 2x 10/100/1000
    Bluetooth           : 5.0 / BLE (Cypress CYW20704)
    RGBW LED Controller : Texas Instruments LP5562
    Buttons             : 1x Reset (bottom of device)
    UART & SPI header   : see below
    Power & USB 2       : USB Type-C, requires 15W 5V/3A

Stock Firmware Layout:

    The SPI flash stores U-Boot, U-Boot environment, hardware
    calibration, and eero-specific information (keys, serial number,
    MAC address, etc).

    The eMMC is partitioned into an A/B layout with two sets of an ext2
    kernel and ext4 rootfs partition, plus one cache partition.

MAC Address assignment:

    One base MAC address is stored in SPI flash, with a space of 32
    allocated. We follow stock layout, though stock FW also uses higher
    addresses for routing / mesh applications.

    Base / WAN : 48:dd:0c:**:**:xx
    LAN        : 48:dd:0c:**:**:xx + 1
    Bluetooth  : 48:dd:0c:**:**:xx + 2
    Wi-Fi 1    : 48:dd:0c:**:**:xx + 3
    Wi-Fi 2    : 48:dd:0c:**:**:xx + 4

Other OpenWrt details:

    RGBW LED has been configured to the following:
      Red: Failsafe
      Green: Running
      Blue: Upgrade
      White: Boot

    Ethernet ports are not labeled. Default config when viewed from
    back: right port is WAN, left port is LAN.

UART/SPI Header:

    2x5 1.27mm unpopulated SMT header labeled "J2".
    Silkscreen dot marks pin 1; pins 2, 9, and 10 labeled with number.

    Layout:

        SPI cpu bypass --| 1  2 |-- SPI /CS
               SPI CLK --| 3  4 |-- SPI DI
                SPI DO --| 5  6 |-- GND
                  ???? --| 7  8 |-- 3v3
               UART RX --| 9 10 |-- UART TX

    To access the SPI flash chip, pull pin 1 high (connect to 3v3)
    before applying power through USB. This will disconnect the SPI
    lines from the CPU and allow accessing the SPI bus without
    contention. As always, do not connect external power to 3v3!

    UART settings: 115200 8n1

    Logic levels for UART and SPI are 3.3V.

OpenWrt Installation:

    Unfortunately, eero has configured U-Boot to autoboot with no delay
    and no way to abort boot. There does not appear to be any available
    method to boot from an external source without modifications.

    To change the bootdelay variable, editing the SPI flash contents
    directly is required:
    - Use an SPI programmer to make a backup of the flash
    - Locate the APPSBLENV partition and modify the environment to have
      a non-zero bootdelay value in both locations
    - Update the leading CRC32 checksums
    - Flash image back to the device and reboot

    Details of environment layout:
        APPSBLENV is at 0x210000. It contains two redundant copies of
        the following struct, each 0x10000 bytes long.

            struct env {
                uint32_t crc; // calculated only over data[]
                unsigned char flags;
                unsigned char data[0x10000-5];
            };

    Once access to the U-Boot shell is achieved:

    Use TFTP to load the initramfs image to memory:
    `tftpboot <initramfs-image>.itb`
    with a TFTP server at 192.168.1.1

    or use USB:
    `fatload usb <dev[:part]> 0x84000000 <initramfs-image>.itb`
    with FAT32 USB drive connected through a powered hub
    (use `usb part` to view partitions)

    clear the bootargs variable:
    `setenv bootargs`

    then boot:
    `bootm`

    Make backups of SPI and eMMC flash!

    Use SSH/SCP to transfer the sysupgrade file and run sysupgrade.

    The upgrade script will determine which of the two dual-partition
    slots the device is currently booting from. It will install to the
    inactive slot if installing over stock FW, or the current slot if
    upgrading OpenWrt. This leaves the most recent stock FW untouched
    for easier restoration to stock. Don't rely on this as a backup!

Restore Stock Firmware:

    If installed using sysupgrade and one stock partition set remains,
    replace the U-Boot environment variables `bootargs` and `bootcmd`
    with the contents of the `*_eero_backup` versions automatically
    created during install. Stock firmware should overwrite OpenWrt the
    next time it receives an update.

Signed-off-by: Connor Northway <contact@cnorthway.com>
---
 package/boot/uboot-envtools/files/ipq40xx     |   4 +
 package/firmware/ipq-wifi/Makefile            |   2 +
 .../ipq40xx/base-files/etc/board.d/02_network |   5 +
 .../etc/hotplug.d/firmware/11-ath10k-caldata  |   8 +
 .../etc/hotplug.d/usb/20-bluetooth-mac        |  21 +
 .../ipq40xx/base-files/lib/upgrade/eero.sh    |  94 +++++
 .../base-files/lib/upgrade/platform.sh        |   3 +
 .../arch/arm/boot/dts/qcom-ipq4019-cento.dts  | 393 ++++++++++++++++++
 target/linux/ipq40xx/image/generic.mk         |  13 +
 9 files changed, 543 insertions(+)
 create mode 100644 target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
 create mode 100644 target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
 create mode 100644 target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts

Comments

Christian Marangi June 13, 2023, 10:21 p.m. UTC | #1
On Wed, May 17, 2023 at 01:47:02PM -0400, Connor Northway wrote:
> development/internal name: Cento
> public name: eero (2nd-gen)
> model number: J010001
> 
> Hardware Info:
> 
>     SoC                 : Qualcomm IPQ4019
>     RAM                 : 512 MB (Hynix NT5CC256M16ER-EK)
>     SPI Flash           : 8 MB (Winbond W25Q64JVSS)
>     eMMC Flash          : 4 GB (Kingston EMMC04G-M627)
>     Wi-Fi               : 2.4 Ghz 2x2 802.11n (IPQ4019)
>     Wi-Fi               : 5 Ghz 2x2 802.11ac (IPQ4019)
>     Ethernet            : 2x 10/100/1000
>     Bluetooth           : 5.0 / BLE (Cypress CYW20704)
>     RGBW LED Controller : Texas Instruments LP5562
>     Buttons             : 1x Reset (bottom of device)
>     UART & SPI header   : see below
>     Power & USB 2       : USB Type-C, requires 15W 5V/3A
> 
> Stock Firmware Layout:
> 
>     The SPI flash stores U-Boot, U-Boot environment, hardware
>     calibration, and eero-specific information (keys, serial number,
>     MAC address, etc).
> 
>     The eMMC is partitioned into an A/B layout with two sets of an ext2
>     kernel and ext4 rootfs partition, plus one cache partition.
> 
> MAC Address assignment:
> 
>     One base MAC address is stored in SPI flash, with a space of 32
>     allocated. We follow stock layout, though stock FW also uses higher
>     addresses for routing / mesh applications.
> 
>     Base / WAN : 48:dd:0c:**:**:xx
>     LAN        : 48:dd:0c:**:**:xx + 1
>     Bluetooth  : 48:dd:0c:**:**:xx + 2
>     Wi-Fi 1    : 48:dd:0c:**:**:xx + 3
>     Wi-Fi 2    : 48:dd:0c:**:**:xx + 4
> 
> Other OpenWrt details:
> 
>     RGBW LED has been configured to the following:
>       Red: Failsafe
>       Green: Running
>       Blue: Upgrade
>       White: Boot
> 
>     Ethernet ports are not labeled. Default config when viewed from
>     back: right port is WAN, left port is LAN.
> 
> UART/SPI Header:
> 
>     2x5 1.27mm unpopulated SMT header labeled "J2".
>     Silkscreen dot marks pin 1; pins 2, 9, and 10 labeled with number.
> 
>     Layout:
> 
>         SPI cpu bypass --| 1  2 |-- SPI /CS
>                SPI CLK --| 3  4 |-- SPI DI
>                 SPI DO --| 5  6 |-- GND
>                   ???? --| 7  8 |-- 3v3
>                UART RX --| 9 10 |-- UART TX
> 
>     To access the SPI flash chip, pull pin 1 high (connect to 3v3)
>     before applying power through USB. This will disconnect the SPI
>     lines from the CPU and allow accessing the SPI bus without
>     contention. As always, do not connect external power to 3v3!
> 
>     UART settings: 115200 8n1
> 
>     Logic levels for UART and SPI are 3.3V.
> 
> OpenWrt Installation:
> 
>     Unfortunately, eero has configured U-Boot to autoboot with no delay
>     and no way to abort boot. There does not appear to be any available
>     method to boot from an external source without modifications.
> 
>     To change the bootdelay variable, editing the SPI flash contents
>     directly is required:
>     - Use an SPI programmer to make a backup of the flash
>     - Locate the APPSBLENV partition and modify the environment to have
>       a non-zero bootdelay value in both locations
>     - Update the leading CRC32 checksums
>     - Flash image back to the device and reboot
> 
>     Details of environment layout:
>         APPSBLENV is at 0x210000. It contains two redundant copies of
>         the following struct, each 0x10000 bytes long.
> 
>             struct env {
>                 uint32_t crc; // calculated only over data[]
>                 unsigned char flags;
>                 unsigned char data[0x10000-5];
>             };
> 
>     Once access to the U-Boot shell is achieved:
> 
>     Use TFTP to load the initramfs image to memory:
>     `tftpboot <initramfs-image>.itb`
>     with a TFTP server at 192.168.1.1
> 
>     or use USB:
>     `fatload usb <dev[:part]> 0x84000000 <initramfs-image>.itb`
>     with FAT32 USB drive connected through a powered hub
>     (use `usb part` to view partitions)
> 
>     clear the bootargs variable:
>     `setenv bootargs`
> 
>     then boot:
>     `bootm`
> 
>     Make backups of SPI and eMMC flash!
> 
>     Use SSH/SCP to transfer the sysupgrade file and run sysupgrade.
> 
>     The upgrade script will determine which of the two dual-partition
>     slots the device is currently booting from. It will install to the
>     inactive slot if installing over stock FW, or the current slot if
>     upgrading OpenWrt. This leaves the most recent stock FW untouched
>     for easier restoration to stock. Don't rely on this as a backup!
> 
> Restore Stock Firmware:
> 
>     If installed using sysupgrade and one stock partition set remains,
>     replace the U-Boot environment variables `bootargs` and `bootcmd`
>     with the contents of the `*_eero_backup` versions automatically
>     created during install. Stock firmware should overwrite OpenWrt the
>     next time it receives an update.
> 
> Signed-off-by: Connor Northway <contact@cnorthway.com>
> ---
>  package/boot/uboot-envtools/files/ipq40xx     |   4 +
>  package/firmware/ipq-wifi/Makefile            |   2 +
>  .../ipq40xx/base-files/etc/board.d/02_network |   5 +
>  .../etc/hotplug.d/firmware/11-ath10k-caldata  |   8 +
>  .../etc/hotplug.d/usb/20-bluetooth-mac        |  21 +
>  .../ipq40xx/base-files/lib/upgrade/eero.sh    |  94 +++++
>  .../base-files/lib/upgrade/platform.sh        |   3 +
>  .../arch/arm/boot/dts/qcom-ipq4019-cento.dts  | 393 ++++++++++++++++++
>  target/linux/ipq40xx/image/generic.mk         |  13 +
>  9 files changed, 543 insertions(+)
>  create mode 100644 target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
>  create mode 100644 target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
>  create mode 100644 target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
> 
> diff --git a/package/boot/uboot-envtools/files/ipq40xx b/package/boot/uboot-envtools/files/ipq40xx
> index 8d993fae36..e8431048eb 100644
> --- a/package/boot/uboot-envtools/files/ipq40xx
> +++ b/package/boot/uboot-envtools/files/ipq40xx
> @@ -56,6 +56,10 @@ aruba,ap-365)
>  buffalo,wtr-m2133hp)
>  	ubootenv_add_uci_config "/dev/mtd8" "0x0" "0x40000" "0x20000"
>  	;;
> +eero,cento)
> +	ubootenv_add_uci_config "/dev/mtd9" "0x0" "0x10000" "0x10000"
> +	ubootenv_add_uci_config "/dev/mtd9" "0x10000" "0x10000" "0x10000"
> +	;;
>  linksys,ea6350v3)
>  	ubootenv_add_uci_config "/dev/mtd7" "0x0" "0x20000" "0x20000"
>  	;;
> diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile
> index aeb690e913..f2e670e65c 100644
> --- a/package/firmware/ipq-wifi/Makefile
> +++ b/package/firmware/ipq-wifi/Makefile
> @@ -38,6 +38,7 @@ ALLWIFIBOARDS:= \
>  	edgecore_ecw5410 \
>  	edgecore_oap100 \
>  	edimax_cax1800 \
> +	eero_cento\
>  	extreme-networks_ws-ap3915i \
>  	glinet_gl-a1300 \
>  	glinet_gl-ap1300 \
> @@ -143,6 +144,7 @@ $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102))
>  $(eval $(call generate-ipq-wifi-package,edgecore_ecw5410,Edgecore ECW5410))
>  $(eval $(call generate-ipq-wifi-package,edgecore_oap100,Edgecore OAP100))
>  $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800))
> +$(eval $(call generate-ipq-wifi-package,eero_cento,Eero Cento))
>  $(eval $(call generate-ipq-wifi-package,extreme-networks_ws-ap3915i,Edgecore OAP100))
>  $(eval $(call generate-ipq-wifi-package,glinet_gl-a1300,GL.iNet GL-A1300))
>  $(eval $(call generate-ipq-wifi-package,glinet_gl-ap1300,GL.iNet GL-AP1300))
> diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network
> index cb318d36a3..e5bd04a56e 100644
> --- a/target/linux/ipq40xx/base-files/etc/board.d/02_network
> +++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network
> @@ -32,6 +32,7 @@ ipq40xx_setup_interfaces()
>  	asus,map-ac2200|\
>  	cilab,meshpoint-one|\
>  	edgecore,ecw5211|\
> +	eero,cento|\
>  	glinet,gl-ap1300|\
>  	glinet,gl-b2200|\
>  	google,wifi|\
> @@ -159,6 +160,10 @@ ipq40xx_setup_macs()
>  		lan_mac=$(mtd_get_mac_ascii bdcfg lanmac)
>  		label_mac=$lan_mac
>  		;;
> +	eero,cento)
> +		wan_mac=$(mtd_get_mac_ascii 0:IDENTITY mac)
> +		lan_mac=$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 1)
> +		;;
>  	engenius,eap2200|\
>  	engenius,emd1)
>  		lan_mac=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr)
> diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> index 7f41bdcfcc..4b6072f0fe 100644
> --- a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> +++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> @@ -83,6 +83,10 @@ case "$FIRMWARE" in
>  		caldata_extract "ART" 0x1000 0x2f20
>  		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac)
>  		;;
> +	eero,cento)
> +		caldata_extract "0:ART" 0x1000 0x2f20

Can you declare nvmem cell and use that to provide calibration bin?

> +		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 3)
> +		;;
>  	engenius,emd1)
>  		caldata_extract "0:ART" 0x1000 0x2f20
>  		ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr)
> @@ -178,6 +182,10 @@ case "$FIRMWARE" in
>  		caldata_extract "ART" 0x5000 0x2f20
>  		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac_a)
>  		;;
> +	eero,cento)
> +		caldata_extract "0:ART" 0x5000 0x2f20

Ditto.

> +		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 4)
> +		;;
>  	engenius,emd1)
>  		caldata_extract "0:ART" 0x5000 0x2f20
>  		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr) 1)
> diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
> new file mode 100644
> index 0000000000..eada210214
> --- /dev/null
> +++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
> @@ -0,0 +1,21 @@
> +. /lib/functions/system.sh
> +
> +setup_bluetooth_mac()
> +{
> +	local board=$(board_name)
> +
> +	case "$board" in
> +	eero,cento)
> +		hciconfig hci0 up
> +		bdaddr "$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 2)"
> +		hciconfig hci0 reset
> +		hciconfig hci0 down
> +	;;
> +	esac
> +}
> +
> +if [ "${DRIVER}" = "btusb" ]; then
> +	if [ "${ACTION}" = "bind" ]; then
> +		setup_bluetooth_mac
> +	fi
> +fi
> diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
> new file mode 100644
> index 0000000000..c27c4bfad7
> --- /dev/null
> +++ b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
> @@ -0,0 +1,94 @@
> +# Flashes upgrade package using eero's A/B partition scheme
> +# will leave most recent eero installation alone as a backup
> +platform_do_upgrade_eero_cento() {
> +	local tar_file="$1"
> +
> +	local current_bootcmd
> +	local current_bootargs
> +	current_bootcmd=$(fw_printenv -n bootcmd)
> +	current_bootargs=$(fw_printenv -n bootargs)
> +	local first_install
> +	first_install=$(echo "$current_bootcmd" | grep -c "eero_kernel")
> +	if [ $first_install -eq 1 ]; then
> +		echo "first installation! backing up important stock env variables"
> +		fw_setenv -s - <<-EOF
> +			bootcmd_eero_backup $current_bootcmd
> +			bootargs_eero_backup $current_bootargs
> +		EOF
> +	fi
> +
> +###
> +# Determine partitions
> +###
> +	local current_bootpart
> +	local current_rootpart
> +	current_bootpart=$(echo "$current_bootcmd" | sed -nr 's/.*ext2load mmc 0:([[:digit:]]).*/\1/p')
> +	current_rootpart=$(echo "$current_bootargs" | sed -nr 's/.*root=\/dev\/mmcblk0p([[:digit:]]).*/\1/p')
> +
> +	# validate that they're sane; determine inactive partitions
> +	if [ "$current_bootpart" -eq 1 ] && [ "$current_rootpart" -eq 3 ]; then
> +		local inactive_bootpart=2
> +		local inactive_rootpart=4
> +		echo "currently booting from slot A"
> +	elif [ "$current_bootpart" -eq 2 ] && [ "$current_rootpart" -eq 4 ]; then
> +		local inactive_bootpart=1
> +		local inactive_rootpart=3
> +		echo "currently booting from slot B"
> +	else
> +		echo "invalid boot or root partitions"
> +		return 1
> +	fi
> +
> +	# on the first install, we want to install to the inactive slot
> +	# (easier to recover to known-good, configured stock OS).
> +	# on subsequent installs, continue using the active slot to keep
> +	# the stock install intact.
> +	if $first_install; then
> +		local install_bootpart=$inactive_bootpart
> +		local install_rootpart=$inactive_rootpart
> +		echo "installing to inactive slot;"
> +	else
> +		local install_bootpart=$current_bootpart
> +		local install_rootpart=$current_rootpart
> +		echo "installing to currently booted slot;"
> +	fi
> +	echo "boot: $install_bootpart"
> +	echo "root: $install_rootpart"
> +
> +###
> +# Install kernel to ext2 part
> +###
> +	local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$')
> +	board_dir=${board_dir%/}
> +
> +	# mount boot partition
> +	mkdir /mnt/bootpart &&
> +	mount -t ext2 /dev/mmcblk0p"$install_bootpart" /mnt/bootpart ||
> +	{ echo "unable to mount boot partition" && return 1 ; }
> +
> +	# clean it out and install new kernel image
> +	rm -r /mnt/bootpart/* &&
> +	mkdir /mnt/bootpart/lost+found &&
> +	tar Oxf $tar_file ${board_dir}/kernel > /mnt/bootpart/kernel.itb ||
> +	{ echo "unable to install kernel image" && return 1 ; }
> +
> +	echo "installed kernel image to /dev/mmcblk0p$install_bootpart"
> +
> +###
> +# Install rootfs using emmc_do_upgrade
> +###
> +	EMMC_ROOT_DEV=/dev/mmcblk0p"$install_rootpart"
> +	emmc_do_upgrade $tar_file
> +
> +	echo "done with emmc_do_upgrade, rootfs should be installed"
> +
> +###
> +# Finalize env vars
> +###
> +	fw_setenv -s - <<-EOF
> +		bootcmd ext2load mmc 0:$install_bootpart 0x84000000 kernel.itb && bootm; reset
> +		bootargs root=/dev/mmcblk0p$install_rootpart rootwait
> +	EOF
> +
> +	echo "new boot cmd/args set, install complete!"
> +}
> diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> index 988921fa8c..74f425861b 100644
> --- a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> +++ b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> @@ -156,6 +156,9 @@ platform_do_upgrade() {
>  	compex,wpj419)
>  		nand_do_upgrade "$1"
>  		;;
> +	eero,cento)
> +		platform_do_upgrade_eero_cento "$1"
> +		;;
>  	google,wifi)
>  		export_bootdevice
>  		export_partdevice CI_ROOTDEV 0
> diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
> new file mode 100644
> index 0000000000..5c646de918
> --- /dev/null
> +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
> +
> +#include "qcom-ipq4019.dtsi"
> +#include <dt-bindings/input/input.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/leds/common.h>
> +#include <dt-bindings/soc/qcom,tcsr.h>
> +
> +/ {
> +	model = "Eero Cento (J010001)";
> +	compatible = "eero,cento";
> +
> +	chosen {
> +		stdout-path = &blsp1_uart1;
> +	};
> +
> +	aliases {
> +		led-boot = &led_status_white;
> +		led-failsafe = &led_status_red;
> +		led-running = &led_status_green;
> +		led-upgrade = &led_status_blue;
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x80000000 0x20000000>;
> +	};
> +
> +	keys {
> +		compatible = "gpio-keys";
> +
> +		reset {
> +			label = "reset";
> +			gpios = <&tlmm 18 GPIO_ACTIVE_LOW>;
> +			linux,code = <KEY_RESTART>;
> +		};
> +	};
> +
> +	tcsr@1949000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1949000 0x100>;
> +		qcom,wifi_glb_cfg = <TCSR_WIFI_GLB_CFG>;
> +	};
> +
> +	tcsr@194b000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x194b000 0x100>;
> +		qcom,usb-hsphy-mode-select = <TCSR_USB_HSPHY_HOST_MODE>;
> +	};
> +
> +	ess_tcsr@1953000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1953000 0x1000>;
> +		qcom,ess-interface-select = <TCSR_ESS_PSGMII>;
> +	};
> +
> +	tcsr@1957000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1957000 0x100>;
> +		qcom,wifi_noc_memtype_m0_m2 = <TCSR_WIFI_NOC_MEMTYPE_M0_M2>;
> +	};
> +
> +};
> +
> +&tlmm {
> +	serial_0_pins: serial0_pinmux {
> +		mux {
> +			pins = "gpio16", "gpio17";
> +			function = "blsp_uart0";
> +			bias-disable;
> +		};
> +	};
> +
> +	spi_0_pins: spi_0_pinmux {
> +			pinmux {
> +					function = "blsp_spi0";
> +					pins = "gpio13", "gpio14", "gpio15";
> +			};
> +
> +			pinmux_cs {
> +					function = "gpio";
> +					pins = "gpio12";
> +			};
> +
> +			pinconf {
> +					pins = "gpio13", "gpio14", "gpio15";
> +					bias-disable;
> +			};
> +
> +			pinconf_cs {
> +					pins = "gpio12";
> +					bias-disable;
> +					output-high;
> +			};
> +	};
> +
> +	sd_0_pins: sd_0_pinmux {
> +		sd0 {
> +			function = "sdio";
> +			pins = "gpio23", "gpio24", "gpio25", "gpio26", "gpio29",
> +					"gpio30", "gpio31", "gpio32";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +
> +		sdclk {
> +			pins = "gpio27";
> +			function = "sdio";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +
> +		sdcmd {
> +			pins = "gpio28";
> +			function = "sdio";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +	};
> +
> +	i2c_0_pins: i2c_0_pinmux {
> +		mux {
> +			pins = "gpio20", "gpio21";
> +			function = "blsp_i2c0";
> +			bias-disable;
> +		};
> +	};
> +
> +	i2c_1_pins: i2c_1_pinmux {
> +		mux {
> +			pins = "gpio34", "gpio35";
> +			function = "blsp_i2c1";
> +			bias-disable;
> +		};
> +	};
> +
> +	// cypress cyw20704 connected over usb
> +	bluetooth-enable {
> +		gpio-hog;
> +		gpios = <48 GPIO_ACTIVE_HIGH>;
> +		output-high;
> +	};
> +};
> +
> +&usb3_ss_phy {
> +	status = "ok";
> +};
> +
> +&usb3_hs_phy {
> +	status = "ok";
> +};
> +
> +&usb3 {
> +	status = "ok";
> +};
> +
> +&usb2_hs_phy {
> +	status = "ok";
> +};
> +
> +&usb2 {
> +	status = "ok";
> +};
> +
> +&blsp1_uart1 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&serial_0_pins>;
> +	pinctrl-names = "default";
> +};
> +
> +&watchdog {
> +	status = "ok";
> +};
> +
> +&prng {
> +	status = "ok";
> +};
> +
> +&crypto {
> +	status = "ok";
> +};
> +
> +&cryptobam {
> +	status = "ok";
> +};
> +
> +&blsp_dma {
> +	status = "ok";
> +};
> +
> +&blsp1_spi1 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&spi_0_pins>;
> +	pinctrl-names = "default";
> +	cs-gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>;
> +
> +	flash@0 {
> +		reg = <0>;
> +		compatible = "jedec,spi-nor";
> +		spi-max-frequency = <24000000>;
> +
> +		partitions {
> +			compatible = "fixed-partitions";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +
> +			partition@0 {
> +				label = "0:SBL1";
> +				reg = <0x0 0x40000>;
> +				read-only;
> +			};
> +
> +			partition@40000 {
> +				label = "0:MIBIB";
> +				reg = <0x40000 0x20000>;
> +				read-only;
> +			};
> +
> +			partition@60000 {
> +				label = "0:QSEE";
> +				reg = <0x60000 0x60000>;
> +				read-only;
> +			};
> +
> +			partition@c0000 {
> +				label = "0:CDT";
> +				reg = <0xc0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@d0000 {
> +				label = "0:DDRPARAMS";
> +				reg = <0xd0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@e0000 {
> +				label = "0:ART";
> +				reg = <0xe0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@f0000 {
> +				label = "0:APPSBL";
> +				reg = <0xf0000 0x100000>;
> +				read-only;
> +			};
> +
> +			partition@1f0000 {
> +				label = "0:IDENTITY";
> +				reg = <0x1f0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@200000 {
> +				label = "0:PK";
> +				reg = <0x200000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@210000 {
> +				label = "0:APPSBLENV";
> +				reg = <0x210000 0x20000>;
> +				// r/w allowed because installation needs to modify u-boot env
> +			};
> +
> +			partition@230000 {
> +				label = "0:SYSVAR";
> +				reg = <0x230000 0x10000>;
> +				read-only;
> +			};
> +		};
> +	};
> +};
> +
> +&vqmmc {
> +	status = "ok";
> +};
> +
> +&sdhci {
> +	status = "ok";
> +
> +	pinctrl-0 = <&sd_0_pins>;
> +	pinctrl-names = "default";
> +	vqmmc-supply = <&vqmmc>;
> +	non-removable;
> +	no-1-8-v;
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +
> +	emmc@0 {
> +		compatible = "mmc-card";
> +		reg = <0>;
> +	};
> +};
> +
> +&blsp1_i2c3 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&i2c_0_pins>;
> +	pinctrl-names = "default";
> +};
> +
> +&blsp1_i2c4 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&i2c_1_pins>;
> +	pinctrl-names = "default";
> +
> +	led-controller@30 {
> +		compatible = "ti,lp5562";
> +		reg = <0x30>;
> +		clock-mode = /bits/ 8 <2>;
> +		skip-reset;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		// current limits from stock dt
> +
> +		led_status_red: led@0 {
> +			chan-name = "red:status";
> +			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
> +			max-cur = /bits/ 8 <0xFF>; /* 25.5 mA */
> +			reg = <0>;
> +			color = <LED_COLOR_ID_RED>;
> +		};
> +
> +		led_status_green: led@1 {
> +			chan-name = "green:status";
> +			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
> +			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			reg = <1>;
> +			color = <LED_COLOR_ID_GREEN>;
> +		};
> +
> +		led_status_blue: led@2 {
> +			chan-name = "blue:status";
> +			led-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			reg = <2>;
> +			color = <LED_COLOR_ID_BLUE>;
> +		};
> +
> +		led_status_white: led@3 {
> +			chan-name = "white:status";
> +			led-cur = /bits/ 8 <0x96>; /* 15.0 mA */
> +			max-cur = /bits/ 8 <0x96>; /* 15.0 mA */
> +			reg = <3>;
> +			color = <LED_COLOR_ID_WHITE>;
> +		};
> +	};
> +};
> +
> +&gmac {
> +	status = "ok";
> +};
> +
> +&mdio {
> +	status = "ok";
> +};
> +
> +&switch {
> +	status = "ok";
> +};
> +
> +&swport4 {
> +	status = "ok";
> +
> +	label = "wan"; //arbitrary, no marking
> +};
> +
> +&swport5 {
> +	status = "ok";
> +
> +	label = "lan"; //arbitrary, no marking
> +};
> +
> +&wifi0 {
> +	status = "ok";
> +
> +	qcom,ath10k-calibration-variant = "Eero-Cento";
> +	qcom,coexist-support = <1>;
> +	qcom,coexist-gpio-pin = <52>;
> +};
> +
> +&wifi1 {
> +	status = "ok";
> +
> +	qcom,ath10k-calibration-variant = "Eero-Cento";
> +};
> diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk
> index adce4fe0f2..14e209fd2a 100644
> --- a/target/linux/ipq40xx/image/generic.mk
> +++ b/target/linux/ipq40xx/image/generic.mk
> @@ -448,6 +448,19 @@ endef
>  # Missing DSA Setup
>  #TARGET_DEVICES += edgecore_oap100
>  
> +define Device/eero_cento
> +	$(call Device/FitImage)
> +	$(call Device/UbiFit)
> +	DEVICE_VENDOR := Eero
> +	DEVICE_MODEL := Cento (J010001)
> +	SOC := qcom-ipq4019
> +	DEVICE_PACKAGES := e2fsprogs kmod-fs-f2fs mkf2fs kmod-mmc kmod-fs-ext4 \
> +		ipq-wifi-eero_cento kmod-bluetooth bluez-libs bluez-utils
> +	IMAGES := sysupgrade.bin
> +	IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
> +endef
> +TARGET_DEVICES += eero_cento
> +
>  define Device/engenius_eap1300
>  	$(call Device/FitImage)
>  	DEVICE_VENDOR := EnGenius
> -- 
> 2.40.1
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Robert Marko June 16, 2023, 9:03 a.m. UTC | #2
On 17. 05. 2023. 19:47, Connor Northway wrote:
> development/internal name: Cento
> public name: eero (2nd-gen)
> model number: J010001
>
> Hardware Info:
>
>      SoC                 : Qualcomm IPQ4019
>      RAM                 : 512 MB (Hynix NT5CC256M16ER-EK)
>      SPI Flash           : 8 MB (Winbond W25Q64JVSS)
>      eMMC Flash          : 4 GB (Kingston EMMC04G-M627)
>      Wi-Fi               : 2.4 Ghz 2x2 802.11n (IPQ4019)
>      Wi-Fi               : 5 Ghz 2x2 802.11ac (IPQ4019)
>      Ethernet            : 2x 10/100/1000
>      Bluetooth           : 5.0 / BLE (Cypress CYW20704)
>      RGBW LED Controller : Texas Instruments LP5562
>      Buttons             : 1x Reset (bottom of device)
>      UART & SPI header   : see below
>      Power & USB 2       : USB Type-C, requires 15W 5V/3A
>
> Stock Firmware Layout:
>
>      The SPI flash stores U-Boot, U-Boot environment, hardware
>      calibration, and eero-specific information (keys, serial number,
>      MAC address, etc).
>
>      The eMMC is partitioned into an A/B layout with two sets of an ext2
>      kernel and ext4 rootfs partition, plus one cache partition.
>
> MAC Address assignment:
>
>      One base MAC address is stored in SPI flash, with a space of 32
>      allocated. We follow stock layout, though stock FW also uses higher
>      addresses for routing / mesh applications.
>
>      Base / WAN : 48:dd:0c:**:**:xx
>      LAN        : 48:dd:0c:**:**:xx + 1
>      Bluetooth  : 48:dd:0c:**:**:xx + 2
>      Wi-Fi 1    : 48:dd:0c:**:**:xx + 3
>      Wi-Fi 2    : 48:dd:0c:**:**:xx + 4
>
> Other OpenWrt details:
>
>      RGBW LED has been configured to the following:
>        Red: Failsafe
>        Green: Running
>        Blue: Upgrade
>        White: Boot
>
>      Ethernet ports are not labeled. Default config when viewed from
>      back: right port is WAN, left port is LAN.
>
> UART/SPI Header:
>
>      2x5 1.27mm unpopulated SMT header labeled "J2".
>      Silkscreen dot marks pin 1; pins 2, 9, and 10 labeled with number.
>
>      Layout:
>
>          SPI cpu bypass --| 1  2 |-- SPI /CS
>                 SPI CLK --| 3  4 |-- SPI DI
>                  SPI DO --| 5  6 |-- GND
>                    ???? --| 7  8 |-- 3v3
>                 UART RX --| 9 10 |-- UART TX
>
>      To access the SPI flash chip, pull pin 1 high (connect to 3v3)
>      before applying power through USB. This will disconnect the SPI
>      lines from the CPU and allow accessing the SPI bus without
>      contention. As always, do not connect external power to 3v3!
>
>      UART settings: 115200 8n1
>
>      Logic levels for UART and SPI are 3.3V.
>
> OpenWrt Installation:
>
>      Unfortunately, eero has configured U-Boot to autoboot with no delay
>      and no way to abort boot. There does not appear to be any available
>      method to boot from an external source without modifications.
>
>      To change the bootdelay variable, editing the SPI flash contents
>      directly is required:
>      - Use an SPI programmer to make a backup of the flash
>      - Locate the APPSBLENV partition and modify the environment to have
>        a non-zero bootdelay value in both locations
>      - Update the leading CRC32 checksums
>      - Flash image back to the device and reboot
>
>      Details of environment layout:
>          APPSBLENV is at 0x210000. It contains two redundant copies of
>          the following struct, each 0x10000 bytes long.
>
>              struct env {
>                  uint32_t crc; // calculated only over data[]
>                  unsigned char flags;
>                  unsigned char data[0x10000-5];
>              };
>
>      Once access to the U-Boot shell is achieved:
>
>      Use TFTP to load the initramfs image to memory:
>      `tftpboot <initramfs-image>.itb`
>      with a TFTP server at 192.168.1.1
>
>      or use USB:
>      `fatload usb <dev[:part]> 0x84000000 <initramfs-image>.itb`
>      with FAT32 USB drive connected through a powered hub
>      (use `usb part` to view partitions)
>
>      clear the bootargs variable:
>      `setenv bootargs`
>
>      then boot:
>      `bootm`
>
>      Make backups of SPI and eMMC flash!
>
>      Use SSH/SCP to transfer the sysupgrade file and run sysupgrade.
>
>      The upgrade script will determine which of the two dual-partition
>      slots the device is currently booting from. It will install to the
>      inactive slot if installing over stock FW, or the current slot if
>      upgrading OpenWrt. This leaves the most recent stock FW untouched
>      for easier restoration to stock. Don't rely on this as a backup!
>
> Restore Stock Firmware:
>
>      If installed using sysupgrade and one stock partition set remains,
>      replace the U-Boot environment variables `bootargs` and `bootcmd`
>      with the contents of the `*_eero_backup` versions automatically
>      created during install. Stock firmware should overwrite OpenWrt the
>      next time it receives an update.
>
> Signed-off-by: Connor Northway <contact@cnorthway.com>
> ---
>   package/boot/uboot-envtools/files/ipq40xx     |   4 +
>   package/firmware/ipq-wifi/Makefile            |   2 +
>   .../ipq40xx/base-files/etc/board.d/02_network |   5 +
>   .../etc/hotplug.d/firmware/11-ath10k-caldata  |   8 +
>   .../etc/hotplug.d/usb/20-bluetooth-mac        |  21 +
>   .../ipq40xx/base-files/lib/upgrade/eero.sh    |  94 +++++
>   .../base-files/lib/upgrade/platform.sh        |   3 +
>   .../arch/arm/boot/dts/qcom-ipq4019-cento.dts  | 393 ++++++++++++++++++
>   target/linux/ipq40xx/image/generic.mk         |  13 +
>   9 files changed, 543 insertions(+)
>   create mode 100644 target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
>   create mode 100644 target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
>   create mode 100644 target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
>
> diff --git a/package/boot/uboot-envtools/files/ipq40xx b/package/boot/uboot-envtools/files/ipq40xx
> index 8d993fae36..e8431048eb 100644
> --- a/package/boot/uboot-envtools/files/ipq40xx
> +++ b/package/boot/uboot-envtools/files/ipq40xx
> @@ -56,6 +56,10 @@ aruba,ap-365)
>   buffalo,wtr-m2133hp)
>   	ubootenv_add_uci_config "/dev/mtd8" "0x0" "0x40000" "0x20000"
>   	;;
> +eero,cento)
> +	ubootenv_add_uci_config "/dev/mtd9" "0x0" "0x10000" "0x10000"
> +	ubootenv_add_uci_config "/dev/mtd9" "0x10000" "0x10000" "0x10000"
> +	;;
>   linksys,ea6350v3)
>   	ubootenv_add_uci_config "/dev/mtd7" "0x0" "0x20000" "0x20000"
>   	;;
> diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile
> index aeb690e913..f2e670e65c 100644
> --- a/package/firmware/ipq-wifi/Makefile
> +++ b/package/firmware/ipq-wifi/Makefile
> @@ -38,6 +38,7 @@ ALLWIFIBOARDS:= \
>   	edgecore_ecw5410 \
>   	edgecore_oap100 \
>   	edimax_cax1800 \
> +	eero_cento\
>   	extreme-networks_ws-ap3915i \
>   	glinet_gl-a1300 \
>   	glinet_gl-ap1300 \
> @@ -143,6 +144,7 @@ $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102))
>   $(eval $(call generate-ipq-wifi-package,edgecore_ecw5410,Edgecore ECW5410))
>   $(eval $(call generate-ipq-wifi-package,edgecore_oap100,Edgecore OAP100))
>   $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800))
> +$(eval $(call generate-ipq-wifi-package,eero_cento,Eero Cento))
>   $(eval $(call generate-ipq-wifi-package,extreme-networks_ws-ap3915i,Edgecore OAP100))
>   $(eval $(call generate-ipq-wifi-package,glinet_gl-a1300,GL.iNet GL-A1300))
>   $(eval $(call generate-ipq-wifi-package,glinet_gl-ap1300,GL.iNet GL-AP1300))
> diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network
> index cb318d36a3..e5bd04a56e 100644
> --- a/target/linux/ipq40xx/base-files/etc/board.d/02_network
> +++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network
> @@ -32,6 +32,7 @@ ipq40xx_setup_interfaces()
>   	asus,map-ac2200|\
>   	cilab,meshpoint-one|\
>   	edgecore,ecw5211|\
> +	eero,cento|\
>   	glinet,gl-ap1300|\
>   	glinet,gl-b2200|\
>   	google,wifi|\
> @@ -159,6 +160,10 @@ ipq40xx_setup_macs()
>   		lan_mac=$(mtd_get_mac_ascii bdcfg lanmac)
>   		label_mac=$lan_mac
>   		;;
> +	eero,cento)
> +		wan_mac=$(mtd_get_mac_ascii 0:IDENTITY mac)
> +		lan_mac=$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 1)
This should be replaced with NVMEM cells instead.
> +		;;
>   	engenius,eap2200|\
>   	engenius,emd1)
>   		lan_mac=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr)
> diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> index 7f41bdcfcc..4b6072f0fe 100644
> --- a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> +++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
> @@ -83,6 +83,10 @@ case "$FIRMWARE" in
>   		caldata_extract "ART" 0x1000 0x2f20
>   		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac)
>   		;;
> +	eero,cento)
> +		caldata_extract "0:ART" 0x1000 0x2f20
> +		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 3)
Use NVMEM cells for both caldata and MAC setting.
> +		;;
>   	engenius,emd1)
>   		caldata_extract "0:ART" 0x1000 0x2f20
>   		ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr)
> @@ -178,6 +182,10 @@ case "$FIRMWARE" in
>   		caldata_extract "ART" 0x5000 0x2f20
>   		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac_a)
>   		;;
> +	eero,cento)
> +		caldata_extract "0:ART" 0x5000 0x2f20
> +		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 4)
Use NVMEM cells for both caldata and MAC setting.
> +		;;
>   	engenius,emd1)
>   		caldata_extract "0:ART" 0x5000 0x2f20
>   		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr) 1)
> diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
> new file mode 100644
> index 0000000000..eada210214
> --- /dev/null
> +++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
> @@ -0,0 +1,21 @@
> +. /lib/functions/system.sh
> +
> +setup_bluetooth_mac()
> +{
> +	local board=$(board_name)
> +
> +	case "$board" in
> +	eero,cento)
> +		hciconfig hci0 up
> +		bdaddr "$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 2)"
> +		hciconfig hci0 reset
> +		hciconfig hci0 down
> +	;;
> +	esac
> +}
> +
> +if [ "${DRIVER}" = "btusb" ]; then
> +	if [ "${ACTION}" = "bind" ]; then
> +		setup_bluetooth_mac
> +	fi
> +fi
> diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
> new file mode 100644
> index 0000000000..c27c4bfad7
> --- /dev/null
> +++ b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
> @@ -0,0 +1,94 @@
> +# Flashes upgrade package using eero's A/B partition scheme
> +# will leave most recent eero installation alone as a backup
> +platform_do_upgrade_eero_cento() {
> +	local tar_file="$1"
> +
> +	local current_bootcmd
> +	local current_bootargs
> +	current_bootcmd=$(fw_printenv -n bootcmd)
> +	current_bootargs=$(fw_printenv -n bootargs)
> +	local first_install
> +	first_install=$(echo "$current_bootcmd" | grep -c "eero_kernel")
> +	if [ $first_install -eq 1 ]; then
> +		echo "first installation! backing up important stock env variables"
> +		fw_setenv -s - <<-EOF
> +			bootcmd_eero_backup $current_bootcmd
> +			bootargs_eero_backup $current_bootargs
> +		EOF
> +	fi
> +
> +###
> +# Determine partitions
> +###
> +	local current_bootpart
> +	local current_rootpart
> +	current_bootpart=$(echo "$current_bootcmd" | sed -nr 's/.*ext2load mmc 0:([[:digit:]]).*/\1/p')
> +	current_rootpart=$(echo "$current_bootargs" | sed -nr 's/.*root=\/dev\/mmcblk0p([[:digit:]]).*/\1/p')
> +
> +	# validate that they're sane; determine inactive partitions
> +	if [ "$current_bootpart" -eq 1 ] && [ "$current_rootpart" -eq 3 ]; then
> +		local inactive_bootpart=2
> +		local inactive_rootpart=4
> +		echo "currently booting from slot A"
> +	elif [ "$current_bootpart" -eq 2 ] && [ "$current_rootpart" -eq 4 ]; then
> +		local inactive_bootpart=1
> +		local inactive_rootpart=3
> +		echo "currently booting from slot B"
> +	else
> +		echo "invalid boot or root partitions"
> +		return 1
> +	fi
> +
> +	# on the first install, we want to install to the inactive slot
> +	# (easier to recover to known-good, configured stock OS).
> +	# on subsequent installs, continue using the active slot to keep
> +	# the stock install intact.
> +	if $first_install; then
> +		local install_bootpart=$inactive_bootpart
> +		local install_rootpart=$inactive_rootpart
> +		echo "installing to inactive slot;"
> +	else
> +		local install_bootpart=$current_bootpart
> +		local install_rootpart=$current_rootpart
> +		echo "installing to currently booted slot;"
> +	fi
> +	echo "boot: $install_bootpart"
> +	echo "root: $install_rootpart"
> +
> +###
> +# Install kernel to ext2 part
> +###
> +	local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$')
> +	board_dir=${board_dir%/}
> +
> +	# mount boot partition
> +	mkdir /mnt/bootpart &&
> +	mount -t ext2 /dev/mmcblk0p"$install_bootpart" /mnt/bootpart ||
> +	{ echo "unable to mount boot partition" && return 1 ; }
> +
> +	# clean it out and install new kernel image
> +	rm -r /mnt/bootpart/* &&
> +	mkdir /mnt/bootpart/lost+found &&
> +	tar Oxf $tar_file ${board_dir}/kernel > /mnt/bootpart/kernel.itb ||
> +	{ echo "unable to install kernel image" && return 1 ; }
> +
> +	echo "installed kernel image to /dev/mmcblk0p$install_bootpart"
> +
> +###
> +# Install rootfs using emmc_do_upgrade
> +###
> +	EMMC_ROOT_DEV=/dev/mmcblk0p"$install_rootpart"
> +	emmc_do_upgrade $tar_file
> +
> +	echo "done with emmc_do_upgrade, rootfs should be installed"
> +
> +###
> +# Finalize env vars
> +###
> +	fw_setenv -s - <<-EOF
> +		bootcmd ext2load mmc 0:$install_bootpart 0x84000000 kernel.itb && bootm; reset
> +		bootargs root=/dev/mmcblk0p$install_rootpart rootwait
> +	EOF
> +
> +	echo "new boot cmd/args set, install complete!"
> +}
> diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> index 988921fa8c..74f425861b 100644
> --- a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> +++ b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
> @@ -156,6 +156,9 @@ platform_do_upgrade() {
>   	compex,wpj419)
>   		nand_do_upgrade "$1"
>   		;;
> +	eero,cento)
> +		platform_do_upgrade_eero_cento "$1"
> +		;;
>   	google,wifi)
>   		export_bootdevice
>   		export_partdevice CI_ROOTDEV 0
> diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
> new file mode 100644
> index 0000000000..5c646de918
> --- /dev/null
> +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
> +
> +#include "qcom-ipq4019.dtsi"
> +#include <dt-bindings/input/input.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/leds/common.h>
> +#include <dt-bindings/soc/qcom,tcsr.h>
> +
> +/ {
> +	model = "Eero Cento (J010001)";
> +	compatible = "eero,cento";
> +
> +	chosen {
> +		stdout-path = &blsp1_uart1;
> +	};
> +
> +	aliases {
> +		led-boot = &led_status_white;
> +		led-failsafe = &led_status_red;
> +		led-running = &led_status_green;
> +		led-upgrade = &led_status_blue;
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x80000000 0x20000000>;

Is this required at all, bootloader should patch it in.

> +	};
> +
> +	keys {
> +		compatible = "gpio-keys";
> +
> +		reset {
> +			label = "reset";
> +			gpios = <&tlmm 18 GPIO_ACTIVE_LOW>;
> +			linux,code = <KEY_RESTART>;
> +		};
> +	};
> +
> +	tcsr@1949000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1949000 0x100>;
> +		qcom,wifi_glb_cfg = <TCSR_WIFI_GLB_CFG>;
> +	};
> +
> +	tcsr@194b000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x194b000 0x100>;
> +		qcom,usb-hsphy-mode-select = <TCSR_USB_HSPHY_HOST_MODE>;
> +	};
> +
> +	ess_tcsr@1953000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1953000 0x1000>;
> +		qcom,ess-interface-select = <TCSR_ESS_PSGMII>;
> +	};
> +
> +	tcsr@1957000 {
> +		compatible = "qcom,tcsr";
> +		reg = <0x1957000 0x100>;
> +		qcom,wifi_noc_memtype_m0_m2 = <TCSR_WIFI_NOC_MEMTYPE_M0_M2>;
> +	};
> +
> +};
> +
> +&tlmm {
> +	serial_0_pins: serial0_pinmux {
> +		mux {
> +			pins = "gpio16", "gpio17";
> +			function = "blsp_uart0";
> +			bias-disable;
> +		};
> +	};
> +
> +	spi_0_pins: spi_0_pinmux {
> +			pinmux {
> +					function = "blsp_spi0";
> +					pins = "gpio13", "gpio14", "gpio15";
> +			};
> +
> +			pinmux_cs {
> +					function = "gpio";
> +					pins = "gpio12";
> +			};
> +
> +			pinconf {
> +					pins = "gpio13", "gpio14", "gpio15";
> +					bias-disable;
> +			};
> +
> +			pinconf_cs {
> +					pins = "gpio12";
> +					bias-disable;
> +					output-high;
> +			};
> +	};
> +
> +	sd_0_pins: sd_0_pinmux {
> +		sd0 {
> +			function = "sdio";
> +			pins = "gpio23", "gpio24", "gpio25", "gpio26", "gpio29",
> +					"gpio30", "gpio31", "gpio32";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +
> +		sdclk {
> +			pins = "gpio27";
> +			function = "sdio";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +
> +		sdcmd {
> +			pins = "gpio28";
> +			function = "sdio";
> +			drive-strength = <4>;
> +			bias-disable;
> +		};
> +	};
> +
> +	i2c_0_pins: i2c_0_pinmux {
> +		mux {
> +			pins = "gpio20", "gpio21";
> +			function = "blsp_i2c0";
> +			bias-disable;
> +		};
> +	};
> +
> +	i2c_1_pins: i2c_1_pinmux {
> +		mux {
> +			pins = "gpio34", "gpio35";
> +			function = "blsp_i2c1";
> +			bias-disable;
> +		};
> +	};
> +
> +	// cypress cyw20704 connected over usb
> +	bluetooth-enable {
> +		gpio-hog;
> +		gpios = <48 GPIO_ACTIVE_HIGH>;
> +		output-high;
> +	};
> +};
> +
> +&usb3_ss_phy {
> +	status = "ok";
Replace "ok" with "okay", "ok" has been deprecated for years.
Same applies for all nodes.
> +};
> +
> +&usb3_hs_phy {
> +	status = "ok";
> +};
> +
> +&usb3 {
> +	status = "ok";
> +};
> +
> +&usb2_hs_phy {
> +	status = "ok";
> +};
> +
> +&usb2 {
> +	status = "ok";
> +};
> +
> +&blsp1_uart1 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&serial_0_pins>;
> +	pinctrl-names = "default";
> +};
> +
> +&watchdog {
> +	status = "ok";
> +};
> +
> +&prng {
> +	status = "ok";
> +};
> +
> +&crypto {
> +	status = "ok";
> +};
> +
> +&cryptobam {
> +	status = "ok";
> +};
> +
> +&blsp_dma {
> +	status = "ok";
> +};
> +
> +&blsp1_spi1 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&spi_0_pins>;
> +	pinctrl-names = "default";
> +	cs-gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>;
> +
> +	flash@0 {
> +		reg = <0>;
> +		compatible = "jedec,spi-nor";
> +		spi-max-frequency = <24000000>;
> +
> +		partitions {
> +			compatible = "fixed-partitions";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +
> +			partition@0 {
> +				label = "0:SBL1";
> +				reg = <0x0 0x40000>;
> +				read-only;
> +			};
> +
> +			partition@40000 {
> +				label = "0:MIBIB";
> +				reg = <0x40000 0x20000>;
> +				read-only;
> +			};
> +
> +			partition@60000 {
> +				label = "0:QSEE";
> +				reg = <0x60000 0x60000>;
> +				read-only;
> +			};
> +
> +			partition@c0000 {
> +				label = "0:CDT";
> +				reg = <0xc0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@d0000 {
> +				label = "0:DDRPARAMS";
> +				reg = <0xd0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@e0000 {
> +				label = "0:ART";
> +				reg = <0xe0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@f0000 {
> +				label = "0:APPSBL";
> +				reg = <0xf0000 0x100000>;
> +				read-only;
> +			};
> +
> +			partition@1f0000 {
> +				label = "0:IDENTITY";
> +				reg = <0x1f0000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@200000 {
> +				label = "0:PK";
> +				reg = <0x200000 0x10000>;
> +				read-only;
> +			};
> +
> +			partition@210000 {
> +				label = "0:APPSBLENV";
> +				reg = <0x210000 0x20000>;
> +				// r/w allowed because installation needs to modify u-boot env
> +			};
> +
> +			partition@230000 {
> +				label = "0:SYSVAR";
> +				reg = <0x230000 0x10000>;
> +				read-only;
> +			};
> +		};
> +	};
> +};
> +
> +&vqmmc {
> +	status = "ok";
> +};
> +
> +&sdhci {
> +	status = "ok";
> +
> +	pinctrl-0 = <&sd_0_pins>;
> +	pinctrl-names = "default";
> +	vqmmc-supply = <&vqmmc>;
> +	non-removable;
> +	no-1-8-v;
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +
> +	emmc@0 {
> +		compatible = "mmc-card";
> +		reg = <0>;
> +	};
> +};
> +
> +&blsp1_i2c3 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&i2c_0_pins>;
> +	pinctrl-names = "default";
> +};
> +
> +&blsp1_i2c4 {
> +	status = "ok";
> +
> +	pinctrl-0 = <&i2c_1_pins>;
> +	pinctrl-names = "default";
> +
> +	led-controller@30 {
> +		compatible = "ti,lp5562";
> +		reg = <0x30>;
> +		clock-mode = /bits/ 8 <2>;
> +		skip-reset;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		// current limits from stock dt
> +
> +		led_status_red: led@0 {
> +			chan-name = "red:status";
> +			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
> +			max-cur = /bits/ 8 <0xFF>; /* 25.5 mA */
> +			reg = <0>;
> +			color = <LED_COLOR_ID_RED>;
> +		};
> +
> +		led_status_green: led@1 {
> +			chan-name = "green:status";
> +			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
> +			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			reg = <1>;
> +			color = <LED_COLOR_ID_GREEN>;
> +		};
> +
> +		led_status_blue: led@2 {
> +			chan-name = "blue:status";
> +			led-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
> +			reg = <2>;
> +			color = <LED_COLOR_ID_BLUE>;
> +		};
> +
> +		led_status_white: led@3 {
> +			chan-name = "white:status";
> +			led-cur = /bits/ 8 <0x96>; /* 15.0 mA */
> +			max-cur = /bits/ 8 <0x96>; /* 15.0 mA */
> +			reg = <3>;
> +			color = <LED_COLOR_ID_WHITE>;
> +		};
> +	};
> +};
> +
> +&gmac {
> +	status = "ok";
> +};
> +
> +&mdio {
> +	status = "ok";
> +};
> +
> +&switch {
> +	status = "ok";
> +};
> +
> +&swport4 {
> +	status = "ok";
> +
> +	label = "wan"; //arbitrary, no marking
> +};
> +
> +&swport5 {
> +	status = "ok";
> +
> +	label = "lan"; //arbitrary, no marking
> +};
> +
> +&wifi0 {
> +	status = "ok";
> +
> +	qcom,ath10k-calibration-variant = "Eero-Cento";
> +	qcom,coexist-support = <1>;
> +	qcom,coexist-gpio-pin = <52>;
> +};
> +
> +&wifi1 {
> +	status = "ok";
> +
> +	qcom,ath10k-calibration-variant = "Eero-Cento";
> +};
> diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk
> index adce4fe0f2..14e209fd2a 100644
> --- a/target/linux/ipq40xx/image/generic.mk
> +++ b/target/linux/ipq40xx/image/generic.mk
> @@ -448,6 +448,19 @@ endef
>   # Missing DSA Setup
>   #TARGET_DEVICES += edgecore_oap100
>   
> +define Device/eero_cento
> +	$(call Device/FitImage)
> +	$(call Device/UbiFit)
> +	DEVICE_VENDOR := Eero
> +	DEVICE_MODEL := Cento (J010001)
> +	SOC := qcom-ipq4019
> +	DEVICE_PACKAGES := e2fsprogs kmod-fs-f2fs mkf2fs kmod-mmc kmod-fs-ext4 \
What is kmod-mmc required for?
MMC/SDHCI support is compiled in on this target already.
> +		ipq-wifi-eero_cento kmod-bluetooth bluez-libs bluez-utils
> +	IMAGES := sysupgrade.bin
> +	IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata

You are including $(call Device/UbiFit) which already has the same 
recipe for
sysupgrade.bin

> +endef
> +TARGET_DEVICES += eero_cento
> +
>   define Device/engenius_eap1300
>   	$(call Device/FitImage)
>   	DEVICE_VENDOR := EnGenius
Connor Northway Sept. 30, 2023, 8:23 p.m. UTC | #3
Hi,

Thanks for the thorough review and sorry for the delay in getting back
to it!

I just wanted to double check regarding the mac address concern before I
send out another patch.

(CC Christian, who replied with the same question on the original email)

>> +    eero,cento)
>> +        wan_mac=$(mtd_get_mac_ascii 0:IDENTITY mac)
>> +        lan_mac=$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 1)
> This should be replaced with NVMEM cells instead.

>> +    eero,cento)
>> +        caldata_extract "0:ART" 0x1000 0x2f20
>> +        ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY
>> mac) 3)
> Use NVMEM cells for both caldata and MAC setting.

>> +    eero,cento)
>> +        caldata_extract "0:ART" 0x5000 0x2f20
>> +        ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY
>> mac) 4)
> Use NVMEM cells for both caldata and MAC setting.

The pre-calibration bin from the 0:ART partiti
on has an
incorrect/generic mac address, which needs to be patched to the correct
one. If I understand correctly, providing the cal bin with an NVMEM cell
means you also need to provide the mac address with an NVMEM cell at the
same time - you can't patch it later.

To get the mac address into an NVMEM cell you'd need a constant memory
location, and this router only stores its mac address in an ASCII
key-value area, so I can't guarantee a specific offset.

The mtd_get_mac_ascii function has no problem extracting the value.

Let me know if I'm misunderstanding any of this or missing something.
I would definitely prefer to have it all contained to the device tree.

>> +    memory {
>> +        device_type = "memory";
>> +        reg = <0x80000000 0x20000000>;
>
> Is this required at all, bootloader should patch it in.

Nope. Removed it and it works fine. I think it was residual from my
early tests.

> Replace "ok" with "okay", "ok" has been deprec
ated for years.
> Same applies for all nodes.

Will do.

> What is kmod-mmc required for?
> MMC/SDHCI support is compiled in on this target already.

Don't actually need it, you're right. Tested without and it works fine.

> You are including $(call Device/UbiFit) which already has the same
> recipe for sysupgrade.bin

Thanks, will remove.
diff mbox series

Patch

diff --git a/package/boot/uboot-envtools/files/ipq40xx b/package/boot/uboot-envtools/files/ipq40xx
index 8d993fae36..e8431048eb 100644
--- a/package/boot/uboot-envtools/files/ipq40xx
+++ b/package/boot/uboot-envtools/files/ipq40xx
@@ -56,6 +56,10 @@  aruba,ap-365)
 buffalo,wtr-m2133hp)
 	ubootenv_add_uci_config "/dev/mtd8" "0x0" "0x40000" "0x20000"
 	;;
+eero,cento)
+	ubootenv_add_uci_config "/dev/mtd9" "0x0" "0x10000" "0x10000"
+	ubootenv_add_uci_config "/dev/mtd9" "0x10000" "0x10000" "0x10000"
+	;;
 linksys,ea6350v3)
 	ubootenv_add_uci_config "/dev/mtd7" "0x0" "0x20000" "0x20000"
 	;;
diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile
index aeb690e913..f2e670e65c 100644
--- a/package/firmware/ipq-wifi/Makefile
+++ b/package/firmware/ipq-wifi/Makefile
@@ -38,6 +38,7 @@  ALLWIFIBOARDS:= \
 	edgecore_ecw5410 \
 	edgecore_oap100 \
 	edimax_cax1800 \
+	eero_cento\
 	extreme-networks_ws-ap3915i \
 	glinet_gl-a1300 \
 	glinet_gl-ap1300 \
@@ -143,6 +144,7 @@  $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102))
 $(eval $(call generate-ipq-wifi-package,edgecore_ecw5410,Edgecore ECW5410))
 $(eval $(call generate-ipq-wifi-package,edgecore_oap100,Edgecore OAP100))
 $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800))
+$(eval $(call generate-ipq-wifi-package,eero_cento,Eero Cento))
 $(eval $(call generate-ipq-wifi-package,extreme-networks_ws-ap3915i,Edgecore OAP100))
 $(eval $(call generate-ipq-wifi-package,glinet_gl-a1300,GL.iNet GL-A1300))
 $(eval $(call generate-ipq-wifi-package,glinet_gl-ap1300,GL.iNet GL-AP1300))
diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network b/target/linux/ipq40xx/base-files/etc/board.d/02_network
index cb318d36a3..e5bd04a56e 100644
--- a/target/linux/ipq40xx/base-files/etc/board.d/02_network
+++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network
@@ -32,6 +32,7 @@  ipq40xx_setup_interfaces()
 	asus,map-ac2200|\
 	cilab,meshpoint-one|\
 	edgecore,ecw5211|\
+	eero,cento|\
 	glinet,gl-ap1300|\
 	glinet,gl-b2200|\
 	google,wifi|\
@@ -159,6 +160,10 @@  ipq40xx_setup_macs()
 		lan_mac=$(mtd_get_mac_ascii bdcfg lanmac)
 		label_mac=$lan_mac
 		;;
+	eero,cento)
+		wan_mac=$(mtd_get_mac_ascii 0:IDENTITY mac)
+		lan_mac=$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 1)
+		;;
 	engenius,eap2200|\
 	engenius,emd1)
 		lan_mac=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr)
diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
index 7f41bdcfcc..4b6072f0fe 100644
--- a/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
+++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
@@ -83,6 +83,10 @@  case "$FIRMWARE" in
 		caldata_extract "ART" 0x1000 0x2f20
 		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac)
 		;;
+	eero,cento)
+		caldata_extract "0:ART" 0x1000 0x2f20
+		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 3)
+		;;
 	engenius,emd1)
 		caldata_extract "0:ART" 0x1000 0x2f20
 		ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr)
@@ -178,6 +182,10 @@  case "$FIRMWARE" in
 		caldata_extract "ART" 0x5000 0x2f20
 		ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac_a)
 		;;
+	eero,cento)
+		caldata_extract "0:ART" 0x5000 0x2f20
+		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:IDENTITY mac) 4)
+		;;
 	engenius,emd1)
 		caldata_extract "0:ART" 0x5000 0x2f20
 		ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr) 1)
diff --git a/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
new file mode 100644
index 0000000000..eada210214
--- /dev/null
+++ b/target/linux/ipq40xx/base-files/etc/hotplug.d/usb/20-bluetooth-mac
@@ -0,0 +1,21 @@ 
+. /lib/functions/system.sh
+
+setup_bluetooth_mac()
+{
+	local board=$(board_name)
+
+	case "$board" in
+	eero,cento)
+		hciconfig hci0 up
+		bdaddr "$(macaddr_add "$(mtd_get_mac_ascii 0:IDENTITY mac)" 2)"
+		hciconfig hci0 reset
+		hciconfig hci0 down
+	;;
+	esac
+}
+
+if [ "${DRIVER}" = "btusb" ]; then
+	if [ "${ACTION}" = "bind" ]; then
+		setup_bluetooth_mac
+	fi
+fi
diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
new file mode 100644
index 0000000000..c27c4bfad7
--- /dev/null
+++ b/target/linux/ipq40xx/base-files/lib/upgrade/eero.sh
@@ -0,0 +1,94 @@ 
+# Flashes upgrade package using eero's A/B partition scheme
+# will leave most recent eero installation alone as a backup
+platform_do_upgrade_eero_cento() {
+	local tar_file="$1"
+
+	local current_bootcmd
+	local current_bootargs
+	current_bootcmd=$(fw_printenv -n bootcmd)
+	current_bootargs=$(fw_printenv -n bootargs)
+	local first_install
+	first_install=$(echo "$current_bootcmd" | grep -c "eero_kernel")
+	if [ $first_install -eq 1 ]; then
+		echo "first installation! backing up important stock env variables"
+		fw_setenv -s - <<-EOF
+			bootcmd_eero_backup $current_bootcmd
+			bootargs_eero_backup $current_bootargs
+		EOF
+	fi
+
+###
+# Determine partitions
+###
+	local current_bootpart
+	local current_rootpart
+	current_bootpart=$(echo "$current_bootcmd" | sed -nr 's/.*ext2load mmc 0:([[:digit:]]).*/\1/p')
+	current_rootpart=$(echo "$current_bootargs" | sed -nr 's/.*root=\/dev\/mmcblk0p([[:digit:]]).*/\1/p')
+
+	# validate that they're sane; determine inactive partitions
+	if [ "$current_bootpart" -eq 1 ] && [ "$current_rootpart" -eq 3 ]; then
+		local inactive_bootpart=2
+		local inactive_rootpart=4
+		echo "currently booting from slot A"
+	elif [ "$current_bootpart" -eq 2 ] && [ "$current_rootpart" -eq 4 ]; then
+		local inactive_bootpart=1
+		local inactive_rootpart=3
+		echo "currently booting from slot B"
+	else
+		echo "invalid boot or root partitions"
+		return 1
+	fi
+
+	# on the first install, we want to install to the inactive slot
+	# (easier to recover to known-good, configured stock OS).
+	# on subsequent installs, continue using the active slot to keep
+	# the stock install intact.
+	if $first_install; then
+		local install_bootpart=$inactive_bootpart
+		local install_rootpart=$inactive_rootpart
+		echo "installing to inactive slot;"
+	else
+		local install_bootpart=$current_bootpart
+		local install_rootpart=$current_rootpart
+		echo "installing to currently booted slot;"
+	fi
+	echo "boot: $install_bootpart"
+	echo "root: $install_rootpart"
+
+###
+# Install kernel to ext2 part
+###
+	local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$')
+	board_dir=${board_dir%/}
+
+	# mount boot partition
+	mkdir /mnt/bootpart &&
+	mount -t ext2 /dev/mmcblk0p"$install_bootpart" /mnt/bootpart ||
+	{ echo "unable to mount boot partition" && return 1 ; }
+
+	# clean it out and install new kernel image
+	rm -r /mnt/bootpart/* &&
+	mkdir /mnt/bootpart/lost+found &&
+	tar Oxf $tar_file ${board_dir}/kernel > /mnt/bootpart/kernel.itb ||
+	{ echo "unable to install kernel image" && return 1 ; }
+
+	echo "installed kernel image to /dev/mmcblk0p$install_bootpart"
+
+###
+# Install rootfs using emmc_do_upgrade
+###
+	EMMC_ROOT_DEV=/dev/mmcblk0p"$install_rootpart"
+	emmc_do_upgrade $tar_file
+
+	echo "done with emmc_do_upgrade, rootfs should be installed"
+
+###
+# Finalize env vars
+###
+	fw_setenv -s - <<-EOF
+		bootcmd ext2load mmc 0:$install_bootpart 0x84000000 kernel.itb && bootm; reset
+		bootargs root=/dev/mmcblk0p$install_rootpart rootwait
+	EOF
+
+	echo "new boot cmd/args set, install complete!"
+}
diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
index 988921fa8c..74f425861b 100644
--- a/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
+++ b/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh
@@ -156,6 +156,9 @@  platform_do_upgrade() {
 	compex,wpj419)
 		nand_do_upgrade "$1"
 		;;
+	eero,cento)
+		platform_do_upgrade_eero_cento "$1"
+		;;
 	google,wifi)
 		export_bootdevice
 		export_partdevice CI_ROOTDEV 0
diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
new file mode 100644
index 0000000000..5c646de918
--- /dev/null
+++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-cento.dts
@@ -0,0 +1,393 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+#include "qcom-ipq4019.dtsi"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/soc/qcom,tcsr.h>
+
+/ {
+	model = "Eero Cento (J010001)";
+	compatible = "eero,cento";
+
+	chosen {
+		stdout-path = &blsp1_uart1;
+	};
+
+	aliases {
+		led-boot = &led_status_white;
+		led-failsafe = &led_status_red;
+		led-running = &led_status_green;
+		led-upgrade = &led_status_blue;
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x80000000 0x20000000>;
+	};
+
+	keys {
+		compatible = "gpio-keys";
+
+		reset {
+			label = "reset";
+			gpios = <&tlmm 18 GPIO_ACTIVE_LOW>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
+	tcsr@1949000 {
+		compatible = "qcom,tcsr";
+		reg = <0x1949000 0x100>;
+		qcom,wifi_glb_cfg = <TCSR_WIFI_GLB_CFG>;
+	};
+
+	tcsr@194b000 {
+		compatible = "qcom,tcsr";
+		reg = <0x194b000 0x100>;
+		qcom,usb-hsphy-mode-select = <TCSR_USB_HSPHY_HOST_MODE>;
+	};
+
+	ess_tcsr@1953000 {
+		compatible = "qcom,tcsr";
+		reg = <0x1953000 0x1000>;
+		qcom,ess-interface-select = <TCSR_ESS_PSGMII>;
+	};
+
+	tcsr@1957000 {
+		compatible = "qcom,tcsr";
+		reg = <0x1957000 0x100>;
+		qcom,wifi_noc_memtype_m0_m2 = <TCSR_WIFI_NOC_MEMTYPE_M0_M2>;
+	};
+
+};
+
+&tlmm {
+	serial_0_pins: serial0_pinmux {
+		mux {
+			pins = "gpio16", "gpio17";
+			function = "blsp_uart0";
+			bias-disable;
+		};
+	};
+
+	spi_0_pins: spi_0_pinmux {
+			pinmux {
+					function = "blsp_spi0";
+					pins = "gpio13", "gpio14", "gpio15";
+			};
+
+			pinmux_cs {
+					function = "gpio";
+					pins = "gpio12";
+			};
+
+			pinconf {
+					pins = "gpio13", "gpio14", "gpio15";
+					bias-disable;
+			};
+
+			pinconf_cs {
+					pins = "gpio12";
+					bias-disable;
+					output-high;
+			};
+	};
+
+	sd_0_pins: sd_0_pinmux {
+		sd0 {
+			function = "sdio";
+			pins = "gpio23", "gpio24", "gpio25", "gpio26", "gpio29",
+					"gpio30", "gpio31", "gpio32";
+			drive-strength = <4>;
+			bias-disable;
+		};
+
+		sdclk {
+			pins = "gpio27";
+			function = "sdio";
+			drive-strength = <4>;
+			bias-disable;
+		};
+
+		sdcmd {
+			pins = "gpio28";
+			function = "sdio";
+			drive-strength = <4>;
+			bias-disable;
+		};
+	};
+
+	i2c_0_pins: i2c_0_pinmux {
+		mux {
+			pins = "gpio20", "gpio21";
+			function = "blsp_i2c0";
+			bias-disable;
+		};
+	};
+
+	i2c_1_pins: i2c_1_pinmux {
+		mux {
+			pins = "gpio34", "gpio35";
+			function = "blsp_i2c1";
+			bias-disable;
+		};
+	};
+
+	// cypress cyw20704 connected over usb
+	bluetooth-enable {
+		gpio-hog;
+		gpios = <48 GPIO_ACTIVE_HIGH>;
+		output-high;
+	};
+};
+
+&usb3_ss_phy {
+	status = "ok";
+};
+
+&usb3_hs_phy {
+	status = "ok";
+};
+
+&usb3 {
+	status = "ok";
+};
+
+&usb2_hs_phy {
+	status = "ok";
+};
+
+&usb2 {
+	status = "ok";
+};
+
+&blsp1_uart1 {
+	status = "ok";
+
+	pinctrl-0 = <&serial_0_pins>;
+	pinctrl-names = "default";
+};
+
+&watchdog {
+	status = "ok";
+};
+
+&prng {
+	status = "ok";
+};
+
+&crypto {
+	status = "ok";
+};
+
+&cryptobam {
+	status = "ok";
+};
+
+&blsp_dma {
+	status = "ok";
+};
+
+&blsp1_spi1 {
+	status = "ok";
+
+	pinctrl-0 = <&spi_0_pins>;
+	pinctrl-names = "default";
+	cs-gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>;
+
+	flash@0 {
+		reg = <0>;
+		compatible = "jedec,spi-nor";
+		spi-max-frequency = <24000000>;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition@0 {
+				label = "0:SBL1";
+				reg = <0x0 0x40000>;
+				read-only;
+			};
+
+			partition@40000 {
+				label = "0:MIBIB";
+				reg = <0x40000 0x20000>;
+				read-only;
+			};
+
+			partition@60000 {
+				label = "0:QSEE";
+				reg = <0x60000 0x60000>;
+				read-only;
+			};
+
+			partition@c0000 {
+				label = "0:CDT";
+				reg = <0xc0000 0x10000>;
+				read-only;
+			};
+
+			partition@d0000 {
+				label = "0:DDRPARAMS";
+				reg = <0xd0000 0x10000>;
+				read-only;
+			};
+
+			partition@e0000 {
+				label = "0:ART";
+				reg = <0xe0000 0x10000>;
+				read-only;
+			};
+
+			partition@f0000 {
+				label = "0:APPSBL";
+				reg = <0xf0000 0x100000>;
+				read-only;
+			};
+
+			partition@1f0000 {
+				label = "0:IDENTITY";
+				reg = <0x1f0000 0x10000>;
+				read-only;
+			};
+
+			partition@200000 {
+				label = "0:PK";
+				reg = <0x200000 0x10000>;
+				read-only;
+			};
+
+			partition@210000 {
+				label = "0:APPSBLENV";
+				reg = <0x210000 0x20000>;
+				// r/w allowed because installation needs to modify u-boot env
+			};
+
+			partition@230000 {
+				label = "0:SYSVAR";
+				reg = <0x230000 0x10000>;
+				read-only;
+			};
+		};
+	};
+};
+
+&vqmmc {
+	status = "ok";
+};
+
+&sdhci {
+	status = "ok";
+
+	pinctrl-0 = <&sd_0_pins>;
+	pinctrl-names = "default";
+	vqmmc-supply = <&vqmmc>;
+	non-removable;
+	no-1-8-v;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	emmc@0 {
+		compatible = "mmc-card";
+		reg = <0>;
+	};
+};
+
+&blsp1_i2c3 {
+	status = "ok";
+
+	pinctrl-0 = <&i2c_0_pins>;
+	pinctrl-names = "default";
+};
+
+&blsp1_i2c4 {
+	status = "ok";
+
+	pinctrl-0 = <&i2c_1_pins>;
+	pinctrl-names = "default";
+
+	led-controller@30 {
+		compatible = "ti,lp5562";
+		reg = <0x30>;
+		clock-mode = /bits/ 8 <2>;
+		skip-reset;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		// current limits from stock dt
+
+		led_status_red: led@0 {
+			chan-name = "red:status";
+			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
+			max-cur = /bits/ 8 <0xFF>; /* 25.5 mA */
+			reg = <0>;
+			color = <LED_COLOR_ID_RED>;
+		};
+
+		led_status_green: led@1 {
+			chan-name = "green:status";
+			led-cur = /bits/ 8 <0xB4>; /* 18.0 mA */
+			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
+			reg = <1>;
+			color = <LED_COLOR_ID_GREEN>;
+		};
+
+		led_status_blue: led@2 {
+			chan-name = "blue:status";
+			led-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
+			max-cur = /bits/ 8 <0xC8>; /* 20.0 mA */
+			reg = <2>;
+			color = <LED_COLOR_ID_BLUE>;
+		};
+
+		led_status_white: led@3 {
+			chan-name = "white:status";
+			led-cur = /bits/ 8 <0x96>; /* 15.0 mA */
+			max-cur = /bits/ 8 <0x96>; /* 15.0 mA */
+			reg = <3>;
+			color = <LED_COLOR_ID_WHITE>;
+		};
+	};
+};
+
+&gmac {
+	status = "ok";
+};
+
+&mdio {
+	status = "ok";
+};
+
+&switch {
+	status = "ok";
+};
+
+&swport4 {
+	status = "ok";
+
+	label = "wan"; //arbitrary, no marking
+};
+
+&swport5 {
+	status = "ok";
+
+	label = "lan"; //arbitrary, no marking
+};
+
+&wifi0 {
+	status = "ok";
+
+	qcom,ath10k-calibration-variant = "Eero-Cento";
+	qcom,coexist-support = <1>;
+	qcom,coexist-gpio-pin = <52>;
+};
+
+&wifi1 {
+	status = "ok";
+
+	qcom,ath10k-calibration-variant = "Eero-Cento";
+};
diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk
index adce4fe0f2..14e209fd2a 100644
--- a/target/linux/ipq40xx/image/generic.mk
+++ b/target/linux/ipq40xx/image/generic.mk
@@ -448,6 +448,19 @@  endef
 # Missing DSA Setup
 #TARGET_DEVICES += edgecore_oap100
 
+define Device/eero_cento
+	$(call Device/FitImage)
+	$(call Device/UbiFit)
+	DEVICE_VENDOR := Eero
+	DEVICE_MODEL := Cento (J010001)
+	SOC := qcom-ipq4019
+	DEVICE_PACKAGES := e2fsprogs kmod-fs-f2fs mkf2fs kmod-mmc kmod-fs-ext4 \
+		ipq-wifi-eero_cento kmod-bluetooth bluez-libs bluez-utils
+	IMAGES := sysupgrade.bin
+	IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
+endef
+TARGET_DEVICES += eero_cento
+
 define Device/engenius_eap1300
 	$(call Device/FitImage)
 	DEVICE_VENDOR := EnGenius