Message ID | 20220508185313.2222956-15-colin.foster@in-advantage.com (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | add support for VSC7512 control over SPI | expand |
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@in-advantage.com> > --- > 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 >
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@in-advantage.com> > > --- > > 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 > >
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); + + 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 && + state->interface != ocelot_port->phy_mode) { + 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; + + 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); + 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[] = { + { .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,
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@in-advantage.com> --- 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