Merge branch 'net-pcs-xpcs-cleanups-batch-1'

Russell King says:

====================
net: pcs: xpcs: cleanups batch 1

First, sorry for the bland series subject - this is the first in a
number of cleanup series to the XPCS driver. This series has some
functional changes beyond merely cleanups, notably the first patch.

This series starts off with a patch that moves the PCS reset from
the xpcs_create*() family of calls to when phylink first configures
the PHY. The motivation for this change is to get rid of the
interface argument to the xpcs_create*() functions, which I see as
unnecessary complexity. This patch should be tested on Wangxun
and STMMAC drivers.

Patch 2 removes the now unnecessary interface argument from the
internal xpcs_create() and xpcs_init_iface() functions. With this,
xpcs_init_iface() becomes a misnamed function, but patch 3 removes
this function, moving its now meager contents to xpcs_create().

Patch 4 adds xpcs_destroy_pcs() and xpcs_create_pcs_mdiodev()
functions which return and take a phylink_pcs, allowing SJA1105
and Wangxun drivers to be converted to using the phylink_pcs
structure internally.

Patches 5 through 8 convert both these drivers to that end.

Patch 9 drops the interface argument from the remaining xpcs_create*()
functions, addressing the only remaining caller of these functions,
that being the STMMAC driver.

As patch 7 removed the direct calls to the XPCS config/link-up
functions, the last patch makes these functions static.
====================

Link: https://patch.msgid.link/ZvwdKIp3oYSenGdH@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-10-02 17:32:02 -07:00
commit 6b67e098c9
8 changed files with 132 additions and 116 deletions

View File

@ -278,7 +278,7 @@ struct sja1105_private {
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 phylink_pcs *pcs[SJA1105_MAX_NUM_PORTS];
struct sja1105_ptp_data ptp_data;
struct sja1105_tas_data tas_data;
};

View File

@ -15,7 +15,6 @@
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/netdev_features.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
@ -1257,29 +1256,11 @@ static int sja1105_parse_dt(struct sja1105_private *priv)
return rc;
}
/* Convert link speed from SJA1105 to ethtool encoding */
static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv,
u64 speed)
{
if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS])
return SPEED_10;
if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS])
return SPEED_100;
if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS])
return SPEED_1000;
if (speed == priv->info->port_speed[SJA1105_SPEED_2500MBPS])
return SPEED_2500;
return SPEED_UNKNOWN;
}
/* Set link speed in the MAC configuration for a specific port. */
static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
int speed_mbps)
static int sja1105_set_port_speed(struct sja1105_private *priv, int port,
int speed_mbps)
{
struct sja1105_mac_config_entry *mac;
struct device *dev = priv->ds->dev;
u64 speed;
int rc;
/* On P/Q/R/S, one can read from the device via the MAC reconfiguration
* tables. On E/T, MAC reconfig tables are not readable, only writable.
@ -1313,7 +1294,7 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
break;
default:
dev_err(dev, "Invalid speed %iMbps\n", speed_mbps);
dev_err(priv->ds->dev, "Invalid speed %iMbps\n", speed_mbps);
return -EINVAL;
}
@ -1325,11 +1306,31 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
* we need to configure the PCS only (if even that).
*/
if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
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;
speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
mac[port].speed = speed;
return 0;
}
/* Write the MAC Configuration Table entry and, if necessary, the CGU settings,
* after a link speedchange for this port.
*/
static int sja1105_set_port_config(struct sja1105_private *priv, int port)
{
struct sja1105_mac_config_entry *mac;
struct device *dev = priv->ds->dev;
int rc;
/* On P/Q/R/S, one can read from the device via the MAC reconfiguration
* tables. On E/T, MAC reconfig tables are not readable, only writable.
* We have to *know* what the MAC looks like. For the sake of keeping
* the code common, we'll use the static configuration tables as a
* reasonable approximation for both E/T and P/Q/R/S.
*/
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
/* Write to the dynamic reconfiguration tables */
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
@ -1356,12 +1357,8 @@ sja1105_mac_select_pcs(struct phylink_config *config, phy_interface_t iface)
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct sja1105_private *priv = dp->ds->priv;
struct dw_xpcs *xpcs = priv->xpcs[dp->index];
if (xpcs)
return &xpcs->pcs;
return NULL;
return priv->pcs[dp->index];
}
static void sja1105_mac_config(struct phylink_config *config,
@ -1390,7 +1387,8 @@ static void sja1105_mac_link_up(struct phylink_config *config,
struct sja1105_private *priv = dp->ds->priv;
int port = dp->index;
sja1105_adjust_port_config(priv, port, speed);
if (!sja1105_set_port_speed(priv, port, speed))
sja1105_set_port_config(priv, port);
sja1105_inhibit_tx(priv, BIT(port), false);
}
@ -2293,8 +2291,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
{
struct ptp_system_timestamp ptp_sts_before;
struct ptp_system_timestamp ptp_sts_after;
int speed_mbps[SJA1105_MAX_NUM_PORTS];
u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0};
u64 mac_speed[SJA1105_MAX_NUM_PORTS];
struct sja1105_mac_config_entry *mac;
struct dsa_switch *ds = priv->ds;
s64 t1, t2, t3, t4;
@ -2307,17 +2305,16 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
/* Back up the dynamic link speed changed by sja1105_adjust_port_config
/* Back up the dynamic link speed changed by sja1105_set_port_speed()
* in order to temporarily restore it to SJA1105_SPEED_AUTO - which the
* switch wants to see in the static config in order to allow us to
* change it through the dynamic interface later.
*/
for (i = 0; i < ds->num_ports; i++) {
speed_mbps[i] = sja1105_port_speed_to_ethtool(priv,
mac[i].speed);
mac_speed[i] = mac[i].speed;
mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO];
if (priv->xpcs[i])
if (priv->pcs[i])
bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i,
MDIO_MMD_VEND2, MDIO_CTRL1);
}
@ -2374,14 +2371,15 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
}
for (i = 0; i < ds->num_ports; i++) {
struct dw_xpcs *xpcs = priv->xpcs[i];
struct phylink_pcs *pcs = priv->pcs[i];
unsigned int neg_mode;
rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
mac[i].speed = mac_speed[i];
rc = sja1105_set_port_config(priv, i);
if (rc < 0)
goto out;
if (!xpcs)
if (!pcs)
continue;
if (bmcr[i] & BMCR_ANENABLE)
@ -2389,7 +2387,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
else
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode);
rc = pcs->ops->pcs_config(pcs, neg_mode, priv->phy_mode[i],
NULL, true);
if (rc < 0)
goto out;
@ -2405,8 +2404,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
else
speed = SPEED_10;
xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i],
speed, DUPLEX_FULL);
pcs->ops->pcs_link_up(pcs, neg_mode, priv->phy_mode[i],
speed, DUPLEX_FULL);
}
}

