Commit 65b7a2c8 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-dsa-improve-serdes-integration'



Russell King says:

====================
net: dsa: improve serdes integration

Depends on "net: mii clause 37 helpers".

Andrew Lunn mentioned that the Serdes PCS found in Marvell DSA switches
does not automatically update the switch MACs with the link parameters.
Currently, the DSA code implements a work-around for this.

This series improves the Serdes integration, making use of the recent
phylink changes to support split MAC/PCS setups.  One noticable
improvement for userspace is that ethtool can now report the link
partner's advertisement.

This repost has no changes compared to the previous posting; however,
the regression Andrew had found which exists even without this patch
set has now been fixed by Andrew and merged into the net-next tree.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3c04d357 5d5b231d
Loading
Loading
Loading
Loading
+247 −190

File changed.

Preview size limit exceeded, changes collapsed.

+18 −17
Original line number Diff line number Diff line
@@ -399,15 +399,6 @@ struct mv88e6xxx_ops {
	 */
	int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);

#define DUPLEX_UNFORCED		-2

	/* Port's MAC duplex mode
	 *
	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
	 * or DUPLEX_UNFORCED for normal duplex detection.
	 */
	int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);

#define PAUSE_ON		1
#define PAUSE_OFF		0

@@ -417,13 +408,18 @@ struct mv88e6xxx_ops {

#define SPEED_MAX		INT_MAX
#define SPEED_UNFORCED		-2
#define DUPLEX_UNFORCED		-2

	/* Port's MAC speed (in Mbps)
	/* Port's MAC speed (in Mbps) and MAC duplex mode
	 *
	 * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
	 * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value.
	 *
	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
	 * or DUPLEX_UNFORCED for normal duplex detection.
	 */
	int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
	int (*port_set_speed_duplex)(struct mv88e6xxx_chip *chip, int port,
				     int speed, int duplex);

	/* What interface mode should be used for maximum speed? */
	phy_interface_t (*port_max_speed_mode)(int port);
@@ -462,9 +458,6 @@ struct mv88e6xxx_ops {
	 */
	int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port,
				      int upstream_port);
	/* Return the port link state, as required by phylink */
	int (*port_link_state)(struct mv88e6xxx_chip *chip, int port,
			       struct phylink_link_state *state);

	/* Snapshot the statistics for a port. The statistics can then
	 * be read back a leisure but still with a consistent view.
@@ -502,6 +495,17 @@ struct mv88e6xxx_ops {
	/* SERDES lane mapping */
	u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);

	int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port,
				    u8 lane, struct phylink_link_state *state);
	int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port,
				 u8 lane, unsigned int mode,
				 phy_interface_t interface,
				 const unsigned long *advertise);
	int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port,
				     u8 lane);
	int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port,
				  u8 lane, int speed, int duplex);

	/* SERDES interrupt handling */
	unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
					   int port);
@@ -669,9 +673,6 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg,
			u16 mask, u16 val);
int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg,
		       int bit, int val);
int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,
			     int speed, int duplex, int pause,
			     phy_interface_t mode);
struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip);

static inline void mv88e6xxx_reg_lock(struct mv88e6xxx_chip *chip)
+53 −232
Original line number Diff line number Diff line
@@ -162,46 +162,9 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
	return 0;
}

int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
{
	u16 reg;
	int err;

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
	if (err)
		return err;

	reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);

	switch (dup) {
	case DUPLEX_HALF:
		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
		break;
	case DUPLEX_FULL:
		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
		break;
	case DUPLEX_UNFORCED:
		/* normal duplex detection */
		break;
	default:
		return -EOPNOTSUPP;
	}

	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
	if (err)
		return err;

	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");

	return 0;
}

static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
				    int speed, bool alt_bit, bool force_bit)
