linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch
@ 2021-04-22  9:42 Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
                   ` (8 more replies)
  0 siblings, 9 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

LAN937x is a Multi-Port 100BASE-T1 Ethernet Physical Layer switch  
compliant with the IEEE 802.3bw-2015 specification. The device  
provides 100 Mbit/s transmit and receive capability over a single 
Unshielded Twisted Pair (UTP) cable. LAN937x is successive revision 
of KSZ series switch. This series of patches provide the DSA driver  
support for Microchip LAN937X switch and it configures through  
SPI interface. 

This driver shares some of the functions from KSZ common 
layer. 

The LAN937x switch series family consists of following SKUs: 

LAN9370: 
  - 4 T1 Phys 
  - 1 RGMII port 

LAN9371: 
  - 3 T1 Phys & 1 TX Phy 
  - 2 RGMII ports 

LAN9372: 
  - 5 T1 Phys & 1 TX Phy 
  - 2 RGMII ports 

LAN9373: 
  - 5 T1 Phys 
  - 2 RGMII & 1 SGMII port 

LAN9374: 
  - 6 T1 Phys 
  - 2 RGMII ports 

More support will be added at a later stage. 

Changes in v2: 
- return check for register read/writes
- dt compatible compatible check is added against chip id value 
- lan937x_internal_t1_tx_phy_write() is renamed to 
  lan937x_internal_phy_write()
- lan937x_is_internal_tx_phy_port is renamed to 
  lan937x_is_internal_100BTX_phy_port as it is 100Base-Tx phy
- Return value for lan937x_internal_phy_write() is -EOPNOTSUPP 
  in case of failures 
- Return value for lan937x_internal_phy_read() is 0xffff 
  for non existent phy 
- cpu_port checking is removed from lan937x_port_stp_state_set()
- lan937x_phy_link_validate: 100baseT_Full to 100baseT1_Full
- T1 Phy driver is moved to drivers/net/phy/microchip_t1.c 
- Tx phy driver support will be added later 
- Legacy switch checkings in dts file are removed.
- tag_ksz.c: Re-used ksz9477_rcv for lan937x_rcv 
- tag_ksz.c: Xmit() & rcv() Comments are corrected w.r.to host
- net/dsa/Kconfig: Family skew numbers altered in ascending order
- microchip,lan937x.yaml: eth is replaced with ethernet
- microchip,lan937x.yaml: spi1 is replaced with spi 
- microchip,lan937x.yaml: cpu labelling is removed 
- microchip,lan937x.yaml: port@x value will match the reg value now

Prasanna Vengateshan (9):
  dt-bindings: net: dsa: dt bindings for microchip lan937x
  net: phy: Add support for LAN937x T1 phy driver
  net: dsa: tag_ksz: add tag handling for Microchip LAN937x
  net: dsa: microchip: add DSA support for microchip lan937x
  net: dsa: microchip: add support for phylink management
  net: dsa: microchip: add support for ethtool port counters
  net: dsa: microchip: add support for port mirror operations
  net: dsa: microchip: add support for fdb and mdb management
  net: dsa: microchip: add support for vlan operations

 .../bindings/net/dsa/microchip,lan937x.yaml   |  142 ++
 MAINTAINERS                                   |    1 +
 drivers/net/dsa/microchip/Kconfig             |   12 +
 drivers/net/dsa/microchip/Makefile            |    5 +
 drivers/net/dsa/microchip/ksz_common.h        |    5 +
 drivers/net/dsa/microchip/lan937x_dev.c       |  674 +++++++++
 drivers/net/dsa/microchip/lan937x_dev.h       |   68 +
 drivers/net/dsa/microchip/lan937x_main.c      | 1204 +++++++++++++++++
 drivers/net/dsa/microchip/lan937x_reg.h       |  710 ++++++++++
 drivers/net/dsa/microchip/lan937x_spi.c       |  226 ++++
 drivers/net/phy/microchip_t1.c                |  361 ++++-
 include/net/dsa.h                             |    2 +
 net/dsa/Kconfig                               |    4 +-
 net/dsa/tag_ksz.c                             |   58 +
 14 files changed, 3409 insertions(+), 63 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
 create mode 100644 drivers/net/dsa/microchip/lan937x_dev.c
 create mode 100644 drivers/net/dsa/microchip/lan937x_dev.h
 create mode 100644 drivers/net/dsa/microchip/lan937x_main.c
 create mode 100644 drivers/net/dsa/microchip/lan937x_reg.h
 create mode 100644 drivers/net/dsa/microchip/lan937x_spi.c

-- 
2.27.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 15:30   ` Rob Herring
  2021-04-22 17:38   ` Rob Herring
  2021-04-22  9:42 ` [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver Prasanna Vengateshan
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Documentation in .yaml format and updates to the MAINTAINERS
Also 'make dt_binding_check' is passed

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 .../bindings/net/dsa/microchip,lan937x.yaml   | 142 ++++++++++++++++++
 MAINTAINERS                                   |   1 +
 2 files changed, 143 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml

diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
new file mode 100644
index 000000000000..22128a52d699
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
@@ -0,0 +1,142 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/microchip,lan937x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LAN937x Ethernet Switch Series Tree Bindings
+
+maintainers:
+  - UNGLinuxDriver@microchip.com
+
+allOf:
+  - $ref: dsa.yaml#
+
+properties:
+  compatible:
+    enum:
+      - microchip,lan9370
+      - microchip,lan9371
+      - microchip,lan9372
+      - microchip,lan9373
+      - microchip,lan9374
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 50000000
+
+  reset-gpios:
+    description: Optional gpio specifier for a reset line
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    //Ethernet switch connected via spi to the host
+    ethernet {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      fixed-link {
+        speed = <1000>;
+        full-duplex;
+      };
+    };
+
+    spi {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      lan9374: switch@0 {
+        compatible = "microchip,lan9374";
+        reg = <0>;
+
+        spi-max-frequency = <44000000>;
+
+        ethernet-ports {
+          #address-cells = <1>;
+          #size-cells = <0>;
+          port@0 {
+            reg = <0>;
+            label = "lan1";
+            phy-handle = <&t1phy0>;
+          };
+          port@1 {
+            reg = <1>;
+            label = "lan2";
+            phy-handle = <&t1phy1>;
+          };
+          port@2 {
+            reg = <2>;
+            label = "lan4";
+            phy-handle = <&t1phy2>;
+          };
+          port@3 {
+            reg = <3>;
+            label = "lan6";
+            phy-handle = <&t1phy3>;
+          };
+          port@4 {
+            reg = <4>;
+            phy-mode = "rgmii";
+            ethernet = <&ethernet>;
+            fixed-link {
+              speed = <1000>;
+              full-duplex;
+            };
+          };
+          port@5 {
+            reg = <5>;
+            label = "lan7";
+            fixed-link {
+              speed = <1000>;
+              full-duplex;
+            };
+          };
+          port@6 {
+            reg = <6>;
+            label = "lan5";
+            phy-handle = <&t1phy4>;
+          };
+          port@7 {
+            reg = <7>;
+            label = "lan3";
+            phy-handle = <&t1phy5>;
+          };
+        };
+
+        mdio {
+          compatible = "microchip,lan937x-mdio";
+          #address-cells = <1>;
+          #size-cells = <0>;
+
+          t1phy0: ethernet-phy@0{
+            reg = <0x0>;
+          };
+          t1phy1: ethernet-phy@1{
+            reg = <0x1>;
+          };
+          t1phy2: ethernet-phy@2{
+            reg = <0x2>;
+          };
+          t1phy3: ethernet-phy@3{
+            reg = <0x3>;
+          };
+          t1phy4: ethernet-phy@6{
+            reg = <0x6>;
+          };
+          t1phy5: ethernet-phy@7{
+            reg = <0x7>;
+          };
+        };
+      };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index c3c8fa572580..a0fdfef8802a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11752,6 +11752,7 @@ M:	UNGLinuxDriver@microchip.com
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
+F:	Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
 F:	drivers/net/dsa/microchip/*
 F:	include/linux/platform_data/microchip-ksz.h
 F:	net/dsa/tag_ksz.c
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 12:45   ` Andrew Lunn
  2021-04-22  9:42 ` [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Added support for Microchip LAN937x T1 phy driver. The sequence of
initialization is used commonly for both LAN87xx and LAN937x
drivers. The new initialization sequence is an improvement to
existing LAN87xx and the same is shared with LAN937x.

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/phy/microchip_t1.c | 361 +++++++++++++++++++++++++++------
 1 file changed, 300 insertions(+), 61 deletions(-)

diff --git a/drivers/net/phy/microchip_t1.c b/drivers/net/phy/microchip_t1.c
index 4dc00bd5a8d2..348ee7885bfd 100644
--- a/drivers/net/phy/microchip_t1.c
+++ b/drivers/net/phy/microchip_t1.c
@@ -30,16 +30,86 @@
 #define	PHYACC_ATTR_MODE_READ		0
 #define	PHYACC_ATTR_MODE_WRITE		1
 #define	PHYACC_ATTR_MODE_MODIFY		2
+#define PHYACC_ATTR_MODE_POLL		3
 
 #define	PHYACC_ATTR_BANK_SMI		0
 #define	PHYACC_ATTR_BANK_MISC		1
 #define	PHYACC_ATTR_BANK_PCS		2
 #define	PHYACC_ATTR_BANK_AFE		3
+#define PHYACC_ATTR_BANK_DSP		4
 #define	PHYACC_ATTR_BANK_MAX		7
 
 #define DRIVER_AUTHOR	"Nisar Sayed <nisar.sayed@microchip.com>"
 #define DRIVER_DESC	"Microchip LAN87XX T1 PHY driver"
 
+#define REG_PORT_T1_PHY_BASIC_CTRL 0x00
+
+#define PORT_T1_PHY_RESET	BIT(15)
+#define PORT_T1_PHY_LOOPBACK	BIT(14)
+#define PORT_T1_SPEED_100MBIT	BIT(13)
+#define PORT_T1_POWER_DOWN	BIT(11)
+#define PORT_T1_ISOLATE	BIT(10)
+#define PORT_T1_FULL_DUPLEX	BIT(8)
+
+#define REG_PORT_T1_PHY_BASIC_STATUS 0x01
+
+#define PORT_T1_MII_SUPPRESS_CAPABLE	BIT(6)
+#define PORT_T1_LINK_STATUS		BIT(2)
+#define PORT_T1_EXTENDED_CAPABILITY	BIT(0)
+
+#define REG_PORT_T1_PHY_ID_HI 0x02
+#define REG_PORT_T1_PHY_ID_LO 0x03
+
+#define LAN937X_T1_ID_HI 0x0007
+#define LAN937X_T1_ID_LO 0xC150
+
+#define REG_PORT_T1_PHY_M_CTRL 0x09
+
+#define PORT_T1_MANUAL_CFG	BIT(12)
+#define PORT_T1_M_CFG		BIT(11)
+
+#define REG_PORT_T1_MODE_STAT			0x11
+#define T1_PORT_DSCR_LOCK_STATUS_MSK		BIT(3)
+#define T1_PORT_LINK_UP_MSK			BIT(0)
+
+#define REG_PORT_T1_LOOPBACK_CTRL 0x12
+
+#define REG_PORT_T1_RESET_CTRL 0x13
+
+#define T1_PHYADDR_S 11
+
+#define REG_PORT_T1_EXT_REG_CTRL 0x14
+
+#define T1_PCS_STS_CNT_RESET		BIT(15)
+#define T1_IND_DATA_READ		BIT(12)
+#define T1_IND_DATA_WRITE		BIT(11)
+#define T1_REG_BANK_SEL_M		0x7
+#define T1_REG_BANK_SEL_S		8
+#define T1_REG_ADDR_M			0xFF
+
+#define REG_PORT_T1_EXT_REG_RD_DATA 0x15
+#define REG_PORT_T1_EXT_REG_WR_DATA 0x16
+
+#define REG_PORT_T1_PHY_INT_STATUS 0x18
+#define REG_PORT_T1_PHY_INT_ENABLE 0x19
+
+#define T1_LINK_UP_INT		BIT(2)
+#define T1_LINK_DOWN_INT	BIT(1)
+
+#define REG_PORT_T1_POWER_DOWN_CTRL 0x1A
+
+#define T1_HW_INIT_SEQ_ENABLE BIT(8)
+
+#define REG_PORT_T1_PHY_M_STATUS 0x0A
+
+#define PORT_T1_LOCAL_RX_OK	BIT(13)
+#define PORT_T1_REMOTE_RX_OK	BIT(12)
+
+#define LAN87XX_PHY_ID		0x0007c150
+#define LAN937X_T1_PHY_ID	0x0007c181
+#define LAN87XX_PHY_ID_MASK 0xfffffff0
+#define LAN937X_PHY_ID_MASK 0xfffffff0
+
 struct access_ereg_val {
 	u8  mode;
 	u8  bank;
@@ -51,12 +121,15 @@ struct access_ereg_val {
 static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
 		       u8 offset, u16 val)
 {
+	u8 prev_bank;
 	u16 ereg = 0;
 	int rc = 0;
 
+	/* return if mode and bank are invalid */
 	if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX)
 		return -EINVAL;
 
+	/* if the bank is SMI, then call phy_read() & phy_write() directly */
 	if (bank == PHYACC_ATTR_BANK_SMI) {
 		if (mode == PHYACC_ATTR_MODE_WRITE)
 			rc = phy_write(phydev, offset, val);
@@ -66,16 +139,43 @@ static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
 	}
 
 	if (mode == PHYACC_ATTR_MODE_WRITE) {
+		/* Initialize to Write Mode */
 		ereg = LAN87XX_EXT_REG_CTL_WR_CTL;
+
+		/* Write the data to be written in to the Bank */
 		rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val);
 		if (rc < 0)
 			return rc;
 	} else {
+		/* Initialize to Read Mode */
 		ereg = LAN87XX_EXT_REG_CTL_RD_CTL;
 	}
 
 	ereg |= (bank << 8) | offset;
 
+	/* DSP bank access register workaround for lan937x*/
+	if (phydev->phy_id == LAN937X_T1_PHY_ID) {
+		/* Read previous selected bank */
+		rc = phy_read(phydev, LAN87XX_EXT_REG_CTL);
+		if (rc < 0)
+			return rc;
+
+		/* Store the prev_bank */
+		prev_bank = (rc >> T1_REG_BANK_SEL_S) & T1_REG_BANK_SEL_M;
+
+		if (bank != prev_bank && bank == PHYACC_ATTR_BANK_DSP) {
+			u16 t = ereg & ~T1_REG_ADDR_M;
+
+			t &= ~LAN87XX_EXT_REG_CTL_WR_CTL;
+			t |= LAN87XX_EXT_REG_CTL_RD_CTL;
+
+			/* Need to access twice for DSP bank change - dummy access*/
+			rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, t);
+			if (rc < 0)
+				return rc;
+		}
+	}
+
 	rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg);
 	if (rc < 0)
 		return rc;
@@ -104,63 +204,151 @@ static int access_ereg_modify_changed(struct phy_device *phydev,
 	return rc;
 }
 
-static int lan87xx_phy_init(struct phy_device *phydev)
+static int access_ereg_clr_poll_timeout(struct phy_device *phydev, u8 bank,
+					u8 offset, u16 mask, u16 clr)
+{
+	int val;
+
+	if (bank != PHYACC_ATTR_BANK_SMI)
+		return -EINVAL;
+
+	return phy_read_poll_timeout(phydev, offset, val, (val & mask) == clr,
+				     150, 30000, true);
+}
+
+static int mchp_t1_phy_init(struct phy_device *phydev)
 {
 	static const struct access_ereg_val init[] = {
-		/* TX Amplitude = 5 */
-		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B,
-		 0x000A, 0x001E},
-		/* Clear SMI interrupts */
-		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18,
-		 0, 0},
-		/* Clear MISC interrupts */
-		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08,
-		 0, 0},
-		/* Turn on TC10 Ring Oscillator (ROSC) */
-		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20,
-		 0x0020, 0x0020},
-		/* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */
-		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20,
-		 0x283C, 0},
-		/* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */
-		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21,
-		 0x274F, 0},
-		/* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep,
-		 * and Wake_In to wake PHY
-		 */
-		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20,
-		 0x80A7, 0},
-		/* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer
-		 * to 128 uS
-		 */
-		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24,
-		 0xF110, 0},
-		/* Enable HW Init */
-		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A,
-		 0x0100, 0x0100},
+		/* TXPD/TXAMP6 Configs*/
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, 0x0B, 0x002D,
+		  0 },
+		/* HW_Init Hi and Force_ED */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 0x1A, 0x0308,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x18, 0x0D53,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x05, 0x0AB2,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x06, 0x0AB3,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x1A, 0x0AEA,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x1B, 0x0AEB,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x1C, 0x0AEB,
+		  0 },
+		/* Pointer delay */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x02, 0x1C00,
+		  0 },
+		/* ---- tx iir edits ---- */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1000,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1861,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1061,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1922,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1122,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1983,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1183,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1944,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1144,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x18c5,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x10c5,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1846,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1046,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1807,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1007,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1808,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1008,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1809,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1009,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180A,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100A,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180B,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100B,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180C,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100C,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180D,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100D,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180E,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100E,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x180F,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x100F,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1810,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1010,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1811,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1011,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x55, 0x1000,
+		  0 },
+		/* SQI enable */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 0x2E, 0x9572,
+		  0 },
+		/* Flag LPS and WUR as idle errors */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 0x10, 0x0014,
+		  0 },
+		/* Restore state machines without clearing registers */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 0x1A, 0x0200,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 0x10, 0x0094,
+		  0 },
+		{ PHYACC_ATTR_MODE_POLL, PHYACC_ATTR_BANK_SMI, 0x10, 0x0080,
+		  0 },
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE, 0x0B, 0x000C,
+		  0 },
+		/* Read INTERRUPT_SOURCE Register */
+		{ PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18, 0, 0 },
+		/* Read INTERRUPT_SOURCE Register */
+		{ PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08, 0, 0 },
+		/* HW_Init Hi */
+		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI, 0x1A, 0x0300,
+		  0 },
 	};
 	int rc, i;
 
-	/* Start manual initialization procedures in Managed Mode */
-	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
-					0x1a, 0x0000, 0x0100);
-	if (rc < 0)
-		return rc;
-
-	/* Soft Reset the SMI block */
+	/* Set Master Mode */
 	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
-					0x00, 0x8000, 0x8000);
+					REG_PORT_T1_PHY_M_CTRL, PORT_T1_M_CFG,
+					PORT_T1_M_CFG);
 	if (rc < 0)
 		return rc;
 
-	/* Check to see if the self-clearing bit is cleared */
-	usleep_range(1000, 2000);
-	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
-			 PHYACC_ATTR_BANK_SMI, 0x00, 0);
+	/* phy Soft reset */
+	rc = genphy_soft_reset(phydev);
 	if (rc < 0)
 		return rc;
-	if ((rc & 0x8000) != 0)
-		return -ETIMEDOUT;
 
 	/* PHY Initialization */
 	for (i = 0; i < ARRAY_SIZE(init); i++) {
@@ -169,6 +357,11 @@ static int lan87xx_phy_init(struct phy_device *phydev)
 							init[i].offset,
 							init[i].val,
 							init[i].mask);
+		} else if (init[i].mode == PHYACC_ATTR_MODE_POLL) {
+			rc = access_ereg_clr_poll_timeout(phydev, init[i].bank,
+							  init[i].offset,
+							  init[i].val,
+							  init[i].mask);
 		} else {
 			rc = access_ereg(phydev, init[i].mode, init[i].bank,
 					 init[i].offset, init[i].val);
@@ -221,33 +414,79 @@ static irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev)
 
 static int lan87xx_config_init(struct phy_device *phydev)
 {
-	int rc = lan87xx_phy_init(phydev);
+	int rc = mchp_t1_phy_init(phydev);
+
+	if (rc < 0)
+		phydev_err(phydev, "failed to initialize phy\n");
 
 	return rc < 0 ? rc : 0;
 }
 
-static struct phy_driver microchip_t1_phy_driver[] = {
-	{
-		.phy_id         = 0x0007c150,
-		.phy_id_mask    = 0xfffffff0,
-		.name           = "Microchip LAN87xx T1",
+static int lan937x_read_status(struct phy_device *phydev)
+{
+	int val1, val2;
 
-		.features       = PHY_BASIC_T1_FEATURES,
+	val1 = phy_read(phydev, REG_PORT_T1_PHY_M_STATUS);
 
-		.config_init	= lan87xx_config_init,
+	if (val1 < 0)
+		return val1;
 
-		.config_intr    = lan87xx_phy_config_intr,
-		.handle_interrupt = lan87xx_handle_interrupt,
+	val2 = phy_read(phydev, REG_PORT_T1_MODE_STAT);
 
-		.suspend        = genphy_suspend,
-		.resume         = genphy_resume,
-	}
-};
+	if (val2 < 0)
+		return val2;
+
+	if (val1 & (PORT_T1_LOCAL_RX_OK | PORT_T1_REMOTE_RX_OK) &&
+	    val2 & (T1_PORT_DSCR_LOCK_STATUS_MSK | T1_PORT_LINK_UP_MSK))
+		phydev->link = 1;
+	else
+		phydev->link = 0;
+
+	phydev->duplex = DUPLEX_FULL;
+	phydev->speed = SPEED_100;
+	phydev->pause = 0;
+	phydev->asym_pause = 0;
+
+	return 0;
+}
+
+static int lan937x_config_init(struct phy_device *phydev)
+{
+	int rc = mchp_t1_phy_init(phydev);
+
+	if (rc < 0)
+		phydev_err(phydev, "failed to initialize phy\n");
+
+	return rc < 0 ? rc : 0;
+}
+
+static struct phy_driver microchip_t1_phy_driver[] = {
+{
+	.phy_id         = LAN87XX_PHY_ID,
+	.phy_id_mask    = LAN87XX_PHY_ID_MASK,
+	.name           = "LAN87xx T1",
+	.features       = PHY_BASIC_T1_FEATURES,
+	.config_init	= lan87xx_config_init,
+	.config_intr    = lan87xx_phy_config_intr,
+	.handle_interrupt = lan87xx_handle_interrupt,
+	.suspend        = genphy_suspend,
+	.resume         = genphy_resume,
+}, {
+	.phy_id		= LAN937X_T1_PHY_ID,
+	.phy_id_mask	= LAN937X_PHY_ID_MASK,
+	.name			= "LAN937x T1",
+	.read_status	= lan937x_read_status,
+	.features		= PHY_BASIC_T1_FEATURES,
+	.config_init	= lan937x_config_init,
+	.suspend		= genphy_suspend,
+	.resume		= genphy_resume,
+} };
 
 module_phy_driver(microchip_t1_phy_driver);
 
 static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
