diff mbox series

[net-next,4/4] net: dsa: microchip: add SGMII port support to KSZ9477 switch

Message ID 20240809233840.59953-5-Tristram.Ha@microchip.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: dsa: microchip: add SGMII port support to KSZ9477 switch | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 29 this patch: 29
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 29 this patch: 29
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 29 this patch: 29
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 529 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tristram.Ha@microchip.com Aug. 9, 2024, 11:38 p.m. UTC
From: Tristram Ha <tristram.ha@microchip.com>

The SGMII module of KSZ9477 switch can be setup in 3 ways: 0 for direct
connect, 1 for 1000BaseT SFP, and 2 for 10/100/1000 SFP.

SFP is typically used so the default is 1.  The driver can detect
10/100/1000 SFP and change the mode to 2.  For direct connect this mode
has to be explicitly set to 0 as driver cannot detect that
configuration.

The SGMII module can only report basic link status of the SFP, so it is
simulated as a regular internal PHY.

Since the regular PHY in the switch uses interrupt instead of polling the
driver has to handle the SGMII interrupt indicating link on/off.

One issue for the 1000BaseT SFP is there is no link down interrupt, so
the driver has to use polling to detect link down when the link is up.

Recent change in the DSA operation can setup the port before the PHY
interrupt handling function is registered.  As the SGMII interrupt can
be triggered after port setup there is extra code in the interrupt
processing to handle this situation.  Otherwise a kernel fault can be
triggered.

Note the SGMII interrupt cannot be masked in hardware.  Also the module
is not reset when the switch is reset.  It is important to reset the
module properly to make sure the interrupt is not triggered prematurely.

Signed-off-by: Tristram Ha <tristram.ha@microchip.com>
---
 drivers/net/dsa/microchip/ksz9477.c     | 340 +++++++++++++++++++++++-
 drivers/net/dsa/microchip/ksz9477.h     |   2 +
 drivers/net/dsa/microchip/ksz9477_reg.h |   5 +
 drivers/net/dsa/microchip/ksz_common.c  |  28 +-
 drivers/net/dsa/microchip/ksz_common.h  |   6 +
 5 files changed, 376 insertions(+), 5 deletions(-)

Comments

Andrew Lunn Aug. 10, 2024, 5:52 p.m. UTC | #1
On Fri, Aug 09, 2024 at 04:38:40PM -0700, Tristram.Ha@microchip.com wrote:
> From: Tristram Ha <tristram.ha@microchip.com>
> 
> The SGMII module of KSZ9477 switch can be setup in 3 ways: 0 for direct
> connect, 1 for 1000BaseT SFP, and 2 for 10/100/1000 SFP.
> 
> SFP is typically used so the default is 1.  The driver can detect
> 10/100/1000 SFP and change the mode to 2.  For direct connect this mode
> has to be explicitly set to 0 as driver cannot detect that
> configuration.
> 
> The SGMII module can only report basic link status of the SFP, so it is
> simulated as a regular internal PHY.
> 
> Since the regular PHY in the switch uses interrupt instead of polling the
> driver has to handle the SGMII interrupt indicating link on/off.
> 
> One issue for the 1000BaseT SFP is there is no link down interrupt, so
> the driver has to use polling to detect link down when the link is up.
> 
> Recent change in the DSA operation can setup the port before the PHY
> interrupt handling function is registered.  As the SGMII interrupt can
> be triggered after port setup there is extra code in the interrupt
> processing to handle this situation.  Otherwise a kernel fault can be
> triggered.
> 
> Note the SGMII interrupt cannot be masked in hardware.  Also the module
> is not reset when the switch is reset.  It is important to reset the
> module properly to make sure the interrupt is not triggered prematurely.

Why not model this as a PCS? Russell has been converting all PCS like
things in DSA into try PCS drivers. So i suspect Russell will not like
this code, and would prefer a PCS driver.

	Andrew
