From 120ee81bb20b8aa01998916c87f763bdcc9e327f Mon Sep 17 00:00:00 2001 From: "GL.iNet-Hongjian.Zhang" 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 +#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>; +}; + +ð0 { + 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"; + }; + }; + }; +}; + +ð1 { + 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 #include / { - compatible = "glinet,gl-mifi", "qca,ar9331"; + compatible = "glinet,gl-mifi", "qca,ar9330"; model = "GL.iNet GL-MiFi"; - aliases { - label-mac-device = ð0; - }; - - 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 = <®_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"; +}; + +ð1 { + status = "okay"; + + mtd-mac-address = <&art 0x0>; + ifname = "eth1"; gmac-config { device = <&gmac>; @@ -131,14 +164,10 @@ }; }; -ð1 { - 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 + * Copyright (c) 2010 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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(®_mutex); + ret = __ar7240sw_reg_read(mii, reg_addr); + mutex_unlock(®_mutex); + + return ret; +} + +static void ar7240sw_reg_write(struct mii_bus *mii, u32 reg_addr, u32 reg_val) +{ + mutex_lock(®_mutex); + __ar7240sw_reg_write(mii, reg_addr, reg_val); + mutex_unlock(®_mutex); +} + +static u32 ar7240sw_reg_rmw(struct mii_bus *mii, u32 reg, u32 mask, u32 val) +{ + u32 t; + + mutex_lock(®_mutex); + t = __ar7240sw_reg_read(mii, reg); + t &= ~mask; + t |= val; + __ar7240sw_reg_write(mii, reg, t); + mutex_unlock(®_mutex); + + return t; +} + +static void ar7240sw_reg_set(struct mii_bus *mii, u32 reg, u32 val) +{ + u32 t; + + mutex_lock(®_mutex); + t = __ar7240sw_reg_read(mii, reg); + t |= val; + __ar7240sw_reg_write(mii, reg, t); + mutex_unlock(®_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(®_mutex); + ret = __ar7240sw_reg_wait(mii, reg, mask, val, timeout); + mutex_unlock(®_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(®_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(®_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(®_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(®_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