net: freescale: ucc_geth: phylink conversion

ucc_geth is quite capable in terms of supported interfaces, and even
includes an externally controlled PCS (well, TBI). Port that driver to
phylink.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Maxime Chevallier 2024-12-03 13:43:21 +01:00 committed by David S. Miller
parent 02d4a6498b
commit 53036aa8d0
4 changed files with 215 additions and 331 deletions

View File

@ -81,8 +81,7 @@ config UCC_GETH
tristate "Freescale QE Gigabit Ethernet" tristate "Freescale QE Gigabit Ethernet"
depends on QUICC_ENGINE && PPC32 depends on QUICC_ENGINE && PPC32
select FSL_PQ_MDIO select FSL_PQ_MDIO
select PHYLIB select PHYLINK
select FIXED_PHY
help help
This driver supports the Gigabit Ethernet mode of the QUICC Engine, This driver supports the Gigabit Ethernet mode of the QUICC Engine,
which is available on some Freescale SOCs. which is available on some Freescale SOCs.

View File

@ -26,7 +26,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phy_fixed.h> #include <linux/phylink.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
@ -34,6 +34,7 @@
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/rtnetlink.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/irq.h> #include <asm/irq.h>
@ -1265,84 +1266,6 @@ static bool phy_interface_mode_is_reduced(phy_interface_t interface)
interface == PHY_INTERFACE_MODE_RTBI; interface == PHY_INTERFACE_MODE_RTBI;
} }
static int adjust_enet_interface(struct ucc_geth_private *ugeth)
{
struct ucc_geth_info *ug_info;
struct ucc_geth __iomem *ug_regs;
struct ucc_fast __iomem *uf_regs;
u32 upsmr, maccfg2;
u16 value;
ugeth_vdbg("%s: IN", __func__);
ug_info = ugeth->ug_info;
ug_regs = ugeth->ug_regs;
uf_regs = ugeth->uccf->uf_regs;
/* Set MACCFG2 */
maccfg2 = in_be32(&ug_regs->maccfg2);
/* Disable frame length check */
maccfg2 &= ~MACCFG2_LC;
maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK;
if ((ugeth->max_speed == SPEED_10) ||
(ugeth->max_speed == SPEED_100))
maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE;
else if (ugeth->max_speed == SPEED_1000)
maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE;
maccfg2 |= ug_info->padAndCrc;
out_be32(&ug_regs->maccfg2, maccfg2);
/* Set UPSMR */
upsmr = in_be32(&uf_regs->upsmr);
upsmr &= ~(UCC_GETH_UPSMR_RPM | UCC_GETH_UPSMR_R10M |
UCC_GETH_UPSMR_TBIM | UCC_GETH_UPSMR_RMM);
if (phy_interface_mode_is_reduced(ugeth->phy_interface)) {
if (ugeth->phy_interface != PHY_INTERFACE_MODE_RMII)
upsmr |= UCC_GETH_UPSMR_RPM;
switch (ugeth->max_speed) {
case SPEED_10:
upsmr |= UCC_GETH_UPSMR_R10M;
fallthrough;
case SPEED_100:
if (ugeth->phy_interface != PHY_INTERFACE_MODE_RTBI)
upsmr |= UCC_GETH_UPSMR_RMM;
}
}
if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
upsmr |= UCC_GETH_UPSMR_TBIM;
}
if (ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)
upsmr |= UCC_GETH_UPSMR_SGMM;
out_be32(&uf_regs->upsmr, upsmr);
/* Disable autonegotiation in tbi mode, because by default it
comes up in autonegotiation mode. */
/* Note that this depends on proper setting in utbipar register. */
if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) ||
(ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
struct ucc_geth_info *ug_info = ugeth->ug_info;
struct phy_device *tbiphy;
if (!ug_info->tbi_node)
pr_warn("TBI mode requires that the device tree specify a tbi-handle\n");
tbiphy = of_phy_find_device(ug_info->tbi_node);
if (!tbiphy)
pr_warn("Could not get TBI device\n");
value = phy_read(tbiphy, ENET_TBI_MII_CR);
value &= ~0x1000; /* Turn off autonegotiation */
phy_write(tbiphy, ENET_TBI_MII_CR, value);
put_device(&tbiphy->mdio.dev);
}
return 0;
}
static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth) static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
{ {
struct ucc_fast_private *uccf; struct ucc_fast_private *uccf;
@ -1560,64 +1483,62 @@ static void uec_configure_serdes(struct net_device *dev)
put_device(&tbiphy->mdio.dev); put_device(&tbiphy->mdio.dev);
} }
static void ugeth_link_up(struct ucc_geth_private *ugeth, static void ugeth_mac_link_up(struct phylink_config *config, struct phy_device *phy,
struct phy_device *phy, unsigned int mode, phy_interface_t interface,
phy_interface_t interface, int speed, int duplex) int speed, int duplex, bool tx_pause, bool rx_pause)
{ {
struct net_device *ndev = to_net_dev(config->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
struct ucc_geth_info *ug_info = ugeth->ug_info;
struct ucc_geth __iomem *ug_regs = ugeth->ug_regs; struct ucc_geth __iomem *ug_regs = ugeth->ug_regs;
struct ucc_fast __iomem *uf_regs = ugeth->uccf->uf_regs; struct ucc_fast __iomem *uf_regs = ugeth->uccf->uf_regs;
u32 tempval = in_be32(&ug_regs->maccfg2); u32 old_maccfg2, maccfg2 = in_be32(&ug_regs->maccfg2);
u32 upsmr = in_be32(&uf_regs->upsmr); u32 old_upsmr, upsmr = in_be32(&uf_regs->upsmr);
int new_state = 0;
/* Now we make sure that we can be in full duplex mode. old_maccfg2 = maccfg2;
* If not, we operate in half-duplex mode. old_upsmr = upsmr;
*/
if (duplex != ugeth->oldduplex) { /* No length check */
new_state = 1; maccfg2 &= ~MACCFG2_LC;
if (duplex == DUPLEX_HALF) maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK;
tempval &= ~(MACCFG2_FDX); upsmr &= ~(UCC_GETH_UPSMR_RPM | UCC_GETH_UPSMR_R10M |
else UCC_GETH_UPSMR_TBIM | UCC_GETH_UPSMR_RMM);
tempval |= MACCFG2_FDX;
ugeth->oldduplex = duplex; if (speed == SPEED_10 || speed == SPEED_100)
} maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE;
else if (speed == SPEED_1000)
maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE;
maccfg2 |= ug_info->padAndCrc;
if (phy_interface_mode_is_reduced(interface)) {
if (interface != PHY_INTERFACE_MODE_RMII)
upsmr |= UCC_GETH_UPSMR_RPM;
if (speed != ugeth->oldspeed) {
new_state = 1;
switch (speed) { switch (speed) {
case SPEED_1000:
tempval = ((tempval &
~(MACCFG2_INTERFACE_MODE_MASK)) |
MACCFG2_INTERFACE_MODE_BYTE);
break;
case SPEED_100:
case SPEED_10: case SPEED_10:
tempval = ((tempval & upsmr |= UCC_GETH_UPSMR_R10M;
~(MACCFG2_INTERFACE_MODE_MASK)) | fallthrough;
MACCFG2_INTERFACE_MODE_NIBBLE); case SPEED_100:
/* if reduced mode, re-set UPSMR.R10M */ if (interface != PHY_INTERFACE_MODE_RTBI)
if (phy_interface_mode_is_reduced(interface)) { upsmr |= UCC_GETH_UPSMR_RMM;
if (speed == SPEED_10)
upsmr |= UCC_GETH_UPSMR_R10M;
else
upsmr &= ~UCC_GETH_UPSMR_R10M;
}
break;
default:
if (netif_msg_link(ugeth))
pr_warn("%s: Speed (%d) is not 10/100/1000!",
netdev_name(ugeth->ndev), speed);
break;
} }
ugeth->oldspeed = speed;
} }
if (!ugeth->oldlink) { if (interface == PHY_INTERFACE_MODE_TBI ||
new_state = 1; interface == PHY_INTERFACE_MODE_RTBI)
ugeth->oldlink = 1; upsmr |= UCC_GETH_UPSMR_TBIM;
}
if (new_state) { if (interface == PHY_INTERFACE_MODE_SGMII)
upsmr |= UCC_GETH_UPSMR_SGMM;
if (duplex == DUPLEX_HALF)
maccfg2 &= ~(MACCFG2_FDX);
else
maccfg2 |= MACCFG2_FDX;
if (maccfg2 != old_maccfg2 || upsmr != old_upsmr) {
/* /*
* To change the MAC configuration we need to disable * To change the MAC configuration we need to disable
* the controller. To do so, we have to either grab * the controller. To do so, we have to either grab
@ -1628,69 +1549,79 @@ static void ugeth_link_up(struct ucc_geth_private *ugeth,
ugeth_quiesce(ugeth); ugeth_quiesce(ugeth);
ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
out_be32(&ug_regs->maccfg2, tempval); out_be32(&ug_regs->maccfg2, maccfg2);
out_be32(&uf_regs->upsmr, upsmr); out_be32(&uf_regs->upsmr, upsmr);
ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
ugeth_activate(ugeth); ugeth_activate(ugeth);
} }
if (netif_msg_link(ugeth)) if (interface == PHY_INTERFACE_MODE_SGMII)
phy_print_status(phy); uec_configure_serdes(ndev);
}
static void ugeth_link_down(struct ucc_geth_private *ugeth) if (!phylink_autoneg_inband(mode)) {
{ ug_info->aufc = 0;
ugeth->oldlink = 0; ug_info->receiveFlowControl = rx_pause;
ugeth->oldspeed = 0; ug_info->transmitFlowControl = tx_pause;
ugeth->oldduplex = -1;
}
/* Called every time the controller might need to be made init_flow_control_params(ug_info->aufc,
* aware of new link state. The PHY code conveys this ug_info->receiveFlowControl,
* information through variables in the ugeth structure, and this ug_info->transmitFlowControl,
* function converts those variables into the appropriate ug_info->pausePeriod,
* register values, and can bring down the device if needed. ug_info->extensionField,
*/ &ugeth->uccf->uf_regs->upsmr,
&ugeth->ug_regs->uempr,
static void adjust_link(struct net_device *dev) &ugeth->ug_regs->maccfg1);
{
struct ucc_geth_private *ugeth = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
if (phydev->link)
ugeth_link_up(ugeth, phydev, phydev->interface,
phydev->speed, phydev->duplex);
else
ugeth_link_down(ugeth);
}
/* Configure the PHY for dev.
* returns 0 if success. -1 if failure
*/
static int init_phy(struct net_device *dev)
{
struct ucc_geth_private *priv = netdev_priv(dev);
struct ucc_geth_info *ug_info = priv->ug_info;
struct phy_device *phydev;
priv->oldlink = 0;
priv->oldspeed = 0;
priv->oldduplex = -1;
phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0,
priv->phy_interface);
if (!phydev) {
dev_err(&dev->dev, "Could not attach to PHY\n");
return -ENODEV;
} }
if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII) ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
uec_configure_serdes(dev); }
phy_set_max_speed(phydev, priv->max_speed); static void ugeth_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
struct net_device *ndev = to_net_dev(config->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
return 0; ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
}
static void ugeth_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 ucc_geth_private *ugeth = netdev_priv(ndev);
struct ucc_geth_info *ug_info = ugeth->ug_info;
u16 value;
if (state->interface == PHY_INTERFACE_MODE_TBI ||
state->interface == PHY_INTERFACE_MODE_RTBI) {
struct phy_device *tbiphy;
if (!ug_info->tbi_node)
pr_warn("TBI mode requires that the device tree specify a tbi-handle\n");
tbiphy = of_phy_find_device(ug_info->tbi_node);
if (!tbiphy)
pr_warn("Could not get TBI device\n");
value = phy_read(tbiphy, ENET_TBI_MII_CR);
value &= ~0x1000; /* Turn off autonegotiation */
phy_write(tbiphy, ENET_TBI_MII_CR, value);
put_device(&tbiphy->mdio.dev);
}
if (phylink_autoneg_inband(mode)) {
ug_info->aufc = 1;
init_flow_control_params(ug_info->aufc, 1, 1,
ug_info->pausePeriod,
ug_info->extensionField,
&ugeth->uccf->uf_regs->upsmr,
&ugeth->ug_regs->uempr,
&ugeth->ug_regs->maccfg1);
}
} }
static void ugeth_dump_regs(struct ucc_geth_private *ugeth) static void ugeth_dump_regs(struct ucc_geth_private *ugeth)
@ -1962,7 +1893,6 @@ static void ucc_geth_set_multi(struct net_device *dev)
static void ucc_geth_stop(struct ucc_geth_private *ugeth) static void ucc_geth_stop(struct ucc_geth_private *ugeth)
{ {
struct ucc_geth __iomem *ug_regs = ugeth->ug_regs; struct ucc_geth __iomem *ug_regs = ugeth->ug_regs;
struct phy_device *phydev = ugeth->ndev->phydev;
ugeth_vdbg("%s: IN", __func__); ugeth_vdbg("%s: IN", __func__);
@ -1971,7 +1901,7 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth)
* Must be done before disabling the controller * Must be done before disabling the controller
* or deadlock may happen. * or deadlock may happen.
*/ */
phy_stop(phydev); phylink_stop(ugeth->phylink);
/* Disable the controller */ /* Disable the controller */
ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
@ -3213,12 +3143,6 @@ static int ucc_geth_init_mac(struct ucc_geth_private *ugeth)
goto err; goto err;
} }
err = adjust_enet_interface(ugeth);
if (err) {
netif_err(ugeth, ifup, dev, "Cannot configure net device, aborting\n");
goto err;
}
/* Set MACSTNADDR1, MACSTNADDR2 */ /* Set MACSTNADDR1, MACSTNADDR2 */
/* For more details see the hardware spec. */ /* For more details see the hardware spec. */
init_mac_station_addr_regs(dev->dev_addr[0], init_mac_station_addr_regs(dev->dev_addr[0],
@ -3230,12 +3154,6 @@ static int ucc_geth_init_mac(struct ucc_geth_private *ugeth)
&ugeth->ug_regs->macstnaddr1, &ugeth->ug_regs->macstnaddr1,
&ugeth->ug_regs->macstnaddr2); &ugeth->ug_regs->macstnaddr2);
err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
if (err) {
netif_err(ugeth, ifup, dev, "Cannot enable net device, aborting\n");
goto err;
}
return 0; return 0;
err: err:
ucc_geth_stop(ugeth); ucc_geth_stop(ugeth);
@ -3258,10 +3176,10 @@ static int ucc_geth_open(struct net_device *dev)
return -EINVAL; return -EINVAL;
} }
err = init_phy(dev); err = phylink_of_phy_connect(ugeth->phylink, ugeth->dev->of_node, 0);
if (err) { if (err) {
netif_err(ugeth, ifup, dev, "Cannot initialize PHY, aborting\n"); dev_err(&dev->dev, "Could not attach to PHY\n");
return err; return -ENODEV;
} }
err = ucc_geth_init_mac(ugeth); err = ucc_geth_init_mac(ugeth);
@ -3277,7 +3195,7 @@ static int ucc_geth_open(struct net_device *dev)
goto err; goto err;
} }
phy_start(dev->phydev); phylink_start(ugeth->phylink);
napi_enable(&ugeth->napi); napi_enable(&ugeth->napi);
netdev_reset_queue(dev); netdev_reset_queue(dev);
netif_start_queue(dev); netif_start_queue(dev);
@ -3304,7 +3222,7 @@ static int ucc_geth_close(struct net_device *dev)
cancel_work_sync(&ugeth->timeout_work); cancel_work_sync(&ugeth->timeout_work);
ucc_geth_stop(ugeth); ucc_geth_stop(ugeth);
phy_disconnect(dev->phydev); phylink_disconnect_phy(ugeth->phylink);
free_irq(ugeth->ug_info->uf_info.irq, ugeth->ndev); free_irq(ugeth->ug_info->uf_info.irq, ugeth->ndev);
@ -3338,7 +3256,7 @@ static void ucc_geth_timeout_work(struct work_struct *work)
ucc_geth_stop(ugeth); ucc_geth_stop(ugeth);
ucc_geth_init_mac(ugeth); ucc_geth_init_mac(ugeth);
/* Must start PHY here */ /* Must start PHY here */
phy_start(dev->phydev); phylink_start(ugeth->phylink);
netif_tx_start_all_queues(dev); netif_tx_start_all_queues(dev);
} }
@ -3363,6 +3281,7 @@ static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
{ {
struct net_device *ndev = platform_get_drvdata(ofdev); struct net_device *ndev = platform_get_drvdata(ofdev);
struct ucc_geth_private *ugeth = netdev_priv(ndev); struct ucc_geth_private *ugeth = netdev_priv(ndev);
bool mac_wol = false;
if (!netif_running(ndev)) if (!netif_running(ndev))
return 0; return 0;
@ -3380,10 +3299,13 @@ static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
setbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD); setbits32(ugeth->uccf->p_uccm, UCC_GETH_UCCE_MPD);
setbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE); setbits32(&ugeth->ug_regs->maccfg2, MACCFG2_MPE);
ucc_fast_enable(ugeth->uccf, COMM_DIR_RX_AND_TX); ucc_fast_enable(ugeth->uccf, COMM_DIR_RX_AND_TX);
} else if (!ugeth->phy_wol_en) { mac_wol = true;
phy_stop(ndev->phydev);
} }
rtnl_lock();
phylink_suspend(ugeth->phylink, mac_wol);
rtnl_unlock();
return 0; return 0;
} }
@ -3417,12 +3339,9 @@ static int ucc_geth_resume(struct platform_device *ofdev)
} }
} }
ugeth->oldlink = 0; rtnl_lock();
ugeth->oldspeed = 0; phylink_resume(ugeth->phylink);
ugeth->oldduplex = -1; rtnl_unlock();
phy_stop(ndev->phydev);
phy_start(ndev->phydev);
napi_enable(&ugeth->napi); napi_enable(&ugeth->napi);
netif_device_attach(ndev); netif_device_attach(ndev);
@ -3437,13 +3356,12 @@ static int ucc_geth_resume(struct platform_device *ofdev)
static int ucc_geth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int ucc_geth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{ {
struct ucc_geth_private *ugeth = netdev_priv(dev);
if (!netif_running(dev)) if (!netif_running(dev))
return -EINVAL; return -EINVAL;
if (!dev->phydev) return phylink_mii_ioctl(ugeth->phylink, rq, cmd);
return -ENODEV;
return phy_mii_ioctl(dev->phydev, rq, cmd);
} }
static const struct net_device_ops ucc_geth_netdev_ops = { static const struct net_device_ops ucc_geth_netdev_ops = {
@ -3451,7 +3369,6 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
.ndo_stop = ucc_geth_close, .ndo_stop = ucc_geth_close,
.ndo_start_xmit = ucc_geth_start_xmit, .ndo_start_xmit = ucc_geth_start_xmit,
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
.ndo_change_carrier = fixed_phy_change_carrier,
.ndo_set_mac_address = ucc_geth_set_mac_addr, .ndo_set_mac_address = ucc_geth_set_mac_addr,
.ndo_set_rx_mode = ucc_geth_set_multi, .ndo_set_rx_mode = ucc_geth_set_multi,
.ndo_tx_timeout = ucc_geth_timeout, .ndo_tx_timeout = ucc_geth_timeout,
@ -3491,6 +3408,12 @@ static int ucc_geth_parse_clock(struct device_node *np, const char *which,
return 0; return 0;
} }
struct phylink_mac_ops ugeth_mac_ops = {
.mac_link_up = ugeth_mac_link_up,
.mac_link_down = ugeth_mac_link_down,
.mac_config = ugeth_mac_config,
};
static int ucc_geth_probe(struct platform_device* ofdev) static int ucc_geth_probe(struct platform_device* ofdev)
{ {
struct device *device = &ofdev->dev; struct device *device = &ofdev->dev;
@ -3498,8 +3421,10 @@ static int ucc_geth_probe(struct platform_device* ofdev)
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct ucc_geth_private *ugeth = NULL; struct ucc_geth_private *ugeth = NULL;
struct ucc_geth_info *ug_info; struct ucc_geth_info *ug_info;
struct device_node *phy_node;
struct phylink *phylink;
struct resource res; struct resource res;
int err, ucc_num, max_speed = 0; int err, ucc_num;
const unsigned int *prop; const unsigned int *prop;
phy_interface_t phy_interface; phy_interface_t phy_interface;
@ -3537,57 +3462,35 @@ static int ucc_geth_probe(struct platform_device* ofdev)
ug_info->uf_info.regs = res.start; ug_info->uf_info.regs = res.start;
ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); ug_info->uf_info.irq = irq_of_parse_and_map(np, 0);
ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0);
if (!ug_info->phy_node && of_phy_is_fixed_link(np)) {
/*
* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
err = of_phy_register_fixed_link(np);
if (err)
return err;
ug_info->phy_node = of_node_get(np);
}
/* Find the TBI PHY node. If it's not there, we don't support SGMII */ /* Find the TBI PHY node. If it's not there, we don't support SGMII */
ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0); ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
prop = of_get_property(ug_info->phy_node, "interface", NULL); phy_node = of_parse_phandle(np, "phy-handle", 0);
if (prop) { if (phy_node) {
dev_err(&ofdev->dev, prop = of_get_property(phy_node, "interface", NULL);
"Device-tree property 'interface' is no longer supported. Please use 'phy-connection-type' instead."); if (prop) {
err = -EINVAL; dev_err(&ofdev->dev,
goto err_deregister_fixed_link; "Device-tree property 'interface' is no longer supported. Please use 'phy-connection-type' instead.");
of_node_put(phy_node);
err = -EINVAL;
goto err_put_tbi;
}
of_node_put(phy_node);
} }
err = of_get_phy_mode(np, &phy_interface); err = of_get_phy_mode(np, &phy_interface);
if (err) { if (err) {
dev_err(&ofdev->dev, "Invalid phy-connection-type"); dev_err(&ofdev->dev, "Invalid phy-connection-type");
goto err_deregister_fixed_link; goto err_put_tbi;
} }
/* get speed, or derive from PHY interface */ if (phy_interface == PHY_INTERFACE_MODE_GMII ||
if (max_speed == 0) phy_interface_mode_is_rgmii(phy_interface) ||
switch (phy_interface) { phy_interface == PHY_INTERFACE_MODE_TBI ||
case PHY_INTERFACE_MODE_GMII: phy_interface == PHY_INTERFACE_MODE_RTBI ||
case PHY_INTERFACE_MODE_RGMII: phy_interface == PHY_INTERFACE_MODE_SGMII) {
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_RTBI:
case PHY_INTERFACE_MODE_SGMII:
max_speed = SPEED_1000;
break;
default:
max_speed = SPEED_100;
break;
}
if (max_speed == SPEED_1000) {
unsigned int snums = qe_get_num_of_snums(); unsigned int snums = qe_get_num_of_snums();
/* configure muram FIFOs for gigabit operation */
ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT; ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT;
ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT; ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT;
ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT; ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT;
@ -3616,7 +3519,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
dev = devm_alloc_etherdev(&ofdev->dev, sizeof(*ugeth)); dev = devm_alloc_etherdev(&ofdev->dev, sizeof(*ugeth));
if (!dev) { if (!dev) {
err = -ENOMEM; err = -ENOMEM;
goto err_deregister_fixed_link; goto err_put_tbi;
} }
ugeth = netdev_priv(dev); ugeth = netdev_priv(dev);
@ -3643,23 +3546,50 @@ static int ucc_geth_probe(struct platform_device* ofdev)
dev->max_mtu = 1518; dev->max_mtu = 1518;
ugeth->msg_enable = netif_msg_init(debug.msg_enable, UGETH_MSG_DEFAULT); ugeth->msg_enable = netif_msg_init(debug.msg_enable, UGETH_MSG_DEFAULT);
ugeth->phy_interface = phy_interface;
ugeth->max_speed = max_speed;
/* Carrier starts down, phylib will bring it up */ ugeth->phylink_config.dev = &dev->dev;
netif_carrier_off(dev); ugeth->phylink_config.type = PHYLINK_NETDEV;
ugeth->phylink_config.mac_capabilities =
MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD;
__set_bit(PHY_INTERFACE_MODE_MII,
ugeth->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_RMII,
ugeth->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_GMII,
ugeth->phylink_config.supported_interfaces);
phy_interface_set_rgmii(ugeth->phylink_config.supported_interfaces);
if (ug_info->tbi_node) {
__set_bit(PHY_INTERFACE_MODE_SGMII,
ugeth->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_TBI,
ugeth->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_RTBI,
ugeth->phylink_config.supported_interfaces);
}
phylink = phylink_create(&ugeth->phylink_config, dev_fwnode(&dev->dev),
phy_interface, &ugeth_mac_ops);
if (IS_ERR(phylink)) {
err = PTR_ERR(phylink);
goto err_put_tbi;
}
ugeth->phylink = phylink;
err = devm_register_netdev(&ofdev->dev, dev); err = devm_register_netdev(&ofdev->dev, dev);
if (err) { if (err) {
if (netif_msg_probe(ugeth)) if (netif_msg_probe(ugeth))
pr_err("%s: Cannot register net device, aborting\n", pr_err("%s: Cannot register net device, aborting\n",
dev->name); dev->name);
goto err_deregister_fixed_link; goto err_destroy_phylink;
} }
err = of_get_ethdev_address(np, dev); err = of_get_ethdev_address(np, dev);
if (err == -EPROBE_DEFER) if (err == -EPROBE_DEFER)
goto err_deregister_fixed_link; goto err_destroy_phylink;
ugeth->ug_info = ug_info; ugeth->ug_info = ug_info;
ugeth->dev = device; ugeth->dev = device;
@ -3668,11 +3598,11 @@ static int ucc_geth_probe(struct platform_device* ofdev)
return 0; return 0;
err_deregister_fixed_link: err_destroy_phylink:
if (of_phy_is_fixed_link(np)) phylink_destroy(phylink);
of_phy_deregister_fixed_link(np); err_put_tbi:
of_node_put(ug_info->tbi_node); of_node_put(ug_info->tbi_node);
of_node_put(ug_info->phy_node);
return err; return err;
} }
@ -3680,13 +3610,10 @@ static void ucc_geth_remove(struct platform_device* ofdev)
{ {
struct net_device *dev = platform_get_drvdata(ofdev); struct net_device *dev = platform_get_drvdata(ofdev);
struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_geth_private *ugeth = netdev_priv(dev);
struct device_node *np = ofdev->dev.of_node;
ucc_geth_memclean(ugeth); ucc_geth_memclean(ugeth);
if (of_phy_is_fixed_link(np)) phylink_destroy(ugeth->phylink);
of_phy_deregister_fixed_link(np);
of_node_put(ugeth->ug_info->tbi_node); of_node_put(ugeth->ug_info->tbi_node);
of_node_put(ugeth->ug_info->phy_node);
} }
static const struct of_device_id ucc_geth_match[] = { static const struct of_device_id ucc_geth_match[] = {

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/phylink.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <soc/fsl/qe/immap_qe.h> #include <soc/fsl/qe/immap_qe.h>
@ -1074,6 +1075,9 @@ struct ucc_geth_tad_params {
u16 vid; u16 vid;
}; };
struct phylink;
struct phylink_config;
/* GETH protocol initialization structure */ /* GETH protocol initialization structure */
struct ucc_geth_info { struct ucc_geth_info {
struct ucc_fast_info uf_info; struct ucc_fast_info uf_info;
@ -1124,7 +1128,6 @@ struct ucc_geth_info {
u32 eventRegMask; u32 eventRegMask;
u16 pausePeriod; u16 pausePeriod;
u16 extensionField; u16 extensionField;
struct device_node *phy_node;
struct device_node *tbi_node; struct device_node *tbi_node;
u8 weightfactor[NUM_TX_QUEUES]; u8 weightfactor[NUM_TX_QUEUES];
u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES]; u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES];
@ -1209,15 +1212,13 @@ struct ucc_geth_private {
u16 skb_dirtytx[NUM_TX_QUEUES]; u16 skb_dirtytx[NUM_TX_QUEUES];
struct ugeth_mii_info *mii_info; struct ugeth_mii_info *mii_info;
phy_interface_t phy_interface;
int max_speed;
uint32_t msg_enable; uint32_t msg_enable;
int oldspeed;
int oldduplex;
int oldlink;
u32 wol_en; u32 wol_en;
u32 phy_wol_en; u32 phy_wol_en;
struct phylink *phylink;
struct phylink_config phylink_config;
struct device_node *node; struct device_node *node;
}; };

View File

@ -103,26 +103,18 @@ static const char rx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
static int static int
uec_get_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) uec_get_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd)
{ {
struct phy_device *phydev = netdev->phydev; struct ucc_geth_private *ugeth = netdev_priv(netdev);
if (!phydev) return phylink_ethtool_ksettings_get(ugeth->phylink, cmd);
return -ENODEV;
phy_ethtool_ksettings_get(phydev, cmd);
return 0;
} }
static int static int
uec_set_ksettings(struct net_device *netdev, uec_set_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd) const struct ethtool_link_ksettings *cmd)
{ {
struct phy_device *phydev = netdev->phydev; struct ucc_geth_private *ugeth = netdev_priv(netdev);
if (!phydev) return phylink_ethtool_ksettings_set(ugeth->phylink, cmd);
return -ENODEV;
return phy_ethtool_ksettings_set(phydev, cmd);
} }
static void static void
@ -130,15 +122,8 @@ uec_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause) struct ethtool_pauseparam *pause)
{ {
struct ucc_geth_private *ugeth = netdev_priv(netdev); struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = netdev->phydev;
if (phydev) return phylink_ethtool_get_pauseparam(ugeth->phylink, pause);
pause->autoneg = phydev->autoneg;
if (ugeth->ug_info->receiveFlowControl)
pause->rx_pause = 1;
if (ugeth->ug_info->transmitFlowControl)
pause->tx_pause = 1;
} }
static int static int
@ -146,31 +131,11 @@ uec_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause) struct ethtool_pauseparam *pause)
{ {
struct ucc_geth_private *ugeth = netdev_priv(netdev); struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = netdev->phydev;
int ret = 0;
ugeth->ug_info->receiveFlowControl = pause->rx_pause; ugeth->ug_info->receiveFlowControl = pause->rx_pause;
ugeth->ug_info->transmitFlowControl = pause->tx_pause; ugeth->ug_info->transmitFlowControl = pause->tx_pause;
if (phydev && phydev->autoneg) { return phylink_ethtool_set_pauseparam(ugeth->phylink, pause);
if (netif_running(netdev)) {
/* FIXME: automatically restart */
netdev_info(netdev, "Please re-open the interface\n");
}
} else {
struct ucc_geth_info *ug_info = ugeth->ug_info;
ret = init_flow_control_params(ug_info->aufc,
ug_info->receiveFlowControl,
ug_info->transmitFlowControl,
ug_info->pausePeriod,
ug_info->extensionField,
&ugeth->uccf->uf_regs->upsmr,
&ugeth->ug_regs->uempr,
&ugeth->ug_regs->maccfg1);
}
return ret;
} }
static uint32_t static uint32_t
@ -344,13 +309,8 @@ uec_get_drvinfo(struct net_device *netdev,
static void uec_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) static void uec_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{ {
struct ucc_geth_private *ugeth = netdev_priv(netdev); struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = netdev->phydev;
wol->supported = 0; phylink_ethtool_get_wol(ugeth->phylink, wol);
wol->wolopts = 0;
if (phydev)
phy_ethtool_get_wol(phydev, wol);
if (qe_alive_during_sleep()) if (qe_alive_during_sleep())
wol->supported |= WAKE_MAGIC; wol->supported |= WAKE_MAGIC;
@ -361,19 +321,16 @@ static void uec_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
static int uec_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) static int uec_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{ {
struct ucc_geth_private *ugeth = netdev_priv(netdev); struct ucc_geth_private *ugeth = netdev_priv(netdev);
struct phy_device *phydev = netdev->phydev;
int ret = 0; int ret = 0;
if (phydev) { ret = phylink_ethtool_set_wol(ugeth->phylink, wol);
ret = phy_ethtool_set_wol(phydev, wol); if (ret == -EOPNOTSUPP) {
if (ret == -EOPNOTSUPP) { ugeth->phy_wol_en = 0;
ugeth->phy_wol_en = 0; } else if (ret) {
} else if (ret) { return ret;
return ret; } else {
} else { ugeth->phy_wol_en = wol->wolopts;
ugeth->phy_wol_en = wol->wolopts; goto out;
goto out;
}
} }
/* If the PHY isn't handling the WoL and the MAC is asked to more than /* If the PHY isn't handling the WoL and the MAC is asked to more than