-	{ 0x0007c150, 0xfffffff0 },
+	{ LAN87XX_PHY_ID, LAN87XX_PHY_ID_MASK },
+	{ LAN937X_T1_PHY_ID, LAN937X_PHY_ID_MASK },
 	{ }
 };
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 19:18   ` Vladimir Oltean
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

The Microchip LAN937X switches have a tagging protocol which is
very similar to KSZ tagging. So that the implementation is added to
tag_ksz.c and reused common APIs

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 include/net/dsa.h |  2 ++
 net/dsa/Kconfig   |  4 ++--
 net/dsa/tag_ksz.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 507082959aa4..98c1ab6dc4dc 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -50,6 +50,7 @@ struct phylink_link_state;
 #define DSA_TAG_PROTO_OCELOT_8021Q_VALUE	20
 #define DSA_TAG_PROTO_SEVILLE_VALUE		21
 #define DSA_TAG_PROTO_BRCM_LEGACY_VALUE		22
+#define DSA_TAG_PROTO_LAN937X_VALUE		23
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -75,6 +76,7 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_XRS700X		= DSA_TAG_PROTO_XRS700X_VALUE,
 	DSA_TAG_PROTO_OCELOT_8021Q	= DSA_TAG_PROTO_OCELOT_8021Q_VALUE,
 	DSA_TAG_PROTO_SEVILLE		= DSA_TAG_PROTO_SEVILLE_VALUE,
+	DSA_TAG_PROTO_LAN937X		= DSA_TAG_PROTO_LAN937X_VALUE,
 };
 
 struct packet_type;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index cbc2bd643ab2..888eb79a85f2 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -97,10 +97,10 @@ config NET_DSA_TAG_MTK
 	  Mediatek switches.
 
 config NET_DSA_TAG_KSZ
-	tristate "Tag driver for Microchip 8795/9477/9893 families of switches"
+	tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches"
 	help
 	  Say Y if you want to enable support for tagging frames for the
-	  Microchip 8795/9477/9893 families of switches.
+	  Microchip 8795/937x/9477/9893 families of switches.
 
 config NET_DSA_TAG_RTL4_A
 	tristate "Tag driver for Realtek 4 byte protocol A tags"
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 4820dbcedfa2..a67f21bdab8f 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -190,10 +190,68 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
 DSA_TAG_DRIVER(ksz9893_netdev_ops);
 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893);
 
+/* For xmit, 2 bytes are added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : represents tag override, lookup and valid
+ * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
+ *
+ * For rcv, 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ *	  (eg, 0x00=port1, 0x02=port3, 0x07=port8)
+ */
+#define LAN937X_INGRESS_TAG_LEN		2
+
+#define LAN937X_TAIL_TAG_OVERRIDE	BIT(11)
+#define LAN937X_TAIL_TAG_LOOKUP		BIT(12)
+#define LAN937X_TAIL_TAG_VALID		BIT(13)
+#define LAN937X_TAIL_TAG_PORT_MASK	7
+
+static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
+				    struct net_device *dev)
+{
+	struct dsa_port *dp = dsa_slave_to_port(dev);
+	__be16 *tag;
+	u8 *addr;
+	u16 val;
+
+	tag = skb_put(skb, LAN937X_INGRESS_TAG_LEN);
+	addr = skb_mac_header(skb);
+
+	val = BIT(dp->index);
+
+	if (is_link_local_ether_addr(addr))
+		val |= LAN937X_TAIL_TAG_OVERRIDE;
+
+	/* Tail tag valid bit - This bit should always be set by the CPU*/
+	val |= LAN937X_TAIL_TAG_VALID;
+
+	*tag = cpu_to_be16(val);
+
+	return skb;
+}
+
+static const struct dsa_device_ops lan937x_netdev_ops = {
+	.name	= "lan937x",
+	.proto	= DSA_TAG_PROTO_LAN937X,
+	.xmit	= lan937x_xmit,
+	.rcv	= ksz9477_rcv,
+	.overhead = LAN937X_INGRESS_TAG_LEN,
+	.tail_tag = true,
+};
+
+DSA_TAG_DRIVER(lan937x_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X);
+
 static struct dsa_tag_driver *dsa_tag_driver_array[] = {
 	&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
 	&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
 	&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
+	&DSA_TAG_DRIVER_NAME(lan937x_netdev_ops),
 };
 
 module_dsa_tag_drivers(dsa_tag_driver_array);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (2 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 19:59   ` Vladimir Oltean
                     ` (3 more replies)
  2021-04-22  9:42 ` [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
                   ` (4 subsequent siblings)
  8 siblings, 4 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Basic DSA driver support for lan937x and the device will be
configured through SPI interface.

drivers/net/dsa/microchip/ path is already part of MAINTAINERS &
the new files come under this path. Hence no update needed to the
MAINTAINERS

Reused KSZ APIs for port_bridge_join() & port_bridge_leave() and
added support for port_stp_state_set() & port_fast_age().

lan937x_flush_dyn_mac_table() which gets called from
port_fast_age() of KSZ common layer, hence added support for it.

currently port_bridge_flags returns -EOPNOTSUPP, this support
will be added later

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/Kconfig        |  12 +
 drivers/net/dsa/microchip/Makefile       |   5 +
 drivers/net/dsa/microchip/ksz_common.h   |   5 +
 drivers/net/dsa/microchip/lan937x_dev.c  | 674 +++++++++++++++++++++
 drivers/net/dsa/microchip/lan937x_dev.h  |  68 +++
 drivers/net/dsa/microchip/lan937x_main.c | 364 ++++++++++++
 drivers/net/dsa/microchip/lan937x_reg.h  | 710 +++++++++++++++++++++++
 drivers/net/dsa/microchip/lan937x_spi.c  | 226 ++++++++
 8 files changed, 2064 insertions(+)
 create mode 100644 drivers/net/dsa/microchip/lan937x_dev.c
 create mode 100644 drivers/net/dsa/microchip/lan937x_dev.h
 create mode 100644 drivers/net/dsa/microchip/lan937x_main.c
 create mode 100644 drivers/net/dsa/microchip/lan937x_reg.h
 create mode 100644 drivers/net/dsa/microchip/lan937x_spi.c

diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
index 4ec6a47b7f72..ae2484e3759b 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -3,6 +3,18 @@ config NET_DSA_MICROCHIP_KSZ_COMMON
 	select NET_DSA_TAG_KSZ
 	tristate
 
+config NET_DSA_MICROCHIP_LAN937X
+	tristate "Microchip LAN937X series SPI connected switch support"
+	depends on NET_DSA && SPI
+	select NET_DSA_MICROCHIP_KSZ_COMMON
+	select REGMAP_SPI
+	help
+	  This driver adds support for Microchip LAN937X series
+	  switch chips.
+
+	  Select to enable support for registering switches configured
+	  through SPI.
+
 menuconfig NET_DSA_MICROCHIP_KSZ9477
 	tristate "Microchip KSZ9477 series switch support"
 	depends on NET_DSA
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 929caa81e782..13f38efdadf0 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -5,3 +5,8 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C)	+= ksz9477_i2c.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI)	+= ksz9477_spi.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795)		+= ksz8795.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795_SPI)	+= ksz8795_spi.o
+
+obj-$(CONFIG_NET_DSA_MICROCHIP_LAN937X)		+= lan937x.o
+lan937x-objs := lan937x_dev.o
+lan937x-objs += lan937x_main.o
+lan937x-objs += lan937x_spi.o
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index f212775372ce..a0ec3d36c889 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -60,6 +60,8 @@ struct ksz_device {
 
 	struct gpio_desc *reset_gpio;	/* Optional reset GPIO */
 
+	struct device_node *mdio_np;
+
 	/* chip specific data */
 	u32 chip_id;
 	int num_vlans;
@@ -144,6 +146,9 @@ void ksz_switch_remove(struct ksz_device *dev);
 
 int ksz8795_switch_register(struct ksz_device *dev);
 int ksz9477_switch_register(struct ksz_device *dev);
+int lan937x_switch_register(struct ksz_device *dev);
+
+int lan937x_check_device_id(struct ksz_device *dev);
 
 void ksz_update_port_member(struct ksz_device *dev, int port);
 void ksz_init_mib_timer(struct ksz_device *dev);
diff --git a/drivers/net/dsa/microchip/lan937x_dev.c b/drivers/net/dsa/microchip/lan937x_dev.c
new file mode 100644
index 000000000000..21dbc8df5f83
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.c
@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip lan937x dev ops functions
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "lan937x_reg.h"
+#include "ksz_common.h"
+#include "lan937x_dev.h"
+
+const struct mib_names lan937x_mib_names[] = {
+	{ 0x00, "rx_hi" },
+	{ 0x01, "rx_undersize" },
+	{ 0x02, "rx_fragments" },
+	{ 0x03, "rx_oversize" },
+	{ 0x04, "rx_jabbers" },
+	{ 0x05, "rx_symbol_err" },
+	{ 0x06, "rx_crc_err" },
+	{ 0x07, "rx_align_err" },
+	{ 0x08, "rx_mac_ctrl" },
+	{ 0x09, "rx_pause" },
+	{ 0x0A, "rx_bcast" },
+	{ 0x0B, "rx_mcast" },
+	{ 0x0C, "rx_ucast" },
+	{ 0x0D, "rx_64_or_less" },
+	{ 0x0E, "rx_65_127" },
+	{ 0x0F, "rx_128_255" },
+	{ 0x10, "rx_256_511" },
+	{ 0x11, "rx_512_1023" },
+	{ 0x12, "rx_1024_1522" },
+	{ 0x13, "rx_1523_2000" },
+	{ 0x14, "rx_2001" },
+	{ 0x15, "tx_hi" },
+	{ 0x16, "tx_late_col" },
+	{ 0x17, "tx_pause" },
+	{ 0x18, "tx_bcast" },
+	{ 0x19, "tx_mcast" },
+	{ 0x1A, "tx_ucast" },
+	{ 0x1B, "tx_deferred" },
+	{ 0x1C, "tx_total_col" },
+	{ 0x1D, "tx_exc_col" },
+	{ 0x1E, "tx_single_col" },
+	{ 0x1F, "tx_mult_col" },
+	{ 0x80, "rx_total" },
+	{ 0x81, "tx_total" },
+	{ 0x82, "rx_discards" },
+	{ 0x83, "tx_discards" },
+};
+
+struct prt_init {
+	int addr;
+	u8 mask;
+	bool is_set;
+};
+
+int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+	return regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
+}
+
+int lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
+		     u8 bits, bool set)
+{
+	return regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset),
+			   bits, set ? bits : 0);
+}
+
+int lan937x_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
+{
+	return regmap_update_bits(dev->regmap[2], addr, bits, set ? bits : 0);
+}
+
+int lan937x_pread8(struct ksz_device *dev, int port, int offset,
+		   u8 *data)
+{
+	return ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pread16(struct ksz_device *dev, int port, int offset,
+		    u16 *data)
+{
+	return ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pread32(struct ksz_device *dev, int port, int offset,
+		    u32 *data)
+{
+	return ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite8(struct ksz_device *dev, int port,
+		    int offset, u8 data)
+{
+	return ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite16(struct ksz_device *dev, int port,
+		     int offset, u16 data)
+{
+	return ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_pwrite32(struct ksz_device *dev, int port,
+		     int offset, u32 data)
+{
+	return ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+int lan937x_port_cfg32(struct ksz_device *dev, int port, int offset,
+		       u32 bits, bool set)
+{
+	return regmap_update_bits(dev->regmap[2], PORT_CTRL_ADDR(port, offset),
+			   bits, set ? bits : 0);
+}
+
+void lan937x_cfg_port_member(struct ksz_device *dev, int port,
+			     u8 member)
+{
+	lan937x_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member);
+
+	dev->ports[port].member = member;
+}
+
+static void lan937x_flush_dyn_mac_table(struct ksz_device *dev, int port)
+{
+	unsigned int value;
+	u8 data;
+
+	regmap_update_bits(dev->regmap[0], REG_SW_LUE_CTRL_2,
+			   SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S,
+			   SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S);
+
+	if (port < dev->port_cnt) {
+		/* flush individual port */
+		lan937x_pread8(dev, port, P_STP_CTRL, &data);
+		if (!(data & PORT_LEARN_DISABLE))
+			lan937x_pwrite8(dev, port, P_STP_CTRL,
+					data | PORT_LEARN_DISABLE);
+		lan937x_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
+
+		regmap_read_poll_timeout(dev->regmap[0],
+					 S_FLUSH_TABLE_CTRL,
+				value, !(value & SW_FLUSH_DYN_MAC_TABLE), 10, 1000);
+
+		lan937x_pwrite8(dev, port, P_STP_CTRL, data);
+	} else {
+		/* flush all */
+		lan937x_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_STP_TABLE, true);
+	}
+}
+
+static void lan937x_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
+			      u64 *cnt)
+{
+	unsigned int val;
+	u32 data;
+	int ret;
+
+	/* Enable MIB Counter read*/
+	data = MIB_COUNTER_READ;
+	data |= (addr << MIB_COUNTER_INDEX_S);
+	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
+
+	ret = regmap_read_poll_timeout(dev->regmap[2],
+				       PORT_CTRL_ADDR(port,
+						      REG_PORT_MIB_CTRL_STAT__4),
+					   val, !(val & MIB_COUNTER_READ), 10, 1000);
+	/* failed to read MIB. get out of loop */
+	if (ret) {
+		dev_err(dev->dev, "Failed to get MIB\n");
+		return;
+	}
+
+	/* count resets upon read */
+	lan937x_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+	*cnt += data;
+}
+
+static void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+			      u64 *dropped, u64 *cnt)
+{
+	addr = lan937x_mib_names[addr].index;
+	lan937x_r_mib_cnt(dev, port, addr, cnt);
+}
+
+static void lan937x_port_init_cnt(struct ksz_device *dev, int port)
+{
+	struct ksz_port_mib *mib = &dev->ports[port].mib;
+
+	/* flush all enabled port MIB counters */
+	mutex_lock(&mib->cnt_mutex);
+	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
+			 MIB_COUNTER_FLUSH_FREEZE);
+	ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
+	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0);
+	mutex_unlock(&mib->cnt_mutex);
+
+	mib->cnt_ptr = 0;
+	memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
+}
+
+int lan937x_reset_switch(struct ksz_device *dev)
+{
+	u32 data32;
+	u8 data8;
+	int rc;
+
+	/* reset switch */
+	rc = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+	if (rc < 0)
+		return rc;
+
+	/* default configuration */
+	rc = ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+	if (rc < 0)
+		return rc;
+
+	data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
+	      SW_SRC_ADDR_FILTER;
+
+	rc = ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+	if (rc < 0)
+		return rc;
+
+	/* disable interrupts */
+	rc = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+	if (rc < 0)
+		return rc;
+
+	/* set broadcast storm protection 10% rate */
+	rc = regmap_update_bits(dev->regmap[1], REG_SW_MAC_CTRL_2,
+				BROADCAST_STORM_RATE,
+			   (BROADCAST_STORM_VALUE *
+			   BROADCAST_STORM_PROT_RATE) / 100);
+
+	return rc;
+}
+
+static int lan937x_switch_detect(struct ksz_device *dev)
+{
+	u32 id32;
+	int ret;
+
+	/* Read Chip ID */
+	ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
+
+	if (ret)
+		return ret;
+
+	if (id32 != 0) {
+		dev->chip_id = id32;
+		dev_info(dev->dev, "Chip ID: 0x%x", id32);
+		ret = 0;
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void lan937x_switch_exit(struct ksz_device *dev)
+{
+	lan937x_reset_switch(dev);
+}
+
+int lan937x_enable_spi_indirect_access(struct ksz_device *dev)
+{
+	u16 data16;
+	u8 data8;
+	int rc;
+
+	rc = ksz_read8(dev, REG_GLOBAL_CTRL_0, &data8);
+	if (rc < 0)
+		return rc;
+
+	/* Check if PHY register is blocked */
+	if (data8 & SW_PHY_REG_BLOCK) {
+		/* Enable Phy access through SPI*/
+		data8 &= ~SW_PHY_REG_BLOCK;
+
+		rc = ksz_write8(dev, REG_GLOBAL_CTRL_0, data8);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16);
+	if (rc < 0)
+		return rc;
+
+	/* Allow SPI access */
+	data16 |= VPHY_SPI_INDIRECT_ENABLE;
+	rc = ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16);
+
+	return rc;
+}
+
+bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port)
+{
+	/* Check if the port is RGMII */
+	if (port == LAN937X_RGMII_1_PORT || port == LAN937X_RGMII_2_PORT)
+		return false;
+
+	/* Check if the port is SGMII */
+	if (port == LAN937X_SGMII_PORT &&
+	    GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_73)
+		return false;
+
+	return true;
+}
+
+static u32 lan937x_get_port_addr(int port, int offset)
+{
+	return PORT_CTRL_ADDR(port, offset);
+}
+
+bool lan937x_is_internal_100BTX_phy_port(struct ksz_device *dev, int port)
+{
+	/* Check if the port is internal tx phy port */
+	if (lan937x_is_internal_phy_port(dev, port) && port == LAN937X_TXPHY_PORT)
+		if ((GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_71) ||
+		    (GET_CHIP_ID_LSB(dev->chip_id) == CHIP_ID_72))
+			return true;
+
+	return false;
+}
+
+bool lan937x_is_internal_t1_phy_port(struct ksz_device *dev, int port)
+{
+	/* Check if the port is internal t1 phy port */
+	if (lan937x_is_internal_phy_port(dev, port) &&
+	    !lan937x_is_internal_100BTX_phy_port(dev, port))
+		return true;
+
+	return false;
+}
+
+int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
+			       int reg, u16 val)
+{
+	u16 temp, addr_base;
+	unsigned int value;
+	int rc;
+
+	/* Check for internal phy port */
+	if (!lan937x_is_internal_phy_port(dev, addr))
+		return -EOPNOTSUPP;
+
+	if (lan937x_is_internal_100BTX_phy_port(dev, addr))
+		addr_base = REG_PORT_TX_PHY_CTRL_BASE;
+	else
+		addr_base = REG_PORT_T1_PHY_CTRL_BASE;
+
+	temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
+
+	rc = ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+	if (rc < 0)
+		return rc;
+
+	/* Write the data to be written to the VPHY reg */
+	rc = ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
+	if (rc < 0)
+		return rc;
+
+	/* Write the Write En and Busy bit */
+	rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2, (VPHY_IND_WRITE
+				| VPHY_IND_BUSY));
+	if (rc < 0)
+		return rc;
+
+	rc = regmap_read_poll_timeout(dev->regmap[1],
+				      REG_VPHY_IND_CTRL__2,
+				value, !(value & VPHY_IND_BUSY), 10, 1000);
+
+	/* failed to write phy register. get out of loop */
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to write phy register\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
+			      int reg, u16 *val)
+{
+	u16 temp, addr_base;
+	unsigned int value;
+	int rc;
+
+	/* Check for internal phy port, return 0xffff for non-existent phy*/
+	if (!lan937x_is_internal_phy_port(dev, addr))
+		return 0xffff;
+
+	if (lan937x_is_internal_100BTX_phy_port(dev, addr))
+		addr_base = REG_PORT_TX_PHY_CTRL_BASE;
+	else
+		addr_base = REG_PORT_T1_PHY_CTRL_BASE;
+
+	/* get register address based on the logical port */
+	temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
+
+	rc = ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+	if (rc < 0)
+		return rc;
+
+	/* Write Read and Busy bit to start the transaction*/
+	rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY);
+	if (rc < 0)
+		return rc;
+
+	rc = regmap_read_poll_timeout(dev->regmap[1],
+				      REG_VPHY_IND_CTRL__2,
+				value, !(value & VPHY_IND_BUSY), 10, 1000);
+
+	/*  failed to read phy register. get out of loop */
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to read phy register\n");
+		return rc;
+	}
+
+	/* Read the VPHY register which has the PHY data*/
+	rc = ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
+
+	return rc;
+}
+
+static void lan937x_set_gbit(struct ksz_device *dev, bool gbit, u8 *data)
+{
+	if (gbit)
+		*data &= ~PORT_MII_NOT_1GBIT;
+	else
+		*data |= PORT_MII_NOT_1GBIT;
+}
+
+void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+	struct ksz_port *p = &dev->ports[port];
+	u8 data8, member;
+
+	/* enable tag tail for host port */
+	if (cpu_port) {
+		lan937x_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
+				 true);
+		/* Enable jumbo packet in host port so that frames are not
+		 * counted as oversized.
+		 */
+		lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_PACKET,
+				 true);
+		lan937x_pwrite16(dev, port, REG_PORT_MTU__2, FR_SIZE_CPU_PORT);
+	}
+
+	lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_FR_CHK_LENGTH,
+			 false);
+
+	lan937x_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
+
+	/* set back pressure */
+	lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
+
+	/* enable broadcast storm limit */
+	lan937x_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
+
+	/* disable DiffServ priority */
+	lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
+
+	/* replace priority */
+	lan937x_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
+			 false);
+	lan937x_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
+			   MTI_PVID_REPLACE, false);
+
+	/* enable 802.1p priority */
+	lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
+
+	if (!lan937x_is_internal_phy_port(dev, port)) {
+		/* force flow control off*/
+		lan937x_port_cfg(dev, port, REG_PORT_XMII_CTRL_0,
+				 PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
+			     false);
+
+		lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+
+		/* clear MII selection & set it based on interface later */
+		data8 &= ~PORT_MII_SEL_M;
+
+		/* configure MAC based on p->interface */
+		switch (p->interface) {
+		case PHY_INTERFACE_MODE_MII:
+			lan937x_set_gbit(dev, false, &data8);
+			data8 |= PORT_MII_SEL;
+			break;
+		case PHY_INTERFACE_MODE_RMII:
+			lan937x_set_gbit(dev, false, &data8);
+			data8 |= PORT_RMII_SEL;
+			break;
+		default:
+			lan937x_set_gbit(dev, true, &data8);
+			data8 |= PORT_RGMII_SEL;
+
+			data8 &= ~PORT_RGMII_ID_IG_ENABLE;
+			data8 &= ~PORT_RGMII_ID_EG_ENABLE;
+
+			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    p->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+				data8 |= PORT_RGMII_ID_IG_ENABLE;
+
+			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+			    p->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+				data8 |= PORT_RGMII_ID_EG_ENABLE;
+			break;
+		}
+		lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
+	}
+
+	if (cpu_port)
+		member = dev->port_mask;
+	else
+		member = dev->host_mask | p->vid_member;
+
+	lan937x_cfg_port_member(dev, port, member);
+}
+
+static int lan937x_sw_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+	struct ksz_device *dev = bus->priv;
+	u16 val;
+	int rc;
+
+	rc = lan937x_internal_phy_read(dev, addr, regnum, &val);
+	if (rc < 0)
+		return rc;
+
+	return val;
+}
+
+static int lan937x_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+	struct ksz_device *dev = bus->priv;
+
+	return lan937x_internal_phy_write(dev, addr, regnum, val);
+}
+
+static int lan937x_mdio_register(struct dsa_switch *ds)
+{
+	struct ksz_device *dev = ds->priv;
+	int ret;
+
+	dev->mdio_np = of_get_compatible_child(ds->dev->of_node, "microchip,lan937x-mdio");
+
+	if (!dev->mdio_np) {
+		dev_err(ds->dev, "no MDIO bus node\n");
+		return -ENODEV;
+	}
+
+	ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
+
+	if (!ds->slave_mii_bus)
+		return -ENOMEM;
+
+	ds->slave_mii_bus->priv = ds->priv;
+	ds->slave_mii_bus->read = lan937x_sw_mdio_read;
+	ds->slave_mii_bus->write = lan937x_sw_mdio_write;
+	ds->slave_mii_bus->name = "lan937x slave smi";
+	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
+		 ds->index);
+	ds->slave_mii_bus->parent = ds->dev;
+	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
+
+	ret = of_mdiobus_register(ds->slave_mii_bus, dev->mdio_np);
+
+	if (ret) {
+		dev_err(ds->dev, "unable to register MDIO bus %s\n",
+			ds->slave_mii_bus->id);
+		of_node_put(dev->mdio_np);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lan937x_switch_init(struct ksz_device *dev)
+{
+	int i, ret;
+
+	dev->ds->ops = &lan937x_switch_ops;
+
+	/* Check device tree */
+	ret = lan937x_check_device_id(dev);
+
+	if (ret)
+		return ret;
+
+	dev->port_mask = (1 << dev->port_cnt) - 1;
+
+	dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
+	dev->mib_cnt = ARRAY_SIZE(lan937x_mib_names);
+
+	dev->ports = devm_kzalloc(dev->dev,
+				  dev->port_cnt * sizeof(struct ksz_port),
+				  GFP_KERNEL);
+	if (!dev->ports)
+		return -ENOMEM;
+
+	for (i = 0; i < dev->port_cnt; i++) {
+		mutex_init(&dev->ports[i].mib.cnt_mutex);
+		dev->ports[i].mib.counters =
+			devm_kzalloc(dev->dev,
+				     sizeof(u64) *
+				     (dev->mib_cnt + 1),
+				     GFP_KERNEL);
+		if (!dev->ports[i].mib.counters)
+			return -ENOMEM;
+	}
+
+	/* set the real number of ports */
+	dev->ds->num_ports = dev->port_cnt;
+	return 0;
+}
+
+static int lan937x_init(struct ksz_device *dev)
+{
+	int rc;
+
+	rc = lan937x_switch_init(dev);
+	if (rc < 0) {
+		dev_err(dev->dev, "failed to initialize the switch");
+		return rc;
+	}
+
+	/* enable Indirect Access from SPI to the VPHY registers */
+	rc = lan937x_enable_spi_indirect_access(dev);
+	if (rc < 0) {
+		dev_err(dev->dev, "failed to enable spi indirect access");
+		return rc;
+	}
+
+	rc = lan937x_mdio_register(dev->ds);
+	if (rc < 0) {
+		dev_err(dev->dev, "failed to register the mdio");
+		return rc;
+	}
+
+	return 0;
+}
+
+const struct ksz_dev_ops lan937x_dev_ops = {
+	.get_port_addr = lan937x_get_port_addr,
+	.cfg_port_member = lan937x_cfg_port_member,
+	.flush_dyn_mac_table = lan937x_flush_dyn_mac_table,
+	.port_setup = lan937x_port_setup,
+	.r_mib_cnt = lan937x_r_mib_cnt,
+	.r_mib_pkt = lan937x_r_mib_pkt,
+	.port_init_cnt = lan937x_port_init_cnt,
+	.shutdown = lan937x_reset_switch,
+	.detect = lan937x_switch_detect,
+	.init = lan937x_init,
+	.exit = lan937x_switch_exit,
+};
diff --git a/drivers/net/dsa/microchip/lan937x_dev.h b/drivers/net/dsa/microchip/lan937x_dev.h
new file mode 100644
index 000000000000..c98e8140ca30
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip lan937x dev ops headers
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+
+#ifndef __LAN937X_CFG_H
+#define __LAN937X_CFG_H
+
+int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set);
+int lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
+		     u8 bits, bool set);
+int lan937x_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set);
+int lan937x_pread8(struct ksz_device *dev, int port, int offset,
+		   u8 *data);
+int lan937x_pread16(struct ksz_device *dev, int port, int offset,
+		    u16 *data);
+int lan937x_pread32(struct ksz_device *dev, int port, int offset,
+		    u32 *data);
+int lan937x_pwrite8(struct ksz_device *dev, int port,
+		    int offset, u8 data);
+int lan937x_pwrite16(struct ksz_device *dev, int port,
+		     int offset, u16 data);
+int lan937x_pwrite32(struct ksz_device *dev, int port,
+		     int offset, u32 data);
+int lan937x_port_cfg32(struct ksz_device *dev, int port, int offset,
+		       u32 bits, bool set);
+int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
+			       int reg, u16 val);
+int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
+			      int reg, u16 *val);
+bool lan937x_is_internal_100BTX_phy_port(struct ksz_device *dev, int port);
+bool lan937x_is_internal_t1_phy_port(struct ksz_device *dev, int port);
+bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port);
+int lan937x_reset_switch(struct ksz_device *dev);
+void lan937x_cfg_port_member(struct ksz_device *dev, int port,
+			     u8 member);
+void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port);
+int lan937x_enable_spi_indirect_access(struct ksz_device *dev);
+
+struct mib_names {
+	int index;
+	char string[ETH_GSTRING_LEN];
+};
+
+struct lan_alu_struct {
+	/* entry 1 */
+	u8	is_static:1;
+	u8	is_src_filter:1;
+	u8	is_dst_filter:1;
+	u8	prio_age:3;
+	u32	_reserv_0_1:23;
+	u8	mstp:3;
+	/* entry 2 */
+	u8	is_override:1;
+	u8	is_use_fid:1;
+	u32	_reserv_1_1:22;
+	u8	port_forward:8;
+	/* entry 3 & 4*/
+	u32	_reserv_2_1:9;
+	u8	fid:7;
+	u8	mac[ETH_ALEN];
+};
+
+extern const struct dsa_switch_ops lan937x_switch_ops;
+extern const struct ksz_dev_ops lan937x_dev_ops;
+extern const struct mib_names lan937x_mib_names[];
+
+#endif
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
new file mode 100644
index 000000000000..944c6f4d6d60
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver main logic
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/phy.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "lan937x_reg.h"
+#include "ksz_common.h"
+#include "lan937x_dev.h"
+
+static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
+						      int port,
+						      enum dsa_tag_protocol mp)
+{
+	return DSA_TAG_PROTO_LAN937X_VALUE;
+}
+
+static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+	struct ksz_device *dev = ds->priv;
+	u16 val;
+	int rc;
+
+	rc = lan937x_internal_phy_read(dev, addr, reg, &val);
+
+	if (rc < 0)
+		return rc;
+
+	return val;
+}
+
+static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
+			       u16 val)
+{
+	struct ksz_device *dev = ds->priv;
+
+	return lan937x_internal_phy_write(dev, addr, reg, val);
+}
+
+static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
+				       u8 state)
+{
+	struct ksz_device *dev = ds->priv;
+	struct ksz_port *p = &dev->ports[port];
+	int forward = dev->member;
+	int member = -1;
+	u8 data;
+
+	lan937x_pread8(dev, port, P_STP_CTRL, &data);
+	data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		data |= PORT_LEARN_DISABLE;
+		break;
+	case BR_STATE_LISTENING:
+		data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+		if (p->stp_state == BR_STATE_DISABLED)
+			member = dev->host_mask | p->vid_member;
+		break;
+	case BR_STATE_LEARNING:
+		data |= PORT_RX_ENABLE;
+		break;
+	case BR_STATE_FORWARDING:
+		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+
+		member = dev->host_mask | p->vid_member;
+		mutex_lock(&dev->dev_mutex);
+
+		/* Port is a member of a bridge. */
+		if (dev->br_member & (1 << port)) {
+			dev->member |= (1 << port);
+			member = dev->member;
+		}
+		mutex_unlock(&dev->dev_mutex);
+		break;
+	case BR_STATE_BLOCKING:
+		data |= PORT_LEARN_DISABLE;
+		if (p->stp_state == BR_STATE_DISABLED)
+			member = dev->host_mask | p->vid_member;
+		break;
+	default:
+		dev_err(ds->dev, "invalid STP state: %d\n", state);
+		return;
+	}
+
+	lan937x_pwrite8(dev, port, P_STP_CTRL, data);
+
+	p->stp_state = state;
+	mutex_lock(&dev->dev_mutex);
+
+	/* Port membership may share register with STP state. */
+	if (member >= 0 && member != p->member)
+		lan937x_cfg_port_member(dev, port, (u8)member);
+
+	/* Check if forwarding needs to be updated. */
+	if (state != BR_STATE_FORWARDING) {
+		if (dev->br_member & (1 << port))
+			dev->member &= ~(1 << port);
+	}
+
+	/* When topology has changed the function ksz_update_port_member
+	 * should be called to modify port forwarding behavior.
+	 */
+	if (forward != dev->member)
+		ksz_update_port_member(dev, port);
+	mutex_unlock(&dev->dev_mutex);
+}
+
+static phy_interface_t lan937x_get_interface(struct ksz_device *dev, int port)
+{
+	phy_interface_t interface;
+	u8 data8;
+	int rc;
+
+	if (lan937x_is_internal_phy_port(dev, port))
+		return PHY_INTERFACE_MODE_NA;
+
+	/* read interface from REG_PORT_XMII_CTRL_1 register */
+	rc = lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+
+	if (rc < 0)
+		return PHY_INTERFACE_MODE_NA;
+
+	switch (data8 & PORT_MII_SEL_M) {
+	case PORT_RMII_SEL:
+		interface = PHY_INTERFACE_MODE_RMII;
+		break;
+	case PORT_RGMII_SEL:
+		interface = PHY_INTERFACE_MODE_RGMII;
+		if (data8 & PORT_RGMII_ID_EG_ENABLE)
+			interface = PHY_INTERFACE_MODE_RGMII_TXID;
+		if (data8 & PORT_RGMII_ID_IG_ENABLE) {
+			interface = PHY_INTERFACE_MODE_RGMII_RXID;
+			if (data8 & PORT_RGMII_ID_EG_ENABLE)
+				interface = PHY_INTERFACE_MODE_RGMII_ID;
+		}
+		break;
+	case PORT_MII_SEL:
+	default:
+		/* Interface is MII */
+		interface = PHY_INTERFACE_MODE_MII;
+		break;
+	}
+
+	return interface;
+}
+
+static void lan937x_config_cpu_port(struct dsa_switch *ds)
+{
+	struct ksz_device *dev = ds->priv;
+	struct ksz_port *p;
+	int i;
+
+	ds->num_ports = dev->port_cnt;
+
+	for (i = 0; i < dev->port_cnt; i++) {
+		if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+			phy_interface_t interface;
+			const char *prev_msg;
+			const char *prev_mode;
+
+			dev->cpu_port = i;
+			dev->host_mask = (1 << dev->cpu_port);
+			dev->port_mask |= dev->host_mask;
+			p = &dev->ports[i];
+
+			/* Read from XMII register to determine host port
+			 * interface.  If set specifically in device tree
+			 * note the difference to help debugging.
+			 */
+			interface = lan937x_get_interface(dev, i);
+			if (!p->interface) {
+				if (dev->compat_interface)
+					p->interface = dev->compat_interface;
+				else
+					p->interface = interface;
+			}
+
+			if (interface && interface != p->interface) {
+				prev_msg = " instead of ";
+				prev_mode = phy_modes(interface);
+			} else {
+				prev_msg = "";
+				prev_mode = "";
+			}
+
+			dev_info(dev->dev,
+				 "Port%d: using phy mode %s%s%s\n",
+				 i,
+				 phy_modes(p->interface),
+				 prev_msg,
+				 prev_mode);
+
+			/* enable cpu port */
+			lan937x_port_setup(dev, i, true);
+			p->vid_member = dev->port_mask;
+		}
+	}
+
+	dev->member = dev->host_mask;
+
+	for (i = 0; i < dev->port_cnt; i++) {
+		if (i == dev->cpu_port)
+			continue;
+		p = &dev->ports[i];
+
+		/* Initialize to non-zero so that lan937x_cfg_port_member() will
+		 * be called.
+		 */
+		p->vid_member = (1 << i);
+		p->member = dev->port_mask;
+		lan937x_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+	}
+}
+
+static int lan937x_setup(struct dsa_switch *ds)
+{
+	struct ksz_device *dev = ds->priv;
+	int rc;
+
+	dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+				       dev->num_vlans, GFP_KERNEL);
+	if (!dev->vlan_cache)
+		return -ENOMEM;
+
+	rc = lan937x_reset_switch(dev);
+	if (rc < 0) {
+		dev_err(ds->dev, "failed to reset switch\n");
+		return rc;
+	}
+
+	/* Required for port partitioning. */
+	lan937x_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
+		      true);
+
+	lan937x_config_cpu_port(ds);
+
+	ds->configure_vlan_while_not_filtering = true;
+
+	/* Enable aggressive back off & UNH */
+	lan937x_cfg(dev, REG_SW_MAC_CTRL_0, SW_PAUSE_UNH_MODE | SW_NEW_BACKOFF |
+						SW_AGGR_BACKOFF, true);
+
+	lan937x_cfg(dev, REG_SW_MAC_CTRL_1, (MULTICAST_STORM_DISABLE
+							| NO_EXC_COLLISION_DROP), true);
+
+	/* queue based egress rate limit */
+	lan937x_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
+
+	lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true);
+
+	/* enable global MIB counter freeze function */
+	lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true);
+
+	/* disable CLK125 & CLK25, 1: disable, 0: enable*/
+	lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, (SW_CLK125_ENB |
+						SW_CLK25_ENB), true);
+
+	lan937x_enable_spi_indirect_access(dev);
+
+	/* start switch */
+	lan937x_cfg(dev, REG_SW_OPERATION, SW_START, true);
+
+	ksz_init_mib_timer(dev);
+
+	return 0;
+}
+
+static int lan937x_change_mtu(struct dsa_switch *ds, int port, int mtu)
+{
+	struct ksz_device *dev = ds->priv;
+	u16 max_size;
+	int rc;
+
+	if (mtu >= FR_MIN_SIZE) {
+		rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, true);
+		max_size = FR_MAX_SIZE;
+	} else {
+		rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, false);
+		max_size = FR_MIN_SIZE;
+	}
+
+	if (rc < 0) {
+		dev_err(ds->dev, "failed to enable jumbo\n");
+		return rc;
+	}
+
+	/* Write the frame size in PORT_MAX_FR_SIZE register */
+	rc = lan937x_pwrite16(dev, port, PORT_MAX_FR_SIZE, max_size);
+
+	if (rc < 0) {
+		dev_err(ds->dev, "failed to change the mtu\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
+{
+	/* Frame size is 9000 (= 0x2328) if
+	 * jumbo frame support is enabled, PORT_JUMBO_EN bit will be enabled
+	 * based on mtu in lan937x_change_mtu() API
+	 */
+	return FR_MAX_SIZE;
+}
+
+static int	lan937x_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+					      struct switchdev_brport_flags flags,
+					 struct netlink_ext_ack *extack)
+{
+	return -EOPNOTSUPP;
+}
+
+static int	lan937x_port_bridge_flags(struct dsa_switch *ds, int port,
+					  struct switchdev_brport_flags flags,
+					 struct netlink_ext_ack *extack)
+{
+	return -EOPNOTSUPP;
+}
+
+const struct dsa_switch_ops lan937x_switch_ops = {
+	.get_tag_protocol	= lan937x_get_tag_protocol,
+	.setup			= lan937x_setup,
+	.phy_read		= lan937x_phy_read16,
+	.phy_write		= lan937x_phy_write16,
+	.port_enable		= ksz_enable_port,
+	.port_bridge_join	= ksz_port_bridge_join,
+	.port_bridge_leave	= ksz_port_bridge_leave,
+	.port_pre_bridge_flags	= lan937x_port_pre_bridge_flags,
+	.port_bridge_flags	= lan937x_port_bridge_flags,
+	.port_stp_state_set	= lan937x_port_stp_state_set,
+	.port_fast_age		= ksz_port_fast_age,
+	.port_max_mtu		= lan937x_get_max_mtu,
+	.port_change_mtu	= lan937x_change_mtu,
+};
+
+int lan937x_switch_register(struct ksz_device *dev)
+{
+	int ret;
+
+	ret = ksz_switch_register(dev, &lan937x_dev_ops);
+
+	if (ret) {
+		if (dev->mdio_np) {
+			mdiobus_unregister(dev->ds->slave_mii_bus);
+			of_node_put(dev->mdio_np);
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(lan937x_switch_register);
+
+MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@microchip.com>");
+MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h
new file mode 100644
index 000000000000..fba7f9ff57a0
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_reg.h
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip LAN937X switch register definitions
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#ifndef __LAN937X_REG_H
+#define __LAN937X_REG_H
+
+/* 0 - Operation */
+#define REG_CHIP_ID0__1			0x0000
+#define REG_CHIP_ID1__1			0x0001
+#define REG_CHIP_ID2__1			0x0002
+
+#define CHIP_ID_74			0x74
+#define CHIP_ID_73			0x73
+#define CHIP_ID_72			0x72
+#define CHIP_ID_71			0x71
+#define CHIP_ID_70			0x70
+
+#define REG_CHIP_ID3__1			0x0003
+
+#define REG_GLOBAL_CTRL_0		0x0007
+
+#define SW_PHY_REG_BLOCK		BIT(7)
+#define SW_FAST_MODE			BIT(3)
+#define SW_FAST_MODE_OVERRIDE		BIT(2)
+
+#define REG_GLOBAL_OPTIONS		0x000F
+
+#define REG_SW_INT_STATUS__4		0x0010
+#define REG_SW_INT_MASK__4		0x0014
+
+#define LUE_INT				BIT(31)
+#define TRIG_TS_INT			BIT(30)
+#define APB_TIMEOUT_INT			BIT(29)
+#define OVER_TEMP_INT			BIT(28)
+#define HSR_INT				BIT(27)
+#define PIO_INT				BIT(26)
+#define POR_READY_INT			BIT(25)
+
+#define SWITCH_INT_MASK			\
+	(LUE_INT | TRIG_TS_INT | APB_TIMEOUT_INT | OVER_TEMP_INT | HSR_INT | \
+	 PIO_INT | POR_READY_INT)
+
+#define REG_SW_PORT_INT_STATUS__4	0x0018
+#define REG_SW_PORT_INT_MASK__4		0x001C
+
+/* 1 - Global */
+#define REG_SW_GLOBAL_SERIAL_CTRL_0	0x0100
+
+#define SW_LITTLE_ENDIAN		BIT(4)
+#define SPI_AUTO_EDGE_DETECTION		BIT(1)
+#define SPI_CLOCK_OUT_RISING_EDGE	BIT(0)
+
+#define REG_SW_GLOBAL_OUTPUT_CTRL__1	0x0103
+#define SW_CLK125_ENB			BIT(1)
+#define SW_CLK25_ENB			BIT(0)
+
+/* 2 - PHY */
+#define REG_SW_POWER_MANAGEMENT_CTRL	0x0201
+
+/* 3 - Operation Control */
+#define REG_SW_OPERATION		0x0300
+
+#define SW_DOUBLE_TAG			BIT(7)
+#define SW_OVER_TEMP_ENABLE		BIT(2)
+#define SW_RESET			BIT(1)
+#define SW_START			BIT(0)
+
+#define REG_SW_LUE_CTRL_0		0x0310
+#define SW_VLAN_ENABLE			BIT(7)
+#define SW_DROP_INVALID_VID		BIT(6)
+#define SW_AGE_CNT_M			0x7
+#define SW_AGE_CNT_S			3
+#define SW_RESV_MCAST_ENABLE		BIT(2)
+
+#define REG_SW_LUE_CTRL_1		0x0311
+
+#define UNICAST_LEARN_DISABLE		BIT(7)
+#define SW_SRC_ADDR_FILTER		BIT(6)
+#define SW_FLUSH_STP_TABLE		BIT(5)
+#define SW_FLUSH_MSTP_TABLE		BIT(4)
+#define SW_FWD_MCAST_SRC_ADDR		BIT(3)
+#define SW_AGING_ENABLE			BIT(2)
+#define SW_FAST_AGING			BIT(1)
+#define SW_LINK_AUTO_AGING		BIT(0)
+
+#define REG_SW_LUE_CTRL_2		0x0312
+
+#define SW_MID_RANGE_AGE		BIT(7)
+#define SW_LINK_DOWN_FLUSH		BIT(6)
+#define SW_EGRESS_VLAN_FILTER_DYN	BIT(5)
+#define SW_EGRESS_VLAN_FILTER_STA	BIT(4)
+#define SW_FLUSH_OPTION_M		0x3
+#define SW_FLUSH_OPTION_S		2
+#define SW_FLUSH_OPTION_DYN_MAC		1
+#define SW_FLUSH_OPTION_STA_MAC		2
+#define SW_FLUSH_OPTION_BOTH		3
+
+#define REG_SW_LUE_CTRL_3		0x0313
+#define REG_SW_AGE_PERIOD__1		0x0313
+
+#define REG_SW_LUE_INT_STATUS__1	0x0314
+#define REG_SW_LUE_INT_MASK__1		0x0315
+
+#define LEARN_FAIL_INT			BIT(2)
+#define WRITE_FAIL_INT			BIT(0)
+
+#define LUE_INT_MASK			(LEARN_FAIL_INT | WRITE_FAIL_INT)
+
+#define REG_SW_LUE_INDEX_0__2		0x0316
+
+#define ENTRY_INDEX_M			0x0FFF
+
+#define REG_SW_LUE_INDEX_1__2		0x0318
+
+#define FAIL_INDEX_M			0x03FF
+
+#define REG_SW_LUE_INDEX_2__2		0x031A
+
+#define REG_SW_STATIC_AVAIL_ENTRY__4	0x031C
+
+#define SW_INGRESS_FILTERING_NO_LEARN	BIT(15)
+#define SW_STATIC_AVAIL_CNT		0x1FF
+
+#define REG_SW_AGE_PERIOD__2		0x0320
+#define SW_AGE_PERIOD_M			0xFFF
+
+#define REG_SW_LUE_UNK_UCAST_CTRL__2	0x0322
+#define REG_SW_LUE_UNK_CTRL_0__4	0x0322
+
+#define SW_UNK_UCAST_ENABLE		BIT(15)
+#define SW_UNK_PORTS_M			0xFF
+
+#define REG_SW_LUE_UNK_MCAST_CTRL__2	0x0324
+#define SW_UNK_MCAST_ENABLE		BIT(15)
+
+#define REG_SW_LUE_UNK_VID_CTRL__2	0x0326
+#define SW_UNK_VID_ENABLE		BIT(15)
+
+#define SW_VLAN_FLUSH_PORTS_M		0xFF
+
+#define REG_SW_STATIC_ENTRY_LIMIT__4	0x032C
+
+#define REG_SW_MAC_CTRL_0			0x0330
+#define SW_NEW_BACKOFF			BIT(7)
+#define SW_PAUSE_UNH_MODE		BIT(1)
+#define SW_AGGR_BACKOFF			BIT(0)
+
+#define REG_SW_MAC_CTRL_1		0x0331
+#define SW_SHORT_IFG			BIT(7)
+#define MULTICAST_STORM_DISABLE		BIT(6)
+#define SW_BACK_PRESSURE		BIT(5)
+#define FAIR_FLOW_CTRL			BIT(4)
+#define NO_EXC_COLLISION_DROP		BIT(3)
+#define SW_LEGAL_PACKET_DISABLE		BIT(1)
+#define SW_PASS_SHORT_FRAME		BIT(0)
+
+#define REG_SW_MAC_CTRL_2		0x0332
+#define SW_REPLACE_VID			BIT(3)
+#define BROADCAST_STORM_RATE_HI		0x07
+
+#define REG_SW_MAC_CTRL_3		0x0333
+#define BROADCAST_STORM_RATE_LO		0xFF
+#define BROADCAST_STORM_RATE		0x07FF
+
+#define REG_SW_MAC_CTRL_4		0x0334
+#define SW_PASS_PAUSE			BIT(3)
+
+#define REG_SW_MAC_CTRL_5		0x0335
+#define SW_OUT_RATE_LIMIT_QUEUE_BASED	BIT(3)
+
+#define REG_SW_MAC_CTRL_6		0x0336
+#define SW_MIB_COUNTER_FLUSH		BIT(7)
+#define SW_MIB_COUNTER_FREEZE		BIT(6)
+
+#define REG_SW_MRI_CTRL_0		0x0370
+#define SW_IGMP_SNOOP			BIT(6)
+#define SW_IPV6_MLD_OPTION		BIT(3)
+#define SW_IPV6_MLD_SNOOP		BIT(2)
+#define SW_MIRROR_RX_TX			BIT(0)
+
+#define REG_SW_MRI_CTRL_1__4		0x0374
+#define REG_SW_MRI_CTRL_2__4		0x0378
+#define REG_SW_CLASS_D_IP_CTRL__4	0x0374
+
+#define SW_CLASS_D_IP_ENABLE		BIT(31)
+
+#define REG_SW_MRI_CTRL_8		0x0378
+#define SW_RED_COLOR_S			4
+#define SW_YELLOW_COLOR_S		2
+#define SW_GREEN_COLOR_S		0
+#define SW_COLOR_M			0x3
+
+#define REG_PTP_EVENT_PRIO_CTRL		0x037C
+#define REG_PTP_GENERAL_PRIO_CTRL	0x037D
+#define PTP_PRIO_ENABLE			BIT(7)
+
+#define REG_SW_QM_CTRL__4		0x0390
+#define PRIO_SCHEME_SELECT_M		KS_PRIO_M
+#define PRIO_SCHEME_SELECT_S		6
+#define PRIO_MAP_3_HI			0
+#define PRIO_MAP_2_HI			2
+#define PRIO_MAP_0_LO			3
+#define UNICAST_VLAN_BOUNDARY		BIT(1)
+
+#define REG_SW_EEE_QM_CTRL__2		0x03C0
+#define REG_SW_EEE_TXQ_WAIT_TIME__2	0x03C2
+
+/* 4 - */
+#define REG_SW_VLAN_ENTRY__4		0x0400
+#define VLAN_VALID			BIT(31)
+#define VLAN_FORWARD_OPTION		BIT(27)
+#define VLAN_PRIO_M			KS_PRIO_M
+#define VLAN_PRIO_S			24
+#define VLAN_MSTP_M			0x7
+#define VLAN_MSTP_S			12
+#define VLAN_FID_M			0x7F
+
+#define REG_SW_VLAN_ENTRY_UNTAG__4	0x0404
+#define REG_SW_VLAN_ENTRY_PORTS__4	0x0408
+#define REG_SW_VLAN_ENTRY_INDEX__2	0x040C
+
+#define VLAN_INDEX_M			0x0FFF
+
+#define REG_SW_VLAN_CTRL		0x040E
+#define VLAN_START			BIT(7)
+#define VLAN_ACTION			0x3
+#define VLAN_WRITE			1
+#define VLAN_READ			2
+#define VLAN_CLEAR			3
+
+#define REG_SW_ALU_INDEX_0		0x0410
+#define ALU_FID_INDEX_S			16
+#define ALU_FID_SIZE			127
+#define ALU_MAC_ADDR_HI			0xFFFF
+
+#define REG_SW_ALU_INDEX_1		0x0414
+#define ALU_DIRECT_INDEX_M		(BIT(12) - 1)
+
+#define REG_SW_ALU_CTRL__4		0x0418
+#define REG_SW_ALU_CTRL(num)	(REG_SW_ALU_CTRL__4 + ((num) * 4))
+
+#define ALU_STA_DYN_CNT			2
+#define ALU_VALID_CNT_M			(BIT(14) - 1)
+#define ALU_VALID_CNT_S			16
+#define ALU_START			BIT(7)
+#define ALU_VALID			BIT(6)
+#define ALU_VALID_OR_STOP		BIT(5)
+#define ALU_DIRECT			BIT(2)
+#define ALU_ACTION			0x3
+#define ALU_WRITE			1
+#define ALU_READ			2
+#define ALU_SEARCH			3
+
+#define REG_SW_ALU_STAT_CTRL__4		0x041C
+#define ALU_STAT_VALID_CNT_M		(BIT(9) - 1)
+#define ALU_STAT_VALID_CNT_S		20
+#define ALU_STAT_INDEX_M		(BIT(8) - 1)
+#define ALU_STAT_INDEX_S		8
+#define ALU_RESV_MCAST_INDEX_M		(BIT(6) - 1)
+#define ALU_STAT_START			BIT(7)
+#define ALU_STAT_VALID			BIT(6)
+#define ALU_STAT_VALID_OR_STOP		BIT(5)
+#define ALU_STAT_USE_FID		BIT(4)
+#define ALU_STAT_DIRECT			BIT(3)
+#define ALU_RESV_MCAST_ADDR		BIT(2)
+#define ALU_STAT_ACTION			0x3
+#define ALU_STAT_WRITE			1
+#define ALU_STAT_READ			2
+#define ALU_STAT_SEARCH			3
+
+#define REG_SW_ALU_VAL_A		0x0420
+#define ALU_V_STATIC_VALID		BIT(31)
+#define ALU_V_SRC_FILTER		BIT(30)
+#define ALU_V_DST_FILTER		BIT(29)
+#define ALU_V_PRIO_AGE_CNT_M		(BIT(3) - 1)
+#define ALU_V_PRIO_AGE_CNT_S		26
+#define ALU_V_MSTP_M			0x7
+
+#define REG_SW_ALU_VAL_B		0x0424
+#define ALU_V_OVERRIDE			BIT(31)
+#define ALU_V_USE_FID			BIT(30)
+#define ALU_V_PORT_MAP			0xFF
+
+#define REG_SW_ALU_VAL_C		0x0428
+#define ALU_V_FID_M			(BIT(16) - 1)
+#define ALU_V_FID_S			16
+#define ALU_V_MAC_ADDR_HI		0xFFFF
+
+#define REG_SW_ALU_VAL_D		0x042C
+
+#define PORT_CTRL_ADDR(port, addr)	((addr) | (((port) + 1)  << 12))
+
+#define REG_GLOBAL_RR_INDEX__1		0x0600
+
+/* VPHY */
+#define REG_VPHY_CTRL__2		0x0700
+#define REG_VPHY_STAT__2		0x0704
+#define REG_VPHY_ID_HI__2		0x0708
+#define REG_VPHY_ID_LO__2		0x070C
+#define REG_VPHY_AUTO_NEG__2		0x0710
+#define REG_VPHY_REMOTE_CAP__2		0x0714
+
+#define REG_VPHY_EXPANSION__2		0x0718
+
+#define REG_VPHY_M_CTRL__2		0x0724
+#define REG_VPHY_M_STAT__2		0x0728
+
+#define REG_VPHY_EXT_STAT__2		0x073C
+#define VPHY_EXT_1000_X_FULL		BIT(15)
+#define VPHY_EXT_1000_X_HALF		BIT(14)
+#define VPHY_EXT_1000_T_FULL		BIT(13)
+#define VPHY_EXT_1000_T_HALF		BIT(12)
+
+#define REG_VPHY_DEVAD_0__2		0x0740
+#define REG_VPHY_DEVAD_1__2		0x0744
+#define REG_VPHY_DEVAD_2__2		0x0748
+#define REG_VPHY_DEVAD_3__2		0x074C
+
+#define VPHY_DEVAD_UPDATE		BIT(7)
+#define VPHY_DEVAD_M			0x1F
+#define VPHY_DEVAD_S			8
+
+#define REG_VPHY_SMI_ADDR__2		0x0750
+#define REG_VPHY_SMI_DATA_LO__2		0x0754
+#define REG_VPHY_SMI_DATA_HI__2		0x0758
+
+#define REG_VPHY_IND_ADDR__2		0x075C
+#define REG_VPHY_IND_DATA__2		0x0760
+#define REG_VPHY_IND_CTRL__2		0x0768
+
+#define VPHY_IND_WRITE			BIT(1)
+#define VPHY_IND_BUSY			BIT(0)
+
+#define REG_VPHY_SPECIAL_CTRL__2	0x077C
+#define VPHY_SMI_INDIRECT_ENABLE	BIT(15)
+#define VPHY_SW_LOOPBACK		BIT(14)
+#define VPHY_MDIO_INTERNAL_ENABLE	BIT(13)
+#define VPHY_SPI_INDIRECT_ENABLE	BIT(12)
+#define VPHY_PORT_MODE_M		0x3
+#define VPHY_PORT_MODE_S		8
+#define VPHY_MODE_RGMII			0
+#define VPHY_MODE_MII_PHY		1
+#define VPHY_MODE_SGMII			2
+#define VPHY_MODE_RMII_PHY		3
+#define VPHY_SW_COLLISION_TEST		BIT(7)
+#define VPHY_SPEED_DUPLEX_STAT_M	0x7
+#define VPHY_SPEED_DUPLEX_STAT_S	2
+#define VPHY_SPEED_1000			BIT(4)
+#define VPHY_SPEED_100			BIT(3)
+#define VPHY_FULL_DUPLEX		BIT(2)
+
+/* 0 - Operation */
+#define REG_PORT_DEFAULT_VID		0x0000
+
+#define REG_PORT_CUSTOM_VID		0x0002
+#define REG_PORT_PME_STATUS		0x0013
+
+#define REG_PORT_PME_CTRL		0x0017
+#define PME_WOL_MAGICPKT		BIT(2)
+#define PME_WOL_LINKUP			BIT(1)
+#define PME_WOL_ENERGY			BIT(0)
+
+#define REG_PORT_INT_STATUS		0x001B
+#define REG_PORT_INT_MASK		0x001F
+
+#define PORT_TAS_INT			BIT(5)
+#define PORT_SGMII_INT			BIT(3)
+#define PORT_PTP_INT			BIT(2)
+#define PORT_PHY_INT			BIT(1)
+#define PORT_ACL_INT			BIT(0)
+
+#define PORT_INT_MASK			\
+	(				\
+	PORT_TAS_INT |			\
+	PORT_SGMII_INT | PORT_PTP_INT | PORT_PHY_INT | PORT_ACL_INT)
+
+#define REG_PORT_CTRL_0			0x0020
+
+#define PORT_MAC_LOOPBACK		BIT(7)
+#define PORT_MAC_REMOTE_LOOPBACK	BIT(6)
+#define PORT_K2L_INSERT_ENABLE		BIT(5)
+#define PORT_K2L_DEBUG_ENABLE		BIT(4)
+#define PORT_TAIL_TAG_ENABLE		BIT(2)
+#define PORT_QUEUE_SPLIT_ENABLE		0x3
+
+#define REG_PORT_CTRL_1			0x0021
+#define PORT_SRP_ENABLE			0x3
+
+#define REG_PORT_STATUS_0		0x0030
+#define PORT_INTF_SPEED_M		0x3
+#define PORT_INTF_SPEED_S		3
+#define PORT_INTF_FULL_DUPLEX		BIT(2)
+#define PORT_TX_FLOW_CTRL		BIT(1)
+#define PORT_RX_FLOW_CTRL		BIT(0)
+
+#define REG_PORT_STATUS_1		0x0034
+
+/* 1 - PHY */
+#define REG_VPHY_SMI_ADDR		0x14
+#define REG_VPHY_SMI_DATA_LO		0x15
+#define REG_VPHY_SMI_DATA_HI		0x16
+
+#define REG_VPHY_SPECIAL_CTRL_STAT	0x1F
+
+#define REG_PORT_T1_PHY_CTRL_BASE	0x0100
+#define REG_PORT_TX_PHY_CTRL_BASE	0x0280
+#define REG_TX_PHY_CTRL_BASE		0x0980
+
+#define REG_PORT_PHY_CTRL		0x0100
+
+#define PORT_PHY_RESET			BIT(15)
+#define PORT_PHY_LOOPBACK		BIT(14)
+#define PORT_SPEED_100MBIT		BIT(13)
+#define PORT_AUTO_NEG_ENABLE		BIT(12)
+#define PORT_POWER_DOWN			BIT(11)
+#define PORT_ISOLATE			BIT(10)
+#define PORT_AUTO_NEG_RESTART		BIT(9)
+#define PORT_FULL_DUPLEX		BIT(8)
+#define PORT_COLLISION_TEST		BIT(7)
+#define PORT_SPEED_1000MBIT		BIT(6)
+
+#define REG_PORT_PHY_STATUS		0x0102
+
+#define PORT_100BT4_CAPABLE		BIT(15)
+#define PORT_100BTX_FD_CAPABLE		BIT(14)
+#define PORT_100BTX_CAPABLE		BIT(13)
+#define PORT_10BT_FD_CAPABLE		BIT(12)
+#define PORT_10BT_CAPABLE		BIT(11)
+#define PORT_EXTENDED_STATUS		BIT(8)
+#define PORT_MII_SUPPRESS_CAPABLE	BIT(6)
+#define PORT_AUTO_NEG_ACKNOWLEDGE	BIT(5)
+#define PORT_REMOTE_FAULT		BIT(4)
+#define PORT_AUTO_NEG_CAPABLE		BIT(3)
+#define PORT_LINK_STATUS		BIT(2)
+#define PORT_JABBER_DETECT		BIT(1)
+#define PORT_EXTENDED_CAPABILITY	BIT(0)
+
+#define REG_PORT_PHY_ID_HI		0x0104
+#define REG_PORT_PHY_ID_LO		0x0106
+
+#define LAN937X_ID_HI			0x0007
+#define LAN937X_ID_LO			0x1951
+
+#define REG_PORT_PHY_1000_CTRL		0x0112
+#define PORT_AUTO_NEG_MANUAL			BIT(12)
+#define PORT_AUTO_NEG_M		BIT(11)
+#define PORT_AUTO_NEG_M_PREFERRED	BIT(10)
+#define PORT_AUTO_NEG_1000BT_FD		BIT(9)
+#define PORT_AUTO_NEG_1000BT			BIT(8)
+
+#define REG_PORT_PHY_1000_STATUS	0x0114
+
+#define REG_PORT_PHY_RXER_COUNTER	0x012A
+#define REG_PORT_PHY_INT_ENABLE		0x0136
+#define REG_PORT_PHY_INT_STATUS		0x0137
+
+/* Same as PORT_PHY_LOOPBACK */
+#define PORT_PHY_PCS_LOOPBACK		BIT(0)
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_2	0x013A
+
+#define REG_PORT_PHY_DIGITAL_DEBUG_3	0x013C
+#define PORT_100BT_FIXED_LATENCY	BIT(15)
+
+#define REG_PORT_PHY_PHY_CTRL		0x013E
+#define PORT_INT_PIN_HIGH		BIT(14)
+#define PORT_ENABLE_JABBER		BIT(9)
+#define PORT_STAT_SPEED_1000MBIT	BIT(6)
+#define PORT_STAT_SPEED_100MBIT		BIT(5)
+#define PORT_STAT_SPEED_10MBIT		BIT(4)
+#define PORT_STAT_FULL_DUPLEX		BIT(3)
+
+/* Same as PORT_PHY_STAT_M */
+#define PORT_STAT_M		BIT(2)
+#define PORT_RESET			BIT(1)
+#define PORT_LINK_STATUS_FAIL		BIT(0)
+
+/* 3 - xMII */
+#define REG_PORT_XMII_CTRL_0		0x0300
+#define PORT_SGMII_SEL			BIT(7)
+#define PORT_MII_FULL_DUPLEX		BIT(6)
+#define PORT_MII_TX_FLOW_CTRL		BIT(5)
+#define PORT_MII_100MBIT		BIT(4)
+#define PORT_MII_RX_FLOW_CTRL		BIT(3)
+#define PORT_GRXC_ENABLE		BIT(0)
+
+#define REG_PORT_XMII_CTRL_1		0x0301
+#define PORT_MII_NOT_1GBIT		BIT(6)
+#define PORT_MII_SEL_EDGE		BIT(5)
+#define PORT_RGMII_ID_IG_ENABLE		BIT(4)
+#define PORT_RGMII_ID_EG_ENABLE		BIT(3)
+#define PORT_MII_MAC_MODE		BIT(2)
+#define PORT_MII_SEL_M			0x3
+#define PORT_RGMII_SEL			0x0
+#define PORT_RMII_SEL			0x1
+#define PORT_MII_SEL			0x2
+
+#define REG_PORT_XMII_CTRL_2		0x0302
+#define PORT_RGMII_RX_STS_ENABLE	BIT(0)
+
+#define REG_PORT_XMII_CTRL_4		0x0304
+#define REG_PORT_XMII_CTRL_5		0x0305
+
+/* 4 - MAC */
+#define REG_PORT_MAC_CTRL_0		0x0400
+#define PORT_CHECK_LENGTH		BIT(2)
+#define PORT_BROADCAST_STORM		BIT(1)
+#define PORT_JUMBO_PACKET		BIT(0)
+
+#define REG_PORT_MAC_CTRL_1		0x0401
+#define PORT_BACK_PRESSURE		BIT(3)
+#define PORT_PASS_ALL			BIT(0)
+
+#define REG_PORT_MAC_CTRL_2		0x0402
+#define PORT_100BT_EEE_DISABLE		BIT(7)
+#define PORT_1000BT_EEE_DISABLE		BIT(6)
+
+#define REG_PORT_MAC_IN_RATE_LIMIT	0x0403
+
+#define REG_PORT_MTU__2			0x0404
+#define PORT_RATE_LIMIT_M		(BIT(7) - 1)
+
+/* 5 - MIB Counters */
+#define REG_PORT_MIB_CTRL_STAT__4	0x0500
+#define MIB_COUNTER_OVERFLOW		BIT(31)
+#define MIB_COUNTER_VALID		BIT(30)
+#define MIB_COUNTER_READ		BIT(25)
+#define MIB_COUNTER_FLUSH_FREEZE	BIT(24)
+#define MIB_COUNTER_INDEX_M		(BIT(8) - 1)
+#define MIB_COUNTER_INDEX_S		16
+#define MIB_COUNTER_DATA_HI_M		0xF
+
+#define REG_PORT_MIB_DATA		0x0504
+
+/* 8 - Classification and Policing */
+#define REG_PORT_MRI_MIRROR_CTRL	0x0800
+#define PORT_MIRROR_RX			BIT(6)
+#define PORT_MIRROR_TX			BIT(5)
+#define PORT_MIRROR_SNIFFER		BIT(1)
+
+#define REG_PORT_MRI_PRIO_CTRL		0x0801
+#define PORT_HIGHEST_PRIO		BIT(7)
+#define PORT_OR_PRIO			BIT(6)
+#define PORT_MAC_PRIO_ENABLE		BIT(4)
+#define PORT_VLAN_PRIO_ENABLE		BIT(3)
+#define PORT_802_1P_PRIO_ENABLE		BIT(2)
+#define PORT_DIFFSERV_PRIO_ENABLE	BIT(1)
+#define PORT_ACL_PRIO_ENABLE		BIT(0)
+
+#define REG_PORT_MRI_MAC_CTRL		0x0802
+#define PORT_USER_PRIO_CEILING		BIT(7)
+#define PORT_DROP_NON_VLAN		BIT(4)
+#define PORT_DROP_TAG			BIT(3)
+#define PORT_BASED_PRIO_M		KS_PRIO_M
+#define PORT_BASED_PRIO_S		0
+
+#define REG_PORT_MRI_TC_MAP__4		0x0808
+
+/* 9 - Shaping */
+#define REG_PORT_MTI_QUEUE_INDEX__4	0x0900
+
+#define REG_PORT_MTI_QUEUE_CTRL_0__4	0x0904
+#define MTI_PVID_REPLACE		BIT(0)
+
+#define REG_PORT_MTI_QUEUE_CTRL_0	0x0914
+
+/* A - QM */
+#define REG_PORT_QM_CTRL__4		0x0A00
+#define PORT_QM_DROP_PRIO_M		0x3
+
+#define REG_PORT_VLAN_MEMBERSHIP__4	0x0A04
+
+#define REG_PORT_QM_QUEUE_INDEX__4	0x0A08
+#define PORT_QM_QUEUE_INDEX_S		24
+#define PORT_QM_BURST_SIZE_S		16
+#define PORT_QM_MIN_RESV_SPACE_M	(BIT(11) - 1)
+
+#define REG_PORT_QM_WATER_MARK__4	0x0A0C
+#define PORT_QM_HI_WATER_MARK_S		16
+#define PORT_QM_LO_WATER_MARK_S		0
+#define PORT_QM_WATER_MARK_M		(BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_0__4		0x0A10
+#define PORT_QM_TX_CNT_USED_S		0
+#define PORT_QM_TX_CNT_M		(BIT(11) - 1)
+
+#define REG_PORT_QM_TX_CNT_1__4		0x0A14
+#define PORT_QM_TX_CNT_CALCULATED_S	16
+#define PORT_QM_TX_CNT_AVAIL_S		0
+
+/* B - LUE */
+#define REG_PORT_LUE_CTRL		0x0B00
+
+#define PORT_VLAN_LOOKUP_VID_0		BIT(7)
+#define PORT_INGRESS_FILTER		BIT(6)
+#define PORT_DISCARD_NON_VID		BIT(5)
+#define PORT_MAC_BASED_802_1X		BIT(4)
+#define PORT_SRC_ADDR_FILTER		BIT(3)
+
+#define REG_PORT_LUE_MSTP_INDEX		0x0B01
+
+#define REG_PORT_LUE_MSTP_STATE		0x0B04
+
+#define PORT_TX_ENABLE			BIT(2)
+#define PORT_RX_ENABLE			BIT(1)
+#define PORT_LEARN_DISABLE		BIT(0)
+
+#define REG_PORT_LUE_LEARN_CNT__2	0x0B08
+
+#define REG_PORT_LUE_UNK_CTL0	0x0B0E
+#define REG_PORT_LUE_UNK_CTL1	0x0B10
+#define REG_PORT_LUE_UNK_VID_CTRL__2	0x0B12
+
+#define PORT_UNK_UCAST_MCAST_ENABLE		BIT(15)
+#define PORT_UCAST_MCAST_MASK		0xFF
+#define PORT_UNK_VID_ENABLE		BIT(15)
+
+#define PRIO_QUEUES			8
+#define RX_PRIO_QUEUES			8
+#define KS_PRIO_IN_REG			2
+#define TOTAL_PORT_NUM			8
+
+#define LAN937X_COUNTER_NUM		0x20
+#define TOTAL_LAN937X_COUNTER_NUM	(LAN937X_COUNTER_NUM + 2 + 2)
+
+#define SWITCH_COUNTER_NUM		LAN937X_COUNTER_NUM
+
+#define P_BCAST_STORM_CTRL		REG_PORT_MAC_CTRL_0
+#define P_PRIO_CTRL			REG_PORT_MRI_PRIO_CTRL
+#define P_MIRROR_CTRL			REG_PORT_MRI_MIRROR_CTRL
+#define P_STP_CTRL			REG_PORT_LUE_MSTP_STATE
+#define P_PHY_CTRL			REG_PORT_PHY_CTRL
+#define P_NEG_RESTART_CTRL		REG_PORT_PHY_CTRL
+#define P_LINK_STATUS			REG_PORT_PHY_STATUS
+#define P_SPEED_STATUS			REG_PORT_PHY_PHY_CTRL
+#define P_RATE_LIMIT_CTRL		REG_PORT_MAC_IN_RATE_LIMIT
+
+#define S_LINK_AGING_CTRL		REG_SW_LUE_CTRL_1
+#define S_MIRROR_CTRL			REG_SW_MRI_CTRL_0
+#define S_REPLACE_VID_CTRL		REG_SW_MAC_CTRL_2
+#define S_802_1P_PRIO_CTRL		REG_SW_MAC_802_1P_MAP_0
+#define S_TOS_PRIO_CTRL			REG_SW_MAC_TOS_PRIO_0
+#define S_FLUSH_TABLE_CTRL		REG_SW_LUE_CTRL_1
+
+#define REG_SWITCH_RESET		REG_RESET_CTRL
+
+#define SW_FLUSH_DYN_MAC_TABLE		SW_FLUSH_MSTP_TABLE
+
+#define MAX_TIMESTAMP_UNIT		2
+#define MAX_TRIG_UNIT			3
+#define MAX_TIMESTAMP_EVENT_UNIT	8
+#define MAX_GPIO			2
+#define MAX_CLOCK			2
+
+#define PTP_TRIG_UNIT_M			(BIT(MAX_TRIG_UNIT) - 1)
+#define PTP_TS_UNIT_M			(BIT(MAX_TIMESTAMP_UNIT) - 1)
+
+#define TAIL_TAG_PTP			BIT(7)
+#define TAIL_TAG_NEXT_CHIP		BIT(6)
+#define TAIL_TAG_K2L			BIT(5)
+#define TAIL_TAG_PTP_1_STEP		BIT(4)
+#define TAIL_TAG_PTP_P2P		BIT(3)
+#define TAIL_TAG_RX_PORTS_M		0x7
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE		9969
+
+#define SW_CHECK_LENGTH			BIT(3)
+
+#define FR_MIN_SIZE		1522
+#define FR_MAX_SIZE		9000
+
+#define PORT_JUMBO_EN			BIT(0)
+#define PORT_FR_CHK_LENGTH		BIT(2)
+#define PORT_MAX_FR_SIZE		0x404
+
+#define FR_SIZE_CPU_PORT		1540
+
+#define REG_PORT_CTRL_0				0x0020
+#define PORT_MAC_LOOPBACK			BIT(7)
+#define PORT_FORCE_TX_FLOW_CTRL		BIT(5)
+#define PORT_FORCE_RX_FLOW_CTRL		BIT(3)
+
+#define PORT_QUEUE_SPLIT_ENABLE		0x3
+
+/* Get fid from vid, fid 0 is not used if vid is greater than 127 */
+#define LAN937X_GET_FID(vid)	(((vid) % ALU_FID_SIZE) + 1)
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROT_RATE	10
+
+#define MII_BMSR_100BASE_TX_FD		BIT(14)
+
+#define PHY_LINK_UP					1
+#define PHY_LINK_DOWN				0
+
+/*The port number as per the datasheet*/
+#define RGMII_2_PORT_NUM		5
+#define RGMII_1_PORT_NUM		6
+#define SGMII_PORT_NUM			4
+#define TXPHY_PORT_NUM			4
+
+#define GET_CHIP_ID_LSB(chip_id)	(((chip_id) >> 8) & 0xff)
+#define LAN937X_RGMII_2_PORT		(RGMII_2_PORT_NUM - 1)
+#define LAN937X_RGMII_1_PORT		(RGMII_1_PORT_NUM - 1)
+#define LAN937X_SGMII_PORT			(SGMII_PORT_NUM - 1)
+#define LAN937X_TXPHY_PORT			(TXPHY_PORT_NUM - 1)
+
+#endif
diff --git a/drivers/net/dsa/microchip/lan937x_spi.c b/drivers/net/dsa/microchip/lan937x_spi.c
new file mode 100644
index 000000000000..d9731d6afb96
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_spi.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver register access through SPI
+ * Copyright (C) 2019-2021 Microchip Technology Inc.
+ */
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+
+#include "ksz_common.h"
+
+#define SPI_ADDR_SHIFT				24
+#define SPI_ADDR_ALIGN				3
+#define SPI_TURNAROUND_SHIFT		5
+
+KSZ_REGMAP_TABLE(lan937x, 32, SPI_ADDR_SHIFT,
+		 SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
+
+struct lan937x_chip_data {
+	u32 chip_id;
+	const char *dev_name;
+	int num_vlans;
+	int num_alus;
+	int num_statics;
+	int cpu_ports;
+	int port_cnt;
+};
+
+static const struct of_device_id lan937x_dt_ids[];
+
+static const struct lan937x_chip_data lan937x_switch_chips[] = {
+	{
+		.chip_id = 0x00937010,
+		.dev_name = "LAN9370",
+		.num_vlans = 4096,
+		.num_alus = 1024,
+		.num_statics = 256,
+		/* can be configured as cpu port */
+		.cpu_ports = 0x10,
+		/* total port count */
+		.port_cnt = 5,
+	},
+	{
+		.chip_id = 0x00937110,
+		.dev_name = "LAN9371",
+		.num_vlans = 4096,
+		.num_alus = 1024,
+		.num_statics = 256,
+		/* can be configured as cpu port */
+		.cpu_ports = 0x30,
+		/* total port count */
+		.port_cnt = 6,
+	},
+	{
+		.chip_id = 0x00937210,
+		.dev_name = "LAN9372",
+		.num_vlans = 4096,
+		.num_alus = 1024,
+		.num_statics = 256,
+		/* can be configured as cpu port */
+		.cpu_ports = 0x30,
+		/* total port count */
+		.port_cnt = 8,
+	},
+	{
+		.chip_id = 0x00937310,
+		.dev_name = "LAN9373",
+		.num_vlans = 4096,
+		.num_alus = 1024,
+		.num_statics = 256,
+		/* can be configured as cpu port */
+		.cpu_ports = 0x38,
+		/* total port count */
+		.port_cnt = 5,
+	},
+	{
+		.chip_id = 0x00937410,
+		.dev_name = "LAN9374",
+		.num_vlans = 4096,
+		.num_alus = 1024,
+		.num_statics = 256,
+		/* can be configured as cpu port */
+		.cpu_ports = 0x30,
+		/* total port count */
+		.port_cnt = 8,
+	},
+
+};
+
+static int lan937x_spi_probe(struct spi_device *spi)
+{
+	struct regmap_config rc;
+	struct ksz_device *dev;
+	int i, ret;
+
+	dev = ksz_switch_alloc(&spi->dev, spi);
+	if (!dev)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(lan937x_regmap_config); i++) {
+		rc = lan937x_regmap_config[i];
+		rc.lock_arg = &dev->regmap_mutex;
+		dev->regmap[i] = devm_regmap_init_spi(spi, &rc);
+		if (IS_ERR(dev->regmap[i])) {
+			ret = PTR_ERR(dev->regmap[i]);
+			dev_err(&spi->dev,
+				"Failed to initialize regmap%i: %d\n",
+				lan937x_regmap_config[i].val_bits, ret);
+			return ret;
+		}
+	}
+
+	if (spi->dev.platform_data)
+		dev->pdata = spi->dev.platform_data;
+
+	ret = lan937x_switch_register(dev);
+
+	/* Main DSA driver may not be started yet. */
+	if (ret)
+		return ret;
+
+	spi_set_drvdata(spi, dev);
+
+	return 0;
+}
+
+int lan937x_check_device_id(struct ksz_device *dev)
+{
+	const struct lan937x_chip_data *dt_chip_data;
+	const struct of_device_id *match;
+	int i;
+
+	dt_chip_data = of_device_get_match_data(dev->dev);
+
+	if (!dt_chip_data)
+		return -EINVAL;
+
+	for (match = lan937x_dt_ids; match->compatible[0]; match++) {
+		const struct lan937x_chip_data *chip_data = match->data;
+
+		/* Check for chip id */
+		if (chip_data->chip_id != dev->chip_id)
+			continue;
+
+		/* Check for Device Tree and Chip ID */
+		if (dt_chip_data->chip_id != dev->chip_id) {
+			dev_err(dev->dev, "Device tree specifies chip %s but found %s, please fix it!\n",
+				dt_chip_data->dev_name, chip_data->dev_name);
+			return -ENODEV;
+		}
+
+		break;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lan937x_switch_chips); i++) {
+		const struct lan937x_chip_data *chip = &lan937x_switch_chips[i];
+
+		if (dev->chip_id == chip->chip_id) {
+			dev->name = chip->dev_name;
+			dev->num_vlans = chip->num_vlans;
+			dev->num_alus = chip->num_alus;
+			dev->num_statics = chip->num_statics;
+			dev->port_cnt = chip->port_cnt;
+			dev->cpu_ports = chip->cpu_ports;
+			break;
+		}
+	}
+
+	/* no switch found */
+	if (!dev->port_cnt)
+		return -ENODEV;
+
+	return 0;
+}
+EXPORT_SYMBOL(lan937x_check_device_id);
+
+static int lan937x_spi_remove(struct spi_device *spi)
+{
+	struct ksz_device *dev = spi_get_drvdata(spi);
+
+	if (dev)
+		ksz_switch_remove(dev);
+
+	return 0;
+}
+
+static void lan937x_spi_shutdown(struct spi_device *spi)
+{
+	struct ksz_device *dev = spi_get_drvdata(spi);
+
+	if (dev && dev->dev_ops->shutdown)
+		dev->dev_ops->shutdown(dev);
+}
+
+static const struct of_device_id lan937x_dt_ids[] = {
+	{ .compatible = "microchip,lan9370", .data = &lan937x_switch_chips[0] },
+	{ .compatible = "microchip,lan9371", .data = &lan937x_switch_chips[1] },
+	{ .compatible = "microchip,lan9372", .data = &lan937x_switch_chips[2] },
+	{ .compatible = "microchip,lan9373", .data = &lan937x_switch_chips[3] },
+	{ .compatible = "microchip,lan9374", .data = &lan937x_switch_chips[4] },
+	{},
+};
+MODULE_DEVICE_TABLE(of, lan937x_dt_ids);
+
+static struct spi_driver lan937x_spi_driver = {
+	.driver = {
+		.name	= "lan937x-switch",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(lan937x_dt_ids),
+	},
+	.probe	= lan937x_spi_probe,
+	.remove	= lan937x_spi_remove,
+	.shutdown = lan937x_spi_shutdown,
+};
+
+module_spi_driver(lan937x_spi_driver);
+
+MODULE_ALIAS("spi:lan937x");
+
+MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@microchip.com>");
+MODULE_DESCRIPTION("Microchip LAN937x Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (3 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 20:05   ` Vladimir Oltean
  2021-04-22  9:42 ` [PATCH v2 net-next 6/9] net: dsa: microchip: add support for ethtool port counters Prasanna Vengateshan
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Support for phylink_validate() and reused KSZ commmon API for
phylink_mac_link_down() operation

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/lan937x_main.c | 42 ++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 944c6f4d6d60..93c392081423 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -312,6 +312,46 @@ static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
 	return FR_MAX_SIZE;
 }
 
+static void lan937x_phylink_validate(struct dsa_switch *ds, int port,
+				     unsigned long *supported,
+			  struct phylink_link_state *state)
+{
+	struct ksz_device *dev = ds->priv;
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	if (phy_interface_mode_is_rgmii(state->interface) ||
+	    state->interface == PHY_INTERFACE_MODE_SGMII ||
+		state->interface == PHY_INTERFACE_MODE_RMII ||
+		state->interface == PHY_INTERFACE_MODE_MII ||
+		lan937x_is_internal_100BTX_phy_port(dev, port)) {
+		phylink_set(mask, 10baseT_Half);
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Half);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, Autoneg);
+		phylink_set_port_modes(mask);
+		phylink_set(mask, Pause);
+		phylink_set(mask, Asym_Pause);
+	}
+
+	/*  For RGMII & SGMII interfaces */
+	if (phy_interface_mode_is_rgmii(state->interface) ||
+	    state->interface == PHY_INTERFACE_MODE_SGMII) {
+		phylink_set(mask, 1000baseT_Full);
+	}
+
+	/* For T1 PHY */
+	if (lan937x_is_internal_t1_phy_port(dev, port)) {
+		phylink_set(mask, 100baseT1_Full);
+		phylink_set_port_modes(mask);
+	}
+
+	bitmap_and(supported, supported, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
 static int	lan937x_port_pre_bridge_flags(struct dsa_switch *ds, int port,
 					      struct switchdev_brport_flags flags,
 					 struct netlink_ext_ack *extack)
@@ -340,6 +380,8 @@ const struct dsa_switch_ops lan937x_switch_ops = {
 	.port_fast_age		= ksz_port_fast_age,
 	.port_max_mtu		= lan937x_get_max_mtu,
 	.port_change_mtu	= lan937x_change_mtu,
+	.phylink_validate	= lan937x_phylink_validate,
+	.phylink_mac_link_down	= ksz_mac_link_down,
 };
 
 int lan937x_switch_register(struct ksz_device *dev)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 6/9] net: dsa: microchip: add support for ethtool port counters
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (4 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Reused the KSZ common APIs for get_ethtool_stats() & get_sset_count()
along with relevant lan937x hooks for KSZ common layer and added
support for get_strings()

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/lan937x_main.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 93c392081423..573d2dd906f5 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -43,6 +43,21 @@ static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
 	return lan937x_internal_phy_write(dev, addr, reg, val);
 }
 
+static void lan937x_get_strings(struct dsa_switch *ds, int port,
+				u32 stringset, uint8_t *buf)
+{
+	struct ksz_device *dev = ds->priv;
+	int i;
+
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	for (i = 0; i < dev->mib_cnt; i++) {
+		memcpy(buf + i * ETH_GSTRING_LEN, lan937x_mib_names[i].string,
+		       ETH_GSTRING_LEN);
+	}
+}
+
 static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
 				       u8 state)
 {
@@ -372,6 +387,9 @@ const struct dsa_switch_ops lan937x_switch_ops = {
 	.phy_read		= lan937x_phy_read16,
 	.phy_write		= lan937x_phy_write16,
 	.port_enable		= ksz_enable_port,
+	.get_strings		= lan937x_get_strings,
+	.get_ethtool_stats	= ksz_get_ethtool_stats,
+	.get_sset_count		= ksz_sset_count,
 	.port_bridge_join	= ksz_port_bridge_join,
 	.port_bridge_leave	= ksz_port_bridge_leave,
 	.port_pre_bridge_flags	= lan937x_port_pre_bridge_flags,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (5 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 6/9] net: dsa: microchip: add support for ethtool port counters Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 20:11   ` Vladimir Oltean
  2021-04-22  9:42 ` [PATCH v2 net-next 8/9] net: dsa: microchip: add support for fdb and mdb management Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
  8 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Added support for port_mirror_add() and port_mirror_del operations

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/lan937x_main.c | 50 ++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 573d2dd906f5..bfce5098ea69 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -128,6 +128,54 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
 	mutex_unlock(&dev->dev_mutex);
 }
 
+static int lan937x_port_mirror_add(struct dsa_switch *ds, int port,
+				   struct dsa_mall_mirror_tc_entry *mirror,
+				   bool ingress)
+{
+	struct ksz_device *dev = ds->priv;
+	int rc;
+
+	if (ingress)
+		rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
+	else
+		rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
+
+	if (rc < 0)
+		return rc;
+
+	rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
+	if (rc < 0)
+		return rc;
+
+	/* configure mirror port */
+	rc = lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+			      PORT_MIRROR_SNIFFER, true);
+	if (rc < 0)
+		return rc;
+
+	rc = lan937x_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+	return rc;
+}
+
+static void lan937x_port_mirror_del(struct dsa_switch *ds, int port,
+				    struct dsa_mall_mirror_tc_entry *mirror)
+{
+	struct ksz_device *dev = ds->priv;
+	u8 data;
+
+	if (mirror->ingress)
+		lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
+	else
+		lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
+
+	lan937x_pread8(dev, port, P_MIRROR_CTRL, &data);
+
+	if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
+		lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+				 PORT_MIRROR_SNIFFER, false);
+}
+
 static phy_interface_t lan937x_get_interface(struct ksz_device *dev, int port)
 {
 	phy_interface_t interface;
@@ -396,6 +444,8 @@ const struct dsa_switch_ops lan937x_switch_ops = {
 	.port_bridge_flags	= lan937x_port_bridge_flags,
 	.port_stp_state_set	= lan937x_port_stp_state_set,
 	.port_fast_age		= ksz_port_fast_age,
+	.port_mirror_add	= lan937x_port_mirror_add,
+	.port_mirror_del	= lan937x_port_mirror_del,
 	.port_max_mtu		= lan937x_get_max_mtu,
 	.port_change_mtu	= lan937x_change_mtu,
 	.phylink_validate	= lan937x_phylink_validate,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 8/9] net: dsa: microchip: add support for fdb and mdb management
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (6 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22  9:42 ` [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
  8 siblings, 0 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Support for fdb_add, mdb_add, fdb_del, mdb_del and
fdb_dump operations

It aligns with latest update of removing switchdev
transactional logic from mdb entries

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/lan937x_main.c | 516 +++++++++++++++++++++++
 1 file changed, 516 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index bfce5098ea69..7f6183dc0e31 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -14,6 +14,68 @@
 #include "ksz_common.h"
 #include "lan937x_dev.h"
 
+static int lan937x_read_table(struct ksz_device *dev, u32 *table)
+{
+	int rc;
+
+	/* read alu table */
+	rc = ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
+
+	return rc;
+}
+
+static int lan937x_write_table(struct ksz_device *dev, u32 *table)
+{
+	int rc;
+
+	/* write alu table */
+	rc = ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
+	if (rc < 0)
+		return rc;
+
+	rc = ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
+
+	return rc;
+}
+
+static int lan937x_wait_alu_ready(int alu, struct ksz_device *dev)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(dev->regmap[2], REG_SW_ALU_CTRL(alu),
+					val, !(val & ALU_START), 10, 1000);
+}
+
+static int lan937x_wait_alu_sta_ready(struct ksz_device *dev)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(dev->regmap[2],
+					REG_SW_ALU_STAT_CTRL__4,
+					val, !(val & ALU_STAT_START),
+					10, 1000);
+}
+
 static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
 						      int port,
 						      enum dsa_tag_protocol mp)
