summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2020-07-19 18:05:49 -0700
committerDavid S. Miller <davem@davemloft.net>2020-07-19 18:05:49 -0700
commitff9a8c48eb426881843be4042eef3a1a629b72a4 (patch)
treeb5d401626a0bc50d3feb4c897a2a864ed472fff7 /drivers
parenta050d82f5b04e585a6780b2a71faf1bd65d73928 (diff)
parent07095c025ac2f75c14550c287683ae90a8cf706e (diff)
Merge branch 'net-enetc-remove-bootloader-dependency'
Michael Walle says: ==================== net: enetc: remove bootloader dependency These patches were picked from the following series: https://lore.kernel.org/netdev/1567779344-30965-1-git-send-email-claudiu.manoil@nxp.com/ They have never been resent. I've picked them up, addressed Andrews comments, fixed some more bugs and asked Claudiu if I can keep their SOB tags; he agreed. I've tested this on our board which happens to have a bootloader which doesn't do the enetc setup in all cases. Though, only SGMII mode was tested. changes since v6: - dropped _LPA_ infix for USXGMII constants changes since v5: - fixed pcs->autoneg_complete and pcs->link assignment. Thanks Vladimir. changes since v4: - moved (and renamed) the USXGMII constants to include/uapi/linux/mdio.h. Suggested by Russell King. changes since v3: - rebased to latest net-next where devm_mdiobus_free() was removed. replace it by mdiobus_free(). The internal MDIO bus is optional, if there is any error, we try to run with the bootloader default PCS settings, thus in the error case, we need to free the mdiobus. changes since v2: - removed SOBs from "net: enetc: Initialize SerDes for SGMII and USXGMII protocols" because almost everything has changed. - get a phy_device for the internal PCS PHY so we can use the phy_ functions instead of raw mdiobus writes - reuse macros already defined in fsl_mdio.h, move missing bits from felix to fsl_mdio.h, because they share the same PCS PHY building block - added 2500BaseX mode (based on felix init routine) - changed xgmii mode to usxgmii mode, because it is actually USXGMII and felix does the same. - fixed devad, which is 0x1f (MMD_VEND2) changes since v1: - mdiobus id is '"imdio-%s", dev_name(dev)' because the plain dev_name() is used by the emdio. - use mdiobus_write() instead of imdio->write(imdio, ..), since this is already a full featured mdiobus - set phy_mask to ~0 to avoid scanning the bus - use phy_interface_mode_is_rgmii(phy_mode) to also include the RGMII modes with pad delays. - move enetc_imdio_init() to enetc_pf.c, there shouldn't be any other users, should it? - renamed serdes to SerDes - printing the error code of mdiobus_register() in the error path - call mdiobus_unregister() on _remove() - call devm_mdiobus_free() if mdiobus_register() fails, since an error is not fatal ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/dsa/ocelot/felix_vsc9959.c45
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h3
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.c188
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.h5
4 files changed, 184 insertions, 57 deletions
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 1bdf74fd8be4..9b720c8ddfc3 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -11,35 +11,15 @@
#include <linux/packing.h>
#include <net/pkt_sched.h>
#include <linux/iopoll.h>
+#include <linux/mdio.h>
#include <linux/pci.h>
#include "felix.h"
#define VSC9959_VCAP_IS2_CNT 1024
#define VSC9959_VCAP_IS2_ENTRY_WIDTH 376
#define VSC9959_VCAP_PORT_CNT 6
-
-/* TODO: should find a better place for these */
-#define USXGMII_BMCR_RESET BIT(15)
-#define USXGMII_BMCR_AN_EN BIT(12)
-#define USXGMII_BMCR_RST_AN BIT(9)
-#define USXGMII_BMSR_LNKS(status) (((status) & GENMASK(2, 2)) >> 2)
-#define USXGMII_BMSR_AN_CMPL(status) (((status) & GENMASK(5, 5)) >> 5)
-#define USXGMII_ADVERTISE_LNKS(x) (((x) << 15) & BIT(15))
-#define USXGMII_ADVERTISE_FDX BIT(12)
-#define USXGMII_ADVERTISE_SPEED(x) (((x) << 9) & GENMASK(11, 9))
-#define USXGMII_LPA_LNKS(lpa) ((lpa) >> 15)
-#define USXGMII_LPA_DUPLEX(lpa) (((lpa) & GENMASK(12, 12)) >> 12)
-#define USXGMII_LPA_SPEED(lpa) (((lpa) & GENMASK(11, 9)) >> 9)
-
#define VSC9959_TAS_GCL_ENTRY_MAX 63
-enum usxgmii_speed {
- USXGMII_SPEED_10 = 0,
- USXGMII_SPEED_100 = 1,
- USXGMII_SPEED_1000 = 2,
- USXGMII_SPEED_2500 = 4,
-};
-
static const u32 vsc9959_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x0089a0),
REG(ANA_VLANMASK, 0x0089a4),
@@ -845,11 +825,10 @@ static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs,
{
/* Configure device ability for the USXGMII Replicator */
phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
- USXGMII_ADVERTISE_SPEED(USXGMII_SPEED_2500) |
- USXGMII_ADVERTISE_LNKS(1) |
+ MDIO_USXGMII_2500FULL |
+ MDIO_USXGMII_LINK |
ADVERTISE_SGMII |
- ADVERTISE_LPACK |
- USXGMII_ADVERTISE_FDX);
+ ADVERTISE_LPACK);
}
void vsc9959_pcs_config(struct ocelot *ocelot, int port,
@@ -1063,8 +1042,8 @@ static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs,
return;
pcs->autoneg = true;
- pcs->autoneg_complete = USXGMII_BMSR_AN_CMPL(status);
- pcs->link = USXGMII_BMSR_LNKS(status);
+ pcs->autoneg_complete = !!(status & BMSR_ANEGCOMPLETE);
+ pcs->link = !!(status & BMSR_LSTATUS);
if (!pcs->link || !pcs->autoneg_complete)
return;
@@ -1073,24 +1052,24 @@ static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs,
if (lpa < 0)
return;
- switch (USXGMII_LPA_SPEED(lpa)) {
- case USXGMII_SPEED_10:
+ switch (lpa & MDIO_USXGMII_SPD_MASK) {
+ case MDIO_USXGMII_10:
pcs->speed = SPEED_10;
break;
- case USXGMII_SPEED_100:
+ case MDIO_USXGMII_100:
pcs->speed = SPEED_100;
break;
- case USXGMII_SPEED_1000:
+ case MDIO_USXGMII_1000:
pcs->speed = SPEED_1000;
break;
- case USXGMII_SPEED_2500:
+ case MDIO_USXGMII_2500:
pcs->speed = SPEED_2500;
break;
default:
break;
}
- if (USXGMII_LPA_DUPLEX(lpa))
+ if (lpa & MDIO_USXGMII_FULL_DUPLEX)
pcs->duplex = DUPLEX_FULL;
else
pcs->duplex = DUPLEX_HALF;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index fc357bc56835..135bf46354ea 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -224,6 +224,9 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PM0_MAXFRM 0x8014
#define ENETC_SET_TX_MTU(val) ((val) << 16)
#define ENETC_SET_MAXFRM(val) ((val) & 0xffff)
+
+#define ENETC_PM_IMDIO_BASE 0x8030
+
#define ENETC_PM0_IF_MODE 0x8300
#define ENETC_PMO_IFM_RG BIT(2)
#define ENETC_PM0_IFM_RLP (BIT(5) | BIT(11))
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 4fac57dbb3c8..5a08f66b123c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
+#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/fsl/enetc_mdio.h>
#include <linux/of_mdio.h>
@@ -481,7 +482,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));
@@ -497,9 +499,11 @@ 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_interface_mode_is_rgmii(phy_mode))
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)
+
+ if (phy_mode == PHY_INTERFACE_MODE_USXGMII)
enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_XGMII);
}
@@ -523,7 +527,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);
@@ -775,27 +779,27 @@ static void enetc_mdio_remove(struct enetc_pf *pf)
mdiobus_unregister(pf->mdio);
}
-static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
+static int enetc_of_get_phy(struct enetc_pf *pf)
{
- struct enetc_pf *pf = enetc_si_priv(priv->si);
- struct device_node *np = priv->dev->of_node;
+ struct device *dev = &pf->si->pdev->dev;
+ struct device_node *np = dev->of_node;
struct device_node *mdio_np;
int err;
- priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
- if (!priv->phy_node) {
+ 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");
@@ -803,15 +807,15 @@ 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;
}
}
- err = of_get_phy_mode(np, &priv->if_mode);
+ err = of_get_phy_mode(np, &pf->if_mode);
if (err) {
- dev_err(priv->dev, "missing phy type\n");
- of_node_put(priv->phy_node);
+ dev_err(dev, "missing phy type\n");
+ of_node_put(pf->phy_node);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
else
@@ -823,14 +827,143 @@ static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
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 int enetc_imdio_init(struct enetc_pf *pf, bool is_c45)
+{
+ struct device *dev = &pf->si->pdev->dev;
+ struct enetc_mdio_priv *mdio_priv;
+ struct phy_device *pcs;
+ struct mii_bus *bus;
+ int err;
+
+ bus = mdiobus_alloc_size(sizeof(*mdio_priv));
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = "Freescale ENETC internal MDIO Bus";
+ bus->read = enetc_mdio_read;
+ bus->write = enetc_mdio_write;
+ bus->parent = dev;
+ bus->phy_mask = ~0;
+ 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-imdio", dev_name(dev));
+
+ err = mdiobus_register(bus);
+ if (err) {
+ dev_err(dev, "cannot register internal MDIO bus (%d)\n", err);
+ goto free_mdio_bus;
+ }
+
+ pcs = get_phy_device(bus, 0, is_c45);
+ if (IS_ERR(pcs)) {
+ err = PTR_ERR(pcs);
+ dev_err(dev, "cannot get internal PCS PHY (%d)\n", err);
+ goto unregister_mdiobus;
+ }
+
+ pf->imdio = bus;
+ pf->pcs = pcs;
+
+ return 0;
+
+unregister_mdiobus:
+ mdiobus_unregister(bus);
+free_mdio_bus:
+ mdiobus_free(bus);
+ return err;
+}
+
+static void enetc_imdio_remove(struct enetc_pf *pf)
+{
+ if (pf->pcs)
+ put_device(&pf->pcs->mdio.dev);
+ if (pf->imdio) {
+ mdiobus_unregister(pf->imdio);
+ mdiobus_free(pf->imdio);
+ }
+}
+
+static void enetc_configure_sgmii(struct phy_device *pcs)
+{
+ /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
+ * for the MAC PCS in order to acknowledge the AN.
+ */
+ phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK);
+
+ phy_write(pcs, ENETC_PCS_IF_MODE,
+ ENETC_PCS_IF_MODE_SGMII_EN |
+ ENETC_PCS_IF_MODE_USE_SGMII_AN);
+
+ /* Adjust link timer for SGMII */
+ phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
+ phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
+
+ phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE);
+}
+
+static void enetc_configure_2500basex(struct phy_device *pcs)
+{
+ phy_write(pcs, ENETC_PCS_IF_MODE,
+ ENETC_PCS_IF_MODE_SGMII_EN |
+ ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500));
+
+ phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_RESET);
+}
+
+static void enetc_configure_usxgmii(struct phy_device *pcs)
+{
+ /* Configure device ability for the USXGMII Replicator */
+ phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
+ ADVERTISE_SGMII | ADVERTISE_LPACK |
+ MDIO_USXGMII_FULL_DUPLEX);
+
+ /* Restart PCS AN */
+ phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
+}
+
+static int enetc_configure_serdes(struct enetc_ndev_priv *priv)
+{
+ bool is_c45 = priv->if_mode == PHY_INTERFACE_MODE_USXGMII;
+ 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_2500BASEX &&
+ priv->if_mode != PHY_INTERFACE_MODE_USXGMII)
+ return 0;
+
+ err = enetc_imdio_init(pf, is_c45);
+ if (err)
+ return err;
+
+ switch (priv->if_mode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ enetc_configure_sgmii(pf->pcs);
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ enetc_configure_2500basex(pf->pcs);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ enetc_configure_usxgmii(pf->pcs);
+ break;
+ default:
+ dev_err(&pf->si->pdev->dev, "Unsupported link mode %s\n",
+ phy_modes(priv->if_mode));
+ }
+
+ return 0;
}
static int enetc_pf_probe(struct pci_dev *pdev,
@@ -864,6 +997,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);
@@ -878,6 +1015,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);
@@ -893,9 +1032,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)
@@ -906,7 +1045,6 @@ static int enetc_pf_probe(struct pci_dev *pdev,
return 0;
err_reg_netdev:
- enetc_of_put_phy(priv);
enetc_free_msix(priv);
err_alloc_msix:
enetc_free_si_resources(priv);
@@ -914,6 +1052,7 @@ err_alloc_si_res:
si->ndev = NULL;
free_netdev(ndev);
err_alloc_netdev:
+ enetc_of_put_phy(pf);
err_map_pf_space:
enetc_pci_remove(pdev);
@@ -932,8 +1071,9 @@ static void enetc_pf_remove(struct pci_dev *pdev)
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
+ enetc_imdio_remove(pf);
enetc_mdio_remove(pf);
- enetc_of_put_phy(priv);
+ enetc_of_put_phy(pf);
enetc_free_msix(priv);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 59e65a6f6c3e..0d0ee91282a5 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -44,6 +44,11 @@ struct enetc_pf {
DECLARE_BITMAP(active_vlans, VLAN_N_VID);
struct mii_bus *mdio; /* saved for cleanup */
+ struct mii_bus *imdio;
+ struct phy_device *pcs;
+
+ struct device_node *phy_node;
+ phy_interface_t if_mode;
};
int enetc_msg_psi_init(struct enetc_pf *pf);