View File

@ -400,7 +400,7 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
}
for (port = 0; port < ds->num_ports; port++) {
struct dw_xpcs *xpcs;
struct phylink_pcs *pcs;
if (dsa_is_unused_port(ds, port))
continue;
@ -409,13 +409,13 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
continue;
xpcs = xpcs_create_mdiodev(bus, port, priv->phy_mode[port]);
if (IS_ERR(xpcs)) {
rc = PTR_ERR(xpcs);
pcs = xpcs_create_pcs_mdiodev(bus, port);
if (IS_ERR(pcs)) {
rc = PTR_ERR(pcs);
goto out_pcs_free;
}
priv->xpcs[port] = xpcs;
priv->pcs[port] = pcs;
}
priv->mdio_pcs = bus;
@ -424,11 +424,10 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
out_pcs_free:
for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port])
continue;
xpcs_destroy(priv->xpcs[port]);
priv->xpcs[port] = NULL;
if (priv->pcs[port]) {
xpcs_destroy_pcs(priv->pcs[port]);
priv->pcs[port] = NULL;
}
}
mdiobus_unregister(bus);
@ -446,11 +445,10 @@ static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
return;
for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port])
continue;
xpcs_destroy(priv->xpcs[port]);
priv->xpcs[port] = NULL;
if (priv->pcs[port]) {
xpcs_destroy_pcs(priv->pcs[port]);
priv->pcs[port] = NULL;
}
}
mdiobus_unregister(priv->mdio_pcs);

View File

