Commit 10eb4667 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-phy-add-Lynx-PCS-MDIO-module'



Ioana Ciornei says:

====================
net: phy: add Lynx PCS MDIO module

Add support for the Lynx PCS as a separate module in drivers/net/phy/.
The advantage of this structure is that multiple ethernet or switch
drivers used on NXP hardware (ENETC, Seville, Felix DSA switch etc) can
share the same implementation of PCS configuration and runtime
management.

The module implements phylink_pcs_ops and exports a phylink_pcs
(incorporated into a lynx_pcs) which can be directly passed to phylink
through phylink_pcs_set.

The first 3 patches add some missing pieces in phylink and the locked
mdiobus write accessor. Next, the Lynx PCS MDIO module is added as a
standalone module. The majority of the code is extracted from the Felix
DSA driver. The last patch makes the necessary changes in the Felix and
Seville drivers in order to use the new common PCS implementation.

At the moment, USXGMII (only with in-band AN), SGMII, QSGMII (with and
without in-band AN) and 2500Base-X (only w/o in-band AN) are supported
by the Lynx PCS MDIO module since these were also supported by Felix and
no functional change is intended at this time.

Changes in v2:
 * got rid of the mdio_lynx_pcs structure and directly exported the
 functions without the need of an indirection
 * made the necessary adjustments for this in the Felix DSA driver
 * solved the broken allmodconfig build test by making the module
 tristate instead of bool
 * fixed a memory leakage in the Felix driver (the pcs structure was
 allocated twice)

Changes in v3:
 * added support for PHYLINK PCS ops in DSA (patch 5/9)
 * cleanup in Felix PHYLINK operations and migrate to
 phylink_mac_link_up() being the callback of choice for applying MAC
 configuration (patches 6-8)

Changes in v4:
 * use the newly introduced phylink PCS mechanism
 * install the phylink_pcs in the phylink_mac_config DSA ops
 * remove the direct implementations of the PCS ops
 * do no use the SGMII_ prefix when referring to the IF_MORE register
 * add a phylink helper to decode the USXGMII code word
 * remove cleanup patches for Felix (these have been already accepted)
 * Seville (recently introduced) now has PCS support through the same
 Lynx PCS module

Changes in v5:
 - move the pcs-lynx driver to drivers/net/pcs
 - reword the commit message a bit in 4/5
 - add error checking and error propagation in 4/5
 - s/IF_MODE_DUPLEX/IF_MODE_HALF_DUPLEX in 4/5
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d6043299 588d0550
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -10293,6 +10293,13 @@ S: Maintained
W:	http://linux-test-project.github.io/
T:	git git://github.com/linux-test-project/ltp.git
LYNX PCS MODULE
M:	Ioana Ciornei <ioana.ciornei@nxp.com>
L:	netdev@vger.kernel.org
S:	Supported
F:	drivers/net/phy/pcs-lynx.c
F:	include/linux/pcs-lynx.h
M68K ARCHITECTURE
M:	Geert Uytterhoeven <geert@linux-m68k.org>
L:	linux-m68k@lists.linux-m68k.org
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ config NET_DSA_MSCC_FELIX
	select MSCC_OCELOT_SWITCH_LIB
	select NET_DSA_TAG_OCELOT
	select FSL_ENETC_MDIO
	select PCS_LYNX
	help
	  This driver supports network switches from the Vitesse /
	  Microsemi / Microchip Ocelot family of switching cores that are
+4 −24
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/of_net.h>
#include <linux/pci.h>
#include <linux/of.h>
#include <linux/pcs-lynx.h>
#include <net/pkt_sched.h>
#include <net/dsa.h>
#include "felix.h"
@@ -196,27 +197,16 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port,
		felix->info->phylink_validate(ocelot, port, supported, state);
}

static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port,
					   struct phylink_link_state *state)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);

	if (felix->info->pcs_link_state)
		felix->info->pcs_link_state(ocelot, port, state);

	return 0;
}

static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
				     unsigned int link_an_mode,
				     const struct phylink_link_state *state)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	struct dsa_port *dp = dsa_to_port(ds, port);

	if (felix->info->pcs_config)
		felix->info->pcs_config(ocelot, port, link_an_mode, state);
	if (felix->pcs[port])
		phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs);
}

