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

Merge branch 'mv88e6xxx-phylink_pcs'



Russell King says:

====================
Convert mv88e6xxx to phylink_pcs

This series (previously posted with further patches on the 26 June as
RFC) converts mv88e6xxx to phylink_pcs, and thus moves it from being
a pre-March 2020 legacy driver.

The first four patches lay the ground-work for the conversion by
adding four new methods to the phylink_pcs operations structure:

  pcs_enable() - called when the PCS is going to start to be used
  pcs_disable() - called when the PCS is no longer being used

  pcs_pre_config() - called before the MAC configuration method
  pcs_post_config() - called after the MAC configuration method
      Both of these are necessary for some of the mv88e639x
      workarounds.

We also add the ability to inform phylink of a change to the PCS
state without involving the MAC later, by providing
phylink_pcs_change() which takes a phylink_pcs structure rather than
a phylink structure. phylink maintains which instance the PCS is
conencted to, so internally it can do the right thing when the PCS
is in-use.

Then we provide some additional mdiobus and mdiodev accessors that
we will be using in the new PCS drivers.

The changes for mv88e6xxx follow, and the first one needs to be
explicitly pointed out - we (Andrew and myself) have both decided that
all possible approaches to maintaining backwards compatibility with DT
have been exhaused - everyone has some objection to everything that
has been proposed. So, after many years of trying, we have decided
that this is just an impossibility, and with this patch, we are now
intentionally and knowingly breaking any DT that does not specify the
CPU and DSA port fixed-link parameters. Hence why Andrew has recently
been submitting DT update patches. It is regrettable that it has come
to this.

Following this, we start preparing 88e6xxx for phylink_pcs conversion
by padding the mac_select_pcs() DSA method, and the internal hooks to
create and tear-down PCS instances. Rather than bloat the already very
large mv88e6xxx_ops structure, I decided that it would be better that
the new internal chip specific PCS methods are all grouped within their
own structure - and this structure can be declared in the PCS drivers
themselves.

Then we have the actual conversion patches, one for each family of PCS.

Lastly, we clean up the driver after conversion, removing all the now
redundant code.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6963e463 d20acfdd
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -9,6 +9,9 @@ mv88e6xxx-objs += global2.o
mv88e6xxx-objs += global2_avb.o
mv88e6xxx-objs += global2_scratch.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
mv88e6xxx-objs += pcs-6185.o
mv88e6xxx-objs += pcs-6352.o
mv88e6xxx-objs += pcs-639x.o
mv88e6xxx-objs += phy.o
mv88e6xxx-objs += port.o
mv88e6xxx-objs += port_hidden.o
+54 −374

File changed.

Preview size limit exceeded, changes collapsed.

+12 −21
Original line number Diff line number Diff line
@@ -205,6 +205,7 @@ struct mv88e6xxx_irq_ops;
struct mv88e6xxx_gpio_ops;
struct mv88e6xxx_avb_ops;
struct mv88e6xxx_ptp_ops;
struct mv88e6xxx_pcs_ops;

struct mv88e6xxx_irq {
	u16 masked;
@@ -285,9 +286,8 @@ struct mv88e6xxx_port {
	u8 cmode;
	bool mirror_ingress;
	bool mirror_egress;
	unsigned int serdes_irq;
	char serdes_irq_name[64];
	struct devlink_region *region;
	void *pcs_private;

	/* MacAuth Bypass control flag */
	bool mab;
@@ -590,31 +590,12 @@ struct mv88e6xxx_ops {

	int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);

	/* Power on/off a SERDES interface */
	int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, int lane,
			    bool up);

	/* SERDES lane mapping */
	int (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);

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

	/* SERDES interrupt handling */
	unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
					   int port);
	int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, int lane,
				 bool enable);
	irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port,
					 int lane);

	/* Statistics from the SERDES interface */
	int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
@@ -664,6 +645,8 @@ struct mv88e6xxx_ops {
	void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port,
				 struct phylink_config *config);

	const struct mv88e6xxx_pcs_ops *pcs_ops;

	/* Max Frame Size */
	int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu);
};
@@ -736,6 +719,14 @@ struct mv88e6xxx_ptp_ops {
	u32 cc_mult_dem;
};

struct mv88e6xxx_pcs_ops {
	int (*pcs_init)(struct mv88e6xxx_chip *chip, int port);
	void (*pcs_teardown)(struct mv88e6xxx_chip *chip, int port);
	struct phylink_pcs *(*pcs_select)(struct mv88e6xxx_chip *chip, int port,
					  phy_interface_t mode);

};