@ -500,23 +500,22 @@ int stmmac_pcs_setup(struct net_device *ndev)
struct fwnode_handle *devnode, *pcsnode;
struct dw_xpcs *xpcs = NULL;
struct stmmac_priv *priv;
int addr, mode, ret;
int addr, ret;
priv = netdev_priv(ndev);
mode = priv->plat->phy_interface;
devnode = priv->plat->port_node;
if (priv->plat->pcs_init) {
ret = priv->plat->pcs_init(priv);
} else if (fwnode_property_present(devnode, "pcs-handle")) {
pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
xpcs = xpcs_create_fwnode(pcsnode, mode);
xpcs = xpcs_create_fwnode(pcsnode);
fwnode_handle_put(pcsnode);
ret = PTR_ERR_OR_ZERO(xpcs);
} else if (priv->plat->mdio_bus_data &&
priv->plat->mdio_bus_data->pcs_mask) {
addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
xpcs = xpcs_create_mdiodev(priv->mii, addr, mode);
xpcs = xpcs_create_mdiodev(priv->mii, addr);
ret = PTR_ERR_OR_ZERO(xpcs);
} else {
return 0;

View File

@ -122,7 +122,7 @@ static int txgbe_pcs_write(struct mii_bus *bus, int addr, int devnum, int regnum
static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
{
struct mii_bus *mii_bus;
struct dw_xpcs *xpcs;
struct phylink_pcs *pcs;
struct pci_dev *pdev;
struct wx *wx;
int ret = 0;
@ -147,11 +147,11 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
if (ret)
return ret;
xpcs = xpcs_create_mdiodev(mii_bus, 0, PHY_INTERFACE_MODE_10GBASER);
if (IS_ERR(xpcs))
return PTR_ERR(xpcs);
pcs = xpcs_create_pcs_mdiodev(mii_bus, 0);
if (IS_ERR(pcs))
return PTR_ERR(pcs);
txgbe->xpcs = xpcs;
txgbe->pcs = pcs;
return 0;
}
@ -163,7 +163,7 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi
struct txgbe *txgbe = wx->priv;
if (interface == PHY_INTERFACE_MODE_10GBASER)
return &txgbe->xpcs->pcs;
return txgbe->pcs;
return NULL;
}
@ -302,7 +302,7 @@ irqreturn_t txgbe_link_irq_handler(int irq, void *data)
status = rd32(wx, TXGBE_CFG_PORT_ST);
up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP);
phylink_pcs_change(&txgbe->xpcs->pcs, up);
phylink_pcs_change(txgbe->pcs, up);
return IRQ_HANDLED;
}
@ -778,7 +778,7 @@ int txgbe_init_phy(struct txgbe *txgbe)
err_destroy_phylink:
phylink_destroy(wx->phylink);
err_destroy_xpcs:
xpcs_destroy(txgbe->xpcs);
xpcs_destroy_pcs(txgbe->pcs);
err_unregister_swnode:
software_node_unregister_node_group(txgbe->nodes.group);
@ -798,6 +798,6 @@ void txgbe_remove_phy(struct txgbe *txgbe)
clkdev_drop(txgbe->clock);
clk_unregister(txgbe->clk);
phylink_destroy(txgbe->wx->phylink);
xpcs_destroy(txgbe->xpcs);
xpcs_destroy_pcs(txgbe->pcs);
software_node_unregister_node_group(txgbe->nodes.group);
}

View File

