ath79: add Cisco Meraki MR18
authorChristian Lamparter <chunkeey@gmail.com>
Fri, 30 Jul 2021 15:59:25 +0000 (17:59 +0200)
committerChristian Lamparter <chunkeey@gmail.com>
Thu, 19 May 2022 14:39:11 +0000 (16:39 +0200)
Specifications:

SOC:    Atheros/Qualcomm QCA9557-AT4A @ 720MHz
RAM:    2x Winbond W9751G6KB-25 (128 MiB)
FLASH:  Hynix H27U1G8F2BTR-BC TSOP48 ONFI NAND (128 MiB)
WIFI1:  Atheros AR9550 5.0GHz (SoC)
WIFI2:  Atheros AR9582-AR1A 2.4GHz
WIFI2:  Atheros AR9582-AR1A 2.4GHz + 5GHz
PHYETH: Atheros AR8035-A, 802.3af PoE capable Atheros (1x Gigabit LAN)
LED:    1x Power-LED, 1 x RGB Tricolor-LED
INPUT:  One Reset Button
UART:   JP1 on PCB (Labeled UART), 3.3v-Level, 115200n8
        (VCC, RX, TX, GND - VCC is closest to the boot set jumper
 under the console pins.)

Flashing instructions:

Depending on the installed firmware, there fastly different methods
to flash a MR18. These have been documented on:
<https://openwrt.org/toh/meraki/mr18>

Note: upgrades from AR71XX are possible, but require the force
sysupgrade option.

The LEDs has changed since AR71XX. The white LED is now used during
the boot and when upgrading instead of the green tricolor LED.

Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
package/system/gpio-cdev/nu801/Makefile
target/linux/ath79/dts/qca9557_meraki_mr18.dts [new file with mode: 0644]
target/linux/ath79/image/lzma-loader/src/ar71xx_regs.h
target/linux/ath79/image/lzma-loader/src/board.c
target/linux/ath79/image/nand.mk
target/linux/ath79/nand/base-files/etc/board.d/02_network
target/linux/ath79/nand/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom

index 1ac5c6d51002c9ba702b085e6aadaad0c828c8eb..7629d5c574c054dfed3062d3f4f44709ed4e8ed6 100644 (file)
@@ -20,7 +20,7 @@ define Package/nu801
   SECTION:=utils
   CATEGORY:=Utilities
   SUBMENU:=Userspace GPIO Drivers
-  DEPENDS:=@TARGET_x86
+  DEPENDS:=@TARGET_x86||@TARGET_ath79_nand
   KCONFIG:=CONFIG_GPIO_CDEV=y
   TITLE:=NU801 LED Driver
 endef