Andrew Lunn Aug. 11, 2024, 4:32 p.m. UTC | #2
On Fri, Aug 09, 2024 at 04:38:40PM -0700, Tristram.Ha@microchip.com wrote:
> From: Tristram Ha <tristram.ha@microchip.com>
> 
> The SGMII module of KSZ9477 switch can be setup in 3 ways: 0 for direct
> connect, 1 for 1000BaseT SFP, and 2 for 10/100/1000 SFP.
> 
> SFP is typically used so the default is 1.  The driver can detect
> 10/100/1000 SFP and change the mode to 2.  For direct connect this mode
> has to be explicitly set to 0 as driver cannot detect that
> configuration.

Is 1 actually 1000BaseX? An SFP module using fibre would typically
want 1000BaseX, and only support one speed. An SFP module using copper
typically has a PHY in it, it performs auto-neg on the media side, and
then uses SGMII inband signalling to tell the MAC what data rate,
symbol duplication to do. And maybe mode 0 has in-band signalling
turned off, in which case 1000BaseX and SGMII become identical,
because it is the signalling which is different.

	Andrew
Tristram.Ha@microchip.com Aug. 13, 2024, 8:54 p.m. UTC | #3
> On Fri, Aug 09, 2024 at 04:38:40PM -0700, Tristram.Ha@microchip.com wrote:
> > From: Tristram Ha <tristram.ha@microchip.com>
> >
> > The SGMII module of KSZ9477 switch can be setup in 3 ways: 0 for direct
> > connect, 1 for 1000BaseT SFP, and 2 for 10/100/1000 SFP.
> >
> > SFP is typically used so the default is 1.  The driver can detect
> > 10/100/1000 SFP and change the mode to 2.  For direct connect this mode
> > has to be explicitly set to 0 as driver cannot detect that
> > configuration.
> 
> Is 1 actually 1000BaseX? An SFP module using fibre would typically
> want 1000BaseX, and only support one speed. An SFP module using copper
> typically has a PHY in it, it performs auto-neg on the media side, and
> then uses SGMII inband signalling to tell the MAC what data rate,
> symbol duplication to do. And maybe mode 0 has in-band signalling
> turned off, in which case 1000BaseX and SGMII become identical,
> because it is the signalling which is different.

There are 2 ways to program the hardware registers so that the SGMII
module can communicate with either 1000Base-T/LX/SX SFP or
10/100/1000Base-T SFP.  After a cable is plugged in to the SFP the link
interrupt is triggered.  For 10/100/1000Base-T SFP the connected speed
is also reported.  For fiber type SFP this information is not revealed
so the driver assumes 1000 speed.  In fact 100Base-FX fiber SFP is not
supported and does not work.
Tristram.Ha@microchip.com Aug. 13, 2024, 8:59 p.m. UTC | #4
> On Fri, Aug 09, 2024 at 04:38:40PM -0700, Tristram.Ha@microchip.com wrote:
> > From: Tristram Ha <tristram.ha@microchip.com>
> >
> > The SGMII module of KSZ9477 switch can be setup in 3 ways: 0 for direct
> > connect, 1 for 1000BaseT SFP, and 2 for 10/100/1000 SFP.
> >
> > SFP is typically used so the default is 1.  The driver can detect
> > 10/100/1000 SFP and change the mode to 2.  For direct connect this mode
> > has to be explicitly set to 0 as driver cannot detect that
> > configuration.
> >
> > The SGMII module can only report basic link status of the SFP, so it is
> > simulated as a regular internal PHY.
> >
> > Since the regular PHY in the switch uses interrupt instead of polling the
> > driver has to handle the SGMII interrupt indicating link on/off.
> >
> > One issue for the 1000BaseT SFP is there is no link down interrupt, so
> > the driver has to use polling to detect link down when the link is up.
> >
> > Recent change in the DSA operation can setup the port before the PHY
> > interrupt handling function is registered.  As the SGMII interrupt can
> > be triggered after port setup there is extra code in the interrupt
> > processing to handle this situation.  Otherwise a kernel fault can be
> > triggered.
> >
> > Note the SGMII interrupt cannot be masked in hardware.  Also the module
> > is not reset when the switch is reset.  It is important to reset the
> > module properly to make sure the interrupt is not triggered prematurely.
> 
> Why not model this as a PCS? Russell has been converting all PCS like
> things in DSA into try PCS drivers. So i suspect Russell will not like
> this code, and would prefer a PCS driver.

