Commit 2227ec7b authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'sja1105-xpcs'



Vladimir Oltean says:

====================
Port the SJA1105 DSA driver to XPCS

As requested when adding support for the NXP SJA1110, the SJA1105 driver
could make use of the common XPCS driver, to eliminate some hardware
specific code duplication.

This series modifies the XPCS driver so that it can accommodate the XPCS
instantiation from NXP switches, and the SJA1105 driver so it can expose
what the XPCS driver expects.

Tested on NXP SJA1105S and SJA1110A.

Changes in v3:
None. This is a resend of v2 which had "changes requested" even though
there was no direct feedback.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6e49699 56b63466
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -13203,6 +13203,7 @@ M: Vladimir Oltean <olteanv@gmail.com>
L:	linux-kernel@vger.kernel.org
S:	Maintained
F:	drivers/net/dsa/sja1105
F:	drivers/net/pcs/pcs-xpcs-nxp.c
NXP TDA998X DRM DRIVER
M:	Russell King <linux@armlinux.org.uk>
@@ -17676,6 +17677,7 @@ M: Jose Abreu <Jose.Abreu@synopsys.com>
L:	netdev@vger.kernel.org
S:	Supported
F:	drivers/net/pcs/pcs-xpcs.c
F:	drivers/net/pcs/pcs-xpcs.h
F:	include/linux/pcs/pcs-xpcs.h
SYNOPSYS DESIGNWARE I2C DRIVER
+1 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ config NET_DSA_SJA1105
tristate "NXP SJA1105 Ethernet switch family support"
	depends on NET_DSA && SPI
	select NET_DSA_TAG_SJA1105
	select PCS_XPCS
	select PACKING
	select CRC32
	help
+9 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ struct sja1105_regs {
	u64 stats[__MAX_SJA1105_STATS_AREA][SJA1105_MAX_NUM_PORTS];
	u64 mdio_100base_tx;
	u64 mdio_100base_t1;
	u64 pcs_base[SJA1105_MAX_NUM_PORTS];
};

struct sja1105_mdio_private {
@@ -133,6 +134,8 @@ struct sja1105_info {
	bool (*rxtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
	void (*txtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb);
	int (*clocking_setup)(struct sja1105_private *priv);
	int (*pcs_mdio_read)(struct mii_bus *bus, int phy, int reg);
	int (*pcs_mdio_write)(struct mii_bus *bus, int phy, int reg, u16 val);
	const char *name;
	bool supports_mii[SJA1105_MAX_NUM_PORTS];
	bool supports_rmii[SJA1105_MAX_NUM_PORTS];
@@ -265,6 +268,8 @@ struct sja1105_private {
	struct sja1105_cbs_entry *cbs;
	struct mii_bus *mdio_base_t1;
	struct mii_bus *mdio_base_tx;
	struct mii_bus *mdio_pcs;
	struct dw_xpcs *xpcs[SJA1105_MAX_NUM_PORTS];
	struct sja1105_tagger_data tagger_data;
	struct sja1105_ptp_data ptp_data;
	struct sja1105_tas_data tas_data;
@@ -297,6 +302,10 @@ void sja1105_frame_memory_partitioning(struct sja1105_private *priv);
/* From sja1105_mdio.c */
int sja1105_mdiobus_register(struct dsa_switch *ds);
void sja1105_mdiobus_unregister(struct dsa_switch *ds);
int sja1105_pcs_mdio_read(struct mii_bus *bus, int phy, int reg);
int sja1105_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val);
int sja1110_pcs_mdio_read(struct mii_bus *bus, int phy, int reg);
int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val);

/* From sja1105_devlink.c */
int sja1105_devlink_setup(struct dsa_switch *ds);
+39 −147
Original line number Diff line number Diff line
@@ -16,13 +16,13 @@
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/of_device.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/netdev_features.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/if_ether.h>
#include <linux/dsa/8021q.h>
#include "sja1105.h"
#include "sja1105_sgmii.h"
#include "sja1105_tas.h"

#define SJA1105_UNKNOWN_MULTICAST	0x010000000000ull
@@ -209,12 +209,14 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv)
				goto unsupported;

			mii->xmii_mode[i] = XMII_MODE_SGMII;
			mii->special[i] = true;
			break;
		case PHY_INTERFACE_MODE_2500BASEX:
			if (!priv->info->supports_2500basex[i])
				goto unsupported;

