SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=Userspace GPIO Drivers
- DEPENDS:=@(TARGET_x86||TARGET_bcm53xx)
+ DEPENDS:=@(TARGET_ath79_nand||TARGET_bcm53xx||TARGET_x86)
KCONFIG:=CONFIG_GPIO_CDEV=y
TITLE:=NU801 LED Driver
endef
--- /dev/null
+// 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 = ð0;
+ led-boot = &white;
+ led-failsafe = &orange;
+ led-running = &green;
+ led-upgrade = &white;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ white: white {
+ label = "white:power";
+ gpios = <&gpio 18 GPIO_ACTIVE_LOW>;
+ };
+
+ orange: orange {
+ label = "orange:power";
+ gpios = <&gpio 21 GPIO_ACTIVE_HIGH>;
+ panic-indicator;
+ };
+ };
+
+ uleds {
+ compatible = "virtual-leds";
+#if 0
+ /*
+ * RGB leds are not supported by uleds driver.
+ * but this is what the definitions for a as
+ * of yet unwritten leds_nu801 would look like.
+ */
+
+ 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
+ red {
+ label = "red:tricolor";
+ color = <LED_COLOR_ID_RED>;
+ };
+
+ green: green {
+ label = "green:tricolor";
+ color = <LED_COLOR_ID_GREEN>;
+ };
+
+ blue {
+ label = "blue:tricolor";
+ color = <LED_COLOR_ID_BLUE>;
+ };
+#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 {
+ /*
+ * This is not always present. And if
+ * it is, then Meraki (or contractor)
+ * used a different ecc method than
+ * the one we need for the UBI partition.
+ * Reading this causes various reading
+ * errors.
+ *
+ * As a result: Please don't convert
+ * this to nvmem-cells. Instead there's
+ * a ubi-volume "caldata" that has the
+ * necessary data.
+ */
+
+ 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>;
+ };
+};
+
+ð0 {
+ 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;
+};
#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 */
#include <stddef.h>
#include "config.h"
+#include "printf.h"
#include "ar71xx_regs.h"
#define READREG(r) *(volatile unsigned int *)(r)
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();
}
mv $@.tmp $@
endef
+define Build/meraki-header
+ -$(STAGING_DIR_HOST)/bin/mkmerakifw \
+ -B $(1) -s \
+ -i $@ \
+ -o $@.new
+ @mv $@.new $@
+endef
+
# attention: only zlib compression is allowed for the boot fs
define Build/zyxel-buildkerneljffs
mkdir -p $@.tmp/boot
endef
TARGET_DEVICES += linksys_ea4500-v3
+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 | meraki-header MR18
+# Initramfs-build fails due to size issues
+# KERNEL_INITRAMFS := $$(KERNEL)
+ KERNEL_INITRAMFS :=
+ IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
+endef
+TARGET_DEVICES += meraki_mr18
+
# fake rootfs is mandatory, pad-offset 64 equals (1 * uimage_header)
define Device/netgear_ath79_nand
DEVICE_VENDOR := NETGEAR
case "$board" in
aerohive,hiveap-121|\
- glinet,gl-e750)
+ glinet,gl-e750|\
+ meraki,mr18)
ucidef_set_interface_lan "eth0"
;;
domywifi,dw33d)
wan_mac=$(mtd_get_mac_binary art 0x0)
lan_mac=$(macaddr_add "$wan_mac" 1)
;;
+ meraki,mr18)
+ lan_mac=$(mtd_get_mac_binary_ubi board-config 102)
+ ;;
netgear,wndr3700-v4|\
netgear,wndr4300|\
netgear,wndr4300sw|\
8dev,rambutan)
caldata_extract "caldata" 0x1000 0x800
;;
+ 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)
+ ;;
+ *)
+ caldata_die "board $board is not supported yet"
+ ;;
+ esac
+ ;;
+"ath9k-eeprom-pci-0000:00:00.0.bin")
+ case $board in
+ 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)
+ ;;
+ *)
+ caldata_die "board $board is not supported yet"
+ ;;
+ 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"
;;