I am not familiar with PCS.  It seems too complicated after looking at
the phylink_pcs structure and associated phylink_pcs_ops functions.

The SGMII module can only report link and does not even restart
auto-negotiation.  It is a set once and forget operation.
diff mbox series

Patch

diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 518ba4a1e34b..6ac0a06a4b74 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -342,6 +342,257 @@  static int ksz9477_wait_alu_sta_ready(struct ksz_device *dev)
 					10, 1000);
 }
 
+static void port_sgmii_s(struct ksz_device *dev, uint port, u16 devid, u16 reg,
+			 u16 len)
+{
+	u32 data;
+
+	data = devid & PORT_SGMII_DEVICE_ID_M;
+	data <<= PORT_SGMII_DEVICE_ID_S;
+	data |= reg;
+	if (len > 1)
+		data |= PORT_SGMII_AUTO_INCR;
+	ksz_pwrite32(dev, port, REG_PORT_SGMII_ADDR__4, data);
+}
+
+static void port_sgmii_r(struct ksz_device *dev, uint port, u16 devid, u16 reg,
+			 u16 *buf, u16 len)
+{
+	u32 data;
+
+	port_sgmii_s(dev, port, devid, reg, len);
+	while (len) {
+		ksz_pread32(dev, port, REG_PORT_SGMII_DATA__4, &data);
+		*buf++ = (u16)data;
+		len--;
+	}
+}
+
+static void port_sgmii_w(struct ksz_device *dev, uint port, u16 devid, u16 reg,
+			 u16 *buf, u16 len)
+{
+	u32 data;
+
+	port_sgmii_s(dev, port, devid, reg, len);
+	while (len) {
+		data = *buf++;
+		ksz_pwrite32(dev, port, REG_PORT_SGMII_DATA__4, data);
+		len--;
+	}
+}
+
+static int port_sgmii_detect(struct ksz_device *dev, uint p)
+{
+	int ret = 0;
+
+	if (dev->sgmii_mode) {
+		struct ksz_port *port = &dev->ports[p];
+		u16 buf[6];
+
+		port_sgmii_r(dev, p, SR_MII, 0, buf, 6);
+		if (buf[5] & SR_MII_REMOTE_ACK) {
+			if (buf[5] & (SR_MII_REMOTE_HALF_DUPLEX |
+				      SR_MII_REMOTE_FULL_DUPLEX))
+				port->fiber = 1;
+			else if (dev->sgmii_mode == 1)
+				dev->sgmii_mode = 2;
+			ret = 1;
+		} else if (dev->sgmii_mode == 1) {
+			port->fiber = 1;
+			ret = 1;
+		}
+	} else {
+		/* Need to be told to run in direct mode. */
+		ret = 1;
+	}
+	return ret;
+}
+
+static void port_sgmii_reset(struct ksz_device *dev, uint p)
+{
+	u16 ctrl;
+
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+	ctrl |= SR_MII_RESET;
+	port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+}
+
+static void port_sgmii_setup(struct ksz_device *dev, uint p, bool pcs,
+			     bool master, bool autoneg, int speed, int duplex)
+{
+	u16 ctrl;
+	u16 cfg;
+	u16 adv;
+
+	/* SGMII registers are not changed by reset. */
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_AUTO_NEG_CTRL, &cfg, 1);
+	if (cfg & SR_MII_AUTO_NEG_COMPLETE_INTR)
+		return;
+	cfg = 0;
+	if (pcs)
+		cfg |= SR_MII_PCS_SGMII << SR_MII_PCS_MODE_S;
+	if (master) {
+		cfg |= SR_MII_TX_CFG_PHY_MASTER;
+		cfg |= SR_MII_SGMII_LINK_UP;
+	}
+	cfg |= SR_MII_AUTO_NEG_COMPLETE_INTR;
+	port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_AUTO_NEG_CTRL, &cfg, 1);
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+	if (master || !autoneg) {
+		switch (speed) {
+		case 1:
+			ctrl |= SR_MII_SPEED_100MBIT;
+			break;
+		case 2:
+			ctrl |= SR_MII_SPEED_1000MBIT;
+			break;
+		}
+	}
+	if (!autoneg) {
+		ctrl &= ~SR_MII_AUTO_NEG_ENABLE;
+		port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+		return;
+	} else if (!(ctrl & SR_MII_AUTO_NEG_ENABLE)) {
+		ctrl |= SR_MII_AUTO_NEG_ENABLE;
+		port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+	}
+
+	/* Need to write to advertise register to send correct signal. */
+	/* Default value is 0x0020. */
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_AUTO_NEGOTIATION, &adv, 1);
+	adv = SR_MII_AUTO_NEG_ASYM_PAUSE_RX << SR_MII_AUTO_NEG_PAUSE_S;
+	if (duplex)
+		adv |= SR_MII_AUTO_NEG_FULL_DUPLEX;
+	else
+		adv |= SR_MII_AUTO_NEG_HALF_DUPLEX;
+	port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_AUTO_NEGOTIATION, &adv, 1);
+	if (master && autoneg) {
+		ctrl |= SR_MII_AUTO_NEG_RESTART;
+		port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+	}
+}
+
+static int sgmii_port_get_speed(struct ksz_device *dev, uint p)
+{
+	struct ksz_port *info = &dev->ports[p];
+	int ret = 0;
+	u16 status;
+	u16 speed;
+	u16 data;
+	u8 link;
+
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_STATUS, &status, 1);
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_STATUS, &status, 1);
+	port_sgmii_r(dev, p, SR_MII, MMD_SR_MII_AUTO_NEG_STATUS, &data, 1);
+
+	/* Typical register values in different modes.
+	 * 10/100/1000: 1f0001 = 01ad  1f0005 = 4000  1f8002 = 0008
+	 *              1f0001 = 01bd  1f0005 = d000  1f8002 = 001a
+	 * 1000:        1f0001 = 018d  1f0005 = 0000  1f8002 = 0000
+	 *              1f0001 = 01ad  1f0005 = 40a0  1f8002 = 0000
+	 *              1f0001 = 01ad  1f0005 = 41a0  1f8002 = 0000
+	 * fiber:       1f0001 = 0189  1f0005 = 0000  1f8002 = 0000
+	 *              1f0001 = 01ad  1f0005 = 41a0  1f8002 = 0000
+	 */
+
+	/* Running in fiber mode. */
+	if (info->fiber && !data &&
+	    (status & (PORT_AUTO_NEG_ACKNOWLEDGE | PORT_LINK_STATUS)) ==
+	    (PORT_AUTO_NEG_ACKNOWLEDGE | PORT_LINK_STATUS)) {
+		data = SR_MII_STAT_LINK_UP |
+		       (SR_MII_STAT_1000_MBPS << SR_MII_STAT_S) |
+		       SR_MII_STAT_FULL_DUPLEX;
+	}
+	if (data & SR_MII_STAT_LINK_UP)
+		ret = 1;
+
+	link = (data & ~SR_MII_AUTO_NEG_COMPLETE_INTR);
+	if (info->sgmii_link == link)
+		return ret;
+
+	if (data & SR_MII_STAT_LINK_UP) {
+		u16 ctrl;
+
+		/* Need to update control register with same link setting. */
+		ctrl = SR_MII_AUTO_NEG_ENABLE;
+		speed = (data >> SR_MII_STAT_S) & SR_MII_STAT_M;
+		if (speed == SR_MII_STAT_1000_MBPS)
+			ctrl |= SR_MII_SPEED_1000MBIT;
+		else if (speed == SR_MII_STAT_100_MBPS)
+			ctrl |= SR_MII_SPEED_100MBIT;
+		if (data & SR_MII_STAT_FULL_DUPLEX)
+			ctrl |= SR_MII_FULL_DUPLEX;
+		port_sgmii_w(dev, p, SR_MII, MMD_SR_MII_CTRL, &ctrl, 1);
+
+		speed = (data >> SR_MII_STAT_S) & SR_MII_STAT_M;
+		info->phydev.speed = SPEED_10;
+		if (speed == SR_MII_STAT_1000_MBPS)
+			info->phydev.speed = SPEED_1000;
+		else if (speed == SR_MII_STAT_100_MBPS)
+			info->phydev.speed = SPEED_100;
+
+		info->phydev.duplex = 0;
+		if (data & SR_MII_STAT_FULL_DUPLEX)
+			info->phydev.duplex = 1;
+	}
+	ret |= 2;
+	info->sgmii_link = link;
+	info->phydev.link = (ret & 1);
+	return ret;
+}
+
+static void sgmii_check_work(struct work_struct *work)
+{
+	struct ksz_device *dev = container_of(work, struct ksz_device,
+					      sgmii_check.work);
+	struct ksz_port *p = &dev->ports[KSZ9477_SGMII_PORT];
+
+	if (p->sgmii && p->phydev.link) {
+		int ret = sgmii_port_get_speed(dev, KSZ9477_SGMII_PORT);
+		struct dsa_switch *ds = dev->ds;
+		struct phy_device *phydev;
+
+		phydev = mdiobus_get_phy(ds->user_mii_bus, KSZ9477_SGMII_PORT);
+		if ((ret & 2) && phydev)
+			phy_trigger_machine(phydev);
+		if (p->phydev.link)
+			schedule_delayed_work(&dev->sgmii_check,
+					      msecs_to_jiffies(500));
+	}
+}
+
+static void sgmii_initial_setup(struct ksz_device *dev, int port)
+{
+	struct ksz_port *p = &dev->ports[port];
+	/* Assume SGMII mode is 2. */
+	bool master = false;
+	bool autoneg = true;
+	bool pcs = true;
+
+	if (!p->sgmii || p->sgmii_setup)
+		return;
+
+	INIT_DELAYED_WORK(&dev->sgmii_check, sgmii_check_work);
+	if (dev->sgmii_mode == 0) {
+		master = true;
+		autoneg = false;
+	} else if (dev->sgmii_mode == 1) {
+		pcs = false;
+		master = true;
+	}
+	port_sgmii_setup(dev, port, pcs, master, autoneg, 2, 1);
+
+	/* Make invalid so the correct value is set. */
+	p->sgmii_link = 0xff;
+	p->sgmii_setup = 1;
+	sgmii_port_get_speed(dev, port);
+
+	/* Need to check link down if using fiber SFP. */
+	if (dev->sgmii_mode == 1 && p->phydev.link)
+		schedule_delayed_work(&dev->sgmii_check,
+				      msecs_to_jiffies(1000));
+}
+
 int ksz9477_reset_switch(struct ksz_device *dev)
 {
 	u8 data8;
@@ -354,6 +605,14 @@  int ksz9477_reset_switch(struct ksz_device *dev)
 	regmap_update_bits(ksz_regmap_8(dev), REG_SW_GLOBAL_SERIAL_CTRL_0,
 			   SPI_AUTO_EDGE_DETECTION, 0);
 
+	/* Only reset SGMII module when the driver is stopped. */
+	if (dev->chip_id == KSZ9477_CHIP_ID) {
+		struct ksz_port *p = &dev->ports[KSZ9477_SGMII_PORT];
+
+		if (p->sgmii_setup)
+			port_sgmii_reset(dev, KSZ9477_SGMII_PORT);
+	}
+
 	/* default configuration */
 	ksz_write8(dev, REG_SW_LUE_CTRL_1,
 		   SW_AGING_ENABLE | SW_LINK_AUTO_AGING | SW_SRC_ADDR_FILTER);
@@ -510,7 +769,7 @@  int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data)
 	 * A fixed PHY can be setup in the device tree, but this function is
 	 * still called for that port during initialization.
 	 * For RGMII PHY there is no way to access it so the fixed PHY should