static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
@@ -306,10 +296,6 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
	ocelot_fields_write(ocelot, port,
			    QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);

	if (felix->info->pcs_link_up)
		felix->info->pcs_link_up(ocelot, port, link_an_mode, interface,
					 speed, duplex);

	if (felix->info->port_sched_speed_set)
		felix->info->port_sched_speed_set(ocelot, port, speed);
}
@@ -626,11 +612,6 @@ static int felix_setup(struct dsa_switch *ds)

	ds->mtu_enforcement_ingress = true;
	ds->configure_vlan_while_not_filtering = true;
	/* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040)
	 * isn't instantiated for the Felix PF.
	 * In-band AN may take a few ms to complete, so we need to poll.
	 */
	ds->pcs_poll = true;

	return 0;
}
@@ -786,7 +767,6 @@ const struct dsa_switch_ops felix_switch_ops = {
	.get_sset_count		= felix_get_sset_count,
	.get_ts_info		= felix_get_ts_info,
	.phylink_validate	= felix_phylink_validate,
	.phylink_mac_link_state	= felix_phylink_mac_pcs_get_state,
	.phylink_mac_config	= felix_phylink_mac_config,
	.phylink_mac_link_down	= felix_phylink_mac_link_down,
	.phylink_mac_link_up	= felix_phylink_mac_link_up,
+1 −19
Original line number Diff line number Diff line
@@ -28,15 +28,6 @@ struct felix_info {
	int				imdio_pci_bar;
	int	(*mdio_bus_alloc)(struct ocelot *ocelot);
	void	(*mdio_bus_free)(struct ocelot *ocelot);
	void	(*pcs_config)(struct ocelot *ocelot, int port,
			      unsigned int link_an_mode,
			      const struct phylink_link_state *state);
	void	(*pcs_link_up)(struct ocelot *ocelot, int port,
			       unsigned int link_an_mode,
			       phy_interface_t interface,
			       int speed, int duplex);
	void	(*pcs_link_state)(struct ocelot *ocelot, int port,
				  struct phylink_link_state *state);
	void	(*phylink_validate)(struct ocelot *ocelot, int port,
				    unsigned long *supported,
				    struct phylink_link_state *state);
@@ -59,20 +50,11 @@ struct felix {
	const struct felix_info		*info;
	struct ocelot			ocelot;
	struct mii_bus			*imdio;
	struct phy_device		**pcs;
	struct lynx_pcs			**pcs;
	resource_size_t			switch_base;
	resource_size_t			imdio_base;
};

void vsc9959_pcs_link_state(struct ocelot *ocelot, int port,
			    struct phylink_link_state *state);
void vsc9959_pcs_config(struct ocelot *ocelot, int port,
			unsigned int link_an_mode,
			const struct phylink_link_state *state);
void vsc9959_pcs_link_up(struct ocelot *ocelot, int port,
			 unsigned int link_an_mode,
			 phy_interface_t interface,
			 int speed, int duplex);
void vsc9959_mdio_bus_free(struct ocelot *ocelot);

#endif
+20 −354
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot.h>
#include <linux/packing.h>
#include <linux/pcs-lynx.h>
#include <net/pkt_sched.h>
#include <linux/iopoll.h>
#include <linux/mdio.h>
@@ -766,347 +767,6 @@ static int vsc9959_reset(struct ocelot *ocelot)
	return 0;
}

/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the
 * device tree. If we are in MLO_AN_PHY mode, we program directly state->speed
 * into the PCS, which is retrieved out-of-band over MDIO. This also has the
 * benefit of working with SGMII fixed-links, like downstream switches, where
 * both link partners attempt to operate as AN slaves and therefore AN never
 * completes.  But it also has the disadvantage that some PHY chips don't pass
 * traffic if SGMII AN is enabled but not completed (acknowledged by us), so
 * setting MLO_AN_INBAND is actually required for those.
 */
static void vsc9959_pcs_config_sgmii(struct phy_device *pcs,
				     unsigned int link_an_mode,
				     const struct phylink_link_state *state)
{
	int bmsr, bmcr;

	/* Some PHYs like VSC8234 don't like it when AN restarts on
	 * their system  side and they restart line side AN too, going
	 * into an endless link up/down loop.  Don't restart PCS AN if
	 * link is up already.
	 * We do check that AN is enabled just in case this is the 1st
	 * call, PCS detects a carrier but AN is disabled from power on
	 * or by boot loader.
	 */
	bmcr = phy_read(pcs, MII_BMCR);
	if (bmcr < 0)
		return;

	bmsr = phy_read(pcs, MII_BMSR);
	if (bmsr < 0)
		return;

	if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS))
		return;

	/* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
	 * for the MAC PCS in order to acknowledge the AN.
	 */
	phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII |
				      ADVERTISE_LPACK);

	phy_write(pcs, ENETC_PCS_IF_MODE,
		  ENETC_PCS_IF_MODE_SGMII_EN |
		  ENETC_PCS_IF_MODE_USE_SGMII_AN);

	/* Adjust link timer for SGMII */
	phy_write(pcs, ENETC_PCS_LINK_TIMER1,
		  ENETC_PCS_LINK_TIMER1_VAL);
	phy_write(pcs, ENETC_PCS_LINK_TIMER2,
		  ENETC_PCS_LINK_TIMER2_VAL);

	phy_set_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}