static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip,
					   int port, int speed, bool alt_bit,
					   bool force_bit, int duplex)
{
	u16 reg, ctrl;
	int err;
@@ -239,11 +202,29 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
		return -EOPNOTSUPP;
	}

	switch (duplex) {
	case DUPLEX_HALF:
		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
		break;
	case DUPLEX_FULL:
		ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
		break;
	case DUPLEX_UNFORCED:
		/* normal duplex detection */
		break;
	default:
		return -EOPNOTSUPP;
	}

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
	if (err)
		return err;

	reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK;
	reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
		 MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);

	if (alt_bit)
		reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
	if (force_bit) {
@@ -261,12 +242,16 @@ static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
	else
		dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");

	return 0;
}

/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = 200;
@@ -275,11 +260,13 @@ int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
		return -EOPNOTSUPP;

	/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
					       duplex);
}

/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = 1000;
@@ -287,11 +274,13 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed == 200 || speed > 1000)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
					       duplex);
}

/* Support 10, 100 Mbps (e.g. 88E6250 family) */
int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = 100;
@@ -299,11 +288,13 @@ int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed > 100)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
					       duplex);
}

/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */
int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = port < 5 ? 1000 : 2500;
@@ -317,7 +308,8 @@ int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed == 2500 && port < 5)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, !port, true);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true,
					       duplex);
}

phy_interface_t mv88e6341_port_max_speed_mode(int port)
@@ -329,7 +321,8 @@ phy_interface_t mv88e6341_port_max_speed_mode(int port)
}

/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = 1000;
@@ -340,11 +333,13 @@ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed == 200 && port < 5)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false,
					       duplex);
}

/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = port < 9 ? 1000 : 2500;
@@ -358,7 +353,8 @@ int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed == 2500 && port < 9)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
					       duplex);
}

phy_interface_t mv88e6390_port_max_speed_mode(int port)
@@ -370,7 +366,8 @@ phy_interface_t mv88e6390_port_max_speed_mode(int port)
}

/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				     int speed, int duplex)
{
	if (speed == SPEED_MAX)
		speed = port < 9 ? 1000 : 10000;
@@ -381,7 +378,8 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
	if (speed >= 2500 && port < 9)
		return -EOPNOTSUPP;

	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
	return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
					       duplex);
}

phy_interface_t mv88e6390x_port_max_speed_mode(int port)
@@ -586,183 +584,6 @@ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
	return 0;
}

int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state)
{
	int err;
	u16 reg;

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
	if (err)
		return err;

	if (port < 5) {
		switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) {
		case MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF:
			state->speed = SPEED_10;
			state->duplex = DUPLEX_HALF;
			break;
		case MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF:
			state->speed = SPEED_100;
			state->duplex = DUPLEX_HALF;
			break;
		case MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL:
			state->speed = SPEED_10;
			state->duplex = DUPLEX_FULL;
			break;
		case MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL:
			state->speed = SPEED_100;
			state->duplex = DUPLEX_FULL;
			break;
		default:
			state->speed = SPEED_UNKNOWN;
			state->duplex = DUPLEX_UNKNOWN;
			break;
		}
	} else {
		switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) {
		case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF:
			state->speed = SPEED_10;
			state->duplex = DUPLEX_HALF;
			break;
		case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF:
			state->speed = SPEED_100;
			state->duplex = DUPLEX_HALF;
			break;
		case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL:
			state->speed = SPEED_10;
			state->duplex = DUPLEX_FULL;
			break;
		case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL:
			state->speed = SPEED_100;
			state->duplex = DUPLEX_FULL;
			break;
		default:
			state->speed = SPEED_UNKNOWN;
			state->duplex = DUPLEX_UNKNOWN;
			break;
		}
	}

	state->link = !!(reg & MV88E6250_PORT_STS_LINK);
	state->an_enabled = 1;
	state->an_complete = state->link;
	state->interface = PHY_INTERFACE_MODE_NA;

	return 0;
}

