mirror of
https://github.com/FUjr/gl-infra-builder.git
synced 2025-12-16 09:10:02 +00:00
1419 lines
37 KiB
Diff
1419 lines
37 KiB
Diff
From 4a3e729b75cb2e9a194dcff40134c30805ff39c6 Mon Sep 17 00:00:00 2001
|
|
From: lincolnzh <hongjian.zhang@gl-inet.com>
|
|
Date: Fri, 11 Nov 2022 14:54:21 +0800
|
|
Subject: [PATCH] add switch driver ag71xx_ar7240
|
|
|
|
---
|
|
target/linux/ath79/dts/qca953x.dtsi | 7 +-
|
|
.../net/ethernet/atheros/ag71xx/Makefile | 1 +
|
|
.../ethernet/atheros/ag71xx/ag71xx_ar7240.c | 1356 +++++++++++++++++
|
|
3 files changed, 1360 insertions(+), 4 deletions(-)
|
|
create mode 100755 target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
|
|
|
|
diff --git a/target/linux/ath79/dts/qca953x.dtsi b/target/linux/ath79/dts/qca953x.dtsi
|
|
index 745c736b74..0745e8d1eb 100644
|
|
--- a/target/linux/ath79/dts/qca953x.dtsi
|
|
+++ b/target/linux/ath79/dts/qca953x.dtsi
|
|
@@ -8,7 +8,7 @@
|
|
#address-cells = <1>;
|
|
#size-cells = <1>;
|
|
|
|
- chosen {
|
|
+ bootargs: chosen {
|
|
bootargs = "console=ttyS0,115200n8";
|
|
};
|
|
|
|
@@ -245,14 +245,13 @@
|
|
builtin-switch;
|
|
|
|
builtin_switch: switch0@1f {
|
|
- compatible = "qca,ar8229";
|
|
+ compatible = "qca,ar8229-builtin";
|
|
|
|
reg = <0x1f>;
|
|
resets = <&rst 8>;
|
|
reset-names = "switch";
|
|
phy-mode = "gmii";
|
|
- qca,phy4-mii-enable;
|
|
- qca,mib-poll-interval = <500>;
|
|
+ phy4-mii-enable;
|
|
|
|
mdio-bus {
|
|
#address-cells = <1>;
|
|
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..5e3c9f6551 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..f9ab762f84
|
|
--- /dev/null
|
|
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
|
|
@@ -0,0 +1,1356 @@
|
|
+/*
|
|
+ * 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(®_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 device_node *np = mdiodev->dev.of_node;
|
|
+ struct mii_bus *mii = mdiodev->bus;
|
|
+ struct ar7240sw *as;
|
|
+ struct switch_dev *swdev;
|
|
+ struct reset_control *switch_reset;
|
|
+ u32 ctrl;
|
|
+ phy_interface_t phy_if_mode;
|
|
+ int 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";
|
|
+ err = of_get_phy_mode(np,&phy_if_mode);
|
|
+ if (err) {
|
|
+ pr_err("%s: get PHY mode error\n",
|
|
+ dev_name(&mdiodev->dev));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ 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");
|
|
--
|
|
2.17.1
|
|
|