@@ -128,6 +190,455 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
 	mutex_unlock(&dev->dev_mutex);
 }
 
+static u8 lan937x_get_fid(u16 vid)
+{
+	if (vid > ALU_FID_SIZE)
+		return LAN937X_GET_FID(vid);
+	else
+		return vid;
+}
+
+static int lan937x_port_fdb_add(struct dsa_switch *ds, int port,
+				const unsigned char *addr, u16 vid)
+{
+	struct ksz_device *dev = ds->priv;
+	u8 fid = lan937x_get_fid(vid);
+	u32 alu_table[4];
+	int rc, i;
+	u32 data;
+	u8 val;
+
+	mutex_lock(&dev->alu_mutex);
+
+	for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+		/* find any entry with mac & fid */
+		data = fid << ALU_FID_INDEX_S;
+		data |= ((addr[0] << 8) | addr[1]);
+
+		rc = ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+		if (rc < 0)
+			goto exit;
+
+		data = ((addr[2] << 24) | (addr[3] << 16));
+		data |= ((addr[4] << 8) | addr[5]);
+
+		rc = ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+		if (rc < 0)
+			goto exit;
+
+		/* start read operation */
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), ALU_READ | ALU_START);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_ready(i, dev);
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to read ALU\n");
+			goto exit;
+		}
+
+		/* read ALU entry */
+		rc = lan937x_read_table(dev, alu_table);
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to read ALU\n");
+			goto exit;
+		}
+
+		/* update ALU entry */
+		alu_table[0] = ALU_V_STATIC_VALID;
+
+		/* update port number */
+		alu_table[1] |= BIT(port);
+
+		if (fid)
+			alu_table[1] |= ALU_V_USE_FID;
+		alu_table[2] = (fid << ALU_V_FID_S);
+		alu_table[2] |= ((addr[0] << 8) | addr[1]);
+		alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
+		alu_table[3] |= ((addr[4] << 8) | addr[5]);
+
+		rc = lan937x_write_table(dev, alu_table);
+		if (rc < 0)
+			goto exit;
+
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), ALU_WRITE | ALU_START);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_ready(i, dev);
+
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to write ALU\n");
+			goto exit;
+		}
+
+		rc = ksz_read8(dev, REG_SW_LUE_INT_STATUS__1, &val);
+		if (rc < 0)
+			goto exit;
+
+		/* ALU write failed & do not return before checking ALU2*/
+		if (val & WRITE_FAIL_INT && i == 1)
+			dev_err(dev->dev, "Failed to write ALU\n");
+
+		/* ALU1 write failed and attempt to write ALU2, otherwise exit*/
+		if (val & WRITE_FAIL_INT) {
+			/* Write to clear the Write Fail */
+			rc = ksz_write8(dev, REG_SW_LUE_INT_STATUS__1, WRITE_FAIL_INT);
+			if (rc < 0)
+				goto exit;
+		} else {
+			goto exit;
+		}
+	}
+
+exit:
+	mutex_unlock(&dev->alu_mutex);
+
+	return rc;
+}
+
+static int lan937x_port_fdb_del(struct dsa_switch *ds, int port,
+				const unsigned char *addr, u16 vid)
+{
+	struct ksz_device *dev = ds->priv;
+	u8 fid = lan937x_get_fid(vid);
+	u32 alu_table[4];
+	int rc, i;
+	u32 data;
+
+	mutex_lock(&dev->alu_mutex);
+
+	for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+		/* read any entry with mac & fid */
+		data = fid << ALU_FID_INDEX_S;
+		data |= ((addr[0] << 8) | addr[1]);
+		rc = ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+		if (rc < 0)
+			goto exit;
+
+		data = ((addr[2] << 24) | (addr[3] << 16));
+		data |= ((addr[4] << 8) | addr[5]);
+		rc = ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+		if (rc < 0)
+			goto exit;
+
+		/* start read operation */
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), ALU_READ | ALU_START);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_ready(i, dev);
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to read ALU\n");
+			goto exit;
+		}
+
+		rc = ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
+		if (rc < 0)
+			goto exit;
+
+		if (alu_table[0] & ALU_V_STATIC_VALID) {
+			/* read ALU entry */
+			rc = lan937x_read_table(dev, alu_table);
+			if (rc < 0) {
+				dev_err(dev->dev, "Failed to read ALU\n");
+				goto exit;
+			}
+
+			/* clear forwarding port */
+			alu_table[1] &= ~BIT(port);
+
+			/* if there is no port to forward, clear table */
+			if ((alu_table[1] & ALU_V_PORT_MAP) == 0) {
+				alu_table[0] = 0;
+				alu_table[1] = 0;
+				alu_table[2] = 0;
+				alu_table[3] = 0;
+			}
+		} else {
+			alu_table[0] = 0;
+			alu_table[1] = 0;
+			alu_table[2] = 0;
+			alu_table[3] = 0;
+		}
+
+		rc = lan937x_write_table(dev, alu_table);
+		if (rc < 0)
+			goto exit;
+
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), ALU_WRITE | ALU_START);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_ready(i, dev);
+		if (rc < 0)
+			dev_err(dev->dev, "Failed to write ALU\n");
+	}
+
+exit:
+	mutex_unlock(&dev->alu_mutex);
+
+	return rc;
+}
+
+static void lan937x_convert_alu(struct lan_alu_struct *alu, u32 *alu_table)
+{
+	alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
+	alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
+	alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
+	alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
+			ALU_V_PRIO_AGE_CNT_M;
+	alu->mstp = alu_table[0] & ALU_V_MSTP_M;
+
+	alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
+	alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
+	alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
+
+	alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
+
+	alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
+	alu->mac[1] = alu_table[2] & 0xFF;
+	alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
+	alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
+	alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
+	alu->mac[5] = alu_table[3] & 0xFF;
+}
+
+static int lan937x_port_fdb_dump(struct dsa_switch *ds, int port,
+				 dsa_fdb_dump_cb_t *cb, void *data)
+{
+	struct ksz_device *dev = ds->priv;
+	struct lan_alu_struct alu;
+	u32 lan937x_data;
+	u32 alu_table[4];
+	int timeout;
+	int rc, i;
+
+	mutex_lock(&dev->alu_mutex);
+
+	for (i = 0; i < ALU_STA_DYN_CNT; i++) {
+		/* start ALU search */
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), ALU_START | ALU_SEARCH);
+
+		if (rc < 0)
+			goto exit;
+
+		do {
+			timeout = 1000;
+			do {
+				rc = ksz_read32(dev, REG_SW_ALU_CTRL(i), &lan937x_data);
+
+				if (rc < 0)
+					goto exit;
+
+				if ((lan937x_data & ALU_VALID) || !(lan937x_data & ALU_START))
+					break;
+				usleep_range(1, 10);
+			} while (timeout-- > 0);
+
+			if (!timeout) {
+				dev_err(dev->dev, "Failed to search ALU\n");
+				rc = -ETIMEDOUT;
+				goto exit;
+			}
+
+			/* read ALU table */
+			rc = lan937x_read_table(dev, alu_table);
+			if (rc < 0)
+				goto exit;
+
+			lan937x_convert_alu(&alu, alu_table);
+
+			if (alu.port_forward & BIT(port)) {
+				rc = cb(alu.mac, alu.fid, alu.is_static, data);
+				if (rc)
+					goto exit;
+			}
+		} while (lan937x_data & ALU_START);
+
+exit:
+		/* stop ALU search & continue to next ALU if available */
+		rc = ksz_write32(dev, REG_SW_ALU_CTRL(i), 0);
+	}
+
+	mutex_unlock(&dev->alu_mutex);
+
+	return rc;
+}
+
+static int lan937x_port_mdb_add(struct dsa_switch *ds, int port,
+				const struct switchdev_obj_port_mdb *mdb)
+{
+	struct ksz_device *dev = ds->priv;
+	u8 fid = lan937x_get_fid(mdb->vid);
+	u32 static_table[4];
+	u32 mac_hi, mac_lo;
+	int index, rc;
+	u32 data;
+
+	mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+	mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+	mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+	mutex_lock(&dev->alu_mutex);
+
+	for (index = 0; index < dev->num_statics; index++) {
+		/* find empty slot first */
+		data = (index << ALU_STAT_INDEX_S) |
+			ALU_STAT_READ | ALU_STAT_START;
+		rc = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_sta_ready(dev);
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to read ALU STATIC\n");
+			goto exit;
+		}
+
+		/* read ALU static table */
+		rc = lan937x_read_table(dev, static_table);
+		if (rc < 0)
+			goto exit;
+
+		if (static_table[0] & ALU_V_STATIC_VALID) {
+			/* check this has same fid & mac address */
+			if (((static_table[2] >> ALU_V_FID_S) == fid) &&
+			    ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+			    static_table[3] == mac_lo) {
+				/* found matching one */
+				break;
+			}
+		} else {
+			/* found empty one */
+			break;
+		}
+	}
+
+	/* no available entry */
+	if (index == dev->num_statics) {
+		rc = -ENOSPC;
+		goto exit;
+	}
+
+	/* add entry */
+	static_table[0] = ALU_V_STATIC_VALID;
+
+	static_table[1] |= BIT(port);
+	if (fid)
+		static_table[1] |= ALU_V_USE_FID;
+	static_table[2] = (fid << ALU_V_FID_S);
+	static_table[2] |= mac_hi;
+	static_table[3] = mac_lo;
+
+	rc = lan937x_write_table(dev, static_table);
+	if (rc < 0)
+		goto exit;
+
+	data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+	rc = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+	if (rc < 0)
+		goto exit;
+
+	/* wait to be finished */
+	rc = lan937x_wait_alu_sta_ready(dev);
+	if (rc < 0)
+		dev_err(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+	mutex_unlock(&dev->alu_mutex);
+	return rc;
+}
+
+static int lan937x_port_mdb_del(struct dsa_switch *ds, int port,
+				const struct switchdev_obj_port_mdb *mdb)
+{
+	struct ksz_device *dev = ds->priv;
+	u8 fid = lan937x_get_fid(mdb->vid);
+	u32 static_table[4];
+	u32 mac_hi, mac_lo;
+	int index, rc;
+	u32 data;
+
+	mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+	mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+	mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+	mutex_lock(&dev->alu_mutex);
+
+	for (index = 0; index < dev->num_statics; index++) {
+		/* find empty slot first */
+		data = (index << ALU_STAT_INDEX_S) |
+			ALU_STAT_READ | ALU_STAT_START;
+		rc = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+		if (rc < 0)
+			goto exit;
+
+		/* wait to be finished */
+		rc = lan937x_wait_alu_sta_ready(dev);
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to read ALU STATIC\n");
+			goto exit;
+		}
+
+		/* read ALU static table */
+		rc = lan937x_read_table(dev, static_table);
+
+		if (rc < 0)
+			goto exit;
+
+		if (static_table[0] & ALU_V_STATIC_VALID) {
+			/* check this has same fid & mac address */
+
+			if (((static_table[2] >> ALU_V_FID_S) == fid) &&
+			    ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+			    static_table[3] == mac_lo) {
+				/* found matching one */
+				break;
+			}
+		}
+	}
+
+	/* no available entry */
+	if (index == dev->num_statics)
+		goto exit;
+
+	/* clear port based on port arg */
+	static_table[1] &= ~BIT(port);
+
+	if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
+		/* delete entry */
+		static_table[0] = 0;
+		static_table[1] = 0;
+		static_table[2] = 0;
+		static_table[3] = 0;
+	}
+
+	rc = lan937x_write_table(dev, static_table);
+	if (rc < 0)
+		goto exit;
+
+	data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+	rc = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+	if (rc < 0)
+		goto exit;
+
+	/* wait to be finished */
+	rc = lan937x_wait_alu_sta_ready(dev);
+	if (rc < 0)
+		dev_err(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+	mutex_unlock(&dev->alu_mutex);
+
+	return rc;
+}
+
 static int lan937x_port_mirror_add(struct dsa_switch *ds, int port,
 				   struct dsa_mall_mirror_tc_entry *mirror,
 				   bool ingress)