#define STATS_TYPE_PORT		BIT(0)
#define STATS_TYPE_BANK0	BIT(1)
#define STATS_TYPE_BANK1	BIT(2)
+190 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Marvell 88E6185 family SERDES PCS support
 *
 * Copyright (c) 2008 Marvell Semiconductor
 *
 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
 */
#include <linux/phylink.h>

#include "global2.h"
#include "port.h"
#include "serdes.h"

struct mv88e6185_pcs {
	struct phylink_pcs phylink_pcs;
	unsigned int irq;
	char name[64];

	struct mv88e6xxx_chip *chip;
	int port;
};

static struct mv88e6185_pcs *pcs_to_mv88e6185_pcs(struct phylink_pcs *pcs)
{
	return container_of(pcs, struct mv88e6185_pcs, phylink_pcs);
}

static irqreturn_t mv88e6185_pcs_handle_irq(int irq, void *dev_id)
{
	struct mv88e6185_pcs *mpcs = dev_id;
	struct mv88e6xxx_chip *chip;
	irqreturn_t ret = IRQ_NONE;
	bool link_up;
	u16 status;
	int port;
	int err;

	chip = mpcs->chip;
	port = mpcs->port;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	mv88e6xxx_reg_unlock(chip);

	if (!err) {
		link_up = !!(status & MV88E6XXX_PORT_STS_LINK);

		phylink_pcs_change(&mpcs->phylink_pcs, link_up);

		ret = IRQ_HANDLED;
	}

	return ret;
}

static void mv88e6185_pcs_get_state(struct phylink_pcs *pcs,
				    struct phylink_link_state *state)
{
	struct mv88e6185_pcs *mpcs = pcs_to_mv88e6185_pcs(pcs);
	struct mv88e6xxx_chip *chip = mpcs->chip;
	int port = mpcs->port;
	u16 status;
	int err;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	mv88e6xxx_reg_unlock(chip);

	if (err)
		status = 0;

	state->link = !!(status & MV88E6XXX_PORT_STS_LINK);
	if (state->link) {
		state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ?
			DUPLEX_FULL : DUPLEX_HALF;

		switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) {
		case MV88E6XXX_PORT_STS_SPEED_1000:
			state->speed = SPEED_1000;
			break;

		case MV88E6XXX_PORT_STS_SPEED_100:
			state->speed = SPEED_100;
			break;

		case MV88E6XXX_PORT_STS_SPEED_10:
			state->speed = SPEED_10;
			break;

		default:
			state->link = false;
			break;
		}
	}
}

static int mv88e6185_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
				phy_interface_t interface,
				const unsigned long *advertising,
				bool permit_pause_to_mac)
{
	return 0;
}

static void mv88e6185_pcs_an_restart(struct phylink_pcs *pcs)
{
}

static const struct phylink_pcs_ops mv88e6185_phylink_pcs_ops = {
	.pcs_get_state = mv88e6185_pcs_get_state,
	.pcs_config = mv88e6185_pcs_config,
	.pcs_an_restart = mv88e6185_pcs_an_restart,
};

static int mv88e6185_pcs_init(struct mv88e6xxx_chip *chip, int port)
{
	struct mv88e6185_pcs *mpcs;
	struct device *dev;
	unsigned int irq;
	int err;

	/* There are no configurable serdes lanes on this switch chip, so
	 * we use the static cmode configuration to determine whether we
	 * have a PCS or not.
	 */
	if (chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_SERDES &&
	    chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_1000BASE_X)
		return 0;

	dev = chip->dev;

	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
	if (!mpcs)
		return -ENOMEM;

	mpcs->chip = chip;
	mpcs->port = port;
	mpcs->phylink_pcs.ops = &mv88e6185_phylink_pcs_ops;

	irq = mv88e6xxx_serdes_irq_mapping(chip, port);
	if (irq) {
		snprintf(mpcs->name, sizeof(mpcs->name),
			 "mv88e6xxx-%s-serdes-%d", dev_name(dev), port);

		err = request_threaded_irq(irq, NULL, mv88e6185_pcs_handle_irq,
					   IRQF_ONESHOT, mpcs->name, mpcs);
		if (err) {
			kfree(mpcs);
			return err;
		}

		mpcs->irq = irq;
	} else {
		mpcs->phylink_pcs.poll = true;
	}

	chip->ports[port].pcs_private = &mpcs->phylink_pcs;

	return 0;
}

