Skip to content

Commit

Permalink
ath79: add support for Huawei AP5030DN
Browse files Browse the repository at this point in the history
Huawei AP5030DN is a dual-band, dual-radio 802.11ac Wave 1 3x3 MIMO enterprise
access point with two Gigabit Ethernet ports and PoE support.

Hardware highlights:
- CPU: QCA9550 SoC at 720MHz
- RAM: 256MB DDR2
- Flash: 32MB SPI-NOR
- Wi-Fi 2.4GHz: QCA9550-internal radio
- Wi-Fi 5GHz: QCA9880 PCIe WLAN SoC
- Ethernet 1: 10/100/1000 Mbps Ethernet through Broadcom B50612E PHY
- Ethernet 2: 10/100/1000 Mbps Ethernet through Marvell 88E1510 PHY
- PoE: input through Ethernet 1 port
- Standalone 12V/2A power input
- Serial console externally available through RJ45 port
- External watchdog: SGM706 (1.6s timeout)

Serial console:
  9600n8 (9600 baud, no stop bits, no parity, 8 data bits)

MAC addresses:
  Each device has 32 consecutive MAC addresses allocated by
  the vendor, which don't overlap between devices.
  This was confirmed with multiple devices with consecutive
  serial numbers.
  The MAC address range starts with the address on the label.
  To be able to distinguish between the interfaces,
  the following MAC address scheme is used:
    - eth0 = label MAC
    - eth1 = label MAC + 1
    - radio0 (Wi-Fi 5GHz) = label MAC + 2
    - radio1 (Wi-Fi 2.4GHz) = label MAC + 3

Installation:
0. Connect some sort of RJ45-to-USB adapter to "Console" port of the AP

1. Power up the AP

2. At prompt "Press f or F  to stop Auto-Boot in 3 seconds",
   do what they say.
   Log in with default admin password "[email protected]".

3. Boot the OpenWrt initramfs from TFTP using the hidden script "run ramboot".
   Replace IP address as needed:

   > setenv serverip 192.168.1.10
   > setenv ipaddr 192.168.1.1
   > setenv rambootfile
     openwrt-ath79-generic-huawei_ap5030dn-initramfs-kernel.bin
   > saveenv
   > run ramboot

4. Optional but recommended as the factory firmware cannot
   be downloaded publicly:
   Back up contents of "firmware" partition using the web interface or ssh:

   $ ssh [email protected] cat /dev/mtd11 > huawei_ap5030dn_fw_backup.bin

5. Run sysupgrade using sysupgrade image. OpenWrt
   shall boot from flash afterwards.

Return to factory firmware (using firmware upgrade package downloaded from
non-public Huawei website):
1. Start a TFTP server in the directory where
   the firmware upgrade package is located

2. Boot to u-boot as described above

3. Install firmware upgrade package and format the config partitions:

   > update system FatAP5X30XN_SOMEVERSION.bin
   > format_fs

Return to factory firmware (from previously created backup):
1. Copy over the firmware partition backup to /tmp,
   for example using scp

2. Use sysupgrade with force to restore the backup:
   sysupgrade -F huawei_ap5030dn_fw_backup.bin

3. Boot AP to U-Boot as described above

Quirks and known issues:
- On initial power-up, the Huawei-modified bootloader suspends both ethernet
PHYs (it sets the "Power Down" bit in the MII control register). Unfortunately,
at the time of the initial port, the kernel driver for the B50612E/BCM54612E PHY
behind eth0 doesn't have a resume callback defined which would clear this bit.
This makes the PHY unusable since it remains suspended forever. This is why the
backported kernel patches in this commit are required which add this callback
and for completeness also a suspend callback.
- The stock firmware has a semi dual boot concept where the primary kernel uses
a squashfs as root partition and the secondary kernel uses an initramfs. This
dual boot concept is circumvented on purpose to gain more flash space and since
the stock firmware's flash layout isn't compatible with mtdsplit.
- The external watchdog's timeout of 1.6s is very hard to satisfy during bootup.
This is why the GPIO15 pin connected to the watchdog input is configured
directly in the LZMA loader to output the CPU_CLK/4 signal which keeps the
watchdog happy until the wdt-gpio kernel driver takes over. Because it would
also take too long to read the whole kernel image from flash, the uImage header
only includes the loader which then reads the kernel image from flash after
GPIO15 is configured.