@@ -444,6 +955,11 @@ const struct dsa_switch_ops lan937x_switch_ops = {
 	.port_bridge_flags	= lan937x_port_bridge_flags,
 	.port_stp_state_set	= lan937x_port_stp_state_set,
 	.port_fast_age		= ksz_port_fast_age,
+	.port_fdb_dump		= lan937x_port_fdb_dump,
+	.port_fdb_add		= lan937x_port_fdb_add,
+	.port_fdb_del		= lan937x_port_fdb_del,
+	.port_mdb_add           = lan937x_port_mdb_add,
+	.port_mdb_del           = lan937x_port_mdb_del,
 	.port_mirror_add	= lan937x_port_mirror_add,
 	.port_mirror_del	= lan937x_port_mirror_del,
 	.port_max_mtu		= lan937x_get_max_mtu,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations
  2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
                   ` (7 preceding siblings ...)
  2021-04-22  9:42 ` [PATCH v2 net-next 8/9] net: dsa: microchip: add support for fdb and mdb management Prasanna Vengateshan
@ 2021-04-22  9:42 ` Prasanna Vengateshan
  2021-04-22 19:03   ` Vladimir Oltean
  8 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-22  9:42 UTC (permalink / raw)
  To: andrew, netdev, olteanv, robh+dt
  Cc: UNGLinuxDriver, hkallweit1, linux, davem, kuba, linux-kernel,
	vivien.didelot, f.fainelli, devicetree

Support for VLAN add, del, prepare and filtering operations.

It aligns with latest update of removing switchdev
transactional logic from VLAN objects

Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
---
 drivers/net/dsa/microchip/lan937x_main.c | 214 +++++++++++++++++++++++
 1 file changed, 214 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 7f6183dc0e31..35f3456c3506 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -14,6 +14,103 @@
 #include "ksz_common.h"
 #include "lan937x_dev.h"
 
+static int lan937x_wait_vlan_ctrl_ready(struct ksz_device *dev)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(dev->regmap[0], REG_SW_VLAN_CTRL,
+					val, !(val & VLAN_START), 10, 1000);
+}
+
+static int lan937x_get_vlan_table(struct ksz_device *dev, u16 vid,
+				  u32 *vlan_table)
+{
+	int rc;
+
+	mutex_lock(&dev->vlan_mutex);
+
+	rc = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
+	if (rc < 0)
+		goto exit;
+
+	/* wait to be cleared */
+	rc = lan937x_wait_vlan_ctrl_ready(dev);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+	if (rc < 0)
+		goto exit;
+
+exit:
+	mutex_unlock(&dev->vlan_mutex);
+
+	return rc;
+}
+
+static int lan937x_set_vlan_table(struct ksz_device *dev, u16 vid,
+				  u32 *vlan_table)
+{
+	int rc;
+
+	mutex_lock(&dev->vlan_mutex);
+
+	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
+	if (rc < 0)
+		goto exit;
+
+	/* wait to be cleared */
+	rc = lan937x_wait_vlan_ctrl_ready(dev);
+	if (rc < 0)
+		goto exit;
+
+	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+	if (rc < 0)
+		goto exit;
+
+	/* update vlan cache table */
+	dev->vlan_cache[vid].table[0] = vlan_table[0];
+	dev->vlan_cache[vid].table[1] = vlan_table[1];
+	dev->vlan_cache[vid].table[2] = vlan_table[2];
+
+exit:
+	mutex_unlock(&dev->vlan_mutex);
+
+	return rc;
+}
+
 static int lan937x_read_table(struct ksz_device *dev, u32 *table)
 {
 	int rc;
@@ -190,6 +287,120 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
 	mutex_unlock(&dev->dev_mutex);
 }
 