static void mv88e6185_pcs_teardown(struct mv88e6xxx_chip *chip, int port)
{
	struct mv88e6185_pcs *mpcs;

	mpcs = chip->ports[port].pcs_private;
	if (!mpcs)
		return;

	if (mpcs->irq)
		free_irq(mpcs->irq, mpcs);

	kfree(mpcs);

	chip->ports[port].pcs_private = NULL;
}

static struct phylink_pcs *mv88e6185_pcs_select(struct mv88e6xxx_chip *chip,
						int port,
						phy_interface_t interface)
{
	return chip->ports[port].pcs_private;
}

const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops = {
	.pcs_init = mv88e6185_pcs_init,
	.pcs_teardown = mv88e6185_pcs_teardown,
	.pcs_select = mv88e6185_pcs_select,
};
+390 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Marvell 88E6352 family SERDES PCS support
 *
 * Copyright (c) 2008 Marvell Semiconductor
 *
 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
 */
#include <linux/phylink.h>

#include "global2.h"
#include "port.h"
#include "serdes.h"

/* Definitions from drivers/net/phy/marvell.c, which would be good to reuse. */
#define MII_M1011_PHY_STATUS		17
#define MII_M1011_IMASK			18
#define MII_M1011_IMASK_LINK_CHANGE	BIT(10)
#define MII_M1011_IEVENT		19
#define MII_M1011_IEVENT_LINK_CHANGE	BIT(10)
#define MII_MARVELL_PHY_PAGE		22
#define MII_MARVELL_FIBER_PAGE		1

struct marvell_c22_pcs {
	struct mdio_device mdio;
	struct phylink_pcs phylink_pcs;
	unsigned int irq;
	char name[64];
	bool (*link_check)(struct marvell_c22_pcs *mpcs);
	struct mv88e6xxx_port *port;
};

static struct marvell_c22_pcs *pcs_to_marvell_c22_pcs(struct phylink_pcs *pcs)
{
	return container_of(pcs, struct marvell_c22_pcs, phylink_pcs);
}

static int marvell_c22_pcs_set_fiber_page(struct marvell_c22_pcs *mpcs)
{
	u16 page;
	int err;

	mutex_lock(&mpcs->mdio.bus->mdio_lock);

	err = __mdiodev_read(&mpcs->mdio, MII_MARVELL_PHY_PAGE);
	if (err < 0) {
		dev_err(mpcs->mdio.dev.parent,
			"%s: can't read Serdes page register: %pe\n",
			mpcs->name, ERR_PTR(err));
		return err;
	}

	page = err;

	err = __mdiodev_write(&mpcs->mdio, MII_MARVELL_PHY_PAGE,
			      MII_MARVELL_FIBER_PAGE);
	if (err) {
		dev_err(mpcs->mdio.dev.parent,
			"%s: can't set Serdes page register: %pe\n",
			mpcs->name, ERR_PTR(err));
		return err;
	}

	return page;
}

static int marvell_c22_pcs_restore_page(struct marvell_c22_pcs *mpcs,
					int oldpage, int ret)
{
	int err;

	if (oldpage >= 0) {
		err = __mdiodev_write(&mpcs->mdio, MII_MARVELL_PHY_PAGE,
				      oldpage);
		if (err)
			dev_err(mpcs->mdio.dev.parent,
				"%s: can't restore Serdes page register: %pe\n",
				mpcs->name, ERR_PTR(err));
		if (!err || ret < 0)
			err = ret;
	} else {
		err = oldpage;
	}
	mutex_unlock(&mpcs->mdio.bus->mdio_lock);

	return err;
}

static irqreturn_t marvell_c22_pcs_handle_irq(int irq, void *dev_id)
{
	struct marvell_c22_pcs *mpcs = dev_id;
	irqreturn_t status = IRQ_NONE;
	int err, oldpage;

	oldpage = marvell_c22_pcs_set_fiber_page(mpcs);
	if (oldpage < 0)
		goto fail;

	err = __mdiodev_read(&mpcs->mdio, MII_M1011_IEVENT);
	if (err >= 0 && err & MII_M1011_IEVENT_LINK_CHANGE) {
		phylink_pcs_change(&mpcs->phylink_pcs, true);
		status = IRQ_HANDLED;
	}

fail:
	marvell_c22_pcs_restore_page(mpcs, oldpage, 0);

	return status;
}