Signed-off-by: Marco von Rosenberg <[email protected]>
  • Loading branch information
CodingMarco committed Mar 31, 2024
1 parent 163c87d commit a0094c5
Show file tree
Hide file tree
Showing 9 changed files with 370 additions and 0 deletions.
3 changes: 3 additions & 0 deletions package/boot/uboot-envtools/files/ath79
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ domywifi,dw33d)
glinet,gl-ar150)
ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x8000" "0x10000"
;;
huawei,ap5030dn)
ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x20000" "0x20000"
;;
netgear,wndr3700|\
netgear,wndr3700-v2|\
netgear,wndrmac-v1)
Expand Down
257 changes: 257 additions & 0 deletions target/linux/ath79/dts/qca9550_huawei_ap5030dn.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT

#include "qca955x.dtsi"

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>

/ {
model = "Huawei AP5030DN";
compatible = "huawei,ap5030dn", "qca,qca9550", "qca,qca9558";

chosen {
bootargs = "console=ttyS0,9600n8";
};

aliases {
led-boot = &led_function_red;
led-failsafe = &led_function_red;
led-running = &led_function_green;
led-upgrade = &led_function_red;
};

leds {
compatible = "gpio-leds";

led_function_green: led-status-red {
function = LED_FUNCTION_STATUS;
color = <LED_COLOR_ID_GREEN>;
gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
};

led_function_red: led-status-green {
function = LED_FUNCTION_STATUS;
color = <LED_COLOR_ID_RED>;
gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
};

};

keys {
compatible = "gpio-keys";

restart {
label = "reset";
linux,code = <KEY_RESTART>;
gpios = <&gpio 21 GPIO_ACTIVE_LOW>;
debounce-interval = <60>;
};
};

watchdog {
pinctrl-names = "default";
pinctrl-0 = <&wdt_gpio15>;

compatible = "linux,wdt-gpio";
gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
hw_algo = "toggle";
hw_margin_ms = <100>;
always-running;
};

virtual_flash {
compatible = "mtd-concat";
devices = <&fwconcat0 &fwconcat1>;

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

partition@0 {
label = "firmware";
reg = <0x0 0x1e00000>;
compatible = "openwrt,uimage", "denx,uimage";
};
};
};
};

&spi {
status = "okay";

flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <25000000>;

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

partition@0 {
label = "u-boot-a";
reg = <0x0 0x80000>;
read-only;
};

// The BootupA/B partitions store the addresses
// of the main and backup kernel in flash (which is the same here).
// During sysupgrade, these addresses are set to the start of the
// "firmware" partition.
partition@80000 {
label = "BootupA";
reg = <0x80000 0x20000>;
};

partition@a0000 {
label = "BootupB";
reg = <0xa0000 0x20000>;
};

partition@c0000 {
label = "u-boot-env";
reg = <0xc0000 0x20000>;
read-only;
};

partition@e0000 {
label = "BoardData";
reg = <0xe0000 0x20000>;
read-only;
};

// In the vendor layout, there are the "SysImageA" (12 MiB)
// and the "ConfigA" (3 MiB) partitions here.
fwconcat0: partition@100000 {
label = "fwconcat0";
reg = <0x100000 0xF00000>;
};

partition@1000000 {
label = "u-boot-b";
reg = <0x1000000 0x80000>;
read-only;
};

partition@1080000 {
label = "ResultA";
reg = <0x1080000 0x20000>;
read-only;
};

partition@10a0000 {
label = "ResultB";
reg = <0x10a0000 0x20000>;
read-only;
};

// In the vendor layout, there are the "SysImageB" (12 MiB)
// and the "ConfigB" (3 MiB) partitions here.
fwconcat1: partition@10c0000 {
label = "fwconcat1";
reg = <0x10c0000 0xF00000>;
};

art: partition@1fc0000 {
label = "art";
reg = <0x1fc0000 0x40000>;
read-only;

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

macaddr_art_2005b: macaddr@2005b {
compatible = "mac-base";
reg = <0x2005b 0x6>;
#nvmem-cell-cells = <1>;
};

cal_art_1000: cal@1000 {
reg = <0x1000 0x440>;
};

cal_art_5000: cal@5000 {
reg = <0x5000 0x844>;
};
};
};
};
};
};