+static int lan937x_port_vlan_filtering(struct dsa_switch *ds, int port,
+				       bool flag,
+				       struct netlink_ext_ack *extack)
+{
+	struct ksz_device *dev = ds->priv;
+	int rc;
+
+	if (flag) {
+		rc = lan937x_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+				      PORT_VLAN_LOOKUP_VID_0, true);
+		if (rc < 0)
+			return rc;
+
+		rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
+	} else {
+		rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
+		if (rc < 0)
+			return rc;
+
+		rc = lan937x_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+				      PORT_VLAN_LOOKUP_VID_0, false);
+	}
+
+	return rc;
+}
+
+static int lan937x_port_vlan_add(struct dsa_switch *ds, int port,
+				 const struct switchdev_obj_port_vlan *vlan,
+				 struct netlink_ext_ack *extack)
+{
+	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	struct ksz_device *dev = ds->priv;
+	u32 vlan_table[3];
+	int rc;
+
+	rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to get vlan table\n");
+		return rc;
+	}
+
+	vlan_table[0] = VLAN_VALID | (vlan->vid & VLAN_FID_M);
+
+	/* set/clear switch port when updating vlan table registers */
+	if (untagged)
+		vlan_table[1] |= BIT(port);
+	else
+		vlan_table[1] &= ~BIT(port);
+	vlan_table[1] &= ~(BIT(dev->cpu_port));
+
+	vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
+
+	rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to set vlan table\n");
+		return rc;
+	}
+
+	/* change PVID */
+	if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+		rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vlan->vid);
+
+		if (rc < 0) {
+			dev_err(dev->dev, "Failed to set pvid\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int lan937x_port_vlan_del(struct dsa_switch *ds, int port,
+				 const struct switchdev_obj_port_vlan *vlan)
+{
+	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	struct ksz_device *dev = ds->priv;
+	u32 vlan_table[3];
+	u16 pvid;
+	int rc;
+
+	lan937x_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
+	pvid &= 0xFFF;
+
+	rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
+
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to get vlan table\n");
+		return rc;
+	}
+	/* clear switch port number */
+	vlan_table[2] &= ~BIT(port);
+
+	if (pvid == vlan->vid)
+		pvid = 1;
+
+	if (untagged)
+		vlan_table[1] &= ~BIT(port);
+
+	rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to set vlan table\n");
+		return rc;
+	}
+
+	rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
+
+	if (rc < 0) {
+		dev_err(dev->dev, "Failed to set pvid\n");
+		return rc;
+	}
+
+	return 0;
+}
+
 static u8 lan937x_get_fid(u16 vid)
 {
 	if (vid > ALU_FID_SIZE)
@@ -955,6 +1166,9 @@ const struct dsa_switch_ops lan937x_switch_ops = {
 	.port_bridge_flags	= lan937x_port_bridge_flags,
 	.port_stp_state_set	= lan937x_port_stp_state_set,
 	.port_fast_age		= ksz_port_fast_age,
+	.port_vlan_filtering	= lan937x_port_vlan_filtering,
+	.port_vlan_add		= lan937x_port_vlan_add,
+	.port_vlan_del		= lan937x_port_vlan_del,
 	.port_fdb_dump		= lan937x_port_fdb_dump,
 	.port_fdb_add		= lan937x_port_fdb_add,
 	.port_fdb_del		= lan937x_port_fdb_del,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver
  2021-04-22  9:42 ` [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver Prasanna Vengateshan
@ 2021-04-22 12:45   ` Andrew Lunn
  2021-04-26  4:06     ` Prasanna Vengateshan
  0 siblings, 1 reply; 31+ messages in thread
From: Andrew Lunn @ 2021-04-22 12:45 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: netdev, olteanv, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> +#define PORT_T1_PHY_RESET	BIT(15)
> +#define PORT_T1_PHY_LOOPBACK	BIT(14)
> +#define PORT_T1_SPEED_100MBIT	BIT(13)
> +#define PORT_T1_POWER_DOWN	BIT(11)
> +#define PORT_T1_ISOLATE	BIT(10)
> +#define PORT_T1_FULL_DUPLEX	BIT(8)

These appear to be standard BMCR_ values. Please don't define your
own.

> +
> +#define REG_PORT_T1_PHY_BASIC_STATUS 0x01
> +
> +#define PORT_T1_MII_SUPPRESS_CAPABLE	BIT(6)
> +#define PORT_T1_LINK_STATUS		BIT(2)
> +#define PORT_T1_EXTENDED_CAPABILITY	BIT(0)
> +
> +#define REG_PORT_T1_PHY_ID_HI 0x02
> +#define REG_PORT_T1_PHY_ID_LO 0x03

MII_PHYSID1 and MII_PHYSID2

Please go through all these #defines and replace them with the
standard ones Linux provides. You are obfusticating the code by not
using what people already know.

      Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
@ 2021-04-22 15:30   ` Rob Herring
  2021-04-22 17:38   ` Rob Herring
  1 sibling, 0 replies; 31+ messages in thread
From: Rob Herring @ 2021-04-22 15:30 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: davem, devicetree, vivien.didelot, olteanv, linux, robh+dt, kuba,
	linux-kernel, hkallweit1, f.fainelli, netdev, UNGLinuxDriver,
	andrew

On Thu, 22 Apr 2021 15:12:49 +0530, Prasanna Vengateshan wrote:
> Documentation in .yaml format and updates to the MAINTAINERS
> Also 'make dt_binding_check' is passed
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  .../bindings/net/dsa/microchip,lan937x.yaml   | 142 ++++++++++++++++++
>  MAINTAINERS                                   |   1 +
>  2 files changed, 143 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/net/dsa/microchip,lan937x.example.dt.yaml:0:0: /example-0/spi/switch@0/mdio: failed to match any schema with compatible: ['microchip,lan937x-mdio']

See https://patchwork.ozlabs.org/patch/1469135

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
  2021-04-22 15:30   ` Rob Herring
@ 2021-04-22 17:38   ` Rob Herring
  2021-04-26  4:05     ` Prasanna Vengateshan
  1 sibling, 1 reply; 31+ messages in thread
From: Rob Herring @ 2021-04-22 17:38 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, olteanv, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:49PM +0530, Prasanna Vengateshan wrote:
> Documentation in .yaml format and updates to the MAINTAINERS
> Also 'make dt_binding_check' is passed
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  .../bindings/net/dsa/microchip,lan937x.yaml   | 142 ++++++++++++++++++
>  MAINTAINERS                                   |   1 +
>  2 files changed, 143 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> 
> diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> new file mode 100644
> index 000000000000..22128a52d699
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> @@ -0,0 +1,142 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/dsa/microchip,lan937x.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: LAN937x Ethernet Switch Series Tree Bindings
> +
> +maintainers:
> +  - UNGLinuxDriver@microchip.com
> +
> +allOf:
> +  - $ref: dsa.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - microchip,lan9370
> +      - microchip,lan9371
> +      - microchip,lan9372
> +      - microchip,lan9373
> +      - microchip,lan9374
> +
> +  reg:
> +    maxItems: 1
> +
> +  spi-max-frequency:
> +    maximum: 50000000
> +
> +  reset-gpios:
> +    description: Optional gpio specifier for a reset line
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    //Ethernet switch connected via spi to the host

If this is on SPI, why is it not under the spi bus node?

> +    ethernet {
> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      fixed-link {
> +        speed = <1000>;
> +        full-duplex;
> +      };
> +    };
> +
> +    spi {
> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      lan9374: switch@0 {
> +        compatible = "microchip,lan9374";
> +        reg = <0>;
> +
> +        spi-max-frequency = <44000000>;
> +
> +        ethernet-ports {
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          port@0 {
> +            reg = <0>;
> +            label = "lan1";
> +            phy-handle = <&t1phy0>;
> +          };
> +          port@1 {
> +            reg = <1>;
> +            label = "lan2";
> +            phy-handle = <&t1phy1>;
> +          };
> +          port@2 {
> +            reg = <2>;
> +            label = "lan4";
> +            phy-handle = <&t1phy2>;
> +          };
> +          port@3 {
> +            reg = <3>;
> +            label = "lan6";
> +            phy-handle = <&t1phy3>;
> +          };
> +          port@4 {
> +            reg = <4>;
> +            phy-mode = "rgmii";
> +            ethernet = <&ethernet>;

You are missing 'ethernet' label.

> +            fixed-link {
> +              speed = <1000>;
> +              full-duplex;
> +            };
> +          };
> +          port@5 {
> +            reg = <5>;
> +            label = "lan7";
> +            fixed-link {
> +              speed = <1000>;
> +              full-duplex;
> +            };
> +          };
> +          port@6 {
> +            reg = <6>;
> +            label = "lan5";
> +            phy-handle = <&t1phy4>;
> +          };
> +          port@7 {
> +            reg = <7>;
> +            label = "lan3";
> +            phy-handle = <&t1phy5>;
> +          };
> +        };
> +
> +        mdio {
> +          compatible = "microchip,lan937x-mdio";

You can just drop this to make the example pass. Or convert that binding 
to schema.

> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +
> +          t1phy0: ethernet-phy@0{
> +            reg = <0x0>;
> +          };
> +          t1phy1: ethernet-phy@1{
> +            reg = <0x1>;
> +          };
> +          t1phy2: ethernet-phy@2{
> +            reg = <0x2>;
> +          };
> +          t1phy3: ethernet-phy@3{
> +            reg = <0x3>;
> +          };
> +          t1phy4: ethernet-phy@6{
> +            reg = <0x6>;
> +          };
> +          t1phy5: ethernet-phy@7{
> +            reg = <0x7>;
> +          };
> +        };
> +      };
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c3c8fa572580..a0fdfef8802a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11752,6 +11752,7 @@ M:	UNGLinuxDriver@microchip.com
>  L:	netdev@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
> +F:	Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
>  F:	drivers/net/dsa/microchip/*
>  F:	include/linux/platform_data/microchip-ksz.h
>  F:	net/dsa/tag_ksz.c
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations
  2021-04-22  9:42 ` [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
@ 2021-04-22 19:03   ` Vladimir Oltean
  2021-05-06 14:51     ` Prasanna Vengateshan
  0 siblings, 1 reply; 31+ messages in thread
From: Vladimir Oltean @ 2021-04-22 19:03 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:57PM +0530, Prasanna Vengateshan wrote:
> Support for VLAN add, del, prepare and filtering operations.
> 
> It aligns with latest update of removing switchdev
> transactional logic from VLAN objects

Maybe more in the commit message about what the patch does, as opposed
to mentioning that you had to rebase it, would be helpful.

> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  drivers/net/dsa/microchip/lan937x_main.c | 214 +++++++++++++++++++++++
>  1 file changed, 214 insertions(+)
> 
> diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
> index 7f6183dc0e31..35f3456c3506 100644
> --- a/drivers/net/dsa/microchip/lan937x_main.c
> +++ b/drivers/net/dsa/microchip/lan937x_main.c
> @@ -14,6 +14,103 @@
>  #include "ksz_common.h"
>  #include "lan937x_dev.h"
>  
> +static int lan937x_wait_vlan_ctrl_ready(struct ksz_device *dev)
> +{
> +	unsigned int val;
> +
> +	return regmap_read_poll_timeout(dev->regmap[0], REG_SW_VLAN_CTRL,
> +					val, !(val & VLAN_START), 10, 1000);
> +}
> +
> +static int lan937x_get_vlan_table(struct ksz_device *dev, u16 vid,
> +				  u32 *vlan_table)
> +{
> +	int rc;
> +
> +	mutex_lock(&dev->vlan_mutex);
> +
> +	rc = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
> +	if (rc < 0)
> +		goto exit;
> +
> +	/* wait to be cleared */
> +	rc = lan937x_wait_vlan_ctrl_ready(dev);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
> +	if (rc < 0)
> +		goto exit;
> +
> +exit:
> +	mutex_unlock(&dev->vlan_mutex);
> +
> +	return rc;
> +}
> +
> +static int lan937x_set_vlan_table(struct ksz_device *dev, u16 vid,
> +				  u32 *vlan_table)
> +{
> +	int rc;
> +
> +	mutex_lock(&dev->vlan_mutex);
> +
> +	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
> +	if (rc < 0)
> +		goto exit;
> +
> +	/* wait to be cleared */
> +	rc = lan937x_wait_vlan_ctrl_ready(dev);
> +	if (rc < 0)
> +		goto exit;
> +
> +	rc = ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
> +	if (rc < 0)
> +		goto exit;
> +
> +	/* update vlan cache table */
> +	dev->vlan_cache[vid].table[0] = vlan_table[0];
> +	dev->vlan_cache[vid].table[1] = vlan_table[1];
> +	dev->vlan_cache[vid].table[2] = vlan_table[2];
> +
> +exit:
> +	mutex_unlock(&dev->vlan_mutex);
> +
> +	return rc;
> +}
> +
>  static int lan937x_read_table(struct ksz_device *dev, u32 *table)
>  {
>  	int rc;
> @@ -190,6 +287,120 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
>  	mutex_unlock(&dev->dev_mutex);
>  }
>  
> +static int lan937x_port_vlan_filtering(struct dsa_switch *ds, int port,
> +				       bool flag,
> +				       struct netlink_ext_ack *extack)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	int rc;
> +
> +	if (flag) {
> +		rc = lan937x_port_cfg(dev, port, REG_PORT_LUE_CTRL,
> +				      PORT_VLAN_LOOKUP_VID_0, true);
> +		if (rc < 0)
> +			return rc;

What does this bit do?

> +
> +		rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);

How about this bit?

I see one bit is per port and the other is global.
Just FYI, you can have this configuration:

ip link add br0 type bridge vlan_filtering 0
ip link add br1 type bridge vlan_filtering 1
ip link set swp0 master br0
ip link set swp1 master br0
ip link set swp2 master br1
ip link set swp3 master br1

Do the swp0 and swp1 ports remain VLAN-unaware after you touch this
REG_SW_LUE_CTRL_0 bit?

> +	} else {
> +		rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
> +		if (rc < 0)
> +			return rc;
> +
> +		rc = lan937x_port_cfg(dev, port, REG_PORT_LUE_CTRL,
> +				      PORT_VLAN_LOOKUP_VID_0, false);
> +	}
> +
> +	return rc;
> +}
> +
> +static int lan937x_port_vlan_add(struct dsa_switch *ds, int port,
> +				 const struct switchdev_obj_port_vlan *vlan,
> +				 struct netlink_ext_ack *extack)
> +{
> +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
> +	struct ksz_device *dev = ds->priv;
> +	u32 vlan_table[3];

Maybe a structure would be nicer to read than an u32 array?

> +	int rc;
> +
> +	rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to get vlan table\n");

One of the reasons for which the extack exists is so that you can report
errors to user space and not to the console.

		NL_SET_ERR_MSG_MOD(extack, "Failed to get vlan table");

> +		return rc;
> +	}
> +
> +	vlan_table[0] = VLAN_VALID | (vlan->vid & VLAN_FID_M);
> +
> +	/* set/clear switch port when updating vlan table registers */
> +	if (untagged)
> +		vlan_table[1] |= BIT(port);
> +	else
> +		vlan_table[1] &= ~BIT(port);
> +	vlan_table[1] &= ~(BIT(dev->cpu_port));
> +
> +	vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);

What's the business with the CPU port here? Does DSA not call
.port_vlan_add for the CPU port separately?

> +
> +	rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to set vlan table\n");
> +		return rc;
> +	}
> +
> +	/* change PVID */
> +	if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
> +		rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vlan->vid);
> +
> +		if (rc < 0) {
> +			dev_err(dev->dev, "Failed to set pvid\n");
> +			return rc;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int lan937x_port_vlan_del(struct dsa_switch *ds, int port,
> +				 const struct switchdev_obj_port_vlan *vlan)
> +{
> +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
> +	struct ksz_device *dev = ds->priv;
> +	u32 vlan_table[3];
> +	u16 pvid;
> +	int rc;
> +
> +	lan937x_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
> +	pvid &= 0xFFF;
> +
> +	rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
> +
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to get vlan table\n");
> +		return rc;
> +	}
> +	/* clear switch port number */
> +	vlan_table[2] &= ~BIT(port);
> +
> +	if (pvid == vlan->vid)
> +		pvid = 1;

According to Documentation/networking/switchdev.rst:

When the bridge has VLAN filtering enabled and a PVID is not configured on the
ingress port, untagged and 802.1p tagged packets must be dropped. When the bridge
has VLAN filtering enabled and a PVID exists on the ingress port, untagged and
priority-tagged packets must be accepted and forwarded according to the
bridge's port membership of the PVID VLAN. When the bridge has VLAN filtering
disabled, the presence/lack of a PVID should not influence the packet
forwarding decision.

So please don't reset the pvid.

> +
> +	if (untagged)
> +		vlan_table[1] &= ~BIT(port);
> +
> +	rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to set vlan table\n");
> +		return rc;
> +	}
> +
> +	rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
> +
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to set pvid\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
>  static u8 lan937x_get_fid(u16 vid)
>  {
>  	if (vid > ALU_FID_SIZE)
> @@ -955,6 +1166,9 @@ const struct dsa_switch_ops lan937x_switch_ops = {
>  	.port_bridge_flags	= lan937x_port_bridge_flags,
>  	.port_stp_state_set	= lan937x_port_stp_state_set,
>  	.port_fast_age		= ksz_port_fast_age,
> +	.port_vlan_filtering	= lan937x_port_vlan_filtering,
> +	.port_vlan_add		= lan937x_port_vlan_add,
> +	.port_vlan_del		= lan937x_port_vlan_del,
>  	.port_fdb_dump		= lan937x_port_fdb_dump,
>  	.port_fdb_add		= lan937x_port_fdb_add,
>  	.port_fdb_del		= lan937x_port_fdb_del,
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x
  2021-04-22  9:42 ` [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
@ 2021-04-22 19:18   ` Vladimir Oltean
  2021-04-26  4:33     ` Prasanna Vengateshan
  0 siblings, 1 reply; 31+ messages in thread
From: Vladimir Oltean @ 2021-04-22 19:18 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:51PM +0530, Prasanna Vengateshan wrote:
> The Microchip LAN937X switches have a tagging protocol which is
> very similar to KSZ tagging. So that the implementation is added to
> tag_ksz.c and reused common APIs
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  include/net/dsa.h |  2 ++
>  net/dsa/Kconfig   |  4 ++--
>  net/dsa/tag_ksz.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 62 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 507082959aa4..98c1ab6dc4dc 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -50,6 +50,7 @@ struct phylink_link_state;
>  #define DSA_TAG_PROTO_OCELOT_8021Q_VALUE	20
>  #define DSA_TAG_PROTO_SEVILLE_VALUE		21
>  #define DSA_TAG_PROTO_BRCM_LEGACY_VALUE		22
> +#define DSA_TAG_PROTO_LAN937X_VALUE		23
>  
>  enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
> @@ -75,6 +76,7 @@ enum dsa_tag_protocol {
>  	DSA_TAG_PROTO_XRS700X		= DSA_TAG_PROTO_XRS700X_VALUE,
>  	DSA_TAG_PROTO_OCELOT_8021Q	= DSA_TAG_PROTO_OCELOT_8021Q_VALUE,
>  	DSA_TAG_PROTO_SEVILLE		= DSA_TAG_PROTO_SEVILLE_VALUE,
> +	DSA_TAG_PROTO_LAN937X		= DSA_TAG_PROTO_LAN937X_VALUE,
>  };
>  
>  struct packet_type;
> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> index cbc2bd643ab2..888eb79a85f2 100644
> --- a/net/dsa/Kconfig
> +++ b/net/dsa/Kconfig
> @@ -97,10 +97,10 @@ config NET_DSA_TAG_MTK
>  	  Mediatek switches.
>  
>  config NET_DSA_TAG_KSZ
> -	tristate "Tag driver for Microchip 8795/9477/9893 families of switches"
> +	tristate "Tag driver for Microchip 8795/937x/9477/9893 families of switches"
>  	help
>  	  Say Y if you want to enable support for tagging frames for the
> -	  Microchip 8795/9477/9893 families of switches.
> +	  Microchip 8795/937x/9477/9893 families of switches.
>  
>  config NET_DSA_TAG_RTL4_A
>  	tristate "Tag driver for Realtek 4 byte protocol A tags"
> diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
> index 4820dbcedfa2..a67f21bdab8f 100644
> --- a/net/dsa/tag_ksz.c
> +++ b/net/dsa/tag_ksz.c
> @@ -190,10 +190,68 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
>  DSA_TAG_DRIVER(ksz9893_netdev_ops);
>  MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893);
>  
> +/* For xmit, 2 bytes are added before FCS.
> + * ---------------------------------------------------------------------------
> + * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
> + * ---------------------------------------------------------------------------
> + * tag0 : represents tag override, lookup and valid
> + * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
> + *
> + * For rcv, 1 byte is added before FCS.
> + * ---------------------------------------------------------------------------
> + * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
> + * ---------------------------------------------------------------------------
> + * tag0 : zero-based value represents port
> + *	  (eg, 0x00=port1, 0x02=port3, 0x07=port8)
> + */
> +#define LAN937X_INGRESS_TAG_LEN		2
> +
> +#define LAN937X_TAIL_TAG_OVERRIDE	BIT(11)

I had to look up the datasheet, to see what this is, because "override"
can mean many things, not all of them are desirable.

Port Blocking Override
When set, packets will be sent from the specified port(s) regardless, and any port
blocking (see Port Transmit Enable in Port MSTP State Register) is ignored.

Do you think you can name it LAN937X_TAIL_TAG_BLOCKING_OVERRIDE? I know
it's longer, but it's also more suggestive.

> +#define LAN937X_TAIL_TAG_LOOKUP		BIT(12)
> +#define LAN937X_TAIL_TAG_VALID		BIT(13)
> +#define LAN937X_TAIL_TAG_PORT_MASK	7
> +
> +static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
> +				    struct net_device *dev)
> +{
> +	struct dsa_port *dp = dsa_slave_to_port(dev);
> +	__be16 *tag;
> +	u8 *addr;
> +	u16 val;
> +
> +	tag = skb_put(skb, LAN937X_INGRESS_TAG_LEN);

Here we go again.. why is it called INGRESS_TAG_LEN if it is used during
xmit, and only during xmit?

> +	addr = skb_mac_header(skb);

My personal choice would have been:

	const struct ethhdr *hdr = eth_hdr(skb);

	if (is_link_local_ether_addr(hdr->h_dest))

> +
> +	val = BIT(dp->index);
> +
> +	if (is_link_local_ether_addr(addr))
> +		val |= LAN937X_TAIL_TAG_OVERRIDE;
> +
> +	/* Tail tag valid bit - This bit should always be set by the CPU*/
> +	val |= LAN937X_TAIL_TAG_VALID;
> +
> +	*tag = cpu_to_be16(val);
> +
> +	return skb;
> +}
> +
> +static const struct dsa_device_ops lan937x_netdev_ops = {
> +	.name	= "lan937x",
> +	.proto	= DSA_TAG_PROTO_LAN937X,
> +	.xmit	= lan937x_xmit,
> +	.rcv	= ksz9477_rcv,
> +	.overhead = LAN937X_INGRESS_TAG_LEN,
> +	.tail_tag = true,
> +};
> +
> +DSA_TAG_DRIVER(lan937x_netdev_ops);
> +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X);
> +
>  static struct dsa_tag_driver *dsa_tag_driver_array[] = {
>  	&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
>  	&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
>  	&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
> +	&DSA_TAG_DRIVER_NAME(lan937x_netdev_ops),
>  };
>  
>  module_dsa_tag_drivers(dsa_tag_driver_array);
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
@ 2021-04-22 19:59   ` Vladimir Oltean
  2021-04-22 23:28     ` Andrew Lunn
  2021-04-27  7:43     ` Prasanna Vengateshan
  2021-04-22 23:32   ` Andrew Lunn
                     ` (2 subsequent siblings)
  3 siblings, 2 replies; 31+ messages in thread
From: Vladimir Oltean @ 2021-04-22 19:59 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:52PM +0530, Prasanna Vengateshan wrote:
> Basic DSA driver support for lan937x and the device will be
> configured through SPI interface.
> 
> drivers/net/dsa/microchip/ path is already part of MAINTAINERS &
> the new files come under this path. Hence no update needed to the
> MAINTAINERS
> 
> Reused KSZ APIs for port_bridge_join() & port_bridge_leave() and
> added support for port_stp_state_set() & port_fast_age().
> 
> lan937x_flush_dyn_mac_table() which gets called from
> port_fast_age() of KSZ common layer, hence added support for it.
> 
> currently port_bridge_flags returns -EOPNOTSUPP, this support
> will be added later
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
(...)
> +int lan937x_reset_switch(struct ksz_device *dev)
> +{
> +	u32 data32;
> +	u8 data8;
> +	int rc;
> +
> +	/* reset switch */
> +	rc = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* default configuration */
> +	rc = ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
> +	if (rc < 0)
> +		return rc;
> +
> +	data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
> +	      SW_SRC_ADDR_FILTER;
> +
> +	rc = ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* disable interrupts */
> +	rc = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* set broadcast storm protection 10% rate */
> +	rc = regmap_update_bits(dev->regmap[1], REG_SW_MAC_CTRL_2,
> +				BROADCAST_STORM_RATE,
> +			   (BROADCAST_STORM_VALUE *
> +			   BROADCAST_STORM_PROT_RATE) / 100);

Why do you think this is a sane enough configuration to enable by
default? We have tc-flower policers for this kind of stuff. If the
broadcast policer is global to the switch and not per port, you can
model it using:

tc qdisc add dev lan1 ingress_block 1 clsact
tc qdisc add dev lan2 ingress_block 1 clsact
tc qdisc add dev lan3 ingress_block 1 clsact
tc filter add block 1 flower skip_sw dst_mac ff:ff:ff:ff:ff:ff \
	action police \
	rate 43Mbit \
	burst 10000

> +
> +	return rc;
> +}
> +
(...)
> +int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
> +			       int reg, u16 val)
> +{
> +	u16 temp, addr_base;
> +	unsigned int value;
> +	int rc;
> +
> +	/* Check for internal phy port */
> +	if (!lan937x_is_internal_phy_port(dev, addr))
> +		return -EOPNOTSUPP;
> +
> +	if (lan937x_is_internal_100BTX_phy_port(dev, addr))
> +		addr_base = REG_PORT_TX_PHY_CTRL_BASE;
> +	else
> +		addr_base = REG_PORT_T1_PHY_CTRL_BASE;
> +
> +	temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
> +
> +	rc = ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Write the data to be written to the VPHY reg */
> +	rc = ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Write the Write En and Busy bit */
> +	rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2, (VPHY_IND_WRITE
> +				| VPHY_IND_BUSY));

This isn't quite the coding style that the kernel community is used to
seeing. This looks more adequate:

	rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2,
			 (VPHY_IND_WRITE | VPHY_IND_BUSY));