-	 * be used.  For SGMII PHY the supporting code will be added later.
+	 * be used.  SGMII PHY is simulated as a regular PHY.
 	 */
 	if (!dev->info->internal_phy[addr]) {
 		struct ksz_port *p = &dev->ports[addr];
@@ -520,7 +779,10 @@  int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data)
 			val = 0x1140;
 			break;
 		case MII_BMSR:
-			val = 0x796d;
+			if (p->phydev.link)
+				val = 0x796d;
+			else
+				val = 0x7949;
 			break;
 		case MII_PHYSID1:
 			val = 0x0022;
@@ -533,6 +795,10 @@  int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data)
 			break;
 		case MII_LPA:
 			val = 0xc5e1;
+			if (p->phydev.speed == SPEED_10)
+				val &= ~0x0180;
+			if (p->phydev.duplex == 0)
+				val &= ~0x0140;
 			break;
 		case MII_CTRL1000:
 			val = 0x0700;
@@ -543,6 +809,24 @@  int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data)
 			else
 				val = 0;
 			break;
+		case MII_ESTATUS:
+			val = 0x3000;
+			break;
+
+		/* This register holds the PHY interrupt status. */
+		case MII_TPISTATUS:
+			val = (LINK_DOWN_INT | LINK_UP_INT) << 8;
+			if (p->phydev.interrupts == PHY_INTERRUPT_ENABLED) {
+				if (p->phydev.link)
+					val |= LINK_UP_INT;
+				else
+					val |= LINK_DOWN_INT;
+			}
+			p->phydev.interrupts = 0;
+			break;
+		default:
+			val = 0;
+			break;
 		}
 	} else {
 		ret = ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
@@ -1143,6 +1427,16 @@  void ksz9477_get_caps(struct ksz_device *dev, int port,
 
 	if (dev->info->gbit_capable[port])
 		config->mac_capabilities |= MAC_1000FD;
+	if (dev->info->supports_sgmii[port]) {
+		struct dsa_switch *ds = dev->ds;
+		struct phy_device *phydev;
+
+		phydev = mdiobus_get_phy(ds->user_mii_bus, port);
+
+		/* Change this port interface to SGMII. */
+		if (phydev)
+			phydev->interface = PHY_INTERFACE_MODE_SGMII;
+	}
 }
 
 int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs)