static int marvell_c22_pcs_modify(struct marvell_c22_pcs *mpcs, u8 reg,
				  u16 mask, u16 val)
{
	int oldpage, err = 0;

	oldpage = marvell_c22_pcs_set_fiber_page(mpcs);
	if (oldpage >= 0)
		err = __mdiodev_modify(&mpcs->mdio, reg, mask, val);

	return marvell_c22_pcs_restore_page(mpcs, oldpage, err);
}

static int marvell_c22_pcs_power(struct marvell_c22_pcs *mpcs,
				 bool on)
{
	u16 val = on ? 0 : BMCR_PDOWN;

	return marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_PDOWN, val);
}

static int marvell_c22_pcs_control_irq(struct marvell_c22_pcs *mpcs,
				       bool enable)
{
	u16 val = enable ? MII_M1011_IMASK_LINK_CHANGE : 0;

	return marvell_c22_pcs_modify(mpcs, MII_M1011_IMASK,
				      MII_M1011_IMASK_LINK_CHANGE, val);
}

static int marvell_c22_pcs_enable(struct phylink_pcs *pcs)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);
	int err;

	err = marvell_c22_pcs_power(mpcs, true);
	if (err)
		return err;

	return marvell_c22_pcs_control_irq(mpcs, !!mpcs->irq);
}

static void marvell_c22_pcs_disable(struct phylink_pcs *pcs)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);

	marvell_c22_pcs_control_irq(mpcs, false);
	marvell_c22_pcs_power(mpcs, false);
}

static void marvell_c22_pcs_get_state(struct phylink_pcs *pcs,
				      struct phylink_link_state *state)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);
	int oldpage, bmsr, lpa, status;

	state->link = false;

	if (mpcs->link_check && !mpcs->link_check(mpcs))
		return;

	oldpage = marvell_c22_pcs_set_fiber_page(mpcs);
	if (oldpage >= 0) {
		bmsr = __mdiodev_read(&mpcs->mdio, MII_BMSR);
		lpa = __mdiodev_read(&mpcs->mdio, MII_LPA);
		status = __mdiodev_read(&mpcs->mdio, MII_M1011_PHY_STATUS);
	}

	if (marvell_c22_pcs_restore_page(mpcs, oldpage, 0) >= 0 &&
	    bmsr >= 0 && lpa >= 0 && status >= 0)
		mv88e6xxx_pcs_decode_state(mpcs->mdio.dev.parent, bmsr, lpa,
					   status, state);
}

static int marvell_c22_pcs_config(struct phylink_pcs *pcs,
				  unsigned int neg_mode,
				  phy_interface_t interface,
				  const unsigned long *advertising,
				  bool permit_pause_to_mac)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);
	int oldpage, adv, err, ret = 0;
	u16 bmcr;

	adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising);
	if (adv < 0)
		return 0;

	bmcr = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? BMCR_ANENABLE : 0;

	oldpage = marvell_c22_pcs_set_fiber_page(mpcs);
	if (oldpage < 0)
		goto restore;

	err = __mdiodev_modify_changed(&mpcs->mdio, MII_ADVERTISE, 0xffff, adv);
	ret = err;
	if (err < 0)
		goto restore;

	err = __mdiodev_modify_changed(&mpcs->mdio, MII_BMCR, BMCR_ANENABLE,
				       bmcr);
	if (err < 0) {
		ret = err;
		goto restore;
	}

	/* If the ANENABLE bit was changed, the PHY will restart negotiation,
	 * so we don't need to flag a change to trigger its own restart.
	 */
	if (err)
		ret = 0;

restore:
	return marvell_c22_pcs_restore_page(mpcs, oldpage, ret);
}

static void marvell_c22_pcs_an_restart(struct phylink_pcs *pcs)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);

	marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_ANRESTART, BMCR_ANRESTART);
}

static void marvell_c22_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
				    phy_interface_t interface, int speed,
				    int duplex)
{
	struct marvell_c22_pcs *mpcs = pcs_to_marvell_c22_pcs(pcs);
	u16 bmcr;
	int err;

	if (phylink_autoneg_inband(mode))
		return;

	bmcr = mii_bmcr_encode_fixed(speed, duplex);

	err = marvell_c22_pcs_modify(mpcs, MII_BMCR, BMCR_SPEED100 |
				     BMCR_FULLDPLX | BMCR_SPEED1000, bmcr);
	if (err)
		dev_err(mpcs->mdio.dev.parent,
			"%s: failed to configure mpcs: %pe\n", mpcs->name,
			ERR_PTR(err));
}