static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs,
				       unsigned int link_an_mode,
				       const struct phylink_link_state *state)
{
	/* Configure device ability for the USXGMII Replicator */
	phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
		      MDIO_USXGMII_2500FULL |
		      MDIO_USXGMII_LINK |
		      ADVERTISE_SGMII |
		      ADVERTISE_LPACK);
}

void vsc9959_pcs_config(struct ocelot *ocelot, int port,
			unsigned int link_an_mode,
			const struct phylink_link_state *state)
{
	struct felix *felix = ocelot_to_felix(ocelot);
	struct phy_device *pcs = felix->pcs[port];

	if (!pcs)
		return;

	/* The PCS does not implement the BMSR register fully, so capability
	 * detection via genphy_read_abilities does not work. Since we can get
	 * the PHY config word from the LPA register though, there is still
	 * value in using the generic phy_resolve_aneg_linkmode function. So
	 * populate the supported and advertising link modes manually here.
	 */
	linkmode_set_bit_array(phy_basic_ports_array,
			       ARRAY_SIZE(phy_basic_ports_array),
			       pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, pcs->supported);
	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported);
	if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX ||
	    pcs->interface == PHY_INTERFACE_MODE_USXGMII)
		linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
				 pcs->supported);
	if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX)
		linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
				 pcs->supported);
	phy_advertise_supported(pcs);

	if (!phylink_autoneg_inband(link_an_mode))
		return;

	switch (pcs->interface) {
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_QSGMII:
		vsc9959_pcs_config_sgmii(pcs, link_an_mode, state);
		break;
	case PHY_INTERFACE_MODE_2500BASEX:
		phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n");
		break;
	case PHY_INTERFACE_MODE_USXGMII:
		vsc9959_pcs_config_usxgmii(pcs, link_an_mode, state);
		break;
	default:
		dev_err(ocelot->dev, "Unsupported link mode %s\n",
			phy_modes(pcs->interface));
	}
}

static void vsc9959_pcs_link_up_sgmii(struct phy_device *pcs,
				      unsigned int link_an_mode,
				      int speed, int duplex)
{
	u16 if_mode = ENETC_PCS_IF_MODE_SGMII_EN;

	switch (speed) {
	case SPEED_1000:
		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_1000);
		break;
	case SPEED_100:
		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_100);
		break;
	case SPEED_10:
		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_10);
		break;
	default:
		phydev_err(pcs, "Invalid PCS speed %d\n", speed);
		return;
	}

	if (duplex == DUPLEX_HALF)
		if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;

	phy_write(pcs, ENETC_PCS_IF_MODE, if_mode);
	phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}

