diff mbox series

[RFC,vN,net-next,2/2] net: mscc: ocelot: add support for VSC75XX SPI control

Message ID 20210504051130.1207550-2-colin.foster@in-advantage.com
State New
Headers show
Series None | expand

Commit Message

Colin Foster May 4, 2021, 5:11 a.m. UTC
Add support for control for VSC75XX chips over SPI control. Starting with the
VSC9959 code, this will utilize a spi bus instead of PCIe or memory-mapped IO to
control the chip.

Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
---
 arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts |  124 ++
 drivers/net/dsa/ocelot/Kconfig                |   11 +
 drivers/net/dsa/ocelot/Makefile               |    5 +
 drivers/net/dsa/ocelot/felix_vsc7512_spi.c    | 1214 +++++++++++++++++
 include/soc/mscc/ocelot.h                     |   15 +
 5 files changed, 1369 insertions(+)
 create mode 100644 arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts
 create mode 100644 drivers/net/dsa/ocelot/felix_vsc7512_spi.c

Comments

Andrew Lunn May 4, 2021, 12:31 p.m. UTC | #1
On Mon, May 03, 2021 at 10:11:27PM -0700, Colin Foster wrote:
> Add support for control for VSC75XX chips over SPI control. Starting with the
> VSC9959 code, this will utilize a spi bus instead of PCIe or memory-mapped IO to
> control the chip.

Hi Colin

Please fix your subject line for the next version. vN should of been
v1. The number is important so we can tell revisions apart.

> 
> Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
> ---
>  arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts |  124 ++
>  drivers/net/dsa/ocelot/Kconfig                |   11 +
>  drivers/net/dsa/ocelot/Makefile               |    5 +
>  drivers/net/dsa/ocelot/felix_vsc7512_spi.c    | 1214 +++++++++++++++++
>  include/soc/mscc/ocelot.h                     |   15 +

Please split this patch up. The DT overlay will probably be merged via
ARM SOC, not netdev. You also need to document the device tree
binding, as a separate patch.

> +	fragment@3 {
> +		target = <&spi0>;
> +		__overlay__ {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			cs-gpios = <&gpio 8 1>;
> +			status = "okay";
> +
> +			vsc7512: vsc7512@0{
> +				compatible = "mscc,vsc7512";
> +				spi-max-frequency = <250000>;
> +				reg = <0>;
> +
> +				ports {
> +					#address-cells = <1>;
> +					#size-cells = <0>;
> +
> +					port@0 {
> +						reg = <0>;
> +						ethernet = <&ethernet>;
> +						phy-mode = "internal";
> +
> +						fixed-link {
> +							speed = <1000>;
> +							full-duplex;
> +						};
> +					};
> +
> +					port@1 {
> +						reg = <1>;
> +						label = "swp1";
> +						status = "disabled";
> +					};
> +
> +					port@2 {
> +						reg = <2>;
> +						label = "swp2";
> +						status = "disabled";
> +					};

I'm surprised all the ports are disabled. I could understand that for
a DTSI file, but a DTS overlay?

> +++ b/drivers/net/dsa/ocelot/felix_vsc7512_spi.c
> @@ -0,0 +1,1214 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/* Copyright 2017 Microsemi Corporation
> + * Copyright 2018-2019 NXP Semiconductors
> + */
> +#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 <linux/spi/spi.h>
> +#include <linux/packing.h>
> +#include <linux/pcs-lynx.h>
> +#include <net/pkt_sched.h>
> +#include <linux/iopoll.h>
> +#include <linux/kconfig.h>
> +#include <linux/mdio.h>
> +#include "felix.h"
> +
> +#define VSC7512_TAS_GCL_ENTRY_MAX 63
> +
> +// Note: These addresses and offsets are all shifted down
> +// by two. This is because the SPI protocol needs them to
> +// be before they get sent out.
> +//
> +// An alternative is to keep them standardized, but then
> +// a separate spi_bus regmap would need to be defined.
> +//
> +// That might be optimal though. The 'Read' protocol of
> +// the VSC driver might be much quicker if we add padding
> +// bytes, which I don't think regmap supports.

C style comments please.

> +static void vsc7512_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,
> +	};

This function seems out of place. Why would SPI access change what the
ports are capable of doing? Please split this up into more
patches. Keep the focus of this patch as being adding SPI support.

	 Andrew
Alexandre Belloni May 4, 2021, 1:55 p.m. UTC | #2
On 04/05/2021 12:59:43+0000, Vladimir Oltean wrote:
> > > +static void vsc7512_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,
> > > +	};
> > 
> > This function seems out of place. Why would SPI access change what the
> > ports are capable of doing? Please split this up into more
> > patches. Keep the focus of this patch as being adding SPI support.
> 
> What is going on is that this is just the way in which the drivers are
> structured. Colin is not really "adding SPI support" to any of the
> existing DSA switches that are supported (VSC9953, VSC9959) as much as
> "adding support for a new switch which happens to be controlled over
> SPI" (VSC7512).

Note that this should not only be about vsc7512 as the whole ocelot
family (vsc7511, vsc7512, vsc7513 and vsc7514) can be connected over
spi. Also, they can all be used in a DSA configuration, over PCIe, just
like Felix.

> The layering is as follows:
> - drivers/net/dsa/ocelot/felix_vsc7512_spi.c: deals with the most
>   hardware specific SoC support. The regmap is defined here, so are the
>   port capabilities.
> - drivers/net/dsa/ocelot/felix.c: common integration with DSA
> - drivers/net/ethernet/mscc/ocelot*.c: the SoC-independent hardware
>   support.
> 
> I'm not actually sure that splitting the port PHY mode support in a
> separate patch is possible while keeping functional intermediate
> results. But I do agree about the rest, splitting the device tree
> changes, etc.
Vladimir Oltean May 4, 2021, 2:36 p.m. UTC | #3
On Tue, May 04, 2021 at 03:55:27PM +0200, Alexandre Belloni wrote:
> On 04/05/2021 12:59:43+0000, Vladimir Oltean wrote:
> > > > +static void vsc7512_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,
> > > > +	};
> > > 
> > > This function seems out of place. Why would SPI access change what the
> > > ports are capable of doing? Please split this up into more
> > > patches. Keep the focus of this patch as being adding SPI support.
> > 
> > What is going on is that this is just the way in which the drivers are
> > structured. Colin is not really "adding SPI support" to any of the
> > existing DSA switches that are supported (VSC9953, VSC9959) as much as
> > "adding support for a new switch which happens to be controlled over
> > SPI" (VSC7512).
> 
> Note that this should not only be about vsc7512 as the whole ocelot
> family (vsc7511, vsc7512, vsc7513 and vsc7514) can be connected over
> spi. Also, they can all be used in a DSA configuration, over PCIe, just
> like Felix.

I see. From the Linux device driver model's perspective, a SPI driver
for VSC7512 is still different than an MMIO driver for the same hardware
is, and that is working a bit against us. I don't know much about regmap
for SPI, specifically how are the protocol buffers constructed, and if
it's easy or not to have a driver-specified hook in which the memory
address for the SPI reads and writes is divided by 4. If I understand
correctly, that's about the only major difference between a VSC7512
driver for SPI vs MMIO, and would allow reusing the same regmaps as e.g.
the ones in drivers/net/ethernet/ocelot_vsc7514.c. Avoiding duplication
for the rest could be handled with a lot of EXPORT_SYMBOL, although
right now, I am not sure that is quite mandated yet. I know that the
hardware is capable of a lot more flexibility than what the Linux
drivers currently make of, but let's not think of overly complex ways of
managing that entire complexity space unless somebody actually needs it.

As to phylink, I had some old patches converting ocelot to phylink in
the blind, but given the fact that I don't have any vsc7514 board and I
was relying on Horatiu to test them, those patches didn't land anywhere
and would be quite obsolete now.
I don't know how similar VSC7512 (Colin's chip) and VSC7514 (the chip
supported by the switchdev ocelot) are in terms of hardware interfaces.
If the answer is "not very", then this is a bit of a moot point, but if
they are, then ocelot might first have to be converted to phylink, and
then its symbols exported such that DSA can use them too.

What Colin appears to be doing differently to all other Ocelot/Felix
drivers is that he has a single devm_regmap_init_spi() in felix_spi_probe.
Whereas everyone else uses a separate devm_regmap_init_mmio() per each
memory region, tucked away in ocelot_regmap_init(). I still haven't
completely understood why that is, but this is the reason why he needs
the "offset" passed to all I/O accessors: since he uses a single regmap,
the offset is what accesses one memory region or another in his case.

I see Colin uses some regmap accesses in order to set up the SPI bus
interface:

felix_spi_probe
-> felix_spi_init_bus

before the ocelot hardware library is up and running, which is at this
point:

felix_spi_probe
-> dsa_register_switch
   -> felix_setup
      -> ocelot_init

I suspect that if Colin could defer his felix_spi_init_bus work until
some later point, such as until vsc7512_reset, then he could preserve
the existing code structure, with one regmap per register region as
opposed to a single regmap.

By the way, I am not opposed to more refactoring being done to the felix
driver itself, for example if ocelot_init needs to be done before
dsa_register_switch, I'm in favor of that if it's helpful. There is a
comment on top of felix_setup() which is no longer true, it reads:

/* Hardware initialization done here so that we can allocate structures with
 * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
 * us to allocate structures twice (leak memory) and map PCI memory twice
 * (which will not work).
 */

What was actually wrong there has been solved in the meantime by commit
b4024c9e5c57 ("felix: Fix initialization of ioremap resources"). So now
there isn't any problem with EPROBE_DEFER, we can initialize the regmap
anywhere.
Colin Foster May 6, 2021, 6:34 p.m. UTC | #4
Vladimir, Thank you very much for the feedback. I appreciate your time
and your patience when dealing with my many blunders. This is a large
learning experience for me.

On Thu, May 06, 2021 at 01:22:04PM +0300, Vladimir Oltean wrote:
> On Mon, May 03, 2021 at 10:11:27PM -0700, Colin Foster wrote:
> > Add support for control for VSC75XX chips over SPI control. Starting with the
> > VSC9959 code, this will utilize a spi bus instead of PCIe or memory-mapped IO to
> > control the chip.
> > 
> > Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
> > ---
> >  arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts |  124 ++
> >  drivers/net/dsa/ocelot/Kconfig                |   11 +
> >  drivers/net/dsa/ocelot/Makefile               |    5 +
> >  drivers/net/dsa/ocelot/felix_vsc7512_spi.c    | 1214 +++++++++++++++++
> >  include/soc/mscc/ocelot.h                     |   15 +
> >  5 files changed, 1369 insertions(+)
> >  create mode 100644 arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts
> >  create mode 100644 drivers/net/dsa/ocelot/felix_vsc7512_spi.c
> > 
> > diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
> > index 932b6b6fe817..2db147ce9fe7 100644
> > --- a/drivers/net/dsa/ocelot/Kconfig
> > +++ b/drivers/net/dsa/ocelot/Kconfig
> > @@ -14,6 +14,17 @@ config NET_DSA_MSCC_FELIX
> >  	  This driver supports the VSC9959 (Felix) switch, which is embedded as
> >  	  a PCIe function of the NXP LS1028A ENETC RCiEP.
> >  
> > +config NET_DSA_MSCC_FELIX_SPI
> > +	tristate "Ocelot / Felix Ethernet SPI switch support"
> > +	depends on NET_DSA && SPI
> > +	depends on NET_VENDOR_MICROSEMI
> > +	select MSCC_OCELOT_SWITCH_LIB
> > +	select NET_DSA_TAG_OCELOT_8021Q
> > +	select NET_DSA_TAG_OCELOT
> > +	select PCS_LYNX
> 
> You most probably don't need to select PCS_LYNX (that's an NXP thing).
> For that reason, you might want to call your module NET_DSA_MSCC_OCELOT_SPI.
> The "felix" name is just the name of the common DSA driver and of the
> VSC9959 chip. VSC7512 is probably called Ocelot-1 according to Microchip
> marketing.

Thank you. I couldn't find where "Felix" and "Seville" actually came
from. My next RFC submission will include these changes.

> 
> > +	help
> > +	  This driver supports the VSC75XX chips when controlled through SPI.
> > +
> 
> Better mention which VSC75XX chips, that XX is pretty unspecific.

Agreed (and I agree with the next several suggestions)

> 
> >  config NET_DSA_MSCC_SEVILLE
> >  	tristate "Ocelot / Seville Ethernet switch support"
> >  	depends on NET_DSA
> > diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile
> > index f6dd131e7491..f2c9c52ba76c 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_FELIX_SPI) += mscc_felix_spi.o
> >  obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o
> >  
> >  mscc_felix-objs := \
> >  	felix.o \
> >  	felix_vsc9959.o
> >  
> > +mscc_felix_spi-objs := \
> > +	felix.o \
> > +	felix_vsc7512_spi.o
> > +
> >  mscc_seville-objs := \
> >  	felix.o \
> >  	seville_vsc9953.o
> > diff --git a/drivers/net/dsa/ocelot/felix_vsc7512_spi.c b/drivers/net/dsa/ocelot/felix_vsc7512_spi.c
> > new file mode 100644
> > index 000000000000..1cb5758b752c
> > --- /dev/null
> > +++ b/drivers/net/dsa/ocelot/felix_vsc7512_spi.c
> 
> ocelot_vsc7512_spi.c maybe?
> 
> > @@ -0,0 +1,1214 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> > +/* Copyright 2017 Microsemi Corporation
> > + * Copyright 2018-2019 NXP Semiconductors
> > + */
> > +#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 <linux/spi/spi.h>
> > +#include <linux/packing.h>
> > +#include <linux/pcs-lynx.h>
> > +#include <net/pkt_sched.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/kconfig.h>
> > +#include <linux/mdio.h>
> > +#include "felix.h"
> 
> Not sure if all the includes are needed, please remove the ones that
> aren't.
> 
> > +
> > +#define VSC7512_TAS_GCL_ENTRY_MAX 63
> 
> VSC7512 to my knowledge does not have a TSN time-aware shaper, so you
> can delete.
> 
> > +
> > +// Note: These addresses and offsets are all shifted down
> > +// by two. This is because the SPI protocol needs them to
> > +// be before they get sent out.
> > +//
> > +// An alternative is to keep them standardized, but then
> > +// a separate spi_bus regmap would need to be defined.
> > +//
> > +// That might be optimal though. The 'Read' protocol of
> > +// the VSC driver might be much quicker if we add padding
> > +// bytes, which I don't think regmap supports.
> > +static const u32 vsc7512_ana_regmap[] = {
> > +	REG(ANA_ADVLEARN,			0x2400),
> > +	REG(ANA_VLANMASK,			0x2401),
> > +	REG_RESERVED(ANA_PORT_B_DOMAIN),
> > +	REG(ANA_ANAGEFIL,			0x2403),
> > +	REG(ANA_ANEVENTS,			0x2404),
> > +	REG(ANA_STORMLIMIT_BURST,		0x2405),
> > +	REG(ANA_STORMLIMIT_CFG,			0x2406),
> > +	REG(ANA_ISOLATED_PORTS,			0x240a),
> > +	REG(ANA_COMMUNITY_PORTS,		0x240b),
> > +	REG(ANA_AUTOAGE,			0x240c),
> > +	REG(ANA_MACTOPTIONS,			0x240d),
> > +	REG(ANA_LEARNDISC,			0x240e),
> > +	REG(ANA_AGENCTRL,			0x240f),
> > +	REG(ANA_MIRRORPORTS,			0x2410),
> > +	REG(ANA_EMIRRORPORTS,			0x2411),
> > +	REG(ANA_FLOODING,			0x2412),
> > +	REG(ANA_FLOODING_IPMC,			0x2413),
> > +	REG(ANA_SFLOW_CFG,			0x2414),
> > +	REG(ANA_PORT_MODE,			0x2420),
> > +	REG(ANA_PGID_PGID,			0x2300),
> > +	REG(ANA_TABLES_ANMOVED,			0x22cc),
> > +	REG(ANA_TABLES_MACHDATA,		0x22cd),
> > +	REG(ANA_TABLES_MACLDATA,		0x22ce),
> > +	REG(ANA_TABLES_MACACCESS,		0x22cf),
> > +	REG(ANA_TABLES_MACTINDX,		0x22d0),
> > +	REG(ANA_TABLES_VLANACCESS,		0x22d1),
> > +	REG(ANA_TABLES_VLANTIDX,		0x22d2),
> > +	REG(ANA_TABLES_ENTRYLIM,		0x22c0),
> > +	REG(ANA_TABLES_PTP_ID_HIGH,		0x22d5),
> > +	REG(ANA_TABLES_PTP_ID_LOW,		0x22d6),
> > +	REG(ANA_PORT_VLAN_CFG,			0x1c00),
> > +	REG(ANA_PORT_DROP_CFG,			0x1c01),
> > +	REG(ANA_PORT_QOS_CFG,			0x1c02),
> > +	REG(ANA_PORT_VCAP_CFG,			0x1c03),
> > +	REG(ANA_PORT_VCAP_S1_KEY_CFG,		0x1c04),
> > +	REG(ANA_PORT_VCAP_S2_CFG,		0x1c07),
> > +	REG(ANA_PORT_PCP_DEI_MAP,		0x1c08),
> > +	REG(ANA_PORT_CPU_FWD_CFG,		0x1c18),
> > +	REG(ANA_PORT_CPU_FWD_BPDU_CFG,		0x1c19),
> > +	REG(ANA_PORT_CPU_FWD_GARP_CFG,		0x1c1a),
> > +	REG(ANA_PORT_CPU_FWD_CCM_CFG,		0x1c1b),
> > +	REG(ANA_PORT_PORT_CFG,			0x1c1c),
> > +	REG(ANA_PORT_POL_CFG,			0x1c1d),
> > +	REG(ANA_PORT_PTP_CFG,			0x1c1e),
> > +	REG(ANA_PORT_PTP_DLY1_CFG,		0x1c1f),
> > +	REG(ANA_PORT_PTP_DLY2_CFG,		0x1c20),
> > +	REG(ANA_PFC_PFC_CFG,			0x2200),
> > +	REG_RESERVED(ANA_PFC_PFC_TIMER),
> > +	REG_RESERVED(ANA_IPT_OAM_MEP_CFG),
> > +	REG_RESERVED(ANA_IPT_IPT),
> > +	REG_RESERVED(ANA_PPT_PPT),
> > +	REG_RESERVED(ANA_FID_MAP_FID_MAP),
> > +	REG(ANA_AGGR_CFG,			0x242d),
> > +	REG(ANA_CPUQ_CFG,			0x242e),
> > +	REG_RESERVED(ANA_CPUQ_CFG2),
> > +	REG(ANA_CPUQ_8021_CFG,			0x242f),
> > +	REG(ANA_DSCP_CFG,			0x2440),
> > +	REG(ANA_DSCP_REWR_CFG,			0x2480),
> > +	REG(ANA_VCAP_RNG_TYPE_CFG,		0x2490),
> > +	REG(ANA_VCAP_RNG_VAL_CFG,		0x2498),
> > +	REG_RESERVED(ANA_VRAP_CFG),
> > +	REG_RESERVED(ANA_VRAP_HDR_DATA),
> > +	REG_RESERVED(ANA_VRAP_HDR_MASK),
> > +	REG(ANA_DISCARD_CFG,			0x24a3),
> > +	REG(ANA_FID_CFG,			0x24a4),
> > +	REG(ANA_POL_PIR_CFG,			0x1000),
> > +	REG(ANA_POL_CIR_CFG,			0x1001),
> > +	REG(ANA_POL_MODE_CFG,			0x1002),
> > +	REG(ANA_POL_PIR_STATE,			0x1003),
> > +	REG(ANA_POL_CIR_STATE,			0x1004),
> > +	REG_RESERVED(ANA_POL_STATE),
> > +	REG(ANA_POL_FLOWC,			0x22e0),
> > +	REG(ANA_POL_HYST,			0x22fb),
> > +	REG_RESERVED(ANA_POL_MISC_CFG),
> > +};
> > +
> > +static const u32 vsc7512_qs_regmap[] = {
> > +	REG(QS_XTR_GRP_CFG,			0x0000),
> > +	REG(QS_XTR_RD,				0x0002),
> > +	REG(QS_XTR_FRM_PRUNING,			0x004),
> > +	REG(QS_XTR_FLUSH,			0x0006),
> > +	REG(QS_XTR_DATA_PRESENT,		0x0007),
> > +
> > +	REG(QS_INJ_GRP_CFG,			0x0009),
> > +	REG(QS_INJ_WR,				0x000b),
> > +	REG(QS_INJ_CTRL,			0x000d),
> > +	REG(QS_INJ_STATUS,			0x000f),
> > +	REG(QS_INJ_ERR,				0x0010),
> > +	REG_RESERVED(QS_INH_DBG),
> > +};
> > +
> > +static const u32 vsc7512_vcap_regmap[] = {
> > +	REG(VCAP_CORE_UPDATE_CTRL,		0x000000),
> > +	REG(VCAP_CORE_MV_CFG,			0x000001),
> > +	REG(VCAP_CACHE_ENTRY_DAT,		0x000002),
> > +	REG(VCAP_CACHE_MASK_DAT,		0x000042),
> > +	REG(VCAP_CACHE_ACTION_DAT,		0x000082),
> > +	REG(VCAP_CACHE_CNT_DAT,			0x0000c2),
> > +	REG(VCAP_CACHE_TG_DAT,			0x0000e2),
> > +	REG(VCAP_CONST_VCAP_VER,		0x0000e6),
> > +	REG(VCAP_CONST_ENTRY_WIDTH,		0x0000e7),
> > +	REG(VCAP_CONST_ENTRY_CNT,		0x0000e8),
> > +	REG(VCAP_CONST_ENTRY_SWCNT,		0x0000e9),
> > +	REG(VCAP_CONST_ENTRY_TG_WIDTH,		0x0000ea),
> > +	REG(VCAP_CONST_ACTION_DEF_CNT,		0x0000eb),
> > +	REG(VCAP_CONST_ACTION_WIDTH,		0x0000ec),
> > +	REG(VCAP_CONST_CNT_WIDTH,		0x0000ed),
> > +	REG(VCAP_CONST_CORE_CNT,		0x0000ee),
> > +	REG(VCAP_CONST_IF_CNT,			0x0000ef),
> > +};
> > +
> > +static const u32 vsc7512_qsys_regmap[] = {
> > +	REG(QSYS_PORT_MODE,			0x4480),
> > +	REG(QSYS_SWITCH_PORT_MODE,		0x448d),
> > +	REG(QSYS_STAT_CNT_CFG,			0x4499),
> > +	REG(QSYS_EEE_CFG,			0x449a),
> > +	REG(QSYS_EEE_THRES,			0x44a5),
> > +	REG(QSYS_IGR_NO_SHARING,		0x44a6),
> > +	REG(QSYS_EGR_NO_SHARING,		0x44a7),
> > +	REG(QSYS_SW_STATUS,			0x44a8),
> > +	REG(QSYS_EXT_CPU_CFG,			0x44b4),
> > +	REG_RESERVED(QSYS_PAD_CFG),
> > +	REG(QSYS_CPU_GROUP_MAP,			0x44b6),
> > +	REG_RESERVED(QSYS_QMAP),
> > +	REG_RESERVED(QSYS_ISDX_SGRP),
> > +	REG_RESERVED(QSYS_TIMED_FRAME_ENTRY),
> > +	REG(QSYS_TFRM_MISC,			0x44c4),
> > +	REG(QSYS_TFRM_PORT_DLY,			0x44c5),
> > +	REG(QSYS_TFRM_TIMER_CFG_1,		0x44c6),
> > +	REG(QSYS_TFRM_TIMER_CFG_2,		0x44c7),
> > +	REG(QSYS_TFRM_TIMER_CFG_3,		0x44c8),
> > +	REG(QSYS_TFRM_TIMER_CFG_4,		0x44c9),
> > +	REG(QSYS_TFRM_TIMER_CFG_5,		0x44ca),
> > +	REG(QSYS_TFRM_TIMER_CFG_6,		0x44cb),
> > +	REG(QSYS_TFRM_TIMER_CFG_7,		0x44cc),
> > +	REG(QSYS_TFRM_TIMER_CFG_8,		0x44cd),
> > +	REG(QSYS_RED_PROFILE,			0x44ce),
> > +	REG(QSYS_RES_QOS_MODE,			0x44de),
> > +	REG(QSYS_RES_CFG,			0x4800),
> > +	REG(QSYS_RES_STAT,			0x4801),
> > +	REG(QSYS_EGR_DROP_MODE,			0x44df),
> > +	REG(QSYS_EQ_CTRL,			0x44e0),
> > +	REG_RESERVED(QSYS_EVENTS_CORE),
> > +	REG(QSYS_CIR_CFG,			0x0000),
> > +	REG(QSYS_EIR_CFG,			0x0001),
> > +	REG(QSYS_SE_CFG,			0x0002),
> > +	REG(QSYS_SE_DWRR_CFG,			0x0003),
> > +	REG_RESERVED(QSYS_SE_CONNECT),
> > +	REG(QSYS_SE_DLB_SENSE,			0x0010),
> > +	REG(QSYS_CIR_STATE,			0x0011),
> > +	REG(QSYS_EIR_STATE,			0x0012),
> > +	REG_RESERVED(QSYS_SE_STATE),
> > +	REG(QSYS_HSCH_MISC_CFG,			0x44e2),
> > +};
> > +
> > +static const u32 vsc7512_rew_regmap[] = {
> > +	REG(REW_PORT_VLAN_CFG,			0x000),
> > +	REG(REW_TAG_CFG,			0x001),
> > +	REG(REW_PORT_CFG,			0x002),
> > +	REG(REW_DSCP_CFG,			0x003),
> > +	REG(REW_PCP_DEI_QOS_MAP_CFG,		0x004),
> > +	REG(REW_PTP_CFG,			0x014),
> > +	REG(REW_PTP_DLY1_CFG,			0x015),
> > +	REG(REW_RED_TAG_CFG,			0x016),
> > +	REG(REW_DSCP_REMAP_DP1_CFG,		0x104),
> > +	REG(REW_DSCP_REMAP_CFG,			0x144),
> > +	REG_RESERVED(REW_STAT_CFG),
> > +	REG_RESERVED(REW_REW_STICKY),
> > +	REG_RESERVED(REW_PPT),
> > +};
> > +
> > +static const u32 vsc7512_sys_regmap[] = {
> > +	REG(SYS_COUNT_RX_OCTETS,		0x000),
> > +	REG(SYS_COUNT_RX_MULTICAST,		0x002),
> > +	REG(SYS_COUNT_RX_SHORTS,		0x004),
> > +	REG(SYS_COUNT_RX_FRAGMENTS,		0x005),
> > +	REG(SYS_COUNT_RX_JABBERS,		0x006),
> > +	REG(SYS_COUNT_RX_64,			0x009),
> > +	REG(SYS_COUNT_RX_65_127,		0x00a),
> > +	REG(SYS_COUNT_RX_128_255,		0x00b),
> > +	REG(SYS_COUNT_RX_256_1023,		0x00c),
> > +	REG(SYS_COUNT_RX_1024_1526,		0x00d),
> > +	REG(SYS_COUNT_RX_1527_MAX,		0x00e),
> > +	REG(SYS_COUNT_RX_LONGS,			0x011),
> > +	REG(SYS_COUNT_TX_OCTETS,		0x080),
> > +	REG(SYS_COUNT_TX_COLLISION,		0x084),
> > +	REG(SYS_COUNT_TX_DROPS,			0x085),
> > +	REG(SYS_COUNT_TX_64,			0x087),
> > +	REG(SYS_COUNT_TX_65_127,		0x088),
> > +	REG(SYS_COUNT_TX_128_511,		0x089),
> > +	REG(SYS_COUNT_TX_512_1023,		0x08a),
> > +	REG(SYS_COUNT_TX_1024_1526,		0x08b),
> > +	REG(SYS_COUNT_TX_1527_MAX,		0x08c),
> > +	REG(SYS_COUNT_TX_AGING,			0x09e),
> > +	REG(SYS_RESET_CFG,			0x142),
> > +	REG(SYS_VLAN_ETYPE_CFG,			0x144),
> > +	REG(SYS_PORT_MODE,			0x145),
> > +	REG(SYS_FRONT_PORT_MODE,		0x152),
> > +	REG(SYS_FRM_AGING,			0x15d),
> > +	REG(SYS_STAT_CFG,			0x15e),
> > +	REG_RESERVED(SYS_MISC_CFG),
> > +	REG(SYS_REW_MAC_HIGH_CFG,		0x16c),
> > +	REG(SYS_REW_MAC_LOW_CFG,		0x177),
> > +	REG(SYS_PAUSE_CFG,			0x182),
> > +	REG(SYS_PAUSE_TOT_CFG,			0x18e),
> > +	REG(SYS_ATOP,				0x18f),
> > +	REG(SYS_ATOP_TOT_CFG,			0x19b),
> > +	REG(SYS_MAC_FC_CFG,			0x19c),
> > +	REG(SYS_MMGT,				0x1a7),
> > +	REG_RESERVED(SYS_MMGT_FAST),
> > +	REG_RESERVED(SYS_EVENTS_DIF),
> > +	REG_RESERVED(SYS_EVENTS_CORE),
> > +	REG_RESERVED(SYS_CNT),
> > +	REG(SYS_PTP_STATUS,			0x1ae),
> > +	REG(SYS_PTP_TXSTAMP,			0x1af),
> > +	REG(SYS_PTP_NXT,			0x1b0),
> > +	REG(SYS_PTP_CFG,			0x1b1),
> > +	REG_RESERVED(SYS_CM_ADDR),
> > +	REG_RESERVED(SYS_CM_DATA_WR),
> > +	REG_RESERVED(SYS_CM_DATA_RD),
> > +	REG_RESERVED(SYS_CM_OP),
> > +	REG_RESERVED(SYS_CM_DATA),
> > +};
> > +
> > +static const u32 vsc7512_ptp_regmap[] = {
> > +	REG(PTP_PIN_CFG,			0x000000),
> > +	REG(PTP_PIN_TOD_SEC_MSB,		0x000001),
> > +	REG(PTP_PIN_TOD_SEC_LSB,		0x000002),
> > +	REG(PTP_PIN_TOD_NSEC,			0x000003),
> > +	REG(PTP_PIN_WF_HIGH_PERIOD,		0x000005),
> > +	REG(PTP_PIN_WF_LOW_PERIOD,		0x000006),
> > +	REG(PTP_CFG_MISC,			0x000028),
> > +	REG(PTP_CLK_CFG_ADJ_CFG,		0x000029),
> > +	REG(PTP_CLK_CFG_ADJ_FREQ,		0x00002a),
> > +};
> > +
> > +static const u32 vsc7512_gcb_regmap[] = {
> > +	REG(GCB_SOFT_RST,			0xc002),
> > +};
> > +
> > +static const u32 vsc7512_dev_gmii_regmap[] = {
> > +	REG(DEV_CLOCK_CFG,			0x0),
> > +	REG(DEV_PORT_MISC,			0x1),
> > +	REG(DEV_EEE_CFG,			0x3),
> > +	REG(DEV_RX_PATH_DELAY,			0x4),
> > +	REG(DEV_TX_PATH_DELAY,			0x5),
> > +	REG(DEV_PTP_PREDICT_CFG,		0x6),
> > +	REG(DEV_MAC_ENA_CFG,			0x7),
> > +	REG(DEV_MAC_MODE_CFG,			0x8),
> > +	REG(DEV_MAC_MAXLEN_CFG,			0x9),
> > +	REG(DEV_MAC_TAGS_CFG,			0xa),
> > +	REG(DEV_MAC_ADV_CHK_CFG,		0xb),
> > +	REG(DEV_MAC_IFG_CFG,			0xc),
> > +	REG(DEV_MAC_HDX_CFG,			0xd),
> > +	REG(DEV_MAC_DBG_CFG,			0xe),
> > +	REG(DEV_MAC_FC_MAC_LOW_CFG,		0xf),
> > +	REG(DEV_MAC_FC_MAC_HIGH_CFG,		0x10),
> > +	REG(DEV_MAC_STICKY,			0x11),
> > +	REG_RESERVED(PCS1G_CFG),
> > +	REG_RESERVED(PCS1G_MODE_CFG),
> > +	REG_RESERVED(PCS1G_SD_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_NP_CFG),
> > +	REG_RESERVED(PCS1G_LB_CFG),
> > +	REG_RESERVED(PCS1G_DBG_CFG),
> > +	REG_RESERVED(PCS1G_CDET_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_STATUS),
> > +	REG_RESERVED(PCS1G_ANEG_NP_STATUS),
> > +	REG_RESERVED(PCS1G_LINK_STATUS),
> > +	REG_RESERVED(PCS1G_LINK_DOWN_CNT),
> > +	REG_RESERVED(PCS1G_STICKY),
> > +	REG_RESERVED(PCS1G_DEBUG_STATUS),
> > +	REG_RESERVED(PCS1G_LPI_CFG),
> > +	REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT),
> > +	REG_RESERVED(PCS1G_LPI_STATUS),
> > +	REG_RESERVED(PCS1G_TSTPAT_MODE_CFG),
> > +	REG_RESERVED(PCS1G_TSTPAT_STATUS),
> > +	REG_RESERVED(DEV_PCS_FX100_CFG),
> > +	REG_RESERVED(DEV_PCS_FX100_STATUS),
> > +};
> > +
> > +static const u32 vsc7512_cpu_org_regmap[] = {
> > +	REG(DEV_CPUORG_IF_CTRL,			0x0000),
> > +	REG(DEV_CPUORG_IF_CFGSTAT,		0x0001),
> > +	REG(DEV_CPUORG_ORG_CFG,			0x0002),
> > +	REG(DEV_CPUORG_ERR_CNTS,		0x0003),
> > +	REG(DEV_CPUORG_TIMEOUT_CFG,		0x0004),
> > +	REG(DEV_CPUORG_GPR,			0x0005),
> > +	REG(DEV_CPUORG_MAILBOX_SET,		0x0006),
> > +	REG(DEV_CPUORG_MAILBOX_CLR,		0x0007),
> > +	REG(DEV_CPUORG_MAILBOX,			0x0008),
> > +	REG(DEV_CPUORG_SEMA_CFG,		0x0009),
> > +	REG(DEV_CPUORG_SEMA0,			0x000a),
> > +	REG(DEV_CPUORG_SEMA0_OWNER,		0x000b),
> > +	REG(DEV_CPUORG_SEMA1,			0x000c),
> > +	REG(DEV_CPUORG_SEMA1_OWNER,		0x000d),
> > +};
> 
> I know I changed my mind, but if the regmap is the same as for
> drivers/net/ethernet/mscc/ocelot_vsc7514.c, just divided by 4, then it
> makes a lot more sense to implement the custom regmap accessors and
> reuse the regmaps from there.