@@ -1218,6 +1512,8 @@  void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
 		     PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
 		     !dev->info->internal_phy[port]);
 
+	sgmii_initial_setup(dev, port);
+
 	if (cpu_port)
 		member = dsa_user_ports(ds);
 	else
@@ -1296,6 +1592,10 @@  void ksz9477_config_cpu_port(struct dsa_switch *ds)
 			continue;
 		ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
 	}
+	if (dev->chip_id == KSZ9477_CHIP_ID) {
+		/* Switch reset does not reset SGMII module. */
+		port_sgmii_reset(dev, KSZ9477_SGMII_PORT);
+	}
 }
 
 int ksz9477_enable_stp_addr(struct ksz_device *dev)
@@ -1370,6 +1670,12 @@  int ksz9477_setup(struct dsa_switch *ds)
 	 */
 	ksz_write8(dev, REG_SW_PME_CTRL, 0);
 
+	if (dev->chip_id == KSZ9477_CHIP_ID) {
+		struct ksz_port *p = &dev->ports[KSZ9477_SGMII_PORT];
+
+		p->sgmii = port_sgmii_detect(dev, KSZ9477_SGMII_PORT);
+	}
+
 	return 0;
 }
 
@@ -1484,6 +1790,8 @@  int ksz9477_switch_init(struct ksz_device *dev)
 
 void ksz9477_switch_exit(struct ksz_device *dev)
 {
+	if (delayed_work_pending(&dev->sgmii_check))
+		cancel_delayed_work_sync(&dev->sgmii_check);
 	ksz9477_reset_switch(dev);
 }
 
