diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/enetc/enetc_pf.c')
-rw-r--r-- | drivers/net/ethernet/freescale/enetc/enetc_pf.c | 226 |
1 files changed, 189 insertions, 37 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index ac62464e0416..99ad75800160 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -2,6 +2,7 @@ /* Copyright 2017-2019 NXP */ #include <linux/module.h> +#include <linux/fsl/enetc_mdio.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include "enetc_pf.h" @@ -507,7 +508,8 @@ static void enetc_port_si_configure(struct enetc_si *si) enetc_port_wr(hw, ENETC_PSIVLANFMR, ENETC_PSIVLANFMR_VS); } -static void enetc_configure_port_mac(struct enetc_hw *hw) +static void enetc_configure_port_mac(struct enetc_hw *hw, + phy_interface_t phy_mode) { enetc_port_wr(hw, ENETC_PM0_MAXFRM, ENETC_SET_MAXFRM(ENETC_RX_MAXFRM_SIZE)); @@ -523,10 +525,23 @@ static void enetc_configure_port_mac(struct enetc_hw *hw) ENETC_PM0_CMD_TXP | ENETC_PM0_PROMISC | ENETC_PM0_TX_EN | ENETC_PM0_RX_EN); /* set auto-speed for RGMII */ - if (enetc_port_rd(hw, ENETC_PM0_IF_MODE) & ENETC_PMO_IFM_RG) + if (enetc_port_rd(hw, ENETC_PM0_IF_MODE) & ENETC_PMO_IFM_RG || + phy_mode == PHY_INTERFACE_MODE_RGMII) { enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_RGAUTO); - if (enetc_global_rd(hw, ENETC_G_EPFBLPR(1)) == ENETC_G_EPFBLPR1_XGMII) + enetc_port_wr(hw, ENETC_PM1_IF_MODE, ENETC_PM0_IFM_RGAUTO); + } + + if (phy_mode == PHY_INTERFACE_MODE_XGMII || + phy_mode == PHY_INTERFACE_MODE_USXGMII) { enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_XGMII); + enetc_port_wr(hw, ENETC_PM1_IF_MODE, ENETC_PM0_IFM_XGMII); + } + + /* on LS1028A the MAC Rx FIFO defaults to value 2, which is too high and + * may lead to Rx lock-up under traffic. Set it to 1 instead, as + * recommended by the hardware team. + */ + enetc_port_wr(hw, ENETC_PM0_RX_FIFO, ENETC_PM0_RX_FIFO_VAL); } static void enetc_configure_port_pmac(struct enetc_hw *hw) @@ -549,7 +564,7 @@ static void enetc_configure_port(struct enetc_pf *pf) enetc_configure_port_pmac(hw); - enetc_configure_port_mac(hw); + enetc_configure_port_mac(hw, pf->if_mode); enetc_port_si_configure(pf->si); @@ -740,38 +755,86 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, ndev->features &= ~NETIF_F_HW_CSUM; } - ndev->priv_flags |= IFF_UNICAST_FLT; + ndev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; + + if (si->hw_features & ENETC_SI_F_QBV) + priv->active_offloads |= ENETC_F_QBV; + + if (enetc_tsn_is_enabled() && (si->hw_features & ENETC_SI_F_QBU)) + priv->active_offloads |= ENETC_F_QBU; /* pick up primary MAC address from SI */ enetc_get_primary_mac_addr(&si->hw, ndev->dev_addr); } -static int enetc_of_get_phy(struct enetc_ndev_priv *priv) +static int enetc_mdio_probe(struct enetc_pf *pf) { - struct enetc_pf *pf = enetc_si_priv(priv->si); - struct device_node *np = priv->dev->of_node; - struct device_node *mdio_np; + struct device *dev = &pf->si->pdev->dev; + struct enetc_mdio_priv *mdio_priv; + struct device_node *np; + struct mii_bus *bus; int err; + bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); + if (!bus) + return -ENOMEM; + + bus->name = "Freescale ENETC MDIO Bus"; + bus->read = enetc_mdio_read; + bus->write = enetc_mdio_write; + bus->parent = dev; + mdio_priv = bus->priv; + mdio_priv->hw = &pf->si->hw; + mdio_priv->mdio_base = ENETC_EMDIO_BASE; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + + np = of_get_child_by_name(dev->of_node, "mdio"); if (!np) { - dev_err(priv->dev, "missing ENETC port node\n"); - return -ENODEV; + dev_err(dev, "MDIO node missing\n"); + return -EINVAL; } - priv->phy_node = of_parse_phandle(np, "phy-handle", 0); - if (!priv->phy_node) { + err = of_mdiobus_register(bus, np); + if (err) { + of_node_put(np); + dev_err(dev, "cannot register MDIO bus\n"); + return err; + } + + of_node_put(np); + pf->mdio = bus; + + return 0; +} + +static void enetc_mdio_remove(struct enetc_pf *pf) +{ + if (pf->mdio) + mdiobus_unregister(pf->mdio); +} + +static int enetc_of_get_phy(struct enetc_pf *pf) +{ + struct device *dev = &pf->si->pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *mdio_np; + int phy_mode; + int err; + + pf->phy_node = of_parse_phandle(np, "phy-handle", 0); + if (!pf->phy_node) { if (!of_phy_is_fixed_link(np)) { - dev_err(priv->dev, "PHY not specified\n"); + dev_err(dev, "PHY not specified\n"); return -ENODEV; } err = of_phy_register_fixed_link(np); if (err < 0) { - dev_err(priv->dev, "fixed link registration failed\n"); + dev_err(dev, "fixed link registration failed\n"); return err; } - priv->phy_node = of_node_get(np); + pf->phy_node = of_node_get(np); } mdio_np = of_get_child_by_name(np, "mdio"); @@ -779,34 +842,109 @@ static int enetc_of_get_phy(struct enetc_ndev_priv *priv) of_node_put(mdio_np); err = enetc_mdio_probe(pf); if (err) { - of_node_put(priv->phy_node); + of_node_put(pf->phy_node); return err; } } - priv->if_mode = of_get_phy_mode(np); - if ((int)priv->if_mode < 0) { - dev_err(priv->dev, "missing phy type\n"); - of_node_put(priv->phy_node); - if (of_phy_is_fixed_link(np)) - of_phy_deregister_fixed_link(np); - else - enetc_mdio_remove(pf); - - return -EINVAL; - } + phy_mode = of_get_phy_mode(np); + if (phy_mode < 0) + pf->if_mode = PHY_INTERFACE_MODE_NA; /* fixed link */ + else + pf->if_mode = phy_mode; return 0; } -static void enetc_of_put_phy(struct enetc_ndev_priv *priv) +static void enetc_of_put_phy(struct enetc_pf *pf) { - struct device_node *np = priv->dev->of_node; + struct device_node *np = pf->si->pdev->dev.of_node; if (np && of_phy_is_fixed_link(np)) of_phy_deregister_fixed_link(np); - if (priv->phy_node) - of_node_put(priv->phy_node); + if (pf->phy_node) + of_node_put(pf->phy_node); +} + +static void enetc_configure_sgmii(struct mii_bus *imdio) +{ + /* Set to SGMII mode, use AN */ + imdio->write(imdio, 0, ENETC_PCS_IF_MODE, + ENETC_PCS_IF_MODE_SGMII_AN); + + /* Dev ability - SGMII */ + imdio->write(imdio, 0, ENETC_PCS_DEV_ABILITY, + ENETC_PCS_DEV_ABILITY_SGMII); + + /* Adjust link timer for SGMII */ + imdio->write(imdio, 0, ENETC_PCS_LINK_TIMER1, + ENETC_PCS_LINK_TIMER1_VAL); + imdio->write(imdio, 0, ENETC_PCS_LINK_TIMER2, + ENETC_PCS_LINK_TIMER2_VAL); + + /* restart PCS AN */ + imdio->write(imdio, 0, ENETC_PCS_CR, + ENETC_PCS_CR_RESET_AN | ENETC_PCS_CR_DEF_VAL); +} + +static void enetc_configure_sxgmii(struct mii_bus *imdio) +{ + /* Dev ability - SXGMII */ + imdio->write(imdio, 0, MII_ADDR_C45 | (MDIO_MMD_VEND2 << 16) | + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); + + /* Restart PCS AN */ + imdio->write(imdio, 0, MII_ADDR_C45 | (MDIO_MMD_VEND2 << 16) | + ENETC_PCS_CR, + ENETC_PCS_CR_LANE_RESET | ENETC_PCS_CR_RESET_AN); +} + +static int enetc_imdio_init(struct enetc_pf *pf) +{ + struct device *dev = &pf->si->pdev->dev; + struct enetc_mdio_priv *mdio_priv; + struct mii_bus *bus; + + bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); + if (!bus) + return -ENOMEM; + + bus->name = "FSL ENETC internal MDIO Bus"; + bus->read = enetc_mdio_read; + bus->write = enetc_mdio_write; + bus->parent = dev; + mdio_priv = bus->priv; + mdio_priv->hw = &pf->si->hw; + mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + + pf->imdio = bus; + + return 0; +} + +static int enetc_configure_serdes(struct enetc_ndev_priv *priv) +{ + struct enetc_pf *pf = enetc_si_priv(priv->si); + int err; + + if (priv->if_mode != PHY_INTERFACE_MODE_SGMII && + priv->if_mode != PHY_INTERFACE_MODE_XGMII && + priv->if_mode != PHY_INTERFACE_MODE_USXGMII) + return 0; + + err = enetc_imdio_init(pf); + if (err) + return err; + + if (priv->if_mode == PHY_INTERFACE_MODE_SGMII) + enetc_configure_sgmii(pf->imdio); + + if (priv->if_mode == PHY_INTERFACE_MODE_XGMII || + priv->if_mode == PHY_INTERFACE_MODE_USXGMII) + enetc_configure_sxgmii(pf->imdio); + + return 0; } /* Initialize the entire shared memory for the flow steering entries @@ -907,6 +1045,10 @@ static int enetc_pf_probe(struct pci_dev *pdev, pf->si = si; pf->total_vfs = pci_sriov_get_totalvfs(pdev); + err = enetc_of_get_phy(pf); + if (err) + dev_warn(&pdev->dev, "Fallback to PHY-less operation\n"); + enetc_configure_port(pf); enetc_get_si_caps(si); @@ -921,6 +1063,8 @@ static int enetc_pf_probe(struct pci_dev *pdev, enetc_pf_netdev_setup(si, ndev, &enetc_ndev_ops); priv = netdev_priv(ndev); + priv->phy_node = pf->phy_node; + priv->if_mode = pf->if_mode; enetc_init_si_rings_params(priv); @@ -954,9 +1098,9 @@ static int enetc_pf_probe(struct pci_dev *pdev, goto err_alloc_msix; } - err = enetc_of_get_phy(priv); + err = enetc_configure_serdes(priv); if (err) - dev_warn(&pdev->dev, "Fallback to PHY-less operation\n"); + dev_warn(&pdev->dev, "Attempted serdes config but failed\n"); err = register_netdev(ndev); if (err) @@ -967,11 +1111,11 @@ static int enetc_pf_probe(struct pci_dev *pdev, netif_info(priv, probe, ndev, "%s v%s\n", enetc_drv_name, enetc_drv_ver); + enetc_tsn_pf_init(ndev, pdev); + return 0; err_reg_netdev: - enetc_mdio_remove(pf); - enetc_of_put_phy(priv); enetc_free_msix(priv); err_config_si: err_init_port_rss: @@ -982,6 +1126,8 @@ err_alloc_si_res: si->ndev = NULL; free_netdev(ndev); err_alloc_netdev: + enetc_mdio_remove(pf); + enetc_of_put_phy(pf); err_device_disabled: err_map_pf_space: enetc_pci_remove(pdev); @@ -1002,10 +1148,12 @@ static void enetc_pf_remove(struct pci_dev *pdev) netif_info(priv, drv, si->ndev, "%s v%s remove\n", enetc_drv_name, enetc_drv_ver); + enetc_tsn_pf_deinit(si->ndev); + unregister_netdev(si->ndev); enetc_mdio_remove(pf); - enetc_of_put_phy(priv); + enetc_of_put_phy(pf); enetc_free_msix(priv); @@ -1016,6 +1164,10 @@ static void enetc_pf_remove(struct pci_dev *pdev) enetc_pci_remove(pdev); } +/* Lock for MDIO access errata on LS1028A */ +DEFINE_RWLOCK(enetc_mdio_lock); +EXPORT_SYMBOL_GPL(enetc_mdio_lock); + static const struct pci_device_id enetc_pf_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PF) }, { 0, } /* End of table. */ |