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_t1;
struct mii_bus *mdio_base_tx; struct mii_bus *mdio_base_tx;
struct mii_bus *mdio_pcs; 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_ptp_data ptp_data;
struct sja1105_tas_data tas_data; struct sja1105_tas_data tas_data;
}; };

View File

@ -15,7 +15,6 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/netdev_features.h> #include <linux/netdev_features.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
@ -1257,29 +1256,11 @@ static int sja1105_parse_dt(struct sja1105_private *priv)
return rc; return rc;
} }
/* Convert link speed from SJA1105 to ethtool encoding */ static int sja1105_set_port_speed(struct sja1105_private *priv, int port,
static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv, int speed_mbps)
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)
{ {
struct sja1105_mac_config_entry *mac; struct sja1105_mac_config_entry *mac;
struct device *dev = priv->ds->dev;
u64 speed; u64 speed;
int rc;
/* On P/Q/R/S, one can read from the device via the MAC reconfiguration /* 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. * 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]; speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
break; break;
default: default:
dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); dev_err(priv->ds->dev, "Invalid speed %iMbps\n", speed_mbps);
return -EINVAL; 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). * we need to configure the PCS only (if even that).
*/ */
if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII) 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) else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
else
mac[port].speed = speed; 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 */ /* Write to the dynamic reconfiguration tables */
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 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 dsa_port *dp = dsa_phylink_to_port(config);
struct sja1105_private *priv = dp->ds->priv; struct sja1105_private *priv = dp->ds->priv;
struct dw_xpcs *xpcs = priv->xpcs[dp->index];
if (xpcs) return priv->pcs[dp->index];
return &xpcs->pcs;
return NULL;
} }
static void sja1105_mac_config(struct phylink_config *config, 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; struct sja1105_private *priv = dp->ds->priv;
int port = dp->index; 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); 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_before;
struct ptp_system_timestamp ptp_sts_after; struct ptp_system_timestamp ptp_sts_after;
int speed_mbps[SJA1105_MAX_NUM_PORTS];
u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0}; u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0};
u64 mac_speed[SJA1105_MAX_NUM_PORTS];
struct sja1105_mac_config_entry *mac; struct sja1105_mac_config_entry *mac;
struct dsa_switch *ds = priv->ds; struct dsa_switch *ds = priv->ds;
s64 t1, t2, t3, t4; 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; 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 * 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 * switch wants to see in the static config in order to allow us to
* change it through the dynamic interface later. * change it through the dynamic interface later.
*/ */
for (i = 0; i < ds->num_ports; i++) { for (i = 0; i < ds->num_ports; i++) {
speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, mac_speed[i] = mac[i].speed;
mac[i].speed);
mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; 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, bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i,
MDIO_MMD_VEND2, MDIO_CTRL1); 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++) { 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; 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) if (rc < 0)
goto out; goto out;
if (!xpcs) if (!pcs)
continue; continue;
if (bmcr[i] & BMCR_ANENABLE) if (bmcr[i] & BMCR_ANENABLE)
@ -2389,7 +2387,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
else else
neg_mode = PHYLINK_PCS_NEG_OUTBAND; 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) if (rc < 0)
goto out; goto out;
@ -2405,8 +2404,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
else else
speed = SPEED_10; speed = SPEED_10;
xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i], pcs->ops->pcs_link_up(pcs, neg_mode, priv->phy_mode[i],
speed, DUPLEX_FULL); 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++) { for (port = 0; port < ds->num_ports; port++) {
struct dw_xpcs *xpcs; struct phylink_pcs *pcs;
if (dsa_is_unused_port(ds, port)) if (dsa_is_unused_port(ds, port))
continue; continue;
@ -409,13 +409,13 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX) priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
continue; continue;
xpcs = xpcs_create_mdiodev(bus, port, priv->phy_mode[port]); pcs = xpcs_create_pcs_mdiodev(bus, port);
if (IS_ERR(xpcs)) { if (IS_ERR(pcs)) {
rc = PTR_ERR(xpcs); rc = PTR_ERR(pcs);
goto out_pcs_free; goto out_pcs_free;
} }
priv->xpcs[port] = xpcs; priv->pcs[port] = pcs;
} }
priv->mdio_pcs = bus; priv->mdio_pcs = bus;
@ -424,11 +424,10 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
out_pcs_free: out_pcs_free:
for (port = 0; port < ds->num_ports; port++) { for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port]) if (priv->pcs[port]) {
continue; xpcs_destroy_pcs(priv->pcs[port]);
priv->pcs[port] = NULL;
xpcs_destroy(priv->xpcs[port]); }
priv->xpcs[port] = NULL;
} }
mdiobus_unregister(bus); mdiobus_unregister(bus);
@ -446,11 +445,10 @@ static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
return; return;
for (port = 0; port < ds->num_ports; port++) { for (port = 0; port < ds->num_ports; port++) {
if (!priv->xpcs[port]) if (priv->pcs[port]) {
continue; xpcs_destroy_pcs(priv->pcs[port]);
priv->pcs[port] = NULL;
xpcs_destroy(priv->xpcs[port]); }
priv->xpcs[port] = NULL;
} }
mdiobus_unregister(priv->mdio_pcs); 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 fwnode_handle *devnode, *pcsnode;
struct dw_xpcs *xpcs = NULL; struct dw_xpcs *xpcs = NULL;
struct stmmac_priv *priv; struct stmmac_priv *priv;
int addr, mode, ret; int addr, ret;
priv = netdev_priv(ndev); priv = netdev_priv(ndev);
mode = priv->plat->phy_interface;
devnode = priv->plat->port_node; devnode = priv->plat->port_node;
if (priv->plat->pcs_init) { if (priv->plat->pcs_init) {
ret = priv->plat->pcs_init(priv); ret = priv->plat->pcs_init(priv);
} else if (fwnode_property_present(devnode, "pcs-handle")) { } else if (fwnode_property_present(devnode, "pcs-handle")) {
pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0); pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
xpcs = xpcs_create_fwnode(pcsnode, mode); xpcs = xpcs_create_fwnode(pcsnode);
fwnode_handle_put(pcsnode); fwnode_handle_put(pcsnode);
ret = PTR_ERR_OR_ZERO(xpcs); ret = PTR_ERR_OR_ZERO(xpcs);
} else if (priv->plat->mdio_bus_data && } else if (priv->plat->mdio_bus_data &&
priv->plat->mdio_bus_data->pcs_mask) { priv->plat->mdio_bus_data->pcs_mask) {
addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1; 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); ret = PTR_ERR_OR_ZERO(xpcs);
} else { } else {
return 0; 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) static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
{ {
struct mii_bus *mii_bus; struct mii_bus *mii_bus;
struct dw_xpcs *xpcs; struct phylink_pcs *pcs;
struct pci_dev *pdev; struct pci_dev *pdev;
struct wx *wx; struct wx *wx;
int ret = 0; int ret = 0;
@ -147,11 +147,11 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
if (ret) if (ret)
return ret; return ret;
xpcs = xpcs_create_mdiodev(mii_bus, 0, PHY_INTERFACE_MODE_10GBASER); pcs = xpcs_create_pcs_mdiodev(mii_bus, 0);
if (IS_ERR(xpcs)) if (IS_ERR(pcs))
return PTR_ERR(xpcs); return PTR_ERR(pcs);
txgbe->xpcs = xpcs; txgbe->pcs = pcs;
return 0; return 0;
} }
@ -163,7 +163,7 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi
struct txgbe *txgbe = wx->priv; struct txgbe *txgbe = wx->priv;
if (interface == PHY_INTERFACE_MODE_10GBASER) if (interface == PHY_INTERFACE_MODE_10GBASER)
return &txgbe->xpcs->pcs; return txgbe->pcs;
return NULL; return NULL;
} }
@ -302,7 +302,7 @@ irqreturn_t txgbe_link_irq_handler(int irq, void *data)
status = rd32(wx, TXGBE_CFG_PORT_ST); status = rd32(wx, TXGBE_CFG_PORT_ST);
up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP); up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP);
phylink_pcs_change(&txgbe->xpcs->pcs, up); phylink_pcs_change(txgbe->pcs, up);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -778,7 +778,7 @@ int txgbe_init_phy(struct txgbe *txgbe)
err_destroy_phylink: err_destroy_phylink:
phylink_destroy(wx->phylink); phylink_destroy(wx->phylink);
err_destroy_xpcs: err_destroy_xpcs:
xpcs_destroy(txgbe->xpcs); xpcs_destroy_pcs(txgbe->pcs);
err_unregister_swnode: err_unregister_swnode:
software_node_unregister_node_group(txgbe->nodes.group); software_node_unregister_node_group(txgbe->nodes.group);
@ -798,6 +798,6 @@ void txgbe_remove_phy(struct txgbe *txgbe)
clkdev_drop(txgbe->clock); clkdev_drop(txgbe->clock);
clk_unregister(txgbe->clk); clk_unregister(txgbe->clk);
phylink_destroy(txgbe->wx->phylink); phylink_destroy(txgbe->wx->phylink);
xpcs_destroy(txgbe->xpcs); xpcs_destroy_pcs(txgbe->pcs);
software_node_unregister_node_group(txgbe->nodes.group); software_node_unregister_node_group(txgbe->nodes.group);
} }