&wmac {
status = "okay";

nvmem-cells = <&macaddr_art_2005b 3>, <&cal_art_1000>;
nvmem-cell-names = "mac-address", "calibration";
};

&pcie0 {
status = "okay";

wifi@0,0 {
compatible = "qcom,ath10k";
reg = <0x0000 0 0 0 0>;
nvmem-cells = <&macaddr_art_2005b 2>, <&cal_art_5000>;
nvmem-cell-names = "mac-address", "calibration";
};
};

&eth0 {
status = "okay";

nvmem-cells = <&macaddr_art_2005b 0>;
nvmem-cell-names = "mac-address";

pll-data = <0xa6000000 0xa0000101 0xa0001313>;
phy-handle = <&phy0>;

gmac-config {
device = <&gmac>;
rxdv-delay = <3>;
rxd-delay = <3>;
txen-delay = <0>;
txd-delay = <0>;
};
};

&eth1 {
status = "okay";

nvmem-cells = <&macaddr_art_2005b 1>;
nvmem-cell-names = "mac-address";

pll-data = <0x03000101 0x00000101 0x00001313>;
phy-handle = <&phy1>;
};

&mdio0 {
status = "okay";

phy0: ethernet-phy@18 {
reg = <0x18>;
};
};

&mdio1 {
status = "okay";

phy1: ethernet-phy@1 {
reg = <1>;
};
};

&pinmux {
wdt_gpio15: pinmux_wdt_gpio15 {
pinctrl-single,bits = <0xc 0x0 0xFF000000>;
};
};

&wdt {
status = "disabled";
};
4 changes: 4 additions & 0 deletions target/linux/ath79/generic/base-files/etc/board.d/02_network
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ ath79_setup_interfaces()
engenius,enstationac-v1|\
engenius,ews511ap|\
engenius,ews660ap|\
huawei,ap5030dn|\
ocedo,ursus|\
ruckus,zf7363|\
ruckus,zf7372|\
Expand Down Expand Up @@ -747,6 +748,9 @@ ath79_setup_macs()
hak5,packet-squirrel)
label_mac=$(mtd_get_mac_binary u-boot 0x1fc00)
;;
huawei,ap5030dn)
label_mac=$(mtd_get_mac_binary art 0x2005b)
;;
iodata,etg3-r)
lan_mac=$(mtd_get_mac_ascii u-boot-env ethaddr)
wan_mac=$(macaddr_add "$lan_mac" -1)
Expand Down
8 changes: 8 additions & 0 deletions target/linux/ath79/generic/base-files/lib/upgrade/platform.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ platform_do_upgrade() {
ROOTFS_FILE="root.squashfs"
platform_do_upgrade_failsafe_datachk "$1"
;;
huawei,ap5030dn)
# Store beginning address of the "firmware" partition
# as KernelA address and KernelB address, each to BootupA & BootupB
# This is the address from which the bootloader will try to load the kernel.
echo -n -e "\x9e\x10\x00\x00\x9e\x10\x00\x00" | dd of=$(find_mtd_part BootupA) bs=1 seek=$((0x254)) conv=notrunc
echo -n -e "\x9e\x10\x00\x00\x9e\x10\x00\x00" | dd of=$(find_mtd_part BootupB) bs=1 seek=$((0x254)) conv=notrunc
default_do_upgrade "$1"
;;
jjplus,ja76pf2)
platform_do_upgrade_redboot_fis "$1" linux
;;
Expand Down
16 changes: 16 additions & 0 deletions target/linux/ath79/image/generic.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1813,6 +1813,22 @@ define Device/hiwifi_hc6361
endef
TARGET_DEVICES += hiwifi_hc6361

