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 (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series [RFC,vN,net-next,1/2] net: mscc: ocelot: add support for non-mmio regmaps | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers success CCed 13 of 13 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit fail Errors and warnings before: 8 this patch: 10
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: DT compatible string "mscc,vsc7512" appears un-documented -- check ./Documentation/devicetree/bindings/ WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 101 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns WARNING: please write a paragraph that describes the config symbol fully
netdev/build_allmodconfig_warn fail Errors and warnings before: 8 this patch: 10
netdev/header_inline success Link

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
Vladimir Oltean May 4, 2021, 12:59 p.m. UTC | #2
On Tue, May 04, 2021 at 02:31:34PM +0200, Andrew Lunn 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.
> 
> 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.

Yes, it was my indication to use --subject-prefix="[PATCH vN net-next]",
I was expecting Colin to replace N with 1, 2, 3 etc but I didn't make
that clear enough :)

> > 
> > 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";

Additionally, being a completely off-chip switch, are you sure that the
phy-mode is "internal"?

> > +						fixed-link {
> > +							speed = <1000>;
> > +							full-duplex;
> > +						};
> > +					};
> > +
> > +					port@1 {
> > +						reg = <1>;
> > +						label = "swp1";
> > +						status = "disabled";
> > +					};
> > +
> > +					port@2 {
> > +						reg = <2>;
> > +						label = "swp2";
> > +						status = "disabled";
> > +					};
> > +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).
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.
Andrew Lunn May 4, 2021, 1:36 p.m. UTC | #3
> > 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).
> 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.

Hi Vladimir

I took a quick look at the data sheet. It says in section 2.1.5
Management:

  External access to registers through PCIe, SPI, MIIM, or through an
  Ethernet port with inline Microsemi’s Versatile Register Access
  Protocol (VRAP)

So maybe the basic 7512 support should be separate from how you access
the registers, so that somebody can later add MMIO or MDIO support?

    Andrew

P.S.

I did not know about VRAP. Marvell has something similar. It would be
nice to put together some shared generic code to support
this. Statistics would really benefit from it.
Alexandre Belloni May 4, 2021, 1:55 p.m. UTC | #4
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 | #5
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.
Alexandre Belloni May 4, 2021, 3:08 p.m. UTC | #6
On 04/05/2021 14:36:34+0000, Vladimir Oltean wrote:
> 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.
> 

I've been thinking about defining the .reg_read and .reg_write functions
of the regmap_config to properly abstract accesses and leave the current
ocelot core as it is.

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

VSC7512 and VSC7514 are exactly the same chip. VSC7514 has the MIPS
CPU enabled.

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

Yes, this is the main pain point. You only have one chip select so from
the regmap point of view, there is only one region. I'm wondering
whether we could actually register multiple regmap for a single SPI
device (and then do the offsetting in .reg_read/.reg_write) which would
help.
Colin Foster May 5, 2021, 12:35 p.m. UTC | #7
Hi Vladimir and Andrew,

On Tue, May 04, 2021 at 12:59:43PM +0000, Vladimir Oltean wrote:
> On Tue, May 04, 2021 at 02:31:34PM +0200, Andrew Lunn 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.
> > 
> > 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.
> 
> Yes, it was my indication to use --subject-prefix="[PATCH vN net-next]",
> I was expecting Colin to replace N with 1, 2, 3 etc but I didn't make
> that clear enough :)
> 

Ha. Yes, I suppose I took that too literally. I'll fix it in vO :)

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

I will take this out of the patch, though the feedback is helpful. I
suspect that the end result will be an example in
Documentation/devicetree/bindings/net/dsa/ocelot.txt because there isn't
any commercial hardware available with this functionality, as far as I
know. (If there is I'd love to get my hands on it!)

> > 
> > > +	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";
> 
> Additionally, being a completely off-chip switch, are you sure that the
> phy-mode is "internal"?

No, I'm not sure. I don't remember my justification but I had come
across something that made me believe that there needed to be at least
one "internal phy-mode" for DSA to work. This might actually make sense,
however, since it would be the port internal to the on-chip processor.

My hope was that I would've been able to test this with actual hardware
a couple weeks ago and see everything in action. Unfortunately there 
seems to be a hardware issue on my setup I'll need EE support to
troubleshoot.

When the hardware is finally communicating, I plan to do this type of
functional verification. I've been in charge of writing the interface
layer of this chip family in the past, but I have coworkers who are
familiar with the actual operation who's advice I'll seek.

> 
> > > +						fixed-link {
> > > +							speed = <1000>;
> > > +							full-duplex;
> > > +						};
> > > +					};
> > > +
> > > +					port@1 {
> > > +						reg = <1>;
> > > +						label = "swp1";
> > > +						status = "disabled";
> > > +					};
> > > +
> > > +					port@2 {
> > > +						reg = <2>;
> > > +						label = "swp2";
> > > +						status = "disabled";
> > > +					};
> > > +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).
> 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.
Colin Foster May 5, 2021, 1:20 p.m. UTC | #8
On Tue, May 04, 2021 at 05:08:52PM +0200, Alexandre Belloni wrote:
> 
> 
> On 04/05/2021 14:36:34+0000, Vladimir Oltean wrote:
> > 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.
> > 
> 
> I've been thinking about defining the .reg_read and .reg_write functions
> of the regmap_config to properly abstract accesses and leave the current
> ocelot core as it is.

I considered keeping the regmap definitions from the initial ocelot 
(VSC7514) driver for this. Define a .reg_read and .reg_write to do 
address translation, byte-pad reads, etc. I believe that would require
abandoning devm_regmap_init_spi in favor of a custom implementation.
There were good things I wanted to keep from using init_spi though -
endian checking, possible async capabilities, etc.

drivers/net/dsa/qca8k.c has an example of what I'd start with as far as
defining a custom regmap. It doesn't use SPI, but has custom read /
write functions that could do whatever translation is necessary.

> 
> > 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.
> > 
> 
> VSC7512 and VSC7514 are exactly the same chip. VSC7514 has the MIPS
> CPU enabled.
> 
> > 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.
> > 
> 
> Yes, this is the main pain point. You only have one chip select so from
> the regmap point of view, there is only one region. I'm wondering
> whether we could actually register multiple regmap for a single SPI
> device (and then do the offsetting in .reg_read/.reg_write) which would
> help.

Exactly, this was the main difference. The SPI regmap has no concept of
__iomem, which was a main feature of the underlying ocelot core of
having multiple regmaps. 

So instead of having "offset" in all ocelot accesses, allocate each
regmap in felix_vsc7512_spi.c as part of a struct { u32; regmap; }
during each felix->info->init_regmap call. Use this u32 (or resource, or
whatever it may be) to do the offset in the reg_read / reg_write. That
seems like it should work. This would again require abandoning
devm_regmap_init_spi, which I'm considering more and more...

> 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
Vladimir Oltean May 6, 2021, 10:22 a.m. UTC | #9
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.

> +	help
> +	  This driver supports the VSC75XX chips when controlled through SPI.
> +

Better mention which VSC75XX chips, that XX is pretty unspecific.

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

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

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

> +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),
> +};
> +
> +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).

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

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

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

> +	case PHY_INTERFACE_MODE_2500BASEX:
> +		if (port == 4 || port == 5)
> +			return -EOPNOTSUPP;

Is this copy-paste or is it correct?

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

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

> +	.num_ports =			6,

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

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

> +
> +	// 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:

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

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

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

> +
> +	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? :)

> +
> +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
>
Colin Foster May 6, 2021, 6:34 p.m. UTC | #10
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 | #11
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 | #12
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 {