diff --git a/target/linux/ath79/dts/qca9557_meraki_mr18.dts b/target/linux/ath79/dts/qca9557_meraki_mr18.dts
new file mode 100644 (file)
index 0000000..29520bb
--- /dev/null
@@ -0,0 +1,184 @@
+// 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>
+
+/ {
+       compatible = "meraki,mr18", "qca,qca9558";
+       model = "Meraki MR18";
+
+       aliases {
+               label-mac-device = &eth0;
+               led-boot = &white;
+               led-failsafe = &orange;
+               led-running = &green;
+               led-upgrade = &white;
+       };
+
+       leds {
+               compatible = "gpio-leds";
+
+               white: white {
+                       label = "mr18:white:power";
+                       gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+               };
+
+               orange: orange {
+                       label = "mr18:orange:power";
+                       gpios = <&gpio 21 GPIO_ACTIVE_HIGH>;
+                       panic-indicator;
+               };
+       };
+
+       uleds {
+               compatible = "virtual-leds";
+
+#if 0
+               /* not supported by uleds driver */
+               rgbled-0 {
+                       function = LED_FUNCTION_POWER;
+                        color = <LED_COLOR_ID_RGB>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       led@0 {
+                               reg = <0>;
+                               color = <LED_COLOR_ID_RED>;
+                       };
+
+                       green: led@1 {
+                               reg = <1>;
+                               color = <LED_COLOR_ID_GREEN>;
+                       };
+
+                        led@2 {
+                               reg = <2>;
+                                color = <LED_COLOR_ID_BLUE>;
+                       };
+               };
+#else
+               green: green {
+                       label = "green:tricolor";
+                       color = <LED_COLOR_ID_GREEN>;
+               };
+
+               blue {
+                       color = <LED_COLOR_ID_BLUE>;
+                       label = "mr18:blue:tricolor";
+               };
+
+               red {
+                       label = "mr18:red:tricolor";
+                       color = <LED_COLOR_ID_RED>;
+               };
+#endif
+       };
+
+       button {
+               compatible = "gpio-keys";
+
+               reset {
+                       label = "Reset";
+                       linux,code = <KEY_RESTART>;
+                       gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <60>;
+               };
+
+       };
+};
+
+&nand {
+       status = "okay";
+
+       nand-ecc-mode = "soft";
+       nand-ecc-algo = "bch";
+       nand-ecc-strength = <4>;
+       nand-ecc-step-size = <512>;
+       nand-is-boot-medium;
+
+       partitions {
+               compatible = "fixed-partitions";
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               partition@0 {
+                       label = "nandloader";
+                       reg = <0x0 0x80000>;
+                       read-only;
+               };
+
+               partition@80000 {
+                       label = "kernel";
+                       reg = <0x80000 0x800000>;
+               };
+
+               partition@880000 {
+                       label = "recovery";
+                       reg = <0x880000 0x800000>;
+               };
+
+               partition@1080000 {
+                       label = "ubi";
+                       reg = <0x1080000 0x6f00000>;
+               };
+
+               partition@7fe0000 {
+                       label = "odm-caldata";
+                       reg = <0x7fe0000 0x20000>;
+                       read-only;
+               };
+       };
+};
+
+&pcie0 {
+       status = "okay";
+
+       wifi@0,0 {
+               compatible = "pci168c,0033";
+               reg = <0x0000 0 0 0 0>;
+               qca,no-eeprom;
+       };
+};
+
+&pcie1 {
+       status = "okay";
+
+       wifi@0,0 {
+               compatible = "pci168c,0033";
+               reg = <0x0000 0 0 0 0>;
+               qca,no-eeprom;
+       };
+};
+
+&uart {
+       status = "okay";
+};
+
+&mdio0 {
+       status = "okay";
+
+       phy: ethernet-phy@3 {
+               reg = <3>;
+       };
+};
+
+&eth0 {
+       status = "okay";
+       pll-data = <0xa6000000 0xa0000101 0x80001313>;
+       phy-handle = <&phy>;
+
+       gmac-config {
+               device = <&gmac>;
+               rgmii-enabled = <1>;
+               rxd-delay = <3>;
+               rxdv-delay = <3>;
+       };
+};
+
+&wmac {
+       status = "okay";
+       qca,no-eeprom;
+};
index 19a4785bb44aed601d69ef92b74f9517ac3b675a..245042fdab74af1ab442f11614668a668872aad2 100644 (file)
 #define AR9300_OTP_STATUS_SM_BUSY      0x1
 #define AR9300_OTP_READ_DATA   0x15f1c
 
+#define QCA955X_OTP_BASE        (AR71XX_APB_BASE + 0x00130000)
+#define QCA955X_OTP_REG_MEM_0  0x0000
+#define QCA955X_OTP_REG_INTF2  0x1008
+#define QCA955X_OTP_REG_STATUS0        0x1018
+#define QCA955X_OTP_STATUS0_EFUSE_VALID        BIT(2)
+
+#define QCA955X_OTP_REG_STATUS1                0x101c
+#define QCA955X_OTP_REG_LDO_CTRL       0x1024
+#define QCA955X_OTP_REG_LDO_STATUS     0x102c
+#define QCA955X_OTP_LDO_STATUS_POWER_ON                BIT(0)
+
 /*
  * DDR_CTRL block
  */
 
 #define QCA955X_RESET_REG_BOOTSTRAP            0xb0
 #define QCA955X_RESET_REG_EXT_INT_STATUS       0xac
+#define QCA955X_RESET_REG_RESET_MODULE         0x1c
 
 #define MISC_INT_ETHSW                 BIT(12)
 #define MISC_INT_TIMER4                        BIT(10)
 #define AR934X_RESET_MBOX              BIT(1)
 #define AR934X_RESET_I2S               BIT(0)
 
+#define QCA955X_RESET_SGMII_ANALOG     BIT(12)
+#define QCA955X_RESET_SGMII            BIT(8)
+
 #define AR933X_BOOTSTRAP_MDIO_GPIO_EN  BIT(18)
 #define AR933X_BOOTSTRAP_EEPBUSY       BIT(4)
 #define AR933X_BOOTSTRAP_REF_CLK_40    BIT(0)
 #define QCA955X_ETH_CFG_RGMII_GMAC0    BIT(0)
 #define QCA955X_ETH_CFG_SGMII_GMAC0    BIT(6)
 
+#define QCA955X_GMAC_REG_SGMII_SERDES  0x0018
+
 #endif /* __ASM_MACH_AR71XX_REGS_H */