			mii->xmii_mode[i] = XMII_MODE_SGMII;
			mii->special[i] = true;
			break;
unsupported:
		default:
@@ -1002,93 +1004,6 @@ static int sja1105_parse_dt(struct sja1105_private *priv)
	return rc;
}

static int sja1105_sgmii_read(struct sja1105_private *priv, int port, int mmd,
			      int pcs_reg)
{
	u64 addr = (mmd << 16) | pcs_reg;
	u32 val;
	int rc;

	if (port != SJA1105_SGMII_PORT)
		return -ENODEV;

	rc = sja1105_xfer_u32(priv, SPI_READ, addr, &val, NULL);
	if (rc < 0)
		return rc;

	return val;
}

static int sja1105_sgmii_write(struct sja1105_private *priv, int port, int mmd,
			       int pcs_reg, u16 pcs_val)
{
	u64 addr = (mmd << 16) | pcs_reg;
	u32 val = pcs_val;
	int rc;

	if (port != SJA1105_SGMII_PORT)
		return -ENODEV;

	rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &val, NULL);
	if (rc < 0)
		return rc;

	return val;
}

static void sja1105_sgmii_pcs_config(struct sja1105_private *priv, int port,
				     bool an_enabled, bool an_master)
{
	u16 ac = SJA1105_AC_AUTONEG_MODE_SGMII;

	/* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to
	 * stop the clock during LPI mode, make the MAC reconfigure
	 * autonomously after PCS autoneg is done, flush the internal FIFOs.
	 */
	sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_DC1,
			    SJA1105_DC1_EN_VSMMD1 |
			    SJA1105_DC1_CLOCK_STOP_EN |
			    SJA1105_DC1_MAC_AUTO_SW |
			    SJA1105_DC1_INIT);
	/* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */
	sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_DC2,
			    SJA1105_DC2_TX_POL_INV_DISABLE);
	/* AUTONEG_CONTROL: Use SGMII autoneg */
	if (an_master)
		ac |= SJA1105_AC_PHY_MODE | SJA1105_AC_SGMII_LINK;
	sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, SJA1105_AC, ac);
	/* BASIC_CONTROL: enable in-band AN now, if requested. Otherwise,
	 * sja1105_sgmii_pcs_force_speed must be called later for the link
	 * to become operational.
	 */
	if (an_enabled)
		sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1,
				    BMCR_ANENABLE | BMCR_ANRESTART);
}

static void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv,
					  int port, int speed)
{
	int pcs_speed;

	switch (speed) {
	case SPEED_1000:
		pcs_speed = BMCR_SPEED1000;
		break;
	case SPEED_100:
		pcs_speed = BMCR_SPEED100;
		break;
	case SPEED_10:
		pcs_speed = BMCR_SPEED10;
		break;
	default:
		dev_err(priv->ds->dev, "Invalid speed %d\n", speed);
		return;
	}
	sja1105_sgmii_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1,
			    pcs_speed | BMCR_FULLDPLX);
}

/* Convert link speed from SJA1105 to ethtool encoding */
static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv,
					 u64 speed)
@@ -1141,6 +1056,9 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
	case SPEED_1000:
		speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
		break;
	case SPEED_2500:
		speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
		break;
	default:
		dev_err(dev, "Invalid speed %iMbps\n", speed_mbps);
		return -EINVAL;
@@ -1155,6 +1073,8 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
	 */
	if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
		mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
	else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
		mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
	else
		mac[port].speed = speed;