@@ -1515,6 +1823,34 @@  static irqreturn_t ksz9477_handle_port_irq(struct ksz_device *dev, u8 port,
 		++cnt;
 		*data &= ~PORT_ACL_INT;
 	}
+	if (*data & PORT_SGMII_INT) {
+		u16 data16 = 0;
+		int ret;
+
+		port_sgmii_w(dev, port, SR_MII, MMD_SR_MII_AUTO_NEG_STATUS,
+			     &data16, 1);
+		ret = sgmii_port_get_speed(dev, port);
+		if ((ret & 2)) {
+			struct ksz_port *p = &dev->ports[port];
+
+			p->phydev.interrupts = PHY_INTERRUPT_ENABLED;
+
+			/* No interrupt for link down. */
+			if (dev->sgmii_mode == 1 && p->phydev.link)
+				schedule_delayed_work(&dev->sgmii_check,
+						      msecs_to_jiffies(500));
+		}
+
+		/* Handle the interrupt if there is no PHY device or its
+		 * interrupt is not registered yet.
+		 */
+		if (!phydev || phydev->interrupts != PHY_INTERRUPT_ENABLED) {
+			if (phydev)
+				phy_trigger_machine(phydev);
+			++cnt;
+			*data &= ~PORT_SGMII_INT;
+		}
+	}
 
 	return (cnt > 0) ? IRQ_HANDLED : IRQ_NONE;
 }
diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h
index 51252d0d0774..ec318491d536 100644
--- a/drivers/net/dsa/microchip/ksz9477.h
+++ b/drivers/net/dsa/microchip/ksz9477.h
@@ -11,6 +11,8 @@ 
 #include <net/dsa.h>
 #include "ksz_common.h"
 
+#define KSZ9477_SGMII_PORT		6
+
 int ksz9477_setup(struct dsa_switch *ds);
 u32 ksz9477_get_port_addr(int port, int offset);
 void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member);
diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h
index da4ef3eb97c7..20e0b233d55d 100644
--- a/drivers/net/dsa/microchip/ksz9477_reg.h
+++ b/drivers/net/dsa/microchip/ksz9477_reg.h
@@ -1038,6 +1038,11 @@ 
 #define SR_MII_AUTO_NEG_FULL_DUPLEX	BIT(5)
 
 #define MMD_SR_MII_REMOTE_CAPABILITY	0x0005
+
+#define SR_MII_REMOTE_ACK		BIT(14)
+#define SR_MII_REMOTE_HALF_DUPLEX	BIT(6)
+#define SR_MII_REMOTE_FULL_DUPLEX	BIT(5)
+
 #define MMD_SR_MII_AUTO_NEG_EXP		0x0006
 #define MMD_SR_MII_AUTO_NEG_EXT		0x000F
 
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 7db74e036c3f..c16e3388dc1a 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -936,8 +936,7 @@  static const struct regmap_range ksz9477_valid_regs[] = {
 	regmap_reg_range(0x701b, 0x701b),
 	regmap_reg_range(0x701f, 0x7020),
 	regmap_reg_range(0x7030, 0x7030),
-	regmap_reg_range(0x7200, 0x7203),
-	regmap_reg_range(0x7206, 0x7207),
+	regmap_reg_range(0x7200, 0x7207),
 	regmap_reg_range(0x7300, 0x7301),
 	regmap_reg_range(0x7400, 0x7401),
 	regmap_reg_range(0x7403, 0x7403),
@@ -1399,6 +1398,8 @@  const struct ksz_chip_data ksz_switch_chips[] = {
 				   false, true, false},
 		.supports_rgmii = {false, false, false, false,
 				   false, true, false},
+		.supports_sgmii = {false, false, false, false,
+				   false, false, true},
 		.internal_phy	= {true, true, true, true,
 				   true, false, false},
 		.gbit_capable	= {true, true, true, true, true, true, true},
@@ -1806,6 +1807,10 @@  static void ksz_phylink_get_caps(struct dsa_switch *ds, int port,
 	if (dev->info->supports_rgmii[port])
 		phy_interface_set_rgmii(config->supported_interfaces);
 
+	if (dev->info->supports_sgmii[port])
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  config->supported_interfaces);
+
 	if (dev->info->internal_phy[port]) {
 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
 			  config->supported_interfaces);
@@ -2089,14 +2094,19 @@  static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
 static int ksz_irq_phy_setup(struct ksz_device *dev)
 {
 	struct dsa_switch *ds = dev->ds;
+	struct ksz_port *p;
 	int phy;
 	int irq;
 	int ret;
 
 	for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) {
 		if (BIT(phy) & ds->phys_mii_mask) {
+			p = &dev->ports[phy];
+			irq = PORT_SRC_PHY_INT;
+			if (p->sgmii)
+				irq = 3;
 			irq = irq_find_mapping(dev->ports[phy].pirq.domain,
-					       PORT_SRC_PHY_INT);
+					       irq);
 			if (irq < 0) {
 				ret = irq;
 				goto out;
@@ -3222,6 +3232,9 @@  static void ksz_phylink_mac_config(struct phylink_config *config,
 		return;
 	}
 
+	if (state->interface == PHY_INTERFACE_MODE_SGMII)
+		return;
+
 	ksz_set_xmii(dev, port, state->interface);
 
 	if (dev->dev_ops->setup_rgmii_delay)
@@ -4456,9 +4469,18 @@  int ksz_switch_register(struct ksz_device *dev)
 	for (port_num = 0; port_num < dev->info->port_cnt; ++port_num)
 		dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA;
 	if (dev->dev->of_node) {
+		u32 mode;
+
 		ret = of_get_phy_mode(dev->dev->of_node, &interface);
 		if (ret == 0)
 			dev->compat_interface = interface;
+
+		dev->sgmii_mode = 1;
+		ret = of_property_read_u32(dev->dev->of_node,
+					   "microchip,sgmii-mode", &mode);
+		if (ret == 0 && mode <= 2)
+			dev->sgmii_mode = (u8)mode;
+
 		ports = of_get_child_by_name(dev->dev->of_node, "ethernet-ports");
 		if (!ports)
 			ports = of_get_child_by_name(dev->dev->of_node, "ports");
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index a2547646026f..e6cb9304e7db 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -84,6 +84,7 @@  struct ksz_chip_data {
 	bool supports_mii[KSZ_MAX_NUM_PORTS];
 	bool supports_rmii[KSZ_MAX_NUM_PORTS];
 	bool supports_rgmii[KSZ_MAX_NUM_PORTS];
+	bool supports_sgmii[KSZ_MAX_NUM_PORTS];
 	bool internal_phy[KSZ_MAX_NUM_PORTS];
 	bool gbit_capable[KSZ_MAX_NUM_PORTS];
 	const struct regmap_access_table *wr_table;
@@ -126,6 +127,9 @@  struct ksz_port {
 	u32 force:1;
 	u32 read:1;			/* read MIB counters in background */
 	u32 freeze:1;			/* MIB counter freeze is enabled */
+	u32 sgmii:1;			/* port is SGMII */
+	u32 sgmii_link:8;
+	u32 sgmii_setup:1;
 
 	struct ksz_port_mib mib;
 	phy_interface_t interface;
@@ -181,6 +185,8 @@  struct ksz_device {
 	struct ksz_port *ports;
 	struct delayed_work mib_read;
 	unsigned long mib_read_interval;
+	struct delayed_work sgmii_check;
+	u8 sgmii_mode;
 	u16 mirror_rx;
 	u16 mirror_tx;
 	u16 port_mask;