View File

@ -329,7 +329,7 @@ struct txgbe {
struct wx *wx; struct wx *wx;
struct txgbe_nodes nodes; struct txgbe_nodes nodes;
struct txgbe_irq misc; struct txgbe_irq misc;
struct dw_xpcs *xpcs; struct phylink_pcs *pcs;
struct platform_device *sfp_dev; struct platform_device *sfp_dev;
struct platform_device *i2c_dev; struct platform_device *i2c_dev;
struct clk_lookup *clock; 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); 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, static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
unsigned int neg_mode) 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); 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, static int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
const unsigned long *advertising, unsigned int neg_mode) const unsigned long *advertising,
unsigned int neg_mode)
{ {
const struct dw_xpcs_compat *compat; const struct dw_xpcs_compat *compat;
int ret; int ret;
@ -881,7 +906,6 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(xpcs_do_config);
static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, 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)); pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
} }
void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex) phy_interface_t interface, int speed, int duplex)
{ {
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 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) if (interface == PHY_INTERFACE_MODE_1000BASEX)
return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex); 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) 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 = { static const struct phylink_pcs_ops xpcs_phylink_ops = {
.pcs_validate = xpcs_validate, .pcs_validate = xpcs_validate,
.pcs_pre_config = xpcs_pre_config,
.pcs_config = xpcs_config, .pcs_config = xpcs_config,
.pcs_get_state = xpcs_get_state, .pcs_get_state = xpcs_get_state,
.pcs_an_restart = xpcs_an_restart, .pcs_an_restart = xpcs_an_restart,
@ -1458,24 +1482,7 @@ static int xpcs_init_id(struct dw_xpcs *xpcs)
return 0; return 0;
} }
static int xpcs_init_iface(struct dw_xpcs *xpcs, phy_interface_t interface) static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
{
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)
{ {
struct dw_xpcs *xpcs; struct dw_xpcs *xpcs;
int ret; int ret;
@ -1492,9 +1499,10 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
if (ret) if (ret)
goto out_clear_clks; goto out_clear_clks;
ret = xpcs_init_iface(xpcs, interface); if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
if (ret) xpcs->pcs.poll = false;
goto out_clear_clks; else
xpcs->need_reset = true;
return xpcs; 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 * 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 * @bus: pointer to the MDIO-bus descriptor for the device to be looked at
* @addr: device MDIO-bus ID * @addr: device MDIO-bus ID
* @interface: requested PHY interface
* *
* Return: a pointer to the DW XPCS handle if successful, otherwise -ENODEV if * 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 * the PCS device couldn't be found on the bus and other negative errno related
* to the data allocation and MDIO-bus communications. * to the data allocation and MDIO-bus communications.
*/ */
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr)
phy_interface_t interface)
{ {
struct mdio_device *mdiodev; struct mdio_device *mdiodev;
struct dw_xpcs *xpcs; struct dw_xpcs *xpcs;
@ -1527,7 +1533,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
if (IS_ERR(mdiodev)) if (IS_ERR(mdiodev))
return ERR_CAST(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 /* xpcs_create() has taken a refcount on the mdiodev if it was
* successful. If xpcs_create() fails, this will free the mdio * 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); 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 * xpcs_create_fwnode() - Create a DW xPCS instance from @fwnode
* @fwnode: fwnode handle poining to the DW XPCS device * @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 * 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 * 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 * other negative errno related to the data allocations and MDIO-bus
* communications. * communications.
*/ */
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode, struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode)
phy_interface_t interface)
{ {
struct mdio_device *mdiodev; struct mdio_device *mdiodev;
struct dw_xpcs *xpcs; struct dw_xpcs *xpcs;
@ -1565,7 +1581,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
if (!mdiodev) if (!mdiodev)
return ERR_PTR(-EPROBE_DEFER); 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 /* xpcs_create() has taken a refcount on the mdiodev if it was
* successful. If xpcs_create() fails, this will free the mdio * 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); 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_DESCRIPTION("Synopsys DesignWare XPCS library");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -61,20 +61,18 @@ struct dw_xpcs {
struct clk_bulk_data clks[DW_XPCS_NUM_CLKS]; struct clk_bulk_data clks[DW_XPCS_NUM_CLKS];
struct phylink_pcs pcs; struct phylink_pcs pcs;
phy_interface_t interface; phy_interface_t interface;
bool need_reset;
}; };
int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface); 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); void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces);
int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
int enable); int enable);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, 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);
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode,
phy_interface_t interface);
void xpcs_destroy(struct dw_xpcs *xpcs); 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 */ #endif /* __LINUX_PCS_XPCS_H */