@@ -1195,10 +1115,9 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port,
			       unsigned int mode,
			       const struct phylink_link_state *state)
{
	struct dsa_port *dp = dsa_to_port(ds, port);
	struct sja1105_private *priv = ds->priv;
	bool is_sgmii;

	is_sgmii = (state->interface == PHY_INTERFACE_MODE_SGMII);
	struct dw_xpcs *xpcs;

	if (sja1105_phy_mode_mismatch(priv, port, state->interface)) {
		dev_err(ds->dev, "Changing PHY mode to %s not supported!\n",
@@ -1206,15 +1125,10 @@ static void sja1105_mac_config(struct dsa_switch *ds, int port,
		return;
	}

	if (phylink_autoneg_inband(mode) && !is_sgmii) {
		dev_err(ds->dev, "In-band AN not supported!\n");
		return;
	}
	xpcs = priv->xpcs[port];

	if (is_sgmii)
		sja1105_sgmii_pcs_config(priv, port,
					 phylink_autoneg_inband(mode),
					 false);
	if (xpcs)
		phylink_set_pcs(dp->pl, &xpcs->pcs);
}

static void sja1105_mac_link_down(struct dsa_switch *ds, int port,
@@ -1235,10 +1149,6 @@ static void sja1105_mac_link_up(struct dsa_switch *ds, int port,

	sja1105_adjust_port_config(priv, port, speed);

	if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII &&
	    !phylink_autoneg_inband(mode))
		sja1105_sgmii_pcs_force_speed(priv, port, speed);

	sja1105_inhibit_tx(priv, BIT(port), false);
}

@@ -1277,44 +1187,16 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port,
	if (mii->xmii_mode[port] == XMII_MODE_RGMII ||
	    mii->xmii_mode[port] == XMII_MODE_SGMII)
		phylink_set(mask, 1000baseT_Full);
	if (priv->info->supports_2500basex[port]) {
		phylink_set(mask, 2500baseT_Full);
		phylink_set(mask, 2500baseX_Full);
	}

	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
	bitmap_and(state->advertising, state->advertising, mask,
		   __ETHTOOL_LINK_MODE_MASK_NBITS);
}

static int sja1105_mac_pcs_get_state(struct dsa_switch *ds, int port,
				     struct phylink_link_state *state)
{
	struct sja1105_private *priv = ds->priv;
	int ais;

	/* Read the vendor-specific AUTONEG_INTR_STATUS register */
	ais = sja1105_sgmii_read(priv, port, MDIO_MMD_VEND2, SJA1105_AIS);
	if (ais < 0)
		return ais;

	switch (SJA1105_AIS_SPEED(ais)) {
	case 0:
		state->speed = SPEED_10;
		break;
	case 1:
		state->speed = SPEED_100;
		break;
	case 2:
		state->speed = SPEED_1000;
		break;
	default:
		dev_err(ds->dev, "Invalid SGMII PCS speed %lu\n",
			SJA1105_AIS_SPEED(ais));
	}
	state->duplex = SJA1105_AIS_DUPLEX_MODE(ais);
	state->an_complete = SJA1105_AIS_COMPLETE(ais);
	state->link = SJA1105_AIS_LINK_STATUS(ais);

	return 0;
}

static int
sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port,
			      const struct sja1105_l2_lookup_entry *requested)
@@ -1990,14 +1872,14 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
	 * change it through the dynamic interface later.
	 */
	for (i = 0; i < ds->num_ports; i++) {
		u32 reg_addr = mdiobus_c45_addr(MDIO_MMD_VEND2, MDIO_CTRL1);

		speed_mbps[i] = sja1105_port_speed_to_ethtool(priv,
							      mac[i].speed);
		mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO];

		if (priv->phy_mode[i] == PHY_INTERFACE_MODE_SGMII)
			bmcr[i] = sja1105_sgmii_read(priv, i,
						     MDIO_MMD_VEND2,
						     MDIO_CTRL1);
		if (priv->xpcs[i])
			bmcr[i] = mdiobus_read(priv->mdio_pcs, i, reg_addr);
	}

	/* No PTP operations can run right now */
@@ -2045,30 +1927,41 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
		goto out;

	for (i = 0; i < ds->num_ports; i++) {
		bool an_enabled;
		struct dw_xpcs *xpcs = priv->xpcs[i];
		unsigned int mode;

		rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
		if (rc < 0)
			goto out;

		if (priv->phy_mode[i] != PHY_INTERFACE_MODE_SGMII)
		if (!xpcs)
			continue;

		an_enabled = !!(bmcr[i] & BMCR_ANENABLE);
		if (bmcr[i] & BMCR_ANENABLE)
			mode = MLO_AN_INBAND;
		else if (priv->fixed_link[i])
			mode = MLO_AN_FIXED;
		else
			mode = MLO_AN_PHY;

		sja1105_sgmii_pcs_config(priv, i, an_enabled, false);
		rc = xpcs_do_config(xpcs, priv->phy_mode[i], mode);
		if (rc < 0)
			goto out;

		if (!an_enabled) {
		if (!phylink_autoneg_inband(mode)) {
			int speed = SPEED_UNKNOWN;

			if (bmcr[i] & BMCR_SPEED1000)
			if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX)
				speed = SPEED_2500;
			else if (bmcr[i] & BMCR_SPEED1000)
				speed = SPEED_1000;
			else if (bmcr[i] & BMCR_SPEED100)
				speed = SPEED_100;
			else
				speed = SPEED_10;

			sja1105_sgmii_pcs_force_speed(priv, i, speed);
			xpcs_link_up(&xpcs->pcs, mode, priv->phy_mode[i],
				     speed, DUPLEX_FULL);
		}
	}

