mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-26 08:51:13 +00:00
690 lines
22 KiB
Diff
690 lines
22 KiB
Diff
|
From 0a2b7489bf60d24a54e16147b416f339ebe4f511 Mon Sep 17 00:00:00 2001
|
||
|
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||
|
Date: Mon, 18 Nov 2019 18:05:01 +0200
|
||
|
Subject: [PATCH] net: mscc: ocelot: convert to PHYLINK
|
||
|
|
||
|
This patch reworks ocelot_board.c (aka the MIPS on the VSC7514) to
|
||
|
register a PHYLINK instance for each port. The registration code is
|
||
|
local to the VSC7514, but the PHYLINK callback implementation is common
|
||
|
so that the Felix DSA front-end can use it as well (but DSA does its own
|
||
|
registration).
|
||
|
|
||
|
Now Felix can use native PHYLINK callbacks instead of the PHYLIB
|
||
|
adaptation layer in DSA, which had issues supporting fixed-link slave
|
||
|
ports (no struct phy_device to pass to the adjust_link callback), as
|
||
|
well as fixed-link CPU port at 2.5Gbps.
|
||
|
|
||
|
The old code from ocelot_port_enable and ocelot_port_disable has been
|
||
|
moved into ocelot_phylink_mac_link_up and ocelot_phylink_mac_link_down.
|
||
|
|
||
|
The PHY connect operation has been moved from ocelot_port_open to
|
||
|
mscc_ocelot_probe in ocelot_board.c.
|
||
|
|
||
|
The phy_set_mode_ext() call for the SerDes PHY has also been moved into
|
||
|
mscc_ocelot_probe from ocelot_port_open, and since that was the only
|
||
|
reason why a reference to it was kept in ocelot_port_private, that
|
||
|
reference was removed.
|
||
|
|
||
|
Again, the usage of phy_interface_t phy_mode is now local to
|
||
|
mscc_ocelot_probe only, after moving the PHY connect operation.
|
||
|
So it was also removed from ocelot_port_private.
|
||
|
*Maybe* in the future, it can be added back to the common struct
|
||
|
ocelot_port, with the purpose of validating mismatches between
|
||
|
state->phy_interface and ocelot_port->phy_mode in PHYLINK callbacks.
|
||
|
But at the moment that is not critical, since other DSA drivers are not
|
||
|
doing that either. No SFP+ modules are in use with Felix/Ocelot yet, to
|
||
|
my knowledge.
|
||
|
|
||
|
In-band AN is not yet supported, due to the fact that this is a mostly
|
||
|
mechanical patch for the moment. The mac_an_restart PHYLINK operation
|
||
|
needs to be implemented, as well as mac_link_state. Both are SerDes
|
||
|
specific, and Felix does not have its PCS configured yet (it works just
|
||
|
by virtue of U-Boot initialization at the moment).
|
||
|
|
||
|
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||
|
---
|
||
|
drivers/net/dsa/ocelot/felix.c | 65 +++++++++----
|
||
|
drivers/net/ethernet/mscc/Kconfig | 2 +-
|
||
|
drivers/net/ethernet/mscc/ocelot.c | 150 ++++++++++++++++--------------
|
||
|
drivers/net/ethernet/mscc/ocelot.h | 13 +--
|
||
|
drivers/net/ethernet/mscc/ocelot_board.c | 154 +++++++++++++++++++++++++++----
|
||
|
include/soc/mscc/ocelot.h | 22 ++++-
|
||
|
6 files changed, 289 insertions(+), 117 deletions(-)
|
||
|
|
||
|
--- a/drivers/net/dsa/ocelot/felix.c
|
||
|
+++ b/drivers/net/dsa/ocelot/felix.c
|
||
|
@@ -58,14 +58,6 @@ static int felix_set_ageing_time(struct
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static void felix_adjust_link(struct dsa_switch *ds, int port,
|
||
|
- struct phy_device *phydev)
|
||
|
-{
|
||
|
- struct ocelot *ocelot = ds->priv;
|
||
|
-
|
||
|
- ocelot_adjust_link(ocelot, port, phydev);
|
||
|
-}
|
||
|
-
|
||
|
static int felix_fdb_dump(struct dsa_switch *ds, int port,
|
||
|
dsa_fdb_dump_cb_t *cb, void *data)
|
||
|
{
|
||
|
@@ -185,21 +177,59 @@ static int felix_tsn_enable(struct dsa_p
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
-static int felix_port_enable(struct dsa_switch *ds, int port,
|
||
|
- struct phy_device *phy)
|
||
|
+static void felix_phylink_validate(struct dsa_switch *ds, int port,
|
||
|
+ unsigned long *supported,
|
||
|
+ struct phylink_link_state *state)
|
||
|
{
|
||
|
struct ocelot *ocelot = ds->priv;
|
||
|
|
||
|
- ocelot_port_enable(ocelot, port, phy);
|
||
|
+ ocelot_phylink_validate(ocelot, port, supported, state);
|
||
|
+}
|
||
|
+
|
||
|
+static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port,
|
||
|
+ struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ struct ocelot *ocelot = ds->priv;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_pcs_get_state(ocelot, port, state);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static void felix_port_disable(struct dsa_switch *ds, int port)
|
||
|
+static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ const struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ struct ocelot *ocelot = ds->priv;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_config(ocelot, port, link_an_mode, state);
|
||
|
+}
|
||
|
+
|
||
|
+static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port)
|
||
|
+{
|
||
|
+ struct ocelot *ocelot = ds->priv;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_an_restart(ocelot, port);
|
||
|
+}
|
||
|
+
|
||
|
+static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface)
|
||
|
+{
|
||
|
+ struct ocelot *ocelot = ds->priv;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface);
|
||
|
+}
|
||
|
+
|
||
|
+static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface,
|
||
|
+ struct phy_device *phydev)
|
||
|
{
|
||
|
struct ocelot *ocelot = ds->priv;
|
||
|
|
||
|
- return ocelot_port_disable(ocelot, port);
|
||
|
+ ocelot_phylink_mac_link_up(ocelot, port, link_an_mode, interface,
|
||
|
+ phydev);
|
||
|
}
|
||
|
|
||
|
static void felix_get_strings(struct dsa_switch *ds, int port,
|
||
|
@@ -417,9 +447,12 @@ static const struct dsa_switch_ops felix
|
||
|
.get_ethtool_stats = felix_get_ethtool_stats,
|
||
|
.get_sset_count = felix_get_sset_count,
|
||
|
.get_ts_info = felix_get_ts_info,
|
||
|
- .adjust_link = felix_adjust_link,
|
||
|
- .port_enable = felix_port_enable,
|
||
|
- .port_disable = felix_port_disable,
|
||
|
+ .phylink_validate = felix_phylink_validate,
|
||
|
+ .phylink_mac_link_state = felix_phylink_mac_pcs_get_state,
|
||
|
+ .phylink_mac_config = felix_phylink_mac_config,
|
||
|
+ .phylink_mac_an_restart = felix_phylink_mac_an_restart,
|
||
|
+ .phylink_mac_link_down = felix_phylink_mac_link_down,
|
||
|
+ .phylink_mac_link_up = felix_phylink_mac_link_up,
|
||
|
.port_fdb_dump = felix_fdb_dump,
|
||
|
.port_fdb_add = felix_fdb_add,
|
||
|
.port_fdb_del = felix_fdb_del,
|
||
|
--- a/drivers/net/ethernet/mscc/Kconfig
|
||
|
+++ b/drivers/net/ethernet/mscc/Kconfig
|
||
|
@@ -15,7 +15,7 @@ config MSCC_OCELOT_SWITCH
|
||
|
tristate "Ocelot switch driver"
|
||
|
depends on NET_SWITCHDEV
|
||
|
depends on HAS_IOMEM
|
||
|
- select PHYLIB
|
||
|
+ select PHYLINK
|
||
|
select REGMAP_MMIO
|
||
|
help
|
||
|
This driver supports the Ocelot network switch device.
|
||
|
--- a/drivers/net/ethernet/mscc/ocelot.c
|
||
|
+++ b/drivers/net/ethernet/mscc/ocelot.c
|
||
|
@@ -13,7 +13,7 @@
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
-#include <linux/phy.h>
|
||
|
+#include <linux/phylink.h>
|
||
|
#include <linux/ptp_clock_kernel.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <linux/iopoll.h>
|
||
|
@@ -406,18 +406,66 @@ static u16 ocelot_wm_enc(u16 value)
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
-void ocelot_adjust_link(struct ocelot *ocelot, int port,
|
||
|
- struct phy_device *phydev)
|
||
|
+void ocelot_phylink_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) {
|
||
|
+ 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, 2500baseT_Full);
|
||
|
+
|
||
|
+ bitmap_and(supported, supported, mask,
|
||
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||
|
+ bitmap_and(state->advertising, state->advertising, mask,
|
||
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_validate);
|
||
|
+
|
||
|
+void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port,
|
||
|
+ struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ state->link = 1;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_mac_pcs_get_state);
|
||
|
+
|
||
|
+void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port)
|
||
|
+{
|
||
|
+ /* Not supported */
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_mac_an_restart);
|
||
|
+
|
||
|
+void ocelot_phylink_mac_config(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ const struct phylink_link_state *state)
|
||
|
{
|
||
|
int speed, mac_speed, mac_mode = DEV_MAC_MODE_CFG_FDX_ENA;
|
||
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||
|
+ u32 mac_fc_cfg;
|
||
|
|
||
|
if (ocelot->quirks & OCELOT_PCS_PERFORMS_RATE_ADAPTATION)
|
||
|
speed = SPEED_1000;
|
||
|
else
|
||
|
- speed = phydev->speed;
|
||
|
+ speed = state->speed;
|
||
|
|
||
|
switch (speed) {
|
||
|
+ case SPEED_UNKNOWN:
|
||
|
+ return;
|
||
|
case SPEED_10:
|
||
|
mac_speed = OCELOT_SPEED_10;
|
||
|
break;
|
||
|
@@ -433,16 +481,11 @@ void ocelot_adjust_link(struct ocelot *o
|
||
|
mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
|
||
|
break;
|
||
|
default:
|
||
|
- dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n",
|
||
|
+ dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
|
||
|
port, speed);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
- phy_print_status(phydev);
|
||
|
-
|
||
|
- if (!phydev->link)
|
||
|
- return;
|
||
|
-
|
||
|
/* Only full duplex supported for now */
|
||
|
ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG);
|
||
|
|
||
|
@@ -469,27 +512,36 @@ void ocelot_adjust_link(struct ocelot *o
|
||
|
QSYS_SWITCH_PORT_MODE, port);
|
||
|
|
||
|
/* Flow control */
|
||
|
- ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
|
||
|
- SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA |
|
||
|
- SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
|
||
|
- SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
|
||
|
- SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed),
|
||
|
- SYS_MAC_FC_CFG, port);
|
||
|
+ mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed);
|
||
|
+ if (state->pause & MLO_PAUSE_RX)
|
||
|
+ mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
|
||
|
+ if (state->pause & MLO_PAUSE_TX)
|
||
|
+ mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
|
||
|
+ SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
|
||
|
+ SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
|
||
|
+ SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
|
||
|
+ ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
|
||
|
+
|
||
|
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
|
||
|
}
|
||
|
-EXPORT_SYMBOL(ocelot_adjust_link);
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_mac_config);
|
||
|
|
||
|
-static void ocelot_port_adjust_link(struct net_device *dev)
|
||
|
+void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface)
|
||
|
{
|
||
|
- struct ocelot_port_private *priv = netdev_priv(dev);
|
||
|
- struct ocelot *ocelot = priv->port.ocelot;
|
||
|
- int port = priv->chip_port;
|
||
|
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||
|
|
||
|
- ocelot_adjust_link(ocelot, port, dev->phydev);
|
||
|
+ ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
|
||
|
+ ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
|
||
|
+ QSYS_SWITCH_PORT_MODE, port);
|
||
|
}
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_mac_link_down);
|
||
|
|
||
|
-void ocelot_port_enable(struct ocelot *ocelot, int port,
|
||
|
- struct phy_device *phy)
|
||
|
+void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface,
|
||
|
+ struct phy_device *phy)
|
||
|
{
|
||
|
/* Enable receiving frames on the port, and activate auto-learning of
|
||
|
* MAC addresses.
|
||
|
@@ -499,62 +551,22 @@ void ocelot_port_enable(struct ocelot *o
|
||
|
ANA_PORT_PORT_CFG_PORTID_VAL(port),
|
||
|
ANA_PORT_PORT_CFG, port);
|
||
|
}
|
||
|
-EXPORT_SYMBOL(ocelot_port_enable);
|
||
|
+EXPORT_SYMBOL(ocelot_phylink_mac_link_up);
|
||
|
|
||
|
static int ocelot_port_open(struct net_device *dev)
|
||
|
{
|
||
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
||
|
- struct ocelot *ocelot = priv->port.ocelot;
|
||
|
- int port = priv->chip_port;
|
||
|
- int err;
|
||
|
-
|
||
|
- if (priv->serdes) {
|
||
|
- err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
|
||
|
- priv->phy_mode);
|
||
|
- if (err) {
|
||
|
- netdev_err(dev, "Could not set mode of SerDes\n");
|
||
|
- return err;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
|
||
|
- priv->phy_mode);
|
||
|
- if (err) {
|
||
|
- netdev_err(dev, "Could not attach to PHY\n");
|
||
|
- return err;
|
||
|
- }
|
||
|
|
||
|
- dev->phydev = priv->phy;
|
||
|
-
|
||
|
- phy_attached_info(priv->phy);
|
||
|
- phy_start(priv->phy);
|
||
|
-
|
||
|
- ocelot_port_enable(ocelot, port, priv->phy);
|
||
|
+ phylink_start(priv->phylink);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-void ocelot_port_disable(struct ocelot *ocelot, int port)
|
||
|
-{
|
||
|
- struct ocelot_port *ocelot_port = ocelot->ports[port];
|
||
|
-
|
||
|
- ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
|
||
|
- ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
|
||
|
- QSYS_SWITCH_PORT_MODE, port);
|
||
|
-}
|
||
|
-EXPORT_SYMBOL(ocelot_port_disable);
|
||
|
-
|
||
|
static int ocelot_port_stop(struct net_device *dev)
|
||
|
{
|
||
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
||
|
- struct ocelot *ocelot = priv->port.ocelot;
|
||
|
- int port = priv->chip_port;
|
||
|
-
|
||
|
- phy_disconnect(priv->phy);
|
||
|
-
|
||
|
- dev->phydev = NULL;
|
||
|
|
||
|
- ocelot_port_disable(ocelot, port);
|
||
|
+ phylink_stop(priv->phylink);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -2251,8 +2263,7 @@ void ocelot_init_port(struct ocelot *oce
|
||
|
EXPORT_SYMBOL(ocelot_init_port);
|
||
|
|
||
|
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
|
||
|
- void __iomem *regs,
|
||
|
- struct phy_device *phy)
|
||
|
+ void __iomem *regs)
|
||
|
{
|
||
|
struct ocelot_port_private *priv;
|
||
|
struct ocelot_port *ocelot_port;
|
||
|
@@ -2265,7 +2276,6 @@ int ocelot_probe_port(struct ocelot *oce
|
||
|
SET_NETDEV_DEV(dev, ocelot->dev);
|
||
|
priv = netdev_priv(dev);
|
||
|
priv->dev = dev;
|
||
|
- priv->phy = phy;
|
||
|
priv->chip_port = port;
|
||
|
ocelot_port = &priv->port;
|
||
|
ocelot_port->ocelot = ocelot;
|
||
|
--- a/drivers/net/ethernet/mscc/ocelot.h
|
||
|
+++ b/drivers/net/ethernet/mscc/ocelot.h
|
||
|
@@ -12,8 +12,7 @@
|
||
|
#include <linux/etherdevice.h>
|
||
|
#include <linux/if_vlan.h>
|
||
|
#include <linux/net_tstamp.h>
|
||
|
-#include <linux/phy.h>
|
||
|
-#include <linux/phy/phy.h>
|
||
|
+#include <linux/phylink.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/ptp_clock_kernel.h>
|
||
|
#include <linux/regmap.h>
|
||
|
@@ -65,14 +64,12 @@ struct ocelot_multicast {
|
||
|
struct ocelot_port_private {
|
||
|
struct ocelot_port port;
|
||
|
struct net_device *dev;
|
||
|
- struct phy_device *phy;
|
||
|
+ struct phylink *phylink;
|
||
|
+ struct phylink_config phylink_config;
|
||
|
u8 chip_port;
|
||
|
|
||
|
u8 vlan_aware;
|
||
|
|
||
|
- phy_interface_t phy_mode;
|
||
|
- struct phy *serdes;
|
||
|
-
|
||
|
struct ocelot_port_tc tc;
|
||
|
};
|
||
|
|
||
|
@@ -83,9 +80,7 @@ void ocelot_port_writel(struct ocelot_po
|
||
|
#define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val))
|
||
|
|
||
|
int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops);
|
||
|
-int ocelot_probe_port(struct ocelot *ocelot, u8 port,
|
||
|
- void __iomem *regs,
|
||
|
- struct phy_device *phy);
|
||
|
+int ocelot_probe_port(struct ocelot *ocelot, u8 port, void __iomem *regs);
|
||
|
|
||
|
void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu,
|
||
|
enum ocelot_tag_prefix injection,
|
||
|
--- a/drivers/net/ethernet/mscc/ocelot_board.c
|
||
|
+++ b/drivers/net/ethernet/mscc/ocelot_board.c
|
||
|
@@ -13,6 +13,7 @@
|
||
|
#include <linux/mfd/syscon.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <net/switchdev.h>
|
||
|
+#include <linux/phy/phy.h>
|
||
|
|
||
|
#include "ocelot.h"
|
||
|
|
||
|
@@ -262,6 +263,91 @@ static const struct ocelot_ops ocelot_op
|
||
|
.reset = ocelot_reset,
|
||
|
};
|
||
|
|
||
|
+static void ocelot_port_phylink_validate(struct phylink_config *config,
|
||
|
+ unsigned long *supported,
|
||
|
+ struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ ocelot_phylink_validate(ocelot, port, supported, state);
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+ocelot_port_phylink_mac_pcs_get_state(struct phylink_config *config,
|
||
|
+ struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_pcs_get_state(ocelot, port, state);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void ocelot_port_phylink_mac_an_restart(struct phylink_config *config)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_an_restart(ocelot, port);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+ocelot_port_phylink_mac_config(struct phylink_config *config,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ const struct phylink_link_state *state)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ ocelot_phylink_mac_config(ocelot, port, link_an_mode, state);
|
||
|
+}
|
||
|
+
|
||
|
+static void ocelot_port_phylink_mac_link_down(struct phylink_config *config,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ return ocelot_phylink_mac_link_down(ocelot, port, link_an_mode,
|
||
|
+ interface);
|
||
|
+}
|
||
|
+
|
||
|
+static void ocelot_port_phylink_mac_link_up(struct phylink_config *config,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface,
|
||
|
+ struct phy_device *phy)
|
||
|
+{
|
||
|
+ struct net_device *ndev = to_net_dev(config->dev);
|
||
|
+ struct ocelot_port_private *priv = netdev_priv(ndev);
|
||
|
+ struct ocelot *ocelot = priv->port.ocelot;
|
||
|
+ int port = priv->chip_port;
|
||
|
+
|
||
|
+ return ocelot_phylink_mac_link_up(ocelot, port, link_an_mode,
|
||
|
+ interface, phy);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct phylink_mac_ops ocelot_phylink_ops = {
|
||
|
+ .validate = ocelot_port_phylink_validate,
|
||
|
+ .mac_link_state = ocelot_port_phylink_mac_pcs_get_state,
|
||
|
+ .mac_an_restart = ocelot_port_phylink_mac_an_restart,
|
||
|
+ .mac_config = ocelot_port_phylink_mac_config,
|
||
|
+ .mac_link_down = ocelot_port_phylink_mac_link_down,
|
||
|
+ .mac_link_up = ocelot_port_phylink_mac_link_up,
|
||
|
+};
|
||
|
+
|
||
|
static int mscc_ocelot_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device_node *np = pdev->dev.of_node;
|
||
|
@@ -369,8 +455,6 @@ static int mscc_ocelot_probe(struct plat
|
||
|
for_each_available_child_of_node(ports, portnp) {
|
||
|
struct ocelot_port_private *priv;
|
||
|
struct ocelot_port *ocelot_port;
|
||
|
- struct device_node *phy_node;
|
||
|
- struct phy_device *phy;
|
||
|
struct resource *res;
|
||
|
struct phy *serdes;
|
||
|
void __iomem *regs;
|
||
|
@@ -389,16 +473,7 @@ static int mscc_ocelot_probe(struct plat
|
||
|
if (IS_ERR(regs))
|
||
|
continue;
|
||
|
|
||
|
- phy_node = of_parse_phandle(portnp, "phy-handle", 0);
|
||
|
- if (!phy_node)
|
||
|
- continue;
|
||
|
-
|
||
|
- phy = of_phy_find_device(phy_node);
|
||
|
- of_node_put(phy_node);
|
||
|
- if (!phy)
|
||
|
- continue;
|
||
|
-
|
||
|
- err = ocelot_probe_port(ocelot, port, regs, phy);
|
||
|
+ err = ocelot_probe_port(ocelot, port, regs);
|
||
|
if (err) {
|
||
|
of_node_put(portnp);
|
||
|
goto out_put_ports;
|
||
|
@@ -412,9 +487,7 @@ static int mscc_ocelot_probe(struct plat
|
||
|
if (phy_mode < 0)
|
||
|
phy_mode = PHY_INTERFACE_MODE_NA;
|
||
|
|
||
|
- priv->phy_mode = phy_mode;
|
||
|
-
|
||
|
- switch (priv->phy_mode) {
|
||
|
+ switch (phy_mode) {
|
||
|
case PHY_INTERFACE_MODE_NA:
|
||
|
continue;
|
||
|
case PHY_INTERFACE_MODE_SGMII:
|
||
|
@@ -451,7 +524,41 @@ static int mscc_ocelot_probe(struct plat
|
||
|
goto out_put_ports;
|
||
|
}
|
||
|
|
||
|
- priv->serdes = serdes;
|
||
|
+ if (serdes) {
|
||
|
+ err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET,
|
||
|
+ phy_mode);
|
||
|
+ if (err) {
|
||
|
+ dev_err(ocelot->dev,
|
||
|
+ "Could not set mode of SerDes\n");
|
||
|
+ of_node_put(portnp);
|
||
|
+ goto out_put_ports;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ priv->phylink_config.dev = &priv->dev->dev;
|
||
|
+ priv->phylink_config.type = PHYLINK_NETDEV;
|
||
|
+
|
||
|
+ priv->phylink = phylink_create(&priv->phylink_config,
|
||
|
+ of_fwnode_handle(portnp),
|
||
|
+ phy_mode, &ocelot_phylink_ops);
|
||
|
+ if (IS_ERR(priv->phylink)) {
|
||
|
+ dev_err(ocelot->dev,
|
||
|
+ "Could not create a phylink instance (%ld)\n",
|
||
|
+ PTR_ERR(priv->phylink));
|
||
|
+ err = PTR_ERR(priv->phylink);
|
||
|
+ priv->phylink = NULL;
|
||
|
+ of_node_put(portnp);
|
||
|
+ goto out_put_ports;
|
||
|
+ }
|
||
|
+
|
||
|
+ err = phylink_of_phy_connect(priv->phylink, portnp, 0);
|
||
|
+ if (err) {
|
||
|
+ dev_err(ocelot->dev, "Could not connect to PHY: %d\n",
|
||
|
+ err);
|
||
|
+ phylink_destroy(priv->phylink);
|
||
|
+ of_node_put(portnp);
|
||
|
+ goto out_put_ports;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
register_netdevice_notifier(&ocelot_netdevice_nb);
|
||
|
@@ -468,12 +575,27 @@ out_put_ports:
|
||
|
static int mscc_ocelot_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct ocelot *ocelot = platform_get_drvdata(pdev);
|
||
|
+ int port;
|
||
|
|
||
|
ocelot_deinit(ocelot);
|
||
|
unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
|
||
|
unregister_switchdev_notifier(&ocelot_switchdev_nb);
|
||
|
unregister_netdevice_notifier(&ocelot_netdevice_nb);
|
||
|
|
||
|
+ for (port = 0; port < ocelot->num_phys_ports; port++) {
|
||
|
+ struct ocelot_port_private *priv;
|
||
|
+
|
||
|
+ priv = container_of(ocelot->ports[port],
|
||
|
+ struct ocelot_port_private,
|
||
|
+ port);
|
||
|
+
|
||
|
+ if (priv->phylink) {
|
||
|
+ rtnl_lock();
|
||
|
+ phylink_destroy(priv->phylink);
|
||
|
+ rtnl_unlock();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
--- a/include/soc/mscc/ocelot.h
|
||
|
+++ b/include/soc/mscc/ocelot.h
|
||
|
@@ -518,17 +518,12 @@ void ocelot_deinit(struct ocelot *ocelot
|
||
|
void ocelot_init_port(struct ocelot *ocelot, int port);
|
||
|
|
||
|
/* DSA callbacks */
|
||
|
-void ocelot_port_enable(struct ocelot *ocelot, int port,
|
||
|
- struct phy_device *phy);
|
||
|
-void ocelot_port_disable(struct ocelot *ocelot, int port);
|
||
|
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
|
||
|
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
|
||
|
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset);
|
||
|
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
|
||
|
struct ethtool_ts_info *info);
|
||
|
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
|
||
|
-void ocelot_adjust_link(struct ocelot *ocelot, int port,
|
||
|
- struct phy_device *phydev);
|
||
|
void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
|
||
|
bool vlan_aware);
|
||
|
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
|
||
|
@@ -597,4 +592,21 @@ int ocelot_rtag_parse_enable(struct ocel
|
||
|
int ocelot_dscp_set(struct ocelot *ocelot, int port,
|
||
|
bool enable, const u8 dscp_ix,
|
||
|
struct tsn_qos_switch_dscp_conf *c);
|
||
|
+void ocelot_phylink_validate(struct ocelot *ocelot, int port,
|
||
|
+ unsigned long *supported,
|
||
|
+ struct phylink_link_state *state);
|
||
|
+void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port,
|
||
|
+ struct phylink_link_state *state);
|
||
|
+void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port);
|
||
|
+void ocelot_phylink_mac_config(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ const struct phylink_link_state *state);
|
||
|
+void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface);
|
||
|
+void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
|
||
|
+ unsigned int link_an_mode,
|
||
|
+ phy_interface_t interface,
|
||
|
+ struct phy_device *phy);
|
||
|
+
|
||
|
#endif
|