/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
 * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
 * auto-negotiation of any link parameters. Electrically it is compatible with
 * a single lane of XAUI.
 * The hardware reference manual wants to call this mode SGMII, but it isn't
 * really, since the fundamental features of SGMII:
 * - Downgrading the link speed by duplicating symbols
 * - Auto-negotiation
 * are not there.
 * The speed is configured at 1000 in the IF_MODE and BMCR MDIO registers
 * because the clock frequency is actually given by a PLL configured in the
 * Reset Configuration Word (RCW).
 * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
 * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
 * lower link speed on line side, the system-side interface remains fixed at
 * 2500 Mbps and we do rate adaptation through pause frames.
 */
static void vsc9959_pcs_link_up_2500basex(struct phy_device *pcs,
					  unsigned int link_an_mode,
					  int speed, int duplex)
{
	u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) |
		      ENETC_PCS_IF_MODE_SGMII_EN;

	if (duplex == DUPLEX_HALF)
		if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF;

	phy_write(pcs, ENETC_PCS_IF_MODE, if_mode);
	phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE);
}

void vsc9959_pcs_link_up(struct ocelot *ocelot, int port,
			 unsigned int link_an_mode,
			 phy_interface_t interface,
			 int speed, int duplex)
{
	struct felix *felix = ocelot_to_felix(ocelot);
	struct phy_device *pcs = felix->pcs[port];

	if (!pcs)
		return;

	if (phylink_autoneg_inband(link_an_mode))
		return;

	switch (interface) {
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_QSGMII:
		vsc9959_pcs_link_up_sgmii(pcs, link_an_mode, speed, duplex);
		break;
	case PHY_INTERFACE_MODE_2500BASEX:
		vsc9959_pcs_link_up_2500basex(pcs, link_an_mode, speed,
					      duplex);
		break;
	case PHY_INTERFACE_MODE_USXGMII:
		phydev_err(pcs, "USXGMII only supports in-band AN for now\n");
		break;
	default:
		dev_err(ocelot->dev, "Unsupported link mode %s\n",
			phy_modes(pcs->interface));
	}
}

static void vsc9959_pcs_link_state_resolve(struct phy_device *pcs,
					   struct phylink_link_state *state)
{
	state->an_complete = pcs->autoneg_complete;
	state->an_enabled = pcs->autoneg;
	state->link = pcs->link;
	state->duplex = pcs->duplex;
	state->speed = pcs->speed;
	/* SGMII AN does not negotiate flow control, but that's ok,
	 * since phylink already knows that, and does:
	 *	link_state.pause |= pl->phy_state.pause;
	 */
	state->pause = MLO_PAUSE_NONE;

	phydev_dbg(pcs,
		   "mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n",
		   phy_modes(pcs->interface),
		   phy_speed_to_str(pcs->speed),
		   phy_duplex_to_str(pcs->duplex),
		   __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->advertising,
		   __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->lp_advertising,
		   pcs->link, pcs->autoneg, pcs->autoneg_complete);
}

static void vsc9959_pcs_link_state_sgmii(struct phy_device *pcs,
					 struct phylink_link_state *state)
{
	int err;

	err = genphy_update_link(pcs);
	if (err < 0)
		return;

	if (pcs->autoneg_complete) {
		u16 lpa = phy_read(pcs, MII_LPA);

		mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa);

		phy_resolve_aneg_linkmode(pcs);
	}
}

static void vsc9959_pcs_link_state_2500basex(struct phy_device *pcs,
					     struct phylink_link_state *state)
{
	int err;

	err = genphy_update_link(pcs);
	if (err < 0)
		return;

	pcs->speed = SPEED_2500;
	pcs->asym_pause = true;
	pcs->pause = true;
}

static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs,
					   struct phylink_link_state *state)
{
	int status, lpa;