int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state)
{
	int err;
	u16 reg;

	switch (chip->ports[port].cmode) {
	case MV88E6XXX_PORT_STS_CMODE_RGMII:
		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL,
					  &reg);
		if (err)
			return err;

		if ((reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK) &&
		    (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK))
			state->interface = PHY_INTERFACE_MODE_RGMII_ID;
		else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK)
			state->interface = PHY_INTERFACE_MODE_RGMII_RXID;
		else if (reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK)
			state->interface = PHY_INTERFACE_MODE_RGMII_TXID;
		else
			state->interface = PHY_INTERFACE_MODE_RGMII;
		break;
	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
		state->interface = PHY_INTERFACE_MODE_1000BASEX;
		break;
	case MV88E6XXX_PORT_STS_CMODE_SGMII:
		state->interface = PHY_INTERFACE_MODE_SGMII;
		break;
	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
		state->interface = PHY_INTERFACE_MODE_2500BASEX;
		break;
	case MV88E6XXX_PORT_STS_CMODE_XAUI:
		state->interface = PHY_INTERFACE_MODE_XAUI;
		break;
	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
		state->interface = PHY_INTERFACE_MODE_RXAUI;
		break;
	default:
		/* we do not support other cmode values here */
		state->interface = PHY_INTERFACE_MODE_NA;
	}

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
	if (err)
		return err;

	switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) {
	case MV88E6XXX_PORT_STS_SPEED_10:
		state->speed = SPEED_10;
		break;
	case MV88E6XXX_PORT_STS_SPEED_100:
		state->speed = SPEED_100;
		break;
	case MV88E6XXX_PORT_STS_SPEED_1000:
		state->speed = SPEED_1000;
		break;
	case MV88E6XXX_PORT_STS_SPEED_10000:
		if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) ==
		    MV88E6XXX_PORT_STS_CMODE_2500BASEX)
			state->speed = SPEED_2500;
		else
			state->speed = SPEED_10000;
		break;
	}

	state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ?
			DUPLEX_FULL : DUPLEX_HALF;
	state->link = !!(reg & MV88E6XXX_PORT_STS_LINK);
	state->an_enabled = 1;
	state->an_complete = state->link;

	return 0;
}

int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state)
{
	if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
		u8 cmode = chip->ports[port].cmode;

		/* When a port is in "Cross-chip serdes" mode, it uses
		 * 1000Base-X full duplex mode, but there is no automatic
		 * link detection. Use the sync OK status for link (as it
		 * would do for 1000Base-X mode.)
		 */
		if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) {
			u16 mac;
			int err;

			err = mv88e6xxx_port_read(chip, port,
						  MV88E6XXX_PORT_MAC_CTL, &mac);
			if (err)
				return err;

			state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK);
			state->an_enabled = 1;
			state->an_complete =
				!!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE);
			state->duplex =
				state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN;
			state->speed =
				state->link ? SPEED_1000 : SPEED_UNKNOWN;

			return 0;
		}
	}

	return mv88e6352_port_link_state(chip, port, state);
}

