diff options
author | David S. Miller <davem@davemloft.net> | 2023-03-20 09:08:48 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2023-03-20 09:08:48 +0000 |
commit | 26271394cf2e92d4112a937045157e3b1db09929 (patch) | |
tree | f922b2a5f1097fae1ae3c137a102d22637ebe80a /drivers/net | |
parent | d4671cb96fa317e2b1f410367a52f731a145e2bc (diff) | |
parent | 4c05e5ceecbbcc0bd570eae218066e1b8bab6438 (diff) |
Merge branch 'ocelot-external-ports'
Colin Foster says:
====================
add support for ocelot external ports
This is the start of part 3 of what is hopefully a 3-part series to add
Ethernet switching support to Ocelot chips.
Part 1 of the series (A New Chip) added general support for Ocelot chips
that were controlled externally via SPI.
https://lore.kernel.org/all/20220815005553.1450359-1-colin.foster@in-advantage.com/
Part 2 of the series (The Ethernet Strikes Back) added DSA Ethernet
support for ports 0-3, which are the four copper ports that are internal
to the chip.
https://lore.kernel.org/all/20230127193559.1001051-1-colin.foster@in-advantage.com/
Part 3 will, at a minimum, add support for ports 4-7, which are
configured to use QSGMII to an external phy (Return Of The QSGMII). With
any luck, and some guidance, support for SGMII, SFPs, etc. will also be
part of this series.
V1 was submitted as an RFC - and that was rightly so. I suspected I
wasn't doing something right, and that was certainly the case. V2 is
much cleaner, so hopefully upgrading it to PATCH status is welcomed.
Thanks to Russell and Vladimir for correcting my course from V1.
In V1 I included a device tree snippet. I won't repeat that here, but
I will include a boot log snippet, in case it is of use:
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/dsa/ocelot/felix.c | 19 | ||||
-rw-r--r-- | drivers/net/dsa/ocelot/felix.h | 7 | ||||
-rw-r--r-- | drivers/net/dsa/ocelot/ocelot_ext.c | 16 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 97 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_net.c | 50 | ||||
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_vsc7514.c | 30 |
6 files changed, 138 insertions, 81 deletions
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index d4cc9e60f369..6dcebcfd71e7 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1056,6 +1056,17 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port, config->supported_interfaces); } +static void felix_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->phylink_mac_config) + felix->info->phylink_mac_config(ocelot, port, mode, state); +} + static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface) @@ -1555,6 +1566,9 @@ static int felix_setup(struct dsa_switch *ds) if (err) return err; + if (ocelot->targets[HSIO]) + ocelot_pll5_init(ocelot); + err = ocelot_init(ocelot); if (err) goto out_mdiobus_free; @@ -1571,6 +1585,10 @@ static int felix_setup(struct dsa_switch *ds) dsa_switch_for_each_available_port(dp, ds) { ocelot_init_port(ocelot, dp->index); + if (felix->info->configure_serdes) + felix->info->configure_serdes(ocelot, dp->index, + dp->dn); + /* Set the default QoS Classification based on PCP and DEI * bits of vlan tag. */ @@ -2085,6 +2103,7 @@ const struct dsa_switch_ops felix_switch_ops = { .get_sset_count = felix_get_sset_count, .get_ts_info = felix_get_ts_info, .phylink_get_caps = felix_phylink_get_caps, + .phylink_mac_config = felix_phylink_mac_config, .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, .phylink_mac_link_down = felix_phylink_mac_link_down, .phylink_mac_link_up = felix_phylink_mac_link_up, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index d5d0b30c0b75..96008c046da5 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -15,6 +15,8 @@ #define OCELOT_PORT_MODE_USXGMII BIT(4) #define OCELOT_PORT_MODE_1000BASEX BIT(5) +struct device_node; + /* Platform-specific information */ struct felix_info { /* Hardcoded resources provided by the hardware instantiation. */ @@ -58,6 +60,11 @@ struct felix_info { void (*tas_guard_bands_update)(struct ocelot *ocelot, int port); void (*port_sched_speed_set)(struct ocelot *ocelot, int port, u32 speed); + void (*phylink_mac_config)(struct ocelot *ocelot, int port, + unsigned int mode, + const struct phylink_link_state *state); + int (*configure_serdes)(struct ocelot *ocelot, int port, + struct device_node *portnp); }; /* Methods for initializing the hardware resources specific to a tagging diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c index 228737a32080..c29bee5a5c48 100644 --- a/drivers/net/dsa/ocelot/ocelot_ext.c +++ b/drivers/net/dsa/ocelot/ocelot_ext.c @@ -20,13 +20,13 @@ static const u32 vsc7512_port_modes[VSC7514_NUM_PORTS] = { OCELOT_PORT_MODE_INTERNAL, OCELOT_PORT_MODE_INTERNAL, OCELOT_PORT_MODE_INTERNAL, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, - OCELOT_PORT_MODE_NONE, + OCELOT_PORT_MODE_SERDES, + OCELOT_PORT_MODE_SERDES, + OCELOT_PORT_MODE_SERDES, + OCELOT_PORT_MODE_SERDES, + OCELOT_PORT_MODE_SERDES, + OCELOT_PORT_MODE_SGMII, + OCELOT_PORT_MODE_SERDES, }; static const struct ocelot_ops ocelot_ext_ops = { @@ -59,6 +59,8 @@ static const struct felix_info vsc7512_info = { .num_ports = VSC7514_NUM_PORTS, .num_tx_queues = OCELOT_NUM_TC, .port_modes = vsc7512_port_modes, + .phylink_mac_config = ocelot_phylink_mac_config, + .configure_serdes = ocelot_port_configure_serdes, }; static int ocelot_ext_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 08acb7b89086..1502bb2c8ea7 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -7,6 +7,8 @@ #include <linux/dsa/ocelot.h> #include <linux/if_bridge.h> #include <linux/iopoll.h> +#include <linux/phy/phy.h> +#include <soc/mscc/ocelot_hsio.h> #include <soc/mscc/ocelot_vcap.h> #include "ocelot.h" #include "ocelot_vcap.h" @@ -211,6 +213,36 @@ static void ocelot_mact_init(struct ocelot *ocelot) ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS); } +void ocelot_pll5_init(struct ocelot *ocelot) +{ + /* Configure PLL5. This will need a proper CCF driver + * The values are coming from the VTSS API for Ocelot + */ + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4, + HSIO_PLL5G_CFG4_IB_CTRL(0x7600) | + HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8)); + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0, + HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) | + HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) | + HSIO_PLL5G_CFG0_ENA_BIAS | + HSIO_PLL5G_CFG0_ENA_VCO_BUF | + HSIO_PLL5G_CFG0_ENA_CP1 | + HSIO_PLL5G_CFG0_SELCPI(2) | + HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) | + HSIO_PLL5G_CFG0_SELBGV820(4) | + HSIO_PLL5G_CFG0_DIV4 | + HSIO_PLL5G_CFG0_ENA_CLKTREE | + HSIO_PLL5G_CFG0_ENA_LANE); + regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2, + HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET | + HSIO_PLL5G_CFG2_EN_RESET_OVERRUN | + HSIO_PLL5G_CFG2_GAIN_TEST(0x8) | + HSIO_PLL5G_CFG2_ENA_AMPCTRL | + HSIO_PLL5G_CFG2_PWD_AMPCTRL_N | + HSIO_PLL5G_CFG2_AMPC_SEL(0x10)); +} +EXPORT_SYMBOL(ocelot_pll5_init); + static void ocelot_vcap_enable(struct ocelot *ocelot, int port) { ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA | @@ -778,6 +810,71 @@ static int ocelot_port_flush(struct ocelot *ocelot, int port) return err; } +int ocelot_port_configure_serdes(struct ocelot *ocelot, int port, + struct device_node *portnp) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct device *dev = ocelot->dev; + int err; + + /* Ensure clock signals and speed are set on all QSGMII links */ + if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_QSGMII) + ocelot_port_rmwl(ocelot_port, 0, + DEV_CLOCK_CFG_MAC_TX_RST | + DEV_CLOCK_CFG_MAC_RX_RST, + DEV_CLOCK_CFG); + + if (ocelot_port->phy_mode != PHY_INTERFACE_MODE_INTERNAL) { + struct phy *serdes = of_phy_get(portnp, NULL); + + if (IS_ERR(serdes)) { + err = PTR_ERR(serdes); + dev_err_probe(dev, err, + "missing SerDes phys for port %d\n", + port); + return err; + } + + err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, + ocelot_port->phy_mode); + of_phy_put(serdes); + if (err) { + dev_err(dev, "Could not SerDes mode on port %d: %pe\n", + port, ERR_PTR(err)); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(ocelot_port_configure_serdes); + +void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + /* Disable HDX fast control */ + ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS, + DEV_PORT_MISC); + + /* SGMII only for now */ + ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA, + PCS1G_MODE_CFG); + ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); + + /* Enable PCS */ + ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); + + /* No aneg on SGMII */ + ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG); + + /* No loopback */ + ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); +} +EXPORT_SYMBOL_GPL(ocelot_phylink_mac_config); + void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port, unsigned int link_an_mode, phy_interface_t interface, diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index ca4bde861397..21a87a3fc556 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -1675,25 +1675,10 @@ static void vsc7514_phylink_mac_config(struct phylink_config *config, { struct net_device *ndev = to_net_dev(config->dev); struct ocelot_port_private *priv = netdev_priv(ndev); - struct ocelot_port *ocelot_port = &priv->port; - - /* Disable HDX fast control */ - ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS, - DEV_PORT_MISC); - - /* SGMII only for now */ - ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA, - PCS1G_MODE_CFG); - ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); - - /* Enable PCS */ - ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); - - /* No aneg on SGMII */ - ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->port.index; - /* No loopback */ - ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); + ocelot_phylink_mac_config(ocelot, port, link_an_mode, state); } static void vsc7514_phylink_mac_link_down(struct phylink_config *config, @@ -1757,34 +1742,11 @@ static int ocelot_port_phylink_create(struct ocelot *ocelot, int port, return -EINVAL; } - /* Ensure clock signals and speed are set on all QSGMII links */ - if (phy_mode == PHY_INTERFACE_MODE_QSGMII) - ocelot_port_rmwl(ocelot_port, 0, - DEV_CLOCK_CFG_MAC_TX_RST | - DEV_CLOCK_CFG_MAC_RX_RST, - DEV_CLOCK_CFG); - ocelot_port->phy_mode = phy_mode; - if (phy_mode != PHY_INTERFACE_MODE_INTERNAL) { - struct phy *serdes = of_phy_get(portnp, NULL); - - if (IS_ERR(serdes)) { - err = PTR_ERR(serdes); - dev_err_probe(dev, err, - "missing SerDes phys for port %d\n", - port); - return err; - } - - err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, phy_mode); - of_phy_put(serdes); - if (err) { - dev_err(dev, "Could not SerDes mode on port %d: %pe\n", - port, ERR_PTR(err)); - return err; - } - } + err = ocelot_port_configure_serdes(ocelot, port, portnp); + if (err) + return err; priv = container_of(ocelot_port, struct ocelot_port_private, port); diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c index 7388c3b0535c..97e90e2869d4 100644 --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -18,7 +18,6 @@ #include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot_vcap.h> -#include <soc/mscc/ocelot_hsio.h> #include <soc/mscc/vsc7514_regs.h> #include "ocelot_fdma.h" #include "ocelot.h" @@ -26,35 +25,6 @@ #define VSC7514_VCAP_POLICER_BASE 128 #define VSC7514_VCAP_POLICER_MAX 191 -static void ocelot_pll5_init(struct ocelot *ocelot) -{ - /* Configure PLL5. This will need a proper CCF driver - * The values are coming from the VTSS API for Ocelot - */ - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4, - HSIO_PLL5G_CFG4_IB_CTRL(0x7600) | - HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8)); - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0, - HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) | - HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) | - HSIO_PLL5G_CFG0_ENA_BIAS | - HSIO_PLL5G_CFG0_ENA_VCO_BUF | - HSIO_PLL5G_CFG0_ENA_CP1 | - HSIO_PLL5G_CFG0_SELCPI(2) | - HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) | - HSIO_PLL5G_CFG0_SELBGV820(4) | - HSIO_PLL5G_CFG0_DIV4 | - HSIO_PLL5G_CFG0_ENA_CLKTREE | - HSIO_PLL5G_CFG0_ENA_LANE); - regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2, - HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET | - HSIO_PLL5G_CFG2_EN_RESET_OVERRUN | - HSIO_PLL5G_CFG2_GAIN_TEST(0x8) | - HSIO_PLL5G_CFG2_ENA_AMPCTRL | - HSIO_PLL5G_CFG2_PWD_AMPCTRL_N | - HSIO_PLL5G_CFG2_AMPC_SEL(0x10)); -} - static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) { int ret; |