gl-infra-builder-FUjr/patches-21.02.2/0002-ath79-add-support-for-gl-mifi.patch
2022-03-14 11:05:18 +08:00

1888 lines
49 KiB
Diff

From 120ee81bb20b8aa01998916c87f763bdcc9e327f Mon Sep 17 00:00:00 2001
From: "GL.iNet-Hongjian.Zhang" <hongjian.zhang@gl-inet.com>
Date: Sat, 18 Sep 2021 10:39:16 +0800
Subject: [PATCH 2/8] ath79: add support for gl mifi
---
target/linux/ath79/dts/ar9330_glinet.dtsi | 218 +++
.../linux/ath79/dts/ar9331_glinet_gl-mifi.dts | 83 +-
.../net/ethernet/atheros/ag71xx/Makefile | 1 +
.../ethernet/atheros/ag71xx/ag71xx_ar7240.c | 1349 +++++++++++++++++
.../net/ethernet/atheros/ag71xx/ag71xx_main.c | 4 +
.../generic/base-files/etc/board.d/01_leds | 1 -
.../etc/uci-defaults/04_led_migration | 3 -
target/linux/ath79/image/generic.mk | 10 -
target/linux/ath79/image/nand.mk | 10 +
.../ath79/nand/base-files/etc/board.d/01_leds | 6 +
.../nand/base-files/etc/board.d/02_network | 3 +
11 files changed, 1647 insertions(+), 41 deletions(-)
create mode 100644 target/linux/ath79/dts/ar9330_glinet.dtsi
create mode 100755 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
diff --git a/target/linux/ath79/dts/ar9330_glinet.dtsi b/target/linux/ath79/dts/ar9330_glinet.dtsi
new file mode 100644
index 0000000000..77387bf37c
--- /dev/null
+++ b/target/linux/ath79/dts/ar9330_glinet.dtsi
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+#include <dt-bindings/clock/ath79-clk.h>
+#include "ath79.dtsi"
+
+/ {
+ compatible = "qca,ar9330";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "mips,mips24Kc";
+ clocks = <&pll ATH79_CLK_CPU>;
+ reg = <0>;
+ };
+ };
+
+ chosen {
+ bootargs = "console=ttyATH0,115200";
+ };
+
+ ahb {
+ apb {
+ ddr_ctrl: memory-controller@18000000 {
+ compatible = "qca,ar7240-ddr-controller";
+ reg = <0x18000000 0x100>;
+
+ #qca,ddr-wb-channel-cells = <1>;
+ };
+
+ uart: uart@18020000 {
+ compatible = "qca,ar9330-uart";
+ reg = <0x18020000 0x14>;
+
+ interrupts = <3>;
+
+ clocks = <&pll ATH79_CLK_REF>;
+ clock-names = "uart";
+
+ status = "disabled";
+ };
+
+ gpio: gpio@18040000 {
+ compatible = "qca,ar7100-gpio";
+ reg = <0x18040000 0x34>;
+ interrupts = <2>;
+
+ ngpios = <30>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ pinmux: pinmux@18040028 {
+ compatible = "pinctrl-single";
+ reg = <0x18040028 0x8>;
+
+ pinctrl-single,bit-per-mux;
+ pinctrl-single,register-width = <32>;
+ pinctrl-single,function-mask = <0x1>;
+ #pinctrl-cells = <2>;
+
+ jtag_disable_pins: pinmux_jtag_disable_pins {
+ pinctrl-single,bits = <0x0 0x1 0x1>;
+ };
+
+ switch_led_disable_pins: pinmux_switch_led_disable_pins {
+ pinctrl-single,bits = <0x0 0x0 0xf8>;
+ };
+ };
+
+ pll: pll-controller@18050000 {
+ compatible = "qca,ar9330-pll";
+ reg = <0x18050000 0x100>;
+
+ #clock-cells = <1>;
+ };
+
+ wdt: wdt@18060008 {
+ compatible = "qca,ar7130-wdt";
+ reg = <0x18060008 0x8>;
+
+ interrupts = <4>;
+
+ clocks = <&pll ATH79_CLK_AHB>;
+ clock-names = "wdt";
+ };
+
+ rst: reset-controller@1806001c {
+ compatible = "qca,ar7100-reset";
+ reg = <0x1806001c 0x4>;
+
+ #reset-cells = <1>;
+ };
+ };
+
+ usb: usb@1b000000 {
+ compatible = "generic-ehci";
+ reg = <0x1b000000 0x200>;
+
+ interrupts = <3>;
+ resets = <&rst 5>;
+ reset-names = "usb-host";
+ dr_mode = "host";
+
+ has-transaction-translator;
+ caps-offset = <0x100>;
+
+ phy-names = "usb-phy";
+ phys = <&usb_phy>;
+
+ status = "disabled";
+ };
+
+ spi: spi@1f000000 {
+ compatible = "qca,ar7100-spi";
+ reg = <0x1f000000 0x10>;
+
+ clocks = <&pll ATH79_CLK_AHB>;
+ clock-names = "ahb";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ status = "disabled";
+ };
+
+ gmac: gmac@18070000 {
+ compatible = "qca,ar9330-gmac";
+ reg = <0x18070000 0x4>;
+ };
+
+ wmac: wmac@18100000 {
+ compatible = "qca,ar9330-wmac";
+ reg = <0x18100000 0x20000>;
+
+ interrupts = <2>;
+
+ status = "disabled";
+ };
+ };
+
+ usb_phy: usb-phy {
+ compatible = "qca,ar7200-usb-phy";
+
+ reset-names = "usb-phy", "usb-suspend-override";
+ resets = <&rst 4>, <&rst 3>;
+
+ #phy-cells = <0>;
+
+ status = "disabled";
+ };
+};
+
+&cpuintc {
+ qca,ddr-wb-channel-interrupts = <2>, <3>;
+ qca,ddr-wb-channels = <&ddr_ctrl 3>, <&ddr_ctrl 2>;
+};
+
+&eth0 {
+ compatible = "qca,ar9330-eth", "syscon";
+
+ pll-data = <0x00110000 0x00001099 0x00991099>;
+
+ resets = <&rst 9>;
+ reset-names = "mac";
+ phy-mode = "mii";
+ phy-handle = <&swphy4>;
+};
+
+&mdio1 {
+ status = "okay";
+ compatible = "qca,ar9330-mdio";
+
+ resets = <&rst 23>;
+ reset-names = "mdio";
+ builtin-switch;
+
+ builtin_switch: switch0@1f {
+ compatible = "qca,ar8216-builtin";
+ reg = <0x1f>;
+ resets = <&rst 8>;
+ reset-names = "switch";
+
+ mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ swphy4: ethernet-phy@4 {
+ reg = <4>;
+ phy-mode = "mii";
+ };
+ };
+ };
+};
+
+&eth1 {
+ compatible = "qca,ar9330-eth", "syscon";
+
+ pll-data = <0x00110000 0x00001099 0x00991099>;
+ phy-mode = "gmii";
+
+ resets = <&rst 13>;
+ reset-names = "mac";
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+};
diff --git a/target/linux/ath79/dts/ar9331_glinet_gl-mifi.dts b/target/linux/ath79/dts/ar9331_glinet_gl-mifi.dts
index 7d6e7bd4af..863275b712 100644
--- a/target/linux/ath79/dts/ar9331_glinet_gl-mifi.dts
+++ b/target/linux/ath79/dts/ar9331_glinet_gl-mifi.dts
@@ -1,39 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
-#include "ar9331.dtsi"
+#include "ar9330_glinet.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
- compatible = "glinet,gl-mifi", "qca,ar9331";
+ compatible = "glinet,gl-mifi", "qca,ar9330";
model = "GL.iNet GL-MiFi";
- aliases {
- label-mac-device = &eth0;
- };
-
- leds {
+ aliases {
+ serial0 = &uart;
+ };
+
+ gl_hw {
+ compatible = "gl-hw-info";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ model = "mifi";
+ wan = "eth0";
+ lan = "eth1";
+ build-in-modem = "1-1.2";
+ reset-button = "gpio-11";
+ usb-port = "1-1.3";
+ factory_data {
+ device_mac = "art";
+ device_ddns = "art", "0x10";
+ device_sn_bak = "art", "0x20";
+ device_sn = "art", "0x30";
+ country_code = "art", "0x88";
+ };
+ };
+
+ leds {
compatible = "gpio-leds";
wlan {
- label = "green:wlan";
+ label = "gl-mifi:green:wlan";
gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "phy0tpt";
};
lan {
- label = "green:lan";
+ label = "gl-mifi:green:lan";
gpios = <&gpio 16 GPIO_ACTIVE_HIGH>;
};
wan {
- label = "green:wan";
+ label = "gl-mifi:green:wan";
gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
};
- 3g4g {
- label = "green:3g4g";
+ net {
+ label = "gl-mifi:green:net";
gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
};
};
@@ -49,14 +69,6 @@
};
};
- reg_usb_vbus: reg_usb_vbus {
- compatible = "regulator-fixed";
- regulator-name = "usb_vbus";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- gpio = <&gpio 6 GPIO_ACTIVE_LOW>;
- };
-
gpio-export {
compatible = "gpio-export";
@@ -65,14 +77,27 @@
gpio-export,output = <0>;
gpios = <&gpio 7 GPIO_ACTIVE_LOW>;
};
+ gpio_usb_power {
+ gpio-export,name = "usb_power";
+ gpio-export,output = <0>;
+ gpios = <&gpio 6 GPIO_ACTIVE_LOW>;
+ };
};
};
+&uart {
+ status = "okay";
+};
+
&usb {
status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
- dr_mode = "host";
- vbus-supply = <&reg_usb_vbus>;
+ hub_port: port@1 {
+ reg = <1>;
+ #trigger-source-cells = <0>;
+ };
};
&usb_phy {
@@ -122,6 +147,14 @@
status = "okay";
mtd-mac-address = <&art 0x0>;
+ ifname = "eth0";
+};
+
+&eth1 {
+ status = "okay";
+
+ mtd-mac-address = <&art 0x0>;
+ ifname = "eth1";
gmac-config {
device = <&gmac>;
@@ -131,14 +164,10 @@
};
};
-&eth1 {
- status = "okay";
-
- mtd-mac-address = <&art 0x0>;
-};
&wmac {
status = "okay";
mtd-cal-data = <&art 0x1000>;
+ mtd-mac-address = <&art 0x0>;
};
diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile
index 87add0d208..480dbb62c0 100644
--- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/Makefile
@@ -9,5 +9,6 @@ ag71xx-y += ag71xx_phy.o
ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o
+obj-$(CONFIG_AG71XX) += ag71xx_ar7240.o
obj-$(CONFIG_AG71XX) += ag71xx_mdio.o
obj-$(CONFIG_AG71XX) += ag71xx.o
diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
new file mode 100755
index 0000000000..600e0d49d2
--- /dev/null
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
@@ -0,0 +1,1349 @@
+/*
+ * Driver for the built-in ethernet switch of the Atheros AR7240 SoC
+ * Copyright (c) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2010 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/mii.h>
+#include <linux/bitops.h>
+#include <linux/switch.h>
+#include "ag71xx.h"
+
+#define BITM(_count) (BIT(_count) - 1)
+#define BITS(_shift, _count) (BITM(_count) << _shift)
+
+#define AR7240_REG_MASK_CTRL 0x00
+#define AR7240_MASK_CTRL_REVISION_M BITM(8)
+#define AR7240_MASK_CTRL_VERSION_M BITM(8)
+#define AR7240_MASK_CTRL_VERSION_S 8
+#define AR7240_MASK_CTRL_VERSION_AR7240 0x01
+#define AR7240_MASK_CTRL_VERSION_AR934X 0x02
+#define AR7240_MASK_CTRL_SOFT_RESET BIT(31)
+
+#define AR7240_REG_MAC_ADDR0 0x20
+#define AR7240_REG_MAC_ADDR1 0x24
+
+#define AR7240_REG_FLOOD_MASK 0x2c
+#define AR7240_FLOOD_MASK_BROAD_TO_CPU BIT(26)
+
+#define AR7240_REG_GLOBAL_CTRL 0x30
+#define AR7240_GLOBAL_CTRL_MTU_M BITM(11)
+#define AR9340_GLOBAL_CTRL_MTU_M BITM(14)
+
+#define AR7240_REG_VTU 0x0040
+#define AR7240_VTU_OP BITM(3)
+#define AR7240_VTU_OP_NOOP 0x0
+#define AR7240_VTU_OP_FLUSH 0x1
+#define AR7240_VTU_OP_LOAD 0x2
+#define AR7240_VTU_OP_PURGE 0x3
+#define AR7240_VTU_OP_REMOVE_PORT 0x4
+#define AR7240_VTU_ACTIVE BIT(3)
+#define AR7240_VTU_FULL BIT(4)
+#define AR7240_VTU_PORT BITS(8, 4)
+#define AR7240_VTU_PORT_S 8
+#define AR7240_VTU_VID BITS(16, 12)
+#define AR7240_VTU_VID_S 16
+#define AR7240_VTU_PRIO BITS(28, 3)
+#define AR7240_VTU_PRIO_S 28
+#define AR7240_VTU_PRIO_EN BIT(31)
+
+#define AR7240_REG_VTU_DATA 0x0044
+#define AR7240_VTUDATA_MEMBER BITS(0, 10)
+#define AR7240_VTUDATA_VALID BIT(11)
+
+#define AR7240_REG_ATU 0x50
+#define AR7240_ATU_FLUSH_ALL 0x1
+
+#define AR7240_REG_AT_CTRL 0x5c
+#define AR7240_AT_CTRL_AGE_TIME BITS(0, 15)
+#define AR7240_AT_CTRL_AGE_EN BIT(17)
+#define AR7240_AT_CTRL_LEARN_CHANGE BIT(18)
+#define AR7240_AT_CTRL_RESERVED BIT(19)
+#define AR7240_AT_CTRL_ARP_EN BIT(20)
+
+#define AR7240_REG_TAG_PRIORITY 0x70
+
+#define AR7240_REG_SERVICE_TAG 0x74
+#define AR7240_SERVICE_TAG_M BITM(16)
+
+#define AR7240_REG_CPU_PORT 0x78
+#define AR7240_MIRROR_PORT_S 4
+#define AR7240_MIRROR_PORT_M BITM(4)
+#define AR7240_CPU_PORT_EN BIT(8)
+
+#define AR7240_REG_MIB_FUNCTION0 0x80
+#define AR7240_MIB_TIMER_M BITM(16)
+#define AR7240_MIB_AT_HALF_EN BIT(16)
+#define AR7240_MIB_BUSY BIT(17)
+#define AR7240_MIB_FUNC_S 24
+#define AR7240_MIB_FUNC_M BITM(3)
+#define AR7240_MIB_FUNC_NO_OP 0x0
+#define AR7240_MIB_FUNC_FLUSH 0x1
+#define AR7240_MIB_FUNC_CAPTURE 0x3
+
+#define AR7240_REG_MDIO_CTRL 0x98
+#define AR7240_MDIO_CTRL_DATA_M BITM(16)
+#define AR7240_MDIO_CTRL_REG_ADDR_S 16
+#define AR7240_MDIO_CTRL_PHY_ADDR_S 21
+#define AR7240_MDIO_CTRL_CMD_WRITE 0
+#define AR7240_MDIO_CTRL_CMD_READ BIT(27)
+#define AR7240_MDIO_CTRL_MASTER_EN BIT(30)
+#define AR7240_MDIO_CTRL_BUSY BIT(31)
+
+#define AR7240_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100)
+
+#define AR7240_REG_PORT_STATUS(_port) (AR7240_REG_PORT_BASE((_port)) + 0x00)
+#define AR7240_PORT_STATUS_SPEED_S 0
+#define AR7240_PORT_STATUS_SPEED_M BITM(2)
+#define AR7240_PORT_STATUS_SPEED_10 0
+#define AR7240_PORT_STATUS_SPEED_100 1
+#define AR7240_PORT_STATUS_SPEED_1000 2
+#define AR7240_PORT_STATUS_TXMAC BIT(2)
+#define AR7240_PORT_STATUS_RXMAC BIT(3)
+#define AR7240_PORT_STATUS_TXFLOW BIT(4)
+#define AR7240_PORT_STATUS_RXFLOW BIT(5)
+#define AR7240_PORT_STATUS_DUPLEX BIT(6)
+#define AR7240_PORT_STATUS_LINK_UP BIT(8)
+#define AR7240_PORT_STATUS_LINK_AUTO BIT(9)
+#define AR7240_PORT_STATUS_LINK_PAUSE BIT(10)
+
+#define AR7240_REG_PORT_CTRL(_port) (AR7240_REG_PORT_BASE((_port)) + 0x04)
+#define AR7240_PORT_CTRL_STATE_M BITM(3)
+#define AR7240_PORT_CTRL_STATE_DISABLED 0
+#define AR7240_PORT_CTRL_STATE_BLOCK 1
+#define AR7240_PORT_CTRL_STATE_LISTEN 2
+#define AR7240_PORT_CTRL_STATE_LEARN 3
+#define AR7240_PORT_CTRL_STATE_FORWARD 4
+#define AR7240_PORT_CTRL_LEARN_LOCK BIT(7)
+#define AR7240_PORT_CTRL_VLAN_MODE_S 8
+#define AR7240_PORT_CTRL_VLAN_MODE_KEEP 0
+#define AR7240_PORT_CTRL_VLAN_MODE_STRIP 1
+#define AR7240_PORT_CTRL_VLAN_MODE_ADD 2
+#define AR7240_PORT_CTRL_VLAN_MODE_DOUBLE_TAG 3
+#define AR7240_PORT_CTRL_IGMP_SNOOP BIT(10)
+#define AR7240_PORT_CTRL_HEADER BIT(11)
+#define AR7240_PORT_CTRL_MAC_LOOP BIT(12)
+#define AR7240_PORT_CTRL_SINGLE_VLAN BIT(13)
+#define AR7240_PORT_CTRL_LEARN BIT(14)
+#define AR7240_PORT_CTRL_DOUBLE_TAG BIT(15)
+#define AR7240_PORT_CTRL_MIRROR_TX BIT(16)
+#define AR7240_PORT_CTRL_MIRROR_RX BIT(17)
+
+#define AR7240_REG_PORT_VLAN(_port) (AR7240_REG_PORT_BASE((_port)) + 0x08)
+
+#define AR7240_PORT_VLAN_DEFAULT_ID_S 0
+#define AR7240_PORT_VLAN_DEST_PORTS_S 16
+#define AR7240_PORT_VLAN_MODE_S 30
+#define AR7240_PORT_VLAN_MODE_PORT_ONLY 0
+#define AR7240_PORT_VLAN_MODE_PORT_FALLBACK 1
+#define AR7240_PORT_VLAN_MODE_VLAN_ONLY 2
+#define AR7240_PORT_VLAN_MODE_SECURE 3
+
+
+#define AR7240_REG_STATS_BASE(_port) (0x20000 + (_port) * 0x100)
+
+#define AR7240_STATS_RXBROAD 0x00
+#define AR7240_STATS_RXPAUSE 0x04
+#define AR7240_STATS_RXMULTI 0x08
+#define AR7240_STATS_RXFCSERR 0x0c
+#define AR7240_STATS_RXALIGNERR 0x10
+#define AR7240_STATS_RXRUNT 0x14
+#define AR7240_STATS_RXFRAGMENT 0x18
+#define AR7240_STATS_RX64BYTE 0x1c
+#define AR7240_STATS_RX128BYTE 0x20
+#define AR7240_STATS_RX256BYTE 0x24
+#define AR7240_STATS_RX512BYTE 0x28
+#define AR7240_STATS_RX1024BYTE 0x2c
+#define AR7240_STATS_RX1518BYTE 0x30
+#define AR7240_STATS_RXMAXBYTE 0x34
+#define AR7240_STATS_RXTOOLONG 0x38
+#define AR7240_STATS_RXGOODBYTE 0x3c
+#define AR7240_STATS_RXBADBYTE 0x44
+#define AR7240_STATS_RXOVERFLOW 0x4c
+#define AR7240_STATS_FILTERED 0x50
+#define AR7240_STATS_TXBROAD 0x54
+#define AR7240_STATS_TXPAUSE 0x58
+#define AR7240_STATS_TXMULTI 0x5c
+#define AR7240_STATS_TXUNDERRUN 0x60
+#define AR7240_STATS_TX64BYTE 0x64
+#define AR7240_STATS_TX128BYTE 0x68
+#define AR7240_STATS_TX256BYTE 0x6c
+#define AR7240_STATS_TX512BYTE 0x70
+#define AR7240_STATS_TX1024BYTE 0x74
+#define AR7240_STATS_TX1518BYTE 0x78
+#define AR7240_STATS_TXMAXBYTE 0x7c
+#define AR7240_STATS_TXOVERSIZE 0x80
+#define AR7240_STATS_TXBYTE 0x84
+#define AR7240_STATS_TXCOLLISION 0x8c
+#define AR7240_STATS_TXABORTCOL 0x90
+#define AR7240_STATS_TXMULTICOL 0x94
+#define AR7240_STATS_TXSINGLECOL 0x98
+#define AR7240_STATS_TXEXCDEFER 0x9c
+#define AR7240_STATS_TXDEFER 0xa0
+#define AR7240_STATS_TXLATECOL 0xa4
+
+#define AR7240_PORT_CPU 0
+#define AR7240_NUM_PORTS 6
+#define AR7240_NUM_PHYS 5
+
+#define AR7240_PHY_ID1 0x004d
+#define AR7240_PHY_ID2 0xd041
+
+#define AR934X_PHY_ID1 0x004d
+#define AR934X_PHY_ID2 0xd042
+
+#define AR7240_MAX_VLANS 16
+
+#define AR934X_REG_OPER_MODE0 0x04
+#define AR934X_OPER_MODE0_MAC_GMII_EN BIT(6)
+#define AR934X_OPER_MODE0_PHY_MII_EN BIT(10)
+
+#define AR934X_REG_OPER_MODE1 0x08
+#define AR934X_REG_OPER_MODE1_PHY4_MII_EN BIT(28)
+
+#define AR934X_REG_FLOOD_MASK 0x2c
+#define AR934X_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p))
+#define AR934X_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p))
+
+#define AR934X_REG_QM_CTRL 0x3c
+#define AR934X_QM_CTRL_ARP_EN BIT(15)
+
+#define AR934X_REG_AT_CTRL 0x5c
+#define AR934X_AT_CTRL_AGE_TIME BITS(0, 15)
+#define AR934X_AT_CTRL_AGE_EN BIT(17)
+#define AR934X_AT_CTRL_LEARN_CHANGE BIT(18)
+
+#define AR934X_MIB_ENABLE BIT(30)
+
+#define AR934X_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100)
+
+#define AR934X_REG_PORT_VLAN1(_port) (AR934X_REG_PORT_BASE((_port)) + 0x08)
+#define AR934X_PORT_VLAN1_DEFAULT_SVID_S 0
+#define AR934X_PORT_VLAN1_FORCE_DEFAULT_VID_EN BIT(12)
+#define AR934X_PORT_VLAN1_PORT_TLS_MODE BIT(13)
+#define AR934X_PORT_VLAN1_PORT_VLAN_PROP_EN BIT(14)
+#define AR934X_PORT_VLAN1_PORT_CLONE_EN BIT(15)
+#define AR934X_PORT_VLAN1_DEFAULT_CVID_S 16
+#define AR934X_PORT_VLAN1_FORCE_PORT_VLAN_EN BIT(28)
+#define AR934X_PORT_VLAN1_ING_PORT_PRI_S 29
+
+#define AR934X_REG_PORT_VLAN2(_port) (AR934X_REG_PORT_BASE((_port)) + 0x0c)
+#define AR934X_PORT_VLAN2_PORT_VID_MEM_S 16
+#define AR934X_PORT_VLAN2_8021Q_MODE_S 30
+#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_ONLY 0
+#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_FALLBACK 1
+#define AR934X_PORT_VLAN2_8021Q_MODE_VLAN_ONLY 2
+#define AR934X_PORT_VLAN2_8021Q_MODE_SECURE 3
+
+#define sw_to_ar7240(_dev) container_of(_dev, struct ar7240sw, swdev)
+
+struct ar7240sw_port_stat {
+ unsigned long rx_broadcast;
+ unsigned long rx_pause;
+ unsigned long rx_multicast;
+ unsigned long rx_fcs_error;
+ unsigned long rx_align_error;
+ unsigned long rx_runt;
+ unsigned long rx_fragments;
+ unsigned long rx_64byte;
+ unsigned long rx_128byte;
+ unsigned long rx_256byte;
+ unsigned long rx_512byte;
+ unsigned long rx_1024byte;
+ unsigned long rx_1518byte;
+ unsigned long rx_maxbyte;
+ unsigned long rx_toolong;
+ unsigned long rx_good_byte;
+ unsigned long rx_bad_byte;
+ unsigned long rx_overflow;
+ unsigned long filtered;
+
+ unsigned long tx_broadcast;
+ unsigned long tx_pause;
+ unsigned long tx_multicast;
+ unsigned long tx_underrun;
+ unsigned long tx_64byte;
+ unsigned long tx_128byte;
+ unsigned long tx_256byte;
+ unsigned long tx_512byte;
+ unsigned long tx_1024byte;
+ unsigned long tx_1518byte;
+ unsigned long tx_maxbyte;
+ unsigned long tx_oversize;
+ unsigned long tx_byte;
+ unsigned long tx_collision;
+ unsigned long tx_abortcol;
+ unsigned long tx_multicol;
+ unsigned long tx_singlecol;
+ unsigned long tx_excdefer;
+ unsigned long tx_defer;
+ unsigned long tx_xlatecol;
+};
+
+struct ar7240sw {
+ struct mii_bus *mii_bus;
+ struct mii_bus *switch_mii_bus;
+ struct device_node *of_node;
+ struct device_node *mdio_node;
+ struct switch_dev swdev;
+ int num_ports;
+ u8 ver;
+ bool vlan;
+ u16 vlan_id[AR7240_MAX_VLANS];
+ u8 vlan_table[AR7240_MAX_VLANS];
+ u8 vlan_tagged;
+ u16 pvid[AR7240_NUM_PORTS];
+ char buf[80];
+
+ rwlock_t stats_lock;
+ struct ar7240sw_port_stat port_stats[AR7240_NUM_PORTS];
+};
+
+struct ar7240sw_hw_stat {
+ char string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int reg;
+};
+
+static DEFINE_MUTEX(reg_mutex);
+
+static inline int sw_is_ar7240(struct ar7240sw *as)
+{
+ return as->ver == AR7240_MASK_CTRL_VERSION_AR7240;
+}
+
+static inline int sw_is_ar934x(struct ar7240sw *as)
+{
+ return as->ver == AR7240_MASK_CTRL_VERSION_AR934X;
+}
+
+static inline u32 ar7240sw_port_mask(struct ar7240sw *as, int port)
+{
+ return BIT(port);
+}
+
+static inline u32 ar7240sw_port_mask_all(struct ar7240sw *as)
+{
+ return BIT(as->swdev.ports) - 1;
+}
+
+static inline u32 ar7240sw_port_mask_but(struct ar7240sw *as, int port)
+{
+ return ar7240sw_port_mask_all(as) & ~BIT(port);
+}
+
+static inline u16 mk_phy_addr(u32 reg)
+{
+ return 0x17 & ((reg >> 4) | 0x10);
+}
+
+static inline u16 mk_phy_reg(u32 reg)
+{
+ return (reg << 1) & 0x1e;
+}
+
+static inline u16 mk_high_addr(u32 reg)
+{
+ return (reg >> 7) & 0x1ff;
+}
+
+static u32 __ar7240sw_reg_read(struct mii_bus *mii, u32 reg)
+{
+ unsigned long flags;
+ u16 phy_addr;
+ u16 phy_reg;
+ u32 hi, lo;
+
+ reg = (reg & 0xfffffffc) >> 2;
+ phy_addr = mk_phy_addr(reg);
+ phy_reg = mk_phy_reg(reg);
+
+ local_irq_save(flags);
+ mutex_lock(&mii->mdio_lock);
+ mii->write(mii, 0x1f, 0x10, mk_high_addr(reg));
+ lo = (u32) mii->read(mii, phy_addr, phy_reg);
+ hi = (u32) mii->read(mii, phy_addr, phy_reg + 1);
+ mutex_unlock(&mii->mdio_lock);
+ local_irq_restore(flags);
+
+ return (hi << 16) | lo;
+}
+
+static void __ar7240sw_reg_write(struct mii_bus *mii, u32 reg, u32 val)
+{
+ unsigned long flags;
+ u16 phy_addr;
+ u16 phy_reg;
+
+ reg = (reg & 0xfffffffc) >> 2;
+ phy_addr = mk_phy_addr(reg);
+ phy_reg = mk_phy_reg(reg);
+
+ local_irq_save(flags);
+ mutex_lock(&mii->mdio_lock);
+ mii->write(mii, 0x1f, 0x10, mk_high_addr(reg));
+ mii->write(mii, phy_addr, phy_reg + 1, (val >> 16));
+ mii->write(mii, phy_addr, phy_reg, (val & 0xffff));
+ mutex_unlock(&mii->mdio_lock);
+ local_irq_restore(flags);
+}
+
+static u32 ar7240sw_reg_read(struct mii_bus *mii, u32 reg_addr)
+{
+ u32 ret;
+
+ mutex_lock(&reg_mutex);
+ ret = __ar7240sw_reg_read(mii, reg_addr);
+ mutex_unlock(&reg_mutex);
+
+ return ret;
+}
+
+static void ar7240sw_reg_write(struct mii_bus *mii, u32 reg_addr, u32 reg_val)
+{
+ mutex_lock(&reg_mutex);
+ __ar7240sw_reg_write(mii, reg_addr, reg_val);
+ mutex_unlock(&reg_mutex);
+}
+
+static u32 ar7240sw_reg_rmw(struct mii_bus *mii, u32 reg, u32 mask, u32 val)
+{
+ u32 t;
+
+ mutex_lock(&reg_mutex);
+ t = __ar7240sw_reg_read(mii, reg);
+ t &= ~mask;
+ t |= val;
+ __ar7240sw_reg_write(mii, reg, t);
+ mutex_unlock(&reg_mutex);
+
+ return t;
+}
+
+static void ar7240sw_reg_set(struct mii_bus *mii, u32 reg, u32 val)
+{
+ u32 t;
+
+ mutex_lock(&reg_mutex);
+ t = __ar7240sw_reg_read(mii, reg);
+ t |= val;
+ __ar7240sw_reg_write(mii, reg, t);
+ mutex_unlock(&reg_mutex);
+}
+
+static int __ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val,
+ unsigned timeout)
+{
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ u32 t;
+
+ t = __ar7240sw_reg_read(mii, reg);
+ if ((t & mask) == val)
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val,
+ unsigned timeout)
+{
+ int ret;
+
+ mutex_lock(&reg_mutex);
+ ret = __ar7240sw_reg_wait(mii, reg, mask, val, timeout);
+ mutex_unlock(&reg_mutex);
+ return ret;
+}
+
+int ar7240sw_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr)
+{
+ u32 t, val = 0xffff;
+ int err;
+ struct ar7240sw *as = bus->priv;
+ struct mii_bus *mii = as->mii_bus;
+
+ if (phy_addr >= AR7240_NUM_PHYS)
+ return 0xffff;
+
+ mutex_lock(&reg_mutex);
+ t = (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) |
+ (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) |
+ AR7240_MDIO_CTRL_MASTER_EN |
+ AR7240_MDIO_CTRL_BUSY |
+ AR7240_MDIO_CTRL_CMD_READ;
+
+ __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t);
+ err = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL,
+ AR7240_MDIO_CTRL_BUSY, 0, 5);
+ if (!err)
+ val = __ar7240sw_reg_read(mii, AR7240_REG_MDIO_CTRL);
+ mutex_unlock(&reg_mutex);
+
+ return val & AR7240_MDIO_CTRL_DATA_M;
+}
+
+int ar7240sw_phy_write(struct mii_bus *bus, int phy_addr, int reg_addr,
+ u16 reg_val)
+{
+ u32 t;
+ int ret;
+ struct ar7240sw *as = bus->priv;
+ struct mii_bus *mii = as->mii_bus;
+
+ if (phy_addr >= AR7240_NUM_PHYS)
+ return -EINVAL;
+
+ mutex_lock(&reg_mutex);
+ t = (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) |
+ (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) |
+ AR7240_MDIO_CTRL_MASTER_EN |
+ AR7240_MDIO_CTRL_BUSY |
+ AR7240_MDIO_CTRL_CMD_WRITE |
+ reg_val;
+
+ __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t);
+ ret = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL,
+ AR7240_MDIO_CTRL_BUSY, 0, 5);
+ mutex_unlock(&reg_mutex);
+
+ return ret;
+}
+
+static int ar7240sw_capture_stats(struct ar7240sw *as)
+{
+ struct mii_bus *mii = as->mii_bus;
+ int port;
+ int ret;
+
+ write_lock(&as->stats_lock);
+
+ /* Capture the hardware statistics for all ports */
+ ar7240sw_reg_rmw(mii, AR7240_REG_MIB_FUNCTION0,
+ (AR7240_MIB_FUNC_M << AR7240_MIB_FUNC_S),
+ (AR7240_MIB_FUNC_CAPTURE << AR7240_MIB_FUNC_S));
+
+ /* Wait for the capturing to complete. */
+ ret = ar7240sw_reg_wait(mii, AR7240_REG_MIB_FUNCTION0,
+ AR7240_MIB_BUSY, 0, 10);
+
+ if (ret)
+ goto unlock;
+
+ for (port = 0; port < AR7240_NUM_PORTS; port++) {
+ unsigned int base;
+ struct ar7240sw_port_stat *stats;
+
+ base = AR7240_REG_STATS_BASE(port);
+ stats = &as->port_stats[port];
+
+#define READ_STAT(_r) ar7240sw_reg_read(mii, base + AR7240_STATS_ ## _r)
+
+ stats->rx_good_byte += READ_STAT(RXGOODBYTE);
+ stats->tx_byte += READ_STAT(TXBYTE);
+
+#undef READ_STAT
+ }
+
+ ret = 0;
+
+unlock:
+ write_unlock(&as->stats_lock);
+ return ret;
+}
+
+static void ar7240sw_disable_port(struct ar7240sw *as, unsigned port)
+{
+ ar7240sw_reg_write(as->mii_bus, AR7240_REG_PORT_CTRL(port),
+ AR7240_PORT_CTRL_STATE_DISABLED);
+}
+
+static void ar7240sw_setup(struct ar7240sw *as)
+{
+ struct mii_bus *mii = as->mii_bus;
+
+ /* Enable CPU port, and disable mirror port */
+ ar7240sw_reg_write(mii, AR7240_REG_CPU_PORT,
+ AR7240_CPU_PORT_EN |
+ (15 << AR7240_MIRROR_PORT_S));
+
+ /* Setup TAG priority mapping */
+ ar7240sw_reg_write(mii, AR7240_REG_TAG_PRIORITY, 0xfa50);
+
+ if (sw_is_ar934x(as)) {
+ /* Enable aging, MAC replacing */
+ ar7240sw_reg_write(mii, AR934X_REG_AT_CTRL,
+ 0x2b /* 5 min age time */ |
+ AR934X_AT_CTRL_AGE_EN |
+ AR934X_AT_CTRL_LEARN_CHANGE);
+ /* Enable ARP frame acknowledge */
+ ar7240sw_reg_set(mii, AR934X_REG_QM_CTRL,
+ AR934X_QM_CTRL_ARP_EN);
+ /* Enable Broadcast/Multicast frames transmitted to the CPU */
+ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK,
+ AR934X_FLOOD_MASK_BC_DP(0) |
+ AR934X_FLOOD_MASK_MC_DP(0));
+
+ /* setup MTU */
+ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL,
+ AR9340_GLOBAL_CTRL_MTU_M,
+ AR9340_GLOBAL_CTRL_MTU_M);
+
+ /* Enable MIB counters */
+ ar7240sw_reg_set(mii, AR7240_REG_MIB_FUNCTION0,
+ AR934X_MIB_ENABLE);
+
+ } else {
+ /* Enable ARP frame acknowledge, aging, MAC replacing */
+ ar7240sw_reg_write(mii, AR7240_REG_AT_CTRL,
+ AR7240_AT_CTRL_RESERVED |
+ 0x2b /* 5 min age time */ |
+ AR7240_AT_CTRL_AGE_EN |
+ AR7240_AT_CTRL_ARP_EN |
+ AR7240_AT_CTRL_LEARN_CHANGE);
+ /* Enable Broadcast frames transmitted to the CPU */
+ ar7240sw_reg_set(mii, AR7240_REG_FLOOD_MASK,
+ AR7240_FLOOD_MASK_BROAD_TO_CPU);
+
+ /* setup MTU */
+ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL,
+ AR7240_GLOBAL_CTRL_MTU_M,
+ AR7240_GLOBAL_CTRL_MTU_M);
+ }
+
+ /* setup Service TAG */
+ ar7240sw_reg_rmw(mii, AR7240_REG_SERVICE_TAG, AR7240_SERVICE_TAG_M, 0);
+}
+
+/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
+static int
+ar7240sw_phy_poll_reset(struct mii_bus *bus)
+{
+ const unsigned int sleep_msecs = 20;
+ int ret, elapsed, i;
+
+ for (elapsed = sleep_msecs; elapsed <= 600;
+ elapsed += sleep_msecs) {
+ msleep(sleep_msecs);
+ for (i = 0; i < AR7240_NUM_PHYS; i++) {
+ ret = ar7240sw_phy_read(bus, i, MII_BMCR);
+ if (ret < 0)
+ return ret;
+ if (ret & BMCR_RESET)
+ break;
+ if (i == AR7240_NUM_PHYS - 1) {
+ usleep_range(1000, 2000);
+ return 0;
+ }
+ }
+ }
+ return -ETIMEDOUT;
+}
+
+static int ar7240sw_reset(struct ar7240sw *as)
+{
+ struct mii_bus *mii = as->mii_bus;
+ struct mii_bus *swmii = as->switch_mii_bus;
+ int ret;
+ int i;
+
+ /* Set all ports to disabled state. */
+ for (i = 0; i < AR7240_NUM_PORTS; i++)
+ ar7240sw_disable_port(as, i);
+
+ /* Wait for transmit queues to drain. */
+ usleep_range(2000, 3000);
+
+ /* Reset the switch. */
+ ar7240sw_reg_write(mii, AR7240_REG_MASK_CTRL,
+ AR7240_MASK_CTRL_SOFT_RESET);
+
+ ret = ar7240sw_reg_wait(mii, AR7240_REG_MASK_CTRL,
+ AR7240_MASK_CTRL_SOFT_RESET, 0, 1000);
+
+ /* setup PHYs */
+ for (i = 0; i < AR7240_NUM_PHYS; i++) {
+ ar7240sw_phy_write(swmii, i, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ ar7240sw_phy_write(swmii, i, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE);
+ }
+ ret = ar7240sw_phy_poll_reset(swmii);
+ if (ret)
+ return ret;
+
+ ar7240sw_setup(as);
+ return ret;
+}
+
+static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask)
+{
+ struct mii_bus *mii = as->mii_bus;
+ u32 ctrl;
+ u32 vid, mode;
+
+ ctrl = AR7240_PORT_CTRL_STATE_FORWARD | AR7240_PORT_CTRL_LEARN |
+ AR7240_PORT_CTRL_SINGLE_VLAN;
+
+ if (port == AR7240_PORT_CPU) {
+ ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port),
+ AR7240_PORT_STATUS_SPEED_1000 |
+ AR7240_PORT_STATUS_TXFLOW |
+ AR7240_PORT_STATUS_RXFLOW |
+ AR7240_PORT_STATUS_TXMAC |
+ AR7240_PORT_STATUS_RXMAC |
+ AR7240_PORT_STATUS_DUPLEX);
+ } else {
+ ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port),
+ AR7240_PORT_STATUS_LINK_AUTO);
+ }
+
+ /* Set the default VID for this port */
+ if (as->vlan) {
+ vid = as->vlan_id[as->pvid[port]];
+ mode = AR7240_PORT_VLAN_MODE_SECURE;
+ } else {
+ vid = port;
+ mode = AR7240_PORT_VLAN_MODE_PORT_ONLY;
+ }
+
+ if (as->vlan) {
+ if (as->vlan_tagged & BIT(port))
+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_ADD <<
+ AR7240_PORT_CTRL_VLAN_MODE_S;
+ else
+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_STRIP <<
+ AR7240_PORT_CTRL_VLAN_MODE_S;
+ } else {
+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_KEEP <<
+ AR7240_PORT_CTRL_VLAN_MODE_S;
+ }
+
+ if (!portmask) {
+ if (port == AR7240_PORT_CPU)
+ portmask = ar7240sw_port_mask_but(as, AR7240_PORT_CPU);
+ else
+ portmask = ar7240sw_port_mask(as, AR7240_PORT_CPU);
+ }
+
+ /* preserve mirror rx&tx flags */
+ ctrl |= ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port)) &
+ (AR7240_PORT_CTRL_MIRROR_RX | AR7240_PORT_CTRL_MIRROR_TX);
+
+ /* allow the port to talk to all other ports, but exclude its
+ * own ID to prevent frames from being reflected back to the
+ * port that they came from */
+ portmask &= ar7240sw_port_mask_but(as, port);
+
+ ar7240sw_reg_write(mii, AR7240_REG_PORT_CTRL(port), ctrl);
+ if (sw_is_ar934x(as)) {
+ u32 vlan1, vlan2;
+
+ vlan1 = (vid << AR934X_PORT_VLAN1_DEFAULT_CVID_S);
+ vlan2 = (portmask << AR934X_PORT_VLAN2_PORT_VID_MEM_S) |
+ (mode << AR934X_PORT_VLAN2_8021Q_MODE_S);
+ ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN1(port), vlan1);
+ ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN2(port), vlan2);
+ } else {
+ u32 vlan;
+
+ vlan = vid | (mode << AR7240_PORT_VLAN_MODE_S) |
+ (portmask << AR7240_PORT_VLAN_DEST_PORTS_S);
+
+ ar7240sw_reg_write(mii, AR7240_REG_PORT_VLAN(port), vlan);
+ }
+}
+
+static int
+ar7240_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ as->vlan_id[val->port_vlan] = val->value.i;
+ return 0;
+}
+
+static int
+ar7240_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ val->value.i = as->vlan_id[val->port_vlan];
+ return 0;
+}
+
+static int
+ar7240_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+
+ /* make sure no invalid PVIDs get set */
+
+ if (vlan >= dev->vlans)
+ return -EINVAL;
+
+ as->pvid[port] = vlan;
+ return 0;
+}
+
+static int
+ar7240_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ *vlan = as->pvid[port];
+ return 0;
+}
+
+static int
+ar7240_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ u8 ports = as->vlan_table[val->port_vlan];
+ int i;
+
+ val->len = 0;
+ for (i = 0; i < as->swdev.ports; i++) {
+ struct switch_port *p;
+
+ if (!(ports & (1 << i)))
+ continue;
+
+ p = &val->value.ports[val->len++];
+ p->id = i;
+ if (as->vlan_tagged & (1 << i))
+ p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+ else
+ p->flags = 0;
+ }
+ return 0;
+}
+
+static int
+ar7240_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ u8 *vt = &as->vlan_table[val->port_vlan];
+ int i, j;
+
+ *vt = 0;
+ for (i = 0; i < val->len; i++) {
+ struct switch_port *p = &val->value.ports[i];
+
+ if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
+ as->vlan_tagged |= (1 << p->id);
+ else {
+ as->vlan_tagged &= ~(1 << p->id);
+ as->pvid[p->id] = val->port_vlan;
+
+ /* make sure that an untagged port does not
+ * appear in other vlans */
+ for (j = 0; j < AR7240_MAX_VLANS; j++) {
+ if (j == val->port_vlan)
+ continue;
+ as->vlan_table[j] &= ~(1 << p->id);
+ }
+ }
+
+ *vt |= 1 << p->id;
+ }
+ return 0;
+}
+
+static int
+ar7240_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ as->vlan = !!val->value.i;
+ return 0;
+}
+
+static int
+ar7240_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ val->value.i = as->vlan;
+ return 0;
+}
+
+static void
+ar7240_vtu_op(struct ar7240sw *as, u32 op, u32 val)
+{
+ struct mii_bus *mii = as->mii_bus;
+
+ if (ar7240sw_reg_wait(mii, AR7240_REG_VTU, AR7240_VTU_ACTIVE, 0, 5))
+ return;
+
+ if ((op & AR7240_VTU_OP) == AR7240_VTU_OP_LOAD) {
+ val &= AR7240_VTUDATA_MEMBER;
+ val |= AR7240_VTUDATA_VALID;
+ ar7240sw_reg_write(mii, AR7240_REG_VTU_DATA, val);
+ }
+ op |= AR7240_VTU_ACTIVE;
+ ar7240sw_reg_write(mii, AR7240_REG_VTU, op);
+}
+
+static int
+ar7240_hw_apply(struct switch_dev *dev)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ u8 portmask[AR7240_NUM_PORTS];
+ int i, j;
+
+ /* flush all vlan translation unit entries */
+ ar7240_vtu_op(as, AR7240_VTU_OP_FLUSH, 0);
+
+ memset(portmask, 0, sizeof(portmask));
+ if (as->vlan) {
+ /* calculate the port destination masks and load vlans
+ * into the vlan translation unit */
+ for (j = 0; j < AR7240_MAX_VLANS; j++) {
+ u8 vp = as->vlan_table[j];
+
+ if (!vp)
+ continue;
+
+ for (i = 0; i < as->swdev.ports; i++) {
+ u8 mask = (1 << i);
+ if (vp & mask)
+ portmask[i] |= vp & ~mask;
+ }
+
+ ar7240_vtu_op(as,
+ AR7240_VTU_OP_LOAD |
+ (as->vlan_id[j] << AR7240_VTU_VID_S),
+ as->vlan_table[j]);
+ }
+ } else {
+ /* vlan disabled:
+ * isolate all ports, but connect them to the cpu port */
+ for (i = 0; i < as->swdev.ports; i++) {
+ if (i == AR7240_PORT_CPU)
+ continue;
+
+ portmask[i] = 1 << AR7240_PORT_CPU;
+ portmask[AR7240_PORT_CPU] |= (1 << i);
+ }
+ }
+
+ /* update the port destination mask registers and tag settings */
+ for (i = 0; i < as->swdev.ports; i++)
+ ar7240sw_setup_port(as, i, portmask[i]);
+
+ return 0;
+}
+
+static int
+ar7240_reset_switch(struct switch_dev *dev)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ ar7240sw_reset(as);
+
+ /* ar7240 reapply hardware settings*/
+ if (sw_is_ar7240(as))
+ ar7240_hw_apply(dev);
+
+ return 0;
+}
+
+static int
+ar7240_get_port_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+ u32 status;
+
+ if (port >= AR7240_NUM_PORTS)
+ return -EINVAL;
+
+ status = ar7240sw_reg_read(mii, AR7240_REG_PORT_STATUS(port));
+ link->aneg = !!(status & AR7240_PORT_STATUS_LINK_AUTO);
+ if (link->aneg) {
+ link->link = !!(status & AR7240_PORT_STATUS_LINK_UP);
+ if (!link->link)
+ return 0;
+ } else {
+ link->link = true;
+ }
+
+ link->duplex = !!(status & AR7240_PORT_STATUS_DUPLEX);
+ link->tx_flow = !!(status & AR7240_PORT_STATUS_TXFLOW);
+ link->rx_flow = !!(status & AR7240_PORT_STATUS_RXFLOW);
+ switch (status & AR7240_PORT_STATUS_SPEED_M) {
+ case AR7240_PORT_STATUS_SPEED_10:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case AR7240_PORT_STATUS_SPEED_100:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case AR7240_PORT_STATUS_SPEED_1000:
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ }
+
+ return 0;
+}
+
+static int
+ar7240_get_port_stats(struct switch_dev *dev, int port,
+ struct switch_port_stats *stats)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+
+ if (port >= AR7240_NUM_PORTS)
+ return -EINVAL;
+
+ ar7240sw_capture_stats(as);
+
+ read_lock(&as->stats_lock);
+ stats->rx_bytes = as->port_stats[port].rx_good_byte;
+ stats->tx_bytes = as->port_stats[port].tx_byte;
+ read_unlock(&as->stats_lock);
+
+ return 0;
+}
+
+static int
+ar7240_set_mirror_monitor_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ int port = val->value.i;
+
+ if (port > 15)
+ return -EINVAL;
+
+ ar7240sw_reg_rmw(mii, AR7240_REG_CPU_PORT,
+ AR7240_MIRROR_PORT_M << AR7240_MIRROR_PORT_S,
+ port << AR7240_MIRROR_PORT_S);
+
+ return 0;
+}
+
+static int
+ar7240_get_mirror_monitor_port(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ u32 ret;
+
+ ret = ar7240sw_reg_read(mii, AR7240_REG_CPU_PORT);
+ val->value.i = (ret >> AR7240_MIRROR_PORT_S) & AR7240_MIRROR_PORT_M;
+
+ return 0;
+}
+
+static int
+ar7240_set_mirror_rx(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ int port = val->port_vlan;
+
+ if (port >= dev->ports)
+ return -EINVAL;
+
+ if (val && val->value.i == 1)
+ ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port),
+ AR7240_PORT_CTRL_MIRROR_RX);
+ else
+ ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port),
+ AR7240_PORT_CTRL_MIRROR_RX, 0);
+
+ return 0;
+}
+
+static int
+ar7240_get_mirror_rx(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ u32 ctrl;
+
+ int port = val->port_vlan;
+
+ if (port >= dev->ports)
+ return -EINVAL;
+
+ ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port));
+
+ if ((ctrl & AR7240_PORT_CTRL_MIRROR_RX) == AR7240_PORT_CTRL_MIRROR_RX)
+ val->value.i = 1;
+ else
+ val->value.i = 0;
+
+ return 0;
+}
+
+static int
+ar7240_set_mirror_tx(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ int port = val->port_vlan;
+
+ if (port >= dev->ports)
+ return -EINVAL;
+
+ if (val && val->value.i == 1)
+ ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port),
+ AR7240_PORT_CTRL_MIRROR_TX);
+ else
+ ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port),
+ AR7240_PORT_CTRL_MIRROR_TX, 0);
+
+ return 0;
+}
+
+static int
+ar7240_get_mirror_tx(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar7240sw *as = sw_to_ar7240(dev);
+ struct mii_bus *mii = as->mii_bus;
+
+ u32 ctrl;
+
+ int port = val->port_vlan;
+
+ if (port >= dev->ports)
+ return -EINVAL;
+
+ ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port));
+
+ if ((ctrl & AR7240_PORT_CTRL_MIRROR_TX) == AR7240_PORT_CTRL_MIRROR_TX)
+ val->value.i = 1;
+ else
+ val->value.i = 0;
+
+ return 0;
+}
+
+static struct switch_attr ar7240_globals[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = ar7240_set_vlan,
+ .get = ar7240_get_vlan,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "mirror_monitor_port",
+ .description = "Mirror monitor port",
+ .set = ar7240_set_mirror_monitor_port,
+ .get = ar7240_get_mirror_monitor_port,
+ .max = 15
+ },
+};
+
+static struct switch_attr ar7240_port[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_rx",
+ .description = "Enable mirroring of RX packets",
+ .set = ar7240_set_mirror_rx,
+ .get = ar7240_get_mirror_rx,
+ .max = 1
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_mirror_tx",
+ .description = "Enable mirroring of TX packets",
+ .set = ar7240_set_mirror_tx,
+ .get = ar7240_get_mirror_tx,
+ .max = 1
+ },
+};
+
+static struct switch_attr ar7240_vlan[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "vid",
+ .description = "VLAN ID",
+ .set = ar7240_set_vid,
+ .get = ar7240_get_vid,
+ .max = 4094,
+ },
+};
+
+static const struct switch_dev_ops ar7240_ops = {
+ .attr_global = {
+ .attr = ar7240_globals,
+ .n_attr = ARRAY_SIZE(ar7240_globals),
+ },
+ .attr_port = {
+ .attr = ar7240_port,
+ .n_attr = ARRAY_SIZE(ar7240_port),
+ },
+ .attr_vlan = {
+ .attr = ar7240_vlan,
+ .n_attr = ARRAY_SIZE(ar7240_vlan),
+ },
+ .get_port_pvid = ar7240_get_pvid,
+ .set_port_pvid = ar7240_set_pvid,
+ .get_vlan_ports = ar7240_get_ports,
+ .set_vlan_ports = ar7240_set_ports,
+ .apply_config = ar7240_hw_apply,
+ .reset_switch = ar7240_reset_switch,
+ .get_port_link = ar7240_get_port_link,
+ .get_port_stats = ar7240_get_port_stats,
+};
+
+static int
+ag71xx_ar7240_probe(struct mdio_device *mdiodev)
+{
+ struct mii_bus *mii = mdiodev->bus;
+ struct ar7240sw *as;
+ struct switch_dev *swdev;
+ struct reset_control *switch_reset;
+ u32 ctrl;
+ int phy_if_mode, err, i;
+
+ as = devm_kzalloc(&mdiodev->dev, sizeof(*as), GFP_KERNEL);
+ if (!as)
+ return -ENOMEM;
+
+ as->mii_bus = mii;
+ as->of_node = mdiodev->dev.of_node;
+ as->mdio_node = of_get_child_by_name(as->of_node, "mdio-bus");
+
+ swdev = &as->swdev;
+
+ switch_reset = devm_reset_control_get_optional(&mdiodev->dev, "switch");
+ if (switch_reset) {
+ reset_control_assert(switch_reset);
+ msleep(50);
+ reset_control_deassert(switch_reset);
+ msleep(200);
+ }
+
+ ctrl = ar7240sw_reg_read(mii, AR7240_REG_MASK_CTRL);
+ as->ver = (ctrl >> AR7240_MASK_CTRL_VERSION_S) &
+ AR7240_MASK_CTRL_VERSION_M;
+
+ if (sw_is_ar7240(as)) {
+ swdev->name = "AR7240/AR9330 built-in switch";
+ swdev->ports = AR7240_NUM_PORTS - 1;
+ } else if (sw_is_ar934x(as)) {
+ swdev->name = "AR934X built-in switch";
+ phy_if_mode = of_get_phy_mode(as->of_node);
+
+ if (phy_if_mode == PHY_INTERFACE_MODE_GMII) {
+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
+ AR934X_OPER_MODE0_MAC_GMII_EN);
+ } else if (phy_if_mode == PHY_INTERFACE_MODE_MII) {
+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0,
+ AR934X_OPER_MODE0_PHY_MII_EN);
+ } else {
+ pr_err("%s: invalid PHY interface mode\n",
+ dev_name(&mdiodev->dev));
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(as->of_node, "phy4-mii-enable")) {
+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1,
+ AR934X_REG_OPER_MODE1_PHY4_MII_EN);
+ swdev->ports = AR7240_NUM_PORTS - 1;
+ } else {
+ swdev->ports = AR7240_NUM_PORTS;
+ }
+ } else {
+ pr_err("%s: unsupported chip, ctrl=%08x\n",
+ dev_name(&mdiodev->dev), ctrl);
+ return -EINVAL;
+ }
+
+ swdev->cpu_port = AR7240_PORT_CPU;
+ swdev->vlans = AR7240_MAX_VLANS;
+ swdev->ops = &ar7240_ops;
+ swdev->alias = dev_name(&mdiodev->dev);
+
+ if ((err = register_switch(&as->swdev, NULL)) < 0)
+ return err;
+
+ pr_info("%s: Found an %s\n", dev_name(&mdiodev->dev), swdev->name);
+
+ as->switch_mii_bus = devm_mdiobus_alloc(&mdiodev->dev);
+ as->switch_mii_bus->name = "ar7240sw_mdio";
+ as->switch_mii_bus->read = ar7240sw_phy_read;
+ as->switch_mii_bus->write = ar7240sw_phy_write;
+ as->switch_mii_bus->priv = as;
+ as->switch_mii_bus->parent = &mdiodev->dev;
+ snprintf(as->switch_mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&mdiodev->dev));
+
+ if(as->mdio_node) {
+ err = of_mdiobus_register(as->switch_mii_bus, as->mdio_node);
+ if (err)
+ return err;
+ }
+
+ /* initialize defaults */
+ for (i = 0; i < AR7240_MAX_VLANS; i++)
+ as->vlan_id[i] = i;
+
+ as->vlan_table[0] = ar7240sw_port_mask_all(as);
+ ar7240sw_reset(as);
+ ar7240_hw_apply(&as->swdev);
+ rwlock_init(&as->stats_lock);
+ dev_set_drvdata(&mdiodev->dev, as);
+ return 0;
+}
+
+static void
+ag71xx_ar7240_remove(struct mdio_device *mdiodev)
+{
+ struct ar7240sw *as = dev_get_drvdata(&mdiodev->dev);
+ if(as->mdio_node)
+ mdiobus_unregister(as->switch_mii_bus);
+ unregister_switch(&as->swdev);
+}
+
+static const struct of_device_id ag71xx_sw_of_match[] = {
+ { .compatible = "qca,ar8216-builtin" },
+ { .compatible = "qca,ar8229-builtin" },
+ { /* sentinel */ },
+};
+
+static struct mdio_driver ag71xx_sw_driver = {
+ .probe = ag71xx_ar7240_probe,
+ .remove = ag71xx_ar7240_remove,
+ .mdiodrv.driver = {
+ .name = "ag71xx-switch",
+ .of_match_table = ag71xx_sw_of_match,
+ },
+};
+
+mdio_module_driver(ag71xx_sw_driver);
+MODULE_LICENSE("GPL");
diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
index 84b0f9e4ac..79b3e64b16 100644
--- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -1516,6 +1516,7 @@ static int ag71xx_probe(struct platform_device *pdev)
struct resource *res;
struct ag71xx *ag;
const void *mac_addr;
+ const char *ifname = NULL;
u32 max_frame_len;
int tx_size, err;
@@ -1730,6 +1731,9 @@ static int ag71xx_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
+ if(!of_property_read_string(np, "ifname",&ifname))
+ memcpy(dev->name,ifname,strlen(ifname)+1);
+
err = register_netdev(dev);
if (err) {
dev_err(&pdev->dev, "unable to register net device\n");
diff --git a/target/linux/ath79/generic/base-files/etc/board.d/01_leds b/target/linux/ath79/generic/base-files/etc/board.d/01_leds
index 7c5eeb1112..985adfc6a7 100755
--- a/target/linux/ath79/generic/base-files/etc/board.d/01_leds
+++ b/target/linux/ath79/generic/base-files/etc/board.d/01_leds
@@ -204,7 +204,6 @@ etactica,eg200)
ucidef_set_led_netdev "lan" "LAN" "red:eth0" "eth0"
ucidef_set_led_oneshot "modbus" "Modbus" "red:modbus" "100" "33"
;;
-glinet,gl-mifi|\
qxwlan,e600g-v2-8m|\
qxwlan,e600g-v2-16m|\
qxwlan,e600gac-v2-8m|\
diff --git a/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration b/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration
index e9b0fade38..fba1fdcd59 100644
--- a/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration
+++ b/target/linux/ath79/generic/base-files/etc/uci-defaults/04_led_migration
@@ -14,9 +14,6 @@ dlink,dap-1330-a1)
engenius,epg5000)
migrate_leds ":wlan-2g=:wlan2g" ":wlan-5g=:wlan5g"
;;
-glinet,gl-mifi)
- migrate_leds ":net=:3g4g"
- ;;
meraki,mr12)
migrate_leds ":wifi=:link" ":wan=:lan"
;;
diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk
index 5ba64b9803..05ed698401 100644
--- a/target/linux/ath79/image/generic.mk
+++ b/target/linux/ath79/image/generic.mk
@@ -1198,16 +1198,6 @@ define Device/glinet_gl-ar750
endef
TARGET_DEVICES += glinet_gl-ar750
-define Device/glinet_gl-mifi
- SOC := ar9331
- DEVICE_VENDOR := GL.iNET
- DEVICE_MODEL := GL-MiFi
- DEVICE_PACKAGES := kmod-usb-chipidea2
- IMAGE_SIZE := 16000k
- SUPPORTED_DEVICES += gl-mifi
-endef
-TARGET_DEVICES += glinet_gl-mifi
-
define Device/glinet_gl-usb150
SOC := ar9331
DEVICE_VENDOR := GL.iNET
diff --git a/target/linux/ath79/image/nand.mk b/target/linux/ath79/image/nand.mk
index cfc07f14a3..0c6dd699f5 100644
--- a/target/linux/ath79/image/nand.mk
+++ b/target/linux/ath79/image/nand.mk
@@ -107,6 +107,16 @@ define Device/glinet_gl-ar300m-nor
endef
TARGET_DEVICES += glinet_gl-ar300m-nor
+define Device/glinet_gl-mifi
+ SOC := ar9331
+ DEVICE_VENDOR := GL.iNET
+ DEVICE_MODEL := GL-MiFi
+ DEVICE_PACKAGES := kmod-usb-chipidea2
+ IMAGE_SIZE := 16000k
+ SUPPORTED_DEVICES += gl-mifi glinet,gl-mifi
+endef
+TARGET_DEVICES += glinet_gl-mifi
+
define Device/glinet_gl-xe300-common
SOC := qca9531
DEVICE_VENDOR := GL.iNet
diff --git a/target/linux/ath79/nand/base-files/etc/board.d/01_leds b/target/linux/ath79/nand/base-files/etc/board.d/01_leds
index 4a90c4673f..16545083fb 100755
--- a/target/linux/ath79/nand/base-files/etc/board.d/01_leds
+++ b/target/linux/ath79/nand/base-files/etc/board.d/01_leds
@@ -11,6 +11,12 @@ glinet,gl-ar300m-nand|\
glinet,gl-ar300m-nor)
ucidef_set_led_netdev "lan" "LAN" "green:lan" "eth0"
;;
+glinet,gl-mifi)
+ ucidef_set_led_wlan "wlan" "WLAN" "gl-mifi:green:wlan" "phy0tpt"
+ ucidef_set_led_netdev "wan" "WAN" "gl-mifi:green:wan" "eth0"
+ ucidef_set_led_switch "lan" "LAN" "gl-mifi:green:lan" "switch0" "0x2"
+ ucidef_set_led_netdev "3gnet" "3GNET" "gl-mifi:green:net" "3g-wan"
+ ;;
glinet,gl-xe300-iot|\
glinet,gl-xe300-nor|\
glinet,gl-xe300-nor-nand)
diff --git a/target/linux/ath79/nand/base-files/etc/board.d/02_network b/target/linux/ath79/nand/base-files/etc/board.d/02_network
index 17348e001b..73e23e5eb9 100755
--- a/target/linux/ath79/nand/base-files/etc/board.d/02_network
+++ b/target/linux/ath79/nand/base-files/etc/board.d/02_network
@@ -21,6 +21,9 @@ ath79_setup_interfaces()
ucidef_add_switch "switch0" \
"0@eth0" "2:lan:2" "3:lan:1" "1:wan"
;;
+ glinet,gl-mifi)
+ ucidef_set_interfaces_lan_wan "eth1" "eth0"
+ ;;
glinet,gl-xe300-iot|\
glinet,gl-xe300-nor|\
glinet,gl-xe300-nor-nand)
--
2.17.1