static const struct phylink_pcs_ops marvell_c22_pcs_ops = {
	.pcs_enable = marvell_c22_pcs_enable,
	.pcs_disable = marvell_c22_pcs_disable,
	.pcs_get_state = marvell_c22_pcs_get_state,
	.pcs_config = marvell_c22_pcs_config,
	.pcs_an_restart = marvell_c22_pcs_an_restart,
	.pcs_link_up = marvell_c22_pcs_link_up,
};

static struct marvell_c22_pcs *marvell_c22_pcs_alloc(struct device *dev,
						     struct mii_bus *bus,
						     unsigned int addr)
{
	struct marvell_c22_pcs *mpcs;

	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
	if (!mpcs)
		return NULL;

	mpcs->mdio.dev.parent = dev;
	mpcs->mdio.bus = bus;
	mpcs->mdio.addr = addr;
	mpcs->phylink_pcs.ops = &marvell_c22_pcs_ops;
	mpcs->phylink_pcs.neg_mode = true;

	return mpcs;
}

static int marvell_c22_pcs_setup_irq(struct marvell_c22_pcs *mpcs,
				     unsigned int irq)
{
	int err;

	mpcs->phylink_pcs.poll = !irq;
	mpcs->irq = irq;

	if (irq) {
		err = request_threaded_irq(irq, NULL,
					   marvell_c22_pcs_handle_irq,
					   IRQF_ONESHOT, mpcs->name, mpcs);
		if (err)
			return err;
	}

	return 0;
}

/* mv88e6352 specifics */

static bool mv88e6352_pcs_link_check(struct marvell_c22_pcs *mpcs)
{
	struct mv88e6xxx_port *port = mpcs->port;
	struct mv88e6xxx_chip *chip = port->chip;
	u8 cmode;

	/* Port 4 can be in auto-media mode. Check that the port is
	 * associated with the mpcs.
	 */
	mv88e6xxx_reg_lock(chip);
	chip->info->ops->port_get_cmode(chip, port->port, &cmode);
	mv88e6xxx_reg_unlock(chip);

	return cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX ||
	       cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
	       cmode == MV88E6XXX_PORT_STS_CMODE_SGMII;
}

static int mv88e6352_pcs_init(struct mv88e6xxx_chip *chip, int port)
{
	struct marvell_c22_pcs *mpcs;
	struct mii_bus *bus;
	struct device *dev;
	unsigned int irq;
	int err;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
	mv88e6xxx_reg_unlock(chip);
	if (err <= 0)
		return err;

	irq = mv88e6xxx_serdes_irq_mapping(chip, port);
	bus = mv88e6xxx_default_mdio_bus(chip);
	dev = chip->dev;

	mpcs = marvell_c22_pcs_alloc(dev, bus, MV88E6352_ADDR_SERDES);
	if (!mpcs)
		return -ENOMEM;

	snprintf(mpcs->name, sizeof(mpcs->name),
		 "mv88e6xxx-%s-serdes-%d", dev_name(dev), port);

	mpcs->link_check = mv88e6352_pcs_link_check;
	mpcs->port = &chip->ports[port];

	err = marvell_c22_pcs_setup_irq(mpcs, irq);
	if (err) {
		kfree(mpcs);
		return err;
	}

	chip->ports[port].pcs_private = &mpcs->phylink_pcs;

	return 0;
}

static void mv88e6352_pcs_teardown(struct mv88e6xxx_chip *chip, int port)
{
	struct marvell_c22_pcs *mpcs;
	struct phylink_pcs *pcs;

	pcs = chip->ports[port].pcs_private;
	if (!pcs)
		return;

	mpcs = pcs_to_marvell_c22_pcs(pcs);

	if (mpcs->irq)
		free_irq(mpcs->irq, mpcs);

	kfree(mpcs);

	chip->ports[port].pcs_private = NULL;
}

static struct phylink_pcs *mv88e6352_pcs_select(struct mv88e6xxx_chip *chip,
						int port,
						phy_interface_t interface)
{
	return chip->ports[port].pcs_private;
}

const struct mv88e6xxx_pcs_ops mv88e6352_pcs_ops = {
	.pcs_init = mv88e6352_pcs_init,
	.pcs_teardown = mv88e6352_pcs_teardown,
	.pcs_select = mv88e6352_pcs_select,
};
Loading