/* Offset 0x02: Jamming Control
 *
 * Do not limit the period of time that this port can be paused for by
+14 −15
Original line number Diff line number Diff line
@@ -298,15 +298,20 @@ int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,

int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);

int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);

int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				    int speed, int duplex);
int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
				     int speed, int duplex);

phy_interface_t mv88e6341_port_max_speed_mode(int port);
phy_interface_t mv88e6390_port_max_speed_mode(int port);
@@ -359,12 +364,6 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
			      phy_interface_t mode);
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state);
int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state);
int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
			      struct phylink_link_state *state);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
				     int upstream_port);
+303 −72
Original line number Diff line number Diff line
@@ -49,6 +49,52 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
	return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
}

static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
					  u16 status, u16 lpa,
					  struct phylink_link_state *state)
{
	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
		state->duplex = status &
				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
			                         DUPLEX_FULL : DUPLEX_HALF;

		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
			state->pause |= MLO_PAUSE_TX;
		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
			state->pause |= MLO_PAUSE_RX;

		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
			if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
				state->speed = SPEED_2500;
			else
				state->speed = SPEED_1000;
			break;
		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
			state->speed = SPEED_100;
			break;
		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
			state->speed = SPEED_10;
			break;
		default:
			dev_err(chip->dev, "invalid PHY speed\n");
			return -EINVAL;
		}
	} else {
		state->link = false;
	}

	if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);

	return 0;
}

int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
			   bool up)
{
@@ -70,6 +116,120 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
	return err;
}

int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
				u8 lane, unsigned int mode,
				phy_interface_t interface,
				const unsigned long *advertise)
{
	u16 adv, bmcr, val;
	bool changed;
	int err;

	switch (interface) {
	case PHY_INTERFACE_MODE_SGMII:
		adv = 0x0001;
		break;

	case PHY_INTERFACE_MODE_1000BASEX:
		adv = linkmode_adv_to_mii_adv_x(advertise,
					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
		break;

	default:
		return 0;
	}

	err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val);
	if (err)
		return err;

	changed = val != adv;
	if (changed) {
		err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv);
		if (err)
			return err;
	}

	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
	if (err)
		return err;

	if (phylink_autoneg_inband(mode))
		bmcr = val | BMCR_ANENABLE;
	else
		bmcr = val & ~BMCR_ANENABLE;

	if (bmcr == val)
		return changed;

	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
}

int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
				   u8 lane, struct phylink_link_state *state)
{
	u16 lpa, status;
	int err;

	err = mv88e6352_serdes_read(chip, 0x11, &status);
	if (err) {
		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
		return err;
	}

	err = mv88e6352_serdes_read(chip, MII_LPA, &lpa);
	if (err) {
		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
		return err;
	}

	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
}

int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
				    u8 lane)
{
	u16 bmcr;
	int err;

	err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr);
	if (err)
		return err;

	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART);
}

int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
				 u8 lane, int speed, int duplex)
{
	u16 val, bmcr;
	int err;

	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
	if (err)
		return err;

	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
	switch (speed) {
	case SPEED_1000:
		bmcr |= BMCR_SPEED1000;
		break;
	case SPEED_100:
		bmcr |= BMCR_SPEED100;
		break;
	case SPEED_10:
		break;
	}

	if (duplex == DUPLEX_FULL)
		bmcr |= BMCR_FULLDPLX;

	if (bmcr == val)
		return 0;

	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
}

u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
	u8 cmode = chip->ports[port].cmode;
@@ -180,26 +340,17 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,

static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
{
	struct dsa_switch *ds = chip->ds;
	u16 status;
	bool up;
	u16 bmsr;
	int err;

	err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
	if (err)
		return;

	/* Status must be read twice in order to give the current link
	 * status. Otherwise the change in link status since the last
	 * read of the register is returned.
	 */
	err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
	if (err)
	/* If the link has dropped, we want to know about it. */
	err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr);
	if (err) {
		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
		return;
	}

	up = status & BMSR_LSTATUS;

	dsa_port_phylink_mac_change(ds, port, up);
	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
}

irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
@@ -410,20 +561,18 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
	int err;

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_CONTROL, &val);
				    MV88E6390_SGMII_BMCR, &val);
	if (err)
		return err;

	if (up)
		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
				  MV88E6390_SGMII_CONTROL_LOOPBACK |
				  MV88E6390_SGMII_CONTROL_PDOWN);
		new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN);
	else
		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
		new_val = val | BMCR_PDOWN;

	if (val != new_val)
		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
					     MV88E6390_SGMII_CONTROL, new_val);
					     MV88E6390_SGMII_BMCR, new_val);

	return err;
}
@@ -540,71 +689,153 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
	return err;
}

static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
					    int port, u8 lane)