index 2f4dd6b1f6456a9b4a1036c0fc96907dee72e0ca..2360a5e3ed3bfd8f124b2428b93b621663eb58b7 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <stddef.h>
 #include "config.h"
+#include "printf.h"
 #include "ar71xx_regs.h"
 
 #define READREG(r)     *(volatile unsigned int *)(r)
@@ -50,7 +51,135 @@ static void tlwr1043nd_init(void)
 static inline void tlwr1043nd_init(void) {}
 #endif
 
+#ifdef CONFIG_BOARD_MERAKI_MR18
+
+static int mr18_extract_sgmii_res_cal(void)
+{
+       unsigned int base;
+       unsigned int reversed_sgmii_value;
+
+       unsigned int otp_value, otp_per_val, rbias_per, read_data;
+       unsigned int rbias_pos_or_neg;
+       unsigned int sgmii_res_cal_value;
+       int res_cal_val;
+
+       base = KSEG1ADDR(QCA955X_OTP_BASE);
+
+       WRITEREG(base + QCA955X_OTP_REG_INTF2, 0x7d);
+       WRITEREG(base + QCA955X_OTP_REG_LDO_CTRL, 0x00);
+
+       while (READREG(base + QCA955X_OTP_REG_LDO_STATUS) &
+               QCA955X_OTP_LDO_STATUS_POWER_ON);
+
+       READREG(base + QCA955X_OTP_REG_MEM_0 + 4);
+
+       while (!(READREG(base + QCA955X_OTP_REG_STATUS0) &
+               QCA955X_OTP_STATUS0_EFUSE_VALID));
+
+       read_data = READREG(base + QCA955X_OTP_REG_STATUS1);
+
+       if (!(read_data & 0x1fff))
+               return 0;
+
+       if (read_data & 0x00001000)
+               otp_value = (read_data & 0xfc0) >> 6;
+       else
+               otp_value = read_data & 0x3f;
+
+       if (otp_value > 31) {
+               otp_per_val = 63 - otp_value;
+               rbias_pos_or_neg = 1;
+       } else {
+               otp_per_val = otp_value;
+               rbias_pos_or_neg = 0;
+       }
+
+       rbias_per = otp_per_val * 15;
+
+       if (rbias_pos_or_neg == 1)
+               res_cal_val = (rbias_per + 34) / 21;
+       else if (rbias_per > 34)
+               res_cal_val = -((rbias_per - 34) / 21);
+       else
+               res_cal_val = (34 - rbias_per) / 21;
+
+       sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
+
+       reversed_sgmii_value  = (sgmii_res_cal_value & 8) >> 3;
+       reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
+       reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
+       reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
+       printf("SGMII cal value = 0x%x\n", reversed_sgmii_value);
+       return reversed_sgmii_value;
+}
+
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION           BIT(23)
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK      0xf
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT     23
+#define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS                BIT(15)
+#define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT       BIT(2)
+#define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK                BIT(1)
+#define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL            BIT(0)
+#define QCA955X_PLL_CLK_CTRL_REG                       0x08
+#define QCA955X_PLL_ETH_XMII_CONTROL_REG               0x28
+#define QCA955X_PLL_ETH_SGMII_CONTROL_REG              0x48
+#define QCA955X_PLL_ETH_SGMII_SERDES_REG               0x4c
+
+static void qca955x_device_reset_clear(unsigned int mask)
+{
+       unsigned int t, reg;
+
+       reg = KSEG1ADDR(AR71XX_RESET_BASE +
+                       QCA955X_RESET_REG_RESET_MODULE);
+
+       t = READREG(reg);
+       WRITEREG(reg, t & ~mask);
+}
+
+static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
+{
+       unsigned int ethbase, pllbase, t;
+
+       ethbase = KSEG1ADDR(QCA955X_GMAC_BASE);
+       pllbase = KSEG1ADDR(AR71XX_PLL_BASE);
+
+       /* To Check the locking of the SGMII PLL */
+       t = READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
+       t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
+              QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
+       t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
+            QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
+       WRITEREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES, t);
+
+       WRITEREG(pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG,
+                QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
+                QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
+                QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL);
+
+       qca955x_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
+       qca955x_device_reset_clear(QCA955X_RESET_SGMII);
+
+       while (!(READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
+               QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS));
+}
+
+static inline void mr18_init(void)
+{
+       int res;
+
+       printf("Meraki MR18\n");
+
+       res = mr18_extract_sgmii_res_cal();
+       if (res >= 0)
+               mr18_setup_qca955x_eth_serdes_cal(res);
+
+}
+#else
+static inline void mr18_init(void) { }
+#endif
+
 void board_init(void)
 {
        tlwr1043nd_init();
+       mr18_init();
 }