I agree. My initial thought was the regmap could utilize consecutive
reads / writes, and that wasn't logic I wanted to recreate. I don't
think the ocelot driver could benefit from async SPI transfers, so that
might be a moot point.

> 
> > +static const u32 *vsc7512_regmap[TARGET_MAX] = {
> > +	[ANA] = vsc7512_ana_regmap,
> > +	[QS] = vsc7512_qs_regmap,
> > +	[QSYS] = vsc7512_qsys_regmap,
> > +	[REW] = vsc7512_rew_regmap,
> > +	[SYS] = vsc7512_sys_regmap,
> > +	[S0] = vsc7512_vcap_regmap,
> > +	[S1] = vsc7512_vcap_regmap,
> > +	[S2] = vsc7512_vcap_regmap,
> > +	[PTP] = vsc7512_ptp_regmap,
> > +	[GCB] = vsc7512_gcb_regmap,
> > +	[DEV_GMII] = vsc7512_dev_gmii_regmap,
> > +	[DEV_CPUORG] = vsc7512_cpu_org_regmap,
> > +};
> > +
> > +static int vsc7512_soft_rst_status(struct ocelot *ocelot)
> > +{
> > +	int val;
> > +
> > +	ocelot_field_read(ocelot, GCB_SOFT_RST_SWC_RST, &val);
> > +
> > +	return val;
> > +}
> > +
> > +#define VSC7512_INIT_TIMEOUT 50000
> > +#define VSC7512_GCB_RST_SLEEP 100
> > +
> > +static int vsc7512_reset(struct ocelot *ocelot)
> > +{
> > +	int val, err;
> > +
> > +	ocelot_field_write(ocelot, GCB_SOFT_RST_SWC_RST, 1);
> > +
> > +	err = readx_poll_timeout(vsc7512_soft_rst_status, ocelot, val, !val,
> > +				 VSC7512_GCB_RST_SLEEP, VSC7512_INIT_TIMEOUT);
> > +	if (err) {
> > +		dev_err(ocelot->dev, "timeout: switch core reset\n");
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> 
> I'm surprised that the reset procedure for VSC7512 is different than
> what is implemented in ocelot_reset() for VSC7514.

I will look more into this and repair as needed. I'm also considering
the ability to use a GPIO as an external reset, which might negate any
reset procedure the MIPS would be doing.

> 
> > +static u16 vsc7512_wm_enc(u16 value)
> > +{
> > +	WARN_ON(value >= 16 * BIT(8));
> > +
> > +	if (value >= BIT(8))
> > +		return BIT(8) | (value / 16);
> > +
> > +	return value;
> > +}
> 
> Don't duplicate, just EXPORT_SYMBOL(ocelot_wm_enc) and call here.

I'll consider this moving forward.

> 
> > +static const struct ocelot_ops vsc7512_ops = {
> > +	.reset		= vsc7512_reset,
> > +	.wm_enc		= vsc7512_wm_enc,
> 
> You must be working on an old kernel or something. There's also a
> watermark decode function which you can reuse from ocelot (ocelot_wm_dec).

Yes, my proof-of-concept was from a 5.10 kernel. I brought it up to
mainline before submitting the patch and must have missed this. Per the
documentation, I'll base my next submission entirely on the latest 5.12
release.

> 
> > +	.port_to_netdev	= felix_port_to_netdev,
> > +	.netdev_to_port	= felix_netdev_to_port,
> > +};
> > +
> > +/* Addresses are relative to the SPI device's base address, downshifted by 2*/
> > +static const struct resource vsc7512_target_io_res[TARGET_MAX] = {
> > +	[ANA] = {
> > +		.start	= 0x1c620000,
> > +		.end	= 0x1c623fff,
> > +		.name	= "ana",
> > +	},
> > +	[QS] = {
> > +		.start	= 0x1c420000,
> > +		.end	= 0x1c42003f,
> > +		.name	= "qs",
> > +	},
> > +	[QSYS] = {
> > +		.start	= 0x1c600000,
> > +		.end	= 0x1c607fff,
> > +		.name	= "qsys",
> > +	},
> > +	[REW] = {
> > +		.start	= 0x1c40c000,
> > +		.end	= 0x1c40ffff,
> > +		.name	= "rew",
> > +	},
> > +	[SYS] = {
> > +		.start	= 0x1c404000,
> > +		.end	= 0x1c407fff,
> > +		.name	= "sys",
> > +	},
> > +	[S0] = {
> > +		.start	= 0x1c410000,
> > +		.end	= 0x1c4100ff,
> > +		.name	= "s0",
> > +	},
> > +	[S1] = {
> > +		.start	= 0x1c414000,
> > +		.end	= 0x1c4140ff,
> > +		.name	= "s1",
> > +	},
> > +	[S2] = {
> > +		.start	= 0x1c418000,
> > +		.end	= 0x1c4180ff,
> > +		.name	= "s2",
> > +	},
> > +	[GCB] =	{
> > +		.start	= 0x1c41c000,
> > +		.end	= 0x1c41c07f,
> > +		.name	= "devcpu_gcb",
> > +	},
> > +	[DEV_CPUORG] = {
> > +		.start	= 0x1c400000,
> > +		.end	= 0x1c4000ff,
> > +		.name	= "devcpu_org",
> > +	},
> > +};
> > +
> > +static const struct resource vsc7512_port_io_res[] = {
> > +	{
> > +		.start	= 0x1c478000,
> > +		.end	= 0x1c47bfff,
> > +		.name	= "port0",
> > +	},
> > +	{
> > +		.start	= 0x1c47c000,
> > +		.end	= 0x1c47ffff,
> > +		.name	= "port1",
> > +	},
> > +	{
> > +		.start	= 0x1c480000,
> > +		.end	= 0x1c483fff,
> > +		.name	= "port2",
> > +	},
> > +	{
> > +		.start	= 0x1c484000,
> > +		.end	= 0x1c487fff,
> > +		.name	= "port3",
> > +	},
> > +	{
> > +		.start	= 0x1c488000,
> > +		.end	= 0x1c48bfff,
> > +		.name	= "port4",
> > +	},
> > +	{
> > +		.start	= 0x1c48c000,
> > +		.end	= 0x1c48ffff,
> > +		.name	= "port5",
> > +	},
> > +};
> > +
> > +static const struct reg_field vsc7512_regfields[REGFIELD_MAX] = {
> > +	[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
> > +	[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
> > +	[ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
> > +	[ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
> > +	[ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
> > +	[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
> > +	[ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
> > +	[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
> > +	[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
> > +	[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
> > +	[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
> > +	[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
> > +	[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
> > +	[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
> > +	[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
> > +	[ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
> > +	[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
> > +	[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
> > +	[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
> > +	[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
> > +	[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
> > +	[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
> > +	[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
> > +	[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
> > +	[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
> > +	[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
> > +	[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
> > +	[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
> > +	[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
> > +	[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
> > +	[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
> > +	[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
> > +	[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
> > +	[GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
> > +	[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
> > +	[SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
> > +	[SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
> > +	/* Replicated per number of ports (12), register size 4 per port */
> > +	[QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 12, 4),
> > +	[SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 12, 4),
> > +	[SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 12, 4),
> > +	[SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 12, 4),
> > +	[SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 12, 4),
> > +};
> > +
> > +static const struct ocelot_stat_layout vsc7512_stats_layout[] = {
> > +	{ .offset = 0x00,	.name = "rx_octets", },
> > +	{ .offset = 0x01,	.name = "rx_unicast", },
> > +	{ .offset = 0x02,	.name = "rx_multicast", },
> > +	{ .offset = 0x03,	.name = "rx_broadcast", },
> > +	{ .offset = 0x04,	.name = "rx_shorts", },
> > +	{ .offset = 0x05,	.name = "rx_fragments", },
> > +	{ .offset = 0x06,	.name = "rx_jabbers", },
> > +	{ .offset = 0x07,	.name = "rx_crc_align_errs", },
> > +	{ .offset = 0x08,	.name = "rx_sym_errs", },
> > +	{ .offset = 0x09,	.name = "rx_frames_below_65_octets", },
> > +	{ .offset = 0x0A,	.name = "rx_frames_65_to_127_octets", },
> > +	{ .offset = 0x0B,	.name = "rx_frames_128_to_255_octets", },
> > +	{ .offset = 0x0C,	.name = "rx_frames_256_to_511_octets", },
> > +	{ .offset = 0x0D,	.name = "rx_frames_512_to_1023_octets", },
> > +	{ .offset = 0x0E,	.name = "rx_frames_1024_to_1526_octets", },
> > +	{ .offset = 0x0F,	.name = "rx_frames_over_1526_octets", },
> > +	{ .offset = 0x10,	.name = "rx_pause", },
> > +	{ .offset = 0x11,	.name = "rx_control", },
> > +	{ .offset = 0x12,	.name = "rx_longs", },
> > +	{ .offset = 0x13,	.name = "rx_classified_drops", },
> > +	{ .offset = 0x14,	.name = "rx_red_prio_0", },
> > +	{ .offset = 0x15,	.name = "rx_red_prio_1", },
> > +	{ .offset = 0x16,	.name = "rx_red_prio_2", },
> > +	{ .offset = 0x17,	.name = "rx_red_prio_3", },
> > +	{ .offset = 0x18,	.name = "rx_red_prio_4", },
> > +	{ .offset = 0x19,	.name = "rx_red_prio_5", },
> > +	{ .offset = 0x1A,	.name = "rx_red_prio_6", },
> > +	{ .offset = 0x1B,	.name = "rx_red_prio_7", },
> > +	{ .offset = 0x1C,	.name = "rx_yellow_prio_0", },
> > +	{ .offset = 0x1D,	.name = "rx_yellow_prio_1", },
> > +	{ .offset = 0x1E,	.name = "rx_yellow_prio_2", },
> > +	{ .offset = 0x1F,	.name = "rx_yellow_prio_3", },
> > +	{ .offset = 0x20,	.name = "rx_yellow_prio_4", },
> > +	{ .offset = 0x21,	.name = "rx_yellow_prio_5", },
> > +	{ .offset = 0x22,	.name = "rx_yellow_prio_6", },
> > +	{ .offset = 0x23,	.name = "rx_yellow_prio_7", },
> > +	{ .offset = 0x24,	.name = "rx_green_prio_0", },
> > +	{ .offset = 0x25,	.name = "rx_green_prio_1", },
> > +	{ .offset = 0x26,	.name = "rx_green_prio_2", },
> > +	{ .offset = 0x27,	.name = "rx_green_prio_3", },
> > +	{ .offset = 0x28,	.name = "rx_green_prio_4", },
> > +	{ .offset = 0x29,	.name = "rx_green_prio_5", },
> > +	{ .offset = 0x2A,	.name = "rx_green_prio_6", },
> > +	{ .offset = 0x2B,	.name = "rx_green_prio_7", },
> > +	{ .offset = 0x40,	.name = "tx_octets", },
> > +	{ .offset = 0x41,	.name = "tx_unicast", },
> > +	{ .offset = 0x42,	.name = "tx_multicast", },
> > +	{ .offset = 0x43,	.name = "tx_broadcast", },
> > +	{ .offset = 0x44,	.name = "tx_collision", },
> > +	{ .offset = 0x45,	.name = "tx_drops", },
> > +	{ .offset = 0x46,	.name = "tx_pause", },
> > +	{ .offset = 0x47,	.name = "tx_frames_below_65_octets", },
> > +	{ .offset = 0x48,	.name = "tx_frames_65_to_127_octets", },
> > +	{ .offset = 0x49,	.name = "tx_frames_128_255_octets", },
> > +	{ .offset = 0x4A,	.name = "tx_frames_256_511_octets", },
> > +	{ .offset = 0x4B,	.name = "tx_frames_512_1023_octets", },
> > +	{ .offset = 0x4C,	.name = "tx_frames_1024_1526_octets", },
> > +	{ .offset = 0x4D,	.name = "tx_frames_over_1526_octets", },
> > +	{ .offset = 0x4E,	.name = "tx_yellow_prio_0", },
> > +	{ .offset = 0x4F,	.name = "tx_yellow_prio_1", },
> > +	{ .offset = 0x50,	.name = "tx_yellow_prio_2", },
> > +	{ .offset = 0x51,	.name = "tx_yellow_prio_3", },
> > +	{ .offset = 0x52,	.name = "tx_yellow_prio_4", },
> > +	{ .offset = 0x53,	.name = "tx_yellow_prio_5", },
> > +	{ .offset = 0x54,	.name = "tx_yellow_prio_6", },
> > +	{ .offset = 0x55,	.name = "tx_yellow_prio_7", },
> > +	{ .offset = 0x56,	.name = "tx_green_prio_0", },
> > +	{ .offset = 0x57,	.name = "tx_green_prio_1", },
> > +	{ .offset = 0x58,	.name = "tx_green_prio_2", },
> > +	{ .offset = 0x59,	.name = "tx_green_prio_3", },
> > +	{ .offset = 0x5A,	.name = "tx_green_prio_4", },
> > +	{ .offset = 0x5B,	.name = "tx_green_prio_5", },
> > +	{ .offset = 0x5C,	.name = "tx_green_prio_6", },
> > +	{ .offset = 0x5D,	.name = "tx_green_prio_7", },
> > +	{ .offset = 0x5E,	.name = "tx_aged", },
> > +	{ .offset = 0x80,	.name = "drop_local", },
> > +	{ .offset = 0x81,	.name = "drop_tail", },
> > +	{ .offset = 0x82,	.name = "drop_yellow_prio_0", },
> > +	{ .offset = 0x83,	.name = "drop_yellow_prio_1", },
> > +	{ .offset = 0x84,	.name = "drop_yellow_prio_2", },
> > +	{ .offset = 0x85,	.name = "drop_yellow_prio_3", },
> > +	{ .offset = 0x86,	.name = "drop_yellow_prio_4", },
> > +	{ .offset = 0x87,	.name = "drop_yellow_prio_5", },
> > +	{ .offset = 0x88,	.name = "drop_yellow_prio_6", },
> > +	{ .offset = 0x89,	.name = "drop_yellow_prio_7", },
> > +	{ .offset = 0x8A,	.name = "drop_green_prio_0", },
> > +	{ .offset = 0x8B,	.name = "drop_green_prio_1", },
> > +	{ .offset = 0x8C,	.name = "drop_green_prio_2", },
> > +	{ .offset = 0x8D,	.name = "drop_green_prio_3", },
> > +	{ .offset = 0x8E,	.name = "drop_green_prio_4", },
> > +	{ .offset = 0x8F,	.name = "drop_green_prio_5", },
> > +	{ .offset = 0x90,	.name = "drop_green_prio_6", },
> > +	{ .offset = 0x91,	.name = "drop_green_prio_7", },
> > +};
> > +
> > +struct felix_spi_data {
> > +	struct regmap *map;
> > +	struct felix felix;
> > +};
> > +
> > +/* TODO: The regmap has overlaps for DEVCPU_ORG and other peripherals. I believe
> > + * this is handled with a page register, and should hopefully be easy to use
> > + * with the existing regmap_config with regmap_range_cfg
> > + */
> 
> Rule of thumb: if you don't need a new regmap (in this case DEVCPU_ORG)
> and you're not even sure about how to access it, then just don't add it
> (at least not now).

Makes sense. Thank you for the feedback.

> 
> > +static const struct regmap_config felix_spi_regmap_config = {
> > +	.reg_bits = 24,
> > +	.reg_stride = 1,
> > +	.val_bits = 32,
> > +
> > +	.write_flag_mask = BIT(7),
> > +	.max_register = 0x3fffffff,
> > +	.use_single_write = true,
> > +	.use_single_read = true,
> > +	.can_multi_write = false,
> > +
> > +	.reg_format_endian = REGMAP_ENDIAN_BIG,
> > +	.val_format_endian = REGMAP_ENDIAN_NATIVE,
> > +};
> > +
> > +#define VSC7512_BYTE_ORDER_LE 0x00000000
> > +#define VSC7512_BYTE_ORDER_BE 0x81818181
> > +#define VSC7512_BIT_ORDER_MSB 0x00000000
> > +#define VSC7512_BIT_ORDER_LSB 0x42424242
> > +
> > +static int felix_spi_init_bus(struct spi_device *spi,
> > +			      struct felix_spi_data *felix_spi)
> > +{
> > +	int err;
> > +	u32 val, check;
> > +
> > +	val = 0;
> > +
> > +#ifdef __LITTLE_ENDIAN
> > +	val |= VSC7512_BYTE_ORDER_LE;
> > +#else
> > +	val |= VSC7512_BYTE_ORDER_BE;
> > +#endif
> > +
> > +	// Hard code this write to set up the interface. After this is done,
> > +	// ocelot_read interface should be able to be used.
> > +	err = regmap_write(felix_spi->map, (0x71000000 >> 2) & 0x3fffff, val);
> > +	if (err < 0) {
> > +		dev_err(&spi->dev, "error %d configuring SPI interface\n", err);
> > +		return err;
> > +	}
> > +
> > +	val = 0x00000000;
> > +	err = regmap_write(felix_spi->map, (0x71000004 >> 2) & 0x3fffff, val);
> 
> I think the last parameter of regmap_write doesn't have to be a
> variable, it can be a constant (zero in this case), so you can remove
> "val = 0" and just pass 0 as last argument.
> 
> > +	if (err < 0) {
> > +		dev_err(&spi->dev, "error %d writing padding bytes\n", err);
> > +		return err;
> > +	}
> > +
> > +	check = val | 0x02000000;
> 
> val is always zero here, no? so "check" is always 0x02000000. It's also
> pretty constant, no need to keep it in a variable.

This will hopefully make more sense on my next RFC. It is as far as my
"functional hardware testing" had gotten, since I was having
communication issues with the chip. I suspect impedance mismatch due to
internal FPGA pull-ups.  Nevertheless:

The least significant nibble here is the number of padding bytes on a
read operation. We can either run the SPI bus at a low frequency (which
is the hard-coded frequency below), manually add a delay between address
and the next clock cycle for the read, or insert a number of padding
bytes between the address and the value. This is outlined in the VSC7512
Rev 4.2 datasheet, section 4.5.2.

I think the best results would depend on the underlying SPI hardware,
but I would like to run at the max_speed_hz rate and have the regmap
handle the delays / padding for me. I'm making up numbers here, but if
the bus runs at 1.25MHz, we might need two padding bytes. In that case,
val would start out at 0x00000002 and when we read back, we would see
that value read back, plus the indication that we read the value over
the SPI bus (0x02000000).

This will be built into the new regmap. When using the generic SPI
regmap this was not possible, so the number of padding bytes was always
zero, and the bus speed always had to be 500KHz regardless of how fast
the bus could be operated.

> 
> > +
> > +	err = regmap_read(felix_spi->map, (0x71000004 >> 2) & 0x3fffff, &val);
> > +	if (err < 0) {
> > +		dev_err(&spi->dev, "Error %d writing padding bytes\n", err);
> > +		return err;
> > +	} else if (check != val) {
> > +		dev_err(&spi->dev,
> > +			"Error configuring SPI bus. V: 0x%08x != 0x%08x\n", val,
> > +			check);
> > +		return -ENODEV;
> > +	}
> 
> Care to explain what you are accessing? Why are you AND-ing the address
> with 0x3fffffff? Whose address is it anyway?

Right wrong or indifferent, it is the calculation from the VSC7512
datasheet, Rev 4.2, section 4.5.2. This is done before the ocelot
regmaps could be used, so this configuration I suspect will have to be
hard-coded to some degree. 

I'll clear this up. Either with a #define to clarify that the math is
actually the SPI address for DEVCPU_ORG:IF_CTRL, or use the
vsc7512_regmap[DEVCPU_ORG] and vsc7512_target_io_res[DEVCPU_ORG]
addresses that are already defined.

> 
> > +
> > +	return err;
> > +}
> > +
> > +static void vsc7512_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, Autoneg);
> > +	phylink_set(mask, Pause);
> > +	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);
> > +
> > +	if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
> > +	    state->interface == PHY_INTERFACE_MODE_INTERNAL ||
> > +	    state->interface == PHY_INTERFACE_MODE_INTERNAL) {
> > +		phylink_set(mask, 2500baseT_Full);
> > +		phylink_set(mask, 2500baseX_Full);
> > +	}
> > +
> > +	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
> > +	bitmap_and(state->advertising, state->advertising, mask,
> > +		   __ETHTOOL_LINK_MODE_MASK_NBITS);
> > +}
> > +
> > +static int vsc7512_prevalidate_phy_mode(struct ocelot *ocelot, int port,
> > +					phy_interface_t phy_mode)
> > +{
> > +	switch (phy_mode) {
> > +	case PHY_INTERFACE_MODE_INTERNAL:
> > +		// TODO: I don't know what ports would not be supported
> > +		// internally.
> > +		return 0;
> 
> PHY_INTERFACE_MODE_INTERNAL means that the MAC is not wired to pinout on
> the SoC, but connected to an internal PHY or to another on-chip MAC.
> I don't really understand the comment.
> 
> > +	case PHY_INTERFACE_MODE_SGMII:
> > +	case PHY_INTERFACE_MODE_QSGMII:
> > +	case PHY_INTERFACE_MODE_USXGMII:
> 
> Sure it supports USXGMII? I think one of the reasons NXP integrated
> their own PCS on the parts with a licensed Microchip switch was to get
> USXGMII support.

I am not. This is something I'll have to look into. I should've pulled
the logic from ocelot_vsc7514.c instead of the felix for this function.
This applies to the surrounding comments as well.

> 
> > +	case PHY_INTERFACE_MODE_2500BASEX:
> > +		if (port == 4 || port == 5)
> > +			return -EOPNOTSUPP;
> 
> Is this copy-paste or is it correct?

Copy-paste

> 
> > +		return 0;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +}
> > +
> > +static int vsc7512_port_setup_tc(struct dsa_switch *ds, int port,
> > +				 enum tc_setup_type type, void *type_data)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static void vsc7512_sched_speed_set(struct ocelot *ocelot, int port, u32 speed)
> > +{
> > +	u8 tas_speed;
> > +
> > +	switch (speed) {
> > +	case SPEED_10:
> > +		tas_speed = OCELOT_SPEED_10;
> > +		break;
> > +
> > +	case SPEED_100:
> > +		tas_speed = OCELOT_SPEED_100;
> > +		break;
> > +
> > +	case SPEED_1000:
> > +		tas_speed = OCELOT_SPEED_1000;
> > +		break;
> > +
> > +	case SPEED_2500:
> > +		tas_speed = OCELOT_SPEED_2500;
> > +		break;
> > +
> > +	default:
> > +		tas_speed = OCELOT_SPEED_1000;
> > +		break;
> > +	}
> > +
> > +	ocelot_rmw_rix(ocelot, QSYS_TAG_CONFIG_LINK_SPEED(tas_speed),
> > +		       QSYS_TAG_CONFIG_LINK_SPEED_M, QSYS_TAG_CONFIG, port);
> > +}
> 
> If vsc7512_port_setup_tc returns -EOPNOTSUPP (i.e. it doesn't offload
> tc-taprio) then vsc7512_sched_speed_set is pointless.
> 
> Also, VSC7512 has TSN?

This is outside my level of understanding at the moment, so I can't
respond with any confidence. I'll research.

> 
> > +
> > +static const struct vcap_field vsc7512_vcap_es0_keys[] = {
> > +	[VCAP_ES0_EGR_PORT]                     = { 0,   4 },
> > +	[VCAP_ES0_IGR_PORT]                     = { 4,   4 },
> > +	[VCAP_ES0_RSV]                          = { 8,   2 },
> > +	[VCAP_ES0_L2_MC]                        = { 10,  1 },
> > +	[VCAP_ES0_L2_BC]                        = { 11,  1 },
> > +	[VCAP_ES0_VID]                          = { 12, 12 },
> > +	[VCAP_ES0_DP]                           = { 24,  1 },
> > +	[VCAP_ES0_PCP]                          = { 25,  3 },
> > +};
> 
> Align with tabs please, to be consistent.
> 
> > +
> > +static const struct vcap_field vsc7512_vcap_es0_actions[]   = {
> > +	[VCAP_ES0_ACT_PUSH_OUTER_TAG]           = { 0,   2 },
> > +	[VCAP_ES0_ACT_PUSH_INNER_TAG]           = { 2,   1 },
> > +	[VCAP_ES0_ACT_TAG_A_TPID_SEL]           = { 3,   2 },
> > +	[VCAP_ES0_ACT_TAG_A_VID_SEL]            = { 5,   1 },
> > +	[VCAP_ES0_ACT_TAG_A_PCP_SEL]            = { 6,   2 },
> > +	[VCAP_ES0_ACT_TAG_A_DEI_SEL]            = { 8,   2 },
> > +	[VCAP_ES0_ACT_TAG_B_TPID_SEL]           = { 10,  2 },
> > +	[VCAP_ES0_ACT_TAG_B_VID_SEL]            = { 12,  1 },
> > +	[VCAP_ES0_ACT_TAG_B_PCP_SEL]            = { 13,  2 },
> > +	[VCAP_ES0_ACT_TAG_B_DEI_SEL]            = { 15,  2 },
> > +	[VCAP_ES0_ACT_VID_A_VAL]                = { 17, 12 },
> > +	[VCAP_ES0_ACT_PCP_A_VAL]                = { 29,  3 },
> > +	[VCAP_ES0_ACT_DEI_A_VAL]                = { 32,  1 },
> > +	[VCAP_ES0_ACT_VID_B_VAL]                = { 33, 12 },
> > +	[VCAP_ES0_ACT_PCP_B_VAL]                = { 45,  3 },
> > +	[VCAP_ES0_ACT_DEI_B_VAL]                = { 48,  1 },
> > +	[VCAP_ES0_ACT_RSV]                      = { 49, 24 },
> > +	[VCAP_ES0_ACT_HIT_STICKY]               = { 73,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is1_keys[] = {
> > +	[VCAP_IS1_HK_TYPE]                      = { 0,    1 },
> > +	[VCAP_IS1_HK_LOOKUP]                    = { 1,    2 },
> > +	[VCAP_IS1_HK_IGR_PORT_MASK]             = { 3,   12 },
> > +	[VCAP_IS1_HK_RSV]                       = { 15,   9 },
> > +	[VCAP_IS1_HK_OAM_Y1731]                 = { 24,   1 },
> > +	[VCAP_IS1_HK_L2_MC]                     = { 25,   1 },
> > +	[VCAP_IS1_HK_L2_BC]                     = { 26,   1 },
> > +	[VCAP_IS1_HK_IP_MC]                     = { 27,   1 },
> > +	[VCAP_IS1_HK_VLAN_TAGGED]               = { 28,   1 },
> > +	[VCAP_IS1_HK_VLAN_DBL_TAGGED]           = { 29,   1 },
> > +	[VCAP_IS1_HK_TPID]                      = { 30,   1 },
> > +	[VCAP_IS1_HK_VID]                       = { 31,  12 },
> > +	[VCAP_IS1_HK_DEI]                       = { 43,   1 },
> > +	[VCAP_IS1_HK_PCP]                       = { 44,   3 },
> > +	/* Specific Fields for IS1 Half Key S1_NORMAL */
> > +	[VCAP_IS1_HK_L2_SMAC]                   = { 47,  48 },
> > +	[VCAP_IS1_HK_ETYPE_LEN]                 = { 95,   1 },
> > +	[VCAP_IS1_HK_ETYPE]                     = { 96,  16 },
> > +	[VCAP_IS1_HK_IP_SNAP]                   = { 112,  1 },
> > +	[VCAP_IS1_HK_IP4]                       = { 113,  1 },
> > +	/* Layer-3 Information */
> > +	[VCAP_IS1_HK_L3_FRAGMENT]               = { 114,  1 },
> > +	[VCAP_IS1_HK_L3_FRAG_OFS_GT0]           = { 115,  1 },
> > +	[VCAP_IS1_HK_L3_OPTIONS]                = { 116,  1 },
> > +	[VCAP_IS1_HK_L3_DSCP]                   = { 117,  6 },
> > +	[VCAP_IS1_HK_L3_IP4_SIP]                = { 123, 32 },
> > +	/* Layer-4 Information */
> > +	[VCAP_IS1_HK_TCP_UDP]                   = { 155,  1 },
> > +	[VCAP_IS1_HK_TCP]                       = { 156,  1 },
> > +	[VCAP_IS1_HK_L4_SPORT]                  = { 157, 16 },
> > +	[VCAP_IS1_HK_L4_RNG]                    = { 173,  8 },
> > +	/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */
> > +	[VCAP_IS1_HK_IP4_INNER_TPID]            = { 47,   1 },
> > +	[VCAP_IS1_HK_IP4_INNER_VID]             = { 48,  12 },
> > +	[VCAP_IS1_HK_IP4_INNER_DEI]             = { 60,   1 },
> > +	[VCAP_IS1_HK_IP4_INNER_PCP]             = { 61,   3 },
> > +	[VCAP_IS1_HK_IP4_IP4]                   = { 64,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_FRAGMENT]           = { 65,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0]       = { 66,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_OPTIONS]            = { 67,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_DSCP]               = { 68,   6 },
> > +	[VCAP_IS1_HK_IP4_L3_IP4_DIP]            = { 74,  32 },
> > +	[VCAP_IS1_HK_IP4_L3_IP4_SIP]            = { 106, 32 },
> > +	[VCAP_IS1_HK_IP4_L3_PROTO]              = { 138,  8 },
> > +	[VCAP_IS1_HK_IP4_TCP_UDP]               = { 146,  1 },
> > +	[VCAP_IS1_HK_IP4_TCP]                   = { 147,  1 },
> > +	[VCAP_IS1_HK_IP4_L4_RNG]                = { 148,  8 },
> > +	[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE]  = { 156, 32 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is1_actions[] = {
> > +	[VCAP_IS1_ACT_DSCP_ENA]                 = { 0,   1 },
> > +	[VCAP_IS1_ACT_DSCP_VAL]                 = { 1,   6 },
> > +	[VCAP_IS1_ACT_QOS_ENA]                  = { 7,   1 },
> > +	[VCAP_IS1_ACT_QOS_VAL]                  = { 8,   3 },
> > +	[VCAP_IS1_ACT_DP_ENA]                   = { 11,  1 },
> > +	[VCAP_IS1_ACT_DP_VAL]                   = { 12,  1 },
> > +	[VCAP_IS1_ACT_PAG_OVERRIDE_MASK]        = { 13,  8 },
> > +	[VCAP_IS1_ACT_PAG_VAL]                  = { 21,  8 },
> > +	[VCAP_IS1_ACT_RSV]                      = { 29,  9 },
> > +	/* The fields below are incorrectly shifted by 2 in the manual */
> > +	[VCAP_IS1_ACT_VID_REPLACE_ENA]          = { 38,  1 },
> > +	[VCAP_IS1_ACT_VID_ADD_VAL]              = { 39, 12 },
> > +	[VCAP_IS1_ACT_FID_SEL]                  = { 51,  2 },
> > +	[VCAP_IS1_ACT_FID_VAL]                  = { 53, 13 },
> > +	[VCAP_IS1_ACT_PCP_DEI_ENA]              = { 66,  1 },
> > +	[VCAP_IS1_ACT_PCP_VAL]                  = { 67,  3 },
> > +	[VCAP_IS1_ACT_DEI_VAL]                  = { 70,  1 },
> > +	[VCAP_IS1_ACT_VLAN_POP_CNT_ENA]         = { 71,  1 },
> > +	[VCAP_IS1_ACT_VLAN_POP_CNT]             = { 72,  2 },
> > +	[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA]      = { 74,  4 },
> > +	[VCAP_IS1_ACT_HIT_STICKY]               = { 78,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is2_keys[] = {
> > +	/* Common: 46 bits */
> > +	[VCAP_IS2_TYPE]                         = { 0,    4 },
> > +	[VCAP_IS2_HK_FIRST]                     = { 4,    1 },
> > +	[VCAP_IS2_HK_PAG]                       = { 5,    8 },
> > +	[VCAP_IS2_HK_IGR_PORT_MASK]             = { 13,  12 },
> > +	[VCAP_IS2_HK_RSV2]                      = { 25,   1 },
> > +	[VCAP_IS2_HK_HOST_MATCH]                = { 26,   1 },
> > +	[VCAP_IS2_HK_L2_MC]                     = { 27,   1 },
> > +	[VCAP_IS2_HK_L2_BC]                     = { 28,   1 },
> > +	[VCAP_IS2_HK_VLAN_TAGGED]               = { 29,   1 },
> > +	[VCAP_IS2_HK_VID]                       = { 30,  12 },
> > +	[VCAP_IS2_HK_DEI]                       = { 42,   1 },
> > +	[VCAP_IS2_HK_PCP]                       = { 43,   3 },
> > +	/* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */
> > +	[VCAP_IS2_HK_L2_DMAC]                   = { 46,  48 },
> > +	[VCAP_IS2_HK_L2_SMAC]                   = { 94,  48 },
> > +	/* MAC_ETYPE (TYPE=000) */
> > +	[VCAP_IS2_HK_MAC_ETYPE_ETYPE]           = { 142, 16 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0]     = { 158, 16 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1]     = { 174,  8 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2]     = { 182,  3 },
> > +	/* MAC_LLC (TYPE=001) */
> > +	[VCAP_IS2_HK_MAC_LLC_L2_LLC]            = { 142, 40 },
> > +	/* MAC_SNAP (TYPE=010) */
> > +	[VCAP_IS2_HK_MAC_SNAP_L2_SNAP]          = { 142, 40 },
> > +	/* MAC_ARP (TYPE=011) */
> > +	[VCAP_IS2_HK_MAC_ARP_SMAC]              = { 46,  48 },
> > +	[VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK]     = { 94,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK]    = { 95,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_LEN_OK]            = { 96,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_TARGET_MATCH]      = { 97,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_SENDER_MATCH]      = { 98,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN]    = { 99,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_OPCODE]            = { 100,  2 },
> > +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP]        = { 102, 32 },
> > +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP]        = { 134, 32 },
> > +	[VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP]        = { 166,  1 },
> > +	/* IP4_TCP_UDP / IP4_OTHER common */
> > +	[VCAP_IS2_HK_IP4]                       = { 46,   1 },
> > +	[VCAP_IS2_HK_L3_FRAGMENT]               = { 47,   1 },
> > +	[VCAP_IS2_HK_L3_FRAG_OFS_GT0]           = { 48,   1 },
> > +	[VCAP_IS2_HK_L3_OPTIONS]                = { 49,   1 },
> > +	[VCAP_IS2_HK_IP4_L3_TTL_GT0]            = { 50,   1 },
> > +	[VCAP_IS2_HK_L3_TOS]                    = { 51,   8 },
> > +	[VCAP_IS2_HK_L3_IP4_DIP]                = { 59,  32 },
> > +	[VCAP_IS2_HK_L3_IP4_SIP]                = { 91,  32 },
> > +	[VCAP_IS2_HK_DIP_EQ_SIP]                = { 123,  1 },
> > +	/* IP4_TCP_UDP (TYPE=100) */
> > +	[VCAP_IS2_HK_TCP]                       = { 124,  1 },
> > +	[VCAP_IS2_HK_L4_DPORT]                  = { 125, 16 },
> > +	[VCAP_IS2_HK_L4_SPORT]                  = { 141, 16 },
> > +	[VCAP_IS2_HK_L4_RNG]                    = { 157,  8 },
> > +	[VCAP_IS2_HK_L4_SPORT_EQ_DPORT]         = { 165,  1 },
> > +	[VCAP_IS2_HK_L4_SEQUENCE_EQ0]           = { 166,  1 },
> > +	[VCAP_IS2_HK_L4_FIN]                    = { 167,  1 },
> > +	[VCAP_IS2_HK_L4_SYN]                    = { 168,  1 },
> > +	[VCAP_IS2_HK_L4_RST]                    = { 169,  1 },
> > +	[VCAP_IS2_HK_L4_PSH]                    = { 170,  1 },
> > +	[VCAP_IS2_HK_L4_ACK]                    = { 171,  1 },
> > +	[VCAP_IS2_HK_L4_URG]                    = { 172,  1 },
> > +	[VCAP_IS2_HK_L4_1588_DOM]               = { 173,  8 },
> > +	[VCAP_IS2_HK_L4_1588_VER]               = { 181,  4 },
> > +	/* IP4_OTHER (TYPE=101) */
> > +	[VCAP_IS2_HK_IP4_L3_PROTO]              = { 124,  8 },
> > +	[VCAP_IS2_HK_L3_PAYLOAD]                = { 132, 56 },
> > +	/* IP6_STD (TYPE=110) */
> > +	[VCAP_IS2_HK_IP6_L3_TTL_GT0]            = { 46,   1 },
> > +	[VCAP_IS2_HK_L3_IP6_SIP]                = { 47, 128 },
> > +	[VCAP_IS2_HK_IP6_L3_PROTO]              = { 175,  8 },
> > +	/* OAM (TYPE=111) */
> > +	[VCAP_IS2_HK_OAM_MEL_FLAGS]             = { 142,  7 },
> > +	[VCAP_IS2_HK_OAM_VER]                   = { 149,  5 },
> > +	[VCAP_IS2_HK_OAM_OPCODE]                = { 154,  8 },
> > +	[VCAP_IS2_HK_OAM_FLAGS]                 = { 162,  8 },
> > +	[VCAP_IS2_HK_OAM_MEPID]                 = { 170, 16 },
> > +	[VCAP_IS2_HK_OAM_CCM_CNTS_EQ0]          = { 186,  1 },
> > +	[VCAP_IS2_HK_OAM_IS_Y1731]              = { 187,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is2_actions[] = {
> > +	[VCAP_IS2_ACT_HIT_ME_ONCE]              = { 0,   1 },
> > +	[VCAP_IS2_ACT_CPU_COPY_ENA]             = { 1,   1 },
> > +	[VCAP_IS2_ACT_CPU_QU_NUM]               = { 2,   3 },
> > +	[VCAP_IS2_ACT_MASK_MODE]                = { 5,   2 },
> > +	[VCAP_IS2_ACT_MIRROR_ENA]               = { 7,   1 },
> > +	[VCAP_IS2_ACT_LRN_DIS]                  = { 8,   1 },
> > +	[VCAP_IS2_ACT_POLICE_ENA]               = { 9,   1 },
> > +	[VCAP_IS2_ACT_POLICE_IDX]               = { 10,  9 },
> > +	[VCAP_IS2_ACT_POLICE_VCAP_ONLY]         = { 19,  1 },
> > +	[VCAP_IS2_ACT_PORT_MASK]                = { 20, 11 },
> > +	[VCAP_IS2_ACT_REW_OP]                   = { 31,  9 },
> > +	[VCAP_IS2_ACT_SMAC_REPLACE_ENA]         = { 40,  1 },
> > +	[VCAP_IS2_ACT_RSV]                      = { 41,  2 },
> > +	[VCAP_IS2_ACT_ACL_ID]                   = { 43,  6 },
> > +	[VCAP_IS2_ACT_HIT_CNT]                  = { 49, 32 },
> > +};
> > +
> > +static struct vcap_props vsc7512_vcap_props[] = {
> > +	[VCAP_ES0] = {
> > +		.action_type_width = 0,
> > +		.action_table = {
> > +			[ES0_ACTION_TYPE_NORMAL] = {
> > +				.width = 73,
> > +				.count = 1,
> > +			},
> > +		},
> > +		.target = S0,
> > +		.keys = vsc7512_vcap_es0_keys,
> > +		.actions = vsc7512_vcap_es0_actions,
> > +	},
> > +	[VCAP_IS1] = {
> > +		.action_type_width = 0,
> > +		.action_table = {
> > +			[IS1_ACTION_TYPE_NORMAL] = {
> > +				.width = 78,
> > +				.count = 4,
> > +			},
> > +		},
> > +		.target = S1,
> > +		.keys = vsc7512_vcap_is1_keys,
> > +		.actions = vsc7512_vcap_is1_actions,
> > +	},
> > +	[VCAP_IS2] = {
> > +		.action_type_width = 1,
> > +		.action_table = {
> > +			[IS2_ACTION_TYPE_NORMAL] = {
> > +				.width = 49,
> > +				.count = 2,
> > +			},
> > +			[IS2_ACTION_TYPE_SMAC_SIP] = {
> > +				.width = 6,
> > +				.count = 4,
> > +			},
> > +		},
> > +		.target = S2,
> > +		.keys = vsc7512_vcap_is2_keys,
> > +		.actions = vsc7512_vcap_is2_actions,
> > +	},
> > +};
> > +
> > +static struct regmap *vsc7512_regmap_init(struct ocelot *ocelot,
> > +					  struct resource *res, u32 *offset)
> > +{
> > +	struct felix *felix = ocelot_to_felix(ocelot);
> > +	struct felix_spi_data *felix_spi = container_of(felix, struct
> > +			felix_spi_data, felix);
> > +
> > +	// Use offset instead of res, since we don't have MMIO for SPI
> > +	*offset = res->start;
> > +	return felix_spi->map;
> > +}
> > +
> > +static const struct felix_info felix_spi_info = {
> > +	.target_io_res =		vsc7512_target_io_res,
> > +	.port_io_res =			vsc7512_port_io_res,
> > +	.imdio_res =			NULL,
> > +	.regfields =			vsc7512_regfields,
> > +	.map =				vsc7512_regmap,
> > +	.ops =				&vsc7512_ops,
> > +	.stats_layout =			vsc7512_stats_layout,
> > +	.num_stats =			ARRAY_SIZE(vsc7512_stats_layout),
> > +
> > +	.vcap =				vsc7512_vcap_props,
> > +
> > +	// Not sure about these
> > +	.num_mact_rows =		2048,
> 
> Chapter 3.9.1. MAC table
> The analyzer contains a MAC table with 4096 entries containing
> information about stations learned by the device. The table is organized
> as a hash table with four buckets and 1024 rows. Each row is indexed by
> an 10-bit hash value, which is calculated based on the station’s (MAC,
> VID) pair, as shown in the following illustration.
> 
> So... 1024, not 2048.

Thank you.

> 
> > +	.num_ports =			6,
> 
> Confused, Microchip says it's a 10 port switch:
> https://www.microchip.com/wwwproducts/en/VSC7512

Yes, a copy issue from the 9959 driver.

> 
> > +	.num_tx_queues =		OCELOT_NUM_TC,
> > +	.switch_pci_bar =		0,
> > +	.imdio_pci_bar =		0,
> 
> In hindsight, these have nothing to do in struct felix_info. And they
> are only used in felix_pci_probe for vsc9959. Could you please send
> one more patch, before this one but part of the same series, removing
> switch_pci_bar and imdio_pci_bar from struct felix_info, and replacing
> their use with some constants VSC9959_SWITCH_PCI_BAR, VSC9959_IMDIO_PCI_BAR?
> These represent the organization of hardware blocks within PCIe PF
> 0000:00:00.5 (the switch) on NXP LS1028A.

Thank you. I will gladly do this.

> 
> > +
> > +	// Force ocelot->ptp to 0
> > +	.ptp_caps =			NULL,
> 
> Comment is weird, this is not forcing anything to anything.
> 
> > +
> > +	// No need for this?
> > +	.mdio_bus_alloc =		NULL,
> > +	.mdio_bus_free =		NULL,
> 
> Yes, no need for this => just delete the NULL pointer initializers.
> 
> > +	.phylink_validate =		vsc7512_phylink_validate,
> > +	.prevalidate_phy_mode =		vsc7512_prevalidate_phy_mode,
> > +	.port_setup_tc =		vsc7512_port_setup_tc,
> > +	.port_sched_speed_set =		vsc7512_sched_speed_set,
> > +	.init_regmap =			vsc7512_regmap_init,
> 
> Please align the "=" character too, like this:

Yes, I'll work to be more consistent.

> 
> 	.prevalidate_phy_mode		= vsc7512_prevalidate_phy_mode,
> 
> > +};
> > +
> > +static int felix_spi_probe(struct spi_device *spi)
> > +{
> > +	struct dsa_switch *ds;
> > +	struct ocelot *ocelot;
> > +	struct felix *felix;
> > +	int err;
> > +	struct felix_spi_data *felix_spi;
> 
> As part of the networking coding style, please keep variable declaration
> in decreasing order of line length.

I must've missed this convention. Thank you.

> 
> > +
> > +	pr_info("felix_spi: CS: %d M: %d, BPW: %d, MaxSpeed: %d\n",
> > +		spi->chip_select, spi->mode, spi->bits_per_word,
> > +		spi->max_speed_hz);
> 
> I find it weird and confusing that you print the bits_per_word and
> max_speed_hz and then you change them immediately afterwards. What's the
> user going to think?
> 
> Also, please don't hardcode the SPIfrequency in the driver, DT exists
> for that.

More discussion from above. The bus operating frequency and the
communication protocol must be coordinated (Figures 61-63 of the VSC7512
datasheet, Rev 4.2)

This first implementation forced 500KHz in order to ensure we'd be in
the "SI_CLK Slow" mode. I believe this was necessary for regmap_init_spi
to have a fighting chance to work. Address shifting and conditional
padding bytes / delays appear to be outside the realm of the standard
regmap_spi.

Version 2 of the RFC patch will have custom read / wrtie addressing, and
should therefore be able to support padding bytes on reads. This, in
turn, should support any bus frequency up to the maximum supported
frequency by the chip itself.

> 
> > +	felix_spi = devm_kzalloc(&spi->dev, sizeof(struct felix_spi_data),
> > +				 GFP_KERNEL);
> > +
> > +	if (!felix_spi)
> > +		return -ENOMEM;
> > +
> > +	dev_set_drvdata(&spi->dev, felix_spi);
> > +
> > +	spi->bits_per_word = 8;
> > +
> > +	/* TODO: There are a couple of goals to achieve. Step 1: Get the device
> > +	 * working in slow SPI mode with a fixed bit and byte order. Fixing this
> > +	 * should allow devm_regmap_init_spi to be used directly, though there
> > +	 * are penalties incurred. Specifically the bus is running much slower
> > +	 * than it could, hindering performance.
> > +	 *
> > +	 * Operating at a faster SPI rate could boost performance significantly
> > +	 * with fixed delays or padding bytes. Especially on consecutive reads.
> > +	 *
> > +	 * Ideally the SPI bus would be configured to run at the configured
> > +	 * speed / fastest speed possible with consecutive reads / writes
> > +	 * enabled, if supported. For reads, we would have to reason about the
> > +	 * speed vs the delay time or the number of padding bytes.
> > +	 */
> > +
> > +	spi->max_speed_hz = 500000;
> > +	err = spi_setup(spi);
> > +	if (err < 0) {
> > +		dev_err(&spi->dev, "Error %d initializing SPI\n", err);
> > +		return err;
> > +	}
> > +
> > +	felix_spi->map = devm_regmap_init_spi(spi, &felix_spi_regmap_config);
> > +
> > +	if (IS_ERR(felix_spi->map)) {
> > +		dev_err(&spi->dev, "regmap init failed\n");
> > +		return PTR_ERR(felix_spi->map);
> > +	}
> > +
> > +	err = felix_spi_init_bus(spi, felix_spi);
> > +	if (err < 0) {
> > +		dev_err(&spi->dev, "device init failed: %d\n", err);
> > +		return err;
> > +	}
> > +
> > +	felix = &felix_spi->felix;
> > +
> > +	ocelot = &felix->ocelot;
> > +	ocelot->dev = &spi->dev;
> > +
> > +	// Not sure about this
> > +	ocelot->num_flooding_pgids = OCELOT_NUM_TC;
> 
> In general when you're not sure, use git blame and see why the property
> was added and why. In this case, see commit 3c7b51bd39b2 ("net: dsa:
> felix: allow flooding for all traffic classes") and then edd2410b165e
> ("net: mscc: ocelot: fix dropping of unknown IPv4 multicast on Seville").
> In this case, num_flooding_pgids should most likely be 1.

Thank you.

> 
> > +
> > +	felix->info = &felix_spi_info;
> > +
> > +	ocelot->ptp = 0;
> 
> ocelot, as a member structure of felix, is zero-initialized because it
> is allocated with kzalloc. So this doesn't do anything.
> 
> > +
> > +	ds = kzalloc(sizeof(*ds), GFP_KERNEL);
> > +	if (!ds) {
> > +		err = -ENOMEM;
> > +		dev_err(&spi->dev, "Failed to allocate DSA switch\n");
> > +		goto err_alloc_ds;
> > +	}
> > +
> > +	ds->dev = &spi->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;
> > +
> > +	err = dsa_register_switch(ds);
> > +	if (err) {
> > +		dev_err(&spi->dev, "Failed to register DSA switch: %d\n", err);
> > +		goto err_register_ds;
> > +	}
> > +
> > +	return 0;
> > +
> > +err_register_ds:
> > +	kfree(ds);
> > +err_alloc_ds:
> > +	kfree(felix);
> > +	return err;
> > +}
> > +
> > +static int felix_spi_remove(struct spi_device *pdev)
> > +{
> > +	struct felix *felix;
> > +
> > +	felix = spi_get_drvdata(pdev);
> > +
> > +	return 0;
> > +}
> 
> You got bored of writing code here? :)

A bad habit of mine. I'm trying to test the code as I'm writing it.
Communication doesn't work, so I can't get past probe. I'm excited for
the day I can see this memory leak in action.

I'll repair this for RFC V2, regardless of whether I can test it.

> 
> > +
> > +const struct of_device_id vsc7512_of_match[] = { { .compatible =
> > +							   "mscc,vsc7512" },
> > +						 {} };
> > +MODULE_DEVICE_TABLE(of, vsc7512_of_match);
> 
> Indentation is very odd.
> 
> > +
> > +static struct spi_driver felix_vsc7512_spi_driver = {
> > +	.driver = {
> > +			.name = "vsc7512",
> > +			.of_match_table = of_match_ptr(vsc7512_of_match),
> > +		},
> > +	.probe = felix_spi_probe,
> > +	.remove = felix_spi_remove,
> > +};
> > +module_spi_driver(felix_vsc7512_spi_driver);
> > +
> > +MODULE_DESCRIPTION("Felix Switch SPI driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index ad45c1af4be9..8b6e0574f294 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -127,6 +127,7 @@ enum ocelot_target {
> >  	PTP,
> >  	GCB,
> >  	DEV_GMII,
> > +	DEV_CPUORG,
> >  	TARGET_MAX,
> >  };
> >  
> > @@ -444,6 +445,20 @@ enum ocelot_reg {
> >  	PCS1G_TSTPAT_STATUS,
> >  	DEV_PCS_FX100_CFG,
> >  	DEV_PCS_FX100_STATUS,
> > +	DEV_CPUORG_IF_CTRL = DEV_CPUORG << TARGET_OFFSET,
> > +	DEV_CPUORG_IF_CFGSTAT,
> > +	DEV_CPUORG_ORG_CFG,
> > +	DEV_CPUORG_ERR_CNTS,
> > +	DEV_CPUORG_TIMEOUT_CFG,
> > +	DEV_CPUORG_GPR,
> > +	DEV_CPUORG_MAILBOX_SET,
> > +	DEV_CPUORG_MAILBOX_CLR,
> > +	DEV_CPUORG_MAILBOX,
> > +	DEV_CPUORG_SEMA_CFG,
> > +	DEV_CPUORG_SEMA0,
> > +	DEV_CPUORG_SEMA0_OWNER,
> > +	DEV_CPUORG_SEMA1,
> > +	DEV_CPUORG_SEMA1_OWNER,
> >  };
> >  
> >  enum ocelot_regfield {
> > -- 
> > 2.25.1
> >
Vladimir Oltean May 7, 2021, 8:47 a.m. UTC | #5
On Thu, May 06, 2021 at 11:34:18AM -0700, Colin Foster wrote:
> Vladimir, Thank you very much for the feedback. I appreciate your time
> and your patience when dealing with my many blunders. This is a large
> learning experience for me.

You haven't seen mine...

> On Thu, May 06, 2021 at 01:22:04PM +0300, Vladimir Oltean wrote:
> > On Mon, May 03, 2021 at 10:11:27PM -0700, Colin Foster wrote:
> > > Add support for control for VSC75XX chips over SPI control. Starting with the
> > > VSC9959 code, this will utilize a spi bus instead of PCIe or memory-mapped IO to
> > > control the chip.
> > > 
> > > Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
> > > ---
> > >  arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts |  124 ++
> > >  drivers/net/dsa/ocelot/Kconfig                |   11 +
> > >  drivers/net/dsa/ocelot/Makefile               |    5 +
> > >  drivers/net/dsa/ocelot/felix_vsc7512_spi.c    | 1214 +++++++++++++++++
> > >  include/soc/mscc/ocelot.h                     |   15 +
> > >  5 files changed, 1369 insertions(+)
> > >  create mode 100644 arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts
> > >  create mode 100644 drivers/net/dsa/ocelot/felix_vsc7512_spi.c
> > > 
> > > diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
> > > index 932b6b6fe817..2db147ce9fe7 100644
> > > --- a/drivers/net/dsa/ocelot/Kconfig
> > > +++ b/drivers/net/dsa/ocelot/Kconfig
> > > @@ -14,6 +14,17 @@ config NET_DSA_MSCC_FELIX
> > >  	  This driver supports the VSC9959 (Felix) switch, which is embedded as
> > >  	  a PCIe function of the NXP LS1028A ENETC RCiEP.
> > >  
> > > +config NET_DSA_MSCC_FELIX_SPI
> > > +	tristate "Ocelot / Felix Ethernet SPI switch support"
> > > +	depends on NET_DSA && SPI
> > > +	depends on NET_VENDOR_MICROSEMI
> > > +	select MSCC_OCELOT_SWITCH_LIB
> > > +	select NET_DSA_TAG_OCELOT_8021Q
> > > +	select NET_DSA_TAG_OCELOT
> > > +	select PCS_LYNX
> > 
> > You most probably don't need to select PCS_LYNX (that's an NXP thing).
> > For that reason, you might want to call your module NET_DSA_MSCC_OCELOT_SPI.
> > The "felix" name is just the name of the common DSA driver and of the
> > VSC9959 chip. VSC7512 is probably called Ocelot-1 according to Microchip
> > marketing.
> 
> Thank you. I couldn't find where "Felix" and "Seville" actually came
> from. My next RFC submission will include these changes.

felix = code name for the VSC9959 from NXP LS1028A, derivative of Ocelot-1
seville = code name for the VSC9953 from NXP T1040, derivative of Serval-1

The common DSA driver layer is called felix because felix was the first
switch it supported. But since probing is now handled individually and
the PCIe probing of felix is done in a different driver compared to the
platform device probing of seville, you can name your VSC7512 driver in
any way you feel appropriate.

> > I'm surprised that the reset procedure for VSC7512 is different than
> > what is implemented in ocelot_reset() for VSC7514.
> 
> I will look more into this and repair as needed. I'm also considering
> the ability to use a GPIO as an external reset, which might negate any
> reset procedure the MIPS would be doing.

I think some memories need to be initialized too, not sure if you can
achieve that with a reset pin only.

> > You must be working on an old kernel or something. There's also a
> > watermark decode function which you can reuse from ocelot (ocelot_wm_dec).
> 
> Yes, my proof-of-concept was from a 5.10 kernel. I brought it up to
> mainline before submitting the patch and must have missed this. Per the
> documentation, I'll base my next submission entirely on the latest 5.12
> release.

What you actually mean when you set the patch subject prefix to
"PATCH net-next" is that your patches can be applied to the current
master branch of this tree, not on any release tree:
https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/

The official kernel tag v5.12 has just been released, and kernel v5.13
entered release candidate status (starting with rc1 and ending with rc7
or rc8, depending on how many fixes it gets). The development process
happens simultaneously with the release candidates for the official
v5.13 release, but new development is done for v5.14 only (that's why it
is called "next"). When Linus decides that the v5.13 release candidates
are mature enough to print the v5.13 final release tag, development for
v5.14 stops too, the maintainers stop accepting development patches for
a little while, everybody prepares a pull request, there is a "memory
barrier" until the official v5.14 rc1 is released which contains all
pull requests with new features from the maintainers' development trees,
and then these "next" trees reopen for development that will be included
in v5.15.

This is a bit oversimplified, but hopefully it should give you an idea
why sometimes you are told to rebase on a different tree, or why you
can't send patches right now, etc.

> > > +static int felix_spi_remove(struct spi_device *pdev)
> > > +{
> > > +	struct felix *felix;
> > > +
> > > +	felix = spi_get_drvdata(pdev);
> > > +
> > > +	return 0;
> > > +}
> > 
> > You got bored of writing code here? :)
> 
> A bad habit of mine. I'm trying to test the code as I'm writing it.
> Communication doesn't work, so I can't get past probe. I'm excited for
> the day I can see this memory leak in action.
> 
> I'll repair this for RFC V2, regardless of whether I can test it.

By testing, you mean this?

echo spiN.M > /sys/bus/spi/drivers/vsc7512/unbind
echo spiN.M > /sys/bus/spi/drivers/vsc7512/bind

(where of course, N is the bus number and M is the chip select)
Colin Foster May 8, 2021, 10:26 p.m. UTC | #6
On Thu, May 06, 2021 at 01:22:04PM +0300, Vladimir Oltean wrote:
> On Mon, May 03, 2021 at 10:11:27PM -0700, Colin Foster wrote:
> > +static const u32 vsc7512_dev_gmii_regmap[] = {
> > +	REG(DEV_CLOCK_CFG,			0x0),
> > +	REG(DEV_PORT_MISC,			0x1),
> > +	REG(DEV_EEE_CFG,			0x3),
> > +	REG(DEV_RX_PATH_DELAY,			0x4),
> > +	REG(DEV_TX_PATH_DELAY,			0x5),
> > +	REG(DEV_PTP_PREDICT_CFG,		0x6),
> > +	REG(DEV_MAC_ENA_CFG,			0x7),
> > +	REG(DEV_MAC_MODE_CFG,			0x8),
> > +	REG(DEV_MAC_MAXLEN_CFG,			0x9),
> > +	REG(DEV_MAC_TAGS_CFG,			0xa),
> > +	REG(DEV_MAC_ADV_CHK_CFG,		0xb),
> > +	REG(DEV_MAC_IFG_CFG,			0xc),
> > +	REG(DEV_MAC_HDX_CFG,			0xd),
> > +	REG(DEV_MAC_DBG_CFG,			0xe),
> > +	REG(DEV_MAC_FC_MAC_LOW_CFG,		0xf),
> > +	REG(DEV_MAC_FC_MAC_HIGH_CFG,		0x10),
> > +	REG(DEV_MAC_STICKY,			0x11),
> > +	REG_RESERVED(PCS1G_CFG),
> > +	REG_RESERVED(PCS1G_MODE_CFG),
> > +	REG_RESERVED(PCS1G_SD_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_NP_CFG),
> > +	REG_RESERVED(PCS1G_LB_CFG),
> > +	REG_RESERVED(PCS1G_DBG_CFG),
> > +	REG_RESERVED(PCS1G_CDET_CFG),
> > +	REG_RESERVED(PCS1G_ANEG_STATUS),
> > +	REG_RESERVED(PCS1G_ANEG_NP_STATUS),
> > +	REG_RESERVED(PCS1G_LINK_STATUS),
> > +	REG_RESERVED(PCS1G_LINK_DOWN_CNT),
> > +	REG_RESERVED(PCS1G_STICKY),
> > +	REG_RESERVED(PCS1G_DEBUG_STATUS),
> > +	REG_RESERVED(PCS1G_LPI_CFG),
> > +	REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT),
> > +	REG_RESERVED(PCS1G_LPI_STATUS),
> > +	REG_RESERVED(PCS1G_TSTPAT_MODE_CFG),
> > +	REG_RESERVED(PCS1G_TSTPAT_STATUS),
> > +	REG_RESERVED(DEV_PCS_FX100_CFG),
> > +	REG_RESERVED(DEV_PCS_FX100_STATUS),
> > +};
> > +
> > +static const u32 vsc7512_cpu_org_regmap[] = {
> > +	REG(DEV_CPUORG_IF_CTRL,			0x0000),
> > +	REG(DEV_CPUORG_IF_CFGSTAT,		0x0001),
> > +	REG(DEV_CPUORG_ORG_CFG,			0x0002),
> > +	REG(DEV_CPUORG_ERR_CNTS,		0x0003),
> > +	REG(DEV_CPUORG_TIMEOUT_CFG,		0x0004),
> > +	REG(DEV_CPUORG_GPR,			0x0005),
> > +	REG(DEV_CPUORG_MAILBOX_SET,		0x0006),
> > +	REG(DEV_CPUORG_MAILBOX_CLR,		0x0007),
> > +	REG(DEV_CPUORG_MAILBOX,			0x0008),
> > +	REG(DEV_CPUORG_SEMA_CFG,		0x0009),
> > +	REG(DEV_CPUORG_SEMA0,			0x000a),
> > +	REG(DEV_CPUORG_SEMA0_OWNER,		0x000b),
> > +	REG(DEV_CPUORG_SEMA1,			0x000c),
> > +	REG(DEV_CPUORG_SEMA1_OWNER,		0x000d),
> > +};
> 
> I know I changed my mind, but if the regmap is the same as for
> drivers/net/ethernet/mscc/ocelot_vsc7514.c, just divided by 4, then it
> makes a lot more sense to implement the custom regmap accessors and
> reuse the regmaps from there.

I moved these to a new file, ocelot_regs.c, to be built with the ocelot
library and referenced by both the ocelot and this new "ocelot_spi".

> > +static u16 vsc7512_wm_enc(u16 value)
> > +{
> > +	WARN_ON(value >= 16 * BIT(8));
> > +
> > +	if (value >= BIT(8))
> > +		return BIT(8) | (value / 16);
> > +
> > +	return value;
> > +}
> 
> Don't duplicate, just EXPORT_SYMBOL(ocelot_wm_enc) and call here.

As above, I moved this to a new file ocelot_wm.c as part of the library,
with the prototypes in ocelot.h. Including wm_dec and wm_stats.
Otherwise these functions are only built as the vsc7514 driver.

> 
> > +static const struct ocelot_ops vsc7512_ops = {
> > +	.reset		= vsc7512_reset,
> > +	.wm_enc		= vsc7512_wm_enc,
> 
> You must be working on an old kernel or something. There's also a
> watermark decode function which you can reuse from ocelot (ocelot_wm_dec).
> 
> > +	.port_to_netdev	= felix_port_to_netdev,
> > +	.netdev_to_port	= felix_netdev_to_port,
> > +};
> > +
> > +/* Addresses are relative to the SPI device's base address, downshifted by 2*/
> > +static const struct resource vsc7512_target_io_res[TARGET_MAX] = {
> > +	[ANA] = {
> > +		.start	= 0x1c620000,
> > +		.end	= 0x1c623fff,
> > +		.name	= "ana",
> > +	},
> > +	[QS] = {
> > +		.start	= 0x1c420000,
> > +		.end	= 0x1c42003f,
> > +		.name	= "qs",
> > +	},
> > +	[QSYS] = {
> > +		.start	= 0x1c600000,
> > +		.end	= 0x1c607fff,
> > +		.name	= "qsys",
> > +	},
> > +	[REW] = {
> > +		.start	= 0x1c40c000,
> > +		.end	= 0x1c40ffff,
> > +		.name	= "rew",
> > +	},
> > +	[SYS] = {
> > +		.start	= 0x1c404000,
> > +		.end	= 0x1c407fff,
> > +		.name	= "sys",
> > +	},
> > +	[S0] = {
> > +		.start	= 0x1c410000,
> > +		.end	= 0x1c4100ff,
> > +		.name	= "s0",
> > +	},
> > +	[S1] = {
> > +		.start	= 0x1c414000,
> > +		.end	= 0x1c4140ff,
> > +		.name	= "s1",
> > +	},
> > +	[S2] = {
> > +		.start	= 0x1c418000,
> > +		.end	= 0x1c4180ff,
> > +		.name	= "s2",
> > +	},
> > +	[GCB] =	{
> > +		.start	= 0x1c41c000,
> > +		.end	= 0x1c41c07f,
> > +		.name	= "devcpu_gcb",
> > +	},
> > +	[DEV_CPUORG] = {
> > +		.start	= 0x1c400000,
> > +		.end	= 0x1c4000ff,
> > +		.name	= "devcpu_org",
> > +	},
> > +};
> > +
> > +static const struct resource vsc7512_port_io_res[] = {
> > +	{
> > +		.start	= 0x1c478000,
> > +		.end	= 0x1c47bfff,
> > +		.name	= "port0",
> > +	},
> > +	{
> > +		.start	= 0x1c47c000,
> > +		.end	= 0x1c47ffff,
> > +		.name	= "port1",
> > +	},
> > +	{
> > +		.start	= 0x1c480000,
> > +		.end	= 0x1c483fff,
> > +		.name	= "port2",
> > +	},
> > +	{
> > +		.start	= 0x1c484000,
> > +		.end	= 0x1c487fff,
> > +		.name	= "port3",
> > +	},
> > +	{
> > +		.start	= 0x1c488000,
> > +		.end	= 0x1c48bfff,
> > +		.name	= "port4",
> > +	},
> > +	{
> > +		.start	= 0x1c48c000,
> > +		.end	= 0x1c48ffff,
> > +		.name	= "port5",
> > +	},
> > +};
> > +
> > +static const struct reg_field vsc7512_regfields[REGFIELD_MAX] = {
> > +	[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
> > +	[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
> > +	[ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
> > +	[ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
> > +	[ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
> > +	[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
> > +	[ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
> > +	[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
> > +	[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
> > +	[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
> > +	[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
> > +	[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
> > +	[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
> > +	[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
> > +	[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
> > +	[ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
> > +	[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
> > +	[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
> > +	[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
> > +	[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
> > +	[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
> > +	[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
> > +	[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
> > +	[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
> > +	[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
> > +	[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
> > +	[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
> > +	[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
> > +	[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
> > +	[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
> > +	[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
> > +	[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
> > +	[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
> > +	[GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
> > +	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
> > +	[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
> > +	[SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
> > +	[SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
> > +	/* Replicated per number of ports (12), register size 4 per port */
> > +	[QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 12, 4),
> > +	[QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 12, 4),
> > +	[SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 12, 4),
> > +	[SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 12, 4),
> > +	[SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 12, 4),
> > +	[SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 12, 4),
> > +	[SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 12, 4),
> > +};

Move this into the newly-created ocelot_regs.c file?

> > +
> > +static const struct ocelot_stat_layout vsc7512_stats_layout[] = {
> > +	{ .offset = 0x00,	.name = "rx_octets", },
> > +	{ .offset = 0x01,	.name = "rx_unicast", },
> > +	{ .offset = 0x02,	.name = "rx_multicast", },
> > +	{ .offset = 0x03,	.name = "rx_broadcast", },
> > +	{ .offset = 0x04,	.name = "rx_shorts", },
> > +	{ .offset = 0x05,	.name = "rx_fragments", },
> > +	{ .offset = 0x06,	.name = "rx_jabbers", },
> > +	{ .offset = 0x07,	.name = "rx_crc_align_errs", },
> > +	{ .offset = 0x08,	.name = "rx_sym_errs", },
> > +	{ .offset = 0x09,	.name = "rx_frames_below_65_octets", },
> > +	{ .offset = 0x0A,	.name = "rx_frames_65_to_127_octets", },
> > +	{ .offset = 0x0B,	.name = "rx_frames_128_to_255_octets", },
> > +	{ .offset = 0x0C,	.name = "rx_frames_256_to_511_octets", },
> > +	{ .offset = 0x0D,	.name = "rx_frames_512_to_1023_octets", },
> > +	{ .offset = 0x0E,	.name = "rx_frames_1024_to_1526_octets", },
> > +	{ .offset = 0x0F,	.name = "rx_frames_over_1526_octets", },
> > +	{ .offset = 0x10,	.name = "rx_pause", },
> > +	{ .offset = 0x11,	.name = "rx_control", },
> > +	{ .offset = 0x12,	.name = "rx_longs", },
> > +	{ .offset = 0x13,	.name = "rx_classified_drops", },
> > +	{ .offset = 0x14,	.name = "rx_red_prio_0", },
> > +	{ .offset = 0x15,	.name = "rx_red_prio_1", },
> > +	{ .offset = 0x16,	.name = "rx_red_prio_2", },
> > +	{ .offset = 0x17,	.name = "rx_red_prio_3", },
> > +	{ .offset = 0x18,	.name = "rx_red_prio_4", },
> > +	{ .offset = 0x19,	.name = "rx_red_prio_5", },
> > +	{ .offset = 0x1A,	.name = "rx_red_prio_6", },
> > +	{ .offset = 0x1B,	.name = "rx_red_prio_7", },
> > +	{ .offset = 0x1C,	.name = "rx_yellow_prio_0", },
> > +	{ .offset = 0x1D,	.name = "rx_yellow_prio_1", },
> > +	{ .offset = 0x1E,	.name = "rx_yellow_prio_2", },
> > +	{ .offset = 0x1F,	.name = "rx_yellow_prio_3", },
> > +	{ .offset = 0x20,	.name = "rx_yellow_prio_4", },
> > +	{ .offset = 0x21,	.name = "rx_yellow_prio_5", },
> > +	{ .offset = 0x22,	.name = "rx_yellow_prio_6", },
> > +	{ .offset = 0x23,	.name = "rx_yellow_prio_7", },
> > +	{ .offset = 0x24,	.name = "rx_green_prio_0", },
> > +	{ .offset = 0x25,	.name = "rx_green_prio_1", },
> > +	{ .offset = 0x26,	.name = "rx_green_prio_2", },
> > +	{ .offset = 0x27,	.name = "rx_green_prio_3", },
> > +	{ .offset = 0x28,	.name = "rx_green_prio_4", },
> > +	{ .offset = 0x29,	.name = "rx_green_prio_5", },
> > +	{ .offset = 0x2A,	.name = "rx_green_prio_6", },
> > +	{ .offset = 0x2B,	.name = "rx_green_prio_7", },
> > +	{ .offset = 0x40,	.name = "tx_octets", },
> > +	{ .offset = 0x41,	.name = "tx_unicast", },
> > +	{ .offset = 0x42,	.name = "tx_multicast", },
> > +	{ .offset = 0x43,	.name = "tx_broadcast", },
> > +	{ .offset = 0x44,	.name = "tx_collision", },
> > +	{ .offset = 0x45,	.name = "tx_drops", },
> > +	{ .offset = 0x46,	.name = "tx_pause", },
> > +	{ .offset = 0x47,	.name = "tx_frames_below_65_octets", },
> > +	{ .offset = 0x48,	.name = "tx_frames_65_to_127_octets", },
> > +	{ .offset = 0x49,	.name = "tx_frames_128_255_octets", },
> > +	{ .offset = 0x4A,	.name = "tx_frames_256_511_octets", },
> > +	{ .offset = 0x4B,	.name = "tx_frames_512_1023_octets", },
> > +	{ .offset = 0x4C,	.name = "tx_frames_1024_1526_octets", },
> > +	{ .offset = 0x4D,	.name = "tx_frames_over_1526_octets", },
> > +	{ .offset = 0x4E,	.name = "tx_yellow_prio_0", },
> > +	{ .offset = 0x4F,	.name = "tx_yellow_prio_1", },
> > +	{ .offset = 0x50,	.name = "tx_yellow_prio_2", },
> > +	{ .offset = 0x51,	.name = "tx_yellow_prio_3", },
> > +	{ .offset = 0x52,	.name = "tx_yellow_prio_4", },
> > +	{ .offset = 0x53,	.name = "tx_yellow_prio_5", },
> > +	{ .offset = 0x54,	.name = "tx_yellow_prio_6", },
> > +	{ .offset = 0x55,	.name = "tx_yellow_prio_7", },
> > +	{ .offset = 0x56,	.name = "tx_green_prio_0", },
> > +	{ .offset = 0x57,	.name = "tx_green_prio_1", },
> > +	{ .offset = 0x58,	.name = "tx_green_prio_2", },
> > +	{ .offset = 0x59,	.name = "tx_green_prio_3", },
> > +	{ .offset = 0x5A,	.name = "tx_green_prio_4", },
> > +	{ .offset = 0x5B,	.name = "tx_green_prio_5", },
> > +	{ .offset = 0x5C,	.name = "tx_green_prio_6", },
> > +	{ .offset = 0x5D,	.name = "tx_green_prio_7", },
> > +	{ .offset = 0x5E,	.name = "tx_aged", },
> > +	{ .offset = 0x80,	.name = "drop_local", },
> > +	{ .offset = 0x81,	.name = "drop_tail", },
> > +	{ .offset = 0x82,	.name = "drop_yellow_prio_0", },
> > +	{ .offset = 0x83,	.name = "drop_yellow_prio_1", },
> > +	{ .offset = 0x84,	.name = "drop_yellow_prio_2", },
> > +	{ .offset = 0x85,	.name = "drop_yellow_prio_3", },
> > +	{ .offset = 0x86,	.name = "drop_yellow_prio_4", },
> > +	{ .offset = 0x87,	.name = "drop_yellow_prio_5", },
> > +	{ .offset = 0x88,	.name = "drop_yellow_prio_6", },
> > +	{ .offset = 0x89,	.name = "drop_yellow_prio_7", },
> > +	{ .offset = 0x8A,	.name = "drop_green_prio_0", },
> > +	{ .offset = 0x8B,	.name = "drop_green_prio_1", },
> > +	{ .offset = 0x8C,	.name = "drop_green_prio_2", },
> > +	{ .offset = 0x8D,	.name = "drop_green_prio_3", },
> > +	{ .offset = 0x8E,	.name = "drop_green_prio_4", },
> > +	{ .offset = 0x8F,	.name = "drop_green_prio_5", },
> > +	{ .offset = 0x90,	.name = "drop_green_prio_6", },
> > +	{ .offset = 0x91,	.name = "drop_green_prio_7", },
> > +};

Share this in a new ocelot_stats.c file? Just want to check that it
makes sense to create all these smaller files that just contain tables
before going ahead with it.

> > +
> > +static const struct vcap_field vsc7512_vcap_es0_actions[]   = {
> > +	[VCAP_ES0_ACT_PUSH_OUTER_TAG]           = { 0,   2 },
> > +	[VCAP_ES0_ACT_PUSH_INNER_TAG]           = { 2,   1 },
> > +	[VCAP_ES0_ACT_TAG_A_TPID_SEL]           = { 3,   2 },
> > +	[VCAP_ES0_ACT_TAG_A_VID_SEL]            = { 5,   1 },
> > +	[VCAP_ES0_ACT_TAG_A_PCP_SEL]            = { 6,   2 },
> > +	[VCAP_ES0_ACT_TAG_A_DEI_SEL]            = { 8,   2 },
> > +	[VCAP_ES0_ACT_TAG_B_TPID_SEL]           = { 10,  2 },
> > +	[VCAP_ES0_ACT_TAG_B_VID_SEL]            = { 12,  1 },
> > +	[VCAP_ES0_ACT_TAG_B_PCP_SEL]            = { 13,  2 },
> > +	[VCAP_ES0_ACT_TAG_B_DEI_SEL]            = { 15,  2 },
> > +	[VCAP_ES0_ACT_VID_A_VAL]                = { 17, 12 },
> > +	[VCAP_ES0_ACT_PCP_A_VAL]                = { 29,  3 },
> > +	[VCAP_ES0_ACT_DEI_A_VAL]                = { 32,  1 },
> > +	[VCAP_ES0_ACT_VID_B_VAL]                = { 33, 12 },
> > +	[VCAP_ES0_ACT_PCP_B_VAL]                = { 45,  3 },
> > +	[VCAP_ES0_ACT_DEI_B_VAL]                = { 48,  1 },
> > +	[VCAP_ES0_ACT_RSV]                      = { 49, 24 },
> > +	[VCAP_ES0_ACT_HIT_STICKY]               = { 73,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is1_keys[] = {
> > +	[VCAP_IS1_HK_TYPE]                      = { 0,    1 },
> > +	[VCAP_IS1_HK_LOOKUP]                    = { 1,    2 },
> > +	[VCAP_IS1_HK_IGR_PORT_MASK]             = { 3,   12 },
> > +	[VCAP_IS1_HK_RSV]                       = { 15,   9 },
> > +	[VCAP_IS1_HK_OAM_Y1731]                 = { 24,   1 },
> > +	[VCAP_IS1_HK_L2_MC]                     = { 25,   1 },
> > +	[VCAP_IS1_HK_L2_BC]                     = { 26,   1 },
> > +	[VCAP_IS1_HK_IP_MC]                     = { 27,   1 },
> > +	[VCAP_IS1_HK_VLAN_TAGGED]               = { 28,   1 },
> > +	[VCAP_IS1_HK_VLAN_DBL_TAGGED]           = { 29,   1 },
> > +	[VCAP_IS1_HK_TPID]                      = { 30,   1 },
> > +	[VCAP_IS1_HK_VID]                       = { 31,  12 },
> > +	[VCAP_IS1_HK_DEI]                       = { 43,   1 },
> > +	[VCAP_IS1_HK_PCP]                       = { 44,   3 },
> > +	/* Specific Fields for IS1 Half Key S1_NORMAL */
> > +	[VCAP_IS1_HK_L2_SMAC]                   = { 47,  48 },
> > +	[VCAP_IS1_HK_ETYPE_LEN]                 = { 95,   1 },
> > +	[VCAP_IS1_HK_ETYPE]                     = { 96,  16 },
> > +	[VCAP_IS1_HK_IP_SNAP]                   = { 112,  1 },
> > +	[VCAP_IS1_HK_IP4]                       = { 113,  1 },
> > +	/* Layer-3 Information */
> > +	[VCAP_IS1_HK_L3_FRAGMENT]               = { 114,  1 },
> > +	[VCAP_IS1_HK_L3_FRAG_OFS_GT0]           = { 115,  1 },
> > +	[VCAP_IS1_HK_L3_OPTIONS]                = { 116,  1 },
> > +	[VCAP_IS1_HK_L3_DSCP]                   = { 117,  6 },
> > +	[VCAP_IS1_HK_L3_IP4_SIP]                = { 123, 32 },
> > +	/* Layer-4 Information */
> > +	[VCAP_IS1_HK_TCP_UDP]                   = { 155,  1 },
> > +	[VCAP_IS1_HK_TCP]                       = { 156,  1 },
> > +	[VCAP_IS1_HK_L4_SPORT]                  = { 157, 16 },
> > +	[VCAP_IS1_HK_L4_RNG]                    = { 173,  8 },
> > +	/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */
> > +	[VCAP_IS1_HK_IP4_INNER_TPID]            = { 47,   1 },
> > +	[VCAP_IS1_HK_IP4_INNER_VID]             = { 48,  12 },
> > +	[VCAP_IS1_HK_IP4_INNER_DEI]             = { 60,   1 },
> > +	[VCAP_IS1_HK_IP4_INNER_PCP]             = { 61,   3 },
> > +	[VCAP_IS1_HK_IP4_IP4]                   = { 64,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_FRAGMENT]           = { 65,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0]       = { 66,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_OPTIONS]            = { 67,   1 },
> > +	[VCAP_IS1_HK_IP4_L3_DSCP]               = { 68,   6 },
> > +	[VCAP_IS1_HK_IP4_L3_IP4_DIP]            = { 74,  32 },
> > +	[VCAP_IS1_HK_IP4_L3_IP4_SIP]            = { 106, 32 },
> > +	[VCAP_IS1_HK_IP4_L3_PROTO]              = { 138,  8 },
> > +	[VCAP_IS1_HK_IP4_TCP_UDP]               = { 146,  1 },
> > +	[VCAP_IS1_HK_IP4_TCP]                   = { 147,  1 },
> > +	[VCAP_IS1_HK_IP4_L4_RNG]                = { 148,  8 },
> > +	[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE]  = { 156, 32 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is1_actions[] = {
> > +	[VCAP_IS1_ACT_DSCP_ENA]                 = { 0,   1 },
> > +	[VCAP_IS1_ACT_DSCP_VAL]                 = { 1,   6 },
> > +	[VCAP_IS1_ACT_QOS_ENA]                  = { 7,   1 },
> > +	[VCAP_IS1_ACT_QOS_VAL]                  = { 8,   3 },
> > +	[VCAP_IS1_ACT_DP_ENA]                   = { 11,  1 },
> > +	[VCAP_IS1_ACT_DP_VAL]                   = { 12,  1 },
> > +	[VCAP_IS1_ACT_PAG_OVERRIDE_MASK]        = { 13,  8 },
> > +	[VCAP_IS1_ACT_PAG_VAL]                  = { 21,  8 },
> > +	[VCAP_IS1_ACT_RSV]                      = { 29,  9 },
> > +	/* The fields below are incorrectly shifted by 2 in the manual */
> > +	[VCAP_IS1_ACT_VID_REPLACE_ENA]          = { 38,  1 },
> > +	[VCAP_IS1_ACT_VID_ADD_VAL]              = { 39, 12 },
> > +	[VCAP_IS1_ACT_FID_SEL]                  = { 51,  2 },
> > +	[VCAP_IS1_ACT_FID_VAL]                  = { 53, 13 },
> > +	[VCAP_IS1_ACT_PCP_DEI_ENA]              = { 66,  1 },
> > +	[VCAP_IS1_ACT_PCP_VAL]                  = { 67,  3 },
> > +	[VCAP_IS1_ACT_DEI_VAL]                  = { 70,  1 },
> > +	[VCAP_IS1_ACT_VLAN_POP_CNT_ENA]         = { 71,  1 },
> > +	[VCAP_IS1_ACT_VLAN_POP_CNT]             = { 72,  2 },
> > +	[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA]      = { 74,  4 },
> > +	[VCAP_IS1_ACT_HIT_STICKY]               = { 78,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is2_keys[] = {
> > +	/* Common: 46 bits */
> > +	[VCAP_IS2_TYPE]                         = { 0,    4 },
> > +	[VCAP_IS2_HK_FIRST]                     = { 4,    1 },
> > +	[VCAP_IS2_HK_PAG]                       = { 5,    8 },
> > +	[VCAP_IS2_HK_IGR_PORT_MASK]             = { 13,  12 },
> > +	[VCAP_IS2_HK_RSV2]                      = { 25,   1 },
> > +	[VCAP_IS2_HK_HOST_MATCH]                = { 26,   1 },
> > +	[VCAP_IS2_HK_L2_MC]                     = { 27,   1 },
> > +	[VCAP_IS2_HK_L2_BC]                     = { 28,   1 },
> > +	[VCAP_IS2_HK_VLAN_TAGGED]               = { 29,   1 },
> > +	[VCAP_IS2_HK_VID]                       = { 30,  12 },
> > +	[VCAP_IS2_HK_DEI]                       = { 42,   1 },
> > +	[VCAP_IS2_HK_PCP]                       = { 43,   3 },
> > +	/* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */
> > +	[VCAP_IS2_HK_L2_DMAC]                   = { 46,  48 },
> > +	[VCAP_IS2_HK_L2_SMAC]                   = { 94,  48 },
> > +	/* MAC_ETYPE (TYPE=000) */
> > +	[VCAP_IS2_HK_MAC_ETYPE_ETYPE]           = { 142, 16 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0]     = { 158, 16 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1]     = { 174,  8 },
> > +	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2]     = { 182,  3 },
> > +	/* MAC_LLC (TYPE=001) */
> > +	[VCAP_IS2_HK_MAC_LLC_L2_LLC]            = { 142, 40 },
> > +	/* MAC_SNAP (TYPE=010) */
> > +	[VCAP_IS2_HK_MAC_SNAP_L2_SNAP]          = { 142, 40 },
> > +	/* MAC_ARP (TYPE=011) */
> > +	[VCAP_IS2_HK_MAC_ARP_SMAC]              = { 46,  48 },
> > +	[VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK]     = { 94,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK]    = { 95,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_LEN_OK]            = { 96,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_TARGET_MATCH]      = { 97,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_SENDER_MATCH]      = { 98,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN]    = { 99,   1 },
> > +	[VCAP_IS2_HK_MAC_ARP_OPCODE]            = { 100,  2 },
> > +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP]        = { 102, 32 },
> > +	[VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP]        = { 134, 32 },
> > +	[VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP]        = { 166,  1 },
> > +	/* IP4_TCP_UDP / IP4_OTHER common */
> > +	[VCAP_IS2_HK_IP4]                       = { 46,   1 },
> > +	[VCAP_IS2_HK_L3_FRAGMENT]               = { 47,   1 },
> > +	[VCAP_IS2_HK_L3_FRAG_OFS_GT0]           = { 48,   1 },
> > +	[VCAP_IS2_HK_L3_OPTIONS]                = { 49,   1 },
> > +	[VCAP_IS2_HK_IP4_L3_TTL_GT0]            = { 50,   1 },
> > +	[VCAP_IS2_HK_L3_TOS]                    = { 51,   8 },
> > +	[VCAP_IS2_HK_L3_IP4_DIP]                = { 59,  32 },
> > +	[VCAP_IS2_HK_L3_IP4_SIP]                = { 91,  32 },
> > +	[VCAP_IS2_HK_DIP_EQ_SIP]                = { 123,  1 },
> > +	/* IP4_TCP_UDP (TYPE=100) */
> > +	[VCAP_IS2_HK_TCP]                       = { 124,  1 },
> > +	[VCAP_IS2_HK_L4_DPORT]                  = { 125, 16 },
> > +	[VCAP_IS2_HK_L4_SPORT]                  = { 141, 16 },
> > +	[VCAP_IS2_HK_L4_RNG]                    = { 157,  8 },
> > +	[VCAP_IS2_HK_L4_SPORT_EQ_DPORT]         = { 165,  1 },
> > +	[VCAP_IS2_HK_L4_SEQUENCE_EQ0]           = { 166,  1 },
> > +	[VCAP_IS2_HK_L4_FIN]                    = { 167,  1 },
> > +	[VCAP_IS2_HK_L4_SYN]                    = { 168,  1 },
> > +	[VCAP_IS2_HK_L4_RST]                    = { 169,  1 },
> > +	[VCAP_IS2_HK_L4_PSH]                    = { 170,  1 },
> > +	[VCAP_IS2_HK_L4_ACK]                    = { 171,  1 },
> > +	[VCAP_IS2_HK_L4_URG]                    = { 172,  1 },
> > +	[VCAP_IS2_HK_L4_1588_DOM]               = { 173,  8 },
> > +	[VCAP_IS2_HK_L4_1588_VER]               = { 181,  4 },
> > +	/* IP4_OTHER (TYPE=101) */
> > +	[VCAP_IS2_HK_IP4_L3_PROTO]              = { 124,  8 },
> > +	[VCAP_IS2_HK_L3_PAYLOAD]                = { 132, 56 },
> > +	/* IP6_STD (TYPE=110) */
> > +	[VCAP_IS2_HK_IP6_L3_TTL_GT0]            = { 46,   1 },
> > +	[VCAP_IS2_HK_L3_IP6_SIP]                = { 47, 128 },
> > +	[VCAP_IS2_HK_IP6_L3_PROTO]              = { 175,  8 },
> > +	/* OAM (TYPE=111) */
> > +	[VCAP_IS2_HK_OAM_MEL_FLAGS]             = { 142,  7 },
> > +	[VCAP_IS2_HK_OAM_VER]                   = { 149,  5 },
> > +	[VCAP_IS2_HK_OAM_OPCODE]                = { 154,  8 },
> > +	[VCAP_IS2_HK_OAM_FLAGS]                 = { 162,  8 },
> > +	[VCAP_IS2_HK_OAM_MEPID]                 = { 170, 16 },
> > +	[VCAP_IS2_HK_OAM_CCM_CNTS_EQ0]          = { 186,  1 },
> > +	[VCAP_IS2_HK_OAM_IS_Y1731]              = { 187,  1 },
> > +};
> > +
> > +static const struct vcap_field vsc7512_vcap_is2_actions[] = {
> > +	[VCAP_IS2_ACT_HIT_ME_ONCE]              = { 0,   1 },
> > +	[VCAP_IS2_ACT_CPU_COPY_ENA]             = { 1,   1 },
> > +	[VCAP_IS2_ACT_CPU_QU_NUM]               = { 2,   3 },
> > +	[VCAP_IS2_ACT_MASK_MODE]                = { 5,   2 },
> > +	[VCAP_IS2_ACT_MIRROR_ENA]               = { 7,   1 },
> > +	[VCAP_IS2_ACT_LRN_DIS]                  = { 8,   1 },
> > +	[VCAP_IS2_ACT_POLICE_ENA]               = { 9,   1 },
> > +	[VCAP_IS2_ACT_POLICE_IDX]               = { 10,  9 },
> > +	[VCAP_IS2_ACT_POLICE_VCAP_ONLY]         = { 19,  1 },
> > +	[VCAP_IS2_ACT_PORT_MASK]                = { 20, 11 },
> > +	[VCAP_IS2_ACT_REW_OP]                   = { 31,  9 },
> > +	[VCAP_IS2_ACT_SMAC_REPLACE_ENA]         = { 40,  1 },
> > +	[VCAP_IS2_ACT_RSV]                      = { 41,  2 },
> > +	[VCAP_IS2_ACT_ACL_ID]                   = { 43,  6 },
> > +	[VCAP_IS2_ACT_HIT_CNT]                  = { 49, 32 },
> > +};
> > +
> > +static struct vcap_props vsc7512_vcap_props[] = {
> > +	[VCAP_ES0] = {
> > +		.action_type_width = 0,
> > +		.action_table = {
> > +			[ES0_ACTION_TYPE_NORMAL] = {
> > +				.width = 73,
> > +				.count = 1,
> > +			},
> > +		},
> > +		.target = S0,
> > +		.keys = vsc7512_vcap_es0_keys,
> > +		.actions = vsc7512_vcap_es0_actions,
> > +	},
> > +	[VCAP_IS1] = {
> > +		.action_type_width = 0,
> > +		.action_table = {
> > +			[IS1_ACTION_TYPE_NORMAL] = {
> > +				.width = 78,
> > +				.count = 4,
> > +			},
> > +		},
> > +		.target = S1,
> > +		.keys = vsc7512_vcap_is1_keys,
> > +		.actions = vsc7512_vcap_is1_actions,
> > +	},
> > +	[VCAP_IS2] = {
> > +		.action_type_width = 1,
> > +		.action_table = {
> > +			[IS2_ACTION_TYPE_NORMAL] = {
> > +				.width = 49,
> > +				.count = 2,
> > +			},
> > +			[IS2_ACTION_TYPE_SMAC_SIP] = {
> > +				.width = 6,
> > +				.count = 4,
> > +			},
> > +		},
> > +		.target = S2,
> > +		.keys = vsc7512_vcap_is2_keys,
> > +		.actions = vsc7512_vcap_is2_actions,
> > +	},
> > +};

Again, more tables that could be shared with the library? Perhaps this
time in the existing ocelot_vcap.c file?

I'm wondering if a different structure for all these shared registers
should be considered, instead of just plopping them in the ocelot
library.
diff mbox series

Patch

diff --git a/arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts b/arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts
new file mode 100644
index 000000000000..f2dd8a7edc56
--- /dev/null
+++ b/arch/arm/boot/dts/rpi-vsc7512-spi-overlay.dts
@@ -0,0 +1,124 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Innovative Advantage Inc - https://www.in-advantage.com/
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0_cs_pins>;
+		frag0: __overlay__ {
+		brcm,pins = <8>;
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			cs-gpios = <&gpio 8 1>;
+			status = "okay";
+
+			vsc7512: vsc7512@0{
+				compatible = "mscc,vsc7512";
+				spi-max-frequency = <250000>;
+				reg = <0>;
+
+				ports {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						reg = <0>;
+						ethernet = <&ethernet>;
+						phy-mode = "internal";
+
+						fixed-link {
+							speed = <1000>;
+							full-duplex;
+						};
+					};
+
+					port@1 {
+						reg = <1>;
+						label = "swp1";
+						status = "disabled";
+					};
+
+					port@2 {
+						reg = <2>;
+						label = "swp2";
+						status = "disabled";
+					};
+
+					port@3 {
+						reg = <3>;
+						label = "swp3";
+						status = "disabled";
+					};
+
+					port@4 {
+						reg = <4>;
+						label = "swp4";
+						status = "disabled";
+					};
+
+					port@5 {
+						reg = <5>;
+						label = "swp5";
+						status = "disabled";
+					};
+
+					port@6 {
+						reg = <6>;
+						label = "swp6";
+						status = "disabled";
+					};
+
+					port@7 {
+						reg = <7>;
+						label = "swp7";
+						status = "disabled";
+					};
+
+					port@8 {
+						reg = <8>;
+						label = "swp8";
+						status = "disabled";
+					};
+
+					port@9 {
+						reg = <9>;
+						label = "swp9";
+						status = "disabled";
+					};
+
+					port@10 {
+						reg = <10>;
+						label = "swp10";
+						status = "disabled";
+					};
+				};
+			};
+		};
+	};
+};
diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig
index 932b6b6fe817..2db147ce9fe7 100644
--- a/drivers/net/dsa/ocelot/Kconfig
+++ b/drivers/net/dsa/ocelot/Kconfig
@@ -14,6 +14,17 @@  config NET_DSA_MSCC_FELIX
 	  This driver supports the VSC9959 (Felix) switch, which is embedded as
 	  a PCIe function of the NXP LS1028A ENETC RCiEP.
 
+config NET_DSA_MSCC_FELIX_SPI
+	tristate "Ocelot / Felix Ethernet SPI switch support"
+	depends on NET_DSA && SPI
+	depends on NET_VENDOR_MICROSEMI
+	select MSCC_OCELOT_SWITCH_LIB
+	select NET_DSA_TAG_OCELOT_8021Q
+	select NET_DSA_TAG_OCELOT
+	select PCS_LYNX
+	help
+	  This driver supports the VSC75XX chips when controlled through SPI.
+
 config NET_DSA_MSCC_SEVILLE
 	tristate "Ocelot / Seville Ethernet switch support"
 	depends on NET_DSA
diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile
index f6dd131e7491..f2c9c52ba76c 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_FELIX_SPI) += mscc_felix_spi.o
 obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o
 
 mscc_felix-objs := \
 	felix.o \
 	felix_vsc9959.o
 
+mscc_felix_spi-objs := \
+	felix.o \
+	felix_vsc7512_spi.o
+
 mscc_seville-objs := \
 	felix.o \
 	seville_vsc9953.o
diff --git a/drivers/net/dsa/ocelot/felix_vsc7512_spi.c b/drivers/net/dsa/ocelot/felix_vsc7512_spi.c
new file mode 100644
index 000000000000..1cb5758b752c
--- /dev/null
+++ b/drivers/net/dsa/ocelot/felix_vsc7512_spi.c
@@ -0,0 +1,1214 @@ 
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Copyright 2017 Microsemi Corporation
+ * Copyright 2018-2019 NXP Semiconductors
+ */
+#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 <linux/spi/spi.h>
+#include <linux/packing.h>
+#include <linux/pcs-lynx.h>
+#include <net/pkt_sched.h>
+#include <linux/iopoll.h>
+#include <linux/kconfig.h>
+#include <linux/mdio.h>
+#include "felix.h"
+
+#define VSC7512_TAS_GCL_ENTRY_MAX 63
+
+// Note: These addresses and offsets are all shifted down
+// by two. This is because the SPI protocol needs them to
+// be before they get sent out.
+//
+// An alternative is to keep them standardized, but then
+// a separate spi_bus regmap would need to be defined.
+//
+// That might be optimal though. The 'Read' protocol of
+// the VSC driver might be much quicker if we add padding
+// bytes, which I don't think regmap supports.
+static const u32 vsc7512_ana_regmap[] = {
+	REG(ANA_ADVLEARN,			0x2400),
+	REG(ANA_VLANMASK,			0x2401),
+	REG_RESERVED(ANA_PORT_B_DOMAIN),
+	REG(ANA_ANAGEFIL,			0x2403),
+	REG(ANA_ANEVENTS,			0x2404),
+	REG(ANA_STORMLIMIT_BURST,		0x2405),
+	REG(ANA_STORMLIMIT_CFG,			0x2406),
+	REG(ANA_ISOLATED_PORTS,			0x240a),
+	REG(ANA_COMMUNITY_PORTS,		0x240b),
+	REG(ANA_AUTOAGE,			0x240c),
+	REG(ANA_MACTOPTIONS,			0x240d),
+	REG(ANA_LEARNDISC,			0x240e),
+	REG(ANA_AGENCTRL,			0x240f),
+	REG(ANA_MIRRORPORTS,			0x2410),
+	REG(ANA_EMIRRORPORTS,			0x2411),
+	REG(ANA_FLOODING,			0x2412),
+	REG(ANA_FLOODING_IPMC,			0x2413),
+	REG(ANA_SFLOW_CFG,			0x2414),
+	REG(ANA_PORT_MODE,			0x2420),
+	REG(ANA_PGID_PGID,			0x2300),
+	REG(ANA_TABLES_ANMOVED,			0x22cc),
+	REG(ANA_TABLES_MACHDATA,		0x22cd),
+	REG(ANA_TABLES_MACLDATA,		0x22ce),
+	REG(ANA_TABLES_MACACCESS,		0x22cf),
+	REG(ANA_TABLES_MACTINDX,		0x22d0),
+	REG(ANA_TABLES_VLANACCESS,		0x22d1),
+	REG(ANA_TABLES_VLANTIDX,		0x22d2),
+	REG(ANA_TABLES_ENTRYLIM,		0x22c0),
+	REG(ANA_TABLES_PTP_ID_HIGH,		0x22d5),
+	REG(ANA_TABLES_PTP_ID_LOW,		0x22d6),
+	REG(ANA_PORT_VLAN_CFG,			0x1c00),
+	REG(ANA_PORT_DROP_CFG,			0x1c01),
+	REG(ANA_PORT_QOS_CFG,			0x1c02),
+	REG(ANA_PORT_VCAP_CFG,			0x1c03),
+	REG(ANA_PORT_VCAP_S1_KEY_CFG,		0x1c04),
+	REG(ANA_PORT_VCAP_S2_CFG,		0x1c07),
+	REG(ANA_PORT_PCP_DEI_MAP,		0x1c08),
+	REG(ANA_PORT_CPU_FWD_CFG,		0x1c18),
+	REG(ANA_PORT_CPU_FWD_BPDU_CFG,		0x1c19),
+	REG(ANA_PORT_CPU_FWD_GARP_CFG,		0x1c1a),
+	REG(ANA_PORT_CPU_FWD_CCM_CFG,		0x1c1b),
+	REG(ANA_PORT_PORT_CFG,			0x1c1c),
+	REG(ANA_PORT_POL_CFG,			0x1c1d),
+	REG(ANA_PORT_PTP_CFG,			0x1c1e),
+	REG(ANA_PORT_PTP_DLY1_CFG,		0x1c1f),
+	REG(ANA_PORT_PTP_DLY2_CFG,		0x1c20),
+	REG(ANA_PFC_PFC_CFG,			0x2200),
+	REG_RESERVED(ANA_PFC_PFC_TIMER),
+	REG_RESERVED(ANA_IPT_OAM_MEP_CFG),
+	REG_RESERVED(ANA_IPT_IPT),
+	REG_RESERVED(ANA_PPT_PPT),
+	REG_RESERVED(ANA_FID_MAP_FID_MAP),
+	REG(ANA_AGGR_CFG,			0x242d),
+	REG(ANA_CPUQ_CFG,			0x242e),
+	REG_RESERVED(ANA_CPUQ_CFG2),
+	REG(ANA_CPUQ_8021_CFG,			0x242f),
+	REG(ANA_DSCP_CFG,			0x2440),
+	REG(ANA_DSCP_REWR_CFG,			0x2480),
+	REG(ANA_VCAP_RNG_TYPE_CFG,		0x2490),
+	REG(ANA_VCAP_RNG_VAL_CFG,		0x2498),
+	REG_RESERVED(ANA_VRAP_CFG),
+	REG_RESERVED(ANA_VRAP_HDR_DATA),
+	REG_RESERVED(ANA_VRAP_HDR_MASK),
+	REG(ANA_DISCARD_CFG,			0x24a3),
+	REG(ANA_FID_CFG,			0x24a4),
+	REG(ANA_POL_PIR_CFG,			0x1000),
+	REG(ANA_POL_CIR_CFG,			0x1001),
+	REG(ANA_POL_MODE_CFG,			0x1002),
+	REG(ANA_POL_PIR_STATE,			0x1003),
+	REG(ANA_POL_CIR_STATE,			0x1004),
+	REG_RESERVED(ANA_POL_STATE),
+	REG(ANA_POL_FLOWC,			0x22e0),
+	REG(ANA_POL_HYST,			0x22fb),
+	REG_RESERVED(ANA_POL_MISC_CFG),
+};
+
+static const u32 vsc7512_qs_regmap[] = {
+	REG(QS_XTR_GRP_CFG,			0x0000),
+	REG(QS_XTR_RD,				0x0002),
+	REG(QS_XTR_FRM_PRUNING,			0x004),
+	REG(QS_XTR_FLUSH,			0x0006),
+	REG(QS_XTR_DATA_PRESENT,		0x0007),
+
+	REG(QS_INJ_GRP_CFG,			0x0009),
+	REG(QS_INJ_WR,				0x000b),
+	REG(QS_INJ_CTRL,			0x000d),
+	REG(QS_INJ_STATUS,			0x000f),
+	REG(QS_INJ_ERR,				0x0010),
+	REG_RESERVED(QS_INH_DBG),
+};
+
+static const u32 vsc7512_vcap_regmap[] = {
+	REG(VCAP_CORE_UPDATE_CTRL,		0x000000),
+	REG(VCAP_CORE_MV_CFG,			0x000001),
+	REG(VCAP_CACHE_ENTRY_DAT,		0x000002),
+	REG(VCAP_CACHE_MASK_DAT,		0x000042),
+	REG(VCAP_CACHE_ACTION_DAT,		0x000082),
+	REG(VCAP_CACHE_CNT_DAT,			0x0000c2),
+	REG(VCAP_CACHE_TG_DAT,			0x0000e2),
+	REG(VCAP_CONST_VCAP_VER,		0x0000e6),
+	REG(VCAP_CONST_ENTRY_WIDTH,		0x0000e7),
+	REG(VCAP_CONST_ENTRY_CNT,		0x0000e8),
+	REG(VCAP_CONST_ENTRY_SWCNT,		0x0000e9),
+	REG(VCAP_CONST_ENTRY_TG_WIDTH,		0x0000ea),
+	REG(VCAP_CONST_ACTION_DEF_CNT,		0x0000eb),
+	REG(VCAP_CONST_ACTION_WIDTH,		0x0000ec),
+	REG(VCAP_CONST_CNT_WIDTH,		0x0000ed),
+	REG(VCAP_CONST_CORE_CNT,		0x0000ee),
+	REG(VCAP_CONST_IF_CNT,			0x0000ef),
+};
+
+static const u32 vsc7512_qsys_regmap[] = {
+	REG(QSYS_PORT_MODE,			0x4480),
+	REG(QSYS_SWITCH_PORT_MODE,		0x448d),
+	REG(QSYS_STAT_CNT_CFG,			0x4499),
+	REG(QSYS_EEE_CFG,			0x449a),
+	REG(QSYS_EEE_THRES,			0x44a5),
+	REG(QSYS_IGR_NO_SHARING,		0x44a6),
+	REG(QSYS_EGR_NO_SHARING,		0x44a7),
+	REG(QSYS_SW_STATUS,			0x44a8),
+	REG(QSYS_EXT_CPU_CFG,			0x44b4),
+	REG_RESERVED(QSYS_PAD_CFG),
+	REG(QSYS_CPU_GROUP_MAP,			0x44b6),
+	REG_RESERVED(QSYS_QMAP),
+	REG_RESERVED(QSYS_ISDX_SGRP),
+	REG_RESERVED(QSYS_TIMED_FRAME_ENTRY),
+	REG(QSYS_TFRM_MISC,			0x44c4),
+	REG(QSYS_TFRM_PORT_DLY,			0x44c5),
+	REG(QSYS_TFRM_TIMER_CFG_1,		0x44c6),
+	REG(QSYS_TFRM_TIMER_CFG_2,		0x44c7),
+	REG(QSYS_TFRM_TIMER_CFG_3,		0x44c8),
+	REG(QSYS_TFRM_TIMER_CFG_4,		0x44c9),
+	REG(QSYS_TFRM_TIMER_CFG_5,		0x44ca),
+	REG(QSYS_TFRM_TIMER_CFG_6,		0x44cb),
+	REG(QSYS_TFRM_TIMER_CFG_7,		0x44cc),
+	REG(QSYS_TFRM_TIMER_CFG_8,		0x44cd),
+	REG(QSYS_RED_PROFILE,			0x44ce),
+	REG(QSYS_RES_QOS_MODE,			0x44de),
+	REG(QSYS_RES_CFG,			0x4800),
+	REG(QSYS_RES_STAT,			0x4801),
+	REG(QSYS_EGR_DROP_MODE,			0x44df),
+	REG(QSYS_EQ_CTRL,			0x44e0),
+	REG_RESERVED(QSYS_EVENTS_CORE),
+	REG(QSYS_CIR_CFG,			0x0000),
+	REG(QSYS_EIR_CFG,			0x0001),
+	REG(QSYS_SE_CFG,			0x0002),
+	REG(QSYS_SE_DWRR_CFG,			0x0003),
+	REG_RESERVED(QSYS_SE_CONNECT),
+	REG(QSYS_SE_DLB_SENSE,			0x0010),
+	REG(QSYS_CIR_STATE,			0x0011),
+	REG(QSYS_EIR_STATE,			0x0012),
+	REG_RESERVED(QSYS_SE_STATE),
+	REG(QSYS_HSCH_MISC_CFG,			0x44e2),
+};
+
+static const u32 vsc7512_rew_regmap[] = {
+	REG(REW_PORT_VLAN_CFG,			0x000),
+	REG(REW_TAG_CFG,			0x001),
+	REG(REW_PORT_CFG,			0x002),
+	REG(REW_DSCP_CFG,			0x003),
+	REG(REW_PCP_DEI_QOS_MAP_CFG,		0x004),
+	REG(REW_PTP_CFG,			0x014),
+	REG(REW_PTP_DLY1_CFG,			0x015),
+	REG(REW_RED_TAG_CFG,			0x016),
+	REG(REW_DSCP_REMAP_DP1_CFG,		0x104),
+	REG(REW_DSCP_REMAP_CFG,			0x144),
+	REG_RESERVED(REW_STAT_CFG),
+	REG_RESERVED(REW_REW_STICKY),
+	REG_RESERVED(REW_PPT),
+};
+
+static const u32 vsc7512_sys_regmap[] = {
+	REG(SYS_COUNT_RX_OCTETS,		0x000),
+	REG(SYS_COUNT_RX_MULTICAST,		0x002),
+	REG(SYS_COUNT_RX_SHORTS,		0x004),
+	REG(SYS_COUNT_RX_FRAGMENTS,		0x005),
+	REG(SYS_COUNT_RX_JABBERS,		0x006),
+	REG(SYS_COUNT_RX_64,			0x009),
+	REG(SYS_COUNT_RX_65_127,		0x00a),
+	REG(SYS_COUNT_RX_128_255,		0x00b),
+	REG(SYS_COUNT_RX_256_1023,		0x00c),
+	REG(SYS_COUNT_RX_1024_1526,		0x00d),
+	REG(SYS_COUNT_RX_1527_MAX,		0x00e),
+	REG(SYS_COUNT_RX_LONGS,			0x011),
+	REG(SYS_COUNT_TX_OCTETS,		0x080),
+	REG(SYS_COUNT_TX_COLLISION,		0x084),
+	REG(SYS_COUNT_TX_DROPS,			0x085),
+	REG(SYS_COUNT_TX_64,			0x087),
+	REG(SYS_COUNT_TX_65_127,		0x088),
+	REG(SYS_COUNT_TX_128_511,		0x089),
+	REG(SYS_COUNT_TX_512_1023,		0x08a),
+	REG(SYS_COUNT_TX_1024_1526,		0x08b),
+	REG(SYS_COUNT_TX_1527_MAX,		0x08c),
+	REG(SYS_COUNT_TX_AGING,			0x09e),
+	REG(SYS_RESET_CFG,			0x142),
+	REG(SYS_VLAN_ETYPE_CFG,			0x144),
+	REG(SYS_PORT_MODE,			0x145),
+	REG(SYS_FRONT_PORT_MODE,		0x152),
+	REG(SYS_FRM_AGING,			0x15d),
+	REG(SYS_STAT_CFG,			0x15e),
+	REG_RESERVED(SYS_MISC_CFG),
+	REG(SYS_REW_MAC_HIGH_CFG,		0x16c),
+	REG(SYS_REW_MAC_LOW_CFG,		0x177),
+	REG(SYS_PAUSE_CFG,			0x182),
+	REG(SYS_PAUSE_TOT_CFG,			0x18e),
+	REG(SYS_ATOP,				0x18f),
+	REG(SYS_ATOP_TOT_CFG,			0x19b),
+	REG(SYS_MAC_FC_CFG,			0x19c),
+	REG(SYS_MMGT,				0x1a7),
+	REG_RESERVED(SYS_MMGT_FAST),
+	REG_RESERVED(SYS_EVENTS_DIF),
+	REG_RESERVED(SYS_EVENTS_CORE),
+	REG_RESERVED(SYS_CNT),
+	REG(SYS_PTP_STATUS,			0x1ae),
+	REG(SYS_PTP_TXSTAMP,			0x1af),
+	REG(SYS_PTP_NXT,			0x1b0),
+	REG(SYS_PTP_CFG,			0x1b1),
+	REG_RESERVED(SYS_CM_ADDR),
+	REG_RESERVED(SYS_CM_DATA_WR),
+	REG_RESERVED(SYS_CM_DATA_RD),
+	REG_RESERVED(SYS_CM_OP),
+	REG_RESERVED(SYS_CM_DATA),
+};
+
+static const u32 vsc7512_ptp_regmap[] = {
+	REG(PTP_PIN_CFG,			0x000000),
+	REG(PTP_PIN_TOD_SEC_MSB,		0x000001),
+	REG(PTP_PIN_TOD_SEC_LSB,		0x000002),
+	REG(PTP_PIN_TOD_NSEC,			0x000003),
+	REG(PTP_PIN_WF_HIGH_PERIOD,		0x000005),
+	REG(PTP_PIN_WF_LOW_PERIOD,		0x000006),
+	REG(PTP_CFG_MISC,			0x000028),
+	REG(PTP_CLK_CFG_ADJ_CFG,		0x000029),
+	REG(PTP_CLK_CFG_ADJ_FREQ,		0x00002a),
+};
+
+static const u32 vsc7512_gcb_regmap[] = {
+	REG(GCB_SOFT_RST,			0xc002),
+};
+
+static const u32 vsc7512_dev_gmii_regmap[] = {
+	REG(DEV_CLOCK_CFG,			0x0),
+	REG(DEV_PORT_MISC,			0x1),
+	REG(DEV_EEE_CFG,			0x3),
+	REG(DEV_RX_PATH_DELAY,			0x4),
+	REG(DEV_TX_PATH_DELAY,			0x5),
+	REG(DEV_PTP_PREDICT_CFG,		0x6),
+	REG(DEV_MAC_ENA_CFG,			0x7),
+	REG(DEV_MAC_MODE_CFG,			0x8),
+	REG(DEV_MAC_MAXLEN_CFG,			0x9),
+	REG(DEV_MAC_TAGS_CFG,			0xa),
+	REG(DEV_MAC_ADV_CHK_CFG,		0xb),
+	REG(DEV_MAC_IFG_CFG,			0xc),
+	REG(DEV_MAC_HDX_CFG,			0xd),
+	REG(DEV_MAC_DBG_CFG,			0xe),
+	REG(DEV_MAC_FC_MAC_LOW_CFG,		0xf),
+	REG(DEV_MAC_FC_MAC_HIGH_CFG,		0x10),
+	REG(DEV_MAC_STICKY,			0x11),
+	REG_RESERVED(PCS1G_CFG),
+	REG_RESERVED(PCS1G_MODE_CFG),
+	REG_RESERVED(PCS1G_SD_CFG),
+	REG_RESERVED(PCS1G_ANEG_CFG),
+	REG_RESERVED(PCS1G_ANEG_NP_CFG),
+	REG_RESERVED(PCS1G_LB_CFG),
+	REG_RESERVED(PCS1G_DBG_CFG),
+	REG_RESERVED(PCS1G_CDET_CFG),
+	REG_RESERVED(PCS1G_ANEG_STATUS),
+	REG_RESERVED(PCS1G_ANEG_NP_STATUS),
+	REG_RESERVED(PCS1G_LINK_STATUS),
+	REG_RESERVED(PCS1G_LINK_DOWN_CNT),
+	REG_RESERVED(PCS1G_STICKY),
+	REG_RESERVED(PCS1G_DEBUG_STATUS),
+	REG_RESERVED(PCS1G_LPI_CFG),
+	REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT),
+	REG_RESERVED(PCS1G_LPI_STATUS),
+	REG_RESERVED(PCS1G_TSTPAT_MODE_CFG),
+	REG_RESERVED(PCS1G_TSTPAT_STATUS),
+	REG_RESERVED(DEV_PCS_FX100_CFG),
+	REG_RESERVED(DEV_PCS_FX100_STATUS),
+};
+
+static const u32 vsc7512_cpu_org_regmap[] = {
+	REG(DEV_CPUORG_IF_CTRL,			0x0000),
+	REG(DEV_CPUORG_IF_CFGSTAT,		0x0001),
+	REG(DEV_CPUORG_ORG_CFG,			0x0002),
+	REG(DEV_CPUORG_ERR_CNTS,		0x0003),
+	REG(DEV_CPUORG_TIMEOUT_CFG,		0x0004),
+	REG(DEV_CPUORG_GPR,			0x0005),
+	REG(DEV_CPUORG_MAILBOX_SET,		0x0006),
+	REG(DEV_CPUORG_MAILBOX_CLR,		0x0007),
+	REG(DEV_CPUORG_MAILBOX,			0x0008),
+	REG(DEV_CPUORG_SEMA_CFG,		0x0009),
+	REG(DEV_CPUORG_SEMA0,			0x000a),
+	REG(DEV_CPUORG_SEMA0_OWNER,		0x000b),
+	REG(DEV_CPUORG_SEMA1,			0x000c),
+	REG(DEV_CPUORG_SEMA1_OWNER,		0x000d),
+};
+
+static const u32 *vsc7512_regmap[TARGET_MAX] = {
+	[ANA] = vsc7512_ana_regmap,
+	[QS] = vsc7512_qs_regmap,
+	[QSYS] = vsc7512_qsys_regmap,
+	[REW] = vsc7512_rew_regmap,
+	[SYS] = vsc7512_sys_regmap,
+	[S0] = vsc7512_vcap_regmap,
+	[S1] = vsc7512_vcap_regmap,
+	[S2] = vsc7512_vcap_regmap,
+	[PTP] = vsc7512_ptp_regmap,
+	[GCB] = vsc7512_gcb_regmap,
+	[DEV_GMII] = vsc7512_dev_gmii_regmap,
+	[DEV_CPUORG] = vsc7512_cpu_org_regmap,
+};
+
+static int vsc7512_soft_rst_status(struct ocelot *ocelot)
+{
+	int val;
+
+	ocelot_field_read(ocelot, GCB_SOFT_RST_SWC_RST, &val);
+
+	return val;
+}
+
+#define VSC7512_INIT_TIMEOUT 50000
+#define VSC7512_GCB_RST_SLEEP 100
+
+static int vsc7512_reset(struct ocelot *ocelot)
+{
+	int val, err;
+
+	ocelot_field_write(ocelot, GCB_SOFT_RST_SWC_RST, 1);
+
+	err = readx_poll_timeout(vsc7512_soft_rst_status, ocelot, val, !val,
+				 VSC7512_GCB_RST_SLEEP, VSC7512_INIT_TIMEOUT);
+	if (err) {
+		dev_err(ocelot->dev, "timeout: switch core reset\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static u16 vsc7512_wm_enc(u16 value)
+{
+	WARN_ON(value >= 16 * BIT(8));
+
+	if (value >= BIT(8))
+		return BIT(8) | (value / 16);
+
+	return value;
+}
+
+static const struct ocelot_ops vsc7512_ops = {
+	.reset		= vsc7512_reset,
+	.wm_enc		= vsc7512_wm_enc,
+	.port_to_netdev	= felix_port_to_netdev,
+	.netdev_to_port	= felix_netdev_to_port,
+};
+
+/* Addresses are relative to the SPI device's base address, downshifted by 2*/
+static const struct resource vsc7512_target_io_res[TARGET_MAX] = {
+	[ANA] = {
+		.start	= 0x1c620000,
+		.end	= 0x1c623fff,
+		.name	= "ana",
+	},
+	[QS] = {
+		.start	= 0x1c420000,
+		.end	= 0x1c42003f,
+		.name	= "qs",
+	},
+	[QSYS] = {
+		.start	= 0x1c600000,
+		.end	= 0x1c607fff,
+		.name	= "qsys",
+	},
+	[REW] = {
+		.start	= 0x1c40c000,
+		.end	= 0x1c40ffff,
+		.name	= "rew",
+	},
+	[SYS] = {
+		.start	= 0x1c404000,
+		.end	= 0x1c407fff,
+		.name	= "sys",
+	},
+	[S0] = {
+		.start	= 0x1c410000,
+		.end	= 0x1c4100ff,
+		.name	= "s0",
+	},
+	[S1] = {
+		.start	= 0x1c414000,
+		.end	= 0x1c4140ff,
+		.name	= "s1",
+	},
+	[S2] = {
+		.start	= 0x1c418000,
+		.end	= 0x1c4180ff,
+		.name	= "s2",
+	},
+	[GCB] =	{
+		.start	= 0x1c41c000,
+		.end	= 0x1c41c07f,
+		.name	= "devcpu_gcb",
+	},
+	[DEV_CPUORG] = {
+		.start	= 0x1c400000,
+		.end	= 0x1c4000ff,
+		.name	= "devcpu_org",
+	},
+};
+
+static const struct resource vsc7512_port_io_res[] = {
+	{
+		.start	= 0x1c478000,
+		.end	= 0x1c47bfff,
+		.name	= "port0",
+	},
+	{
+		.start	= 0x1c47c000,
+		.end	= 0x1c47ffff,
+		.name	= "port1",
+	},
+	{
+		.start	= 0x1c480000,
+		.end	= 0x1c483fff,
+		.name	= "port2",
+	},
+	{
+		.start	= 0x1c484000,
+		.end	= 0x1c487fff,
+		.name	= "port3",
+	},
+	{
+		.start	= 0x1c488000,
+		.end	= 0x1c48bfff,
+		.name	= "port4",
+	},
+	{
+		.start	= 0x1c48c000,
+		.end	= 0x1c48ffff,
+		.name	= "port5",
+	},
+};
+
+static const struct reg_field vsc7512_regfields[REGFIELD_MAX] = {
+	[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
+	[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
+	[ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
+	[ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
+	[ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
+	[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
+	[ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
+	[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
+	[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
+	[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
+	[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
+	[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
+	[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
+	[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
+	[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
+	[ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
+	[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
+	[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
+	[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
+	[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
+	[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
+	[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
+	[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
+	[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
+	[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
+	[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
+	[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
+	[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
+	[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
+	[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
+	[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
+	[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
+	[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
+	[GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
+	[QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
+	[QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
+	[QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
+	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
+	[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
+	[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
+	[SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
+	[SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
+	/* Replicated per number of ports (12), register size 4 per port */
+	[QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 12, 4),
+	[QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 12, 4),
+	[QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 12, 4),
+	[QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 12, 4),
+	[QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 12, 4),
+	[QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 12, 4),
+	[SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 12, 4),
+	[SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 12, 4),
+	[SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 12, 4),
+	[SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 12, 4),
+	[SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 12, 4),
+	[SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 12, 4),
+	[SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 12, 4),
+};
+
+static const struct ocelot_stat_layout vsc7512_stats_layout[] = {
+	{ .offset = 0x00,	.name = "rx_octets", },
+	{ .offset = 0x01,	.name = "rx_unicast", },
+	{ .offset = 0x02,	.name = "rx_multicast", },
+	{ .offset = 0x03,	.name = "rx_broadcast", },
+	{ .offset = 0x04,	.name = "rx_shorts", },
+	{ .offset = 0x05,	.name = "rx_fragments", },
+	{ .offset = 0x06,	.name = "rx_jabbers", },
+	{ .offset = 0x07,	.name = "rx_crc_align_errs", },
+	{ .offset = 0x08,	.name = "rx_sym_errs", },
+	{ .offset = 0x09,	.name = "rx_frames_below_65_octets", },
+	{ .offset = 0x0A,	.name = "rx_frames_65_to_127_octets", },
+	{ .offset = 0x0B,	.name = "rx_frames_128_to_255_octets", },
+	{ .offset = 0x0C,	.name = "rx_frames_256_to_511_octets", },
+	{ .offset = 0x0D,	.name = "rx_frames_512_to_1023_octets", },
+	{ .offset = 0x0E,	.name = "rx_frames_1024_to_1526_octets", },
+	{ .offset = 0x0F,	.name = "rx_frames_over_1526_octets", },
+	{ .offset = 0x10,	.name = "rx_pause", },
+	{ .offset = 0x11,	.name = "rx_control", },
+	{ .offset = 0x12,	.name = "rx_longs", },
+	{ .offset = 0x13,	.name = "rx_classified_drops", },
+	{ .offset = 0x14,	.name = "rx_red_prio_0", },
+	{ .offset = 0x15,	.name = "rx_red_prio_1", },
+	{ .offset = 0x16,	.name = "rx_red_prio_2", },
+	{ .offset = 0x17,	.name = "rx_red_prio_3", },
+	{ .offset = 0x18,	.name = "rx_red_prio_4", },
+	{ .offset = 0x19,	.name = "rx_red_prio_5", },
+	{ .offset = 0x1A,	.name = "rx_red_prio_6", },
+	{ .offset = 0x1B,	.name = "rx_red_prio_7", },
+	{ .offset = 0x1C,	.name = "rx_yellow_prio_0", },
+	{ .offset = 0x1D,	.name = "rx_yellow_prio_1", },
+	{ .offset = 0x1E,	.name = "rx_yellow_prio_2", },
+	{ .offset = 0x1F,	.name = "rx_yellow_prio_3", },
+	{ .offset = 0x20,	.name = "rx_yellow_prio_4", },
+	{ .offset = 0x21,	.name = "rx_yellow_prio_5", },
+	{ .offset = 0x22,	.name = "rx_yellow_prio_6", },
+	{ .offset = 0x23,	.name = "rx_yellow_prio_7", },
+	{ .offset = 0x24,	.name = "rx_green_prio_0", },
+	{ .offset = 0x25,	.name = "rx_green_prio_1", },
+	{ .offset = 0x26,	.name = "rx_green_prio_2", },
+	{ .offset = 0x27,	.name = "rx_green_prio_3", },
+	{ .offset = 0x28,	.name = "rx_green_prio_4", },
+	{ .offset = 0x29,	.name = "rx_green_prio_5", },
+	{ .offset = 0x2A,	.name = "rx_green_prio_6", },
+	{ .offset = 0x2B,	.name = "rx_green_prio_7", },
+	{ .offset = 0x40,	.name = "tx_octets", },
+	{ .offset = 0x41,	.name = "tx_unicast", },
+	{ .offset = 0x42,	.name = "tx_multicast", },
+	{ .offset = 0x43,	.name = "tx_broadcast", },
+	{ .offset = 0x44,	.name = "tx_collision", },
+	{ .offset = 0x45,	.name = "tx_drops", },
+	{ .offset = 0x46,	.name = "tx_pause", },
+	{ .offset = 0x47,	.name = "tx_frames_below_65_octets", },
+	{ .offset = 0x48,	.name = "tx_frames_65_to_127_octets", },
+	{ .offset = 0x49,	.name = "tx_frames_128_255_octets", },
+	{ .offset = 0x4A,	.name = "tx_frames_256_511_octets", },
+	{ .offset = 0x4B,	.name = "tx_frames_512_1023_octets", },
+	{ .offset = 0x4C,	.name = "tx_frames_1024_1526_octets", },
+	{ .offset = 0x4D,	.name = "tx_frames_over_1526_octets", },
+	{ .offset = 0x4E,	.name = "tx_yellow_prio_0", },
+	{ .offset = 0x4F,	.name = "tx_yellow_prio_1", },
+	{ .offset = 0x50,	.name = "tx_yellow_prio_2", },
+	{ .offset = 0x51,	.name = "tx_yellow_prio_3", },
+	{ .offset = 0x52,	.name = "tx_yellow_prio_4", },
+	{ .offset = 0x53,	.name = "tx_yellow_prio_5", },
+	{ .offset = 0x54,	.name = "tx_yellow_prio_6", },
+	{ .offset = 0x55,	.name = "tx_yellow_prio_7", },
+	{ .offset = 0x56,	.name = "tx_green_prio_0", },
+	{ .offset = 0x57,	.name = "tx_green_prio_1", },
+	{ .offset = 0x58,	.name = "tx_green_prio_2", },
+	{ .offset = 0x59,	.name = "tx_green_prio_3", },
+	{ .offset = 0x5A,	.name = "tx_green_prio_4", },
+	{ .offset = 0x5B,	.name = "tx_green_prio_5", },
+	{ .offset = 0x5C,	.name = "tx_green_prio_6", },
+	{ .offset = 0x5D,	.name = "tx_green_prio_7", },
+	{ .offset = 0x5E,	.name = "tx_aged", },
+	{ .offset = 0x80,	.name = "drop_local", },
+	{ .offset = 0x81,	.name = "drop_tail", },
+	{ .offset = 0x82,	.name = "drop_yellow_prio_0", },
+	{ .offset = 0x83,	.name = "drop_yellow_prio_1", },
+	{ .offset = 0x84,	.name = "drop_yellow_prio_2", },
+	{ .offset = 0x85,	.name = "drop_yellow_prio_3", },
+	{ .offset = 0x86,	.name = "drop_yellow_prio_4", },
+	{ .offset = 0x87,	.name = "drop_yellow_prio_5", },
+	{ .offset = 0x88,	.name = "drop_yellow_prio_6", },
+	{ .offset = 0x89,	.name = "drop_yellow_prio_7", },
+	{ .offset = 0x8A,	.name = "drop_green_prio_0", },
+	{ .offset = 0x8B,	.name = "drop_green_prio_1", },
+	{ .offset = 0x8C,	.name = "drop_green_prio_2", },
+	{ .offset = 0x8D,	.name = "drop_green_prio_3", },
+	{ .offset = 0x8E,	.name = "drop_green_prio_4", },
+	{ .offset = 0x8F,	.name = "drop_green_prio_5", },
+	{ .offset = 0x90,	.name = "drop_green_prio_6", },
+	{ .offset = 0x91,	.name = "drop_green_prio_7", },
+};
+
+struct felix_spi_data {
+	struct regmap *map;
+	struct felix felix;
+};
+
+/* TODO: The regmap has overlaps for DEVCPU_ORG and other peripherals. I believe
+ * this is handled with a page register, and should hopefully be easy to use
+ * with the existing regmap_config with regmap_range_cfg
+ */
+static const struct regmap_config felix_spi_regmap_config = {
+	.reg_bits = 24,
+	.reg_stride = 1,
+	.val_bits = 32,
+
+	.write_flag_mask = BIT(7),
+	.max_register = 0x3fffffff,
+	.use_single_write = true,
+	.use_single_read = true,
+	.can_multi_write = false,
+
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
+#define VSC7512_BYTE_ORDER_LE 0x00000000
+#define VSC7512_BYTE_ORDER_BE 0x81818181
+#define VSC7512_BIT_ORDER_MSB 0x00000000
+#define VSC7512_BIT_ORDER_LSB 0x42424242
+
+static int felix_spi_init_bus(struct spi_device *spi,
+			      struct felix_spi_data *felix_spi)
+{
+	int err;
+	u32 val, check;
+
+	val = 0;
+
+#ifdef __LITTLE_ENDIAN
+	val |= VSC7512_BYTE_ORDER_LE;
+#else
+	val |= VSC7512_BYTE_ORDER_BE;
+#endif
+
+	// Hard code this write to set up the interface. After this is done,
+	// ocelot_read interface should be able to be used.
+	err = regmap_write(felix_spi->map, (0x71000000 >> 2) & 0x3fffff, val);
+	if (err < 0) {
+		dev_err(&spi->dev, "error %d configuring SPI interface\n", err);
+		return err;
+	}
+
+	val = 0x00000000;
+	err = regmap_write(felix_spi->map, (0x71000004 >> 2) & 0x3fffff, val);
+	if (err < 0) {
+		dev_err(&spi->dev, "error %d writing padding bytes\n", err);
+		return err;
+	}
+
+	check = val | 0x02000000;
+
+	err = regmap_read(felix_spi->map, (0x71000004 >> 2) & 0x3fffff, &val);
+	if (err < 0) {
+		dev_err(&spi->dev, "Error %d writing padding bytes\n", err);
+		return err;
+	} else if (check != val) {
+		dev_err(&spi->dev,
+			"Error configuring SPI bus. V: 0x%08x != 0x%08x\n", val,
+			check);
+		return -ENODEV;
+	}
+
+	return err;
+}
+
+static void vsc7512_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, Autoneg);
+	phylink_set(mask, Pause);
+	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);
+
+	if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
+	    state->interface == PHY_INTERFACE_MODE_INTERNAL ||
+	    state->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		phylink_set(mask, 2500baseT_Full);
+		phylink_set(mask, 2500baseX_Full);
+	}
+
+	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static int vsc7512_prevalidate_phy_mode(struct ocelot *ocelot, int port,
+					phy_interface_t phy_mode)
+{
+	switch (phy_mode) {
+	case PHY_INTERFACE_MODE_INTERNAL:
+		// TODO: I don't know what ports would not be supported
+		// internally.
+		return 0;
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
+	case PHY_INTERFACE_MODE_USXGMII:
+	case PHY_INTERFACE_MODE_2500BASEX:
+		if (port == 4 || port == 5)
+			return -EOPNOTSUPP;
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int vsc7512_port_setup_tc(struct dsa_switch *ds, int port,
+				 enum tc_setup_type type, void *type_data)
+{
+	return -EOPNOTSUPP;
+}
+
+static void vsc7512_sched_speed_set(struct ocelot *ocelot, int port, u32 speed)
+{
+	u8 tas_speed;
+
+	switch (speed) {
+	case SPEED_10:
+		tas_speed = OCELOT_SPEED_10;
+		break;
+
+	case SPEED_100:
+		tas_speed = OCELOT_SPEED_100;
+		break;
+
+	case SPEED_1000:
+		tas_speed = OCELOT_SPEED_1000;
+		break;
+
+	case SPEED_2500:
+		tas_speed = OCELOT_SPEED_2500;
+		break;
+
+	default:
+		tas_speed = OCELOT_SPEED_1000;
+		break;
+	}
+
+	ocelot_rmw_rix(ocelot, QSYS_TAG_CONFIG_LINK_SPEED(tas_speed),
+		       QSYS_TAG_CONFIG_LINK_SPEED_M, QSYS_TAG_CONFIG, port);
+}
+
+static const struct vcap_field vsc7512_vcap_es0_keys[] = {
+	[VCAP_ES0_EGR_PORT]                     = { 0,   4 },
+	[VCAP_ES0_IGR_PORT]                     = { 4,   4 },
+	[VCAP_ES0_RSV]                          = { 8,   2 },
+	[VCAP_ES0_L2_MC]                        = { 10,  1 },
+	[VCAP_ES0_L2_BC]                        = { 11,  1 },
+	[VCAP_ES0_VID]                          = { 12, 12 },
+	[VCAP_ES0_DP]                           = { 24,  1 },
+	[VCAP_ES0_PCP]                          = { 25,  3 },
+};
+
+static const struct vcap_field vsc7512_vcap_es0_actions[]   = {
+	[VCAP_ES0_ACT_PUSH_OUTER_TAG]           = { 0,   2 },
+	[VCAP_ES0_ACT_PUSH_INNER_TAG]           = { 2,   1 },
+	[VCAP_ES0_ACT_TAG_A_TPID_SEL]           = { 3,   2 },
+	[VCAP_ES0_ACT_TAG_A_VID_SEL]            = { 5,   1 },
+	[VCAP_ES0_ACT_TAG_A_PCP_SEL]            = { 6,   2 },
+	[VCAP_ES0_ACT_TAG_A_DEI_SEL]            = { 8,   2 },
+	[VCAP_ES0_ACT_TAG_B_TPID_SEL]           = { 10,  2 },
+	[VCAP_ES0_ACT_TAG_B_VID_SEL]            = { 12,  1 },
+	[VCAP_ES0_ACT_TAG_B_PCP_SEL]            = { 13,  2 },
+	[VCAP_ES0_ACT_TAG_B_DEI_SEL]            = { 15,  2 },
+	[VCAP_ES0_ACT_VID_A_VAL]                = { 17, 12 },
+	[VCAP_ES0_ACT_PCP_A_VAL]                = { 29,  3 },
+	[VCAP_ES0_ACT_DEI_A_VAL]                = { 32,  1 },
+	[VCAP_ES0_ACT_VID_B_VAL]                = { 33, 12 },
+	[VCAP_ES0_ACT_PCP_B_VAL]                = { 45,  3 },
+	[VCAP_ES0_ACT_DEI_B_VAL]                = { 48,  1 },
+	[VCAP_ES0_ACT_RSV]                      = { 49, 24 },
+	[VCAP_ES0_ACT_HIT_STICKY]               = { 73,  1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is1_keys[] = {
+	[VCAP_IS1_HK_TYPE]                      = { 0,    1 },
+	[VCAP_IS1_HK_LOOKUP]                    = { 1,    2 },
+	[VCAP_IS1_HK_IGR_PORT_MASK]             = { 3,   12 },
+	[VCAP_IS1_HK_RSV]                       = { 15,   9 },
+	[VCAP_IS1_HK_OAM_Y1731]                 = { 24,   1 },
+	[VCAP_IS1_HK_L2_MC]                     = { 25,   1 },
+	[VCAP_IS1_HK_L2_BC]                     = { 26,   1 },
+	[VCAP_IS1_HK_IP_MC]                     = { 27,   1 },
+	[VCAP_IS1_HK_VLAN_TAGGED]               = { 28,   1 },
+	[VCAP_IS1_HK_VLAN_DBL_TAGGED]           = { 29,   1 },
+	[VCAP_IS1_HK_TPID]                      = { 30,   1 },
+	[VCAP_IS1_HK_VID]                       = { 31,  12 },
+	[VCAP_IS1_HK_DEI]                       = { 43,   1 },
+	[VCAP_IS1_HK_PCP]                       = { 44,   3 },
+	/* Specific Fields for IS1 Half Key S1_NORMAL */
+	[VCAP_IS1_HK_L2_SMAC]                   = { 47,  48 },
+	[VCAP_IS1_HK_ETYPE_LEN]                 = { 95,   1 },
+	[VCAP_IS1_HK_ETYPE]                     = { 96,  16 },
+	[VCAP_IS1_HK_IP_SNAP]                   = { 112,  1 },
+	[VCAP_IS1_HK_IP4]                       = { 113,  1 },
+	/* Layer-3 Information */
+	[VCAP_IS1_HK_L3_FRAGMENT]               = { 114,  1 },
+	[VCAP_IS1_HK_L3_FRAG_OFS_GT0]           = { 115,  1 },
+	[VCAP_IS1_HK_L3_OPTIONS]                = { 116,  1 },
+	[VCAP_IS1_HK_L3_DSCP]                   = { 117,  6 },
+	[VCAP_IS1_HK_L3_IP4_SIP]                = { 123, 32 },
+	/* Layer-4 Information */
+	[VCAP_IS1_HK_TCP_UDP]                   = { 155,  1 },
+	[VCAP_IS1_HK_TCP]                       = { 156,  1 },
+	[VCAP_IS1_HK_L4_SPORT]                  = { 157, 16 },
+	[VCAP_IS1_HK_L4_RNG]                    = { 173,  8 },
+	/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */
+	[VCAP_IS1_HK_IP4_INNER_TPID]            = { 47,   1 },
+	[VCAP_IS1_HK_IP4_INNER_VID]             = { 48,  12 },
+	[VCAP_IS1_HK_IP4_INNER_DEI]             = { 60,   1 },
+	[VCAP_IS1_HK_IP4_INNER_PCP]             = { 61,   3 },
+	[VCAP_IS1_HK_IP4_IP4]                   = { 64,   1 },
+	[VCAP_IS1_HK_IP4_L3_FRAGMENT]           = { 65,   1 },
+	[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0]       = { 66,   1 },
+	[VCAP_IS1_HK_IP4_L3_OPTIONS]            = { 67,   1 },
+	[VCAP_IS1_HK_IP4_L3_DSCP]               = { 68,   6 },
+	[VCAP_IS1_HK_IP4_L3_IP4_DIP]            = { 74,  32 },
+	[VCAP_IS1_HK_IP4_L3_IP4_SIP]            = { 106, 32 },
+	[VCAP_IS1_HK_IP4_L3_PROTO]              = { 138,  8 },
+	[VCAP_IS1_HK_IP4_TCP_UDP]               = { 146,  1 },
+	[VCAP_IS1_HK_IP4_TCP]                   = { 147,  1 },
+	[VCAP_IS1_HK_IP4_L4_RNG]                = { 148,  8 },
+	[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE]  = { 156, 32 },
+};
+
+static const struct vcap_field vsc7512_vcap_is1_actions[] = {
+	[VCAP_IS1_ACT_DSCP_ENA]                 = { 0,   1 },
+	[VCAP_IS1_ACT_DSCP_VAL]                 = { 1,   6 },
+	[VCAP_IS1_ACT_QOS_ENA]                  = { 7,   1 },
+	[VCAP_IS1_ACT_QOS_VAL]                  = { 8,   3 },
+	[VCAP_IS1_ACT_DP_ENA]                   = { 11,  1 },
+	[VCAP_IS1_ACT_DP_VAL]                   = { 12,  1 },
+	[VCAP_IS1_ACT_PAG_OVERRIDE_MASK]        = { 13,  8 },
+	[VCAP_IS1_ACT_PAG_VAL]                  = { 21,  8 },
+	[VCAP_IS1_ACT_RSV]                      = { 29,  9 },
+	/* The fields below are incorrectly shifted by 2 in the manual */
+	[VCAP_IS1_ACT_VID_REPLACE_ENA]          = { 38,  1 },
+	[VCAP_IS1_ACT_VID_ADD_VAL]              = { 39, 12 },
+	[VCAP_IS1_ACT_FID_SEL]                  = { 51,  2 },
+	[VCAP_IS1_ACT_FID_VAL]                  = { 53, 13 },
+	[VCAP_IS1_ACT_PCP_DEI_ENA]              = { 66,  1 },
+	[VCAP_IS1_ACT_PCP_VAL]                  = { 67,  3 },
+	[VCAP_IS1_ACT_DEI_VAL]                  = { 70,  1 },
+	[VCAP_IS1_ACT_VLAN_POP_CNT_ENA]         = { 71,  1 },
+	[VCAP_IS1_ACT_VLAN_POP_CNT]             = { 72,  2 },
+	[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA]      = { 74,  4 },
+	[VCAP_IS1_ACT_HIT_STICKY]               = { 78,  1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is2_keys[] = {
+	/* Common: 46 bits */
+	[VCAP_IS2_TYPE]                         = { 0,    4 },
+	[VCAP_IS2_HK_FIRST]                     = { 4,    1 },
+	[VCAP_IS2_HK_PAG]                       = { 5,    8 },
+	[VCAP_IS2_HK_IGR_PORT_MASK]             = { 13,  12 },
+	[VCAP_IS2_HK_RSV2]                      = { 25,   1 },
+	[VCAP_IS2_HK_HOST_MATCH]                = { 26,   1 },
+	[VCAP_IS2_HK_L2_MC]                     = { 27,   1 },
+	[VCAP_IS2_HK_L2_BC]                     = { 28,   1 },
+	[VCAP_IS2_HK_VLAN_TAGGED]               = { 29,   1 },
+	[VCAP_IS2_HK_VID]                       = { 30,  12 },
+	[VCAP_IS2_HK_DEI]                       = { 42,   1 },
+	[VCAP_IS2_HK_PCP]                       = { 43,   3 },
+	/* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */
+	[VCAP_IS2_HK_L2_DMAC]                   = { 46,  48 },
+	[VCAP_IS2_HK_L2_SMAC]                   = { 94,  48 },
+	/* MAC_ETYPE (TYPE=000) */
+	[VCAP_IS2_HK_MAC_ETYPE_ETYPE]           = { 142, 16 },
+	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0]     = { 158, 16 },
+	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1]     = { 174,  8 },
+	[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2]     = { 182,  3 },
+	/* MAC_LLC (TYPE=001) */
+	[VCAP_IS2_HK_MAC_LLC_L2_LLC]            = { 142, 40 },
+	/* MAC_SNAP (TYPE=010) */
+	[VCAP_IS2_HK_MAC_SNAP_L2_SNAP]          = { 142, 40 },
+	/* MAC_ARP (TYPE=011) */
+	[VCAP_IS2_HK_MAC_ARP_SMAC]              = { 46,  48 },
+	[VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK]     = { 94,   1 },
+	[VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK]    = { 95,   1 },
+	[VCAP_IS2_HK_MAC_ARP_LEN_OK]            = { 96,   1 },
+	[VCAP_IS2_HK_MAC_ARP_TARGET_MATCH]      = { 97,   1 },
+	[VCAP_IS2_HK_MAC_ARP_SENDER_MATCH]      = { 98,   1 },
+	[VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN]    = { 99,   1 },
+	[VCAP_IS2_HK_MAC_ARP_OPCODE]            = { 100,  2 },
+	[VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP]        = { 102, 32 },
+	[VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP]        = { 134, 32 },
+	[VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP]        = { 166,  1 },
+	/* IP4_TCP_UDP / IP4_OTHER common */
+	[VCAP_IS2_HK_IP4]                       = { 46,   1 },
+	[VCAP_IS2_HK_L3_FRAGMENT]               = { 47,   1 },
+	[VCAP_IS2_HK_L3_FRAG_OFS_GT0]           = { 48,   1 },
+	[VCAP_IS2_HK_L3_OPTIONS]                = { 49,   1 },
+	[VCAP_IS2_HK_IP4_L3_TTL_GT0]            = { 50,   1 },
+	[VCAP_IS2_HK_L3_TOS]                    = { 51,   8 },
+	[VCAP_IS2_HK_L3_IP4_DIP]                = { 59,  32 },
+	[VCAP_IS2_HK_L3_IP4_SIP]                = { 91,  32 },
+	[VCAP_IS2_HK_DIP_EQ_SIP]                = { 123,  1 },
+	/* IP4_TCP_UDP (TYPE=100) */
+	[VCAP_IS2_HK_TCP]                       = { 124,  1 },
+	[VCAP_IS2_HK_L4_DPORT]                  = { 125, 16 },
+	[VCAP_IS2_HK_L4_SPORT]                  = { 141, 16 },
+	[VCAP_IS2_HK_L4_RNG]                    = { 157,  8 },
+	[VCAP_IS2_HK_L4_SPORT_EQ_DPORT]         = { 165,  1 },
+	[VCAP_IS2_HK_L4_SEQUENCE_EQ0]           = { 166,  1 },
+	[VCAP_IS2_HK_L4_FIN]                    = { 167,  1 },
+	[VCAP_IS2_HK_L4_SYN]                    = { 168,  1 },
+	[VCAP_IS2_HK_L4_RST]                    = { 169,  1 },
+	[VCAP_IS2_HK_L4_PSH]                    = { 170,  1 },
+	[VCAP_IS2_HK_L4_ACK]                    = { 171,  1 },
+	[VCAP_IS2_HK_L4_URG]                    = { 172,  1 },
+	[VCAP_IS2_HK_L4_1588_DOM]               = { 173,  8 },
+	[VCAP_IS2_HK_L4_1588_VER]               = { 181,  4 },
+	/* IP4_OTHER (TYPE=101) */
+	[VCAP_IS2_HK_IP4_L3_PROTO]              = { 124,  8 },
+	[VCAP_IS2_HK_L3_PAYLOAD]                = { 132, 56 },
+	/* IP6_STD (TYPE=110) */
+	[VCAP_IS2_HK_IP6_L3_TTL_GT0]            = { 46,   1 },
+	[VCAP_IS2_HK_L3_IP6_SIP]                = { 47, 128 },
+	[VCAP_IS2_HK_IP6_L3_PROTO]              = { 175,  8 },
+	/* OAM (TYPE=111) */
+	[VCAP_IS2_HK_OAM_MEL_FLAGS]             = { 142,  7 },
+	[VCAP_IS2_HK_OAM_VER]                   = { 149,  5 },
+	[VCAP_IS2_HK_OAM_OPCODE]                = { 154,  8 },
+	[VCAP_IS2_HK_OAM_FLAGS]                 = { 162,  8 },
+	[VCAP_IS2_HK_OAM_MEPID]                 = { 170, 16 },
+	[VCAP_IS2_HK_OAM_CCM_CNTS_EQ0]          = { 186,  1 },
+	[VCAP_IS2_HK_OAM_IS_Y1731]              = { 187,  1 },
+};
+
+static const struct vcap_field vsc7512_vcap_is2_actions[] = {
+	[VCAP_IS2_ACT_HIT_ME_ONCE]              = { 0,   1 },
+	[VCAP_IS2_ACT_CPU_COPY_ENA]             = { 1,   1 },
+	[VCAP_IS2_ACT_CPU_QU_NUM]               = { 2,   3 },
+	[VCAP_IS2_ACT_MASK_MODE]                = { 5,   2 },
+	[VCAP_IS2_ACT_MIRROR_ENA]               = { 7,   1 },
+	[VCAP_IS2_ACT_LRN_DIS]                  = { 8,   1 },
+	[VCAP_IS2_ACT_POLICE_ENA]               = { 9,   1 },
+	[VCAP_IS2_ACT_POLICE_IDX]               = { 10,  9 },
+	[VCAP_IS2_ACT_POLICE_VCAP_ONLY]         = { 19,  1 },
+	[VCAP_IS2_ACT_PORT_MASK]                = { 20, 11 },
+	[VCAP_IS2_ACT_REW_OP]                   = { 31,  9 },
+	[VCAP_IS2_ACT_SMAC_REPLACE_ENA]         = { 40,  1 },
+	[VCAP_IS2_ACT_RSV]                      = { 41,  2 },
+	[VCAP_IS2_ACT_ACL_ID]                   = { 43,  6 },
+	[VCAP_IS2_ACT_HIT_CNT]                  = { 49, 32 },
+};
+
+static struct vcap_props vsc7512_vcap_props[] = {
+	[VCAP_ES0] = {
+		.action_type_width = 0,
+		.action_table = {
+			[ES0_ACTION_TYPE_NORMAL] = {
+				.width = 73,
+				.count = 1,
+			},
+		},
+		.target = S0,
+		.keys = vsc7512_vcap_es0_keys,
+		.actions = vsc7512_vcap_es0_actions,
+	},
+	[VCAP_IS1] = {
+		.action_type_width = 0,
+		.action_table = {
+			[IS1_ACTION_TYPE_NORMAL] = {
+				.width = 78,
+				.count = 4,
+			},
+		},
+		.target = S1,
+		.keys = vsc7512_vcap_is1_keys,
+		.actions = vsc7512_vcap_is1_actions,
+	},
+	[VCAP_IS2] = {
+		.action_type_width = 1,
+		.action_table = {
+			[IS2_ACTION_TYPE_NORMAL] = {
+				.width = 49,
+				.count = 2,
+			},
+			[IS2_ACTION_TYPE_SMAC_SIP] = {
+				.width = 6,
+				.count = 4,
+			},
+		},
+		.target = S2,
+		.keys = vsc7512_vcap_is2_keys,
+		.actions = vsc7512_vcap_is2_actions,
+	},
+};
+
+static struct regmap *vsc7512_regmap_init(struct ocelot *ocelot,
+					  struct resource *res, u32 *offset)
+{
+	struct felix *felix = ocelot_to_felix(ocelot);
+	struct felix_spi_data *felix_spi = container_of(felix, struct
+			felix_spi_data, felix);
+
+	// Use offset instead of res, since we don't have MMIO for SPI
+	*offset = res->start;
+	return felix_spi->map;
+}
+
+static const struct felix_info felix_spi_info = {
+	.target_io_res =		vsc7512_target_io_res,
+	.port_io_res =			vsc7512_port_io_res,
+	.imdio_res =			NULL,
+	.regfields =			vsc7512_regfields,
+	.map =				vsc7512_regmap,
+	.ops =				&vsc7512_ops,
+	.stats_layout =			vsc7512_stats_layout,
+	.num_stats =			ARRAY_SIZE(vsc7512_stats_layout),
+
+	.vcap =				vsc7512_vcap_props,
+
+	// Not sure about these
+	.num_mact_rows =		2048,
+	.num_ports =			6,
+	.num_tx_queues =		OCELOT_NUM_TC,
+	.switch_pci_bar =		0,
+	.imdio_pci_bar =		0,
+
+	// Force ocelot->ptp to 0
+	.ptp_caps =			NULL,
+
+	// No need for this?
+	.mdio_bus_alloc =		NULL,
+	.mdio_bus_free =		NULL,
+	.phylink_validate =		vsc7512_phylink_validate,
+	.prevalidate_phy_mode =		vsc7512_prevalidate_phy_mode,
+	.port_setup_tc =		vsc7512_port_setup_tc,
+	.port_sched_speed_set =		vsc7512_sched_speed_set,
+	.init_regmap =			vsc7512_regmap_init,
+};
+
+static int felix_spi_probe(struct spi_device *spi)
+{
+	struct dsa_switch *ds;
+	struct ocelot *ocelot;
+	struct felix *felix;
+	int err;
+	struct felix_spi_data *felix_spi;
+
+	pr_info("felix_spi: CS: %d M: %d, BPW: %d, MaxSpeed: %d\n",
+		spi->chip_select, spi->mode, spi->bits_per_word,
+		spi->max_speed_hz);
+
+	felix_spi = devm_kzalloc(&spi->dev, sizeof(struct felix_spi_data),
+				 GFP_KERNEL);
+
+	if (!felix_spi)
+		return -ENOMEM;
+
+	dev_set_drvdata(&spi->dev, felix_spi);
+
+	spi->bits_per_word = 8;
+
+	/* TODO: There are a couple of goals to achieve. Step 1: Get the device
+	 * working in slow SPI mode with a fixed bit and byte order. Fixing this
+	 * should allow devm_regmap_init_spi to be used directly, though there
+	 * are penalties incurred. Specifically the bus is running much slower
+	 * than it could, hindering performance.
+	 *
+	 * Operating at a faster SPI rate could boost performance significantly
+	 * with fixed delays or padding bytes. Especially on consecutive reads.
+	 *
+	 * Ideally the SPI bus would be configured to run at the configured
+	 * speed / fastest speed possible with consecutive reads / writes
+	 * enabled, if supported. For reads, we would have to reason about the
+	 * speed vs the delay time or the number of padding bytes.
+	 */
+
+	spi->max_speed_hz = 500000;
+	err = spi_setup(spi);
+	if (err < 0) {
+		dev_err(&spi->dev, "Error %d initializing SPI\n", err);
+		return err;
+	}
+
+	felix_spi->map = devm_regmap_init_spi(spi, &felix_spi_regmap_config);
+
+	if (IS_ERR(felix_spi->map)) {
+		dev_err(&spi->dev, "regmap init failed\n");
+		return PTR_ERR(felix_spi->map);
+	}
+
+	err = felix_spi_init_bus(spi, felix_spi);
+	if (err < 0) {
+		dev_err(&spi->dev, "device init failed: %d\n", err);
+		return err;
+	}
+
+	felix = &felix_spi->felix;
+
+	ocelot = &felix->ocelot;
+	ocelot->dev = &spi->dev;
+
+	// Not sure about this
+	ocelot->num_flooding_pgids = OCELOT_NUM_TC;
+
+	felix->info = &felix_spi_info;
+
+	ocelot->ptp = 0;
+
+	ds = kzalloc(sizeof(*ds), GFP_KERNEL);
+	if (!ds) {
+		err = -ENOMEM;
+		dev_err(&spi->dev, "Failed to allocate DSA switch\n");
+		goto err_alloc_ds;
+	}
+
+	ds->dev = &spi->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;
+
+	err = dsa_register_switch(ds);
+	if (err) {
+		dev_err(&spi->dev, "Failed to register DSA switch: %d\n", err);
+		goto err_register_ds;
+	}
+
+	return 0;
+
+err_register_ds:
+	kfree(ds);
+err_alloc_ds:
+	kfree(felix);
+	return err;
+}
+
+static int felix_spi_remove(struct spi_device *pdev)
+{
+	struct felix *felix;
+
+	felix = spi_get_drvdata(pdev);
+
+	return 0;
+}
+
+const struct of_device_id vsc7512_of_match[] = { { .compatible =
+							   "mscc,vsc7512" },
+						 {} };
+MODULE_DEVICE_TABLE(of, vsc7512_of_match);
+
+static struct spi_driver felix_vsc7512_spi_driver = {
+	.driver = {
+			.name = "vsc7512",
+			.of_match_table = of_match_ptr(vsc7512_of_match),
+		},
+	.probe = felix_spi_probe,
+	.remove = felix_spi_remove,
+};
+module_spi_driver(felix_vsc7512_spi_driver);
+
+MODULE_DESCRIPTION("Felix Switch SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index ad45c1af4be9..8b6e0574f294 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -127,6 +127,7 @@  enum ocelot_target {
 	PTP,
 	GCB,
 	DEV_GMII,
+	DEV_CPUORG,
 	TARGET_MAX,
 };
 
@@ -444,6 +445,20 @@  enum ocelot_reg {
 	PCS1G_TSTPAT_STATUS,
 	DEV_PCS_FX100_CFG,
 	DEV_PCS_FX100_STATUS,
+	DEV_CPUORG_IF_CTRL = DEV_CPUORG << TARGET_OFFSET,
+	DEV_CPUORG_IF_CFGSTAT,
+	DEV_CPUORG_ORG_CFG,
+	DEV_CPUORG_ERR_CNTS,
+	DEV_CPUORG_TIMEOUT_CFG,
+	DEV_CPUORG_GPR,
+	DEV_CPUORG_MAILBOX_SET,
+	DEV_CPUORG_MAILBOX_CLR,
+	DEV_CPUORG_MAILBOX,
+	DEV_CPUORG_SEMA_CFG,
+	DEV_CPUORG_SEMA0,
+	DEV_CPUORG_SEMA0_OWNER,
+	DEV_CPUORG_SEMA1,
+	DEV_CPUORG_SEMA1_OWNER,
 };
 
 enum ocelot_regfield {