aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch')
-rw-r--r--target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch442
1 files changed, 0 insertions, 442 deletions
diff --git a/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch b/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch
deleted file mode 100644
index e2de403c2d..0000000000
--- a/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch
+++ /dev/null
@@ -1,442 +0,0 @@
-From 0e219a6aa07754d81a25c9e4408d81d194cd2000 Mon Sep 17 00:00:00 2001
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-Date: Fri, 22 Nov 2019 13:45:52 +0200
-Subject: [PATCH] net: dsa: felix: Add PCS operations for PHYLINK
-
-This removes the bootloader dependency for SGMII PCS pre-configuration,
-as well as adds support for monitoring the in-band SGMII AN between the
-PCS and the system-side link partner (PHY or other MAC).
-
-Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
----
- drivers/net/dsa/ocelot/felix.c | 23 ++-
- drivers/net/dsa/ocelot/felix.h | 7 +-
- drivers/net/dsa/ocelot/felix_vsc9959.c | 292 ++++++++++++++++++++++++++++++++-
- 3 files changed, 314 insertions(+), 8 deletions(-)
-
---- a/drivers/net/dsa/ocelot/felix.c
-+++ b/drivers/net/dsa/ocelot/felix.c
-@@ -265,7 +265,7 @@ static int felix_get_ts_info(struct dsa_
- static int felix_init_structs(struct felix *felix, int num_phys_ports)
- {
- struct ocelot *ocelot = &felix->ocelot;
-- resource_size_t base;
-+ resource_size_t switch_base;
- int port, i, err;
-
- ocelot->num_phys_ports = num_phys_ports;
-@@ -281,7 +281,8 @@ static int felix_init_structs(struct fel
- ocelot->ops = felix->info->ops;
- ocelot->quirks = felix->info->quirks;
-
-- base = pci_resource_start(felix->pdev, felix->info->pci_bar);
-+ switch_base = pci_resource_start(felix->pdev,
-+ felix->info->switch_pci_bar);
-
- for (i = 0; i < TARGET_MAX; i++) {
- struct regmap *target;
-@@ -292,8 +293,8 @@ static int felix_init_structs(struct fel
-
- res = &felix->info->target_io_res[i];
- res->flags = IORESOURCE_MEM;
-- res->start += base;
-- res->end += base;
-+ res->start += switch_base;
-+ res->end += switch_base;
-
- target = ocelot_regmap_init(ocelot, res);
- if (IS_ERR(target)) {
-@@ -327,8 +328,8 @@ static int felix_init_structs(struct fel
-
- res = &felix->info->port_io_res[port];
- res->flags = IORESOURCE_MEM;
-- res->start += base;
-- res->end += base;
-+ res->start += switch_base;
-+ res->end += switch_base;
-
- port_regs = devm_ioremap_resource(ocelot->dev, res);
- if (IS_ERR(port_regs)) {
-@@ -342,6 +343,12 @@ static int felix_init_structs(struct fel
- ocelot->ports[port] = ocelot_port;
- }
-
-+ if (felix->info->mdio_bus_alloc) {
-+ err = felix->info->mdio_bus_alloc(ocelot);
-+ if (err < 0)
-+ return err;
-+ }
-+
- return 0;
- }
-
-@@ -377,6 +384,10 @@ static int felix_setup(struct dsa_switch
- static void felix_teardown(struct dsa_switch *ds)
- {
- struct ocelot *ocelot = ds->priv;
-+ struct felix *felix = ocelot_to_felix(ocelot);
-+
-+ if (felix->imdio)
-+ mdiobus_unregister(felix->imdio);
-
- /* stop workqueue thread */
- ocelot_deinit(ocelot);
---- a/drivers/net/dsa/ocelot/felix.h
-+++ b/drivers/net/dsa/ocelot/felix.h
-@@ -10,6 +10,7 @@
- struct felix_info {
- struct resource *target_io_res;
- struct resource *port_io_res;
-+ struct resource *imdio_res;
- const struct reg_field *regfields;
- const u32 *const *map;
- const struct ocelot_ops *ops;
-@@ -17,8 +18,10 @@ struct felix_info {
- const struct ocelot_stat_layout *stats_layout;
- unsigned int num_stats;
- int num_ports;
-- int pci_bar;
-+ int switch_pci_bar;
-+ int imdio_pci_bar;
- unsigned long quirks;
-+ int (*mdio_bus_alloc)(struct ocelot *ocelot);
- };
-
- extern struct felix_info felix_info_vsc9959;
-@@ -33,6 +36,8 @@ struct felix {
- struct pci_dev *pdev;
- struct felix_info *info;
- struct ocelot ocelot;
-+ struct mii_bus *imdio;
-+ struct phy_device **pcs;
- };
-
- #endif
---- a/drivers/net/dsa/ocelot/felix_vsc9959.c
-+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
-@@ -2,6 +2,7 @@
- /* Copyright 2017 Microsemi Corporation
- * Copyright 2018-2019 NXP Semiconductors
- */
-+#include <linux/fsl/enetc_mdio.h>
- #include <soc/mscc/ocelot_sys.h>
- #include <soc/mscc/ocelot.h>
- #include <linux/iopoll.h>
-@@ -390,6 +391,15 @@ static struct resource vsc9959_port_io_r
- },
- };
-
-+/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an
-+ * SGMII/QSGMII MAC PCS can be found.
-+ */
-+static struct resource vsc9959_imdio_res = {
-+ .start = 0x8030,
-+ .end = 0x8040,
-+ .name = "imdio",
-+};
-+
- static const struct reg_field vsc9959_regfields[] = {
- [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6),
- [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5),
-@@ -569,13 +579,291 @@ static int vsc9959_reset(struct ocelot *
- return 0;
- }
-
-+void vsc9959_pcs_validate(struct ocelot *ocelot, int port,
-+ unsigned long *supported,
-+ struct phylink_link_state *state)
-+{
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
-+
-+ if (state->interface != PHY_INTERFACE_MODE_NA &&
-+ state->interface != PHY_INTERFACE_MODE_GMII &&
-+ state->interface != PHY_INTERFACE_MODE_SGMII &&
-+ state->interface != PHY_INTERFACE_MODE_QSGMII &&
-+ state->interface != PHY_INTERFACE_MODE_USXGMII) {
-+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
-+ return;
-+ }
-+
-+ /* No half-duplex. */
-+ phylink_set_port_modes(mask);
-+ phylink_set(mask, Autoneg);
-+ phylink_set(mask, Pause);
-+ phylink_set(mask, Asym_Pause);
-+ phylink_set(mask, 10baseT_Full);
-+ phylink_set(mask, 100baseT_Full);
-+ phylink_set(mask, 1000baseT_Full);
-+ phylink_set(mask, 1000baseX_Full);
-+ phylink_set(mask, 2500baseT_Full);
-+ phylink_set(mask, 2500baseX_Full);
-+ phylink_set(mask, 1000baseKX_Full);
-+
-+ bitmap_and(supported, supported, mask,
-+ __ETHTOOL_LINK_MODE_MASK_NBITS);
-+ bitmap_and(state->advertising, state->advertising, mask,
-+ __ETHTOOL_LINK_MODE_MASK_NBITS);
-+}
-+
-+static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port)
-+{
-+ struct felix *felix = ocelot_to_felix(ocelot);
-+ struct phy_device *pcs = felix->pcs[port];
-+
-+ if (!pcs)
-+ return;
-+
-+ phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART);
-+}
-+
-+static void vsc9959_pcs_init_sgmii(struct phy_device *pcs,
-+ unsigned int link_an_mode,
-+ const struct phylink_link_state *state)
-+{
-+ /* 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);
-+
-+ /* Set to SGMII mode, use AN */
-+ phy_write(pcs, ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_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_SPEED1000 |
-+ BMCR_FULLDPLX |
-+ BMCR_ANENABLE);
-+}
-+
-+#define ADVERTISE_USXGMII_FDX BIT(12)
-+
-+static void vsc9959_pcs_init_sxgmii(struct phy_device *pcs,
-+ unsigned int link_an_mode,
-+ const struct phylink_link_state *state)
-+{
-+ /* Configure device ability for the USXGMII Replicator */
-+ phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
-+ ADVERTISE_SGMII |
-+ ADVERTISE_LPACK |
-+ ADVERTISE_USXGMII_FDX);
-+}
-+
-+static void vsc9959_pcs_init(struct ocelot *ocelot, int port,
-+ unsigned int link_an_mode,
-+ const struct phylink_link_state *state)
-+{
-+ struct felix *felix = ocelot_to_felix(ocelot);
-+ struct phy_device *pcs = felix->pcs[port];
-+
-+ if (!pcs)
-+ return;
-+
-+ if (link_an_mode == MLO_AN_FIXED) {
-+ phydev_err(pcs, "Fixed modes are not yet supported.\n");
-+ return;
-+ }
-+
-+ pcs->interface = state->interface;
-+ if (pcs->interface == PHY_INTERFACE_MODE_USXGMII)
-+ pcs->is_c45 = true;
-+ else
-+ pcs->is_c45 = false;
-+
-+ /* The PCS does not implement the BMSR register fully, so capability
-+ * detection via genphy_read_abilities does not work. Since we can get
-+ * the PHY config word from the LPA register though, there is still
-+ * value in using the generic phy_resolve_aneg_linkmode function. So
-+ * populate the supported and advertising link modes manually here.
-+ */
-+ linkmode_set_bit_array(phy_basic_ports_array,
-+ ARRAY_SIZE(phy_basic_ports_array),
-+ pcs->supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pcs->supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported);
-+ phy_advertise_supported(pcs);
-+
-+ switch (pcs->interface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ vsc9959_pcs_init_sgmii(pcs, link_an_mode, state);
-+ break;
-+ case PHY_INTERFACE_MODE_USXGMII:
-+ vsc9959_pcs_init_sxgmii(pcs, link_an_mode, state);
-+ break;
-+ default:
-+ dev_err(ocelot->dev, "Unsupported link mode %s\n",
-+ phy_modes(pcs->interface));
-+ }
-+}
-+
-+static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port,
-+ struct phylink_link_state *state)
-+{
-+ struct felix *felix = ocelot_to_felix(ocelot);
-+ struct phy_device *pcs = felix->pcs[port];
-+ int err;
-+
-+ if (!pcs)
-+ return;
-+
-+ /* Reading PCS status not yet supported for USXGMII */
-+ if (pcs->is_c45) {
-+ state->link = 1;
-+ return;
-+ }
-+
-+ pcs->speed = SPEED_UNKNOWN;
-+ pcs->duplex = DUPLEX_UNKNOWN;
-+ pcs->pause = 0;
-+ pcs->asym_pause = 0;
-+
-+ err = genphy_update_link(pcs);
-+ if (err < 0)
-+ return;
-+
-+ if (pcs->autoneg_complete) {
-+ u16 lpa = phy_read(pcs, MII_LPA);
-+
-+ switch (state->interface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa);
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ phy_resolve_aneg_linkmode(pcs);
-+ }
-+
-+ state->an_complete = pcs->autoneg_complete;
-+ state->an_enabled = pcs->autoneg;
-+ state->link = pcs->link;
-+ state->duplex = pcs->duplex;
-+ state->speed = pcs->speed;
-+ /* SGMII AN does not negotiate flow control, but that's ok,
-+ * since phylink already knows that, and does:
-+ * link_state.pause |= pl->phy_state.pause;
-+ */
-+ state->pause = pcs->pause;
-+
-+ dev_dbg(ocelot->dev,
-+ "%s: mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n",
-+ __func__,
-+ phy_modes(state->interface),
-+ phy_speed_to_str(state->speed),
-+ phy_duplex_to_str(state->duplex),
-+ __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
-+ __ETHTOOL_LINK_MODE_MASK_NBITS, state->lp_advertising,
-+ state->link, state->an_enabled, state->an_complete);
-+}
-+
- static const struct ocelot_ops vsc9959_ops = {
- .reset = vsc9959_reset,
-+ .pcs_init = vsc9959_pcs_init,
-+ .pcs_an_restart = vsc9959_pcs_an_restart,
-+ .pcs_link_state = vsc9959_pcs_link_state,
-+ .pcs_validate = vsc9959_pcs_validate,
- };
-
-+static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
-+{
-+ struct felix *felix = ocelot_to_felix(ocelot);
-+ struct enetc_mdio_priv *mdio_priv;
-+ struct device *dev = ocelot->dev;
-+ resource_size_t imdio_base;
-+ void __iomem *imdio_regs;
-+ struct resource *res;
-+ struct enetc_hw *hw;
-+ struct mii_bus *bus;
-+ int port;
-+ int rc;
-+
-+ felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
-+ sizeof(struct phy_device),
-+ GFP_KERNEL);
-+ if (!felix->pcs) {
-+ dev_err(dev, "failed to allocate array for PCS PHYs\n");
-+ return -ENOMEM;
-+ }
-+
-+ imdio_base = pci_resource_start(felix->pdev,
-+ felix->info->imdio_pci_bar);
-+
-+ res = felix->info->imdio_res;
-+ res->flags = IORESOURCE_MEM;
-+ res->start += imdio_base;
-+ res->end += imdio_base;
-+
-+ imdio_regs = devm_ioremap_resource(dev, res);
-+ if (IS_ERR(imdio_regs)) {
-+ dev_err(dev, "failed to map internal MDIO registers\n");
-+ return PTR_ERR(imdio_regs);
-+ }
-+
-+ hw = enetc_hw_alloc(dev, imdio_regs);
-+ if (IS_ERR(hw)) {
-+ dev_err(dev, "failed to allocate ENETC HW structure\n");
-+ return PTR_ERR(hw);
-+ }
-+
-+ bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
-+ if (!bus)
-+ return -ENOMEM;
-+
-+ bus->name = "VSC9959 internal MDIO bus";
-+ bus->read = enetc_mdio_read;
-+ bus->write = enetc_mdio_write;
-+ bus->parent = dev;
-+ mdio_priv = bus->priv;
-+ mdio_priv->hw = hw;
-+ /* This gets added to imdio_regs, which already maps addresses
-+ * starting with the proper offset.
-+ */
-+ mdio_priv->mdio_base = 0;
-+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
-+
-+ /* Needed in order to initialize the bus mutex lock */
-+ rc = mdiobus_register(bus);
-+ if (rc < 0) {
-+ dev_err(dev, "failed to register MDIO bus\n");
-+ return rc;
-+ }
-+
-+ felix->imdio = bus;
-+
-+ for (port = 0; port < felix->info->num_ports; port++) {
-+ struct phy_device *pcs;
-+ bool is_c45 = false;
-+
-+ pcs = get_phy_device(felix->imdio, port, is_c45);
-+ if (IS_ERR(pcs))
-+ continue;
-+
-+ felix->pcs[port] = pcs;
-+
-+ dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
-+ }
-+
-+ return 0;
-+}
-+
- struct felix_info felix_info_vsc9959 = {
- .target_io_res = vsc9959_target_io_res,
- .port_io_res = vsc9959_port_io_res,
-+ .imdio_res = &vsc9959_imdio_res,
- .regfields = vsc9959_regfields,
- .map = vsc9959_regmap,
- .ops = &vsc9959_ops,
-@@ -583,6 +871,8 @@ struct felix_info felix_info_vsc9959 = {
- .num_stats = ARRAY_SIZE(vsc9959_stats_layout),
- .shared_queue_sz = 128 * 1024,
- .num_ports = 6,
-- .pci_bar = 4,
-+ .switch_pci_bar = 4,
-+ .imdio_pci_bar = 0,
- .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION,
-+ .mdio_bus_alloc = vsc9959_mdio_bus_alloc,
- };