> +	if (rc < 0)
> +		return rc;
> +
> +	rc = regmap_read_poll_timeout(dev->regmap[1],
> +				      REG_VPHY_IND_CTRL__2,
> +				value, !(value & VPHY_IND_BUSY), 10, 1000);

very, very odd indentation.

> +
> +	/* failed to write phy register. get out of loop */

What loop? The regmap_read_poll_timeout? If you're here you're already
out of it, aren't you?

> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to write phy register\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
> +			      int reg, u16 *val)
> +{
> +	u16 temp, addr_base;
> +	unsigned int value;
> +	int rc;
> +
> +	/* Check for internal phy port, return 0xffff for non-existent phy*/
> +	if (!lan937x_is_internal_phy_port(dev, addr))
> +		return 0xffff;
> +
> +	if (lan937x_is_internal_100BTX_phy_port(dev, addr))
> +		addr_base = REG_PORT_TX_PHY_CTRL_BASE;
> +	else
> +		addr_base = REG_PORT_T1_PHY_CTRL_BASE;
> +
> +	/* get register address based on the logical port */
> +	temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
> +
> +	rc = ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* Write Read and Busy bit to start the transaction*/
> +	rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = regmap_read_poll_timeout(dev->regmap[1],
> +				      REG_VPHY_IND_CTRL__2,
> +				value, !(value & VPHY_IND_BUSY), 10, 1000);
> +
> +	/*  failed to read phy register. get out of loop */
> +	if (rc < 0) {
> +		dev_err(dev->dev, "Failed to read phy register\n");
> +		return rc;
> +	}
> +
> +	/* Read the VPHY register which has the PHY data*/
> +	rc = ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
> +
> +	return rc;
> +}
> +
> +static void lan937x_set_gbit(struct ksz_device *dev, bool gbit, u8 *data)
> +{
> +	if (gbit)
> +		*data &= ~PORT_MII_NOT_1GBIT;
> +	else
> +		*data |= PORT_MII_NOT_1GBIT;
> +}
> +
> +void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port)
> +{
> +	struct ksz_port *p = &dev->ports[port];
> +	u8 data8, member;
> +
> +	/* enable tag tail for host port */
> +	if (cpu_port) {
> +		lan937x_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
> +				 true);
> +		/* Enable jumbo packet in host port so that frames are not
> +		 * counted as oversized.
> +		 */
> +		lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_PACKET,
> +				 true);
> +		lan937x_pwrite16(dev, port, REG_PORT_MTU__2, FR_SIZE_CPU_PORT);
> +	}
> +
> +	lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_FR_CHK_LENGTH,
> +			 false);
> +
> +	lan937x_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
> +
> +	/* set back pressure */
> +	lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
> +
> +	/* enable broadcast storm limit */
> +	lan937x_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
> +
> +	/* disable DiffServ priority */
> +	lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
> +
> +	/* replace priority */
> +	lan937x_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
> +			 false);
> +	lan937x_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
> +			   MTI_PVID_REPLACE, false);
> +
> +	/* enable 802.1p priority */
> +	lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
> +
> +	if (!lan937x_is_internal_phy_port(dev, port)) {
> +		/* force flow control off*/
> +		lan937x_port_cfg(dev, port, REG_PORT_XMII_CTRL_0,
> +				 PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
> +			     false);

Why do you force flow control off? Doesn't this PHY support PAUSE
autoneg, and doesn't phylink give you rx_pause/tx_pause from the flow
control resolution?

> +
> +		lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
> +
> +		/* clear MII selection & set it based on interface later */
> +		data8 &= ~PORT_MII_SEL_M;
> +
> +		/* configure MAC based on p->interface */
> +		switch (p->interface) {
> +		case PHY_INTERFACE_MODE_MII:
> +			lan937x_set_gbit(dev, false, &data8);
> +			data8 |= PORT_MII_SEL;
> +			break;
> +		case PHY_INTERFACE_MODE_RMII:
> +			lan937x_set_gbit(dev, false, &data8);
> +			data8 |= PORT_RMII_SEL;
> +			break;
> +		default:
> +			lan937x_set_gbit(dev, true, &data8);
> +			data8 |= PORT_RGMII_SEL;
> +
> +			data8 &= ~PORT_RGMII_ID_IG_ENABLE;
> +			data8 &= ~PORT_RGMII_ID_EG_ENABLE;
> +
> +			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> +			    p->interface == PHY_INTERFACE_MODE_RGMII_RXID)
> +				data8 |= PORT_RGMII_ID_IG_ENABLE;
> +
> +			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> +			    p->interface == PHY_INTERFACE_MODE_RGMII_TXID)
> +				data8 |= PORT_RGMII_ID_EG_ENABLE;

This is interesting. If you have an RGMII port connected to an external
PHY, how do you ensure that either the lan937x driver, or the PHY driver,
but not both, enable RGMII delays? Was RGMII tested with a PHY or just
fixed-link?

> +			break;
> +		}
> +		lan937x_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
> +	}
> +
> +	if (cpu_port)
> +		member = dev->port_mask;
> +	else
> +		member = dev->host_mask | p->vid_member;
> +
> +	lan937x_cfg_port_member(dev, port, member);
> +}
> +
> +static int lan937x_sw_mdio_read(struct mii_bus *bus, int addr, int regnum)
> +{
> +	struct ksz_device *dev = bus->priv;
> +	u16 val;
> +	int rc;
> +
> +	rc = lan937x_internal_phy_read(dev, addr, regnum, &val);
> +	if (rc < 0)
> +		return rc;
> +
> +	return val;
> +}
> +
> +static int lan937x_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
> +{
> +	struct ksz_device *dev = bus->priv;
> +
> +	return lan937x_internal_phy_write(dev, addr, regnum, val);
> +}
> +
> +static int lan937x_mdio_register(struct dsa_switch *ds)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	int ret;
> +
> +	dev->mdio_np = of_get_compatible_child(ds->dev->of_node, "microchip,lan937x-mdio");

I think it is strange to have a node with a compatible string but no
dedicated driver? I think the most popular option is to set:

	dev->mdio_np = of_get_child_by_name(node, "mdio");

and just create an "mdio" subnode with no compatible.

> +
> +	if (!dev->mdio_np) {
> +		dev_err(ds->dev, "no MDIO bus node\n");
> +		return -ENODEV;
> +	}
> +
> +	ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
> +
> +	if (!ds->slave_mii_bus)
> +		return -ENOMEM;
> +
> +	ds->slave_mii_bus->priv = ds->priv;
> +	ds->slave_mii_bus->read = lan937x_sw_mdio_read;
> +	ds->slave_mii_bus->write = lan937x_sw_mdio_write;
> +	ds->slave_mii_bus->name = "lan937x slave smi";
> +	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
> +		 ds->index);
> +	ds->slave_mii_bus->parent = ds->dev;
> +	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
> +
> +	ret = of_mdiobus_register(ds->slave_mii_bus, dev->mdio_np);
> +
> +	if (ret) {
> +		dev_err(ds->dev, "unable to register MDIO bus %s\n",
> +			ds->slave_mii_bus->id);
> +		of_node_put(dev->mdio_np);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int lan937x_switch_init(struct ksz_device *dev)
> +{
> +	int i, ret;
> +
> +	dev->ds->ops = &lan937x_switch_ops;
> +
> +	/* Check device tree */
> +	ret = lan937x_check_device_id(dev);
> +
> +	if (ret)
> +		return ret;
> +
> +	dev->port_mask = (1 << dev->port_cnt) - 1;
> +
> +	dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
> +	dev->mib_cnt = ARRAY_SIZE(lan937x_mib_names);
> +
> +	dev->ports = devm_kzalloc(dev->dev,
> +				  dev->port_cnt * sizeof(struct ksz_port),
> +				  GFP_KERNEL);
> +	if (!dev->ports)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < dev->port_cnt; i++) {
> +		mutex_init(&dev->ports[i].mib.cnt_mutex);
> +		dev->ports[i].mib.counters =
> +			devm_kzalloc(dev->dev,
> +				     sizeof(u64) *
> +				     (dev->mib_cnt + 1),
> +				     GFP_KERNEL);
> +		if (!dev->ports[i].mib.counters)
> +			return -ENOMEM;
> +	}
> +
> +	/* set the real number of ports */
> +	dev->ds->num_ports = dev->port_cnt;
> +	return 0;
> +}
> +
> +static int lan937x_init(struct ksz_device *dev)
> +{
> +	int rc;
> +
> +	rc = lan937x_switch_init(dev);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "failed to initialize the switch");
> +		return rc;
> +	}
> +
> +	/* enable Indirect Access from SPI to the VPHY registers */
> +	rc = lan937x_enable_spi_indirect_access(dev);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "failed to enable spi indirect access");
> +		return rc;
> +	}
> +
> +	rc = lan937x_mdio_register(dev->ds);
> +	if (rc < 0) {
> +		dev_err(dev->dev, "failed to register the mdio");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +const struct ksz_dev_ops lan937x_dev_ops = {
> +	.get_port_addr = lan937x_get_port_addr,
> +	.cfg_port_member = lan937x_cfg_port_member,
> +	.flush_dyn_mac_table = lan937x_flush_dyn_mac_table,
> +	.port_setup = lan937x_port_setup,
> +	.r_mib_cnt = lan937x_r_mib_cnt,
> +	.r_mib_pkt = lan937x_r_mib_pkt,
> +	.port_init_cnt = lan937x_port_init_cnt,
> +	.shutdown = lan937x_reset_switch,
> +	.detect = lan937x_switch_detect,
> +	.init = lan937x_init,
> +	.exit = lan937x_switch_exit,
> +};
> diff --git a/drivers/net/dsa/microchip/lan937x_dev.h b/drivers/net/dsa/microchip/lan937x_dev.h
> new file mode 100644
> index 000000000000..c98e8140ca30
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan937x_dev.h
> @@ -0,0 +1,68 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Microchip lan937x dev ops headers
> + * Copyright (C) 2019-2021 Microchip Technology Inc.
> + */
> +
> +#ifndef __LAN937X_CFG_H
> +#define __LAN937X_CFG_H
> +
> +int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set);
> +int lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
> +		     u8 bits, bool set);
> +int lan937x_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set);
> +int lan937x_pread8(struct ksz_device *dev, int port, int offset,
> +		   u8 *data);
> +int lan937x_pread16(struct ksz_device *dev, int port, int offset,
> +		    u16 *data);
> +int lan937x_pread32(struct ksz_device *dev, int port, int offset,
> +		    u32 *data);
> +int lan937x_pwrite8(struct ksz_device *dev, int port,
> +		    int offset, u8 data);
> +int lan937x_pwrite16(struct ksz_device *dev, int port,
> +		     int offset, u16 data);
> +int lan937x_pwrite32(struct ksz_device *dev, int port,
> +		     int offset, u32 data);
> +int lan937x_port_cfg32(struct ksz_device *dev, int port, int offset,
> +		       u32 bits, bool set);
> +int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
> +			       int reg, u16 val);
> +int lan937x_internal_phy_read(struct ksz_device *dev, int addr,
> +			      int reg, u16 *val);
> +bool lan937x_is_internal_100BTX_phy_port(struct ksz_device *dev, int port);
> +bool lan937x_is_internal_t1_phy_port(struct ksz_device *dev, int port);
> +bool lan937x_is_internal_phy_port(struct ksz_device *dev, int port);
> +int lan937x_reset_switch(struct ksz_device *dev);
> +void lan937x_cfg_port_member(struct ksz_device *dev, int port,
> +			     u8 member);
> +void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port);
> +int lan937x_enable_spi_indirect_access(struct ksz_device *dev);
> +
> +struct mib_names {
> +	int index;
> +	char string[ETH_GSTRING_LEN];
> +};
> +
> +struct lan_alu_struct {
> +	/* entry 1 */
> +	u8	is_static:1;
> +	u8	is_src_filter:1;
> +	u8	is_dst_filter:1;
> +	u8	prio_age:3;
> +	u32	_reserv_0_1:23;
> +	u8	mstp:3;
> +	/* entry 2 */
> +	u8	is_override:1;
> +	u8	is_use_fid:1;
> +	u32	_reserv_1_1:22;
> +	u8	port_forward:8;
> +	/* entry 3 & 4*/
> +	u32	_reserv_2_1:9;
> +	u8	fid:7;
> +	u8	mac[ETH_ALEN];
> +};
> +
> +extern const struct dsa_switch_ops lan937x_switch_ops;
> +extern const struct ksz_dev_ops lan937x_dev_ops;
> +extern const struct mib_names lan937x_mib_names[];
> +
> +#endif
> diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
> new file mode 100644
> index 000000000000..944c6f4d6d60
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan937x_main.c
> @@ -0,0 +1,364 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Microchip LAN937X switch driver main logic
> + * Copyright (C) 2019-2021 Microchip Technology Inc.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/iopoll.h>
> +#include <linux/phy.h>
> +#include <linux/if_bridge.h>
> +#include <net/dsa.h>
> +#include <net/switchdev.h>
> +
> +#include "lan937x_reg.h"
> +#include "ksz_common.h"
> +#include "lan937x_dev.h"
> +
> +static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds,
> +						      int port,
> +						      enum dsa_tag_protocol mp)
> +{
> +	return DSA_TAG_PROTO_LAN937X_VALUE;
> +}
> +
> +static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	u16 val;
> +	int rc;
> +
> +	rc = lan937x_internal_phy_read(dev, addr, reg, &val);
> +
> +	if (rc < 0)
> +		return rc;
> +
> +	return val;
> +}
> +
> +static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg,
> +			       u16 val)
> +{
> +	struct ksz_device *dev = ds->priv;
> +
> +	return lan937x_internal_phy_write(dev, addr, reg, val);
> +}
> +
> +static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
> +				       u8 state)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	struct ksz_port *p = &dev->ports[port];
> +	int forward = dev->member;
> +	int member = -1;
> +	u8 data;
> +
> +	lan937x_pread8(dev, port, P_STP_CTRL, &data);
> +	data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
> +
> +	switch (state) {
> +	case BR_STATE_DISABLED:
> +		data |= PORT_LEARN_DISABLE;
> +		break;
> +	case BR_STATE_LISTENING:
> +		data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
> +		if (p->stp_state == BR_STATE_DISABLED)
> +			member = dev->host_mask | p->vid_member;
> +		break;
> +	case BR_STATE_LEARNING:
> +		data |= PORT_RX_ENABLE;
> +		break;
> +	case BR_STATE_FORWARDING:
> +		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
> +
> +		member = dev->host_mask | p->vid_member;
> +		mutex_lock(&dev->dev_mutex);
> +
> +		/* Port is a member of a bridge. */
> +		if (dev->br_member & (1 << port)) {
> +			dev->member |= (1 << port);
> +			member = dev->member;
> +		}
> +		mutex_unlock(&dev->dev_mutex);
> +		break;
> +	case BR_STATE_BLOCKING:
> +		data |= PORT_LEARN_DISABLE;
> +		if (p->stp_state == BR_STATE_DISABLED)
> +			member = dev->host_mask | p->vid_member;
> +		break;
> +	default:
> +		dev_err(ds->dev, "invalid STP state: %d\n", state);
> +		return;
> +	}
> +
> +	lan937x_pwrite8(dev, port, P_STP_CTRL, data);
> +
> +	p->stp_state = state;
> +	mutex_lock(&dev->dev_mutex);
> +
> +	/* Port membership may share register with STP state. */
> +	if (member >= 0 && member != p->member)
> +		lan937x_cfg_port_member(dev, port, (u8)member);
> +
> +	/* Check if forwarding needs to be updated. */
> +	if (state != BR_STATE_FORWARDING) {
> +		if (dev->br_member & (1 << port))
> +			dev->member &= ~(1 << port);
> +	}
> +
> +	/* When topology has changed the function ksz_update_port_member
> +	 * should be called to modify port forwarding behavior.
> +	 */
> +	if (forward != dev->member)
> +		ksz_update_port_member(dev, port);
> +	mutex_unlock(&dev->dev_mutex);
> +}
> +
> +static phy_interface_t lan937x_get_interface(struct ksz_device *dev, int port)
> +{
> +	phy_interface_t interface;
> +	u8 data8;
> +	int rc;
> +
> +	if (lan937x_is_internal_phy_port(dev, port))
> +		return PHY_INTERFACE_MODE_NA;

I think conventional wisdom is to use PHY_INTERFACE_MODE_INTERNAL for
internal ports, as the name would suggest.

> +
> +	/* read interface from REG_PORT_XMII_CTRL_1 register */
> +	rc = lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
> +
> +	if (rc < 0)
> +		return PHY_INTERFACE_MODE_NA;
> +
> +	switch (data8 & PORT_MII_SEL_M) {
> +	case PORT_RMII_SEL:
> +		interface = PHY_INTERFACE_MODE_RMII;
> +		break;
> +	case PORT_RGMII_SEL:
> +		interface = PHY_INTERFACE_MODE_RGMII;
> +		if (data8 & PORT_RGMII_ID_EG_ENABLE)
> +			interface = PHY_INTERFACE_MODE_RGMII_TXID;
> +		if (data8 & PORT_RGMII_ID_IG_ENABLE) {
> +			interface = PHY_INTERFACE_MODE_RGMII_RXID;
> +			if (data8 & PORT_RGMII_ID_EG_ENABLE)
> +				interface = PHY_INTERFACE_MODE_RGMII_ID;
> +		}
> +		break;
> +	case PORT_MII_SEL:
> +	default:
> +		/* Interface is MII */
> +		interface = PHY_INTERFACE_MODE_MII;
> +		break;
> +	}
> +
> +	return interface;
> +}
> +
> +static void lan937x_config_cpu_port(struct dsa_switch *ds)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	struct ksz_port *p;
> +	int i;
> +
> +	ds->num_ports = dev->port_cnt;
> +
> +	for (i = 0; i < dev->port_cnt; i++) {
> +		if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
> +			phy_interface_t interface;
> +			const char *prev_msg;
> +			const char *prev_mode;
> +
> +			dev->cpu_port = i;
> +			dev->host_mask = (1 << dev->cpu_port);
> +			dev->port_mask |= dev->host_mask;
> +			p = &dev->ports[i];
> +
> +			/* Read from XMII register to determine host port
> +			 * interface.  If set specifically in device tree
> +			 * note the difference to help debugging.
> +			 */
> +			interface = lan937x_get_interface(dev, i);
> +			if (!p->interface) {
> +				if (dev->compat_interface)
> +					p->interface = dev->compat_interface;

Compatibility with what? This is a new driver.

> +				else
> +					p->interface = interface;
> +			}
> +
> +			if (interface && interface != p->interface) {
> +				prev_msg = " instead of ";
> +				prev_mode = phy_modes(interface);
> +			} else {
> +				prev_msg = "";
> +				prev_mode = "";
> +			}
> +
> +			dev_info(dev->dev,
> +				 "Port%d: using phy mode %s%s%s\n",
> +				 i,
> +				 phy_modes(p->interface),
> +				 prev_msg,
> +				 prev_mode);

It's unlikely that anyone is going to be able to find such a composite
error message string using grep.

> +
> +			/* enable cpu port */
> +			lan937x_port_setup(dev, i, true);
> +			p->vid_member = dev->port_mask;
> +		}
> +	}
> +
> +	dev->member = dev->host_mask;
> +
> +	for (i = 0; i < dev->port_cnt; i++) {
> +		if (i == dev->cpu_port)
> +			continue;
> +		p = &dev->ports[i];
> +
> +		/* Initialize to non-zero so that lan937x_cfg_port_member() will
> +		 * be called.
> +		 */
> +		p->vid_member = (1 << i);
> +		p->member = dev->port_mask;
> +		lan937x_port_stp_state_set(ds, i, BR_STATE_DISABLED);
> +	}
> +}
> +
> +static int lan937x_setup(struct dsa_switch *ds)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	int rc;
> +
> +	dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
> +				       dev->num_vlans, GFP_KERNEL);
> +	if (!dev->vlan_cache)
> +		return -ENOMEM;
> +
> +	rc = lan937x_reset_switch(dev);
> +	if (rc < 0) {
> +		dev_err(ds->dev, "failed to reset switch\n");
> +		return rc;
> +	}
> +
> +	/* Required for port partitioning. */
> +	lan937x_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
> +		      true);
> +
> +	lan937x_config_cpu_port(ds);
> +
> +	ds->configure_vlan_while_not_filtering = true;

You can delete this line, it's implicitly true now.

> +
> +	/* Enable aggressive back off & UNH */
> +	lan937x_cfg(dev, REG_SW_MAC_CTRL_0, SW_PAUSE_UNH_MODE | SW_NEW_BACKOFF |
> +						SW_AGGR_BACKOFF, true);
> +
> +	lan937x_cfg(dev, REG_SW_MAC_CTRL_1, (MULTICAST_STORM_DISABLE
> +							| NO_EXC_COLLISION_DROP), true);

Odd indentation, no explanation.

> +
> +	/* queue based egress rate limit */
> +	lan937x_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
> +
> +	lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true);
> +
> +	/* enable global MIB counter freeze function */
> +	lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true);
> +
> +	/* disable CLK125 & CLK25, 1: disable, 0: enable*/
> +	lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, (SW_CLK125_ENB |
> +						SW_CLK25_ENB), true);

Do you run checkpatch from time to time?

