Commit 26271394 authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents d4671cb9 4c05e5ce
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@
#define VSC7512_SIO_CTRL_RES_START	0x710700f8
#define VSC7512_SIO_CTRL_RES_SIZE	0x00000100

#define VSC7512_HSIO_RES_START		0x710d0000
#define VSC7512_HSIO_RES_SIZE		0x00000128

#define VSC7512_ANA_RES_START		0x71880000
#define VSC7512_ANA_RES_SIZE		0x00010000

@@ -129,8 +132,13 @@ static const struct resource vsc7512_sgpio_resources[] = {
	DEFINE_RES_REG_NAMED(VSC7512_SIO_CTRL_RES_START, VSC7512_SIO_CTRL_RES_SIZE, "gcb_sio"),
};

static const struct resource vsc7512_serdes_resources[] = {
	DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"),
};

static const struct resource vsc7512_switch_resources[] = {
	DEFINE_RES_REG_NAMED(VSC7512_ANA_RES_START, VSC7512_ANA_RES_SIZE, "ana"),
	DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio"),
	DEFINE_RES_REG_NAMED(VSC7512_QS_RES_START, VSC7512_QS_RES_SIZE, "qs"),
	DEFINE_RES_REG_NAMED(VSC7512_QSYS_RES_START, VSC7512_QSYS_RES_SIZE, "qsys"),
	DEFINE_RES_REG_NAMED(VSC7512_REW_RES_START, VSC7512_REW_RES_SIZE, "rew"),
@@ -176,6 +184,11 @@ static const struct mfd_cell vsc7512_devs[] = {
		.use_of_reg = true,
		.num_resources = ARRAY_SIZE(vsc7512_miim1_resources),
		.resources = vsc7512_miim1_resources,
	}, {
		.name = "ocelot-serdes",
		.of_compatible = "mscc,vsc7514-serdes",
		.num_resources = ARRAY_SIZE(vsc7512_serdes_resources),
		.resources = vsc7512_serdes_resources,
	}, {
		.name = "ocelot-ext-switch",
		.of_compatible = "mscc,vsc7512-switch",
+19 −0
Original line number Diff line number Diff line
@@ -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,
+7 −0
Original line number Diff line number Diff line
@@ -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
+9 −7
Original line number Diff line number Diff line
@@ -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)
+97 −0
Original line number Diff line number Diff line
@@ -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,
Loading