	status = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_BMSR);
	if (status < 0)
		return;

	pcs->autoneg = true;
	pcs->autoneg_complete = !!(status & BMSR_ANEGCOMPLETE);
	pcs->link = !!(status & BMSR_LSTATUS);

	if (!pcs->link || !pcs->autoneg_complete)
		return;

	lpa = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_LPA);
	if (lpa < 0)
		return;

	switch (lpa & MDIO_USXGMII_SPD_MASK) {
	case MDIO_USXGMII_10:
		pcs->speed = SPEED_10;
		break;
	case MDIO_USXGMII_100:
		pcs->speed = SPEED_100;
		break;
	case MDIO_USXGMII_1000:
		pcs->speed = SPEED_1000;
		break;
	case MDIO_USXGMII_2500:
		pcs->speed = SPEED_2500;
		break;
	default:
		break;
	}

	if (lpa & MDIO_USXGMII_FULL_DUPLEX)
		pcs->duplex = DUPLEX_FULL;
	else
		pcs->duplex = DUPLEX_HALF;
}

void vsc9959_pcs_link_state(struct ocelot *ocelot, int port,
			    struct phylink_link_state *state)
{
	struct felix *felix = ocelot_to_felix(ocelot);
	struct phy_device *pcs = felix->pcs[port];

	if (!pcs)
		return;

	pcs->speed = SPEED_UNKNOWN;
	pcs->duplex = DUPLEX_UNKNOWN;
	pcs->pause = 0;
	pcs->asym_pause = 0;

	switch (pcs->interface) {
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_QSGMII:
		vsc9959_pcs_link_state_sgmii(pcs, state);
		break;
	case PHY_INTERFACE_MODE_2500BASEX:
		vsc9959_pcs_link_state_2500basex(pcs, state);
		break;
	case PHY_INTERFACE_MODE_USXGMII:
		vsc9959_pcs_link_state_usxgmii(pcs, state);
		break;
	default:
		return;
	}

	vsc9959_pcs_link_state_resolve(pcs, state);
}

static void vsc9959_phylink_validate(struct ocelot *ocelot, int port,
				     unsigned long *supported,
				     struct phylink_link_state *state)
@@ -1195,7 +855,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
	int rc;

	felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
				  sizeof(struct phy_device *),
				  sizeof(struct lynx_pcs *),
				  GFP_KERNEL);
	if (!felix->pcs) {
		dev_err(dev, "failed to allocate array for PCS PHYs\n");
@@ -1246,18 +906,26 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)

	for (port = 0; port < felix->info->num_ports; port++) {
		struct ocelot_port *ocelot_port = ocelot->ports[port];
		struct phy_device *pcs;
		bool is_c45 = false;
		struct mdio_device *pcs;
		struct lynx_pcs *lynx;

		if (dsa_is_unused_port(felix->ds, port))
			continue;

		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_USXGMII)
			is_c45 = true;
		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
			continue;

		pcs = get_phy_device(felix->imdio, port, is_c45);
		pcs = mdio_device_create(felix->imdio, port);
		if (IS_ERR(pcs))
			continue;

		pcs->interface = ocelot_port->phy_mode;
		felix->pcs[port] = pcs;
		lynx = lynx_pcs_create(pcs);
		if (!lynx) {
			mdio_device_free(pcs);
			continue;
		}

		felix->pcs[port] = lynx;

		dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
	}
@@ -1271,12 +939,13 @@ void vsc9959_mdio_bus_free(struct ocelot *ocelot)
	int port;

	for (port = 0; port < ocelot->num_phys_ports; port++) {
		struct phy_device *pcs = felix->pcs[port];
		struct lynx_pcs *pcs = felix->pcs[port];

		if (!pcs)
			continue;

		put_device(&pcs->mdio.dev);
		mdio_device_free(pcs->mdio);
		lynx_pcs_destroy(pcs);
	}
	mdiobus_unregister(felix->imdio);
}
@@ -1499,9 +1168,6 @@ static const struct felix_info felix_info_vsc9959 = {
	.imdio_pci_bar		= 0,
	.mdio_bus_alloc		= vsc9959_mdio_bus_alloc,
	.mdio_bus_free		= vsc9959_mdio_bus_free,
	.pcs_config		= vsc9959_pcs_config,
	.pcs_link_up		= vsc9959_pcs_link_up,
	.pcs_link_state		= vsc9959_pcs_link_state,
	.phylink_validate	= vsc9959_phylink_validate,
	.prevalidate_phy_mode	= vsc9959_prevalidate_phy_mode,
	.port_setup_tc          = vsc9959_port_setup_tc,
Loading