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? > + > + 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. > + 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 >