summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/enetc/enetc_pf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/freescale/enetc/enetc_pf.c')
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.c226
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. */