@@ -3649,7 +3542,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
	.port_change_mtu	= sja1105_change_mtu,
	.port_max_mtu		= sja1105_get_max_mtu,
	.phylink_validate	= sja1105_phylink_validate,
	.phylink_mac_link_state	= sja1105_mac_pcs_get_state,
	.phylink_mac_config	= sja1105_mac_config,
	.phylink_mac_link_up	= sja1105_mac_link_up,
	.phylink_mac_link_down	= sja1105_mac_link_down,
+255 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2021, NXP Semiconductors
 */
#include <linux/pcs/pcs-xpcs.h>
#include <linux/of_mdio.h>
#include "sja1105.h"

#define SJA1110_PCS_BANK_REG		SJA1110_SPI_ADDR(0x3fc)

int sja1105_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
{
	struct sja1105_mdio_private *mdio_priv = bus->priv;
	struct sja1105_private *priv = mdio_priv->priv;
	u64 addr;
	u32 tmp;
	u16 mmd;
	int rc;

	if (!(reg & MII_ADDR_C45))
		return -EINVAL;

	mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
	addr = (mmd << 16) | (reg & GENMASK(15, 0));

	if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
		return 0xffff;

	if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
		return NXP_SJA1105_XPCS_ID >> 16;
	if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
		return NXP_SJA1105_XPCS_ID & GENMASK(15, 0);

	rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
	if (rc < 0)
		return rc;

	return tmp & 0xffff;
}

int sja1105_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
{
	struct sja1105_mdio_private *mdio_priv = bus->priv;
	struct sja1105_private *priv = mdio_priv->priv;
	u64 addr;
	u32 tmp;
	u16 mmd;

	if (!(reg & MII_ADDR_C45))
		return -EINVAL;

	mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
	addr = (mmd << 16) | (reg & GENMASK(15, 0));
	tmp = val;

	if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
		return -EINVAL;

	return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
}

int sja1110_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
{
	struct sja1105_mdio_private *mdio_priv = bus->priv;
	struct sja1105_private *priv = mdio_priv->priv;
	const struct sja1105_regs *regs = priv->info->regs;
	int offset, bank;
	u64 addr;
	u32 tmp;
	u16 mmd;
	int rc;

	if (!(reg & MII_ADDR_C45))
		return -EINVAL;

	if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
		return -ENODEV;

	mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
	addr = (mmd << 16) | (reg & GENMASK(15, 0));

	if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
		return NXP_SJA1110_XPCS_ID >> 16;
	if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
		return NXP_SJA1110_XPCS_ID & GENMASK(15, 0);

	bank = addr >> 8;
	offset = addr & GENMASK(7, 0);

	/* This addressing scheme reserves register 0xff for the bank address
	 * register, so that can never be addressed.
	 */
	if (WARN_ON(offset == 0xff))
		return -ENODEV;

	tmp = bank;

	rc = sja1105_xfer_u32(priv, SPI_WRITE,
			      regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
			      &tmp, NULL);
	if (rc < 0)
		return rc;

	rc = sja1105_xfer_u32(priv, SPI_READ, regs->pcs_base[phy] + offset,
			      &tmp, NULL);
	if (rc < 0)
		return rc;

	return tmp & 0xffff;
}

int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
{
	struct sja1105_mdio_private *mdio_priv = bus->priv;
	struct sja1105_private *priv = mdio_priv->priv;
	const struct sja1105_regs *regs = priv->info->regs;
	int offset, bank;
	u64 addr;
	u32 tmp;
	u16 mmd;
	int rc;

	if (!(reg & MII_ADDR_C45))
		return -EINVAL;

	if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
		return -ENODEV;

	mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
	addr = (mmd << 16) | (reg & GENMASK(15, 0));

	bank = addr >> 8;
	offset = addr & GENMASK(7, 0);

	/* This addressing scheme reserves register 0xff for the bank address
	 * register, so that can never be addressed.
	 */
	if (WARN_ON(offset == 0xff))
		return -ENODEV;

	tmp = bank;

	rc = sja1105_xfer_u32(priv, SPI_WRITE,
			      regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
			      &tmp, NULL);
	if (rc < 0)
		return rc;

	tmp = val;

	return sja1105_xfer_u32(priv, SPI_WRITE, regs->pcs_base[phy] + offset,
				&tmp, NULL);
}