int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
				u8 lane, unsigned int mode,
				phy_interface_t interface,
				const unsigned long *advertise)
{
	u8 cmode = chip->ports[port].cmode;
	struct dsa_switch *ds = chip->ds;
	int duplex = DUPLEX_UNKNOWN;
	int speed = SPEED_UNKNOWN;
	phy_interface_t mode;
	int link, err;
	u16 status;
	u16 val, bmcr, adv;
	bool changed;
	int err;

	switch (interface) {
	case PHY_INTERFACE_MODE_SGMII:
		adv = 0x0001;
		break;

	case PHY_INTERFACE_MODE_1000BASEX:
		adv = linkmode_adv_to_mii_adv_x(advertise,
					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
		break;

	case PHY_INTERFACE_MODE_2500BASEX:
		adv = linkmode_adv_to_mii_adv_x(advertise,
					ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
		break;

	default:
		return 0;
	}

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_ADVERTISE, &val);
	if (err)
		return err;

	changed = val != adv;
	if (changed) {
		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
					     MV88E6390_SGMII_ADVERTISE, adv);
		if (err)
			return err;
	}

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_BMCR, &val);
	if (err)
		return err;

	if (phylink_autoneg_inband(mode))
		bmcr = val | BMCR_ANENABLE;
	else
		bmcr = val & ~BMCR_ANENABLE;

	/* setting ANENABLE triggers a restart of negotiation */
	if (bmcr == val)
		return changed;

	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
				      MV88E6390_SGMII_BMCR, bmcr);
}

int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
				   u8 lane, struct phylink_link_state *state)
{
	u16 lpa, status;
	int err;

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_PHY_STATUS, &status);
	if (err) {
		dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
		return;
		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
		return err;
	}

	link = status & MV88E6390_SGMII_PHY_STATUS_LINK ?
	       LINK_FORCED_UP : LINK_FORCED_DOWN;
	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_LPA, &lpa);
	if (err) {
		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
		return err;
	}

	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
		duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
			 DUPLEX_FULL : DUPLEX_HALF;
	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
}

		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
			if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
				speed = SPEED_2500;
			else
				speed = SPEED_1000;
int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
				    u8 lane)
{
	u16 bmcr;
	int err;

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_BMCR, &bmcr);
	if (err)
		return err;

	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
				      MV88E6390_SGMII_BMCR,
				      bmcr | BMCR_ANRESTART);
}

int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
				 u8 lane, int speed, int duplex)
{
	u16 val, bmcr;
	int err;

	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_BMCR, &val);
	if (err)
		return err;

	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
	switch (speed) {
	case SPEED_2500:
	case SPEED_1000:
		bmcr |= BMCR_SPEED1000;
		break;
		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
			speed = SPEED_100;
	case SPEED_100:
		bmcr |= BMCR_SPEED100;
		break;
		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
			speed = SPEED_10;
	case SPEED_10:
		break;
		default:
			dev_err(chip->dev, "invalid PHY speed\n");
			return;
	}

	if (duplex == DUPLEX_FULL)
		bmcr |= BMCR_FULLDPLX;

	if (bmcr == val)
		return 0;

	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
				      MV88E6390_SGMII_BMCR, bmcr);
}

	switch (cmode) {
	case MV88E6XXX_PORT_STS_CMODE_SGMII:
		mode = PHY_INTERFACE_MODE_SGMII;
		break;
	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
		mode = PHY_INTERFACE_MODE_1000BASEX;
		break;
	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
		mode = PHY_INTERFACE_MODE_2500BASEX;
		break;
	default:
		mode = PHY_INTERFACE_MODE_NA;
static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
					    int port, u8 lane)
{
	u16 bmsr;
	int err;

	/* If the link has dropped, we want to know about it. */
	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
				    MV88E6390_SGMII_BMSR, &bmsr);
	if (err) {
		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
		return;
	}

	err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex,
				       PAUSE_OFF, mode);
	if (err)
		dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n",
			err);
	else
		dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP);
	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
}

static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
Loading