index b28c3e38567d753f3820f13c5d3a2e6194b7492d..1c254f9ae24663886a99af7e0b0bdadec94b4224 100644 (file)
@@ -11,6 +11,14 @@ define Build/dongwon-header
        mv $@.tmp $@
 endef
 
+define Build/MerakiNAND
+        -$(STAGING_DIR_HOST)/bin/mkmerakifw \
+                -B $(1) -s \
+                -i $@ \
+                -o $@.new
+        @mv $@.new $@
+endef
+
 define Build/meraki-old-nand
        -$(STAGING_DIR_HOST)/bin/mkmerakifw-old \
                -B $(1) -s \
@@ -219,6 +227,21 @@ define Device/glinet_gl-xe300
 endef
 TARGET_DEVICES += glinet_gl-xe300
 
+define Device/meraki_mr18
+  SOC := qca9557
+  DEVICE_VENDOR := Meraki
+  DEVICE_MODEL := MR18
+  DEVICE_PACKAGES := kmod-spi-gpio nu801
+  KERNEL_SIZE := 8m
+  BLOCKSIZE := 128k
+  PAGESIZE := 2048
+  LOADER_TYPE := bin
+  KERNEL := kernel-bin | append-dtb | lzma | loader-kernel | MerakiNAND MR18
+  KERNEL_INITRAMFS := $$(KERNEL)
+  IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
+endef
+TARGET_DEVICES += meraki_mr18
+
 define Device/meraki_z1
   SOC = ar9344
   DEVICE_VENDOR := Meraki
index 88c8ea07e5f4c81a4f53fbf5e0c69e4d60f7d16f..5be8efd09c8aa58b300502d17c3b57017cdf2c25 100644 (file)
@@ -50,6 +50,9 @@ ath79_setup_interfaces()
                ucidef_add_switch "switch0" \
                        "0@eth0" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1"
                ;;
+       meraki,mr18)
+               ucidef_set_interface_lan "eth0"
+               ;;
        meraki,z1)
                ucidef_add_switch "switch0" \
                        "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan"
@@ -88,6 +91,9 @@ ath79_setup_macs()
        netgear,wndr4500-v3)
                wan_mac=$(mtd_get_mac_binary caldata 0x6)
                ;;
+       meraki,mr18)
+               lan_mak=$(mtd_get_mac_binary_ubi board-config 102)
+               ;;
        meraki,z1)
                lan_mac=$(mtd_get_mac_binary_ubi board-config 102)
                wan_mac=$(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) 1)
index caa0fc64ecb29d025dd3ad08489bad2850d3113a..cdd4a83298f453b3427a7e67b8ea8272e227c526 100644 (file)
@@ -20,6 +20,16 @@ case "$FIRMWARE" in
        netgear,wndr4500-v3)
                caldata_extract "caldata" 0x1000 0x440
                ;;
+       meraki,mr18)
+               . /lib/upgrade/nand.sh
+
+               if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+                       caldata_extract_ubi "caldata" 0x1000 0x440
+               else
+                       caldata_extract "odm-caldata" 0x1000 0x440
+               fi
+               ath9k_patch_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) +1)
+               ;;
        meraki,z1)
                . /lib/upgrade/nand.sh
 
@@ -45,6 +55,16 @@ case "$FIRMWARE" in
        netgear,wndr4500-v3)
                caldata_extract "caldata" 0x5000 0x440
                ;;
+       meraki,mr18)
+               . /lib/upgrade/nand.sh
+
+               if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+                       caldata_extract_ubi "caldata" 0x5000 0x440
+               else
+                       caldata_extract "odm-caldata" 0x5000 0x440
+               fi
+               ath9k_patch_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) 2)
+               ;;
        meraki,z1)
                . /lib/upgrade/nand.sh
 
@@ -60,4 +80,21 @@ case "$FIRMWARE" in
                ;;
        esac
        ;;
+"ath9k-eeprom-pci-0000:01:00.0.bin")
+       case $board in
+       meraki,mr18)
+               . /lib/upgrade/nand.sh
+
+               if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+                       caldata_extract_ubi "caldata" 0x9000 0x440
+               else
+                       caldata_extract "odm-caldata" 0x9000 0x440
+               fi
+               ath9k_patch_mac $(macaddr_add $(mtd_get_mac_binary_ubi board-config 102) 3)
+               ;;
+       *)
+               caldata_die "board $board is not supported yet"
+               ;;
+       esac
+       ;;
 esac