> +
> +	lan937x_enable_spi_indirect_access(dev);
> +
> +	/* start switch */
> +	lan937x_cfg(dev, REG_SW_OPERATION, SW_START, true);
> +
> +	ksz_init_mib_timer(dev);
> +
> +	return 0;
> +}
> +
> +static int lan937x_change_mtu(struct dsa_switch *ds, int port, int mtu)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	u16 max_size;
> +	int rc;
> +
> +	if (mtu >= FR_MIN_SIZE) {
> +		rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, true);
> +		max_size = FR_MAX_SIZE;
> +	} else {
> +		rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, false);
> +		max_size = FR_MIN_SIZE;
> +	}
> +
> +	if (rc < 0) {
> +		dev_err(ds->dev, "failed to enable jumbo\n");
> +		return rc;
> +	}
> +
> +	/* Write the frame size in PORT_MAX_FR_SIZE register */
> +	rc = lan937x_pwrite16(dev, port, PORT_MAX_FR_SIZE, max_size);
> +
> +	if (rc < 0) {
> +		dev_err(ds->dev, "failed to change the mtu\n");
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
> +{
> +	/* Frame size is 9000 (= 0x2328) if
> +	 * jumbo frame support is enabled, PORT_JUMBO_EN bit will be enabled
> +	 * based on mtu in lan937x_change_mtu() API
> +	 */
> +	return FR_MAX_SIZE;

Frame size is one thing. But MTU is L2 payload, which excludes MAC DA,
MAC SA, EtherType and VLAN ID. So does the switch really accept a packet
with an L2 payload length of 9000 bytes and a VLAN tag?

> +}
> +
> +static int	lan937x_port_pre_bridge_flags(struct dsa_switch *ds, int port,
> +					      struct switchdev_brport_flags flags,
> +					 struct netlink_ext_ack *extack)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static int	lan937x_port_bridge_flags(struct dsa_switch *ds, int port,
> +					  struct switchdev_brport_flags flags,
> +					 struct netlink_ext_ack *extack)
> +{
> +	return -EOPNOTSUPP;
> +}

This shouldn't have been implemented but is necessary due to a bug, see
commit 70a7c484c7c3 ("net: dsa: fix bridge support for drivers without
port_bridge_flags callback").

> +
> +const struct dsa_switch_ops lan937x_switch_ops = {
> +	.get_tag_protocol	= lan937x_get_tag_protocol,
> +	.setup			= lan937x_setup,
> +	.phy_read		= lan937x_phy_read16,
> +	.phy_write		= lan937x_phy_write16,
> +	.port_enable		= ksz_enable_port,
> +	.port_bridge_join	= ksz_port_bridge_join,
> +	.port_bridge_leave	= ksz_port_bridge_leave,
> +	.port_pre_bridge_flags	= lan937x_port_pre_bridge_flags,
> +	.port_bridge_flags	= lan937x_port_bridge_flags,
> +	.port_stp_state_set	= lan937x_port_stp_state_set,
> +	.port_fast_age		= ksz_port_fast_age,
> +	.port_max_mtu		= lan937x_get_max_mtu,
> +	.port_change_mtu	= lan937x_change_mtu,
> +};
> +
> +int lan937x_switch_register(struct ksz_device *dev)
> +{
> +	int ret;
> +
> +	ret = ksz_switch_register(dev, &lan937x_dev_ops);
> +
> +	if (ret) {
> +		if (dev->mdio_np) {
> +			mdiobus_unregister(dev->ds->slave_mii_bus);

I don't see any mdiobus_unregister when the driver is removed?

> +			of_node_put(dev->mdio_np);

Also, why keep mdio_np inside ksz_device, therefore for the entire
lifetime of the driver? Why do you need it? Not to mention you don't
appear to be ever calling of_node_put on unbind, unless I'm missing
something.

> +		}
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(lan937x_switch_register);
> +
> +MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@microchip.com>");
> +MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/dsa/microchip/lan937x_spi.c b/drivers/net/dsa/microchip/lan937x_spi.c
> new file mode 100644
> index 000000000000..d9731d6afb96
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan937x_spi.c
> @@ -0,0 +1,226 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Microchip LAN937X switch driver register access through SPI
> + * Copyright (C) 2019-2021 Microchip Technology Inc.
> + */
> +#include <asm/unaligned.h>

Why do you need this?

> +
> +#include <linux/delay.h>

Or this?

> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +#include <linux/of_device.h>
> +
> +#include "ksz_common.h"
> +
> +#define SPI_ADDR_SHIFT				24
> +#define SPI_ADDR_ALIGN				3
> +#define SPI_TURNAROUND_SHIFT		5
> +
> +KSZ_REGMAP_TABLE(lan937x, 32, SPI_ADDR_SHIFT,
> +		 SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
> +
> +struct lan937x_chip_data {
> +	u32 chip_id;
> +	const char *dev_name;
> +	int num_vlans;
> +	int num_alus;
> +	int num_statics;
> +	int cpu_ports;
> +	int port_cnt;
> +};
> +
> +static const struct of_device_id lan937x_dt_ids[];
> +
> +static const struct lan937x_chip_data lan937x_switch_chips[] = {
> +	{
> +		.chip_id = 0x00937010,
> +		.dev_name = "LAN9370",
> +		.num_vlans = 4096,
> +		.num_alus = 1024,
> +		.num_statics = 256,
> +		/* can be configured as cpu port */
> +		.cpu_ports = 0x10,
> +		/* total port count */
> +		.port_cnt = 5,
> +	},
> +	{
> +		.chip_id = 0x00937110,
> +		.dev_name = "LAN9371",
> +		.num_vlans = 4096,
> +		.num_alus = 1024,
> +		.num_statics = 256,
> +		/* can be configured as cpu port */
> +		.cpu_ports = 0x30,
> +		/* total port count */
> +		.port_cnt = 6,
> +	},
> +	{
> +		.chip_id = 0x00937210,
> +		.dev_name = "LAN9372",
> +		.num_vlans = 4096,
> +		.num_alus = 1024,
> +		.num_statics = 256,
> +		/* can be configured as cpu port */
> +		.cpu_ports = 0x30,
> +		/* total port count */
> +		.port_cnt = 8,
> +	},
> +	{
> +		.chip_id = 0x00937310,
> +		.dev_name = "LAN9373",
> +		.num_vlans = 4096,
> +		.num_alus = 1024,
> +		.num_statics = 256,
> +		/* can be configured as cpu port */
> +		.cpu_ports = 0x38,
> +		/* total port count */
> +		.port_cnt = 5,
> +	},
> +	{
> +		.chip_id = 0x00937410,
> +		.dev_name = "LAN9374",
> +		.num_vlans = 4096,
> +		.num_alus = 1024,
> +		.num_statics = 256,
> +		/* can be configured as cpu port */
> +		.cpu_ports = 0x30,
> +		/* total port count */
> +		.port_cnt = 8,
> +	},
> +

The new line shouldn't be here?

> +};
> +

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management
  2021-04-22  9:42 ` [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
@ 2021-04-22 20:05   ` Vladimir Oltean
  0 siblings, 0 replies; 31+ messages in thread
From: Vladimir Oltean @ 2021-04-22 20:05 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:53PM +0530, Prasanna Vengateshan wrote:
> Support for phylink_validate() and reused KSZ commmon API for
> phylink_mac_link_down() operation
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  drivers/net/dsa/microchip/lan937x_main.c | 42 ++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 
> diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
> index 944c6f4d6d60..93c392081423 100644
> --- a/drivers/net/dsa/microchip/lan937x_main.c
> +++ b/drivers/net/dsa/microchip/lan937x_main.c
> @@ -312,6 +312,46 @@ static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
>  	return FR_MAX_SIZE;
>  }
>  
> +static void lan937x_phylink_validate(struct dsa_switch *ds, int port,
> +				     unsigned long *supported,
> +			  struct phylink_link_state *state)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
> +
> +	if (phy_interface_mode_is_rgmii(state->interface) ||
> +	    state->interface == PHY_INTERFACE_MODE_SGMII ||
> +		state->interface == PHY_INTERFACE_MODE_RMII ||
> +		state->interface == PHY_INTERFACE_MODE_MII ||
> +		lan937x_is_internal_100BTX_phy_port(dev, port)) {
> +		phylink_set(mask, 10baseT_Half);
> +		phylink_set(mask, 10baseT_Full);
> +		phylink_set(mask, 100baseT_Half);
> +		phylink_set(mask, 100baseT_Full);
> +		phylink_set(mask, Autoneg);
> +		phylink_set_port_modes(mask);
> +		phylink_set(mask, Pause);
> +		phylink_set(mask, Asym_Pause);

Why do you advertise pause if you don't react to it (I commented on the
previous patch that you force flow control off)?

And the Microchip KSZ DSA phylink integration is the strangest I've ever
seen, bar none.

Do your RGMII ports work at 10/100/1000 Mbps? If yes, how?

Do your SGMII ports work in fixed-link, at 10/100/1000? How about with a
PHY that expects in-band autoneg?

> +	}
> +
> +	/*  For RGMII & SGMII interfaces */
> +	if (phy_interface_mode_is_rgmii(state->interface) ||
> +	    state->interface == PHY_INTERFACE_MODE_SGMII) {
> +		phylink_set(mask, 1000baseT_Full);
> +	}
> +
> +	/* For T1 PHY */
> +	if (lan937x_is_internal_t1_phy_port(dev, port)) {
> +		phylink_set(mask, 100baseT1_Full);
> +		phylink_set_port_modes(mask);
> +	}
> +
> +	bitmap_and(supported, supported, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +	bitmap_and(state->advertising, state->advertising, mask,
> +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> +}
> +
>  static int	lan937x_port_pre_bridge_flags(struct dsa_switch *ds, int port,
>  					      struct switchdev_brport_flags flags,
>  					 struct netlink_ext_ack *extack)
> @@ -340,6 +380,8 @@ const struct dsa_switch_ops lan937x_switch_ops = {
>  	.port_fast_age		= ksz_port_fast_age,
>  	.port_max_mtu		= lan937x_get_max_mtu,
>  	.port_change_mtu	= lan937x_change_mtu,
> +	.phylink_validate	= lan937x_phylink_validate,
> +	.phylink_mac_link_down	= ksz_mac_link_down,
>  };
>  
>  int lan937x_switch_register(struct ksz_device *dev)
> -- 
> 2.27.0
> 

Fundamentally, what is the purpose of this patch?

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations
  2021-04-22  9:42 ` [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
@ 2021-04-22 20:11   ` Vladimir Oltean
  0 siblings, 0 replies; 31+ messages in thread
From: Vladimir Oltean @ 2021-04-22 20:11 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, Apr 22, 2021 at 03:12:55PM +0530, Prasanna Vengateshan wrote:
> Added support for port_mirror_add() and port_mirror_del operations
> 
> Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> ---
>  drivers/net/dsa/microchip/lan937x_main.c | 50 ++++++++++++++++++++++++
>  1 file changed, 50 insertions(+)
> 
> diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
> index 573d2dd906f5..bfce5098ea69 100644
> --- a/drivers/net/dsa/microchip/lan937x_main.c
> +++ b/drivers/net/dsa/microchip/lan937x_main.c
> @@ -128,6 +128,54 @@ static void lan937x_port_stp_state_set(struct dsa_switch *ds, int port,
>  	mutex_unlock(&dev->dev_mutex);
>  }
>  
> +static int lan937x_port_mirror_add(struct dsa_switch *ds, int port,
> +				   struct dsa_mall_mirror_tc_entry *mirror,
> +				   bool ingress)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	int rc;
> +
> +	if (ingress)
> +		rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
> +	else
> +		rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
> +
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
> +	if (rc < 0)
> +		return rc;

This is odd, you shouldn't have to say 'the port which I'm sniffing is
not a sniffer'.

> +
> +	/* configure mirror port */
> +	rc = lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
> +			      PORT_MIRROR_SNIFFER, true);
> +	if (rc < 0)
> +		return rc;
> +
> +	rc = lan937x_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
> +
> +	return rc;
> +}
> +
> +static void lan937x_port_mirror_del(struct dsa_switch *ds, int port,
> +				    struct dsa_mall_mirror_tc_entry *mirror)
> +{
> +	struct ksz_device *dev = ds->priv;
> +	u8 data;
> +
> +	if (mirror->ingress)
> +		lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
> +	else
> +		lan937x_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
> +
> +	lan937x_pread8(dev, port, P_MIRROR_CTRL, &data);
> +
> +	if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
> +		lan937x_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
> +				 PORT_MIRROR_SNIFFER, false);
> +}
> +

So let me understand. You have a PORT_MIRROR_SNIFFER setting per port.
Presumably the mirrored traffic inside the switch is sent to the ports
which have PORT_MIRROR_SNIFFER = true.
But this isn't the interpretation of the tc utility.

Instead, let's say you have the following sequence of commands:

tc filter add dev lan0 ingress matchall skip_sw action mirred egress mirror dev lan1
tc filter add dev lan2 ingress matchall skip_sw action mirred egress mirror dev lan3

What in your hardware configuration makes traffic from lan0 be mirrored
to lan1 but not to lan3?

>  static phy_interface_t lan937x_get_interface(struct ksz_device *dev, int port)
>  {
>  	phy_interface_t interface;
> @@ -396,6 +444,8 @@ const struct dsa_switch_ops lan937x_switch_ops = {
>  	.port_bridge_flags	= lan937x_port_bridge_flags,
>  	.port_stp_state_set	= lan937x_port_stp_state_set,
>  	.port_fast_age		= ksz_port_fast_age,
> +	.port_mirror_add	= lan937x_port_mirror_add,
> +	.port_mirror_del	= lan937x_port_mirror_del,
>  	.port_max_mtu		= lan937x_get_max_mtu,
>  	.port_change_mtu	= lan937x_change_mtu,
>  	.phylink_validate	= lan937x_phylink_validate,
> -- 
> 2.27.0
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22 19:59   ` Vladimir Oltean
@ 2021-04-22 23:28     ` Andrew Lunn
  2021-04-26  8:54       ` Prasanna Vengateshan
  2021-04-27  7:43     ` Prasanna Vengateshan
  1 sibling, 1 reply; 31+ messages in thread
From: Andrew Lunn @ 2021-04-22 23:28 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Prasanna Vengateshan, netdev, robh+dt, UNGLinuxDriver,
	hkallweit1, linux, davem, kuba, linux-kernel, vivien.didelot,
	f.fainelli, devicetree

> > +
> > +		lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
> > +
> > +		/* clear MII selection & set it based on interface later */
> > +		data8 &= ~PORT_MII_SEL_M;
> > +
> > +		/* configure MAC based on p->interface */
> > +		switch (p->interface) {
> > +		case PHY_INTERFACE_MODE_MII:
> > +			lan937x_set_gbit(dev, false, &data8);
> > +			data8 |= PORT_MII_SEL;
> > +			break;
> > +		case PHY_INTERFACE_MODE_RMII:
> > +			lan937x_set_gbit(dev, false, &data8);
> > +			data8 |= PORT_RMII_SEL;
> > +			break;
> > +		default:
> > +			lan937x_set_gbit(dev, true, &data8);
> > +			data8 |= PORT_RGMII_SEL;
> > +
> > +			data8 &= ~PORT_RGMII_ID_IG_ENABLE;
> > +			data8 &= ~PORT_RGMII_ID_EG_ENABLE;
> > +
> > +			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> > +			    p->interface == PHY_INTERFACE_MODE_RGMII_RXID)
> > +				data8 |= PORT_RGMII_ID_IG_ENABLE;
> > +
> > +			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> > +			    p->interface == PHY_INTERFACE_MODE_RGMII_TXID)
> > +				data8 |= PORT_RGMII_ID_EG_ENABLE;
> 
> This is interesting. If you have an RGMII port connected to an external
> PHY, how do you ensure that either the lan937x driver, or the PHY driver,
> but not both, enable RGMII delays?

What generally happens is the MAC adds no delays, and the PHY acts
upon the interface mode, inserting delays as requested.

There are a very small number of exceptions to this, for boards which
have a PHY which cannot do delays, and the MAC can. If i remember
correctly, this pretty much limited to one MAC vendor. In that case,
the MAC adds delays, if the interface mode requests it, and it always
passes PHY_INTERFACE_MODE_RGMII to the PHY so it does not add delays.

So what needs to be looked at here is what is passed to the phy
connect call? passing p->interface is definitely wrong if the MAC is
acting on it.

If even if the connect is correct, i would still prefer the MAC not do
the delays, let the PHY do it, like nearly every other setup.

	Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
  2021-04-22 19:59   ` Vladimir Oltean
@ 2021-04-22 23:32   ` Andrew Lunn
  2021-04-22 23:38   ` Andrew Lunn
  2021-04-22 23:43   ` Andrew Lunn
  3 siblings, 0 replies; 31+ messages in thread
From: Andrew Lunn @ 2021-04-22 23:32 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: netdev, olteanv, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> +static void lan937x_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
> +			      u64 *cnt)
> +{
> +	unsigned int val;
> +	u32 data;
> +	int ret;
> +
> +	/* Enable MIB Counter read*/
> +	data = MIB_COUNTER_READ;
> +	data |= (addr << MIB_COUNTER_INDEX_S);
> +	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
> +
> +	ret = regmap_read_poll_timeout(dev->regmap[2],
> +				       PORT_CTRL_ADDR(port,
> +						      REG_PORT_MIB_CTRL_STAT__4),
> +					   val, !(val & MIB_COUNTER_READ), 10, 1000);
> +	/* failed to read MIB. get out of loop */

Another loop which is not a loop. Please review your comments and
check they make sense.

      Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
  2021-04-22 19:59   ` Vladimir Oltean
  2021-04-22 23:32   ` Andrew Lunn
@ 2021-04-22 23:38   ` Andrew Lunn
  2021-04-22 23:43   ` Andrew Lunn
  3 siblings, 0 replies; 31+ messages in thread
From: Andrew Lunn @ 2021-04-22 23:38 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: netdev, olteanv, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> +static void lan937x_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
> +			      u64 *dropped, u64 *cnt)
> +{
> +	addr = lan937x_mib_names[addr].index;
> +	lan937x_r_mib_cnt(dev, port, addr, cnt);
> +}
> +
> +static void lan937x_port_init_cnt(struct ksz_device *dev, int port)
> +{
> +	struct ksz_port_mib *mib = &dev->ports[port].mib;
> +
> +	/* flush all enabled port MIB counters */
> +	mutex_lock(&mib->cnt_mutex);
> +	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
> +			 MIB_COUNTER_FLUSH_FREEZE);
> +	ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
> +	lan937x_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0);
> +	mutex_unlock(&mib->cnt_mutex);
> +
> +	mib->cnt_ptr = 0;
> +	memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));

This setting of cnt_ptr to zero and the memset() seem to be common to
all the drivers. Please add a cleanup patch which moves this into
ksz_init_mib_timer().

	Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
                     ` (2 preceding siblings ...)
  2021-04-22 23:38   ` Andrew Lunn
@ 2021-04-22 23:43   ` Andrew Lunn
  3 siblings, 0 replies; 31+ messages in thread
From: Andrew Lunn @ 2021-04-22 23:43 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: netdev, olteanv, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> +int lan937x_reset_switch(struct ksz_device *dev)
> +{
> +	u32 data32;
> +	u8 data8;
> +	int rc;
> +
> +	/* reset switch */
> +	rc = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
> +	if (rc < 0)
> +		return rc;

Please consistently use ret everywhere, not rc.

       Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x
  2021-04-22 17:38   ` Rob Herring
@ 2021-04-26  4:05     ` Prasanna Vengateshan
  2021-04-26 16:04       ` Florian Fainelli
  0 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-26  4:05 UTC (permalink / raw)
  To: Rob Herring
  Cc: andrew, netdev, olteanv, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, 2021-04-22 at 12:38 -0500, Rob Herring wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Thu, Apr 22, 2021 at 03:12:49PM +0530, Prasanna Vengateshan wrote:
> > Documentation in .yaml format and updates to the MAINTAINERS
> > Also 'make dt_binding_check' is passed
> > 
> > Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> > ---
> >  .../bindings/net/dsa/microchip,lan937x.yaml   | 142 ++++++++++++++++++
> >  MAINTAINERS                                   |   1 +
> >  2 files changed, 143 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> > 
> > diff --git
> > a/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> > b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> > new file mode 100644
> > index 000000000000..22128a52d699
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan937x.yaml
> > @@ -0,0 +1,142 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/net/dsa/microchip,lan937x.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: LAN937x Ethernet Switch Series Tree Bindings
> > +
> > +maintainers:
> > +  - UNGLinuxDriver@microchip.com
> > +
> > +allOf:
> > +  - $ref: dsa.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - microchip,lan9370
> > +      - microchip,lan9371
> > +      - microchip,lan9372
> > +      - microchip,lan9373
> > +      - microchip,lan9374
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  spi-max-frequency:
> > +    maximum: 50000000
> > +
> > +  reset-gpios:
> > +    description: Optional gpio specifier for a reset line
> > +    maxItems: 1
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/gpio/gpio.h>
> > +
> > +    //Ethernet switch connected via spi to the host
> 
> If this is on SPI, why is it not under the spi bus node?
Thanks for reviewing the patch. I will move this comment to above the spi node.