@ -329,7 +329,7 @@ struct txgbe {
struct wx *wx;
struct txgbe_nodes nodes;
struct txgbe_irq misc;
struct dw_xpcs *xpcs;
struct phylink_pcs *pcs;
struct platform_device *sfp_dev;
struct platform_device *i2c_dev;
struct clk_lookup *clock;

View File

@ -659,6 +659,30 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
}
EXPORT_SYMBOL_GPL(xpcs_config_eee);
static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)
{
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
const struct dw_xpcs_compat *compat;
int ret;
if (!xpcs->need_reset)
return;
compat = xpcs_find_compat(xpcs->desc, interface);
if (!compat) {
dev_err(&xpcs->mdiodev->dev, "unsupported interface %s\n",
phy_modes(interface));
return;
}
ret = xpcs_soft_reset(xpcs, compat);
if (ret)
dev_err(&xpcs->mdiodev->dev, "soft reset failed: %pe\n",
ERR_PTR(ret));
xpcs->need_reset = false;
}
static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
unsigned int neg_mode)
{
@ -827,8 +851,9 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret);
}
int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
const unsigned long *advertising, unsigned int neg_mode)
static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
const unsigned long *advertising,
unsigned int neg_mode)
{
const struct dw_xpcs_compat *compat;
int ret;
@ -881,7 +906,6 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
return 0;
}
EXPORT_SYMBOL_GPL(xpcs_do_config);
static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
@ -1183,8 +1207,8 @@ static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
}
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex)
static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex)
{
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
@ -1195,7 +1219,6 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
if (interface == PHY_INTERFACE_MODE_1000BASEX)
return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex);
}
EXPORT_SYMBOL_GPL(xpcs_link_up);
static void xpcs_an_restart(struct phylink_pcs *pcs)
{
@ -1365,6 +1388,7 @@ static const struct dw_xpcs_desc xpcs_desc_list[] = {
static const struct phylink_pcs_ops xpcs_phylink_ops = {
.pcs_validate = xpcs_validate,
.pcs_pre_config = xpcs_pre_config,
.pcs_config = xpcs_config,
.pcs_get_state = xpcs_get_state,
.pcs_an_restart = xpcs_an_restart,
@ -1458,24 +1482,7 @@ static int xpcs_init_id(struct dw_xpcs *xpcs)
return 0;
}
static int xpcs_init_iface(struct dw_xpcs *xpcs, phy_interface_t interface)
{
const struct dw_xpcs_compat *compat;
compat = xpcs_find_compat(xpcs->desc, interface);
if (!compat)
return -EINVAL;
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
xpcs->pcs.poll = false;
return 0;
}
return xpcs_soft_reset(xpcs, compat);
}
static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
phy_interface_t interface)
static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
{
struct dw_xpcs *xpcs;
int ret;
@ -1492,9 +1499,10 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
if (ret)
goto out_clear_clks;
ret = xpcs_init_iface(xpcs, interface);
if (ret)
goto out_clear_clks;
if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
xpcs->pcs.poll = false;
else
xpcs->need_reset = true;
return xpcs;
@ -1511,14 +1519,12 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
* xpcs_create_mdiodev() - create a DW xPCS instance with the MDIO @addr
* @bus: pointer to the MDIO-bus descriptor for the device to be looked at
* @addr: device MDIO-bus ID
* @interface: requested PHY interface
*
* Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if
* the PCS device couldn't be found on the bus and other negative errno related
* to the data allocation and MDIO-bus communications.
*/
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
phy_interface_t interface)
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)
{
struct mdio_device *mdiodev;
struct dw_xpcs *xpcs;
@ -1527,7 +1533,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
if (IS_ERR(mdiodev))
return ERR_CAST(mdiodev);
xpcs = xpcs_create(mdiodev, interface);
xpcs = xpcs_create(mdiodev);
/* xpcs_create() has taken a refcount on the mdiodev if it was
* successful. If xpcs_create() fails, this will free the mdio
@ -1541,10 +1547,21 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
}
EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr)
{
struct dw_xpcs *xpcs;
xpcs = xpcs_create_mdiodev(bus, addr);
if (IS_ERR(xpcs))
return ERR_CAST(xpcs);
return &xpcs->pcs;
}
EXPORT_SYMBOL_GPL(xpcs_create_pcs_mdiodev);
/**
* xpcs_create_fwnode() - Create a DW xPCS instance from @fwnode
* @fwnode: fwnode handle poining to the DW XPCS device
* @interface: requested PHY interface
*
* Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if
* the fwnode device is unavailable or the PCS device couldn't be found on the
@ -1552,8 +1569,7 @@ EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
* other negative errno related to the data allocations and MDIO-bus
* communications.
*/
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
phy_interface_t interface)
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode)
{
struct mdio_device *mdiodev;
struct dw_xpcs *xpcs;
@ -1565,7 +1581,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
if (!mdiodev)
return ERR_PTR(-EPROBE_DEFER);
xpcs = xpcs_create(mdiodev, interface);
xpcs = xpcs_create(mdiodev);
/* xpcs_create() has taken a refcount on the mdiodev if it was
* successful. If xpcs_create() fails, this will free the mdio
@ -1590,5 +1606,11 @@ void xpcs_destroy(struct dw_xpcs *xpcs)
}
EXPORT_SYMBOL_GPL(xpcs_destroy);
void xpcs_destroy_pcs(struct phylink_pcs *pcs)
{
xpcs_destroy(phylink_pcs_to_xpcs(pcs));
}
EXPORT_SYMBOL_GPL(xpcs_destroy_pcs);
MODULE_DESCRIPTION("Synopsys DesignWare XPCS library");
MODULE_LICENSE("GPL v2");

View File

@ -61,20 +61,18 @@ struct dw_xpcs {
struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
struct phylink_pcs pcs;
phy_interface_t interface;
bool need_reset;
};
int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex);
int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
const unsigned long *advertising, unsigned int neg_mode);
void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces);
int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
int enable);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
phy_interface_t interface);
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
phy_interface_t interface);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr);
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode);
void xpcs_destroy(struct dw_xpcs *xpcs);
struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr);
void xpcs_destroy_pcs(struct phylink_pcs *pcs);
#endif /* __LINUX_PCS_XPCS_H */