define Device/huawei_ap5030dn
SOC := qca9550
DEVICE_VENDOR := Huawei
DEVICE_MODEL := AP5030DN
DEVICE_PACKAGES := ath10k-firmware-qca988x-ct kmod-ath10k-ct
LOADER_TYPE := bin
LOADER_FLASH_OFFS := 0x111DC0
KERNEL_SIZE := 15360k
IMAGE_SIZE := 30720k
COMPILE := loader-$(1).bin
COMPILE/loader-$(1).bin := loader-okli-compile | pad-to 64k | uImage none
KERNEL := kernel-bin | append-dtb | lzma | uImage lzma -M 0x4f4b4c49 | loader-okli $(1) 8128
KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | loader-kernel | uImage none
endef
TARGET_DEVICES += huawei_ap5030dn

define Device/iodata_etg3-r
SOC := ar9342
DEVICE_VENDOR := I-O DATA
Expand Down
2 changes: 2 additions & 0 deletions target/linux/ath79/image/lzma-loader/src/ar71xx_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@

#define AR934X_GPIO_OUT_GPIO 0x00

#define QCA955X_GPIO_OUTSEL_CLK_OBS5 0x54

/*
* MII_CTRL block
*/
Expand Down
26 changes: 26 additions & 0 deletions target/linux/ath79/image/lzma-loader/src/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,34 @@ static inline void mr18_init(void)
static inline void mr18_init(void) { }
#endif

#ifdef CONFIG_BOARD_HUAWEI_AP5030DN
static inline void ap5030dn_init(void)
{
const unsigned int ap5030dn_watchdog_gpio = 15;
unsigned int gpiobase, reg;

gpiobase = KSEG1ADDR(AR71XX_GPIO_BASE);

printf("Huawei AP5030DN\n");

reg = READREG(gpiobase + AR71XX_GPIO_REG_OE);
WRITEREG(gpiobase + AR71XX_GPIO_REG_OE,
reg & ~(1 << ap5030dn_watchdog_gpio));

/* Set GPIO15 MUX to output CLK_OBS5 (= CPU_CLK/4)
* to keep the watchdog happy until wdt-gpio takes over
*/
reg = READREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3);
WRITEREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3,
reg | (QCA955X_GPIO_OUTSEL_CLK_OBS5 << 24));
}
#else
static inline void ap5030dn_init(void) { }
#endif

void board_init(void)
{
tlwr1043nd_init();
mr18_init();
ap5030dn_init();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
From 380b50ae3a04222334a3779b3787eba844b1177f Mon Sep 17 00:00:00 2001
From: Marco von Rosenberg <[email protected]>
Date: Thu, 16 Nov 2023 20:32:31 +0100
Subject: net: phy: broadcom: Wire suspend/resume for BCM54612E

The BCM54612E ethernet PHY supports IDDQ-SR.
Therefore wire-up the suspend and resume callbacks
to point to bcm54xx_suspend() and bcm54xx_resume().

Signed-off-by: Marco von Rosenberg <[email protected]>
Acked-by: Florian Fainelli <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
---
drivers/net/phy/broadcom.c | 2 ++
1 file changed, 2 insertions(+)

--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -941,6 +941,8 @@ static struct phy_driver broadcom_driver
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
+ .suspend = bcm54xx_suspend,
+ .resume = bcm54xx_resume,
}, {
.phy_id = PHY_ID_BCM54616S,
.phy_id_mask = 0xfffffff0,
Loading

0 comments on commit a0094c5

Please sign in to comment.