>From c59957adebf39153a9a98af278d6036086654150 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Fri, 25 Oct 2019 12:12:33 +0300 Subject: [PATCH 1/2] convert the Altera TSE driver to phylink Signed-off-by: Petko Manolov --- drivers/net/ethernet/altera/altera_tse.h | 47 ++ .../net/ethernet/altera/altera_tse_ethtool.c | 20 +- drivers/net/ethernet/altera/altera_tse_main.c | 547 ++++++++---------- 3 files changed, 311 insertions(+), 303 deletions(-) diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h index e2feee87180a..781ea1b71289 100644 --- a/drivers/net/ethernet/altera/altera_tse.h +++ b/drivers/net/ethernet/altera/altera_tse.h @@ -38,6 +38,7 @@ #include #include #include +#include #define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000 #define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in @@ -120,13 +121,51 @@ #define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27) #define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31) +#define PCS_CTRL_REG 0x0 +#define PCS_CTRL_RESTART_AN BIT(9) +#define PCS_CTRL_AN_ENABLE BIT(12) +#define PCS_CTRL_RESET BIT(15) +#define PCS_STATUS_REG 0x1 +#define PCS_STATUS_LINK BIT(2) +#define PCS_STATUS_AN_COMPLETE BIT(5) +#define PCS_DEV_ABILITY_REG 0x4 +#define PCS_PARTNER_ABILITY_REG 0x5 +#define PCS_ABILITY_1000BASEX_FD BIT(5) +#define PCS_ABILITY_1000BASEX_HD BIT(6) +#define PCS_ABILITY_1000BASEX_PAUSE_MASK GENMASK(8, 7) +#define PCS_ABILITY_1000BASEX_NO_PAUSE (0 << 7) +#define PCS_ABILITY_1000BASEX_PAUSE_ASYM (1 << 7) +#define PCS_ABILITY_1000BASEX_PAUSE_SYM (2 << 7) +#define PCS_ABILITY_1000BASEX_PAUSE_TXRX (3 << 7) +#define PCS_ABILITY_1000BASEX_RFAULT_MASK GENMASK(13, 12) +#define PCS_ABILITY_1000BASEX_NO_RFAULT (0 << 12) +#define PCS_ABILITY_1000BASEX_OFFLINE (1 << 12) +#define PCS_ABILITY_1000BASEX_FAILURE (2 << 12) +#define PCS_ABILITY_1000BASEX_AN_ERROR (3 << 12) +#define PCS_ABILITY_SGMII_COPPER_SPEED_MASK GENMASK(11, 10) +#define PCS_ABILITY_SGMI_SPEED_10 (0 << 10) +#define PCS_ABILITY_SGMI_SPEED_100 (1 << 10) +#define PCS_ABILITY_SGMI_SPEED_1000 (2 << 10) +#define PCS_ABILITY_SGMII_COPPER_FD BIT(12) +#define PCS_ABILITY_SGMII_ACK BIT(14) +#define PCS_ABILITY_SGMII_COPPER_LINK_STATUS BIT(15) + /* SGMII PCS register addresses */ #define SGMII_PCS_SCRATCH 0x10 #define SGMII_PCS_REV 0x11 #define SGMII_PCS_LINK_TIMER_0 0x12 +#define SGMII_PCS_LINK_TIMER_REG(x) (0x12 + (x)) #define SGMII_PCS_LINK_TIMER_1 0x13 #define SGMII_PCS_IF_MODE 0x14 +#define PCS_IF_MODE_SGMII_ENA BIT(0) +#define PCS_IF_MODE_USE_SGMII_AN BIT(1) +#define PCS_IF_MODE_SGMI_SPEED_MASK GENMASK(3, 2) +#define PCS_IF_MODE_SGMI_SPEED_10 (0 << 2) +#define PCS_IF_MODE_SGMI_SPEED_100 (1 << 2) +#define PCS_IF_MODE_SGMI_SPEED_1000 (2 << 2) +#define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4) +#define PCS_IF_MODE_SGMI_PHY_AN BIT(5) #define SGMII_PCS_DIS_READ_TO 0x15 #define SGMII_PCS_READ_TO 0x16 #define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */ @@ -491,6 +530,14 @@ struct altera_tse_private { u32 msg_enable; struct altera_dmaops *dmaops; + + /* phylink stuff */ + struct phylink *phylink; + + /* PCS address */ + struct { + void __iomem *iomem; + } pcs; }; /* Function prototypes diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c index 7c367713c3e6..e1f69f00f4c2 100644 --- a/drivers/net/ethernet/altera/altera_tse_ethtool.c +++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c @@ -233,6 +233,22 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs, buf[i] = csrrd32(priv->mac_dev, i * 4); } +static int tse_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct altera_tse_private *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_set(priv->phylink, cmd); +} + +static int tse_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct altera_tse_private *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_get(priv->phylink, cmd); +} + static const struct ethtool_ops tse_ethtool_ops = { .get_drvinfo = tse_get_drvinfo, .get_regs_len = tse_reglen, @@ -243,8 +259,8 @@ static const struct ethtool_ops tse_ethtool_ops = { .get_ethtool_stats = tse_fill_stats, .get_msglevel = tse_get_msglevel, .set_msglevel = tse_set_msglevel, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = tse_ethtool_get_link_ksettings, + .set_link_ksettings = tse_ethtool_set_link_ksettings, }; void altera_tse_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index c3c1195021a2..b19b8c166433 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -97,25 +97,32 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv) return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1; } -/* PCS Register read/write functions +/* PCS Register sgmii_pcs_read read/write functions */ static u16 sgmii_pcs_read(struct altera_tse_private *priv, int regnum) { - return csrrd32(priv->mac_dev, - tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff; + u16 ret; + + ret = ioread32(priv->pcs.iomem + (regnum * sizeof(u16))); + + return ret; } static void sgmii_pcs_write(struct altera_tse_private *priv, int regnum, u16 value) { - csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4); + iowrite32(value, priv->pcs.iomem + (regnum * sizeof(u16))); } -/* Check PCS scratch memory */ -static int sgmii_pcs_scratch_test(struct altera_tse_private *priv, u16 value) +static void sgmii_pcs_reset(struct altera_tse_private *priv) { - sgmii_pcs_write(priv, SGMII_PCS_SCRATCH, value); - return (sgmii_pcs_read(priv, SGMII_PCS_SCRATCH) == value); + u16 tmp; + + tmp = sgmii_pcs_read(priv, PCS_CTRL_REG); + tmp |= PCS_CTRL_RESET; + sgmii_pcs_write(priv, PCS_CTRL_REG, tmp); + /* wait until the reset bit is clear */ + while (sgmii_pcs_read(priv, PCS_CTRL_REG) & PCS_CTRL_RESET); } /* MDIO specific functions @@ -126,12 +133,11 @@ static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum) struct altera_tse_private *priv = netdev_priv(ndev); /* set MDIO address */ - csrwr32((mii_id & 0x1f), priv->mac_dev, - tse_csroffs(mdio_phy1_addr)); + csrwr32((mii_id & 0x1f), priv->mac_dev, tse_csroffs(mdio_phy0_addr)); /* get the data */ return csrrd32(priv->mac_dev, - tse_csroffs(mdio_phy1) + regnum * 4) & 0xffff; + tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff; } static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum, @@ -141,11 +147,10 @@ static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum, struct altera_tse_private *priv = netdev_priv(ndev); /* set MDIO address */ - csrwr32((mii_id & 0x1f), priv->mac_dev, - tse_csroffs(mdio_phy1_addr)); - + csrwr32((mii_id & 0x1f), priv->mac_dev, tse_csroffs(mdio_phy0_addr)); /* write the data */ - csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy1) + regnum * 4); + csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4); + return 0; } @@ -626,117 +631,6 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } -/* Called every time the controller might need to be made - * aware of new link state. The PHY code conveys this - * information through variables in the phydev structure, and this - * function converts those variables into the appropriate - * register values, and can bring down the device if needed. - */ -static void altera_tse_adjust_link(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - int new_state = 0; - - /* only change config if there is a link */ - spin_lock(&priv->mac_cfg_lock); - if (phydev->link) { - /* Read old config */ - u32 cfg_reg = ioread32(&priv->mac_dev->command_config); - - /* Check duplex */ - if (phydev->duplex != priv->oldduplex) { - new_state = 1; - if (!(phydev->duplex)) - cfg_reg |= MAC_CMDCFG_HD_ENA; - else - cfg_reg &= ~MAC_CMDCFG_HD_ENA; - - netdev_dbg(priv->dev, "%s: Link duplex = 0x%x\n", - dev->name, phydev->duplex); - - priv->oldduplex = phydev->duplex; - } - - /* Check speed */ - if (phydev->speed != priv->oldspeed) { - new_state = 1; - switch (phydev->speed) { - case 1000: - cfg_reg |= MAC_CMDCFG_ETH_SPEED; - cfg_reg &= ~MAC_CMDCFG_ENA_10; - break; - case 100: - cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; - cfg_reg &= ~MAC_CMDCFG_ENA_10; - break; - case 10: - cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; - cfg_reg |= MAC_CMDCFG_ENA_10; - break; - default: - if (netif_msg_link(priv)) - netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n", - phydev->speed); - break; - } - priv->oldspeed = phydev->speed; - } - iowrite32(cfg_reg, &priv->mac_dev->command_config); - - if (!priv->oldlink) { - new_state = 1; - priv->oldlink = 1; - } - } else if (priv->oldlink) { - new_state = 1; - priv->oldlink = 0; - priv->oldspeed = 0; - priv->oldduplex = -1; - } - - if (new_state && netif_msg_link(priv)) - phy_print_status(phydev); - - spin_unlock(&priv->mac_cfg_lock); -} -static struct phy_device *connect_local_phy(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev = NULL; - char phy_id_fmt[MII_BUS_ID_SIZE + 3]; - - if (priv->phy_addr != POLL_PHY) { - snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, - priv->mdio->id, priv->phy_addr); - - netdev_dbg(dev, "trying to attach to %s\n", phy_id_fmt); - - phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link, - priv->phy_iface); - if (IS_ERR(phydev)) { - netdev_err(dev, "Could not attach to PHY\n"); - phydev = NULL; - } - - } else { - int ret; - phydev = phy_find_first(priv->mdio); - if (phydev == NULL) { - netdev_err(dev, "No PHY found\n"); - return phydev; - } - - ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link, - priv->phy_iface); - if (ret != 0) { - netdev_err(dev, "Could not attach to PHY\n"); - phydev = NULL; - } - } - return phydev; -} - static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); @@ -746,8 +640,10 @@ static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) priv->phy_iface = of_get_phy_mode(np); /* Avoid get phy addr and create mdio if no phy is present */ - if (!priv->phy_iface) + if (!priv->phy_iface) { + netdev_info(dev, "no PHY specified\n"); return 0; + } /* try to get PHY address from device tree, use PHY autodetection if * no valid address is given @@ -766,8 +662,7 @@ static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) } /* Create/attach to MDIO bus */ - ret = altera_tse_mdio_create(dev, - atomic_add_return(1, &instance_count)); + ret = altera_tse_mdio_create(dev, atomic_add_return(1, &instance_count)); if (ret) return -ENODEV; @@ -775,94 +670,6 @@ static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) return 0; } -/* Initialize driver's PHY state, and attach to the PHY - */ -static int init_phy(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev; - struct device_node *phynode; - bool fixed_link = false; - int rc = 0; - - /* Avoid init phy in case of no phy present */ - if (!priv->phy_iface) - return 0; - - priv->oldlink = 0; - priv->oldspeed = 0; - priv->oldduplex = -1; - - phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0); - - if (!phynode) { - /* check if a fixed-link is defined in device-tree */ - if (of_phy_is_fixed_link(priv->device->of_node)) { - rc = of_phy_register_fixed_link(priv->device->of_node); - if (rc < 0) { - netdev_err(dev, "cannot register fixed PHY\n"); - return rc; - } - - /* In the case of a fixed PHY, the DT node associated - * to the PHY is the Ethernet MAC DT node. - */ - phynode = of_node_get(priv->device->of_node); - fixed_link = true; - - netdev_dbg(dev, "fixed-link detected\n"); - phydev = of_phy_connect(dev, phynode, - &altera_tse_adjust_link, - 0, priv->phy_iface); - } else { - netdev_dbg(dev, "no phy-handle found\n"); - if (!priv->mdio) { - netdev_err(dev, "No phy-handle nor local mdio specified\n"); - return -ENODEV; - } - phydev = connect_local_phy(dev); - } - } else { - netdev_dbg(dev, "phy-handle found\n"); - phydev = of_phy_connect(dev, phynode, - &altera_tse_adjust_link, 0, priv->phy_iface); - } - of_node_put(phynode); - - if (!phydev) { - netdev_err(dev, "Could not find the PHY\n"); - if (fixed_link) - of_phy_deregister_fixed_link(priv->device->of_node); - return -ENODEV; - } - - /* Stop Advertising 1000BASE Capability if interface is not GMII - * Note: Checkpatch throws CHECKs for the camel case defines below, - * it's ok to ignore. - */ - if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) || - (priv->phy_iface == PHY_INTERFACE_MODE_RMII)) - phydev->advertising &= ~(SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full); - - /* Broken HW is sometimes missing the pull-up resistor on the - * MDIO line, which results in reads to non-existent devices returning - * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent - * device as well. If a fixed-link is used the phy_id is always 0. - * Note: phydev->phy_id is the result of reading the UID PHY registers. - */ - if ((phydev->phy_id == 0) && !fixed_link) { - netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id); - phy_disconnect(phydev); - return -ENODEV; - } - - netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n", - phydev->mdio.addr, phydev->phy_id, phydev->link); - - return 0; -} - static void tse_update_mac_addr(struct altera_tse_private *priv, u8 *addr) { u32 msb; @@ -1097,66 +904,6 @@ static void tse_set_rx_mode(struct net_device *dev) spin_unlock(&priv->mac_cfg_lock); } -/* Initialise (if necessary) the SGMII PCS component - */ -static int init_sgmii_pcs(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - int n; - unsigned int tmp_reg = 0; - - if (priv->phy_iface != PHY_INTERFACE_MODE_SGMII) - return 0; /* Nothing to do, not in SGMII mode */ - - /* The TSE SGMII PCS block looks a little like a PHY, it is - * mapped into the zeroth MDIO space of the MAC and it has - * ID registers like a PHY would. Sadly this is often - * configured to zeroes, so don't be surprised if it does - * show 0x00000000. - */ - - if (sgmii_pcs_scratch_test(priv, 0x0000) && - sgmii_pcs_scratch_test(priv, 0xffff) && - sgmii_pcs_scratch_test(priv, 0xa5a5) && - sgmii_pcs_scratch_test(priv, 0x5a5a)) { - netdev_info(dev, "PCS PHY ID: 0x%04x%04x\n", - sgmii_pcs_read(priv, MII_PHYSID1), - sgmii_pcs_read(priv, MII_PHYSID2)); - } else { - netdev_err(dev, "SGMII PCS Scratch memory test failed.\n"); - return -ENOMEM; - } - - /* Starting on page 5-29 of the MegaCore Function User Guide - * Set SGMII Link timer to 1.6ms - */ - sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40); - sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03); - - /* Enable SGMII Interface and Enable SGMII Auto Negotiation */ - sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, 0x3); - - /* Enable Autonegotiation */ - tmp_reg = sgmii_pcs_read(priv, MII_BMCR); - tmp_reg |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); - sgmii_pcs_write(priv, MII_BMCR, tmp_reg); - - /* Reset PCS block */ - tmp_reg |= BMCR_RESET; - sgmii_pcs_write(priv, MII_BMCR, tmp_reg); - for (n = 0; n < SGMII_PCS_SW_RESET_TIMEOUT; n++) { - if (!(sgmii_pcs_read(priv, MII_BMCR) & BMCR_RESET)) { - netdev_info(dev, "SGMII PCS block initialised OK\n"); - return 0; - } - udelay(1); - } - - /* We failed to reset the block, return a timeout */ - netdev_err(dev, "SGMII PCS block reset failed.\n"); - return -ETIMEDOUT; -} - /* Open and initialize the interface */ static int tse_open(struct net_device *dev) @@ -1181,14 +928,6 @@ static int tse_open(struct net_device *dev) netdev_warn(dev, "TSE revision %x\n", priv->revision); spin_lock(&priv->mac_cfg_lock); - /* no-op if MAC not operating in SGMII mode*/ - ret = init_sgmii_pcs(dev); - if (ret) { - netdev_err(dev, - "Cannot init the SGMII PCS (error: %d)\n", ret); - spin_unlock(&priv->mac_cfg_lock); - goto phy_error; - } ret = reset_mac(priv); /* Note that reset_mac will fail if the clocks are gated by the PHY @@ -1246,8 +985,12 @@ static int tse_open(struct net_device *dev) spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); - if (dev->phydev) - phy_start(dev->phydev); + ret = phylink_of_phy_connect(priv->phylink, priv->device->of_node, 0); + if (ret) { + netdev_err(dev, "could not connect phylink (%d)\n", ret); + goto tx_request_irq_error; + } + phylink_start(priv->phylink); napi_enable(&priv->napi); netif_start_queue(dev); @@ -1278,10 +1021,7 @@ static int tse_shutdown(struct net_device *dev) int ret; unsigned long int flags; - /* Stop the PHY */ - if (dev->phydev) - phy_stop(dev->phydev); - + phylink_stop(priv->phylink); netif_stop_queue(dev); napi_disable(&priv->napi); @@ -1327,6 +1067,209 @@ static struct net_device_ops altera_tse_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static void alt_tse_validate(struct net_device *ndev, unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + /* We only support SGMII, 802.3z and RGMII modes */ + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_SGMII && + !phy_interface_mode_is_8023z(state->interface) && + !phy_interface_mode_is_rgmii(state->interface)) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + + /* Asymmetric pause is unsupported */ + phylink_set(mask, Pause); + /* Half-duplex at speeds higher than 100Mbit is unsupported */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + if (!phy_interface_mode_is_8023z(state->interface)) { + /* 10M and 100M are only supported in non-802.3z mode */ + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_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 alt_tse_mac_link_state(struct net_device *ndev, + struct phylink_link_state *state) +{ + struct altera_tse_private *priv = netdev_priv(ndev); + u32 pa, stat, if_mode; + + stat = sgmii_pcs_read(priv, PCS_STATUS_REG); + if_mode = sgmii_pcs_read(priv, SGMII_PCS_IF_MODE); + pa = sgmii_pcs_read(priv, PCS_PARTNER_ABILITY_REG); + + state->an_complete = !!(stat & PCS_STATUS_AN_COMPLETE); + state->link = !!(stat & PCS_STATUS_LINK); + state->pause = MLO_PAUSE_NONE; + + if (if_mode & PCS_IF_MODE_SGMII_ENA) { // SGMII mode + if (pa & PCS_ABILITY_SGMII_COPPER_FD) + state->duplex = DUPLEX_FULL; + else + state->duplex = DUPLEX_HALF; + state->link = !!(pa & PCS_ABILITY_SGMII_COPPER_LINK_STATUS); + + switch (pa & PCS_ABILITY_SGMII_COPPER_SPEED_MASK) { + case PCS_ABILITY_SGMI_SPEED_10: + state->speed = SPEED_10; + break; + case PCS_ABILITY_SGMI_SPEED_100: + state->speed = SPEED_100; + break; + case PCS_ABILITY_SGMI_SPEED_1000: + state->speed = SPEED_1000; + break; + default: + netdev_warn(ndev, "bad copper mode\n"); + } + } else { // 1000BASE-X mode + if (pa & PCS_ABILITY_1000BASEX_FD) + state->duplex = DUPLEX_FULL; + else + state->duplex = DUPLEX_HALF; + + switch (pa & PCS_ABILITY_1000BASEX_PAUSE_MASK) { + case PCS_ABILITY_1000BASEX_PAUSE_SYM: + state->pause = MLO_PAUSE_SYM; + break; + case PCS_ABILITY_1000BASEX_PAUSE_ASYM: + state->pause = MLO_PAUSE_ASYM | MLO_PAUSE_TX; + break; + case PCS_ABILITY_1000BASEX_PAUSE_TXRX: + state->pause = MLO_PAUSE_TXRX_MASK; + break; + } + + state->speed = SPEED_1000; + } + + return 0; +} + +static void alt_tse_mac_an_restart(struct net_device *ndev) +{ + struct altera_tse_private *priv = netdev_priv(ndev); + u32 ctrl; + + ctrl = sgmii_pcs_read(priv, PCS_CTRL_REG); + ctrl |= (1 << 12) | (1 << 9); // enable AN and restart it + sgmii_pcs_write(priv, PCS_CTRL_REG, ctrl); + + sgmii_pcs_reset(priv); +} + +static void alt_tse_pcs_config(struct net_device *ndev, + const struct phylink_link_state *state) +{ + struct altera_tse_private *priv = netdev_priv(ndev); + u32 ctrl, if_mode; + + ctrl = sgmii_pcs_read(priv, PCS_CTRL_REG); + if_mode = sgmii_pcs_read(priv, SGMII_PCS_IF_MODE); + + sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40); + sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03); + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if_mode = PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA; + ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX ) { + ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); + if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA); + if_mode |= PCS_IF_MODE_SGMI_SPEED_1000; + } + + sgmii_pcs_write(priv, PCS_CTRL_REG, ctrl); + sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, if_mode); + + sgmii_pcs_reset(priv); +} + +static void alt_tse_mac_config(struct net_device *ndev, unsigned int mode, + const struct phylink_link_state *state) +{ + struct altera_tse_private *priv = netdev_priv(ndev); + u32 ctrl; + + + ctrl = csrrd32(priv->mac_dev, tse_csroffs(command_config)); + ctrl &= ~(MAC_CMDCFG_ENA_10 | MAC_CMDCFG_ETH_SPEED | MAC_CMDCFG_HD_ENA); + + if (state->duplex == DUPLEX_HALF) + ctrl |= MAC_CMDCFG_HD_ENA; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + switch (state->speed) { + case SPEED_1000: + ctrl |= MAC_CMDCFG_ETH_SPEED; + break; + case SPEED_100: + break; + case SPEED_10: + ctrl |= MAC_CMDCFG_ENA_10; + break; + case SPEED_UNKNOWN: + case 0: + break; + default: + netdev_warn(ndev, "wrong speed"); + return; + } + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX ) { + ctrl |= MAC_CMDCFG_ETH_SPEED; + } else { + netdev_warn(ndev, "wrong mode"); + return; + } + + alt_tse_pcs_config(ndev, state); + + spin_lock(&priv->mac_cfg_lock); + csrwr32(ctrl, priv->mac_dev, tse_csroffs(command_config)); + reset_mac(priv); + tse_set_mac(priv, true); + spin_unlock(&priv->mac_cfg_lock); +} + +static void alt_tse_mac_link_down(struct net_device *ndev, unsigned int mode, + phy_interface_t interface) +{ + netdev_info(ndev, "%s\n", __func__); +} + +static void alt_tse_mac_link_up(struct net_device *ndev, unsigned int mode, + phy_interface_t interface, + struct phy_device *phy) +{ + netdev_info(ndev, "%s\n", __func__); +} + +static const struct phylink_mac_ops alt_tse_phylink_ops = { + .validate = alt_tse_validate, + .mac_link_state = alt_tse_mac_link_state, + .mac_an_restart = alt_tse_mac_an_restart, + .mac_config = alt_tse_mac_config, + .mac_link_down = alt_tse_mac_link_down, + .mac_link_up = alt_tse_mac_link_up, +}; + static int request_and_map(struct platform_device *pdev, const char *name, struct resource **res, void __iomem **ptr) { @@ -1364,6 +1307,7 @@ static int altera_tse_probe(struct platform_device *pdev) int ret = -ENODEV; struct resource *control_port; struct resource *dma_res; + struct resource *pcs; struct altera_tse_private *priv; const unsigned char *macaddr; void __iomem *descmap; @@ -1475,6 +1419,11 @@ static int altera_tse_probe(struct platform_device *pdev) if (ret) goto err_free_netdev; + /* PCS address space */ + ret = request_and_map(pdev, "pcs", &pcs, &priv->pcs.iomem); + if (ret) + goto err_free_netdev; + /* Rx IRQ */ priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq"); @@ -1600,11 +1549,13 @@ static int altera_tse_probe(struct platform_device *pdev) (unsigned long) control_port->start, priv->rx_irq, priv->tx_irq); - ret = init_phy(ndev); - if (ret != 0) { - netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret); + priv->phylink = phylink_create(ndev, of_fwnode_handle(priv->device->of_node), + priv->phy_iface, &alt_tse_phylink_ops); + if (IS_ERR(priv->phylink)) { + dev_err(&pdev->dev, "failed to create phylink\n"); goto err_init_phy; } + return 0; err_init_phy: @@ -1624,16 +1575,10 @@ static int altera_tse_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct altera_tse_private *priv = netdev_priv(ndev); - if (ndev->phydev) { - phy_disconnect(ndev->phydev); - - if (of_phy_is_fixed_link(priv->device->of_node)) - of_phy_deregister_fixed_link(priv->device->of_node); - } - platform_set_drvdata(pdev, NULL); altera_tse_mdio_destroy(ndev); unregister_netdev(ndev); + phylink_destroy(priv->phylink); free_netdev(ndev); return 0; -- 2.28.0