mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
net: axienet: convert to phylink API
Convert this driver to use the phylink API rather than the legacy PHY API. This allows for better support for SFP modules connected using a 1000BaseX or SGMII interface. Signed-off-by: Robert Hancock <hancock@sedsystems.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
28ef9ebdb6
commit
f5203a3d9b
3 changed files with 192 additions and 101 deletions
|
@ -27,7 +27,7 @@ config XILINX_EMACLITE
|
|||
config XILINX_AXI_EMAC
|
||||
tristate "Xilinx 10/100/1000 AXI Ethernet support"
|
||||
depends on MICROBLAZE || X86 || ARM || COMPILE_TEST
|
||||
select PHYLIB
|
||||
select PHYLINK
|
||||
---help---
|
||||
This driver supports the 10/100/1000 Ethernet from Xilinx for the
|
||||
AXI bus interface used in Xilinx Virtex FPGAs.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/phylink.h>
|
||||
|
||||
/* Packet size info */
|
||||
#define XAE_HDR_SIZE 14 /* Size of Ethernet header */
|
||||
|
@ -420,6 +421,9 @@ struct axienet_local {
|
|||
/* Connection to PHY device */
|
||||
struct device_node *phy_node;
|
||||
|
||||
struct phylink *phylink;
|
||||
struct phylink_config phylink_config;
|
||||
|
||||
/* Clock for AXI bus */
|
||||
struct clk *clk;
|
||||
|
||||
|
@ -439,7 +443,6 @@ struct axienet_local {
|
|||
phy_interface_t phy_mode;
|
||||
|
||||
u32 options; /* Current options word */
|
||||
u32 last_link;
|
||||
u32 features;
|
||||
|
||||
/* Buffer descriptors */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Copyright (c) 2008-2009 Secret Lab Technologies Ltd.
|
||||
* Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>
|
||||
* Copyright (c) 2010 - 2011 PetaLogix
|
||||
* Copyright (c) 2019 SED Systems, a division of Calian Ltd.
|
||||
* Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6
|
||||
|
@ -519,63 +520,6 @@ static void axienet_device_reset(struct net_device *ndev)
|
|||
netif_trans_update(ndev);
|
||||
}
|
||||
|
||||
/**
|
||||
* axienet_adjust_link - Adjust the PHY link speed/duplex.
|
||||
* @ndev: Pointer to the net_device structure
|
||||
*
|
||||
* This function is called to change the speed and duplex setting after
|
||||
* auto negotiation is done by the PHY. This is the function that gets
|
||||
* registered with the PHY interface through the "of_phy_connect" call.
|
||||
*/
|
||||
static void axienet_adjust_link(struct net_device *ndev)
|
||||
{
|
||||
u32 emmc_reg;
|
||||
u32 link_state;
|
||||
u32 setspeed = 1;
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
struct phy_device *phy = ndev->phydev;
|
||||
|
||||
link_state = phy->speed | (phy->duplex << 1) | phy->link;
|
||||
if (lp->last_link != link_state) {
|
||||
if ((phy->speed == SPEED_10) || (phy->speed == SPEED_100)) {
|
||||
if (lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX)
|
||||
setspeed = 0;
|
||||
} else {
|
||||
if ((phy->speed == SPEED_1000) &&
|
||||
(lp->phy_mode == PHY_INTERFACE_MODE_MII))
|
||||
setspeed = 0;
|
||||
}
|
||||
|
||||
if (setspeed == 1) {
|
||||
emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET);
|
||||
emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK;
|
||||
|
||||
switch (phy->speed) {
|
||||
case SPEED_1000:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_1000;
|
||||
break;
|
||||
case SPEED_100:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_100;
|
||||
break;
|
||||
case SPEED_10:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_10;
|
||||
break;
|
||||
default:
|
||||
dev_err(&ndev->dev, "Speed other than 10, 100 "
|
||||
"or 1Gbps is not supported\n");
|
||||
break;
|
||||
}
|
||||
|
||||
axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg);
|
||||
lp->last_link = link_state;
|
||||
phy_print_status(phy);
|
||||
} else {
|
||||
netdev_err(ndev,
|
||||
"Error setting Axi Ethernet mac speed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* axienet_start_xmit_done - Invoked once a transmit is completed by the
|
||||
* Axi DMA Tx channel.
|
||||
|
@ -956,7 +900,8 @@ static void axienet_dma_err_handler(unsigned long data);
|
|||
* Return: 0, on success.
|
||||
* non-zero error value on failure
|
||||
*
|
||||
* This is the driver open routine. It calls phy_start to start the PHY device.
|
||||
* This is the driver open routine. It calls phylink_start to start the
|
||||
* PHY device.
|
||||
* It also allocates interrupt service routines, enables the interrupt lines
|
||||
* and ISR handling. Axi Ethernet core is reset through Axi DMA core. Buffer
|
||||
* descriptors are initialized.
|
||||
|
@ -965,7 +910,6 @@ static int axienet_open(struct net_device *ndev)
|
|||
{
|
||||
int ret;
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
struct phy_device *phydev = NULL;
|
||||
|
||||
dev_dbg(&ndev->dev, "axienet_open()\n");
|
||||
|
||||
|
@ -983,16 +927,14 @@ static int axienet_open(struct net_device *ndev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (lp->phy_node) {
|
||||
phydev = of_phy_connect(lp->ndev, lp->phy_node,
|
||||
axienet_adjust_link, 0, lp->phy_mode);
|
||||
|
||||
if (!phydev)
|
||||
dev_err(lp->dev, "of_phy_connect() failed\n");
|
||||
else
|
||||
phy_start(phydev);
|
||||
ret = phylink_of_phy_connect(lp->phylink, lp->dev->of_node, 0);
|
||||
if (ret) {
|
||||
dev_err(lp->dev, "phylink_of_phy_connect() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phylink_start(lp->phylink);
|
||||
|
||||
/* Enable tasklets for Axi DMA error handling */
|
||||
tasklet_init(&lp->dma_err_tasklet, axienet_dma_err_handler,
|
||||
(unsigned long) lp);
|
||||
|
@ -1022,8 +964,8 @@ static int axienet_open(struct net_device *ndev)
|
|||
err_rx_irq:
|
||||
free_irq(lp->tx_irq, ndev);
|
||||
err_tx_irq:
|
||||
if (phydev)
|
||||
phy_disconnect(phydev);
|
||||
phylink_stop(lp->phylink);
|
||||
phylink_disconnect_phy(lp->phylink);
|
||||
tasklet_kill(&lp->dma_err_tasklet);
|
||||
dev_err(lp->dev, "request_irq() failed\n");
|
||||
return ret;
|
||||
|
@ -1035,7 +977,7 @@ static int axienet_open(struct net_device *ndev)
|
|||
*
|
||||
* Return: 0, on success.
|
||||
*
|
||||
* This is the driver stop routine. It calls phy_disconnect to stop the PHY
|
||||
* This is the driver stop routine. It calls phylink_disconnect to stop the PHY
|
||||
* device. It also removes the interrupt handlers and disables the interrupts.
|
||||
* The Axi DMA Tx/Rx BDs are released.
|
||||
*/
|
||||
|
@ -1047,6 +989,9 @@ static int axienet_stop(struct net_device *ndev)
|
|||
|
||||
dev_dbg(&ndev->dev, "axienet_close()\n");
|
||||
|
||||
phylink_stop(lp->phylink);
|
||||
phylink_disconnect_phy(lp->phylink);
|
||||
|
||||
axienet_setoptions(ndev, lp->options &
|
||||
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
|
||||
|
||||
|
@ -1087,9 +1032,6 @@ static int axienet_stop(struct net_device *ndev)
|
|||
free_irq(lp->tx_irq, ndev);
|
||||
free_irq(lp->rx_irq, ndev);
|
||||
|
||||
if (ndev->phydev)
|
||||
phy_disconnect(ndev->phydev);
|
||||
|
||||
axienet_dma_bd_release(ndev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1294,12 +1236,9 @@ static void
|
|||
axienet_ethtools_get_pauseparam(struct net_device *ndev,
|
||||
struct ethtool_pauseparam *epauseparm)
|
||||
{
|
||||
u32 regval;
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
epauseparm->autoneg = 0;
|
||||
regval = axienet_ior(lp, XAE_FCC_OFFSET);
|
||||
epauseparm->tx_pause = regval & XAE_FCC_FCTX_MASK;
|
||||
epauseparm->rx_pause = regval & XAE_FCC_FCRX_MASK;
|
||||
|
||||
phylink_ethtool_get_pauseparam(lp->phylink, epauseparm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1318,27 +1257,9 @@ static int
|
|||
axienet_ethtools_set_pauseparam(struct net_device *ndev,
|
||||
struct ethtool_pauseparam *epauseparm)
|
||||
{
|
||||
u32 regval = 0;
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
|
||||
if (netif_running(ndev)) {
|
||||
netdev_err(ndev,
|
||||
"Please stop netif before applying configuration\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
regval = axienet_ior(lp, XAE_FCC_OFFSET);
|
||||
if (epauseparm->tx_pause)
|
||||
regval |= XAE_FCC_FCTX_MASK;
|
||||
else
|
||||
regval &= ~XAE_FCC_FCTX_MASK;
|
||||
if (epauseparm->rx_pause)
|
||||
regval |= XAE_FCC_FCRX_MASK;
|
||||
else
|
||||
regval &= ~XAE_FCC_FCRX_MASK;
|
||||
axienet_iow(lp, XAE_FCC_OFFSET, regval);
|
||||
|
||||
return 0;
|
||||
return phylink_ethtool_set_pauseparam(lp->phylink, epauseparm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1417,6 +1338,24 @@ static int axienet_ethtools_set_coalesce(struct net_device *ndev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
axienet_ethtools_get_link_ksettings(struct net_device *ndev,
|
||||
struct ethtool_link_ksettings *cmd)
|
||||
{
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
|
||||
return phylink_ethtool_ksettings_get(lp->phylink, cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
axienet_ethtools_set_link_ksettings(struct net_device *ndev,
|
||||
const struct ethtool_link_ksettings *cmd)
|
||||
{
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
|
||||
return phylink_ethtool_ksettings_set(lp->phylink, cmd);
|
||||
}
|
||||
|
||||
static const struct ethtool_ops axienet_ethtool_ops = {
|
||||
.get_drvinfo = axienet_ethtools_get_drvinfo,
|
||||
.get_regs_len = axienet_ethtools_get_regs_len,
|
||||
|
@ -1428,8 +1367,141 @@ static const struct ethtool_ops axienet_ethtool_ops = {
|
|||
.set_pauseparam = axienet_ethtools_set_pauseparam,
|
||||
.get_coalesce = axienet_ethtools_get_coalesce,
|
||||
.set_coalesce = axienet_ethtools_set_coalesce,
|
||||
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
||||
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
||||
.get_link_ksettings = axienet_ethtools_get_link_ksettings,
|
||||
.set_link_ksettings = axienet_ethtools_set_link_ksettings,
|
||||
};
|
||||
|
||||
static void axienet_validate(struct phylink_config *config,
|
||||
unsigned long *supported,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(config->dev);
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
||||
|
||||
/* Only support the mode we are configured for */
|
||||
if (state->interface != PHY_INTERFACE_MODE_NA &&
|
||||
state->interface != lp->phy_mode) {
|
||||
netdev_warn(ndev, "Cannot use PHY mode %s, supported: %s\n",
|
||||
phy_modes(state->interface),
|
||||
phy_modes(lp->phy_mode));
|
||||
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||
return;
|
||||
}
|
||||
|
||||
phylink_set(mask, Autoneg);
|
||||
phylink_set_port_modes(mask);
|
||||
|
||||
phylink_set(mask, Asym_Pause);
|
||||
phylink_set(mask, Pause);
|
||||
phylink_set(mask, 1000baseX_Full);
|
||||
phylink_set(mask, 10baseT_Full);
|
||||
phylink_set(mask, 100baseT_Full);
|
||||
phylink_set(mask, 1000baseT_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 axienet_mac_link_state(struct phylink_config *config,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(config->dev);
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
u32 emmc_reg, fcc_reg;
|
||||
|
||||
state->interface = lp->phy_mode;
|
||||
|
||||
emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET);
|
||||
if (emmc_reg & XAE_EMMC_LINKSPD_1000)
|
||||
state->speed = SPEED_1000;
|
||||
else if (emmc_reg & XAE_EMMC_LINKSPD_100)
|
||||
state->speed = SPEED_100;
|
||||
else
|
||||
state->speed = SPEED_10;
|
||||
|
||||
state->pause = 0;
|
||||
fcc_reg = axienet_ior(lp, XAE_FCC_OFFSET);
|
||||
if (fcc_reg & XAE_FCC_FCTX_MASK)
|
||||
state->pause |= MLO_PAUSE_TX;
|
||||
if (fcc_reg & XAE_FCC_FCRX_MASK)
|
||||
state->pause |= MLO_PAUSE_RX;
|
||||
|
||||
state->an_complete = 0;
|
||||
state->duplex = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void axienet_mac_an_restart(struct phylink_config *config)
|
||||
{
|
||||
/* Unsupported, do nothing */
|
||||
}
|
||||
|
||||
static void axienet_mac_config(struct phylink_config *config, unsigned int mode,
|
||||
const struct phylink_link_state *state)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(config->dev);
|
||||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
u32 emmc_reg, fcc_reg;
|
||||
|
||||
emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET);
|
||||
emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK;
|
||||
|
||||
switch (state->speed) {
|
||||
case SPEED_1000:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_1000;
|
||||
break;
|
||||
case SPEED_100:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_100;
|
||||
break;
|
||||
case SPEED_10:
|
||||
emmc_reg |= XAE_EMMC_LINKSPD_10;
|
||||
break;
|
||||
default:
|
||||
dev_err(&ndev->dev,
|
||||
"Speed other than 10, 100 or 1Gbps is not supported\n");
|
||||
break;
|
||||
}
|
||||
|
||||
axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg);
|
||||
|
||||
fcc_reg = axienet_ior(lp, XAE_FCC_OFFSET);
|
||||
if (state->pause & MLO_PAUSE_TX)
|
||||
fcc_reg |= XAE_FCC_FCTX_MASK;
|
||||
else
|
||||
fcc_reg &= ~XAE_FCC_FCTX_MASK;
|
||||
if (state->pause & MLO_PAUSE_RX)
|
||||
fcc_reg |= XAE_FCC_FCRX_MASK;
|
||||
else
|
||||
fcc_reg &= ~XAE_FCC_FCRX_MASK;
|
||||
axienet_iow(lp, XAE_FCC_OFFSET, fcc_reg);
|
||||
}
|
||||
|
||||
static void axienet_mac_link_down(struct phylink_config *config,
|
||||
unsigned int mode,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
/* nothing meaningful to do */
|
||||
}
|
||||
|
||||
static void axienet_mac_link_up(struct phylink_config *config,
|
||||
unsigned int mode,
|
||||
phy_interface_t interface,
|
||||
struct phy_device *phy)
|
||||
{
|
||||
/* nothing meaningful to do */
|
||||
}
|
||||
|
||||
static const struct phylink_mac_ops axienet_phylink_ops = {
|
||||
.validate = axienet_validate,
|
||||
.mac_link_state = axienet_mac_link_state,
|
||||
.mac_an_restart = axienet_mac_an_restart,
|
||||
.mac_config = axienet_mac_config,
|
||||
.mac_link_down = axienet_mac_link_down,
|
||||
.mac_link_up = axienet_mac_link_up,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1777,6 +1849,18 @@ static int axienet_probe(struct platform_device *pdev)
|
|||
"error registering MDIO bus: %d\n", ret);
|
||||
}
|
||||
|
||||
lp->phylink_config.dev = &ndev->dev;
|
||||
lp->phylink_config.type = PHYLINK_NETDEV;
|
||||
|
||||
lp->phylink = phylink_create(&lp->phylink_config, pdev->dev.fwnode,
|
||||
lp->phy_mode,
|
||||
&axienet_phylink_ops);
|
||||
if (IS_ERR(lp->phylink)) {
|
||||
ret = PTR_ERR(lp->phylink);
|
||||
dev_err(&pdev->dev, "phylink_create error (%i)\n", ret);
|
||||
goto free_netdev;
|
||||
}
|
||||
|
||||
ret = register_netdev(lp->ndev);
|
||||
if (ret) {
|
||||
dev_err(lp->dev, "register_netdev() error (%i)\n", ret);
|
||||
|
@ -1797,6 +1881,10 @@ static int axienet_remove(struct platform_device *pdev)
|
|||
struct axienet_local *lp = netdev_priv(ndev);
|
||||
|
||||
unregister_netdev(ndev);
|
||||
|
||||
if (lp->phylink)
|
||||
phylink_destroy(lp->phylink);
|
||||
|
||||
axienet_mdio_teardown(lp);
|
||||
|
||||
if (lp->clk)
|
||||
|
|
Loading…
Reference in a new issue