[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210217062139.7893-3-dqfext@gmail.com>
Date: Wed, 17 Feb 2021 14:21:39 +0800
From: DENG Qingfang <dqfext@...il.com>
To: Andrew Lunn <andrew@...n.ch>,
Vivien Didelot <vivien.didelot@...il.com>,
Florian Fainelli <f.fainelli@...il.com>,
Vladimir Oltean <olteanv@...il.com>,
"David S. Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>
Cc: netdev@...r.kernel.org, Linus Walleij <linus.walleij@...aro.org>,
Mauri Sandberg <sandberg@...lfence.com>
Subject: [RFC net-next 2/2] net: dsa: add Realtek RTL8366S switch driver
Support Realtek RTL8366S/SR switch
Signed-off-by: DENG Qingfang <dqfext@...il.com>
---
drivers/net/dsa/Kconfig | 1 +
drivers/net/dsa/Makefile | 2 +-
drivers/net/dsa/realtek-smi-core.c | 3 +-
drivers/net/dsa/realtek-smi-core.h | 1 +
drivers/net/dsa/rtl8366s.c | 1165 ++++++++++++++++++++++++++++
5 files changed, 1169 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/dsa/rtl8366s.c
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 3af373e90806..52f1df6ef53a 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -75,6 +75,7 @@ config NET_DSA_REALTEK_SMI
tristate "Realtek SMI Ethernet switch family support"
depends on NET_DSA
select NET_DSA_TAG_RTL4_A
+ select NET_DSA_TAG_RTL8366S
select FIXED_PHY
select IRQ_DOMAIN
select REALTEK_PHY
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index f3598c040994..8c51c25d8378 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
-realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o
+realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o rtl8366s.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c
index 8e49d4f85d48..e0ced416c362 100644
--- a/drivers/net/dsa/realtek-smi-core.c
+++ b/drivers/net/dsa/realtek-smi-core.c
@@ -480,9 +480,8 @@ static const struct of_device_id realtek_smi_of_match[] = {
.data = &rtl8366rb_variant,
},
{
- /* FIXME: add support for RTL8366S and more */
.compatible = "realtek,rtl8366s",
- .data = NULL,
+ .data = &rtl8366s_variant,
},
{ /* sentinel */ },
};
diff --git a/drivers/net/dsa/realtek-smi-core.h b/drivers/net/dsa/realtek-smi-core.h
index fcf465f7f922..a5d45b6f6de3 100644
--- a/drivers/net/dsa/realtek-smi-core.h
+++ b/drivers/net/dsa/realtek-smi-core.h
@@ -143,5 +143,6 @@ int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset);
void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
extern const struct realtek_smi_variant rtl8366rb_variant;
+extern const struct realtek_smi_variant rtl8366s_variant;
#endif /* _REALTEK_SMI_H */
diff --git a/drivers/net/dsa/rtl8366s.c b/drivers/net/dsa/rtl8366s.c
new file mode 100644
index 000000000000..718e492f8bbd
--- /dev/null
+++ b/drivers/net/dsa/rtl8366s.c
@@ -0,0 +1,1165 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Realtek SMI subdriver for the Realtek RTL8366S ethernet switch
+ *
+ * Copyright (C) 2021 DENG, Qingfang <dqfext@...il.com>
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@...nwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@...il.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/regmap.h>
+
+#include "realtek-smi-core.h"
+
+#define RTL8366S_PHY_NO_MAX 4
+#define RTL8366S_PHY_PAGE_MAX 7
+#define RTL8366S_PHY_ADDR_MAX 31
+
+/* Switch Global Configuration register */
+#define RTL8366S_SGCR 0x0000
+#define RTL8366S_SGCR_EN_BC_STORM_CTRL BIT(0)
+#define RTL8366S_SGCR_MAX_LENGTH(_x) (_x << 4)
+#define RTL8366S_SGCR_MAX_LENGTH_MASK RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_MAX_LENGTH_1522 RTL8366S_SGCR_MAX_LENGTH(0x0)
+#define RTL8366S_SGCR_MAX_LENGTH_1536 RTL8366S_SGCR_MAX_LENGTH(0x1)
+#define RTL8366S_SGCR_MAX_LENGTH_1552 RTL8366S_SGCR_MAX_LENGTH(0x2)
+#define RTL8366S_SGCR_MAX_LENGTH_16000 RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_EN_VLAN BIT(13)
+
+/* Port Enable Control register */
+#define RTL8366S_PECR 0x0001
+
+/* Switch Security Control registers */
+#define RTL8366S_SSCR0 0x0002
+#define RTL8366S_SSCR1 0x0003
+#define RTL8366S_SSCR2 0x0004
+#define RTL8366S_SSCR2_DROP_UNKNOWN_DA BIT(0)
+
+/* Port Mode Control registers */
+#define RTL8366S_PMC0 0x0005
+#define RTL8366S_PMC0_SPI BIT(0)
+#define RTL8366S_PMC0_EN_AUTOLOAD BIT(1)
+#define RTL8366S_PMC0_PROBE BIT(2)
+#define RTL8366S_PMC0_DIS_BISR BIT(3)
+#define RTL8366S_PMC0_ADCTEST BIT(4)
+#define RTL8366S_PMC0_SRAM_DIAG BIT(5)
+#define RTL8366S_PMC0_EN_SCAN BIT(6)
+#define RTL8366S_PMC0_P4_IOMODE_MASK GENMASK(9, 7)
+#define RTL8366S_PMC0_P4_IOMODE_PHY_TO_GMAC \
+ FIELD_PREP(RTL8366S_PMC0_P4_IOMODE_MASK, 0)
+#define RTL8366S_PMC0_P4_IOMODE_GMAC_TO_RGMII \
+ FIELD_PREP(RTL8366S_PMC0_P4_IOMODE_MASK, 1)
+#define RTL8366S_PMC0_P4_IOMODE_PHY_TO_RGMII \
+ FIELD_PREP(RTL8366S_PMC0_P4_IOMODE_MASK, 2)
+#define RTL8366S_PMC0_P5_IOMODE_MASK GENMASK(12, 10)
+#define RTL8366S_PMC0_SDSMODE_MASK GENMASK(15, 13)
+#define RTL8366S_PMC1 0x0006
+
+/* Port Mirror Control Register */
+#define RTL8366S_PMCR 0x0007
+#define RTL8366S_PMCR_SOURCE_MASK GENMASK(3, 0)
+#define RTL8366S_PMCR_MINITOR_MASK GENMASK(7, 4)
+#define RTL8366S_PMCR_MIRROR_RX BIT(8)
+#define RTL8366S_PMCR_MIRROR_TX BIT(9)
+#define RTL8366S_PMCR_MIRROR_SPC BIT(10)
+#define RTL8366S_PMCR_MIRROR_ISO BIT(11)
+
+/* Keep format on egress */
+#define RTL8366S_EGRESS_KEEP_FORMAT_REG 0x0008
+#define RTL8366S_EGRESS_KEEP_FORMAT_MASK GENMASK(15, 8)
+#define RTL8366S_EGRESS_KEEP_FORMAT_ALL \
+ FIELD_PREP(RTL8366S_EGRESS_KEEP_FORMAT_MASK, RTL8366S_PORT_ALL)
+
+/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
+#define RTL8366S_GREEN_ETHERNET_CTRL_REG 0x000a
+#define RTL8366S_GREEN_ETHERNET_CTRL_MASK 0x0018
+#define RTL8366S_GREEN_ETHERNET_TX BIT(3)
+#define RTL8366S_GREEN_ETHERNET_RX BIT(4)
+
+/* bits 0..7 = port 0, bits 8..15 = port 1 */
+#define RTL8366S_PAACR0 0x0011
+/* bits 0..7 = port 2, bits 8..15 = port 3 */
+#define RTL8366S_PAACR1 0x0012
+/* bits 0..7 = port 4, bits 8..15 = port 5 */
+#define RTL8366S_PAACR2 0x0013
+#define RTL8366S_PAACR_SPEED_10M 0
+#define RTL8366S_PAACR_SPEED_100M 1
+#define RTL8366S_PAACR_SPEED_1000M 2
+#define RTL8366S_PAACR_FULL_DUPLEX BIT(2)
+#define RTL8366S_PAACR_LINK_UP BIT(4)
+#define RTL8366S_PAACR_TX_PAUSE BIT(5)
+#define RTL8366S_PAACR_RX_PAUSE BIT(6)
+#define RTL8366S_PAACR_AN BIT(7)
+
+#define RTL8366S_PAACR_CPU_PORT (RTL8366S_PAACR_SPEED_1000M | \
+ RTL8366S_PAACR_FULL_DUPLEX | \
+ RTL8366S_PAACR_LINK_UP | \
+ RTL8366S_PAACR_TX_PAUSE | \
+ RTL8366S_PAACR_RX_PAUSE)
+
+/* Spanning Tree Protocol register */
+#define RTL8366S_STP_BASE 0x003a
+#define RTL8366S_STP_REG(_p) \
+ (RTL8366S_STP_BASE + (_p))
+#define RTL8366S_STP_MASK GENMASK(1, 0)
+
+enum RTL8366S_STP_STATE
+{
+ RTL8366S_STP_DISABLED = 0,
+ RTL8366S_STP_BLOCKING,
+ RTL8366S_STP_LEARNING,
+ RTL8366S_STP_FORWARDING,
+};
+
+#define RTL8366S_CPU_CTRL_REG 0x004f
+#define RTL8366S_CPU_DROP_UNKNOWN_DA BIT(14)
+#define RTL8366S_CPU_NO_TAG BIT(15)
+
+#define RTL8366S_PORT_VLAN_CTRL_BASE 0x0058
+#define RTL8366S_PORT_VLAN_CTRL_REG(_p) \
+ (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366S_PORT_VLAN_CTRL_MASK 0xf
+#define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4))
+
+#define RTL8366S_PORT_LINK_STATUS_BASE 0x0060
+#define RTL8366S_PORT_STATUS_SPEED_MASK 0x0003
+#define RTL8366S_PORT_STATUS_DUPLEX_MASK 0x0004
+#define RTL8366S_PORT_STATUS_LINK_MASK 0x0010
+#define RTL8366S_PORT_STATUS_TXPAUSE_MASK 0x0020
+#define RTL8366S_PORT_STATUS_RXPAUSE_MASK 0x0040
+#define RTL8366S_PORT_STATUS_AN_MASK 0x0080
+
+#define RTL8366S_RESET_CTRL_REG 0x0100
+#define RTL8366S_CHIP_CTRL_RESET_HW BIT(0)
+#define RTL8366S_CHIP_CTRL_RESET_SW BIT(1)
+
+#define RTL8366S_CHIP_VERSION_CTRL_REG 0x0104
+#define RTL8366S_CHIP_VERSION_MASK 0xf
+#define RTL8366S_CHIP_ID_REG 0x0105
+#define RTL8366S_CHIP_ID_8366 0x8366
+
+/* VLAN 4K access registers */
+#define RTL8366S_VLAN_TB_CTRL_REG 0x010f
+
+#define RTL8366S_TABLE_ACCESS_CTRL_REG 0x0180
+#define RTL8366S_TABLE_VLAN_READ_CTRL 0x0E01
+#define RTL8366S_TABLE_VLAN_WRITE_CTRL 0x0F01
+
+#define RTL8366S_VLAN_TABLE_READ_BASE 0x018B
+#define RTL8366S_VLAN_TABLE_WRITE_BASE 0x0185
+
+/* VLAN filtering registers */
+#define RTL8366S_VLAN_TAGINGRESS_REG 0x0378
+#define RTL8366S_VLAN_MEMBERINGRESS_REG 0x0379
+
+/* Link aggregation registers */
+#define RTL8366S_LAGCR 0x0380
+#define RTL8366S_LAGCR_MODE_DUMP BIT(0)
+#define RTL8366S_LAGCR_HASHSEL_MASK GENMASK(2, 1)
+#define RTL8366S_LAGCR_HASHSEL_DA_SA \
+ FIELD_PREP(RTL8366S_LAGCR_HASHSEL_MASK, 0)
+#define RTL8366S_LAGCR_HASHSEL_DA \
+ FIELD_PREP(RTL8366S_LAGCR_HASHSEL_MASK, 1)
+#define RTL8366S_LAGCR_HASHSEL_SA \
+ FIELD_PREP(RTL8366S_LAGCR_HASHSEL_MASK, 2)
+#define RTL8366S_LAGCR_PORTMAP_MASK GENMASK(8, 3)
+
+#define RTL8366S_LAG_MAPPING_BASE 0x0381
+#define RTL8366S_LAG_MAPPING_BIT 3
+#define RTL8366S_LAG_MAPPING_MASK GENMASK(2, 0)
+
+#define RTL8366S_LAG_FC_CTRL_REG 0x0383
+#define RTL8366S_LAG_FC_MASK GENMASK(5, 0)
+
+#define RTL8366S_LAG_QEMPTY_REG 0x0384
+#define RTL8366S_LAG_QEMPTY_MASK GENMASK(5, 0)
+
+/* RMA register address */
+#define RTL8366S_RMA_CONTROL_REG 0x0391
+#define RTL8366S_RMA_IGMP BIT(10)
+#define RTL8366S_RMA_MLD BIT(11)
+#define RTL8366S_RMA_USER_DEFINED_BASE 0x0392
+
+/* LED control registers */
+#define RTL8366S_LED_BLINKRATE_REG 0x0420
+#define RTL8366S_LED_BLINKRATE_BIT 0
+#define RTL8366S_LED_BLINKRATE_MASK 0x0007
+
+#define RTL8366S_LED_CTRL_REG 0x0421
+#define RTL8366S_LED_0_1_CTRL_REG 0x0422
+#define RTL8366S_LED_2_3_CTRL_REG 0x0423
+
+#define RTL8366S_MIB_COUNT 33
+#define RTL8366S_GLOBAL_MIB_COUNT 1
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET 0x0040
+#define RTL8366S_MIB_COUNTER_BASE 0x1000
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET2 0x0008
+#define RTL8366S_MIB_COUNTER_BASE2 0x1180
+#define RTL8366S_MIB_CTRL_REG 0x11F0
+#define RTL8366S_MIB_CTRL_USER_MASK 0x01FF
+#define RTL8366S_MIB_CTRL_BUSY_MASK 0x0001
+#define RTL8366S_MIB_CTRL_RESET_MASK 0x0002
+
+#define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK 0x0004
+#define RTL8366S_MIB_CTRL_PORT_RESET_BIT 0x0003
+#define RTL8366S_MIB_CTRL_PORT_RESET_MASK 0x01FC
+
+
+#define RTL8366S_VLAN_MC_BASE(_x) (0x0016 + (_x) * 2)
+
+#define RTL8366S_MAC_FORCE_CTRL0_REG 0x0F04
+#define RTL8366S_MAC_FORCE_CTRL1_REG 0x0F05
+
+/* PHY registers control */
+#define RTL8366S_PHY_ACCESS_CTRL_REG 0x8028
+#define RTL8366S_PHY_ACCESS_DATA_REG 0x8029
+
+#define RTL8366S_PHY_CTRL_READ 1
+#define RTL8366S_PHY_CTRL_WRITE 0
+
+#define RTL8366S_PORT_NUM_CPU 5
+#define RTL8366S_NUM_PORTS 6
+#define RTL8366S_NUM_VLANS 16
+#define RTL8366S_NUM_LEDGROUPS 4
+#define RTL8366S_NUM_VIDS 4096
+#define RTL8366S_PRIORITYMAX 7
+#define RTL8366S_FIDMAX 7
+
+
+#define RTL8366S_PORT_1 BIT(0) /* In userspace port 0 */
+#define RTL8366S_PORT_2 BIT(1) /* In userspace port 1 */
+#define RTL8366S_PORT_3 BIT(2) /* In userspace port 2 */
+#define RTL8366S_PORT_4 BIT(3) /* In userspace port 3 */
+#define RTL8366S_PORT_5 BIT(4) /* In userspace port 4 */
+#define RTL8366S_PORT_CPU BIT(5) /* CPU port */
+
+#define RTL8366S_PORT_ALL (RTL8366S_PORT_1 | \
+ RTL8366S_PORT_2 | \
+ RTL8366S_PORT_3 | \
+ RTL8366S_PORT_4 | \
+ RTL8366S_PORT_5 | \
+ RTL8366S_PORT_CPU)
+
+#define RTL8366S_PORT_ALL_BUT_CPU (RTL8366S_PORT_1 | \
+ RTL8366S_PORT_2 | \
+ RTL8366S_PORT_3 | \
+ RTL8366S_PORT_4 | \
+ RTL8366S_PORT_5)
+
+#define RTL8366S_PORT_ALL_EXTERNAL RTL8366S_PORT_ALL_BUT_CPU
+
+#define RTL8366S_PORT_ALL_INTERNAL RTL8366S_PORT_CPU
+
+#define RTL8366S_VLAN_VID_MASK 0xfff
+#define RTL8366S_VLAN_PRIORITY_SHIFT 12
+#define RTL8366S_VLAN_PRIORITY_MASK 0x7
+#define RTL8366S_VLAN_MEMBER_MASK 0x3f
+#define RTL8366S_VLAN_UNTAG_SHIFT 6
+#define RTL8366S_VLAN_UNTAG_MASK 0x3f
+#define RTL8366S_VLAN_FID_SHIFT 12
+#define RTL8366S_VLAN_FID_MASK 0x7
+
+/* Green Ethernet Feature for PHY ports */
+#define RTL8366S_PHY_POWER_SAVING_CTRL_REG 0x000c
+#define RTL8366S_PHY_POWER_SAVING_MASK 0x1000
+
+#define RTL8366S_PHY_REG_MASK 0x001f
+#define RTL8366S_PHY_PAGE_OFFSET 5
+#define RTL8366S_PHY_PAGE_MASK GENMASK(7, 5)
+#define RTL8366S_PHY_NO_OFFSET 9
+#define RTL8366S_PHY_NO_MASK GENMASK(13, 9)
+
+#define RTL8366S_MIB_RXB_ID 0 /* IfInOctets */
+#define RTL8366S_MIB_TXB_ID 20 /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366s_mib_counters[] = {
+ { 0, 0, 4, "IfInOctets" },
+ { 0, 4, 4, "EtherStatsOctets" },
+ { 0, 8, 2, "EtherStatsUnderSizePkts" },
+ { 0, 10, 2, "EtherFragments" },
+ { 0, 12, 2, "EtherStatsPkts64Octets" },
+ { 0, 14, 2, "EtherStatsPkts65to127Octets" },
+ { 0, 16, 2, "EtherStatsPkts128to255Octets" },
+ { 0, 18, 2, "EtherStatsPkts256to511Octets" },
+ { 0, 20, 2, "EtherStatsPkts512to1023Octets" },
+ { 0, 22, 2, "EtherStatsPkts1024to1518Octets" },
+ { 0, 24, 2, "EtherOversizeStats" },
+ { 0, 26, 2, "EtherStatsJabbers" },
+ { 0, 28, 2, "IfInUcastPkts" },
+ { 0, 30, 2, "EtherStatsMulticastPkts" },
+ { 0, 32, 2, "EtherStatsBroadcastPkts" },
+ { 0, 34, 2, "EtherStatsDropEvents" },
+ { 0, 36, 2, "Dot3StatsFCSErrors" },
+ { 0, 38, 2, "Dot3StatsSymbolErrors" },
+ { 0, 40, 2, "Dot3InPauseFrames" },
+ { 0, 42, 2, "Dot3ControlInUnknownOpcodes" },
+ { 0, 44, 4, "IfOutOctets" },
+ { 0, 48, 2, "Dot3StatsSingleCollisionFrames" },
+ { 0, 50, 2, "Dot3StatMultipleCollisionFrames" },
+ { 0, 52, 2, "Dot3sDeferredTransmissions" },
+ { 0, 54, 2, "Dot3StatsLateCollisions" },
+ { 0, 56, 2, "EtherStatsCollisions" },
+ { 0, 58, 2, "Dot3StatsExcessiveCollisions" },
+ { 0, 60, 2, "Dot3OutPauseFrames" },
+ { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards" },
+
+ /*
+ * The following counters are accessible at a different
+ * base address.
+ */
+ { 1, 0, 2, "Dot1dTpPortInDiscards" },
+ { 1, 2, 2, "IfOutUcastPkts" },
+ { 1, 4, 2, "IfOutMulticastPkts" },
+ { 1, 6, 2, "IfOutBroadcastPkts" },
+};
+
+static int rtl8366s_get_mib_counter(struct realtek_smi *smi,
+ int port,
+ struct rtl8366_mib_counter *mib,
+ u64 *mibvalue)
+{
+ u32 addr, val;
+ int ret;
+ int i;
+
+ switch (mib->base) {
+ case 0:
+ addr = RTL8366S_MIB_COUNTER_BASE +
+ RTL8366S_MIB_COUNTER_PORT_OFFSET * port;
+ break;
+ case 1:
+ addr = RTL8366S_MIB_COUNTER_BASE2 +
+ RTL8366S_MIB_COUNTER_PORT_OFFSET2 * port;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ addr += mib->offset;
+
+ /*
+ * Writing access counter address first
+ * then ASIC will prepare 64bits counter wait for being retrived
+ */
+ ret = regmap_write(smi->map, addr, 0);
+ if (ret)
+ return ret;
+
+ /* read MIB control register */
+ ret = regmap_read(smi->map, RTL8366S_MIB_CTRL_REG, &val);
+ if (ret)
+ return -EIO;
+
+ if (val & RTL8366S_MIB_CTRL_BUSY_MASK)
+ return -EBUSY;
+
+ if (val & RTL8366S_MIB_CTRL_RESET_MASK)
+ return -EIO;
+
+ /* Read each individual MIB 16 bits at the time */
+ *mibvalue = 0;
+ for (i = mib->length; i > 0; i--) {
+ ret = regmap_read(smi->map, addr + (i - 1), &val);
+ if (ret)
+ return ret;
+ *mibvalue = (*mibvalue << 16) | (val & 0xFFFF);
+ }
+ return 0;
+}
+
+static int rtl8366s_phy_read(struct realtek_smi *smi, int phy, int regnum)
+{
+ u32 val;
+ u32 reg;
+ int ret;
+
+ if (phy > RTL8366S_PHY_NO_MAX)
+ return -EINVAL;
+
+ ret = regmap_write(smi->map, RTL8366S_PHY_ACCESS_CTRL_REG,
+ RTL8366S_PHY_CTRL_READ);
+ if (ret)
+ return ret;
+
+ reg = 0x8000 | (1 << (phy + RTL8366S_PHY_NO_OFFSET)) | regnum;
+
+ ret = regmap_write(smi->map, reg, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(smi->map, RTL8366S_PHY_ACCESS_DATA_REG, &val);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static int rtl8366s_phy_write(struct realtek_smi *smi, int phy, int regnum,
+ u16 val)
+{
+ u32 reg;
+ int ret;
+
+ if (phy > RTL8366S_PHY_NO_MAX)
+ return -EINVAL;
+
+ ret = regmap_write(smi->map, RTL8366S_PHY_ACCESS_CTRL_REG,
+ RTL8366S_PHY_CTRL_WRITE);
+ if (ret)
+ return ret;
+
+ reg = 0x8000 | (1 << (phy + RTL8366S_PHY_NO_OFFSET)) | regnum;
+
+ ret = regmap_write(smi->map, reg, val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8366s_reset_chip(struct realtek_smi *smi)
+{
+ int timeout = 10;
+ u32 val;
+ int ret;
+
+ realtek_smi_write_reg_noack(smi, RTL8366S_RESET_CTRL_REG,
+ RTL8366S_CHIP_CTRL_RESET_HW);
+ do {
+ usleep_range(20000, 25000);
+ ret = regmap_read(smi->map, RTL8366S_RESET_CTRL_REG, &val);
+ if (ret)
+ return ret;
+
+ if (!(val & RTL8366S_CHIP_CTRL_RESET_HW))
+ break;
+ } while (--timeout);
+
+ if (!timeout) {
+ dev_err(smi->dev, "timeout waiting for the switch to reset\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static enum dsa_tag_protocol rtl8366s_get_tag_protocol(struct dsa_switch *ds,
+ int port,
+ enum dsa_tag_protocol mp)
+{
+ return DSA_TAG_PROTO_RTL8366S;
+}
+
+static void
+rtl8366s_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
+ phy_interface_t interface, struct phy_device *phydev,
+ int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ struct realtek_smi *smi = ds->priv;
+ int ret;
+
+ if (port != RTL8366S_PORT_NUM_CPU)
+ return;
+
+ /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */
+ ret = regmap_update_bits(smi->map, RTL8366S_MAC_FORCE_CTRL1_REG,
+ BIT(port), BIT(port));
+ if (ret) {
+ dev_err(smi->dev, "failed to force 1Gbit on CPU port\n");
+ return;
+ }
+
+ ret = regmap_update_bits(smi->map, RTL8366S_PAACR2,
+ 0xFF00U,
+ RTL8366S_PAACR_CPU_PORT << 8);
+ if (ret) {
+ dev_err(smi->dev, "failed to set PAACR on CPU port\n");
+ return;
+ }
+
+ /* Enable the CPU port */
+ ret = regmap_update_bits(smi->map, RTL8366S_PECR, BIT(port),
+ 0);
+ if (ret) {
+ dev_err(smi->dev, "failed to enable the CPU port\n");
+ return;
+ }
+}
+
+static void
+rtl8366s_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct realtek_smi *smi = ds->priv;
+ int ret;
+
+ if (port != RTL8366S_PORT_NUM_CPU)
+ return;
+
+ /* Disable the CPU port */
+ ret = regmap_update_bits(smi->map, RTL8366S_PECR, BIT(port),
+ BIT(port));
+ if (ret) {
+ dev_err(smi->dev, "failed to disable the CPU port\n");
+ return;
+ }
+}
+
+static int rtl8366s_setup(struct dsa_switch *ds)
+{
+ struct realtek_smi *smi = ds->priv;
+ int ret;
+
+ /* Reset chip */
+ ret = rtl8366s_reset_chip(smi);
+ if (ret)
+ return ret;
+
+ /* Set up the "green ethernet" feature */
+ ret = regmap_update_bits(smi->map, RTL8366S_GREEN_ETHERNET_CTRL_REG,
+ RTL8366S_GREEN_ETHERNET_CTRL_MASK,
+ RTL8366S_GREEN_ETHERNET_TX |
+ RTL8366S_GREEN_ETHERNET_RX);
+ if (ret)
+ return ret;
+
+ /* Enable CPU port with custom tag 8899 */
+ ret = regmap_write(smi->map, RTL8366S_CPU_CTRL_REG,
+ RTL8366S_PORT_CPU);
+ if (ret)
+ return ret;
+
+ /* Make sure we default-enable the fixed CPU port */
+ ret = regmap_update_bits(smi->map, RTL8366S_PECR,
+ RTL8366S_PORT_CPU, 0);
+ if (ret)
+ return ret;
+
+ /* Enable learning for all ports */
+ ret = regmap_write(smi->map, RTL8366S_SSCR0, 0);
+ if (ret)
+ return ret;
+
+ /* Enable auto ageing for all ports */
+ ret = regmap_write(smi->map, RTL8366S_SSCR1, 0);
+ if (ret)
+ return ret;
+
+ /* Don't drop packets whose DA has not been learned */
+ ret = regmap_update_bits(smi->map, RTL8366S_SSCR2,
+ RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
+ if (ret)
+ return ret;
+
+ /* Connect Port 4 PHY to RGMII
+ * TODO: Make it configurable in DTS
+ */
+ ret = regmap_update_bits(smi->map, RTL8366S_PMC0,
+ RTL8366S_PMC0_P4_IOMODE_MASK,
+ RTL8366S_PMC0_P4_IOMODE_PHY_TO_RGMII);
+ if (ret)
+ return ret;
+
+ /* Set up port-based VLAN */
+ ret = rtl8366_init_vlan(smi);
+ if (ret)
+ return ret;
+
+ /* Keep original tagged/untagged on egress */
+ ret = regmap_update_bits(smi->map,
+ RTL8366S_EGRESS_KEEP_FORMAT_REG,
+ RTL8366S_EGRESS_KEEP_FORMAT_MASK,
+ RTL8366S_EGRESS_KEEP_FORMAT_ALL);
+ if (ret)
+ return ret;
+
+ /* Enable Link Aggregation "Dump" mode. The switch will
+ * automatically set hash value mapping to LAG ports.
+ */
+ ret = regmap_write(smi->map, RTL8366S_LAGCR,
+ RTL8366S_LAGCR_MODE_DUMP);
+ if (ret)
+ return ret;
+
+ ret = realtek_smi_setup_mdio(smi);
+ if (ret) {
+ dev_info(smi->dev, "could not set up MDIO bus\n");
+ return -ENODEV;
+ }
+
+ ds->mtu_enforcement_ingress = true;
+
+ return 0;
+}
+
+static int rtl8366s_get_vlan_4k(struct realtek_smi *smi, u32 vid,
+ struct rtl8366_vlan_4k *vlan4k)
+{
+ u32 data[2];
+ int ret;
+ int i;
+
+ memset(vlan4k, 0, sizeof(struct rtl8366_vlan_4k));
+
+ if (vid >= RTL8366S_NUM_VIDS)
+ return -EINVAL;
+
+ /* write VID */
+ ret = regmap_write(smi->map, RTL8366S_VLAN_TABLE_WRITE_BASE,
+ vid & RTL8366S_VLAN_VID_MASK);
+ if (ret)
+ return ret;
+
+ /* write table access control word */
+ ret = regmap_write(smi->map, RTL8366S_TABLE_ACCESS_CTRL_REG,
+ RTL8366S_TABLE_VLAN_READ_CTRL);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_read(smi->map,
+ RTL8366S_VLAN_TABLE_READ_BASE + i,
+ &data[i]);
+ if (ret)
+ return ret;
+ }
+
+ vlan4k->vid = vid;
+ vlan4k->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+ RTL8366S_VLAN_UNTAG_MASK;
+ vlan4k->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+ vlan4k->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+ RTL8366S_VLAN_FID_MASK;
+
+ return 0;
+}
+
+static int rtl8366s_set_vlan_4k(struct realtek_smi *smi,
+ const struct rtl8366_vlan_4k *vlan4k)
+{
+ u32 data[2];
+ int ret;
+ int i;
+
+ if (vlan4k->vid >= RTL8366S_NUM_VIDS ||
+ vlan4k->member > RTL8366S_VLAN_MEMBER_MASK ||
+ vlan4k->untag > RTL8366S_VLAN_UNTAG_MASK ||
+ vlan4k->fid > RTL8366S_FIDMAX)
+ return -EINVAL;
+
+ data[0] = vlan4k->vid & RTL8366S_VLAN_VID_MASK;
+ data[1] = (vlan4k->member & RTL8366S_VLAN_MEMBER_MASK) |
+ ((vlan4k->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+ RTL8366S_VLAN_UNTAG_SHIFT) |
+ ((vlan4k->fid & RTL8366S_VLAN_FID_MASK) <<
+ RTL8366S_VLAN_FID_SHIFT);
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_write(smi->map,
+ RTL8366S_VLAN_TABLE_WRITE_BASE + i,
+ data[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* write table access control word */
+ ret = regmap_write(smi->map, RTL8366S_TABLE_ACCESS_CTRL_REG,
+ RTL8366S_TABLE_VLAN_WRITE_CTRL);
+
+ return ret;
+}
+
+static int rtl8366s_get_vlan_mc(struct realtek_smi *smi, u32 index,
+ struct rtl8366_vlan_mc *vlanmc)
+{
+ u32 data[2];
+ int ret;
+ int i;
+
+ memset(vlanmc, 0, sizeof(struct rtl8366_vlan_mc));
+
+ if (index >= RTL8366S_NUM_VLANS)
+ return -EINVAL;
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_read(smi->map,
+ RTL8366S_VLAN_MC_BASE(index) + i,
+ &data[i]);
+ if (ret)
+ return ret;
+ }
+
+ vlanmc->vid = data[0] & RTL8366S_VLAN_VID_MASK;
+ vlanmc->priority = (data[0] >> RTL8366S_VLAN_PRIORITY_SHIFT) &
+ RTL8366S_VLAN_PRIORITY_MASK;
+ vlanmc->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+ RTL8366S_VLAN_UNTAG_MASK;
+ vlanmc->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+ vlanmc->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+ RTL8366S_VLAN_FID_MASK;
+
+ return 0;
+}
+
+static int rtl8366s_set_vlan_mc(struct realtek_smi *smi, u32 index,
+ const struct rtl8366_vlan_mc *vlanmc)
+{
+ u32 data[2];
+ int ret;
+ int i;
+
+ if (index >= RTL8366S_NUM_VLANS ||
+ vlanmc->vid >= RTL8366S_NUM_VIDS ||
+ vlanmc->priority > RTL8366S_PRIORITYMAX ||
+ vlanmc->member > RTL8366S_VLAN_MEMBER_MASK ||
+ vlanmc->untag > RTL8366S_VLAN_UNTAG_MASK ||
+ vlanmc->fid > RTL8366S_FIDMAX)
+ return -EINVAL;
+
+ data[0] = (vlanmc->vid & RTL8366S_VLAN_VID_MASK) |
+ ((vlanmc->priority & RTL8366S_VLAN_PRIORITY_MASK) <<
+ RTL8366S_VLAN_PRIORITY_SHIFT);
+ data[1] = (vlanmc->member & RTL8366S_VLAN_MEMBER_MASK) |
+ ((vlanmc->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+ RTL8366S_VLAN_UNTAG_SHIFT) |
+ ((vlanmc->fid & RTL8366S_VLAN_FID_MASK) <<
+ RTL8366S_VLAN_FID_SHIFT);
+
+ for (i = 0; i < 2; i++) {
+ ret = regmap_write(smi->map,
+ RTL8366S_VLAN_MC_BASE(index) + i,
+ data[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtl8366s_get_mc_index(struct realtek_smi *smi, int port, int *val)
+{
+ u32 data;
+ int ret;
+
+ if (port >= RTL8366S_NUM_PORTS)
+ return -EINVAL;
+
+ ret = regmap_read(smi->map, RTL8366S_PORT_VLAN_CTRL_REG(port),
+ &data);
+ if (ret)
+ return ret;
+
+ *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) &
+ RTL8366S_PORT_VLAN_CTRL_MASK;
+
+ return 0;
+}
+
+static int rtl8366s_set_mc_index(struct realtek_smi *smi, int port, int index)
+{
+ if (port >= RTL8366S_NUM_PORTS || index >= RTL8366S_NUM_VLANS)
+ return -EINVAL;
+
+ return regmap_update_bits(smi->map, RTL8366S_PORT_VLAN_CTRL_REG(port),
+ RTL8366S_PORT_VLAN_CTRL_MASK <<
+ RTL8366S_PORT_VLAN_CTRL_SHIFT(port),
+ (index & RTL8366S_PORT_VLAN_CTRL_MASK) <<
+ RTL8366S_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366s_enable_vlan(struct realtek_smi *smi, bool enable)
+{
+ return regmap_update_bits(smi->map, RTL8366S_SGCR, RTL8366S_SGCR_EN_VLAN,
+ enable ? RTL8366S_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366s_enable_vlan4k(struct realtek_smi *smi, bool enable)
+{
+ return regmap_update_bits(smi->map, RTL8366S_VLAN_TB_CTRL_REG,
+ 1, enable);
+}
+
+static bool rtl8366s_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan)
+{
+ unsigned int max = RTL8366S_NUM_VLANS;
+
+ if (smi->vlan4k_enabled)
+ max = RTL8366S_NUM_VIDS - 1;
+
+ return vlan && vlan < max;
+}
+
+static int
+rtl8366s_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct realtek_smi *smi = ds->priv;
+
+ return regmap_update_bits(smi->map, RTL8366S_PECR, BIT(port), 0);
+}
+
+static void
+rtl8366s_port_disable(struct dsa_switch *ds, int port)
+{
+ struct realtek_smi *smi = ds->priv;
+
+ regmap_update_bits(smi->map, RTL8366S_PECR, BIT(port), BIT(port));
+}
+
+static int
+rtl8366s_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int port_mask = 0;
+ int i;
+
+ for (i = 0; i < RTL8366S_NUM_PORTS; i++) {
+ unsigned int mask;
+ int ret;
+
+ if (dsa_to_port(ds, i)->bridge_dev != bridge)
+ continue;
+
+ /* Add this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+ mask = BIT(port);
+ mask |= mask << RTL8366S_VLAN_UNTAG_SHIFT;
+ ret = regmap_update_bits(smi->map,
+ RTL8366S_VLAN_MC_BASE(i) + 1,
+ mask, mask);
+ if (ret)
+ return ret;
+
+ port_mask |= BIT(i);
+ }
+
+ /* Add all other ports to this ports portvlan mask */
+ port_mask |= port_mask << RTL8366S_VLAN_UNTAG_SHIFT;
+ return regmap_update_bits(smi->map, RTL8366S_VLAN_MC_BASE(port) + 1,
+ port_mask, port_mask);
+}
+
+static void
+rtl8366s_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int port_mask;
+ int i;
+
+ for (i = 0; i < RTL8366S_NUM_PORTS; i++) {
+ unsigned int mask;
+
+ if (dsa_to_port(ds, i)->bridge_dev != bridge)
+ continue;
+
+ /* Remove this port from the portvlan mask of the other
+ * ports in the bridge
+ */
+ mask = BIT(port);
+ mask |= mask << RTL8366S_VLAN_UNTAG_SHIFT;
+ regmap_update_bits(smi->map,
+ RTL8366S_VLAN_MC_BASE(i) + 1,
+ mask, 0);
+ }
+
+ /* Set the cpu port to be the only one else in the portvlan mask
+ * of this port
+ */
+ port_mask = BIT(port) | BIT(RTL8366S_PORT_NUM_CPU);
+ port_mask |= port_mask << RTL8366S_VLAN_UNTAG_SHIFT;
+ regmap_write(smi->map, RTL8366S_VLAN_MC_BASE(port) + 1,
+ port_mask);
+}
+
+static void
+rtl8366s_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct realtek_smi *smi = ds->priv;
+ enum RTL8366S_STP_STATE val;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ val = RTL8366S_STP_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ case BR_STATE_LISTENING:
+ val = RTL8366S_STP_BLOCKING;
+ break;
+ case BR_STATE_LEARNING:
+ val = RTL8366S_STP_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ val = RTL8366S_STP_FORWARDING;
+ break;
+ }
+
+ /* The HW supports 8 MSTIs but we only use 0 */
+ regmap_update_bits(smi->map, RTL8366S_STP_REG(port),
+ RTL8366S_STP_MASK, val);
+}
+
+static int rtl8366s_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int val;
+ int ret;
+
+ /* Enable/Disable VLAN ingress filtering */
+ val = BIT(port);
+ ret = regmap_update_bits(smi->map, RTL8366S_VLAN_MEMBERINGRESS_REG,
+ val, vlan_filtering ? val : 0);
+ if (ret)
+ return ret;
+
+ /* Disable/Enable keep original tagged/untagged */
+ val = FIELD_PREP(RTL8366S_EGRESS_KEEP_FORMAT_MASK, val);
+ ret = regmap_update_bits(smi->map, RTL8366S_EGRESS_KEEP_FORMAT_REG,
+ val, vlan_filtering ? 0 : val);
+ if (ret)
+ return ret;
+
+ return rtl8366_vlan_filtering(ds, port, vlan_filtering);
+}
+
+static int rtl8366s_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int val, dir;
+ int ret;
+
+ /* The source and the monitor port cannot be the same */
+ if (port == mirror->to_local_port)
+ return -EOPNOTSUPP;
+
+ ret = regmap_read(smi->map, RTL8366S_PMCR, &val);
+ if (ret)
+ return ret;
+
+ dir = ingress ? RTL8366S_PMCR_MIRROR_RX : RTL8366S_PMCR_MIRROR_TX;
+ /* RTL8366S only supports one port mirror set */
+ if (val & dir)
+ return -EEXIST;
+
+ /* If the other direction is active, allow setting up both
+ * directions for the same source and monitor ports
+ */
+ if (val & (RTL8366S_PMCR_MIRROR_RX | RTL8366S_PMCR_MIRROR_TX)) {
+ int source, monitor;
+
+ source = FIELD_GET(RTL8366S_PMCR_SOURCE_MASK, val);
+ monitor = FIELD_GET(RTL8366S_PMCR_MINITOR_MASK, val);
+
+ if (source != port || monitor != mirror->to_local_port)
+ return -EEXIST;
+ } else {
+ val &= ~RTL8366S_PMCR_SOURCE_MASK;
+ val |= FIELD_PREP(RTL8366S_PMCR_SOURCE_MASK, port);
+ val &= ~RTL8366S_PMCR_MINITOR_MASK;
+ val |= FIELD_PREP(RTL8366S_PMCR_MINITOR_MASK,
+ mirror->to_local_port);
+ }
+
+ val |= dir;
+
+ return regmap_write(smi->map, RTL8366S_PMCR, val);
+}
+
+static void rtl8366s_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int dir;
+
+ dir = mirror->ingress ? RTL8366S_PMCR_MIRROR_RX : RTL8366S_PMCR_MIRROR_TX;
+ regmap_update_bits(smi->map, RTL8366S_PMCR, dir, 0);
+}
+
+static int rtl8366s_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+ struct realtek_smi *smi = ds->priv;
+ u32 len;
+
+ /* When a new MTU is set, DSA always set the CPU port's MTU to the
+ * largest MTU of the slave ports. Because the switch only has a global
+ * max length register, only allowing CPU port here is enough.
+ */
+ if (port != RTL8366S_PORT_NUM_CPU)
+ return 0;
+
+ /* Includes Ethernet header and FCS length.
+ *
+ * Note that the CPU tag does not count towards its length, the
+ * same reason why the frame must be padded _before_ inserting
+ * the CPU tag on xmit.
+ */
+ new_mtu += ETH_HLEN + ETH_FCS_LEN;
+ if (new_mtu <= 1522)
+ len = RTL8366S_SGCR_MAX_LENGTH_1522;
+ else if (new_mtu <= 1536)
+ len = RTL8366S_SGCR_MAX_LENGTH_1536;
+ else if (new_mtu <= 1552)
+ len = RTL8366S_SGCR_MAX_LENGTH_1552;
+ else
+ len = RTL8366S_SGCR_MAX_LENGTH_16000;
+
+ return regmap_update_bits(smi->map, RTL8366S_SGCR,
+ RTL8366S_SGCR_MAX_LENGTH_MASK,
+ len);
+}
+
+static int rtl8366s_port_max_mtu(struct dsa_switch *ds, int port)
+{
+ return 16000 - ETH_HLEN - ETH_FCS_LEN;
+}
+
+static int rtl8366s_port_lag_change(struct dsa_switch *ds, int port)
+{
+ const struct dsa_port *dp = dsa_to_port(ds, port);
+ struct realtek_smi *smi = ds->priv;
+ unsigned int val;
+
+ val = FIELD_PREP(RTL8366S_LAGCR_PORTMAP_MASK, BIT(port));
+
+ return regmap_update_bits(smi->map, RTL8366S_LAGCR, val,
+ dp->lag_tx_enabled ? val : 0);
+}
+
+static int rtl8366s_port_lag_join(struct dsa_switch *ds, int port,
+ struct net_device *lag,
+ struct netdev_lag_upper_info *info)
+{
+ struct realtek_smi *smi = ds->priv;
+ const struct dsa_port *dp;
+ unsigned int val;
+ int count = 0;
+
+ /* Only supports hash type */
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ return -EOPNOTSUPP;
+
+ list_for_each_entry(dp, dp->dst->ports, list) {
+ if (dp->lag_dev == lag)
+ count++;
+ /* Only supports 1 LAG set */
+ else if (dp->lag_dev)
+ return -EBUSY;
+ }
+
+ /* Only supports maximum LAG member of 4 */
+ if (count >= 4)
+ return -ENOSPC;
+
+ val = FIELD_PREP(RTL8366S_LAGCR_PORTMAP_MASK, BIT(port));
+
+ return regmap_update_bits(smi->map, RTL8366S_LAGCR, val, val);
+}
+
+static int rtl8366s_port_lag_leave(struct dsa_switch *ds, int port,
+ struct net_device *lag)
+{
+ struct realtek_smi *smi = ds->priv;
+ unsigned int val;
+
+ val = FIELD_PREP(RTL8366S_LAGCR_PORTMAP_MASK, BIT(port));
+
+ return regmap_update_bits(smi->map, RTL8366S_LAGCR, val, 0);
+}
+
+static int rtl8366s_detect(struct realtek_smi *smi)
+{
+ struct device *dev = smi->dev;
+ unsigned int chip_ver;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(smi->map, RTL8366S_CHIP_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(dev, "unable to read chip id (%d)\n", ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case RTL8366S_CHIP_ID_8366:
+ break;
+ default:
+ dev_err(dev, "unknown chip id (%04x)\n", chip_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(smi->map, RTL8366S_CHIP_VERSION_CTRL_REG,
+ &chip_ver);
+ if (ret) {
+ dev_err(dev, "unable to read chip version (%d)\n", ret);
+ return ret;
+ }
+
+ chip_ver &= RTL8366S_CHIP_VERSION_MASK;
+ dev_info(dev, "RTL%04x ver. %u chip found\n", chip_id, chip_ver);
+
+ smi->cpu_port = RTL8366S_PORT_NUM_CPU;
+ smi->num_ports = RTL8366S_NUM_PORTS;
+ smi->num_vlan_mc = RTL8366S_NUM_VLANS;
+ smi->mib_counters = rtl8366s_mib_counters;
+ smi->num_mib_counters = ARRAY_SIZE(rtl8366s_mib_counters);
+
+ return 0;
+}
+
+static const struct dsa_switch_ops rtl8366s_switch_ops = {
+ .get_tag_protocol = rtl8366s_get_tag_protocol,
+ .setup = rtl8366s_setup,
+ .phylink_mac_link_down = rtl8366s_mac_link_down,
+ .phylink_mac_link_up = rtl8366s_mac_link_up,
+ .get_strings = rtl8366_get_strings,
+ .get_ethtool_stats = rtl8366_get_ethtool_stats,
+ .get_sset_count = rtl8366_get_sset_count,
+ .port_enable = rtl8366s_port_enable,
+ .port_disable = rtl8366s_port_disable,
+ .port_bridge_join = rtl8366s_port_bridge_join,
+ .port_bridge_leave = rtl8366s_port_bridge_leave,
+ .port_stp_state_set = rtl8366s_port_stp_state_set,
+ .port_vlan_filtering = rtl8366s_port_vlan_filtering,
+ .port_vlan_add = rtl8366_vlan_add,
+ .port_vlan_del = rtl8366_vlan_del,
+ .port_mirror_add = rtl8366s_port_mirror_add,
+ .port_mirror_del = rtl8366s_port_mirror_del,
+ .port_change_mtu = rtl8366s_port_change_mtu,
+ .port_max_mtu = rtl8366s_port_max_mtu,
+ .port_lag_change = rtl8366s_port_lag_change,
+ .port_lag_join = rtl8366s_port_lag_join,
+ .port_lag_leave = rtl8366s_port_lag_leave,
+};
+
+static const struct realtek_smi_ops rtl8366s_smi_ops = {
+ .detect = rtl8366s_detect,
+ .get_mib_counter = rtl8366s_get_mib_counter,
+ .get_vlan_mc = rtl8366s_get_vlan_mc,
+ .set_vlan_mc = rtl8366s_set_vlan_mc,
+ .get_vlan_4k = rtl8366s_get_vlan_4k,
+ .set_vlan_4k = rtl8366s_set_vlan_4k,
+ .get_mc_index = rtl8366s_get_mc_index,
+ .set_mc_index = rtl8366s_set_mc_index,
+ .is_vlan_valid = rtl8366s_is_vlan_valid,
+ .enable_vlan = rtl8366s_enable_vlan,
+ .enable_vlan4k = rtl8366s_enable_vlan4k,
+ .phy_read = rtl8366s_phy_read,
+ .phy_write = rtl8366s_phy_write,
+};
+
+static const struct realtek_smi_variant rtl8366s_variant = {
+ .ds_ops = &rtl8366s_switch_ops,
+ .ops = &rtl8366s_smi_ops,
+ .clk_delay = 10,
+ .cmd_read = 0xa9,
+ .cmd_write = 0xa8,
+};
+EXPORT_SYMBOL_GPL(rtl8366s_variant);
--
2.25.1
Powered by blists - more mailing lists