> 
> > +    ethernet {
> > +      #address-cells = <1>;
> > +      #size-cells = <0>;
> > +
> > +      fixed-link {
> > +        speed = <1000>;
> > +        full-duplex;
> > +      };
> > +    };
> > +
> > +    spi {
> > +      #address-cells = <1>;
> > +      #size-cells = <0>;
> > +
> > +      lan9374: switch@0 {
> > +        compatible = "microchip,lan9374";
> > +        reg = <0>;
> > +
> > +        spi-max-frequency = <44000000>;
> > +
> > +        ethernet-ports {
> > +          #address-cells = <1>;
> > +          #size-cells = <0>;
> > +          port@0 {
> > +            reg = <0>;
> > +            label = "lan1";
> > +            phy-handle = <&t1phy0>;
> > +          };
> > +          port@1 {
> > +            reg = <1>;
> > +            label = "lan2";
> > +            phy-handle = <&t1phy1>;
> > +          };
> > +          port@2 {
> > +            reg = <2>;
> > +            label = "lan4";
> > +            phy-handle = <&t1phy2>;
> > +          };
> > +          port@3 {
> > +            reg = <3>;
> > +            label = "lan6";
> > +            phy-handle = <&t1phy3>;
> > +          };
> > +          port@4 {
> > +            reg = <4>;
> > +            phy-mode = "rgmii";
> > +            ethernet = <&ethernet>;
> 
> You are missing 'ethernet' label.
This is the cpu port and label is not used anywhere. i received this feedback in
last patch version. 

> 
> > 
> > +          };
> > +        };
> > +
> > +        mdio {
> > +          compatible = "microchip,lan937x-mdio";
> 
> You can just drop this to make the example pass. Or convert that binding
> to schema.
Okay, will remove in the next revision. Also i have received an alternate
suggestion in the other patch to avoid usage of compatible string.

> 
> > 
> > --
> > 2.27.0
> > 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver
  2021-04-22 12:45   ` Andrew Lunn
@ 2021-04-26  4:06     ` Prasanna Vengateshan
  0 siblings, 0 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-26  4:06 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: netdev, olteanv, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, 2021-04-22 at 14:45 +0200, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> > +#define PORT_T1_PHY_RESET    BIT(15)
> > +#define PORT_T1_PHY_LOOPBACK BIT(14)
> > +#define PORT_T1_SPEED_100MBIT        BIT(13)
> > +#define PORT_T1_POWER_DOWN   BIT(11)
> > +#define PORT_T1_ISOLATE      BIT(10)
> > +#define PORT_T1_FULL_DUPLEX  BIT(8)
> 
> These appear to be standard BMCR_ values. Please don't define your
> own.
> 
> > +
> > +#define REG_PORT_T1_PHY_BASIC_STATUS 0x01
> > +
> > +#define PORT_T1_MII_SUPPRESS_CAPABLE BIT(6)
> > +#define PORT_T1_LINK_STATUS          BIT(2)
> > +#define PORT_T1_EXTENDED_CAPABILITY  BIT(0)
> > +
> > +#define REG_PORT_T1_PHY_ID_HI 0x02
> > +#define REG_PORT_T1_PHY_ID_LO 0x03
> 
> MII_PHYSID1 and MII_PHYSID2
> 
> Please go through all these #defines and replace them with the
> standard ones Linux provides. You are obfusticating the code by not
> using what people already know.
Sure, i will review it for all the definitions.

> 
>       Andrew



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x
  2021-04-22 19:18   ` Vladimir Oltean
@ 2021-04-26  4:33     ` Prasanna Vengateshan
  2021-04-26 12:27       ` Andrew Lunn
  0 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-26  4:33 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, 2021-04-22 at 22:18 +0300, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Thu, Apr 22, 2021 at 03:12:51PM +0530, Prasanna Vengateshan wrote:
> > The Microchip LAN937X switches have a tagging protocol which is
> > very similar to KSZ tagging. So that the implementation is added to
> > tag_ksz.c and reused common APIs
> > 
> > Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> > ---
> >  include/net/dsa.h |  2 ++
> >  net/dsa/Kconfig   |  4 ++--
> >  net/dsa/tag_ksz.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 62 insertions(+), 2 deletions(-)
> > 
> > diff --git a/include/net/dsa.h b/include/net/dsa.h
> > index 507082959aa4..98c1ab6dc4dc 100644
> > --- a/include/net/dsa.h
> > +++ b/include/net/dsa.h
> > @@ -50,6 +50,7 @@ struct phylink_link_state;
> >  #define DSA_TAG_PROTO_OCELOT_8021Q_VALUE     20
> >  #define DSA_TAG_PROTO_SEVILLE_VALUE          21
> >  #define DSA_TAG_PROTO_BRCM_LEGACY_VALUE              22
> > +#define DSA_TAG_PROTO_LAN937X_VALUE          23
> > 
> >  enum dsa_tag_protocol {
> >       DSA_TAG_PROTO_NONE              = DSA_TAG_PROTO_NONE_VALUE,
> > @@ -75,6 +76,7 @@ enum dsa_tag_protocol {
> >       DSA_TAG_PROTO_XRS700X           = DSA_TAG_PROTO_XRS700X_VALUE,
> >       DSA_TAG_PROTO_OCELOT_8021Q      = DSA_TAG_PROTO_OCELOT_8021Q_VALUE,
> >       DSA_TAG_PROTO_SEVILLE           = DSA_TAG_PROTO_SEVILLE_VALUE,
> > +     DSA_TAG_PROTO_LAN937X           = DSA_TAG_PROTO_LAN937X_VALUE,
> >  };
> > 
> >  struct packet_type;
> > diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
> > index cbc2bd643ab2..888eb79a85f2 100644
> > --- a/net/dsa/Kconfig
> > +++ b/net/dsa/Kconfig
> > @@ -97,10 +97,10 @@ config NET_DSA_TAG_MTK
> >         Mediatek switches.
> > 
> >  config NET_DSA_TAG_KSZ
> > -     tristate "Tag driver for Microchip 8795/9477/9893 families of
> > switches"
> > +     tristate "Tag driver for Microchip 8795/937x/9477/9893 families of
> > switches"
> >       help
> >         Say Y if you want to enable support for tagging frames for the
> > -       Microchip 8795/9477/9893 families of switches.
> > +       Microchip 8795/937x/9477/9893 families of switches.
> > 
> >  config NET_DSA_TAG_RTL4_A
> >       tristate "Tag driver for Realtek 4 byte protocol A tags"
> > diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
> > index 4820dbcedfa2..a67f21bdab8f 100644
> > --- a/net/dsa/tag_ksz.c
> > +++ b/net/dsa/tag_ksz.c
> > @@ -190,10 +190,68 @@ static const struct dsa_device_ops ksz9893_netdev_ops
> > = {
> >  DSA_TAG_DRIVER(ksz9893_netdev_ops);
> >  MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893);
> > 
> > +/* For xmit, 2 bytes are added before FCS.
> > + * ------------------------------------------------------------------------
> > ---
> > + *
> > DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes)
> > + * ------------------------------------------------------------------------
> > ---
> > + * tag0 : represents tag override, lookup and valid
> > + * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
> > + *
> > + * For rcv, 1 byte is added before FCS.
> > + * ------------------------------------------------------------------------
> > ---
> > + * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
> > + * ------------------------------------------------------------------------
> > ---
> > + * tag0 : zero-based value represents port
> > + *     (eg, 0x00=port1, 0x02=port3, 0x07=port8)
> > + */
> > +#define LAN937X_INGRESS_TAG_LEN              2
> > +
> > +#define LAN937X_TAIL_TAG_OVERRIDE    BIT(11)
> 
> I had to look up the datasheet, to see what this is, because "override"
> can mean many things, not all of them are desirable.
> 
> Port Blocking Override
> When set, packets will be sent from the specified port(s) regardless, and any
> port
> blocking (see Port Transmit Enable in Port MSTP State Register) is ignored.
> 
> Do you think you can name it LAN937X_TAIL_TAG_BLOCKING_OVERRIDE? I know
> it's longer, but it's also more suggestive.
Thanks for reviewing the patch series. Suggestion looks meaningful. Noted for
next rev.

> 
> > +#define LAN937X_TAIL_TAG_LOOKUP              BIT(12)
> > +#define LAN937X_TAIL_TAG_VALID               BIT(13)
> > +#define LAN937X_TAIL_TAG_PORT_MASK   7
> > +
> > +static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
> > +                                 struct net_device *dev)
> > +{
> > +     struct dsa_port *dp = dsa_slave_to_port(dev);
> > +     __be16 *tag;
> > +     u8 *addr;
> > +     u16 val;
> > +r
> > +     tag = skb_put(skb, LAN937X_INGRESS_TAG_LEN);
> 
> Here we go again.. why is it called INGRESS_TAG_LEN if it is used during
> xmit, and only during xmit?
Definition is ingress to the LAN937x since it has different tag length for
ingress and egress of LAN937x. Do you think should it be changed? 


> 
> > +     addr = skb_mac_header(skb);
> 
> My personal choice would have been:
> 
>         const struct ethhdr *hdr = eth_hdr(skb);
> 
>         if (is_link_local_ether_addr(hdr->h_dest))
It makes more understandable since h_dest is passed. Noted.

> 
> > 
> > 2.27.0
> > 



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22 23:28     ` Andrew Lunn
@ 2021-04-26  8:54       ` Prasanna Vengateshan
  2021-04-26 12:34         ` Andrew Lunn
  0 siblings, 1 reply; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-26  8:54 UTC (permalink / raw)
  To: Andrew Lunn, Vladimir Oltean
  Cc: netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux, davem, kuba,
	linux-kernel, vivien.didelot, f.fainelli, devicetree

On Fri, 2021-04-23 at 01:28 +0200, Andrew Lunn wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> > > +
> > > +           lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
> > > +
> > > +           /* clear MII selection & set it based on interface later */
> > > +           data8 &= ~PORT_MII_SEL_M;
> > > +
> > > +           /* configure MAC based on p->interface */
> > > +           switch (p->interface) {
> > > +           case PHY_INTERFACE_MODE_MII:
> > > +                   lan937x_set_gbit(dev, false, &data8);
> > > +                   data8 |= PORT_MII_SEL;
> > > +                   break;
> > > +           case PHY_INTERFACE_MODE_RMII:
> > > +                   lan937x_set_gbit(dev, false, &data8);
> > > +                   data8 |= PORT_RMII_SEL;
> > > +                   break;
> > > +           default:
> > > +                   lan937x_set_gbit(dev, true, &data8);
> > > +                   data8 |= PORT_RGMII_SEL;
> > > +
> > > +                   data8 &= ~PORT_RGMII_ID_IG_ENABLE;
> > > +                   data8 &= ~PORT_RGMII_ID_EG_ENABLE;
> > > +
> > > +                   if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> > > +                       p->interface == PHY_INTERFACE_MODE_RGMII_RXID)
> > > +                           data8 |= PORT_RGMII_ID_IG_ENABLE;
> > > +
> > > +                   if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||
> > > +                       p->interface == PHY_INTERFACE_MODE_RGMII_TXID)
> > > +                           data8 |= PORT_RGMII_ID_EG_ENABLE;
> > 
> > This is interesting. If you have an RGMII port connected to an external
> > PHY, how do you ensure that either the lan937x driver, or the PHY driver,
> > but not both, enable RGMII delays?
> 
> What generally happens is the MAC adds no delays, and the PHY acts
> upon the interface mode, inserting delays as requested.
> 
> There are a very small number of exceptions to this, for boards which
> have a PHY which cannot do delays, and the MAC can. If i remember
> correctly, this pretty much limited to one MAC vendor. In that case,
> the MAC adds delays, if the interface mode requests it, and it always
> passes PHY_INTERFACE_MODE_RGMII to the PHY so it does not add delays.
> 
> So what needs to be looked at here is what is passed to the phy
> connect call? passing p->interface is definitely wrong if the MAC is
> acting on it.
> 
> If even if the connect is correct, i would still prefer the MAC not do
> the delays, let the PHY do it, like nearly every other setup.
> 
>         Andrew
It comes here only if the port is not internal phy which means for MII
interface. As Andrew said, let the phy driver handles the delay if it has the
associated phy vendor driver, otherwise can still be added by MAC if required
(like for cpu port)?

What do you think on the following code?

	struct dsa_port *dp = dsa_to_port(dev->ds, port);
	struct phy_device *phy_dev = dp->slave->phydev;
	.
	.
	.

    	if (!phydev || phy_driver_is_genphy(phydev)) {
		/*Add RGMII internal delay*/
    	}



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x
  2021-04-26  4:33     ` Prasanna Vengateshan
@ 2021-04-26 12:27       ` Andrew Lunn
  0 siblings, 0 replies; 31+ messages in thread
From: Andrew Lunn @ 2021-04-26 12:27 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: Vladimir Oltean, netdev, robh+dt, UNGLinuxDriver, hkallweit1,
	linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> > > +#define LAN937X_TAIL_TAG_LOOKUP              BIT(12)
> > > +#define LAN937X_TAIL_TAG_VALID               BIT(13)
> > > +#define LAN937X_TAIL_TAG_PORT_MASK   7
> > > +
> > > +static struct sk_buff *lan937x_xmit(struct sk_buff *skb,
> > > +                                 struct net_device *dev)
> > > +{
> > > +     struct dsa_port *dp = dsa_slave_to_port(dev);
> > > +     __be16 *tag;
> > > +     u8 *addr;
> > > +     u16 val;
> > > +r
> > > +     tag = skb_put(skb, LAN937X_INGRESS_TAG_LEN);
> > 
> > Here we go again.. why is it called INGRESS_TAG_LEN if it is used during
> > xmit, and only during xmit?

> Definition is ingress to the LAN937x since it has different tag length for
> ingress and egress of LAN937x. Do you think should it be changed? 

tag drivers run on Linux, not the switch. So all ingress/egress should
be relative to linux, not the switch. You are writing a linux driver
here, not firmware running on the switch.

      Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-26  8:54       ` Prasanna Vengateshan
@ 2021-04-26 12:34         ` Andrew Lunn
  0 siblings, 0 replies; 31+ messages in thread
From: Andrew Lunn @ 2021-04-26 12:34 UTC (permalink / raw)
  To: Prasanna Vengateshan
  Cc: Vladimir Oltean, netdev, robh+dt, UNGLinuxDriver, hkallweit1,
	linux, davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

> What do you think on the following code?
> 
> 	struct dsa_port *dp = dsa_to_port(dev->ds, port);
> 	struct phy_device *phy_dev = dp->slave->phydev;
> 	.
> 	.
> 	.
> 
>     	if (!phydev || phy_driver_is_genphy(phydev)) {
> 		/*Add RGMII internal delay*/
>     	}
 
 
phy_driver_is_genphy(phydev) is probably a bad idea. If you get a
delay depends on if they driver is available? I would prefer you
assume the PHY can do delays. So you only need to consider when the
MII port is used for connecting to the CPU. So make use of
dsa_is_cpu_port().

	Andrew

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x
  2021-04-26  4:05     ` Prasanna Vengateshan
@ 2021-04-26 16:04       ` Florian Fainelli
  0 siblings, 0 replies; 31+ messages in thread
From: Florian Fainelli @ 2021-04-26 16:04 UTC (permalink / raw)
  To: Prasanna Vengateshan, Rob Herring
  Cc: andrew, netdev, olteanv, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, devicetree

On 4/25/21 9:05 PM, Prasanna Vengateshan wrote:
>>> +          port@4 {
>>> +            reg = <4>;
>>> +            phy-mode = "rgmii";
>>> +            ethernet = <&ethernet>;
>>
>> You are missing 'ethernet' label.
> This is the cpu port and label is not used anywhere. i received this feedback in
> last patch version. 

Your example of a CPU port node is valid here, we need an 'ethernet'
phandle to know this is a CPU port, otherwise it is just a regular
user-facing port.
-- 
Florian

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x
  2021-04-22 19:59   ` Vladimir Oltean
  2021-04-22 23:28     ` Andrew Lunn
@ 2021-04-27  7:43     ` Prasanna Vengateshan
  1 sibling, 0 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-04-27  7:43 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

On Thu, 2021-04-22 at 22:59 +0300, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Thu, Apr 22, 2021 at 03:12:52PM +0530, Prasanna Vengateshan wrote:
> > Basic DSA driver support for lan937x and the device will be
> > configured through SPI interface.
> > 
> > drivers/net/dsa/microchip/ path is already part of MAINTAINERS &
> > the new files come under this path. Hence no update needed to the
> > MAINTAINERS
> > 
> > Reused KSZ APIs for port_bridge_join() & port_bridge_leave() and
> > added support for port_stp_state_set() & port_fast_age().
> > 
> > lan937x_flush_dyn_mac_table() which gets called from
> > port_fast_age() of KSZ common layer, hence added support for it.
> > 
> > currently port_bridge_flags returns -EOPNOTSUPP, this support
> > will be added later
> > 
> > Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> > ---
> (...)
> > +int lan937x_reset_switch(struct ksz_device *dev)
> > +{
> > +     u32 data32;
> > +     u8 data8;
> > +     int rc;
> > +
> > +     /* reset switch */
> > +     rc = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     /* default configuration */
> > +     rc = ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
> > +           SW_SRC_ADDR_FILTER;
> > +
> > +     rc = ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     /* disable interrupts */
> > +     rc = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     rc = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     rc = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     /* set broadcast storm protection 10% rate */
> > +     rc = regmap_update_bits(dev->regmap[1], REG_SW_MAC_CTRL_2,
> > +                             BROADCAST_STORM_RATE,
> > +                        (BROADCAST_STORM_VALUE *
> > +                        BROADCAST_STORM_PROT_RATE) / 100);
> 
> Why do you think this is a sane enough configuration to enable by
> default? We have tc-flower policers for this kind of stuff. If the
> broadcast policer is global to the switch and not per port, you can
> model it using:
> 
> tc qdisc add dev lan1 ingress_block 1 clsact
> tc qdisc add dev lan2 ingress_block 1 clsact
> tc qdisc add dev lan3 ingress_block 1 clsact
> tc filter add block 1 flower skip_sw dst_mac ff:ff:ff:ff:ff:ff \
>         action police \
>         rate 43Mbit \
>         burst 10000
Noted, What i understand is FLOW_ACTION_POLICE to be implemented later as part
of cls_flower_add(). Will remove this config in the next version.

> 
> > +
> > +     return rc;
> > +}
> > +
> (...)
> > +int lan937x_internal_phy_write(struct ksz_device *dev, int addr,
> > +                            int reg, u16 val)
> > +{
> > +     u16 temp, addr_base;
> > +     unsigned int value;
> > +     int rc;
> > +
> > +     /* Check for internal phy port */
> > +     if (!lan937x_is_internal_phy_port(dev, addr))
> > +             return -EOPNOTSUPP;
> > +
> > +     if (lan937x_is_internal_100BTX_phy_port(dev, addr))
> > +             addr_base = REG_PORT_TX_PHY_CTRL_BASE;
> > +     else
> > +             addr_base = REG_PORT_T1_PHY_CTRL_BASE;
> > +
> > +     temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2)));
> > +
> > +     rc = ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     /* Write the data to be written to the VPHY reg */
> > +     rc = ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
> > +     if (rc < 0)
> > +             return rc;
> > +
> > +     /* Write the Write En and Busy bit */
> > +     rc = ksz_write16(dev, REG_VPHY_IND_CTRL__2, (VPHY_IND_WRITE
> > +                             | VPHY_IND_BUSY));
> 
> This isn't quite the coding style that the kernel community is used to
> seeing. This looks more adequate:
My apologies on the incorrect indentation in some places. I will find out how
did i miss and fix from my side.

> > +
> > +     /* failed to write phy register. get out of loop */
> 
> What loop? The regmap_read_poll_timeout? If you're here you're already
> out of it, aren't you?
"get out of loop" should be removed.


> > +
> > +             
> > 
> > +static int lan937x_mdio_register(struct dsa_switch *ds)
> > +{
> > +     struct ksz_device *dev = ds->priv;
> > +     int ret;
> > +
> > +     dev->mdio_np = of_get_compatible_child(ds->dev->of_node,
> > "microchip,lan937x-mdio");
> 
> I think it is strange to have a node with a compatible string but no
> dedicated driver? I think the most popular option is to set:
> 
>         dev->mdio_np = of_get_child_by_name(node, "mdio");
> 
> and just create an "mdio" subnode with no compatible.
Sure

> 
> > +
> > +     if (!dev->mdio_np) {
> > +             dev_err(ds->dev, "no MDIO bus node\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
> > +
> > +     if (!ds->slave_mii_bus)
> > +             return -ENOMEM;
> > +
> > +     ds->slave_mii_bus->priv = ds->priv;
> > +     ds->slave_mii_bus->read = lan937x_sw_mdio_read;
> > +     ds->slave_mii_bus->write = lan937x_sw_mdio_write;
> > +     ds->slave_mii_bus->name = "lan937x slave smi";
> > +     snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d",
> > +              ds->index);
> > +     ds->slave_mii_bus->parent = ds->dev;
> > +     ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
> > +
> > +     ret = of_mdiobus_register(ds->slave_mii_bus, dev->mdio_np);
> > +
> > +     if (ret) {
> > +             dev_err(ds->dev, "unable to register MDIO bus %s\n",
> > +                     ds->slave_mii_bus->id);
> > +             of_node_put(dev->mdio_np);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > 
> > +
> > +static phy_interface_t lan937x_get_interface(struct ksz_device *dev, int
> > port)
> > +{
> > +     phy_interface_t interface;
> > +     u8 data8;
> > +     int rc;
> > +
> > +     if (lan937x_is_internal_phy_port(dev, port))
> > +             return PHY_INTERFACE_MODE_NA;
> 
> I think conventional wisdom is to use PHY_INTERFACE_MODE_INTERNAL for
> internal ports, as the name would suggest.
Sure.

> 
> > 
> > +static void lan937x_config_cpu_port(struct dsa_switch *ds)
> > +{
> > +     struct ksz_device *dev = ds->priv;
> > +     struct ksz_port *p;
> > +     int i;
> > +
> > +     ds->num_ports = dev->port_cnt;
> > +
> > +     for (i = 0; i < dev->port_cnt; i++) {
> > +             if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
> > +                     phy_interface_t interface;
> > +                     const char *prev_msg;
> > +                     const char *prev_mode;
> > +
> > +                     dev->cpu_port = i;
> > +                     dev->host_mask = (1 << dev->cpu_port);
> > +                     dev->port_mask |= dev->host_mask;
> > +                     p = &dev->ports[i];
> > +
> > +                     /* Read from XMII register to determine host port
> > +                      * interface.  If set specifically in device tree
> > +                      * note the difference to help debugging.
> > +                      */
> > +                     interface = lan937x_get_interface(dev, i);
> > +                     if (!p->interface) {
> > +                             if (dev->compat_interface)
> > +                                     p->interface = dev->compat_interface;
> 
> Compatibility with what? This is a new driver.
Should be removed.

> 
> > +                             else
> > +                                     p->interface = interface;
> > +                     }
> > +
> > +                     if (interface && interface != p->interface) {
> > +                             prev_msg = " instead of ";
> > +                             prev_mode = phy_modes(interface);
> > +                     } else {
> > +                             prev_msg = "";
> > +                             prev_mode = "";
> > +                     }
> > +
> > +                     dev_info(dev->dev,
> > +                              "Port%d: using phy mode %s%s%s\n",
> > +                              i,
> > +                              phy_modes(p->interface),
> > +                              prev_msg,
> > +                              prev_mode);
> 
> It's unlikely that anyone is going to be able to find such a composite
> error message string using grep.
Since the compatibility is going to be removed, it is expected to configure from
DTS. I will retain the dev_info with out the string 'instead of' for just to
inform what interface is currently configured.

> 
> > +
> > +                     /* enable cpu port */
> > +                     lan937x_port_setup(dev, i, true);
> > +                     p->vid_member = dev->port_mask;
> > +             }
> > +     }
> > +
> > +     dev->member = dev->host_mask;
> > +
> > +     for (i = 0; i < dev->port_cnt; i++) {
> > +             if (i == dev->cpu_port)
> > +                     continue;
> > +             p = &dev->ports[i];
> > +
> > +             /* Initialize to non-zero so that lan937x_cfg_port_member()
> > will
> > +              * be called.
> > +              */
> > +             p->vid_member = (1 << i);
> > +             p->member = dev->port_mask;
> > +             lan937x_port_stp_state_set(ds, i, BR_STATE_DISABLED);
> > +     }
> > +}
> > +
> > +static int lan937x_setup(struct dsa_switch *ds)
> > +{
> > +     struct ksz_device *dev = ds->priv;
> > +     int rc;
> > +
> > +     dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
> > +                                    dev->num_vlans, GFP_KERNEL);
> > +     if (!dev->vlan_cache)
> > +             return -ENOMEM;
> > +
> > +     rc = lan937x_reset_switch(dev);
> > +     if (rc < 0) {
> > +             dev_err(ds->dev, "failed to reset switch\n");
> > +             return rc;
> > +     }
> > +
> > +     /* Required for port partitioning. */
> > +     lan937x_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
> > +                   true);
> > +
> > +     lan937x_config_cpu_port(ds);
> > +
> > +     ds->configure_vlan_while_not_filtering = true;
> 
> You can delete this line, it's implicitly true now.
Sure.

> 

> > +
> > +     lan937x_enable_spi_indirect_access(dev);
> > +
> > +     /* start switch */
> > +     lan937x_cfg(dev, REG_SW_OPERATION, SW_START, true);
> > +
> > +     ksz_init_mib_timer(dev);
> > +
> > +     return 0;
> > +}
> > +
> > +static int lan937x_change_mtu(struct dsa_switch *ds, int port, int mtu)
> > +{
> > +     struct ksz_device *dev = ds->priv;
> > +     u16 max_size;
> > +     int rc;
> > +
> > +     if (mtu >= FR_MIN_SIZE) {
> > +             rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0,
> > PORT_JUMBO_EN, true);
> > +             max_size = FR_MAX_SIZE;
> > +     } else {
> > +             rc = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0,
> > PORT_JUMBO_EN, false);
> > +             max_size = FR_MIN_SIZE;
> > +     }
> > +
> > +     if (rc < 0) {
> > +             dev_err(ds->dev, "failed to enable jumbo\n");
> > +             return rc;
> > +     }
> > +
> > +     /* Write the frame size in PORT_MAX_FR_SIZE register */
> > +     rc = lan937x_pwrite16(dev, port, PORT_MAX_FR_SIZE, max_size);
> > +
> > +     if (rc < 0) {
> > +             dev_err(ds->dev, "failed to change the mtu\n");
> > +             return rc;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int lan937x_get_max_mtu(struct dsa_switch *ds, int port)
> > +{
> > +     /* Frame size is 9000 (= 0x2328) if
> > +      * jumbo frame support is enabled, PORT_JUMBO_EN bit will be enabled
> > +      * based on mtu in lan937x_change_mtu() API
> > +      */
> > +     return FR_MAX_SIZE;
> 
> Frame size is one thing. But MTU is L2 payload, which excludes MAC DA,
> MAC SA, EtherType and VLAN ID. So does the switch really accept a packet
> with an L2 payload length of 9000 bytes and a VLAN tag?
(FR_MAX_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN) is supposed to be returned and i
will add the test case from my side. will make the changes in next rev.

> 
> > +}
> > +
> > +static int   lan937x_port_pre_bridge_flags(struct dsa_switch *ds, int port,
> > +                                           struct switchdev_brport_flags
> > flags,
> > +                                      struct netlink_ext_ack *extack)
> > +{
> > +     return -EOPNOTSUPP;
> > +}
> > +
> > +static int   lan937x_port_bridge_flags(struct dsa_switch *ds, int port,
> > +                                       struct switchdev_brport_flags flags,
> > +                                      struct netlink_ext_ack *extack)
> > +{
> > +     return -EOPNOTSUPP;
> > +}
> 
> This shouldn't have been implemented but is necessary due to a bug, see
> commit 70a7c484c7c3 ("net: dsa: fix bridge support for drivers without
> port_bridge_flags callback").
Thanks for pointing out the commit ID. I will remove it.

> 
> > +
> > +const struct dsa_switch_ops lan937x_switch_ops = {
> > +     .get_tag_protocol       = lan937x_get_tag_protocol,
> > +     .setup                  = lan937x_setup,
> > +     .phy_read               = lan937x_phy_read16,
> > +     .phy_write              = lan937x_phy_write16,
> > +     .port_enable            = ksz_enable_port,
> > +     .port_bridge_join       = ksz_port_bridge_join,
> > +     .port_bridge_leave      = ksz_port_bridge_leave,
> > +     .port_pre_bridge_flags  = lan937x_port_pre_bridge_flags,
> > +     .port_bridge_flags      = lan937x_port_bridge_flags,
> > +     .port_stp_state_set     = lan937x_port_stp_state_set,
> > +     .port_fast_age          = ksz_port_fast_age,
> > +     .port_max_mtu           = lan937x_get_max_mtu,
> > +     .port_change_mtu        = lan937x_change_mtu,
> > +};
> > +
> > +int lan937x_switch_register(struct ksz_device *dev)
> > +{
> > +     int ret;
> > +
> > +     ret = ksz_switch_register(dev, &lan937x_dev_ops);
> > +
> > +     if (ret) {
> > +             if (dev->mdio_np) {
> > +                     mdiobus_unregister(dev->ds->slave_mii_bus);
> 
> I don't see any mdiobus_unregister when the driver is removed?
Oops, i will add unregister in ".remove" as well.

> 
> > +                     of_node_put(dev->mdio_np);
> 
> Also, why keep mdio_np inside ksz_device, therefore for the entire
> lifetime of the driver? Why do you need it? 
ksz_switch_register common function is used here. Inside ksz_switch_register
function, mdio is being registered through init(). After the mdio bus
registration, there is dsa_register_switch() in ksz common layer. Hence made
that as part of ksz_device to unregister the mdio if there were any failures and
unregistered along with of_node_put. Do you think this is a bad approach?


> Not to mention you don't
> appear to be ever calling of_node_put on unbind, unless I'm missing
> something.
of_node_put is called as above. And then the same is called, if
of_mdiobus_register() is failed in lan937x_dev.c. Then the next action is to
call during driver removal. Did i miss any other place?


> 
> > +             }
> > +     }
> > +
> > +     return ret;
> > +}
> > +EXPORT_SYMBOL(lan937x_switch_register);
> > +
> > +MODULE_AUTHOR("Prasanna Vengateshan Varadharajan < 
> > Prasanna.Vengateshan@microchip.com>");
> > +MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/net/dsa/microchip/lan937x_spi.c
> > b/drivers/net/dsa/microchip/lan937x_spi.c
> > new file mode 100644
> > index 000000000000..d9731d6afb96
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/lan937x_spi.c
> > @@ -0,0 +1,226 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Microchip LAN937X switch driver register access through SPI
> > + * Copyright (C) 2019-2021 Microchip Technology Inc.
> > + */
> > +#include <asm/unaligned.h>
> 
> Why do you need this?
> 
> > +
> > +#include <linux/delay.h>
> 
> Or this?
Will remove both of them

> 
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/of_device.h>
> > +
> > +#include "ksz_common.h"
> > +
> > +#define SPI_ADDR_SHIFT                               24
> > +#define SPI_ADDR_ALIGN                               3
> > +#define SPI_TURNAROUND_SHIFT         5
> > +
> > +KSZ_REGMAP_TABLE(lan937x, 32, SPI_ADDR_SHIFT,
> > +              SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
> > +
> > +struct lan937x_chip_data {
> > +     u32 chip_id;
> > +     const char *dev_name;
> > +     int num_vlans;
> > +     int num_alus;
> > +     int num_statics;
> > +     int cpu_ports;
> > +     int port_cnt;
> > +};
> > +
> > +static const struct of_device_id lan937x_dt_ids[];
> > +
> > +static const struct lan937x_chip_data lan937x_switch_chips[] = {
> > +     {
> > +             .chip_id = 0x00937010,
> > +             .dev_name = "LAN9370",
> > +             .num_vlans = 4096,
> > +             .num_alus = 1024,
> > +             .num_statics = 256,
> > +             /* can be configured as cpu port */
> > +             .cpu_ports = 0x10,
> > +             /* total port count */
> > +             .port_cnt = 5,
> > +     },
> > +     {
> > +             .chip_id = 0x00937110,
> > +             .dev_name = "LAN9371",
> > +             .num_vlans = 4096,
> > +             .num_alus = 1024,
> > +             .num_statics = 256,
> > +             /* can be configured as cpu port */
> > +             .cpu_ports = 0x30,
> > +             /* total port count */
> > +             .port_cnt = 6,
> > +     },
> > +     {
> > +             .chip_id = 0x00937210,
> > +             .dev_name = "LAN9372",
> > +             .num_vlans = 4096,
> > +             .num_alus = 1024,
> > +             .num_statics = 256,
> > +             /* can be configured as cpu port */
> > +             .cpu_ports = 0x30,
> > +             /* total port count */
> > +             .port_cnt = 8,
> > +     },
> > +     {
> > +             .chip_id = 0x00937310,
> > +             .dev_name = "LAN9373",
> > +             .num_vlans = 4096,
> > +             .num_alus = 1024,
> > +             .num_statics = 256,
> > +             /* can be configured as cpu port */
> > +             .cpu_ports = 0x38,
> > +             /* total port count */
> > +             .port_cnt = 5,
> > +     },
> > +     {
> > +             .chip_id = 0x00937410,
> > +             .dev_name = "LAN9374",
> > +             .num_vlans = 4096,
> > +             .num_alus = 1024,
> > +             .num_statics = 256,
> > +             /* can be configured as cpu port */
> > +             .cpu_ports = 0x30,
> > +             /* total port count */
> > +             .port_cnt = 8,
> > +     },
> > +
> 
> The new line shouldn't be here?
Noted, will remove it.

> 
> > +};
> > +



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations
  2021-04-22 19:03   ` Vladimir Oltean
@ 2021-05-06 14:51     ` Prasanna Vengateshan
  0 siblings, 0 replies; 31+ messages in thread
From: Prasanna Vengateshan @ 2021-05-06 14:51 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: andrew, netdev, robh+dt, UNGLinuxDriver, hkallweit1, linux,
	davem, kuba, linux-kernel, vivien.didelot, f.fainelli,
	devicetree

Hi Vladimir,

On Thu, 2021-04-22 at 22:03 +0300, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the
> content is safe
> 
> On Thu, Apr 22, 2021 at 03:12:57PM +0530, Prasanna Vengateshan wrote:
> > Support for VLAN add, del, prepare and filtering operations.
> > 
> > It aligns with latest update of removing switchdev
> > transactional logic from VLAN objects
> 
> Maybe more in the commit message about what the patch does, as opposed
> to mentioning that you had to rebase it, would be helpful.
Sure.

> 
> > 
> > Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@microchip.com>
> > ---
> >  drivers/net/dsa/microchip/lan937x_main.c | 214 +++++++++++++++++++++++
> >  1 file changed, 214 insertions(+)
> > 
> > diff --git a/drivers/net/dsa/microchip/lan937x_main.c
> > b/drivers/net/dsa/microchip/lan937x_main.c
> > index 7f6183dc0e31..35f3456c3506 100644
> 
> > +
> > +             rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE,
> > true);
> 
> How about this bit?

> 
> I see one bit is per port and the other is global.
> Just FYI, you can have this configuration:
> 
> ip link add br0 type bridge vlan_filtering 0
> ip link add br1 type bridge vlan_filtering 1
> ip link set swp0 master br0
> ip link set swp1 master br0
> ip link set swp2 master br1
> ip link set swp3 master br1
> 
> Do the swp0 and swp1 ports remain VLAN-unaware after you touch this
> REG_SW_LUE_CTRL_0 bit?
vlan aware is global, so ds->vlan_filtering_is_global needs to be true if VLAN
aware is global, will fix this in the next version

> 
> > +     } else {
> > +             rc = lan937x_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE,
> > false);
> > +             if (rc < 0)
> > +                     return rc;
> > +
> > +             rc = lan937x_port_cfg(dev, port, REG_PORT_LUE_CTRL,
> > +                                   PORT_VLAN_LOOKUP_VID_0, false);
> > +     }
> > +
> > +     return rc;
> > +}
> > +
> > +static int lan937x_port_vlan_add(struct dsa_switch *ds, int port,
> > +                              const struct switchdev_obj_port_vlan *vlan,
> > +                              struct netlink_ext_ack *extack)
> > +{
> > +     bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
> > +     struct ksz_device *dev = ds->priv;
> > +     u32 vlan_table[3];
> 
> Maybe a structure would be nicer to read than an u32 array?
Okay, will make a structure.

> 
> > +     int rc;
> > +
> > +     rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
> > +     if (rc < 0) {
> > +             dev_err(dev->dev, "Failed to get vlan table\n");
> 
> One of the reasons for which the extack exists is so that you can report
> errors to user space and not to the console.
Sure, will add it for port_vlan_del() as well

> 
>                 NL_SET_ERR_MSG_MOD(extack, "Failed to get vlan table");
> 
> > +             return rc;
> > +     }
> > +
> > +     vlan_table[0] = VLAN_VALID | (vlan->vid & VLAN_FID_M);
> > +
> > +     /* set/clear switch port when updating vlan table registers */
> > +     if (untagged)
> > +             vlan_table[1] |= BIT(port);
> > +     else
> > +             vlan_table[1] &= ~BIT(port);
> > +     vlan_table[1] &= ~(BIT(dev->cpu_port));
> > +
> > +     vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
> 
> What's the business with the CPU port here? Does DSA not call
> .port_vlan_add for the CPU port separately?
Calls for CPU port as well. This is to be removed.

> 
> > +
> > +     rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
> > +     if (rc < 0) {
> > +             dev_err(dev->dev, "Failed to set vlan table\n");
> > +             return rc;
> > +     }
> > +
> > +     /* change PVID */
> > +     if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
> > +             rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vlan-
> > >vid);
> > +
> > +             if (rc < 0) {
> > +                     dev_err(dev->dev, "Failed to set pvid\n");
> > +                     return rc;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int lan937x_port_vlan_del(struct dsa_switch *ds, int port,
> > +                              const struct switchdev_obj_port_vlan *vlan)
> > +{
> > +     bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
> > +     struct ksz_device *dev = ds->priv;
> > +     u32 vlan_table[3];
> > +     u16 pvid;
> > +     int rc;
> > +
> > +     lan937x_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
> > +     pvid &= 0xFFF;
> > +
> > +     rc = lan937x_get_vlan_table(dev, vlan->vid, vlan_table);
> > +
> > +     if (rc < 0) {
> > +             dev_err(dev->dev, "Failed to get vlan table\n");
> > +             return rc;
> > +     }
> > +     /* clear switch port number */
> > +     vlan_table[2] &= ~BIT(port);
> > +
> > +     if (pvid == vlan->vid)
> > +             pvid = 1;
> 
> According to Documentation/networking/switchdev.rst:
> 
> When the bridge has VLAN filtering enabled and a PVID is not configured on the
> ingress port, untagged and 802.1p tagged packets must be dropped. When the
> bridge
> has VLAN filtering enabled and a PVID exists on the ingress port, untagged and
> priority-tagged packets must be accepted and forwarded according to the
> bridge's port membership of the PVID VLAN. When the bridge has VLAN filtering
> disabled, the presence/lack of a PVID should not influence the packet
> forwarding decision.
> 
> So please don't reset the pvid.
Will remove it in the next rev.

> 
> > +
> > +     if (untagged)
> > +             vlan_table[1] &= ~BIT(port);
> > +
> > +     rc = lan937x_set_vlan_table(dev, vlan->vid, vlan_table);
> > +     if (rc < 0) {
> > +             dev_err(dev->dev, "Failed to set vlan table\n");
> > +             return rc;
> > +     }
> > +
> > +     rc = lan937x_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
> > +
> > +     if (rc < 0) {
> > +             dev_err(dev->dev, "Failed to set pvid\n");
> > +             return rc;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> >  static u8 lan937x_get_fid(u16 vid)
> >  {
> >       if (vid > ALU_FID_SIZE)
> > @@ -955,6 +1166,9 @@ const struct dsa_switch_ops lan937x_switch_ops = {
> >       .port_bridge_flags      = lan937x_port_bridge_flags,
> >       .port_stp_state_set     = lan937x_port_stp_state_set,
> >       .port_fast_age          = ksz_port_fast_age,
> > +     .port_vlan_filtering    = lan937x_port_vlan_filtering,
> > +     .port_vlan_add          = lan937x_port_vlan_add,
> > +     .port_vlan_del          = lan937x_port_vlan_del,
> >       .port_fdb_dump          = lan937x_port_fdb_dump,
> >       .port_fdb_add           = lan937x_port_fdb_add,
> >       .port_fdb_del           = lan937x_port_fdb_del,
> > --
> > 2.27.0
> > 



^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2021-05-06 14:51 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-22  9:42 [PATCH v2 net-next 0/9] net: dsa: microchip: DSA driver support for LAN937x switch Prasanna Vengateshan
2021-04-22  9:42 ` [PATCH v2 net-next 1/9] dt-bindings: net: dsa: dt bindings for microchip lan937x Prasanna Vengateshan
2021-04-22 15:30   ` Rob Herring
2021-04-22 17:38   ` Rob Herring
2021-04-26  4:05     ` Prasanna Vengateshan
2021-04-26 16:04       ` Florian Fainelli
2021-04-22  9:42 ` [PATCH v2 net-next 2/9] net: phy: Add support for LAN937x T1 phy driver Prasanna Vengateshan
2021-04-22 12:45   ` Andrew Lunn
2021-04-26  4:06     ` Prasanna Vengateshan
2021-04-22  9:42 ` [PATCH v2 net-next 3/9] net: dsa: tag_ksz: add tag handling for Microchip LAN937x Prasanna Vengateshan
2021-04-22 19:18   ` Vladimir Oltean
2021-04-26  4:33     ` Prasanna Vengateshan
2021-04-26 12:27       ` Andrew Lunn
2021-04-22  9:42 ` [PATCH v2 net-next 4/9] net: dsa: microchip: add DSA support for microchip lan937x Prasanna Vengateshan
2021-04-22 19:59   ` Vladimir Oltean
2021-04-22 23:28     ` Andrew Lunn
2021-04-26  8:54       ` Prasanna Vengateshan
2021-04-26 12:34         ` Andrew Lunn
2021-04-27  7:43     ` Prasanna Vengateshan
2021-04-22 23:32   ` Andrew Lunn
2021-04-22 23:38   ` Andrew Lunn
2021-04-22 23:43   ` Andrew Lunn
2021-04-22  9:42 ` [PATCH v2 net-next 5/9] net: dsa: microchip: add support for phylink management Prasanna Vengateshan
2021-04-22 20:05   ` Vladimir Oltean
2021-04-22  9:42 ` [PATCH v2 net-next 6/9] net: dsa: microchip: add support for ethtool port counters Prasanna Vengateshan
2021-04-22  9:42 ` [PATCH v2 net-next 7/9] net: dsa: microchip: add support for port mirror operations Prasanna Vengateshan
2021-04-22 20:11   ` Vladimir Oltean
2021-04-22  9:42 ` [PATCH v2 net-next 8/9] net: dsa: microchip: add support for fdb and mdb management Prasanna Vengateshan
2021-04-22  9:42 ` [PATCH v2 net-next 9/9] net: dsa: microchip: add support for vlan operations Prasanna Vengateshan
2021-04-22 19:03   ` Vladimir Oltean
2021-05-06 14:51     ` Prasanna Vengateshan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).