enum sja1105_mdio_opcode {
	SJA1105_C45_ADDR = 0,
	SJA1105_C22 = 1,
@@ -239,6 +386,108 @@ static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
	priv->mdio_base_t1 = NULL;
}

static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
{
	struct sja1105_mdio_private *mdio_priv;
	struct dsa_switch *ds = priv->ds;
	struct mii_bus *bus;
	int rc = 0;
	int port;

	if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write)
		return 0;

	bus = mdiobus_alloc_size(sizeof(*mdio_priv));
	if (!bus)
		return -ENOMEM;

	bus->name = "SJA1105 PCS MDIO bus";
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs",
		 dev_name(ds->dev));
	bus->read = priv->info->pcs_mdio_read;
	bus->write = priv->info->pcs_mdio_write;
	bus->parent = ds->dev;
	/* There is no PHY on this MDIO bus => mask out all PHY addresses
	 * from auto probing.
	 */
	bus->phy_mask = ~0;
	mdio_priv = bus->priv;
	mdio_priv->priv = priv;

	rc = mdiobus_register(bus);
	if (rc) {
		mdiobus_free(bus);
		return rc;
	}

	for (port = 0; port < ds->num_ports; port++) {
		struct mdio_device *mdiodev;
		struct dw_xpcs *xpcs;

		if (dsa_is_unused_port(ds, port))
			continue;

		if (priv->phy_mode[port] != PHY_INTERFACE_MODE_SGMII &&
		    priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
			continue;

		mdiodev = mdio_device_create(bus, port);
		if (IS_ERR(mdiodev)) {
			rc = PTR_ERR(mdiodev);
			goto out_pcs_free;
		}

		xpcs = xpcs_create(mdiodev, priv->phy_mode[port]);
		if (IS_ERR(xpcs)) {
			rc = PTR_ERR(xpcs);
			goto out_pcs_free;
		}

		priv->xpcs[port] = xpcs;
	}

	priv->mdio_pcs = bus;

	return 0;

out_pcs_free:
	for (port = 0; port < ds->num_ports; port++) {
		if (!priv->xpcs[port])
			continue;

		mdio_device_free(priv->xpcs[port]->mdiodev);
		xpcs_destroy(priv->xpcs[port]);
		priv->xpcs[port] = NULL;
	}

	mdiobus_unregister(bus);
	mdiobus_free(bus);

	return rc;
}

static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
{
	struct dsa_switch *ds = priv->ds;
	int port;

	if (!priv->mdio_pcs)
		return;

	for (port = 0; port < ds->num_ports; port++) {
		if (!priv->xpcs[port])
			continue;

		mdio_device_free(priv->xpcs[port]->mdiodev);
		xpcs_destroy(priv->xpcs[port]);
		priv->xpcs[port] = NULL;
	}

	mdiobus_unregister(priv->mdio_pcs);
	mdiobus_free(priv->mdio_pcs);
	priv->mdio_pcs = NULL;
}

int sja1105_mdiobus_register(struct dsa_switch *ds)
{
	struct sja1105_private *priv = ds->priv;
@@ -247,6 +496,10 @@ int sja1105_mdiobus_register(struct dsa_switch *ds)
	struct device_node *mdio_node;
	int rc;

	rc = sja1105_mdiobus_pcs_register(priv);
	if (rc)
		return rc;

	mdio_node = of_get_child_by_name(switch_node, "mdios");
	if (!mdio_node)
		return 0;
@@ -275,6 +528,7 @@ int sja1105_mdiobus_register(struct dsa_switch *ds)
	sja1105_mdiobus_base_tx_unregister(priv);
err_put_mdio_node:
	of_node_put(mdio_node);
	sja1105_mdiobus_pcs_unregister(priv);

	return rc;
}
@@ -285,4 +539,5 @@ void sja1105_mdiobus_unregister(struct dsa_switch *ds)

	sja1105_mdiobus_base_t1_unregister(priv);
	sja1105_mdiobus_base_tx_unregister(priv);
	sja1105_mdiobus_pcs_unregister(priv);
}
Loading