On Mon, May 09, 2022 at 04:27:22PM +0000, Vladimir Oltean wrote: > On Sun, May 08, 2022 at 11:53:11AM -0700, Colin Foster wrote: > > Add control of an external VSC7512 chip by way of the ocelot-mfd interface. > > > > Currently the four copper phy ports are fully functional. Communication to > > external phys is also functional, but the SGMII / QSGMII interfaces are > > currently non-functional. > > > > Signed-off-by: Colin Foster <colin.foster@xxxxxxxxxxxxxxxx> > > --- > > drivers/mfd/ocelot-core.c | 3 + > > drivers/net/dsa/ocelot/Kconfig | 14 ++ > > drivers/net/dsa/ocelot/Makefile | 5 + > > drivers/net/dsa/ocelot/ocelot_ext.c | 368 ++++++++++++++++++++++++++++ > > include/soc/mscc/ocelot.h | 2 + > > 5 files changed, 392 insertions(+) > > create mode 100644 drivers/net/dsa/ocelot/ocelot_ext.c > > > > diff --git a/drivers/mfd/ocelot-core.c b/drivers/mfd/ocelot-core.c > > index 117028f7d845..c582b409a9f3 100644 > > --- a/drivers/mfd/ocelot-core.c > > +++ b/drivers/mfd/ocelot-core.c > > @@ -112,6 +112,9 @@ static const struct mfd_cell vsc7512_devs[] = { > > .of_compatible = "mscc,ocelot-miim", > > .num_resources = ARRAY_SIZE(vsc7512_miim1_resources), > > .resources = vsc7512_miim1_resources, > > + }, { > > + .name = "ocelot-ext-switch", > > + .of_compatible = "mscc,vsc7512-ext-switch", > > }, > > }; > > > > diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig > > index 220b0b027b55..f40b2c7171ad 100644 > > --- a/drivers/net/dsa/ocelot/Kconfig > > +++ b/drivers/net/dsa/ocelot/Kconfig > > @@ -1,4 +1,18 @@ > > # SPDX-License-Identifier: GPL-2.0-only > > +config NET_DSA_MSCC_OCELOT_EXT > > + tristate "Ocelot External Ethernet switch support" > > + depends on NET_DSA && SPI > > + depends on NET_VENDOR_MICROSEMI > > + select MDIO_MSCC_MIIM > > + select MFD_OCELOT_CORE > > + select MSCC_OCELOT_SWITCH_LIB > > + select NET_DSA_TAG_OCELOT_8021Q > > + select NET_DSA_TAG_OCELOT > > + help > > + This driver supports the VSC7511, VSC7512, VSC7513 and VSC7514 chips > > + when controlled through SPI. It can be used with the Microsemi dev > > + boards and an external CPU or custom hardware. > > + > > config NET_DSA_MSCC_FELIX > > tristate "Ocelot / Felix Ethernet switch support" > > depends on NET_DSA && PCI > > diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile > > index f6dd131e7491..d7f3f5a4461c 100644 > > --- a/drivers/net/dsa/ocelot/Makefile > > +++ b/drivers/net/dsa/ocelot/Makefile > > @@ -1,11 +1,16 @@ > > # SPDX-License-Identifier: GPL-2.0-only > > obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o > > +obj-$(CONFIG_NET_DSA_MSCC_OCELOT_EXT) += mscc_ocelot_ext.o > > obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o > > > > mscc_felix-objs := \ > > felix.o \ > > felix_vsc9959.o > > > > +mscc_ocelot_ext-objs := \ > > + felix.o \ > > + ocelot_ext.o > > + > > mscc_seville-objs := \ > > felix.o \ > > seville_vsc9953.o > > diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c > > new file mode 100644 > > index 000000000000..ba924f6b8d12 > > --- /dev/null > > +++ b/drivers/net/dsa/ocelot/ocelot_ext.c > > @@ -0,0 +1,368 @@ > > +// SPDX-License-Identifier: (GPL-2.0 OR MIT) > > +/* > > + * Copyright 2021-2022 Innovative Advantage Inc. > > + */ > > + > > +#include <asm/byteorder.h> > > +#include <linux/iopoll.h> > > +#include <linux/kconfig.h> > > +#include <linux/phylink.h> > > +#include <linux/platform_device.h> > > +#include <linux/regmap.h> > > +#include <soc/mscc/ocelot_ana.h> > > +#include <soc/mscc/ocelot_dev.h> > > +#include <soc/mscc/ocelot_qsys.h> > > +#include <soc/mscc/ocelot_vcap.h> > > +#include <soc/mscc/ocelot_ptp.h> > > +#include <soc/mscc/ocelot_sys.h> > > +#include <soc/mscc/ocelot.h> > > +#include <soc/mscc/vsc7514_regs.h> > > +#include "felix.h" > > + > > +#define VSC7512_NUM_PORTS 11 > > + > > +static const u32 vsc7512_port_modes[VSC7512_NUM_PORTS] = { > > + OCELOT_PORT_MODE_INTERNAL, > > + OCELOT_PORT_MODE_INTERNAL, > > + OCELOT_PORT_MODE_INTERNAL, > > + OCELOT_PORT_MODE_INTERNAL, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > + OCELOT_PORT_MODE_SGMII, > > + OCELOT_PORT_MODE_SGMII | OCELOT_PORT_MODE_QSGMII, > > +}; > > + > > +static const u32 vsc7512_gcb_regmap[] = { > > + REG(GCB_SOFT_RST, 0x0008), > > + REG(GCB_MIIM_MII_STATUS, 0x009c), > > + REG(GCB_PHY_PHY_CFG, 0x00f0), > > + REG(GCB_PHY_PHY_STAT, 0x00f4), > > +}; > > + > > +static const u32 *vsc7512_regmap[TARGET_MAX] = { > > + [ANA] = vsc7514_ana_regmap, > > + [QS] = vsc7514_qs_regmap, > > + [QSYS] = vsc7514_qsys_regmap, > > + [REW] = vsc7514_rew_regmap, > > + [SYS] = vsc7514_sys_regmap, > > + [S0] = vsc7514_vcap_regmap, > > + [S1] = vsc7514_vcap_regmap, > > + [S2] = vsc7514_vcap_regmap, > > + [PTP] = vsc7514_ptp_regmap, > > + [GCB] = vsc7512_gcb_regmap, > > + [DEV_GMII] = vsc7514_dev_gmii_regmap, > > +}; > > + > > +static void ocelot_ext_reset_phys(struct ocelot *ocelot) > > +{ > > + ocelot_write(ocelot, 0, GCB_PHY_PHY_CFG); > > + ocelot_write(ocelot, 0x1ff, GCB_PHY_PHY_CFG); > > + mdelay(500); > > +} > > + > > +static int ocelot_ext_reset(struct ocelot *ocelot) > > +{ > > + int retries = 100; > > + int err, val; > > + > > + ocelot_ext_reset_phys(ocelot); > > + > > + /* Initialize chip memories */ > > + err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); > > + if (err) > > + return err; > > + > > + err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); > > + if (err) > > + return err; > > + > > + /* MEM_INIT is a self-clearing bit. Wait for it to be clear (should be > > + * 100us) before enabling the switch core > > + */ > > + do { > > + msleep(1); > > + err = regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], > > + &val); > > + if (err) > > + return err; > > + } while (val && --retries); > > Can you use readx_poll_timeout() here? It looks like I can, yes. I'll update. > > > + > > + if (!retries) > > + return -ETIMEDOUT; > > + > > + return regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); > > +} > > + > > +static const struct ocelot_ops ocelot_ext_ops = { > > + .reset = ocelot_ext_reset, > > + .wm_enc = ocelot_wm_enc, > > + .wm_dec = ocelot_wm_dec, > > + .wm_stat = ocelot_wm_stat, > > + .port_to_netdev = felix_port_to_netdev, > > + .netdev_to_port = felix_netdev_to_port, > > +}; > > + > > +static const struct resource vsc7512_target_io_res[TARGET_MAX] = { > > + [ANA] = { > > + .start = 0x71880000, > > + .end = 0x7188ffff, > > + .name = "ana", > > + }, > > + [QS] = { > > + .start = 0x71080000, > > + .end = 0x710800ff, > > + .name = "qs", > > + }, > > + [QSYS] = { > > + .start = 0x71800000, > > + .end = 0x719fffff, > > + .name = "qsys", > > + }, > > + [REW] = { > > + .start = 0x71030000, > > + .end = 0x7103ffff, > > + .name = "rew", > > + }, > > + [SYS] = { > > + .start = 0x71010000, > > + .end = 0x7101ffff, > > + .name = "sys", > > + }, > > + [S0] = { > > + .start = 0x71040000, > > + .end = 0x710403ff, > > + .name = "s0", > > + }, > > + [S1] = { > > + .start = 0x71050000, > > + .end = 0x710503ff, > > + .name = "s1", > > + }, > > + [S2] = { > > + .start = 0x71060000, > > + .end = 0x710603ff, > > + .name = "s2", > > + }, > > + [GCB] = { > > + .start = 0x71070000, > > + .end = 0x7107022b, > > + .name = "devcpu_gcb", > > + }, > > +}; > > + > > +static const struct resource vsc7512_port_io_res[] = { > > + { > > + .start = 0x711e0000, > > + .end = 0x711effff, > > + .name = "port0", > > + }, > > + { > > + .start = 0x711f0000, > > + .end = 0x711fffff, > > + .name = "port1", > > + }, > > + { > > + .start = 0x71200000, > > + .end = 0x7120ffff, > > + .name = "port2", > > + }, > > + { > > + .start = 0x71210000, > > + .end = 0x7121ffff, > > + .name = "port3", > > + }, > > + { > > + .start = 0x71220000, > > + .end = 0x7122ffff, > > + .name = "port4", > > + }, > > + { > > + .start = 0x71230000, > > + .end = 0x7123ffff, > > + .name = "port5", > > + }, > > + { > > + .start = 0x71240000, > > + .end = 0x7124ffff, > > + .name = "port6", > > + }, > > + { > > + .start = 0x71250000, > > + .end = 0x7125ffff, > > + .name = "port7", > > + }, > > + { > > + .start = 0x71260000, > > + .end = 0x7126ffff, > > + .name = "port8", > > + }, > > + { > > + .start = 0x71270000, > > + .end = 0x7127ffff, > > + .name = "port9", > > + }, > > + { > > + .start = 0x71280000, > > + .end = 0x7128ffff, > > + .name = "port10", > > + }, > > +}; > > + > > +static void ocelot_ext_phylink_validate(struct ocelot *ocelot, int port, > > + unsigned long *supported, > > + struct phylink_link_state *state) > > +{ > > + struct ocelot_port *ocelot_port = ocelot->ports[port]; > > + > > + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; > > + > > + if (state->interface != PHY_INTERFACE_MODE_NA && > > This check is no longer necessary, please look again at the other > phylink validation functions. > > > + state->interface != ocelot_port->phy_mode) { > > Also, I don't see what is the point of providing one phylink validation > method only to replace it later in the patchset with the generic one. > Please squash "net: dsa: ocelot: utilize phylink_generic_validate" into > this. Yes, that was a poor decision on my part to keep that patch separate. I should have squashed it, since you already pointed this out last round and that is exactly what the last commit of this series was addressing. > > > + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); > > + return; > > + } > > + > > + phylink_set_port_modes(mask); > > + > > + phylink_set(mask, Pause); > > + phylink_set(mask, Autoneg); > > + phylink_set(mask, Asym_Pause); > > + phylink_set(mask, 10baseT_Half); > > + phylink_set(mask, 10baseT_Full); > > + phylink_set(mask, 100baseT_Half); > > + phylink_set(mask, 100baseT_Full); > > + phylink_set(mask, 1000baseT_Half); > > + phylink_set(mask, 1000baseT_Full); > > + > > + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); > > + bitmap_and(state->advertising, state->advertising, mask, > > + __ETHTOOL_LINK_MODE_MASK_NBITS); > > +} > > + > > +static struct regmap *ocelot_ext_regmap_init(struct ocelot *ocelot, > > + struct resource *res) > > +{ > > + return ocelot_init_regmap_from_resource(ocelot->dev, res); > > +} > > + > > +static const struct felix_info vsc7512_info = { > > + .target_io_res = vsc7512_target_io_res, > > + .port_io_res = vsc7512_port_io_res, > > + .regfields = vsc7514_regfields, > > + .map = vsc7512_regmap, > > + .ops = &ocelot_ext_ops, > > + .stats_layout = vsc7514_stats_layout, > > + .vcap = vsc7514_vcap_props, > > + .num_mact_rows = 1024, > > + .num_ports = VSC7512_NUM_PORTS, > > + .num_tx_queues = OCELOT_NUM_TC, > > + .phylink_validate = ocelot_ext_phylink_validate, > > + .port_modes = vsc7512_port_modes, > > + .init_regmap = ocelot_ext_regmap_init, > > +}; > > + > > +static int ocelot_ext_probe(struct platform_device *pdev) > > +{ > > + struct dsa_switch *ds; > > + struct ocelot *ocelot; > > + struct felix *felix; > > + struct device *dev; > > + int err; > > + > > + dev = &pdev->dev; > > I would prefer if this assignment was part of the variable declaration. > > > + > > + felix = kzalloc(sizeof(*felix), GFP_KERNEL); > > + if (!felix) > > + return -ENOMEM; > > + > > + dev_set_drvdata(dev, felix); > > + > > + ocelot = &felix->ocelot; > > + ocelot->dev = dev; > > + > > + ocelot->num_flooding_pgids = 1; > > + > > + felix->info = &vsc7512_info; > > + > > + ds = kzalloc(sizeof(*ds), GFP_KERNEL); > > + if (!ds) { > > + err = -ENOMEM; > > + dev_err(dev, "Failed to allocate DSA switch\n"); > > + goto err_free_felix; > > + } > > + > > + ds->dev = dev; > > + ds->num_ports = felix->info->num_ports; > > + ds->num_tx_queues = felix->info->num_tx_queues; > > + > > + ds->ops = &felix_switch_ops; > > + ds->priv = ocelot; > > + felix->ds = ds; > > + felix->tag_proto = DSA_TAG_PROTO_OCELOT; > > + > > + err = dsa_register_switch(ds); > > + if (err) { > > + dev_err(dev, "Failed to register DSA switch: %d\n", err); > > dev_err_probe please (look at the other drivers) > > > + goto err_free_ds; > > + } > > + > > + return 0; > > + > > +err_free_ds: > > + kfree(ds); > > +err_free_felix: > > + kfree(felix); > > + return err; > > +} > > + > > +static int ocelot_ext_remove(struct platform_device *pdev) > > +{ > > + struct felix *felix = dev_get_drvdata(&pdev->dev); > > + > > + if (!felix) > > + return 0; > > + > > + dsa_unregister_switch(felix->ds); > > + > > + kfree(felix->ds); > > + kfree(felix); > > + > > + dev_set_drvdata(&pdev->dev, NULL); > > + > > + return 0; > > +} > > + > > +static void ocelot_ext_shutdown(struct platform_device *pdev) > > +{ > > + struct felix *felix = dev_get_drvdata(&pdev->dev); > > + > > + if (!felix) > > + return; > > + > > + dsa_switch_shutdown(felix->ds); > > + > > + dev_set_drvdata(&pdev->dev, NULL); > > +} > > + > > +const struct of_device_id ocelot_ext_switch_of_match[] = { > > static > > > + { .compatible = "mscc,vsc7512-ext-switch" }, > > + { }, > > +}; > > +MODULE_DEVICE_TABLE(of, ocelot_ext_switch_of_match); > > + > > +static struct platform_driver ocelot_ext_switch_driver = { > > + .driver = { > > + .name = "ocelot-ext-switch", > > + .of_match_table = of_match_ptr(ocelot_ext_switch_of_match), > > + }, > > + .probe = ocelot_ext_probe, > > + .remove = ocelot_ext_remove, > > + .shutdown = ocelot_ext_shutdown, > > +}; > > +module_platform_driver(ocelot_ext_switch_driver); > > + > > +MODULE_DESCRIPTION("External Ocelot Switch driver"); > > +MODULE_LICENSE("GPL"); > > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h > > index 61888453f913..ade84e86741e 100644 > > --- a/include/soc/mscc/ocelot.h > > +++ b/include/soc/mscc/ocelot.h > > @@ -402,6 +402,8 @@ enum ocelot_reg { > > GCB_MIIM_MII_STATUS, > > GCB_MIIM_MII_CMD, > > GCB_MIIM_MII_DATA, > > + GCB_PHY_PHY_CFG, > > + GCB_PHY_PHY_STAT, > > DEV_CLOCK_CFG = DEV_GMII << TARGET_OFFSET, > > DEV_PORT_MISC, > > DEV_EVENTS, > > -- > > 2.25.1 > >