[PATCH net-next 3/8] net: dsa: microchip: add DSA support for microchip lan937x
From: Prasanna Vengateshan
Date: Thu Jan 28 2021 - 01:48:06 EST
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.
Signed-off-by: Prasanna Vengateshan <prasanna.vengateshan@xxxxxxxxxxxxx>
---
drivers/net/dsa/microchip/Kconfig | 12 +
drivers/net/dsa/microchip/Makefile | 5 +
drivers/net/dsa/microchip/ksz_common.h | 1 +
drivers/net/dsa/microchip/lan937x_dev.c | 859 ++++++++++++++++++++
drivers/net/dsa/microchip/lan937x_dev.h | 79 ++
drivers/net/dsa/microchip/lan937x_main.c | 352 +++++++++
drivers/net/dsa/microchip/lan937x_reg.h | 955 +++++++++++++++++++++++
drivers/net/dsa/microchip/lan937x_spi.c | 104 +++
8 files changed, 2367 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..7cc14b72ad39 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -144,6 +144,7 @@ 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);
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..84540180ff2f
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip lan937x dev ops functions
+ * Copyright (C) 2019-2020 Microchip Technology Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iopoll.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" },
+};
+
+static const struct lan937x_chip_data lan937x_switch_chips[] = {
+ {
+ .chip_id = 0x00937000,
+ .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 = 0x00937100,
+ .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 = 0x00937200,
+ .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 = 0x00937300,
+ .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 = 0x00937400,
+ .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,
+ },
+
+};
+
+void lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+ regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
+}
+
+void lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
+ u8 bits, bool set)
+{
+ regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset),
+ bits, set ? bits : 0);
+}
+
+void lan937x_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
+{
+ regmap_update_bits(dev->regmap[2], addr, bits, set ? bits : 0);
+}
+
+void lan937x_pread8(struct ksz_device *dev, int port, int offset,
+ u8 *data)
+{
+ ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_pread16(struct ksz_device *dev, int port, int offset,
+ u16 *data)
+{
+ ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_pread32(struct ksz_device *dev, int port, int offset,
+ u32 *data)
+{
+ ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_pwrite8(struct ksz_device *dev, int port,
+ int offset, u8 data)
+{
+ ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_pwrite16(struct ksz_device *dev, int port,
+ int offset, u16 data)
+{
+ ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_pwrite32(struct ksz_device *dev, int port,
+ int offset, u32 data)
+{
+ ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
+}
+
+void lan937x_port_cfg32(struct ksz_device *dev, int port, int offset,
+ u32 bits, bool set)
+{
+ 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_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;
+
+ /* reset switch */
+ lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+
+ /* default configuration */
+ ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+ data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
+ SW_SRC_ADDR_FILTER;
+ ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+
+ /* disable interrupts */
+ ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+ ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF);
+ ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+
+ /* set broadcast storm protection 10% rate */
+ regmap_update_bits(dev->regmap[1], REG_SW_MAC_CTRL_2,
+ BROADCAST_STORM_RATE,
+ (BROADCAST_STORM_VALUE *
+ BROADCAST_STORM_PROT_RATE) / 100);
+
+ return 0;
+}
+
+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);
+}
+
+void lan937x_enable_spi_indirect_access(struct ksz_device *dev)
+{
+ u16 data16;
+ u8 data8;
+
+ ksz_read8(dev, REG_GLOBAL_CTRL_0, &data8);
+
+ /* Check if PHY register is blocked */
+ if (data8 & SW_PHY_REG_BLOCK) {
+ /* Enable Phy access through SPI*/
+ data8 &= ~SW_PHY_REG_BLOCK;
+ ksz_write8(dev, REG_GLOBAL_CTRL_0, data8);
+ }
+
+ ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16);
+
+ /* If already the access is not enabled go ahead and allow SPI access */
+ if (!(data16 & VPHY_SPI_INDIRECT_ENABLE)) {
+ data16 |= VPHY_SPI_INDIRECT_ENABLE;
+ ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16);
+ }
+}
+
+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_tx_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_tx_phy_port(dev, port))
+ return true;
+
+ return false;
+}
+
+int lan937x_t1_tx_phy_write(struct ksz_device *dev, int addr,
+ int reg, u16 val)
+{
+ u16 temp, addr_base;
+ unsigned int value;
+ int ret;
+
+ /* Check for internal phy port */
+ if (!lan937x_is_internal_phy_port(dev, addr))
+ return 0;
+
+ if (lan937x_is_internal_tx_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)));
+
+ ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+
+ /* Write the data to be written to the VPHY reg */
+ ksz_write16(dev, REG_VPHY_IND_DATA__2, val);
+
+ /* Write the Write En and Busy bit */
+ ksz_write16(dev, REG_VPHY_IND_CTRL__2, (VPHY_IND_WRITE
+ | VPHY_IND_BUSY));
+
+ ret = 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 (ret) {
+ dev_err(dev->dev, "Failed to write phy register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int lan937x_t1_tx_phy_read(struct ksz_device *dev, int addr,
+ int reg, u16 *val)
+{
+ u16 temp, addr_base;
+ unsigned int value;
+ int ret;
+
+ if (lan937x_is_internal_phy_port(dev, addr)) {
+ if (lan937x_is_internal_tx_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)));
+
+ ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp);
+ /* Write Read and Busy bit to start the transaction*/
+ ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY);
+
+ ret = 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 (ret) {
+ dev_err(dev->dev, "Failed to read phy register\n");
+ return ret;
+ }
+ /* Read the VPHY register which has the PHY data*/
+ ksz_read16(dev, REG_VPHY_IND_DATA__2, val);
+ }
+
+ return 0;
+}
+
+static void lan937x_t1_tx_phy_mod_bits(struct ksz_device *dev, int port,
+ int reg, u16 val, bool set)
+{
+ u16 data;
+
+ /* read phy register */
+ lan937x_t1_tx_phy_read(dev, port, reg, &data);
+
+ /* set/clear the data */
+ if (set)
+ data |= val;
+ else
+ data &= ~val;
+
+ /* write phy register */
+ lan937x_t1_tx_phy_write(dev, port, reg, data);
+}
+
+static u32 lan937x_tx_phy_bank_read(struct ksz_device *dev, int port,
+ u8 bank, u8 reg)
+{
+ u16 data_hi;
+ u16 data_lo;
+ u16 ctrl;
+
+ ctrl = ((u16)bank & TX_REG_BANK_SEL_M) << TX_REG_BANK_SEL_S;
+ ctrl |= ((u16)reg & TX_READ_ADDR_M) << TX_READ_ADDR_S;
+
+ /* write ctrl register with appropriate value */
+ ctrl |= TX_IND_DATA_READ;
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, ctrl);
+
+ /* if bank is WOL value to be written again to reflect correct bank */
+ if (bank == TX_REG_BANK_SEL_WOL)
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, ctrl);
+
+ /* read data hi & low value */
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_TX_READ_DATA_LO, &data_lo);
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_TX_READ_DATA_HI, &data_hi);
+
+ return ((u32)data_hi << 16) | data_lo;
+}
+
+static void lan937x_tx_phy_bank_write(struct ksz_device *dev, int port,
+ u8 bank, u8 reg, u16 val)
+{
+ u16 ctrl;
+
+ /* write the value */
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_WRITE_DATA, val);
+ ctrl = ((u16)bank & TX_REG_BANK_SEL_M) << TX_REG_BANK_SEL_S;
+ ctrl |= (reg & TX_WRITE_ADDR_M);
+
+ if (bank == TX_REG_BANK_SEL_DSP || bank == TX_REG_BANK_SEL_BIST)
+ ctrl |= TX_TEST_MODE;
+ /* write ctrl register with write operation bit set */
+ ctrl |= TX_IND_DATA_WRITE;
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, ctrl);
+}
+
+static void tx_phy_setup(struct ksz_device *dev, int port)
+{
+ u16 data_lo;
+
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_TX_SPECIAL_MODES, &data_lo);
+ /* Need to change configuration from 6 to other value. */
+ data_lo &= TX_PHYADDR_M;
+
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_SPECIAL_MODES, data_lo);
+
+ /* Need to toggle test_mode bit to enable DSP access. */
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, TX_TEST_MODE);
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, 0);
+
+ /* Note TX_TEST_MODE is then always enabled so this is not required. */
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, TX_TEST_MODE);
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_TX_IND_CTRL, 0);
+}
+
+static void tx_phy_port_init(struct ksz_device *dev, int port)
+{
+ u32 data;
+
+ /* Software reset. */
+ lan937x_t1_tx_phy_mod_bits(dev, port, MII_BMCR, BMCR_RESET, true);
+
+ /* tx phy setup */
+ tx_phy_setup(dev, port);
+
+ /* tx phy init sequence */
+ data = lan937x_tx_phy_bank_read(dev, port, TX_REG_BANK_SEL_VMDAC,
+ TX_VMDAC_ZQ_CAL_CTRL);
+ data |= TX_START_ZQ_CAL;
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_VMDAC,
+ TX_VMDAC_ZQ_CAL_CTRL, data);
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_VMDAC, TX_VMDAC_CTRL0,
+ TX_VMDAC_CTRL0_VAL);
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_VMDAC, TX_VMDAC_CTRL1,
+ TX_VMDAC_CTRL1_VAL);
+ data = lan937x_tx_phy_bank_read(dev, port, TX_REG_BANK_SEL_VMDAC,
+ TX_VMDAC_MISC_PCS_CTRL0);
+ data |= TX_MISC_PCS_CTRL0_13;
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_VMDAC,
+ TX_VMDAC_MISC_PCS_CTRL0, data);
+
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_DSP, TX_DSP_DCBLW,
+ TX_DSP_DCBLW_VAL);
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_DSP, TX_DSP_A11_CONFIG,
+ TX_DSP_A11_CONFIG_VAL);
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_DSP, TX_DSP_A10_CONFIG,
+ TX_DSP_A10_CONFIG_VAL);
+ data = lan937x_tx_phy_bank_read(dev, port, TX_REG_BANK_SEL_DSP,
+ TX_DSP_A5_CONFIG);
+ data &= ~(TX_A5_TXCLKPHSEL_M << TX_A5_TXCLKPHSEL_S);
+ data |= (TX_A5_TXCLK_2_NS << TX_A5_TXCLKPHSEL_S);
+ lan937x_tx_phy_bank_write(dev, port, TX_REG_BANK_SEL_VMDAC,
+ TX_DSP_A5_CONFIG, data);
+}
+
+static void lan937x_t1_phy_bank_sel(struct ksz_device *dev, int port,
+ u8 bank, u8 addr, u16 oper)
+{
+ u16 data, ctrl;
+ u8 prev_bank;
+
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_T1_EXT_REG_CTRL, &ctrl);
+ prev_bank = (ctrl >> T1_REG_BANK_SEL_S) & T1_REG_BANK_SEL_M;
+ ctrl &= T1_PCS_STS_CNT_RESET;
+
+ data = ((u16)bank & T1_REG_BANK_SEL_M) << T1_REG_BANK_SEL_S;
+ data |= (addr & T1_REG_ADDR_M);
+ data |= oper;
+ data |= ctrl;
+
+ /* if the bank is DSP need to write twice */
+ if (bank != prev_bank && bank == T1_REG_BANK_SEL_DSP) {
+ u16 t = data & ~T1_REG_ADDR_M;
+
+ t &= ~oper;
+ t |= T1_IND_DATA_READ;
+
+ /* Need to write twice to access correct register. */
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_T1_EXT_REG_CTRL, t);
+ }
+
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_T1_EXT_REG_CTRL, data);
+}
+
+static void lan937x_t1_phy_bank_read(struct ksz_device *dev, int port,
+ u8 bank, u8 addr, u16 *val)
+{
+ /* select the bank for read operation */
+ lan937x_t1_phy_bank_sel(dev, port, bank, addr, T1_IND_DATA_READ);
+
+ /* read bank */
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_T1_EXT_REG_RD_DATA, val);
+}
+
+static void lan937x_t1_phy_bank_write(struct ksz_device *dev, int port,
+ u8 bank, u8 addr, u16 val)
+{
+ /* write the data to be written into the bank */
+ lan937x_t1_tx_phy_write(dev, port, REG_PORT_T1_EXT_REG_WR_DATA, val);
+ /* select the bank for write operation */
+ lan937x_t1_phy_bank_sel(dev, port, bank, addr, T1_IND_DATA_WRITE);
+}
+
+static void t1_phy_port_init(struct ksz_device *dev, int port)
+{
+ u16 val;
+
+ /* Power down the PHY. */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_PHY_BASIC_CTRL,
+ PORT_T1_POWER_DOWN, true);
+
+ /* Make sure software initialization sequence is used. */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_POWER_DOWN_CTRL,
+ T1_HW_INIT_SEQ_ENABLE, false);
+
+ /* Configure T1 phy role */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_PHY_M_CTRL,
+ PORT_T1_M_CFG, true);
+
+ /* Software reset. */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_PHY_BASIC_CTRL,
+ PORT_T1_PHY_RESET, true);
+
+ /* cdr mode */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x34, 0x0001);
+
+ /* setting lock 3 mufac */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x1B, 0x0B6A);
+
+ /* setting pos lock mufac */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x1C, 0x0B6B);
+
+ /* setting lock1 win config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x11, 0x2A74);
+
+ /* setting lock2 win config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x12, 0x2B70);
+
+ /* setting lock3 win config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x13, 0x2B6C);
+
+ /* setting plock */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x14, 0x2974);
+
+ /* setting lock threshold config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x16, 0xC803);
+
+ /* slv fd stg bmp */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x24, 0x0002);
+
+ /* Blw BW config lock stage 3 */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x2A, 0x003C);
+
+ /* Blw BW config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x56, 0x3CAA);
+
+ /* Blw BW config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x57, 0x1E47);
+
+ /* Blw BW config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x58, 0x1E4E);
+
+ /* Blw BW config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x59, 0x1E56);
+
+ /* disable scrambler lock timeout 0-disable 1- enable */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x32, 0x00F6);
+
+ /* reducing energy detect partial timeout */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x3C, 0x64CC);
+
+ lan937x_t1_tx_phy_read(dev, port, 0x0A, &val);
+
+ if ((val & 0x4000) == 0)
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_PCS, 0x26, 0x1770);
+
+ /* pwr dn Config */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x04, 0x16D7);
+
+ /* scrambler lock hysterisis */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_PCS, 0x00, 0x7FFF);
+
+ /* eq status timer control */
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_PCS, 0x02, 0x07FF);
+
+ /* MANUAL STD POLARITY */
+ lan937x_t1_tx_phy_write(dev, port, 0x17, 0x0080);
+
+ /* disable master mode energy detect */
+ lan937x_t1_tx_phy_mod_bits(dev, port, 0x10, 0x0040, false);
+
+ lan937x_t1_phy_bank_read(dev, port, T1_REG_BANK_SEL_AFE, 0x0B, &val);
+
+ val &= ~0x001E;
+ /* increase tx amp to 0b0101 */
+ val |= 0x000A;
+
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_AFE, 0x0B, val);
+
+ lan937x_t1_phy_bank_write(dev, port, T1_REG_BANK_SEL_DSP, 0x25, 0x23E0);
+
+ /* Set HW_INIT */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_POWER_DOWN_CTRL,
+ T1_HW_INIT_SEQ_ENABLE, true);
+
+ /* Power up the PHY. */
+ lan937x_t1_tx_phy_mod_bits(dev, port, REG_PORT_T1_PHY_BASIC_CTRL,
+ PORT_T1_POWER_DOWN, false);
+}
+
+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);
+
+ /* phy init for internal ports */
+ if (lan937x_is_internal_phy_port(dev, port)) {
+ if (lan937x_is_internal_tx_phy_port(dev, port))
+ tx_phy_port_init(dev, port);
+ else
+ t1_phy_port_init(dev, port);
+
+ } else {
+ /* 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_switch_init(struct ksz_device *dev)
+{
+ int i;
+
+ dev->ds->ops = &lan937x_switch_ops;
+
+ 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;
+
+ 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;
+}
+
+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,
+ .port_init_cnt = lan937x_port_init_cnt,
+ .shutdown = lan937x_reset_switch,
+ .detect = lan937x_switch_detect,
+ .init = lan937x_switch_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..e78483793642
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_dev.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip lan937x dev ops headers
+ * Copyright (C) 2019-2020 Microchip Technology Inc.
+ */
+
+#ifndef __LAN937X_CFG_H
+#define __LAN937X_CFG_H
+
+void lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set);
+void lan937x_port_cfg(struct ksz_device *dev, int port, int offset,
+ u8 bits, bool set);
+void lan937x_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set);
+void lan937x_pread8(struct ksz_device *dev, int port, int offset,
+ u8 *data);
+void lan937x_pread16(struct ksz_device *dev, int port, int offset,
+ u16 *data);
+void lan937x_pread32(struct ksz_device *dev, int port, int offset,
+ u32 *data);
+void lan937x_pwrite8(struct ksz_device *dev, int port,
+ int offset, u8 data);
+void lan937x_pwrite16(struct ksz_device *dev, int port,
+ int offset, u16 data);
+void lan937x_pwrite32(struct ksz_device *dev, int port,
+ int offset, u32 data);
+void lan937x_port_cfg32(struct ksz_device *dev, int port, int offset,
+ u32 bits, bool set);
+int lan937x_t1_tx_phy_write(struct ksz_device *dev, int addr,
+ int reg, u16 val);
+int lan937x_t1_tx_phy_read(struct ksz_device *dev, int addr,
+ int reg, u16 *val);
+bool lan937x_is_internal_tx_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_sw_register(struct ksz_device *dev);
+void lan937x_enable_spi_indirect_access(struct ksz_device *dev);
+
+struct mib_names {
+ int index;
+ char string[ETH_GSTRING_LEN];
+};
+
+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;
+};
+
+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..41f7f5f8f435
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver main logic
+ * Copyright (C) 2019-2020 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_get_link_status(struct ksz_device *dev, int port)
+{
+ u16 val1, val2;
+
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_T1_PHY_M_STATUS,
+ &val1);
+
+ lan937x_t1_tx_phy_read(dev, port, REG_PORT_T1_MODE_STAT, &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))
+ return PHY_LINK_UP;
+
+ return PHY_LINK_DOWN;
+}
+
+static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 val;
+
+ lan937x_t1_tx_phy_read(dev, addr, reg, &val);
+
+ if (reg == MII_BMSR && lan937x_is_internal_t1_phy_port(dev, addr)) {
+ /* T1 PHY supports only 100 Mb FD, report through BMSR_100FULL bit*/
+ val |= BMSR_100FULL;
+
+ /* T1 Phy link is based on REG_PORT_T1_PHY_M_STATUS & REG_PORT_T1
+ * _MODE_STAT registers for LAN937x, get the link status
+ * and report through BMSR_LSTATUS bit
+ */
+ if (lan937x_get_link_status(dev, addr) == PHY_LINK_UP)
+ val |= BMSR_LSTATUS;
+ else
+ val &= ~BMSR_LSTATUS;
+ }
+
+ 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_t1_tx_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;
+ if (port != dev->cpu_port)
+ member = 0;
+ break;
+ case BR_STATE_LISTENING:
+ data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+ if (port != dev->cpu_port &&
+ 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);
+
+ /* This function is also used internally. */
+ if (port == dev->cpu_port)
+ break;
+
+ 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 (port != dev->cpu_port &&
+ 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;
+
+ if (lan937x_is_internal_phy_port(dev, port))
+ return PHY_INTERFACE_MODE_NA;
+
+ /* read interface from REG_PORT_XMII_CTRL_1 register */
+ lan937x_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+
+ 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) {
+ dev_warn(dev->dev,
+ "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. Please update your device tree.\n",
+ i);
+ 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 ret = 0;
+
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+
+ ret = lan937x_reset_switch(dev);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+ return ret;
+ }
+
+ /* 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);
+
+ /* enable Indirect Access from SPI to the VPHY registers */
+ 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;
+
+ if (mtu >= FR_MIN_SIZE) {
+ lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, true);
+ max_size = FR_MAX_SIZE;
+ } else {
+ lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, PORT_JUMBO_EN, false);
+ max_size = FR_MIN_SIZE;
+ }
+ /* Write the frame size in PORT_MAX_FR_SIZE register */
+ lan937x_pwrite16(dev, port, PORT_MAX_FR_SIZE, max_size);
+ 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;
+}
+
+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_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)
+{
+ return ksz_switch_register(dev, &lan937x_dev_ops);
+}
+EXPORT_SYMBOL(lan937x_switch_register);
+
+MODULE_AUTHOR("Prasanna Vengateshan Varadharajan <Prasanna.Vengateshan@xxxxxxxxxxxxx>");
+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..d899a43f4d8e
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_reg.h
@@ -0,0 +1,955 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip LAN937X switch register definitions
+ * Copyright (C) 2019-2020 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_ENABLE_REFCLKO BIT(1)
+#define SW_REFCLKO_IS_125MHZ 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_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_PHY_M_STATUS 0x0A
+
+#define PORT_T1_LOCAL_M BIT(14)
+#define PORT_T1_LOCAL_RX_OK BIT(13)
+#define PORT_T1_REMOTE_RX_OK BIT(12)
+#define PORT_T1_IDLE_ERR_CNT_M 0xFF
+
+#define REG_PORT_T1_MDIO_CTRL_1 0x0F
+
+#define T1_MDIO_WAKE_OUT_PIN_REQ BIT(0)
+
+#define REG_PORT_T1_MDIO_CTRL_2 0x10
+
+#define T1_MDIO_MDPREBP BIT(15)
+#define T1_MDIO_PHYADBP BIT(14)
+#define T1_MDIO_WAKE_REQ BIT(13)
+#define T1_MDIO_SLEEP_REQ BIT(12)
+#define TI_MDIO_CTRL_CLOCK_SKEW 0x0C00
+#define T1_MDIO_TX_TERNARY_SYM 0x0300
+#define T1_MDIO_PCS_SC_RESET BIT(7)
+#define T1_MDIO_ENERGY_DETECT_M BIT(6)
+#define T1_MDIO_IGNORE_IDLE BIT(5)
+#define T1_MDIO_TIME_OUT_CNT 0x001E
+#define T1_MDIO_TIME_OUT_ENABLE BIT(0)
+
+#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_BANK_SEL_INST 5
+#define T1_REG_BANK_SEL_DSP 4
+#define T1_REG_BANK_SEL_AFE 3
+#define T1_REG_BANK_SEL_PCS 2
+#define T1_REG_BANK_SEL_MISC 1
+#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_PCS_CTRL 0x17
+
+#define REG_PORT_T1_PHY_INT_STATUS 0x18
+#define REG_PORT_T1_PHY_INT_ENABLE 0x19
+
+#define T1_PHY_CTRL_MAXWAIT_TIMER_INT BIT(8)
+#define T1_ZERO_TIMER_INT BIT(7)
+#define T1_ENERGY_OFF_INT BIT(6)
+#define T1_IDLE_ERR_THRESH_DET_INT BIT(4)
+#define T1_LINK_UP_INT BIT(2)
+#define T1_LINK_DOWN_INT BIT(1)
+#define T1_ENERGY_ON_INT BIT(0)
+
+#define REG_PORT_T1_POWER_DOWN_CTRL 0x1A
+
+#define T1_BIST_LINK_UP BIT(15)
+#define T1_EMERGENCY_STOP BIT(14)
+#define T1_PTP_ENABLE BIT(13)
+#define T1_PCS_LINK_CTRL_ENABLE BIT(12)
+#define T1_DSP_START_EQUALIZER BIT(11)
+#define T1_START_ZQ_CAL BIT(10)
+#define T1_FORCE_TX_ENABLE BIT(9)
+#define T1_HW_INIT_SEQ_ENABLE BIT(8)
+#define T1_WAKE_ENABLE BIT(7)
+#define T1_LINK_UP_REQ BIT(6)
+#define T1_AUTO_CLR_EPDWRDOWN BIT(5)
+#define T1_ENERGY_DETECT_POWER_DOWN BIT(4)
+#define T1_FORCE_ENERGY_DETECT BIT(3)
+#define T1_AUTO_SLEEP_WAKEUP BIT(2)
+#define T1_ENERGY_DETECT_HW_ENABLE BIT(1)
+#define T1_ENERGY_DETECT_ENABLE BIT(0)
+
+#define REG_PORT_T1_BIST_CTRL 0x1B
+#define REG_PORT_T1_BIST_STAT 0x1C
+#define REG_PORT_T1_BIST_ERR_CNT_STS 0x1D
+#define REG_PORT_T1_PCS_RX_ERR_CNT_STS 0x1E
+#define REG_PORT_T1_TS_CTRL_STAT 0x1F
+
+#define REG_PORT_T1_PCS_DESCRM_CTRL_0 0x00
+
+#define T1_PCS_SEND_IDLE_LOC_RCV_STS BIT(15)
+#define T1_PCS_DESCR_RELOCK_ON_IDLE BIT(14)
+#define T1_PCS_DESCR_RELOCK_ON_RCV BIT(13)
+#define T1_PCS_DESCR_RELOCK_ON_EQ BIT(12)
+#define T1_PCS_DESCR_LOCK_SYM_CNT 0x0FC0
+#define T1_PCS_DESCR_LOCK_CHK_WIN 0x003F
+
+#define REG_PORT_T1_PCS_DESCRM_CTRL_1 0x01
+
+#define T1_PCS_DESCR_PIPE_DELAY 0x0700
+#define T1_PCS_FEED_DESCR_LFSR_WIN 0x003F
+
+#define REG_PORT_T1_PCS_EQ_TIMER_CTRL 0x02
+
+#define REG_PORT_T1_PCS_RX_ERR_CNT 0x0E
+
+#define REG_PORT_T1_PCS_ED_STABILITY 0x26
+
+#define REG_PORT_TX_PHY_CTRL 0x00
+#define REG_PORT_TX_PHY_STATUS 0x01
+#define REG_PORT_TX_PHY_ID_HI 0x02
+#define REG_PORT_TX_PHY_ID_LO 0x03
+#define REG_PORT_TX_PHY_AUTO_NEG 0x04
+#define REG_PORT_TX_PHY_REMOTE_CAP 0x05
+
+#define REG_PORT_TX_6 0x06
+#define REG_PORT_TX_7 0x07
+#define REG_PORT_TX_10 0x10
+#define REG_PORT_TX_11 0x11
+
+#define REG_PORT_TX_SPECIAL_MODES 0x12
+
+#define TX_SPECIAL_CFG_M 0x7
+#define TX_SPECIAL_CFG_S 5
+#define TX_PHYADDR_M 0x1F
+
+#define REG_PORT_TX_13 0x13
+
+#define REG_PORT_TX_IND_CTRL 0x14
+
+#define TX_IND_DATA_READ BIT(15)
+#define TX_IND_DATA_WRITE BIT(14)
+#define TX_REG_BANK_SEL_M 0x7
+#define TX_REG_BANK_SEL_S 11
+#define TX_REG_BANK_SEL_VMDAC 7
+#define TX_REG_BANK_SEL_BIST 3
+#define TX_REG_BANK_SEL_WOL 1
+#define TX_REG_BANK_SEL_DSP 0
+#define TX_TEST_MODE BIT(10)
+#define TX_READ_ADDR_M 0x1F
+#define TX_READ_ADDR_S 5
+#define TX_WRITE_ADDR_M 0x1F
+
+#define REG_PORT_TX_READ_DATA_LO 0x15
+#define REG_PORT_TX_READ_DATA_HI 0x16
+#define REG_PORT_TX_WRITE_DATA 0x17
+
+#define REG_PORT_TX_PHY_INT_STATUS 0x1D
+#define REG_PORT_TX_PHY_INT_ENABLE 0x1E
+
+#define TX_ENERGY_ON_INT BIT(7)
+#define TX_AUTO_NEG_COMPLETE_INT BIT(6)
+#define TX_LINK_DOWN_INT BIT(4)
+#define TX_AUTO_NEG_ACK_INT BIT(3)
+#define TX_AUTO_NEG_PAGE_RCV_INT BIT(1)
+
+#define REG_PORT_TX_1F 0x1F
+
+#define TX_DSP_DCBLW 0x00
+
+#define TX_DSP_DCBLW_VAL 0x9aa2
+
+#define TX_DSP_A5_CONFIG 0x16
+
+#define TX_A5_TXCLKPHSEL_M 0x7
+#define TX_A5_TXCLKPHSEL_S 12
+#define TX_A5_TXCLK_0_NS 0
+#define TX_A5_TXCLK_1_NS 1
+#define TX_A5_TXCLK_2_NS 2
+#define TX_A5_TXCLK_3_NS 3
+#define TX_A5_TXCLK_4_NS 4
+#define TX_A5_TXCLK_5_NS 5
+#define TX_A5_TXCLK_6_NS 6
+#define TX_A5_TXCLK_7_NS 7
+
+#define TX_DSP_A10_CONFIG 0x1C
+
+#define TX_DSP_A10_CONFIG_VAL 0x9000
+
+#define TX_DSP_A11_CONFIG 0x1D
+
+#define TX_DSP_A11_CONFIG_VAL 0x7600
+
+#define TX_BIST_FR_PLL_DIV0 0x1C
+#define TX_BIST_FR_PLL_DIV1 0x1D
+
+#define TX_FR_PLL_DIV0 0x0
+#define TX_FR_PLL_DIV1 0x1280
+
+#define TX_VMDAC_ZQ_CAL_CTRL 0x00
+
+#define TX_START_ZQ_CAL BIT(0)
+#define TX_BYPASS_ZQ_CAL BIT(1)
+
+#define TX_VMDAC_ZQ_CAL_STAT 0x01
+
+#define TX_VMDAC_CTRL0 0x02
+#define TX_VDVREF_EN BIT(0)
+#define TX_VDVEXT_EN BIT(1)
+#define TX_A_ZEN BIT(11)
+#define TX_A_RXZEN BIT(12)
+#define TX_VD_INTIR_EN BIT(15)
+
+#define TX_VMDAC_CTRL0_VAL 0x9783
+#define TX_VMDAC_CTRL1 0x03
+
+#define TX_10BT_VD_EN BIT(0)
+#define TX_B_ZEN BIT(11)
+#define TX_B_RXZEN BIT(12)
+#define TX_VD_PD BIT(15)
+
+#define TX_VMDAC_CTRL1_VAL 0x7784
+#define TX_VMDAC_MISC_PCS_CTRL0 0x05
+
+#define TX_MISC_PCS_CTRL0_13 BIT(13)
+#define TX_VMDAC_MISC_PCS_CTRL1 0x06
+
+#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_UCAST_CTRL__2 0x0B0E
+#define REG_PORT_LUE_UNK_MCAST_CTRL__2 0x0B10
+#define REG_PORT_LUE_UNK_VID_CTRL__2 0x0B12
+
+#define PORT_UNK_UCAST_ENABLE BIT(15)
+#define PORT_UNK_MCAST_ENABLE BIT(15)
+#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..20ee931c448d
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan937x_spi.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip LAN937X switch driver register access through SPI
+ * Copyright (C) 2019-2020 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 "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);
+
+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;
+}
+
+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"},
+ { .compatible = "microchip,lan9371"},
+ { .compatible = "microchip,lan9372"},
+ { .compatible = "microchip,lan9373"},
+ { .compatible = "microchip,lan9374"},
+ {},
+};
+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@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Microchip LAN937x Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
--
2.25.1