Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
From: Andrea della Porta @ 2026-04-10 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König, linux-pwm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, Andrea della Porta,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger
In-Reply-To: <cover.1775829499.git.andrea.porta@suse.com>

From: Naushir Patuck <naush@raspberrypi.com>

Add the devicetree binding documentation for the PWM
controller found in the Raspberry Pi RP1 chipset.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
 .../bindings/pwm/raspberrypi,rp1-pwm.yaml     | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml

diff --git a/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
new file mode 100644
index 0000000000000..6f8461d0454f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/raspberrypi,rp1-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Raspberry Pi RP1 PWM controller
+
+maintainers:
+  - Naushir Patuck <naush@raspberrypi.com>
+
+allOf:
+  - $ref: pwm.yaml#
+
+description: |
+  The PWM peripheral is a flexible waveform generator with a
+  variety of operational modes. It has the following features:
+   - four independent output channels
+   - 32-bit counter widths
+   - Seven output generation modes
+   - Optional per-channel output inversion
+   - Optional duty-cycle data FIFO with DMA support
+   - Optional sigma-delta noise shaping engine
+  Serves as a fan speed provider to other nodes for a PWM-connected
+  fan using shared registers (syscon).
+
+properties:
+  compatible:
+    const: raspberrypi,rp1-pwm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#pwm-cells":
+    const: 3
+
+required:
+  - compatible
+  - reg
+  - clocks
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    pwm@98000 {
+      compatible = "raspberrypi,rp1-pwm";
+      reg = <0x98000 0x100>;
+      clocks = <&rp1_clocks 17>;
+      #pwm-cells = <3>;
+    };
-- 
2.35.3


^ permalink raw reply related

* [PATCH v2 0/3] Add RP1 PWM controller support
From: Andrea della Porta @ 2026-04-10 14:09 UTC (permalink / raw)
  To: Uwe Kleine-König, linux-pwm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Broadcom internal kernel review list, Andrea della Porta,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger

This patchset adds support for the PWM controller found on the
Raspberry Pi RP1 southbridge. This is necessary to operate the
cooling fan connected to one of the PWM channels.

The tachometer pin for the fan speed is managed by the firmware 
running on the RP1's M-core. It uses the PHASE2 register
to report the RPM, which is then exported by this driver via
syscon registers. A subsequent patch will add a new device
and driver to read the RPM and export this value via hwmon.
 
Subsequent patches will also add the CPU thermal zone, which
acts as a consumer of the PWM device.

Best regards,
Andrea

CHANGES in V2:

- bindings: added syscon to the description
- bindings: changed additionalProperties to unevaluatedProperties
- dts: reordered pwm node to follow the unit address ordering
- Kconfig/Makefile: renamed config option to PWM_RASPBERRYPI_RP1
- Kconfig: added dependency for syscon/regmap
- driver: added 'Limitations' and 'Datasheet' paragraphs in top comment
- driver: all macros are now prefixed by RP1_PWM_
- driver: implemented waveform callbacks instead of legacy ones
- driver: dropped hwmon device registration for fan speed (this will be
  handled in a separate patch with its own driver reading the value via
  syscon)
- driver: added new regmap/syscon to export the registers.
- driver: added a comment in rp1_pwm_apply_config() to describe what it does
- driver: added a comment to rp1_pwm_request() to define the purpose of the
  last write
- driver: new clk_enabled flag to deal with the clock on suspend/resume path
- driver: clk_rate is now obtained during probe right after exclusive_get()
- driver/Kconfig: module is now static only and has suppress_bind_attr to
  avoid racing with syscon consumer drivers and with syscon unload issue

Naushir Patuck (2):
  dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
  pwm: rp1: Add RP1 PWM controller driver

Stanimir Varbanov (1):
  arm64: dts: broadcom: rpi-5: Add RP1 PWM node

 .../bindings/pwm/raspberrypi,rp1-pwm.yaml     |  54 +++
 .../boot/dts/broadcom/bcm2712-rpi-5-b.dts     |  12 +
 arch/arm64/boot/dts/broadcom/rp1-common.dtsi  |  10 +
 drivers/pwm/Kconfig                           |   9 +
 drivers/pwm/Makefile                          |   1 +
 drivers/pwm/pwm-rp1.c                         | 344 ++++++++++++++++++
 6 files changed, 430 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
 create mode 100644 drivers/pwm/pwm-rp1.c

-- 
2.35.3


^ permalink raw reply

* Re: [PATCH v5 2/4] ASoC: codecs: Add TAS67524 quad-channel audio amplifier driver
From: Mark Brown @ 2026-04-10 14:02 UTC (permalink / raw)
  To: Sen Wang
  Cc: linux-sound, lgirdwood, robh, krzk+dt, conor+dt, devicetree,
	perex, tiwai, shenghao-ding, kevin-lu, baojun.xu, niranjan.hy,
	l-badrinarayanan, devarsht, v-singh1, linux-kernel
In-Reply-To: <20260409220607.686146-3-sen@ti.com>

[-- Attachment #1: Type: text/plain, Size: 445 bytes --]

On Thu, Apr 09, 2026 at 05:06:04PM -0500, Sen Wang wrote:
> The TAS675x (TAS6754, TAS67524) are quad-channel, digital-input
> Class-D amplifiers with an integrated DSP, controlled over I2C.
> They support I2S and TDM serial audio interfaces.

This looks mostly good, but one issue I see is that AFAICT we only stop
fault_check_work during runtime suspsend - if runtime PM is disabled, or
if the driver is removed, the work will be left running.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v3 0/7] arm64: dts: ti: k3-am62a7-sk: Split r5f memory region
From: Markus Schneider-Pargmann @ 2026-04-10 13:54 UTC (permalink / raw)
  To: Vignesh Raghavendra, Rob Herring, Markus Schneider-Pargmann (TI)
  Cc: Bjorn Andersson, Mathieu Poirier, Krzysztof Kozlowski,
	Conor Dooley, Suman Anna, Nishanth Menon, Tero Kristo,
	Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-remoteproc, devicetree,
	linux-kernel, linux-arm-kernel
In-Reply-To: <6a4aecff-f662-4620-8572-3309ea6a81e2@ti.com>

[-- Attachment #1: Type: text/plain, Size: 4163 bytes --]

Hi Vignesh,

On Thu Apr 9, 2026 at 11:46 AM CEST, Vignesh Raghavendra wrote:
> Hi Markus
>
> On 08/04/26 20:33, Rob Herring wrote:
>> On Wed, Mar 18, 2026 at 10:14 AM Markus Schneider-Pargmann (TI)
>> <msp@baylibre.com> wrote:
>>>
>>> Hi,
>>>
>>> Split the firmware memory region in more specific parts so it is better
>>> described where which information is stored. Specifically the LPM metadata
>>> region is important as bootloader software like U-Boot has to know where
>>> that data is to be able to read that data and resume from RAM.
>>>
>>> IO+DDR is a deep sleep state in which a few pins are set to be sensitive
>>> for wakeup while the DDR is kept in self refresh. Everything else is
>>> powered off.
>>>
>>> The changes in this series were suggested as part of the IO+DDR u-boot series:
>>>   https://lore.kernel.org/r/814c211f-a9eb-4311-bb84-165b1a69755f@ti.com
>>>
>>> There are currently no real users of the memory-region that is split in
>>> this series. The size of the memory-region in total stays the same.
>>> The new layout is derived from the software running on the r5f
>>> processor:
>>>   https://github.com/TexasInstruments/mcupsdk-core-k3/blob/k3_main/examples/drivers/ipc/ipc_rpmsg_echo_linux/am62ax-sk/r5fss0-0_freertos/ti-arm-clang/linker.cmd#L172
>>>   https://github.com/TexasInstruments/mcupsdk-core-k3/blob/k3_main/source/drivers/device_manager/sciclient.h#L459
>>>
>>> Additionally the two important devicetree nodes for resuming from IO+DDR
>>> have the bootph-pre-ram flag added as this data needs to be read before
>>> the RAM is in use.
>>>
>>> Best
>>> Markus
>>>
>>> Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
>>> ---
>>> Changes in v3:
>>> - Squash the enforcement of the memory-region-names requirement in the
>>>   patch adding the memory-region-names, as suggested.
>>> - Link to v2: https://lore.kernel.org/r/20260312-topic-am62a-ioddr-dt-v6-19-v2-0-37cb7ceec658@baylibre.com
>>>
>>> Changes in v2:
>>> - Make memory-region-names required if memory-region is present
>>> - Fixup memory-region and memory-region-names conditions. Require either
>>>   2 or 6 regions for memory-region and memory-region-names
>>> - Reword and restructure the binding documentation for memory-region and
>>>   memory-region-names
>>> - Add memory-region-names to all uses of memory-region
>>> - Link to v1: https://lore.kernel.org/r/20260303-topic-am62a-ioddr-dt-v6-19-v1-0-12fe72bb40d2@baylibre.com
>>>
>>> ---
>>> Markus Schneider-Pargmann (TI) (7):
>>>       dt-bindings: remoteproc: k3-r5f: Split up memory regions
>>>       dt-bindings: remoteproc: k3-r5f: Add memory-region-names
>>>       arm64: dts: ti: k3: Use memory-region-names for r5f
>>>       arm64: dts: ti: k3-am62a7-sk: Split r5f memory region
>>>       arm64: dts: ti: k3-am62p5-sk: Split r5f memory region
>>>       arm64: dts: ti: k3-am62a7-sk: Add r5f nodes to pre-ram bootphase
>>>       arm64: dts: ti: k3-am62p5-sk: Add r5f nodes to pre-ram bootphase
>> 
>> TI folks, Please make sure these dts patches are picked up for 7.1.
>> There's now a crap load of warnings in next with the binding change:
>> 
>>      58 (ti,am62-r5fss): r5f@78000000: 'memory-region-names' is a
>> required property
>
> [...]
>
>> If they aren't applied, making  'memory-region-names' required needs
>> to be dropped from the binding.
>>
>
> This breaks DT backward compatibility. Why is memory-region-names now a
> required item and cannot be assumed as "dma" and "firmware" as default?
> Is that intentional (should have at least had a Fixes tag then if the
> original definition was wrong)?

Conor suggested to make the memory-region-names required for easier
distinction of the layouts:
  https://lore.kernel.org/all/20260303-payphone-pancake-b6068c545bc3@spud/

And a follow-up discussion here:
  https://lore.kernel.org/all/20260313-kettle-craftily-aa087e6b74db@spud/

Also I don't think it really breaks backward compatibility. I don't
think there is any user for it and the previous binding documentation
only refers to it as reserved regions.

Best
Markus


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 289 bytes --]

^ permalink raw reply

* Re: [PATCH v2 5/5] arm64: dts: qcom: sdm845-xiaomi-beryllium-tianma: Disable MDSS
From: David Heidelberg @ 2026-04-10 13:51 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sam Day, linux-arm-msm, devicetree, linux-kernel,
	phone-devel, Petr Hodina
In-Reply-To: <73fyiqoxxg2gg5244wkukepdfw6lnguq63si22qsx2ueyna4hu@l4vyykvn43oo>

On 18/03/2026 15:47, Dmitry Baryshkov wrote:
> On Tue, Mar 17, 2026 at 11:12:35PM +0100, David Heidelberg via B4 Relay wrote:
>> From: Petr Hodina <petr.hodina@protonmail.com>
>>
>> Currently the panel driver for tianma is broken.
>> Disable MDSS to prevent DRM taking over the framebuffer.
> 
> I'd still hope that somebody can get it to work. I'd start by using
> prepare_prev_first. Sumit reported the panel as working in 2020 and I
> have no doubts that it was the case. Another option migt be something
> related to the dispcc (see my [1]).

Sadly, the prepare_prev_first didn't solve the issue. The framebuffer -> MDSS 
takover happens, but at greetd (login) loading screen goes black and never restores.

Will need more work.

David>
> [1] https://lore.kernel.org/linux-arm-msm/20260217-sdm845-hdk-v1-1-866f1965fef7@oss.qualcomm.com/

^ permalink raw reply

* Re: [PATCH v2] dt-bindings: sram: Allow multiple-word prefixes to sram subnode
From: Rob Herring @ 2026-04-10 13:45 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel,
	Luca Weiss, Konrad Dybcio
In-Reply-To: <20260409-topic-sram_dtbindings_misc-v2-1-59dc6b0dec45@oss.qualcomm.com>

On Thu, Apr 9, 2026 at 5:04 AM Konrad Dybcio <konradybcio@kernel.org> wrote:
>
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>
> Currently, foo-sram is allowed, but foo-bar-sram is not.
>
> Allow it so that more complex names aren't unnecessarily simplified.
>
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> Changes in v2:
> - Update the regex to disallow names starting with just a hyphen
> - Rewrite (foo|)+ into (foo)*
> - Link to v1: https://lore.kernel.org/r/20260408-topic-sram_dtbindings_misc-v1-1-00556167e136@oss.qualcomm.com
> ---
>  Documentation/devicetree/bindings/sram/sram.yaml | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Applied, thanks.

Rob

^ permalink raw reply

* Re: [PATCH 4/4] arm64: dts: qcom: purwa-iot-evk: Add camss node
From: Krzysztof Kozlowski @ 2026-04-10 13:43 UTC (permalink / raw)
  To: Wenmeng Liu, Bryan O'Donoghue, Robert Foss, Todor Tomov,
	Vladimir Zapolskiy, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <c9cea1d2-a51f-4c38-8ae9-db868b86a928@oss.qualcomm.com>

On 10/04/2026 10:38, Wenmeng Liu wrote:
>>> +&camss {
>>> +    status = "okay";
>>> +};
>>
>> Hmm.
>>
>> I don't agree with this. Enabling the CAMSS node with just the TPG is of 
>> very low value to an end-user and doesn't "prove out" the CSIPHY, TPG 
>> and RDI path - which is the minimum entry point in upstream right now.
>>
>> I don't support less than a sensor at minimum.
>>
>> You guys must have a sensor you've used with this board ?
>>
> 
> Yes we have, but both not upstreamed sensor, we currently have no plans 
> for sensor upstream, perhaps this work will be carried out later.
> 

Then, as Bryan said, this patch should not be sent. It brings no value
to the user of this DTS, because users-with-out-of-tree-patches can have
this as well out of tree.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH 0/2] arm64: dts: renesas: Add missing #mux-state-cells to usb2phy-reset nodes
From: Ulf Hansson @ 2026-04-10 13:42 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Geert Uytterhoeven, tomm.merciai, peda, p.zabel,
	linux-renesas-soc, biju.das.jz, Magnus Damm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel
In-Reply-To: <adZaXSDsv4f9d8Bp@tom-desktop>

On Wed, 8 Apr 2026 at 15:38, Tommaso Merciai
<tommaso.merciai.xr@bp.renesas.com> wrote:
>
> Hi Geert,
> Thanks for your comments.
>
> On Wed, Apr 08, 2026 at 03:07:44PM +0200, Geert Uytterhoeven wrote:
> > Hi Tommaso,
> >
> > On Tue, 7 Apr 2026 at 17:35, Tommaso Merciai
> > <tommaso.merciai.xr@bp.renesas.com> wrote:
> > > The renesas,rzv2h-usb2phy-reset binding schema defines #mux-state-cells as a
> > > required property. Add it to the USB2 PHY reset nodes in the RZ/V2H and RZ/V2N
> > > device trees to fix dtbs_check warnings.
> > >
> > > "arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dtb: usb20phy-reset@15830000 (renesas,r9a09g056-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk-cn15-emmc.dtb: usb20phy-reset@15830000 (renesas,r9a09g056-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk-cn15-sd.dtb: usb20phy-reset@15830000 (renesas,r9a09g056-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dtb: usb20phy-reset@15830000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dtb: usb21phy-reset@15840000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk-cn15-emmc.dtb: usb20phy-reset@15830000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk-cn15-emmc.dtb: usb21phy-reset@15840000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk-cn15-sd.dtb: usb20phy-reset@15830000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > > "arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk-cn15-sd.dtb: usb21phy-reset@15840000 (renesas,r9a09g057-usb2phy-reset): '#mux-state-cells' is a required property"
> > >
> > > Kind Regards,
> > > Tommaso
> > >
> > > Tommaso Merciai (2):
> > >   arm64: dts: renesas: r9a09g057: Add #mux-state-cells to
> > >     usb2{0,1}phyrst
> > >   arm64: dts: renesas: r9a09g056: Add #mux-state-cells to usb20phyrst
> >
> > Does this series supersedes "[PATCH v5 16/22] arm64: dts: renesas:
> > r9a09g056: Add USB2.0 VBUS_SEL mux-controller support"[1] and "[PATCH
> > v5 17/22] arm64: dts: renesas: r9a09g056: Add USB2.0 PHY VBUS internal
> > regulator node"[2]?
>
> Yes, thanks.
>
> From v5 only [0] missing.
> But I think Ulf is planning to pick [0].

I didn't get confirmation from Peter, so I decided to wait.

If there is an rc8 on Monday I can pick it up for v7.1, otherwise
please re-submit when v7.1-rc1 is out.

>
> I will rebase/send RZ/G3E USB2.0 dt patches later.
>
> [0] https://patchwork.kernel.org/project/linux-renesas-soc/patch/cda933586ef7ca119dbbcef45a921c29dd517698.1775047175.git.tommaso.merciai.xr@bp.renesas.com/
>
>
> Kind Regards,
> Tommaso

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH v3 4/7] arm64: dts: ti: k3-am62a7-sk: Split r5f memory region
From: Markus Schneider-Pargmann @ 2026-04-10 13:42 UTC (permalink / raw)
  To: Vignesh Raghavendra, Markus Schneider-Pargmann (TI),
	Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Suman Anna, Nishanth Menon,
	Tero Kristo
  Cc: Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-remoteproc, devicetree,
	linux-kernel, linux-arm-kernel
In-Reply-To: <8673745d-aad2-49d3-b3af-556de7037b69@ti.com>

[-- Attachment #1: Type: text/plain, Size: 1864 bytes --]

Hi Vignesh,

On Fri Apr 10, 2026 at 6:30 AM CEST, Vignesh Raghavendra wrote:
> Hi Markus
>
> On 18/03/26 20:43, Markus Schneider-Pargmann (TI) wrote:
>> Split the firmware memory region in more specific parts so it is better
>> described where to find which information. Specifically the LPM metadata
>> region is important as bootloader software like U-Boot has to know where
>> that data is to be able to read that data.
>> 
>> Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
>> ---
>>  arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 40 +++++++++++++++++++++++++++++++--
>>  1 file changed, 38 insertions(+), 2 deletions(-)
>> 
>> diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
>> index e99bdbc2e0cbdf858f1631096f9c2a086191bab3..c381cc33064ec427751a9ac5bcdff745a9559a89 100644
>> --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
>> +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
>> @@ -59,9 +59,33 @@ wkup_r5fss0_core0_dma_memory_region: memory@9c800000 {
>>  			no-map;
>>  		};
>>  
>> -		wkup_r5fss0_core0_memory_region: memory@9c900000 {
>> +		wkup_r5fss0_core0_ipc_region: memory@9c900000 {
>
> There are still references to wkup_r5fss0_core0_memory_region in
> k3-am62a-ti-ipc-firmware.dtsi (same comment applies to next 2 patches as
> well)
>
> Dont those need to be updated too?

I only updated the sk boards as these are the only ones that have IO+DDR
support that I know works and need the new memory region layout. But
thinking about this, updating the memory region structure shouldn't be a
problem for the other boards either, of course I can't tell if IO+DDR
would work on them, but the new memory region layout shouldn't break
anything.

I can respin the series or do a followup series with modifications for
all boards if you like.

Best
Markus

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 289 bytes --]

^ permalink raw reply

* [PATCH v1 2/5] irqchip: starfive: Convert the word "jh8100" to "jhb100"
From: Changhuang Liang @ 2026-04-10  9:01 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260410090106.622781-1-changhuang.liang@starfivetech.com>

The StarFive JH8100 SoC was discontinued before production. The
newly taped-out JHB100 SoC uses the same interrupt controller IP.
Rename the driver file, Kconfig symbol, and internal references
from "jh8100" to "jhb100" to accurately reflect the supported
hardware.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 MAINTAINERS                                            |  4 ++--
 drivers/irqchip/Kconfig                                |  6 +++---
 drivers/irqchip/Makefile                               |  2 +-
 ...arfive-jh8100-intc.c => irq-starfive-jhb100-intc.c} | 10 +++++-----
 4 files changed, 11 insertions(+), 11 deletions(-)
 rename drivers/irqchip/{irq-starfive-jh8100-intc.c => irq-starfive-jhb100-intc.c} (94%)

diff --git a/MAINTAINERS b/MAINTAINERS
index a2961727e3d1..93cbe852ac0b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25309,11 +25309,11 @@ F:	Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml
 F:	drivers/phy/starfive/phy-jh7110-pcie.c
 F:	drivers/phy/starfive/phy-jh7110-usb.c
 
-STARFIVE JH8100 EXTERNAL INTERRUPT CONTROLLER DRIVER
+STARFIVE JHB100 EXTERNAL INTERRUPT CONTROLLER DRIVER
 M:	Changhuang Liang <changhuang.liang@starfivetech.com>
 S:	Supported
 F:	Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
-F:	drivers/irqchip/irq-starfive-jh8100-intc.c
+F:	drivers/irqchip/irq-starfive-jhb100-intc.c
 
 STATIC BRANCH/CALL
 M:	Peter Zijlstra <peterz@infradead.org>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f07b00d7fef9..697c6b2e006c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -651,13 +651,13 @@ config SIFIVE_PLIC
 	select IRQ_DOMAIN_HIERARCHY
 	select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
 
-config STARFIVE_JH8100_INTC
-	bool "StarFive JH8100 External Interrupt Controller"
+config STARFIVE_JHB100_INTC
+	bool "StarFive JHB100 External Interrupt Controller"
 	depends on ARCH_STARFIVE || COMPILE_TEST
 	default ARCH_STARFIVE
 	select IRQ_DOMAIN_HIERARCHY
 	help
-	  This enables support for the INTC chip found in StarFive JH8100
+	  This enables support for the INTC chip found in StarFive JHB100
 	  SoC.
 
 	  If you don't know what to do here, say Y.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 26aa3b6ec99f..c686caaa4451 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -108,7 +108,7 @@ obj-$(CONFIG_RISCV_APLIC_MSI)		+= irq-riscv-aplic-msi.o
 obj-$(CONFIG_RISCV_IMSIC)		+= irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o
 obj-$(CONFIG_RISCV_RPMI_SYSMSI)		+= irq-riscv-rpmi-sysmsi.o
 obj-$(CONFIG_SIFIVE_PLIC)		+= irq-sifive-plic.o
-obj-$(CONFIG_STARFIVE_JH8100_INTC)	+= irq-starfive-jh8100-intc.o
+obj-$(CONFIG_STARFIVE_JHB100_INTC)	+= irq-starfive-jhb100-intc.o
 obj-$(CONFIG_ACLINT_SSWI)		+= irq-aclint-sswi.o
 obj-$(CONFIG_IMX_IRQSTEER)		+= irq-imx-irqsteer.o
 obj-$(CONFIG_IMX_INTMUX)		+= irq-imx-intmux.o
diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
similarity index 94%
rename from drivers/irqchip/irq-starfive-jh8100-intc.c
rename to drivers/irqchip/irq-starfive-jhb100-intc.c
index bb62ef363d0b..2c9cdad7f377 100644
--- a/drivers/irqchip/irq-starfive-jh8100-intc.c
+++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
@@ -1,13 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * StarFive JH8100 External Interrupt Controller driver
+ * StarFive JHB100 External Interrupt Controller driver
  *
  * Copyright (C) 2023 StarFive Technology Co., Ltd.
  *
  * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
  */
 
-#define pr_fmt(fmt) "irq-starfive-jh8100: " fmt
+#define pr_fmt(fmt) "irq-starfive-jhb100: " fmt
 
 #include <linux/bitops.h>
 #include <linux/clk.h>
@@ -71,7 +71,7 @@ static void starfive_intc_mask(struct irq_data *d)
 }
 
 static struct irq_chip intc_dev = {
-	.name		= "StarFive JH8100 INTC",
+	.name		= "StarFive JHB100 INTC",
 	.irq_unmask	= starfive_intc_unmask,
 	.irq_mask	= starfive_intc_mask,
 };
@@ -199,9 +199,9 @@ static int starfive_intc_probe(struct platform_device *pdev, struct device_node
 }
 
 IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
-IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_probe)
+IRQCHIP_MATCH("starfive,jhb100-intc", starfive_intc_probe)
 IRQCHIP_PLATFORM_DRIVER_END(starfive_intc)
 
-MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller");
+MODULE_DESCRIPTION("StarFive JHB100 External Interrupt Controller");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH v3 2/2] arm64: dts: qcom: milos: Add IMEM node
From: Konrad Dybcio @ 2026-04-10 13:32 UTC (permalink / raw)
  To: Luca Weiss, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio
  Cc: ~postmarketos/upstreaming, phone-devel, linux-arm-msm, devicetree,
	linux-kernel
In-Reply-To: <20260410-milos-imem-v3-2-d215385fa5ab@fairphone.com>

On 4/10/26 9:31 AM, Luca Weiss wrote:
> Add a node for the IMEM found on Milos, which contains pil-reloc-info
> and the modem tables for IPA, among others.
> 
> Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* [PATCH] arm64: dts: qcom: hamoa: Add interconnects to primary USB3 controller
From: Konrad Dybcio @ 2026-04-10 13:32 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Add the interconnect paths to enable drivers to vote on them, ensuring
the necessary NoC bandwidth is available.

This hasn't been done before, as prior to commit 05566ebcc0cd
("clk: qcom: gcc-x1e80100: Keep GCC USB QTB clock always ON"), this
would cause an inexplicable crash upon resume from system suspend.

Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/hamoa.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi
index 051dee076416..233c7ab4c52c 100644
--- a/arch/arm64/boot/dts/qcom/hamoa.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi
@@ -5318,6 +5318,13 @@ usb_1_ss0: usb@a600000 {
 
 			resets = <&gcc GCC_USB30_PRIM_BCR>;
 
+			interconnects = <&usb_south_anoc MASTER_USB3_0 QCOM_ICC_TAG_ALWAYS
+					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
+					<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+					 &config_noc SLAVE_USB3_0 QCOM_ICC_TAG_ACTIVE_ONLY>;
+			interconnect-names = "usb-ddr",
+					     "apps-usb";
+
 			wakeup-source;
 
 			iommus = <&apps_smmu 0x1420 0x0>;

---
base-commit: db7efce4ae23ad5e42f5f55428f529ff62b86fab
change-id: 20260410-topic-hamoa_usb_icc-7f8d5bbef94b

Best regards,
-- 
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>


^ permalink raw reply related

* Re: [PATCH v3 1/2] dt-bindings: sram: Document qcom,milos-imem
From: Rob Herring (Arm) @ 2026-04-10 13:27 UTC (permalink / raw)
  To: Luca Weiss
  Cc: Krzysztof Kozlowski, Konrad Dybcio, linux-arm-msm, linux-kernel,
	Krzysztof Kozlowski, devicetree, phone-devel, Bjorn Andersson,
	Conor Dooley, ~postmarketos/upstreaming
In-Reply-To: <20260410-milos-imem-v3-1-d215385fa5ab@fairphone.com>


On Fri, 10 Apr 2026 09:31:06 +0200, Luca Weiss wrote:
> Add compatible for Milos SoC IMEM.
> 
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
> ---
>  Documentation/devicetree/bindings/sram/sram.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 

Applied, thanks!


^ permalink raw reply

* Re: [PATCH v2] dt-binding: leds: publish common bindings under dual license
From: Rob Herring @ 2026-04-10 13:22 UTC (permalink / raw)
  To: Corvin Köhne
  Cc: linux-kernel, open list:LED SUBSYSTEM, Pavel Machek,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Lee Jones, Krzysztof Kozlowski, Conor Dooley, Corvin Köhne,
	Ashley Towns, Dan Murphy, Gergo Koteles, INAGAKI Hiroshi,
	Jacek Anaszewski, Olliver Schinagl, Pavel Machek,
	Rafał Miłecki, Roderick Colenbrander,
	Krzysztof Kozlowski
In-Reply-To: <20260408062942.7128-1-corvin.koehne@gmail.com>

On Wed, Apr 08, 2026 at 08:29:42AM +0200, Corvin Köhne wrote:
> From: Corvin Köhne <c.koehne@beckhoff.com>
> 
> Changes leds/common.h DT binding header file to be published under GPLv2
> or BSD-2-Clause license terms. This change allows this common LED
> bindings header file to be used in software components as bootloaders
> and OSes that are not published under GPLv2 terms.
> 
> All contributors to leds/common.h file in copy.
> 
> Cc: Ashley Towns <mail@ashleytowns.id.au>

I don't think a one line change is copyright-able work.

> Cc: Dan Murphy <dmurphy@ti.com>

Dan doesn't appear in git blame.

> Cc: Gergo Koteles <soyer@irl.hu>

Another oneliner.

> Cc: INAGAKI Hiroshi <musashino.open@gmail.com>

only 3 lines... Shrug

> Cc: Jacek Anaszewski <jacek.anaszewski@gmail.com>
> Cc: Olliver Schinagl <oliver@schinagl.nl>

Just adding more colors to the list.

> Cc: Pavel Machek <pavel@ucw.cz>
> Cc: Rafał Miłecki <rafal@milecki.pl>
> Cc: Roderick Colenbrander <roderick@gaikai.com>

5 lines of basically the same thing.

So really, I think it is mainly Jacek's and Pavel's acks we need.

Rob

^ permalink raw reply

* Re: [PATCH v6 10/21] dt-bindings: display: renesas,rzg2l-du: Add support for RZ/G3E SoC
From: Tommaso Merciai @ 2026-04-10 13:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: tomm.merciai, geert, linux-renesas-soc, biju.das.jz,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Michael Turquette, Stephen Boyd, Magnus Damm,
	Tomi Valkeinen, dri-devel, devicetree, linux-kernel, linux-clk
In-Reply-To: <20260409132420.GD2634584@killaraus.ideasonboard.com>

Hi Laurent,

On 4/9/26 15:24, Laurent Pinchart wrote:
> On Thu, Apr 09, 2026 at 01:15:18PM +0200, Tommaso Merciai wrote:
>> On 4/8/26 17:00, Laurent Pinchart wrote:
>>> On Wed, Apr 08, 2026 at 04:44:48PM +0200, Tommaso Merciai wrote:
>>>> On 4/8/26 16:16, Laurent Pinchart wrote:
>>>>> On Wed, Apr 08, 2026 at 04:02:14PM +0200, Tommaso Merciai wrote:
>>>>>> On 4/8/26 14:24, Laurent Pinchart wrote:
>>>>>>> On Wed, Apr 08, 2026 at 12:36:55PM +0200, Tommaso Merciai wrote:
>>>>>>>> The RZ/G3E SoC has 2 LCD controllers (LCDC), each containing a Frame
>>>>>>>> Compression Processor (FCPVD), a Video Signal Processor (VSPD), and a
>>>>>>>> Display Unit (DU).
>>>>>>>>
>>>>>>>>      - LCDC0 supports DSI and LVDS (single or dual-channel) outputs.
>>>>>>>>      - LCDC1 supports DSI, LVDS (single-channel), and RGB outputs.
>>>>>>>>
>>>>>>>> Add a new SoC-specific compatible string 'renesas,r9a09g047-du'.
>>>>>>>>
>>>>>>>> Extend patternProperties from "^port@[0-1]$" to "^port@[0-3]$" to
>>>>>>>> allow up to four output ports, and explicitly disable port@2 and port@3
>>>>>>>> for existing SoCs that do not expose them.
>>>>>>>>
>>>>>>>> Describe the four output ports of the RZ/G3E DU:
>>>>>>>>
>>>>>>>>      - port@0: DSI (available on both LCDC instances)
>>>>>>>>      - port@1: DPAD / parallel RGB (LCDC1 only)
>>>>>>>>      - port@2: LVDS channel 0 (LCDC0 only)
>>>>>>>>      - port@3: LVDS channel 1 (available on both LCDC instances)
>>>>>>>>
>>>>>>>> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
>>>>>>>> ---
>>>>>>>> v5->v6:
>>>>>>>>      - Extend patternProperties from "^port@[0-1]$" to "^port@[0-3]$" and
>>>>>>>>        explicitly disable port@2 and port@3 for existing SoCs that do not expose
>>>>>>>>        them.
>>>>>>>>      - Reworked ports numbering + improved/fixed ports descriptions in the
>>>>>>>>        bindings documentation.
>>>>>>>>      - Improved commit body.
>>>>>>>>
>>>>>>>> v4->v5:
>>>>>>>>      - Dropped renesas,id property and updated bindings
>>>>>>>>        accordingly.
>>>>>>>>
>>>>>>>> v2->v3:
>>>>>>>>      - No changes.
>>>>>>>>
>>>>>>>> v2->v3:
>>>>>>>>      - No changes.
>>>>>>>>
>>>>>>>> v1->v2:
>>>>>>>>      - Use single compatible string instead of multiple compatible strings
>>>>>>>>        for the two DU instances, leveraging a 'renesas,id' property to
>>>>>>>>        differentiate between DU0 and DU1.
>>>>>>>>      - Updated commit message accordingly.
>>>>>>>>
>>>>>>>>      .../bindings/display/renesas,rzg2l-du.yaml    | 30 ++++++++++++++++++-
>>>>>>>>      1 file changed, 29 insertions(+), 1 deletion(-)
>>>>>>>>
>>>>>>>> diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
>>>>>>>> index 5add3b832eab..32da0b5ec88c 100644
>>>>>>>> --- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
>>>>>>>> +++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
>>>>>>>> @@ -20,6 +20,7 @@ properties:
>>>>>>>>            - enum:
>>>>>>>>                - renesas,r9a07g043u-du # RZ/G2UL
>>>>>>>>                - renesas,r9a07g044-du # RZ/G2{L,LC}
>>>>>>>> +          - renesas,r9a09g047-du # RZ/G3E
>>>>>>>>                - renesas,r9a09g057-du # RZ/V2H(P)
>>>>>>>>            - items:
>>>>>>>>                - enum:
>>>>>>>> @@ -61,7 +62,7 @@ properties:
>>>>>>>>            model-dependent. Each port shall have a single endpoint.
>>>>>>>>      
>>>>>>>>          patternProperties:
>>>>>>>> -      "^port@[0-1]$":
>>>>>>>> +      "^port@[0-3]$":
>>>>>>>>              $ref: /schemas/graph.yaml#/properties/port
>>>>>>>>              unevaluatedProperties: false
>>>>>>>>      
>>>>>>>> @@ -103,6 +104,8 @@ allOf:
>>>>>>>>                  port@0:
>>>>>>>>                    description: DPI
>>>>>>>>                  port@1: false
>>>>>>>> +            port@2: false
>>>>>>>> +            port@3: false
>>>>>>>>      
>>>>>>>>                required:
>>>>>>>>                  - port@0
>>>>>>>> @@ -119,6 +122,8 @@ allOf:
>>>>>>>>                    description: DSI
>>>>>>>>                  port@1:
>>>>>>>>                    description: DPI
>>>>>>>> +            port@2: false
>>>>>>>> +            port@3: false
>>>>>>>>      
>>>>>>>>                required:
>>>>>>>>                  - port@0
>>>>>>>> @@ -135,9 +140,32 @@ allOf:
>>>>>>>>                  port@0:
>>>>>>>>                    description: DSI
>>>>>>>>                  port@1: false
>>>>>>>> +            port@2: false
>>>>>>>> +            port@3: false
>>>>>>>>      
>>>>>>>>                required:
>>>>>>>>                  - port@0
>>>>>>>> +  - if:
>>>>>>>> +      properties:
>>>>>>>> +        compatible:
>>>>>>>> +          contains:
>>>>>>>> +            const: renesas,r9a09g047-du
>>>>>>>> +    then:
>>>>>>>> +      properties:
>>>>>>>> +        ports:
>>>>>>>> +          properties:
>>>>>>>> +            port@0:
>>>>>>>> +              description: DSI
>>>>>>>> +            port@1:
>>>>>>>> +              description: DPAD
>>>>>>>> +            port@2:
>>>>>>>> +              description: LVDS, Channel 0
>>>>>>>> +            port@3:
>>>>>>>> +              description: LVDS, Channel 1
>>>>>>>> +
>>>>>>>> +          required:
>>>>>>>> +            - port@0
>>>>>>>> +            - port@3
>>>>>>>
>>>>>>> Why are ports 1 and 2 not required ?
>>>>>>
>>>>>> About this we had a similar discussion on v5[0]
>>>>>> We are using the same compatible and:
>>>>>>
>>>>>> - LCDC0 supports DSI and LVDS (single or dual-channel) outputs.
>>>>>> |
>>>>>> --> then has:
>>>>>> 	port@0
>>>>>> 	port@2
>>>>>> 	port@3
>>>>>> 	
>>>>>>
>>>>>>      - LCDC1 supports DSI, LVDS (single-channel), and RGB outputs.
>>>>>> |
>>>>>> --> then has:
>>>>>> 	port@0
>>>>>> 	port@1
>>>>>> 	port@3
>>>>>
>>>>> Ah yes, I forget there are two LCDC instances with different output
>>>>> configurations.
>>>>>
>>>>> Something still looks a bit weird to me though. For LCDC1, which
>>>>> supports a single LVDS channel, you use the port described as the second
>>>>> LVDS channel. Is there a reason not to use port@2 ?
>>>>
>>>> 9.11 Low Voltage Differential Signaling (LVDS)
>>>> 9.11.1.2 Block Diagram
>>>> Figure 9.11-1 shows a block diagram of LVDS.
>>>>
>>>> LCDC1 is connected to LVDS, Channel 1
>>>> For this reason I'm using port@3.
>>>
>>> Re-reading that, I think I've misinterpreted the hardware architecture.
>>> Doesn't the DU have a single output, that is connected the multiple
>>> encoders (LVDS and DSI for LCDC0 and LVDS, DSI and DPI for LCDC1) ? It
>>> seems modelling it with a single port and multiple endpoints would
>>> better match the device.
>>>
>>> For LVDS in particular, I see a single LVDS encoder with two channels,
>>> so there should not be two LVDS output ports in the DU. The two ports
>>> should be on the output of the LVDS device.
>>
>> You are suggesting the following dt architecture:
>>
>> du0: display@16460000 {
>> 	compatible = "renesas,r9a09g047-du";
>> 	reg = <0 0x16460000 0 0x10000>;
>> 	interrupts = <GIC_SPI 882 IRQ_TYPE_LEVEL_HIGH>;
>> 	clocks = <&cpg CPG_MOD 0xed>,
>> 			<&cpg CPG_MOD 0xee>,
>> 			<&cpg CPG_MOD 0xef>;
>> 	clock-names = "aclk", "pclk", "vclk";
>> 	power-domains = <&cpg>;
>> 	resets = <&cpg 0xdc>;
>> 	renesas,vsps = <&vspd0 0>;
>> 	status = "disabled";
>>
>> 	port {
>> 		du0_out_dsi: endpoint@0 {
>> 			reg = <0>;
>> 		};
>>
>> 		du0_out_lvds0: endpoint@2 {
>> 			reg = <2>;
>> 		};
>>
>> 		du0_out_lvds1: endpoint@3 {
>> 			reg = <3>;
>> 		};
>> 	}
>> };
>>
>> du1: display@16490000 {
>> 	compatible = "renesas,r9a09g047-du";
>> 	reg = <0 0x16490000 0 0x10000>;
>> 	interrupts = <GIC_SPI 922 IRQ_TYPE_LEVEL_HIGH>;
>> 	clocks = <&cpg CPG_MOD 0x1a8>,
>> 			<&cpg CPG_MOD 0x1a9>,
>> 			<&cpg CPG_MOD 0x1aa>;
>> 	clock-names = "aclk", "pclk", "vclk";
>> 	power-domains = <&cpg>;
>> 	resets = <&cpg 0x11e>;
>> 	renesas,vsps = <&vspd1 0>;
>> 	status = "disabled";
>>
>> 	port {
>> 		du1_out_dsi: endpoint@0 {
>> 			reg = <0>;
>> 		};
>>
>> 		du1_out_rgb: endpoint@1 {
>> 			reg = <1>;
>> 		};
>>
>> 		du1_out_lvds1: endpoint@3 {
>> 			reg = <3>;
>> 		};
>> 	}
>> };
>>
>>
>> Please correct me if I'm wrong.
> 
> That's right. It would match the hardware, or at least my understanding
> of the hardware based on the documentation. As far as I can tell, each
> DU has a single 24-bit output port connected to multiple encoders.

Thanks for the clarification.

I want to make sure I understand the intended architecture correctly,
because I see a potential conflict between your feedback on the two patches.

For [1], you confirmed the two separate DU nodes (DU0 and DU1) with the
single-port/multi-endpoint model. That maps to two separate platform 
devices, which means two separate DRM devices.

For [2], you suggested:

"you can have one DRM device that covers two LCDCs, with one CRTC each,
both connected to the same DSI encoder. Userspace then selects which
CRTC drives which connector."

Please correct me if I'm wrong but to me these two appear to be 
incompatible. With two separate DRM devices,the DSI encoder and its 
connector can only belong to one of them. Userspace cannot select 
between CRTCs across two DRM devices.

To support the single-DRM-device model you describe, both DU0 and DU1 
would need to be managed by a single driver instance, similar to R-Car 
DU which aggregate multiple LCDC channels into one DRM device.

Using a single DRM device that spawn 2 crtc (1 du dt node ) this use 
case can be tested with the following cmds:

	modetest -M rzg2l-du -s 58@55:800x600-56.25@XR24
	modetest -M rzg2l-du -s 58@56:800x600-56.25@XR24

Could you clarify which architecture is the intended direction?

Option A: Two separate DRM devices (2 DU dt nodes, current approach),
           with the DSI input selected via DT configuration.
           The dynamic vclk selection I implemented still applies,
           but runtime CRTC switching from userspace is not possible.

Option B: A single DRM device aggregating both DU instances (1 DU dt node),
           with two CRTCs both connected to the DSI encoder.


[1] 
https://patchwork.kernel.org/project/linux-renesas-soc/patch/8f814f22ff62dcde6153260e2c8c29a5415c9a89.1775636898.git.tommaso.merciai.xr@bp.renesas.com/
[2] 
https://patchwork.kernel.org/project/linux-renesas-soc/patch/9e0f64dd5e1efb0d27219416121c91a19da96ebd.1775636898.git.tommaso.merciai.xr@bp.renesas.com/

Kind Regards,
Tommaso

> 
>>>>>> Then port@1 is required for DU1 but not for DU0.
>>>>>> Same port@2 is required for DU0 but not for DU1.
>>>>>>
>>>>>> [0] https://patchwork.kernel.org/project/linux-renesas-soc/patch/ca022fdbba5236c36e0cb3095db4c31e8e0cb1b8.1770996493.git.tommaso.merciai.xr@bp.renesas.com/
>>>>>>
>>>>>>>>
>>>>>>>>      examples:
>>>>>>>>        # RZ/G2L DU
> 


^ permalink raw reply

* Re: [PATCH net-next v3 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
From: Lorenzo Bianconi @ 2026-04-10 13:14 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
	ansuelsmth, benjamin.larsson, linux-arm-kernel, linux-mediatek,
	netdev, devicetree
In-Reply-To: <20260410025717.1809904-1-kuba@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 2119 bytes --]

> This is an AI-generated review of your patch. The human sending this
> email says "For your consideration."
> ---
> dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
> 
> This commit introduces the ethernet-port property to model a net_device
> connected via an external arbiter to the GDM3 or GDM4 ports on EN7581
> and AN7583 SoCs.
> 
> > diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
> > index fbe2ddcdd909c..ebbd433e9c9fb 100644
> > --- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
> > +++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
> > @@ -130,6 +130,30 @@ patternProperties:
> >          maximum: 4
> >          description: GMAC port identifier
> >  
> > +      '#address-cells':
> > +        const: 1
> > +      '#size-cells':
> > +        const: 0
> > +
> > +    patternProperties:
> > +      "^ethernet-port@[0-5]$":
> 
> The commit message states that GDM1 and GDM2 do not support connection
> with the external arbiter. However, since this pattern property appears to
> be placed inside the generic ^ethernet@[1-4]$ block, does this allow a
> device tree to incorrectly configure ethernet-port subnodes on ethernet@1
> or ethernet@2 and still pass schema validation?

ack, I will fix it in v4

> 
> Could this be restricted to GDM3 and GDM4, perhaps by splitting the
> patternProperties or using an if/then block based on the reg property?
> 
> > +        type: object
> > +        unevaluatedProperties: false
> > +        $ref: ethernet-controller.yaml#
> 
> Does referencing ethernet-controller.yaml cause a validation conflict here?
> 
> The ethernet-controller.yaml schema enforces a strict nodename pattern
> of ^ethernet(@.*)?$. Since these new nodes use the -port suffix and are
> named ethernet-port@X, will they unconditionally fail the node name
> validation enforced by the referenced schema during dt_binding_check?

ack, I will fix it in v4

Regards,
Lorenzo

> 
> [ ... ]
> -- 
> pw-bot: cr

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH net-next v3 02/12] net: airoha: Set PPE cpu port for GDM2 if loopback is enabled
From: Lorenzo Bianconi @ 2026-04-10 12:52 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
	ansuelsmth, benjamin.larsson, linux-arm-kernel, linux-mediatek,
	netdev, devicetree
In-Reply-To: <20260410025720.1809934-1-kuba@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 3218 bytes --]

> This is an AI-generated review of your patch. The human sending this
> email says "For your consideration."
> ---
> net: airoha: Move airoha_qdma pointer in airoha_gdm_dev struct
> 
> This commit moves the airoha_qdma pointer from the airoha_gdm_port struct
> to the airoha_gdm_dev struct. This ensures the QDMA block used depends on
> the specific net_device WAN/LAN configuration.
> 
> [ ... ]
> 
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> 
> [ ... ]
> 
> > @@ -1497,9 +1498,10 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
> >  	}
> >  }
> >  
> > -static void airoha_update_hw_stats(struct airoha_gdm_port *port)
> > +static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
> >  {
> > -	struct airoha_eth *eth = port->qdma->eth;
> > +	struct airoha_gdm_port *port = dev->port;
> > +	struct airoha_eth *eth = dev->eth;
> >  	u32 val, i = 0;
> >  
> >  	spin_lock(&port->stats.lock);
> 
> This isn't a bug introduced by this patch, but does this spin_lock()
> carry a deadlock risk?
> 
> Because ndo_get_stats64() can be called from atomic context (e.g., from a
> softirq), if a process context caller holds port->stats.lock and is
> interrupted by a softirq on the same CPU that also triggers dev_get_stats(),
> the softirq could spin forever waiting for the lock.
> 
> Should this be upgraded to spin_lock_bh() to ensure local softirqs are
> disabled during the critical section?

I was not able to spot any point in the networking stack where
ndo_get_stats64 callback is run from a softirq. Moreover,
airoha_update_hw_stats() is never executed in any napi in airoha_eth
driver. It seems to me spin_lock() is enough here. Am I missing something?

> 
> [ ... ]
> 
> > --- a/drivers/net/ethernet/airoha/airoha_eth.h
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> > @@ -534,12 +534,12 @@ struct airoha_qdma {
> >  
> >  struct airoha_gdm_dev {
> >  	struct airoha_gdm_port *port;
> > +	struct airoha_qdma *qdma;
> >  	struct net_device *dev;
> >  	struct airoha_eth *eth;
> >  };
> >  
> >  struct airoha_gdm_port {
> > -	struct airoha_qdma *qdma;
> >  	struct airoha_gdm_dev *dev;
> >  	int id;
> 
> This isn't a bug introduced by this patch, but since the qdma pointer
> is being moved to better align with the hardware structure, should 
> qos_sq_bmap, cpu_tx_packets, and fwd_tx_packets be moved to 
> struct airoha_qdma as well?
> 
> Because QDMA1 is shared by all WAN ports (GDM 2, 3, 4) and qos_sq_bmap
> is currently tracked per-port instead of per-QDMA, each WAN port 
> independently allocates QDMA1 channels starting from index 0. Does this 
> cause different ports to overwrite each other's hardware QoS configurations 
> in the shared QDMA1 block?
> 
> Furthermore, cpu_tx_packets and fwd_tx_packets cache per-channel ETS
> hardware counters but are single u64 scalar variables in the port structure.
> When querying stats for multiple channels, do these scalar caches get
> overwritten with values from completely different channels, resulting in
> incorrect delta calculations?

ack, I will fix it in v4.

Regards,
Lorenzo

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH v2] dt-binding: leds: publish common bindings under dual license
From: Rafał Miłecki @ 2026-04-10 11:53 UTC (permalink / raw)
  To: Corvin Köhne
  Cc: linux-kernel, linux-leds, Pavel Machek, devicetree, Lee Jones,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Corvin Köhne,
	Ashley Towns, Dan Murphy, Gergo Koteles, INAGAKI Hiroshi,
	Jacek Anaszewski, Olliver Schinagl, Pavel Machek,
	Roderick Colenbrander, Krzysztof Kozlowski
In-Reply-To: <20260408062942.7128-1-corvin.koehne@gmail.com>

On 2026-04-08 08:29, Corvin Köhne wrote:
> Changes leds/common.h DT binding header file to be published under 
> GPLv2
> or BSD-2-Clause license terms. This change allows this common LED
> bindings header file to be used in software components as bootloaders
> and OSes that are not published under GPLv2 terms.
> 
> All contributors to leds/common.h file in copy.

Acked-by: Rafał Miłecki <rafal@milecki.pl>


> Cc: Ashley Towns <mail@ashleytowns.id.au>
> Cc: Dan Murphy <dmurphy@ti.com>
> Cc: Gergo Koteles <soyer@irl.hu>
> Cc: INAGAKI Hiroshi <musashino.open@gmail.com>
> Cc: Jacek Anaszewski <jacek.anaszewski@gmail.com>
> Cc: Olliver Schinagl <oliver@schinagl.nl>
> Cc: Pavel Machek <pavel@ucw.cz>
> Cc: Rafał Miłecki <rafal@milecki.pl>
> Cc: Roderick Colenbrander <roderick@gaikai.com>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Corvin Köhne <c.koehne@beckhoff.com>
> ---
>  include/dt-bindings/leds/common.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/include/dt-bindings/leds/common.h 
> b/include/dt-bindings/leds/common.h
> index 4f017bea0123..b7bafbaf7df3 100644
> --- a/include/dt-bindings/leds/common.h
> +++ b/include/dt-bindings/leds/common.h
> @@ -1,4 +1,4 @@
> -/* SPDX-License-Identifier: GPL-2.0 */
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
>  /*
>   * This header provides macros for the common LEDs device tree 
> bindings.
>   *

-- 
Rafał Miłecki

^ permalink raw reply

* Re: [PATCH 0/3] arm-smmu-v3: Add PMCG child support and update PMU MMIO mapping
From: Robin Murphy @ 2026-04-10 12:07 UTC (permalink / raw)
  To: Peng Fan
  Cc: Will Deacon, Joerg Roedel, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Mark Rutland, linux-arm-kernel, iommu, devicetree,
	linux-kernel, linux-perf-users, Peng Fan
In-Reply-To: <adZcaEKm3vIYSy3N@shlinux89>

On 08/04/2026 2:47 pm, Peng Fan wrote:
> On Wed, Apr 08, 2026 at 12:15:31PM +0100, Robin Murphy wrote:
>> On 2026-04-08 8:51 am, Peng Fan (OSS) wrote:
>>> This patch series adds proper support for describing and probing the
>>> Arm SMMU v3 PMCG (Performance Monitor Control Group) as a child node of
>>> the SMMU in Devicetree, and updates the relevant drivers accordingly.
>>>
>>> The SMMU v3 architecture allows an optional PMCG block, typically
>>> associated with TCUs, to be implemented within the SMMU register
>>> address space. For example, mmu700 PMCG is at the offset 0x2000 of the
>>> TCU page 0.
>>
>> But what's wrong with the existing binding? Especially given that it even has
>> an upstream user already:
>>
>> https://git.kernel.org/torvalds/c/aef9703dcbf8
>>
>>> Patch 1 updates the SMMU v3 Devicetree binding to allow PMCG child nodes,
>>> referencing the existing arm,smmu-v3-pmcg binding.
>>>
>>> Patch 2 updates the arm-smmu-v3 driver to populate platform devices for
>>> child nodes described in DT once the SMMU probe succeeds.
>>>
>>> Patch 3 updates the SMMUv3 PMU driver to correctly handle MMIO mapping when
>>> PMCG is described as a child node. The PMCG registers occupy a sub-region
>>> of the parent SMMU MMIO window, which is already requested by the SMMU
>>
>> That has not been the case since 52f3fab0067d ("iommu/arm-smmu-v3: Don't
>> reserve implementation defined register space") nearly 6 years ago, where the
>> whole purpose was to support Arm's PMCG implementation properly. What kernel
>> is this based on?
> 
> Seems I am wrong. I thought PMCG is in page 0, so there were resource
> conflicts. I just retest without this patchset, all goes well.
> 
> But from dt perspective, should the TCU PMCG node be child node of
> SMMU node?

No. PMCGs can be used entirely independently of the SMMU itself, and 
while most of the events do relate to SMMU translation and thus aren't 
necessarily meaningful if it's not in use, there are still some which 
can be useful for basic traffic counting, monitoring GPT/translation 
activity from _other_ security states (if observation is delegated to 
Non-Secure) and possibly other things, even if the "main" Non-Secure 
SMMU interface isn't advertised at all. It would be unreasonable to 
require the SMMU node to be present and enabled *and* have a driver to 
populate PMCGs, to monitor events which are outside the scope of that 
driver.

Thanks,
Robin.

^ permalink raw reply

* Re: [PATCH net-next v3 04/12] net: airoha: Rely on net_device pointer in HTB callbacks
From: Lorenzo Bianconi @ 2026-04-10 11:52 UTC (permalink / raw)
  To: Benjamin Larsson
  Cc: Jakub Kicinski, andrew+netdev, davem, edumazet, pabeni, robh,
	krzk+dt, conor+dt, ansuelsmth, linux-arm-kernel, linux-mediatek,
	netdev, devicetree
In-Reply-To: <bd486b55-a8d7-4811-965a-aa1f3dc98e30@genexis.eu>

[-- Attachment #1: Type: text/plain, Size: 2378 bytes --]

> On 10/04/2026 04:57, Jakub Kicinski wrote:
> > This is an AI-generated review of your patch. The human sending this
> > email says "For your consideration."
> > ---
> > net: airoha: Introduce WAN device flag
> > 
> > This commit introduces a WAN flag to specify whether a device is used to
> > transmit or receive WAN or LAN traffic. It enables specifying multiple LAN
> > devices but limits the configuration to a single WAN device.
> > 
> 
> [...]
> 
> > > +		fallthrough;
> > > +	}
> > > +	case AIROHA_GDM2_IDX:
> > > +		/* GDM2 is always used as wan */
> > > +		dev->flags |= PRIV_FLAG_WAN;
> > >   		break;
> > >   	default:
> > >   		break;
> > >   	}
> > > -	for (i = 0; i < eth->soc->num_ppe; i++)
> > > -		airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
> > > +	airoha_dev_set_qdma(dev);
> > > +	airoha_set_macaddr(dev, netdev->dev_addr);
> > 
> > Since the commit message notes it is possible to specify multiple LAN
> > devices, does unconditionally calling airoha_set_macaddr() for each
> > device overwrite the single shared hardware LAN MAC register?
> > 
> > If so, could this cause only the last initialized LAN device to have its MAC
> > correctly programmed into the hardware, leading to dropped unicast
> > packets for the other LAN interfaces?
> > 
> > > +
> > > +	if (!airoha_is_lan_gdm_dev(dev) &&
> > > +	    (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
> > > +		int err;
> > 
> > [ ... ]
> 
> Hi, the frame engine can be configured with a range of wan mac addresses and
> a range of lan mac addresses via registers that set the top 24 bits and then
> 2 registers that form a range of the low 24 bits.
> 
> Documentation says that packets that fall into this mac address range either
> on the lan side or the wan side are to be treated as layer 3 packages and if
> a packet is not then it will be handled as a layer 2 packet.
> 
> The exact implication of this and if it actually matters is unknown. But
> traffic that comes in on an interface that is not matched by an acceleration
> flow is usually forwarded to the cpu for further processing.

The assumption here is the mac addresses are read from the SNAND or eMMC and
they all share the upper bits. I will fix the issue with a separated patch for
net tree.

Regards,
Lorenzo

> 
> MvH
> Benjamin Larsson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH net-next v3 04/12] net: airoha: Rely on net_device pointer in HTB callbacks
From: Lorenzo Bianconi @ 2026-04-10 11:51 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: andrew+netdev, davem, edumazet, pabeni, robh, krzk+dt, conor+dt,
	ansuelsmth, benjamin.larsson, linux-arm-kernel, linux-mediatek,
	netdev, devicetree
In-Reply-To: <20260410025722.1809973-1-kuba@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 2773 bytes --]

> This is an AI-generated review of your patch. The human sending this
> email says "For your consideration."
> ---
> net: airoha: Introduce WAN device flag
> 
> This commit introduces a WAN flag to specify whether a device is used to
> transmit or receive WAN or LAN traffic. It enables specifying multiple LAN
> devices but limits the configuration to a single WAN device.
> 
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 5b0cd37b155e4..9988011dca53f 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -1796,36 +1796,77 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
> 
> [ ... ]
> 
> > +static int airoha_dev_init(struct net_device *netdev)
> > +{
> > +	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> > +	struct airoha_gdm_port *port = dev->port;
> >  
> >  	switch (port->id) {
> >  	case AIROHA_GDM3_IDX:
> > -	case AIROHA_GDM4_IDX:
> > -		/* If GDM2 is active we can't enable loopback */
> > -		if (!eth->ports[1]) {
> > -			int err;
> > +	case AIROHA_GDM4_IDX: {
> > +		struct airoha_eth *eth = dev->eth;
> >  
> > -			err = airoha_set_gdm2_loopback(dev);
> > -			if (err)
> > -				return err;
> > -		}
> > +		if (eth->ports[1] || airoha_get_wan_gdm_dev(eth))
> > +			break;
> 
> If a board's Device Tree enables both GDM3 and GDM4, and GDM2 is not present,
> does this sequence permanently assign the WAN role to GDM3 since it is
> initialized first? Is there a Device Tree property or fallback configuration
> mechanism to allow users to specify GDM4 as the WAN port instead?

This issue will be fixed with following series that allows to dynamically
select LAN or WAN configuration at runtime.

> 
> > +		fallthrough;
> > +	}
> > +	case AIROHA_GDM2_IDX:
> > +		/* GDM2 is always used as wan */
> > +		dev->flags |= PRIV_FLAG_WAN;
> >  		break;
> >  	default:
> >  		break;
> >  	}
> >  
> > -	for (i = 0; i < eth->soc->num_ppe; i++)
> > -		airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
> > +	airoha_dev_set_qdma(dev);
> > +	airoha_set_macaddr(dev, netdev->dev_addr);
> 
> Since the commit message notes it is possible to specify multiple LAN
> devices, does unconditionally calling airoha_set_macaddr() for each
> device overwrite the single shared hardware LAN MAC register?
> 
> If so, could this cause only the last initialized LAN device to have its MAC
> correctly programmed into the hardware, leading to dropped unicast
> packets for the other LAN interfaces?
> 
> > +
> > +	if (!airoha_is_lan_gdm_dev(dev) &&
> > +	    (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
> > +		int err;
> 
> [ ... ]

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH net-next v3 7/9] net: dsa: lan9645x: add mac table integration
From: Jens Emil Schulz Østergaard @ 2026-04-10 11:48 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com>

Add MAC table support, and dsa fdb callback integration. The mactable is
keyed on (vid,mac) and each bucket has 4 slots. A mac table entry
typically points to a PGID index, the first 9 of which represent a front
port.

Mac table entries for L2 multicast will use a PGID containing a group
port mask. For IP multicast entries in the mac table a trick us used,
where the group port mask is packed into the MAC data, exploiting the
fact that the top bits are fixed, and that the number of switch ports is
small enough to fit in the redundant bits.

Therefore, we can avoid using sparse PGID resources for IP multicast
entries in the mac table.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v3:
- avoid mac add/del dealloc when mac table writes fail
- add mact_lock to change ageing time
- dealloc all mac_entries on deinit
- dsa_dump returns mac table timeout error

Changes in v2:
- use a single lock for hw and sw
- remove unused row struct field and define
- remove list element INIT_LIST_HEAD
- consistent use of err vs ret
- remove mutex_lock in init
- use empty initializer { 0 } -> {}
- do not move fwd_domain_lock init to this unit
- add newline to dev_* log statements
---
 drivers/net/dsa/microchip/lan9645x/Makefile        |   1 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c  | 416 +++++++++++++++++++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.c |  95 +++++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.h |  46 +++
 4 files changed, 558 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile
index e049114b3563..70815edca5b9 100644
--- a/drivers/net/dsa/microchip/lan9645x/Makefile
+++ b/drivers/net/dsa/microchip/lan9645x/Makefile
@@ -2,6 +2,7 @@
 obj-$(CONFIG_NET_DSA_MICROCHIP_LAN9645X) += mchp-lan9645x.o
 
 mchp-lan9645x-objs := \
+	lan9645x_mac.o \
 	lan9645x_main.o \
 	lan9645x_npi.o \
 	lan9645x_phylink.o \
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
new file mode 100644
index 000000000000..f516979225ae
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include "lan9645x_main.h"
+
+#define CMD_IDLE		0
+#define CMD_LEARN		1
+#define CMD_FORGET		2
+#define CMD_AGE			3
+#define CMD_GET_NEXT		4
+#define CMD_INIT		5
+#define CMD_READ		6
+#define CMD_WRITE		7
+#define CMD_SYNC_GET_NEXT	8
+
+static bool lan9645x_mact_entry_equal(struct lan9645x_mact_entry *entry,
+				      const unsigned char *mac, u16 vid)
+{
+	/* The hardware table is keyed on (vid,mac) */
+	return entry->common.key.vid == vid &&
+	       ether_addr_equal(mac, entry->common.key.mac);
+}
+
+static struct lan9645x_mact_entry *
+lan9645x_mact_entry_find(struct lan9645x *lan9645x, const unsigned char *mac,
+			 u16 vid)
+{
+	struct lan9645x_mact_entry *entry;
+
+	lockdep_assert_held(&lan9645x->mact_lock);
+
+	list_for_each_entry(entry, &lan9645x->mac_entries, list)
+		if (lan9645x_mact_entry_equal(entry, mac, vid))
+			return entry;
+
+	return NULL;
+}
+
+static struct lan9645x_mact_entry *
+lan9645x_mact_entry_alloc(struct lan9645x *lan9645x, const unsigned char *mac,
+			  u16 vid, u8 pgid, enum macaccess_entry_type type)
+{
+	struct lan9645x_mact_entry *entry;
+
+	entry = kzalloc_obj(*entry);
+	if (!entry)
+		return NULL;
+
+	ether_addr_copy(entry->common.key.mac, mac);
+	entry->common.key.vid = vid;
+	entry->common.pgid = pgid;
+	entry->common.type = type;
+
+	dev_dbg(lan9645x->dev,
+		"mac=%pM vid=%u pgid=%u type=%d\n",
+		entry->common.key.mac, entry->common.key.vid,
+		entry->common.pgid, entry->common.type);
+
+	return entry;
+}
+
+static void lan9645x_mact_entry_dealloc(struct lan9645x *lan9645x,
+					struct lan9645x_mact_entry *entry)
+{
+	if (!entry)
+		return;
+
+	dev_dbg(lan9645x->dev,
+		"mac=%pM vid=%u pgid=%u type=%d\n",
+		entry->common.key.mac, entry->common.key.vid,
+		entry->common.pgid, entry->common.type);
+
+	list_del(&entry->list);
+	kfree(entry);
+}
+
+static int lan9645x_mac_wait_for_completion(struct lan9645x *lan9645x,
+					    u32 *maca)
+{
+	u32 val = 0;
+	int err;
+
+	lockdep_assert_held(&lan9645x->mact_lock);
+
+	err = lan9645x_rd_poll_timeout(lan9645x, ANA_MACACCESS, val,
+				       ANA_MACACCESS_MAC_TABLE_CMD_GET(val) ==
+				       CMD_IDLE);
+	if (err)
+		return err;
+
+	if (maca)
+		*maca = val;
+
+	return 0;
+}
+
+static void lan9645x_mact_parse(u32 machi, u32 maclo, u32 maca,
+				struct lan9645x_mact_common *rentry)
+{
+	u64 addr = ANA_MACHDATA_MACHDATA_GET(machi);
+
+	addr = addr << 32 | maclo;
+	u64_to_ether_addr(addr, rentry->key.mac);
+	rentry->key.vid = ANA_MACHDATA_VID_GET(machi);
+	rentry->pgid = ANA_MACACCESS_DEST_IDX_GET(maca);
+	rentry->type = ANA_MACACCESS_ENTRYTYPE_GET(maca);
+}
+
+static void lan9645x_mac_select(struct lan9645x *lan9645x,
+				const unsigned char *addr, u16 vid)
+{
+	u64 maddr = ether_addr_to_u64(addr);
+
+	lockdep_assert_held(&lan9645x->mact_lock);
+
+	lan_wr(ANA_MACHDATA_VID_SET(vid) |
+	       ANA_MACHDATA_MACHDATA_SET(maddr >> 32),
+	       lan9645x,
+	       ANA_MACHDATA);
+
+	lan_wr(maddr & GENMASK(31, 0),
+	       lan9645x,
+	       ANA_MACLDATA);
+}
+
+static int __lan9645x_mact_forget(struct lan9645x *lan9645x,
+				  const unsigned char mac[ETH_ALEN],
+				  unsigned int vid,
+				  enum macaccess_entry_type type)
+{
+	lockdep_assert_held(&lan9645x->mact_lock);
+
+	lan9645x_mac_select(lan9645x, mac, vid);
+
+	lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
+	       ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_FORGET),
+	       lan9645x,
+	       ANA_MACACCESS);
+
+	return lan9645x_mac_wait_for_completion(lan9645x, NULL);
+}
+
+int lan9645x_mact_forget(struct lan9645x *lan9645x,
+			 const unsigned char mac[ETH_ALEN], unsigned int vid,
+			 enum macaccess_entry_type type)
+{
+	int err;
+
+	mutex_lock(&lan9645x->mact_lock);
+	err = __lan9645x_mact_forget(lan9645x, mac, vid, type);
+	mutex_unlock(&lan9645x->mact_lock);
+
+	return err;
+}
+
+static bool lan9645x_mac_ports_use_cpu(const unsigned char *mac,
+				       enum macaccess_entry_type type)
+{
+	u32 mc_ports;
+
+	switch (type) {
+	case ENTRYTYPE_MACV4:
+		mc_ports = (mac[1] << 8) | mac[2];
+		break;
+	case ENTRYTYPE_MACV6:
+		mc_ports = (mac[0] << 8) | mac[1];
+		break;
+	default:
+		return false;
+	}
+
+	return !!(mc_ports & BIT(CPU_PORT));
+}
+
+static int __lan9645x_mact_learn_cpu_copy(struct lan9645x *lan9645x, int port,
+					  const unsigned char *addr, u16 vid,
+					  enum macaccess_entry_type type,
+					  bool cpu_copy)
+{
+	lockdep_assert_held(&lan9645x->mact_lock);
+
+	lan9645x_mac_select(lan9645x, addr, vid);
+
+	lan_wr(ANA_MACACCESS_VALID_SET(1) |
+	       ANA_MACACCESS_DEST_IDX_SET(port) |
+	       ANA_MACACCESS_MAC_CPU_COPY_SET(cpu_copy) |
+	       ANA_MACACCESS_ENTRYTYPE_SET(type) |
+	       ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_LEARN),
+	       lan9645x, ANA_MACACCESS);
+
+	return lan9645x_mac_wait_for_completion(lan9645x, NULL);
+}
+
+static int __lan9645x_mact_learn(struct lan9645x *lan9645x, int port,
+				 const unsigned char *addr, u16 vid,
+				 enum macaccess_entry_type type)
+{
+	bool cpu_copy = lan9645x_mac_ports_use_cpu(addr, type);
+
+	return __lan9645x_mact_learn_cpu_copy(lan9645x, port, addr, vid, type,
+					      cpu_copy);
+}
+
+int lan9645x_mact_learn(struct lan9645x *lan9645x, int port,
+			const unsigned char *addr, u16 vid,
+			enum macaccess_entry_type type)
+{
+	int err;
+
+	mutex_lock(&lan9645x->mact_lock);
+	err = __lan9645x_mact_learn(lan9645x, port, addr, vid, type);
+	mutex_unlock(&lan9645x->mact_lock);
+
+	return err;
+}
+
+int lan9645x_mact_flush(struct lan9645x *lan9645x, int port)
+{
+	int err;
+
+	mutex_lock(&lan9645x->mact_lock);
+	/* MAC table entries with dst index matching port are aged on scan. */
+	lan_wr(ANA_ANAGEFIL_PID_EN_SET(1) |
+	       ANA_ANAGEFIL_PID_VAL_SET(port),
+	       lan9645x, ANA_ANAGEFIL);
+
+	/* Flushing requires two scans. First sets AGE_FLAG=1, second removes
+	 * entries with AGE_FLAG=1.
+	 */
+	lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_AGE),
+	       lan9645x,
+	       ANA_MACACCESS);
+
+	err = lan9645x_mac_wait_for_completion(lan9645x, NULL);
+	if (err)
+		goto mact_unlock;
+
+	lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_AGE),
+	       lan9645x,
+	       ANA_MACACCESS);
+
+	err = lan9645x_mac_wait_for_completion(lan9645x, NULL);
+
+mact_unlock:
+	lan_wr(0, lan9645x, ANA_ANAGEFIL);
+	mutex_unlock(&lan9645x->mact_lock);
+	return err;
+}
+
+int lan9645x_mact_entry_add(struct lan9645x *lan9645x, int pgid,
+			    const unsigned char *mac, u16 vid)
+{
+	struct lan9645x_mact_entry *entry;
+	int err;
+
+	mutex_lock(&lan9645x->mact_lock);
+
+	/* Users can not move (vid,mac) to a different port, without removing
+	 * the original entry first. But we overwrite entry in HW, and update
+	 * software pgid for good measure.
+	 */
+	entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
+	if (entry) {
+		err = __lan9645x_mact_learn(lan9645x, pgid, mac, vid,
+					    ENTRYTYPE_LOCKED);
+		if (!err)
+			entry->common.pgid = pgid;
+		mutex_unlock(&lan9645x->mact_lock);
+		return err;
+	}
+
+	entry = lan9645x_mact_entry_alloc(lan9645x, mac, vid, pgid,
+					  ENTRYTYPE_LOCKED);
+	if (!entry) {
+		mutex_unlock(&lan9645x->mact_lock);
+		return -ENOMEM;
+	}
+
+	list_add_tail(&entry->list, &lan9645x->mac_entries);
+
+	err = __lan9645x_mact_learn(lan9645x, pgid, mac, vid, ENTRYTYPE_LOCKED);
+	if (err)
+		lan9645x_mact_entry_dealloc(lan9645x, entry);
+
+	mutex_unlock(&lan9645x->mact_lock);
+	return err;
+}
+
+int lan9645x_mact_entry_del(struct lan9645x *lan9645x, int pgid,
+			    const unsigned char *mac, u16 vid)
+{
+	struct lan9645x_mact_entry *entry;
+	int err;
+
+	mutex_lock(&lan9645x->mact_lock);
+
+	entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
+	if (!entry) {
+		mutex_unlock(&lan9645x->mact_lock);
+		return -ENOENT;
+	}
+
+	WARN_ON(entry->common.pgid != pgid);
+	err = __lan9645x_mact_forget(lan9645x, mac, vid, ENTRYTYPE_LOCKED);
+	if (!err)
+		lan9645x_mact_entry_dealloc(lan9645x, entry);
+
+	mutex_unlock(&lan9645x->mact_lock);
+	return err;
+}
+
+void lan9645x_mac_init(struct lan9645x *lan9645x)
+{
+	u32 val;
+
+	mutex_init(&lan9645x->mact_lock);
+	INIT_LIST_HEAD(&lan9645x->mac_entries);
+
+	/* Clear the MAC table */
+	lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_INIT),
+	       lan9645x, ANA_MACACCESS);
+
+	if (lan9645x_rd_poll_timeout(lan9645x, ANA_MACACCESS, val,
+				     ANA_MACACCESS_MAC_TABLE_CMD_GET(val) ==
+				     CMD_IDLE))
+		dev_err(lan9645x->dev, "mac init timeout\n");
+}
+
+void lan9645x_mac_deinit(struct lan9645x *lan9645x)
+{
+	struct lan9645x_mact_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &lan9645x->mac_entries, list)
+		lan9645x_mact_entry_dealloc(lan9645x, entry);
+
+	mutex_destroy(&lan9645x->mact_lock);
+}
+
+int lan9645x_mact_dsa_dump(struct lan9645x *lan9645x, int port,
+			   dsa_fdb_dump_cb_t *cb, void *data)
+{
+	struct lan9645x_mact_entry entry = {};
+	u32 mach, macl, maca;
+	int err = 0;
+	u32 autoage;
+
+	entry.common.type = ENTRYTYPE_NORMAL;
+
+	mutex_lock(&lan9645x->mact_lock);
+
+	/* The aging filter works both for aging scans and GET_NEXT table scans.
+	 * With it, the HW table iteration only stops at entries matching our
+	 * filter. Since DSA calls us for each port on a table dump, this helps
+	 * avoid unnecessary work.
+	 *
+	 * Disable automatic aging temporarily. First save current state.
+	 */
+	autoage = lan_rd(lan9645x, ANA_AUTOAGE);
+
+	/* Disable aging */
+	lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(0),
+		ANA_AUTOAGE_AGE_PERIOD,
+		lan9645x, ANA_AUTOAGE);
+
+	/* Setup filter on our port */
+	lan_wr(ANA_ANAGEFIL_PID_EN_SET(1) |
+	       ANA_ANAGEFIL_PID_VAL_SET(port),
+	       lan9645x, ANA_ANAGEFIL);
+
+	lan_wr(0, lan9645x, ANA_MACHDATA);
+	lan_wr(0, lan9645x, ANA_MACLDATA);
+
+	while (1) {
+		/* NOTE: we rely on mach, macl and type being set correctly in
+		 * the registers from previous round, vis a vis the GET_NEXT
+		 * semantics, so locking entire loop is important.
+		 */
+		lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_GET_NEXT) |
+		       ANA_MACACCESS_ENTRYTYPE_SET(entry.common.type),
+		       lan9645x, ANA_MACACCESS);
+
+		err = lan9645x_mac_wait_for_completion(lan9645x, &maca);
+		if (err)
+			break;
+
+		if (ANA_MACACCESS_VALID_GET(maca) == 0)
+			break;
+
+		mach = lan_rd(lan9645x, ANA_MACHDATA);
+		macl = lan_rd(lan9645x, ANA_MACLDATA);
+
+		lan9645x_mact_parse(mach, macl, maca, &entry.common);
+
+		if (ANA_MACACCESS_DEST_IDX_GET(maca) == port &&
+		    entry.common.type == ENTRYTYPE_NORMAL) {
+			if (entry.common.key.vid > VLAN_MAX)
+				entry.common.key.vid = 0;
+
+			err = cb(entry.common.key.mac, entry.common.key.vid,
+				 false, data);
+			if (err)
+				break;
+		}
+	}
+
+	/* Remove aging filters and restore aging */
+	lan_wr(0, lan9645x, ANA_ANAGEFIL);
+	lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ANA_AUTOAGE_AGE_PERIOD_GET(autoage)),
+		ANA_AUTOAGE_AGE_PERIOD,
+		lan9645x, ANA_AUTOAGE);
+
+	mutex_unlock(&lan9645x->mact_lock);
+
+	return err;
+}
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
index adbdf2007e9f..764f4d6c0571 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
@@ -71,6 +71,7 @@ static void lan9645x_teardown(struct dsa_switch *ds)
 
 	destroy_workqueue(lan9645x->owq);
 	lan9645x_npi_port_deinit(lan9645x, lan9645x->npi);
+	lan9645x_mac_deinit(lan9645x);
 	mutex_destroy(&lan9645x->fwd_domain_lock);
 }
 
@@ -159,6 +160,7 @@ static int lan9645x_setup(struct dsa_switch *ds)
 	err = lan9645x_vlan_init(lan9645x);
 	if (err)
 		return err;
+	lan9645x_mac_init(lan9645x);
 
 	/* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */
 	lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA |
@@ -285,6 +287,8 @@ static int lan9645x_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
 	u32 age_secs = max(1, msecs / MSEC_PER_SEC / 2);
 	struct lan9645x *lan9645x = ds->priv;
 
+	mutex_lock(&lan9645x->mact_lock);
+
 	/* Entry is must suffer two aging scans before it is removed, so it is
 	 * aged after 2*AGE_PERIOD, and the unit is in seconds.
 	 * An age period of 0 disables automatic aging.
@@ -292,6 +296,8 @@ static int lan9645x_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
 	lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(msecs ? age_secs : 0),
 		ANA_AUTOAGE_AGE_PERIOD,
 		lan9645x, ANA_AUTOAGE);
+
+	mutex_unlock(&lan9645x->mact_lock);
 	return 0;
 }
 
@@ -591,6 +597,89 @@ static int lan9645x_port_vlan_del(struct dsa_switch *ds, int port,
 	return lan9645x_vlan_port_del_vlan(p, vlan->vid);
 }
 
+static void lan9645x_port_fast_age(struct dsa_switch *ds, int port)
+{
+	lan9645x_mact_flush(ds->priv, port);
+}
+
+static int lan9645x_fdb_dump(struct dsa_switch *ds, int port,
+			     dsa_fdb_dump_cb_t *cb, void *data)
+{
+	return lan9645x_mact_dsa_dump(ds->priv, port, cb, data);
+}
+
+static struct net_device *lan9645x_db2bridge(struct dsa_db db)
+{
+	switch (db.type) {
+	case DSA_DB_PORT:
+	case DSA_DB_LAG:
+		return NULL;
+	case DSA_DB_BRIDGE:
+		return db.bridge.dev;
+	default:
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+}
+
+static int lan9645x_fdb_add(struct dsa_switch *ds, int port,
+			    const unsigned char *addr, u16 vid,
+			    struct dsa_db db)
+{
+	struct net_device *br = lan9645x_db2bridge(db);
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct lan9645x *lan9645x = ds->priv;
+
+	if (IS_ERR(br))
+		return PTR_ERR(br);
+
+	if (dsa_port_is_cpu(dp) && !br &&
+	    dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
+		return 0;
+
+	if (!vid)
+		vid = lan9645x_vlan_unaware_pvid(!!br);
+
+	if (dsa_port_is_cpu(dp))
+		return lan9645x_mact_learn(lan9645x, PGID_CPU, addr, vid,
+					   ENTRYTYPE_LOCKED);
+
+	return lan9645x_mact_entry_add(lan9645x, port, addr, vid);
+}
+
+static int lan9645x_fdb_del(struct dsa_switch *ds, int port,
+			    const unsigned char *addr, u16 vid,
+			    struct dsa_db db)
+{
+	struct net_device *br = lan9645x_db2bridge(db);
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct lan9645x *lan9645x = ds->priv;
+	int err;
+
+	if (IS_ERR(br))
+		return PTR_ERR(br);
+
+	if (dsa_port_is_cpu(dp) && !br &&
+	    dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
+		return 0;
+
+	if (!vid)
+		vid = lan9645x_vlan_unaware_pvid(!!br);
+
+	if (dsa_port_is_cpu(dp))
+		return lan9645x_mact_forget(lan9645x, addr, vid,
+					    ENTRYTYPE_LOCKED);
+
+	err = lan9645x_mact_entry_del(lan9645x, port, addr, vid);
+	if (err == -ENOENT) {
+		dev_dbg(lan9645x->dev,
+			"fdb not found port=%d addr=%pM vid=%u\n", port, addr,
+			vid);
+		return 0;
+	}
+
+	return err;
+}
+
 static const struct dsa_switch_ops lan9645x_switch_ops = {
 	.get_tag_protocol		= lan9645x_get_tag_protocol,
 
@@ -618,6 +707,12 @@ static const struct dsa_switch_ops lan9645x_switch_ops = {
 	.port_vlan_filtering		= lan9645x_port_vlan_filtering,
 	.port_vlan_add			= lan9645x_port_vlan_add,
 	.port_vlan_del			= lan9645x_port_vlan_del,
+
+	/* MAC table integration */
+	.port_fast_age			= lan9645x_port_fast_age,
+	.port_fdb_dump			= lan9645x_fdb_dump,
+	.port_fdb_add			= lan9645x_fdb_add,
+	.port_fdb_del			= lan9645x_fdb_del,
 };
 
 static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x)
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
index 3c6996e150e4..a5e64218d783 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
@@ -162,6 +162,33 @@ struct lan9645x_vlan {
 	    s_fwd_ena: 1;
 };
 
+/* MAC table entry types.
+ * ENTRYTYPE_NORMAL is subject to aging.
+ * ENTRYTYPE_LOCKED is not subject to aging.
+ * ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast.
+ * ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast.
+ */
+enum macaccess_entry_type {
+	ENTRYTYPE_NORMAL = 0,
+	ENTRYTYPE_LOCKED,
+	ENTRYTYPE_MACV4,
+	ENTRYTYPE_MACV6,
+};
+
+struct lan9645x_mact_common {
+	struct lan9645x_mact_key {
+		u16 vid;
+		u8 mac[ETH_ALEN] __aligned(2);
+	} key;
+	u32 pgid: 6, /* 0-63 general purpose pgids. */
+	    type: 2;
+};
+
+struct lan9645x_mact_entry {
+	struct lan9645x_mact_common common;
+	struct list_head list;
+};
+
 struct lan9645x {
 	struct device *dev;
 	struct dsa_switch *ds;
@@ -185,6 +212,8 @@ struct lan9645x {
 	u16 bridge_mask; /* Mask for bridged ports */
 	u16 bridge_fwd_mask; /* Mask for forwarding bridged ports */
 	struct mutex fwd_domain_lock; /* lock forwarding configuration */
+	struct list_head mac_entries;
+	struct mutex mact_lock; /* lock mac table and mac_entries list */
 
 	/* VLAN entries */
 	struct lan9645x_vlan vlans[VLAN_N_VID];
@@ -377,4 +406,21 @@ int lan9645x_vlan_port_add_vlan(struct lan9645x_port *p, u16 vid, bool pvid,
 int lan9645x_vlan_port_del_vlan(struct lan9645x_port *p, u16 vid);
 void lan9645x_vlan_set_hostmode(struct lan9645x_port *p);
 
+/* MAC table: lan9645x_mac.c */
+int lan9645x_mact_flush(struct lan9645x *lan9645x, int port);
+int lan9645x_mact_learn(struct lan9645x *lan9645x, int port,
+			const unsigned char *addr, u16 vid,
+			enum macaccess_entry_type type);
+int lan9645x_mact_forget(struct lan9645x *lan9645x,
+			 const unsigned char mac[ETH_ALEN], unsigned int vid,
+			 enum macaccess_entry_type type);
+void lan9645x_mac_init(struct lan9645x *lan9645x);
+void lan9645x_mac_deinit(struct lan9645x *lan9645x);
+int lan9645x_mact_dsa_dump(struct lan9645x *lan9645x, int port,
+			   dsa_fdb_dump_cb_t *cb, void *data);
+int lan9645x_mact_entry_del(struct lan9645x *lan9645x, int pgid,
+			    const unsigned char *mac, u16 vid);
+int lan9645x_mact_entry_add(struct lan9645x *lan9645x, int pgid,
+			    const unsigned char *mac, u16 vid);
+
 #endif /* __LAN9645X_MAIN_H__ */

-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next v3 6/9] net: dsa: lan9645x: add vlan support
From: Jens Emil Schulz Østergaard @ 2026-04-10 11:48 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com>

Add support for vlanaware bridge. We reserve vid 4095 for standalone
mode, to implement fdb-isolation. A vlan-unaware bridge uses vid 0.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v3:
- use SET register macros in vlan_hw_wr
- add vlan id bounds check to vlan_del
- return vlan_hw_wr timeout err on init
- move cpu vlan action after bounds check

Changes in v2:
- redesign based on selftests which rely on changing vlan_default_pvid.
  Our HW limitations were too forward. Following Vladimirs changes to
  ocelot VLAN implementation, we now dynamically change egress tag
  configuration, allowing more states.
- selftests are passing, except an expected failure w.r.t ctag/stag
  conformance, which is a hw limitation.
---
 drivers/net/dsa/microchip/lan9645x/Makefile        |   1 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.c |  49 +++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.h |  28 ++
 drivers/net/dsa/microchip/lan9645x/lan9645x_port.c |   3 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c | 378 +++++++++++++++++++++
 5 files changed, 459 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile
index 7cc0ae0ada40..e049114b3563 100644
--- a/drivers/net/dsa/microchip/lan9645x/Makefile
+++ b/drivers/net/dsa/microchip/lan9645x/Makefile
@@ -6,3 +6,4 @@ mchp-lan9645x-objs := \
 	lan9645x_npi.o \
 	lan9645x_phylink.o \
 	lan9645x_port.o \
+	lan9645x_vlan.o \
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
index e709396c2298..adbdf2007e9f 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
@@ -156,6 +156,9 @@ static int lan9645x_setup(struct dsa_switch *ds)
 	}
 
 	mutex_init(&lan9645x->fwd_domain_lock);
+	err = lan9645x_vlan_init(lan9645x);
+	if (err)
+		return err;
 
 	/* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */
 	lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA |
@@ -542,11 +545,52 @@ static void lan9645x_port_bridge_leave(struct dsa_switch *ds, int port,
 		lan9645x->bridge = NULL;
 
 	__lan9645x_port_set_host_flood(lan9645x, port);
+	lan9645x_vlan_set_hostmode(p);
 	lan9645x_update_fwd_mask(lan9645x);
 
 	mutex_unlock(&lan9645x->fwd_domain_lock);
 }
 
+static int lan9645x_port_vlan_filtering(struct dsa_switch *ds, int port,
+					bool enabled,
+					struct netlink_ext_ack *extack)
+{
+	struct lan9645x *lan9645x = ds->priv;
+	struct lan9645x_port *p;
+
+	p = lan9645x_to_port(lan9645x, port);
+	p->vlan_aware = enabled;
+	lan9645x_vlan_port_apply(p);
+
+	return 0;
+}
+
+static int lan9645x_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan,
+				  struct netlink_ext_ack *extack)
+{
+	struct lan9645x *lan9645x = ds->priv;
+	struct lan9645x_port *p;
+	bool pvid, untagged;
+
+	p = lan9645x_to_port(lan9645x, port);
+	pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
+	untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+
+	return lan9645x_vlan_port_add_vlan(p, vlan->vid, pvid, untagged,
+					   extack);
+}
+
+static int lan9645x_port_vlan_del(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan)
+{
+	struct lan9645x *lan9645x = ds->priv;
+	struct lan9645x_port *p;
+
+	p = lan9645x_to_port(lan9645x, port);
+	return lan9645x_vlan_port_del_vlan(p, vlan->vid);
+}
+
 static const struct dsa_switch_ops lan9645x_switch_ops = {
 	.get_tag_protocol		= lan9645x_get_tag_protocol,
 
@@ -569,6 +613,11 @@ static const struct dsa_switch_ops lan9645x_switch_ops = {
 	.port_bridge_leave		= lan9645x_port_bridge_leave,
 	.port_stp_state_set		= lan9645x_port_bridge_stp_state_set,
 	.port_set_host_flood		= lan9645x_port_set_host_flood,
+
+	/* VLAN integration */
+	.port_vlan_filtering		= lan9645x_port_vlan_filtering,
+	.port_vlan_add			= lan9645x_port_vlan_add,
+	.port_vlan_del			= lan9645x_port_vlan_del,
 };
 
 static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x)
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
index 22576bb8dd52..3c6996e150e4 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
@@ -7,6 +7,7 @@
 
 #include <linux/dsa/lan9645x.h>
 #include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
 #include <linux/regmap.h>
 #include <net/dsa.h>
 
@@ -150,6 +151,17 @@ enum lan9645x_vlan_port_tag {
 	LAN9645X_TAG_ALL = 3,
 };
 
+struct lan9645x_vlan {
+	u32 portmask: 10, /* ports 0-8 + CPU_PORT */
+	    untagged: 9, /* ports 0-8 */
+	    src_chk: 1,
+	    mir: 1,
+	    lrn_dis: 1,
+	    prv_vlan: 1,
+	    fld_dis: 1,
+	    s_fwd_ena: 1;
+};
+
 struct lan9645x {
 	struct device *dev;
 	struct dsa_switch *ds;
@@ -174,6 +186,9 @@ struct lan9645x {
 	u16 bridge_fwd_mask; /* Mask for forwarding bridged ports */
 	struct mutex fwd_domain_lock; /* lock forwarding configuration */
 
+	/* VLAN entries */
+	struct lan9645x_vlan vlans[VLAN_N_VID];
+
 	int num_port_dis;
 	bool dd_dis;
 	bool tsn_dis;
@@ -186,6 +201,9 @@ struct lan9645x_port {
 	u8 stp_state;
 	bool learn_ena;
 
+	bool vlan_aware;
+	u16 pvid;
+
 	bool rx_internal_delay;
 	bool tx_internal_delay;
 };
@@ -349,4 +367,14 @@ void lan9645x_phylink_get_caps(struct lan9645x *lan9645x, int port,
 			       struct phylink_config *c);
 void lan9645x_phylink_port_down(struct lan9645x *lan9645x, int port);
 
+/* VLAN lan9645x_vlan.c */
+int lan9645x_vlan_init(struct lan9645x *lan9645x);
+u16 lan9645x_vlan_unaware_pvid(bool is_bridged);
+void lan9645x_vlan_port_apply(struct lan9645x_port *p);
+int lan9645x_vlan_port_add_vlan(struct lan9645x_port *p, u16 vid, bool pvid,
+				bool untagged,
+				struct netlink_ext_ack *extack);
+int lan9645x_vlan_port_del_vlan(struct lan9645x_port *p, u16 vid);
+void lan9645x_vlan_set_hostmode(struct lan9645x_port *p);
+
 #endif /* __LAN9645X_MAIN_H__ */
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
index 394a20ee678f..661cd00465e2 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
@@ -189,5 +189,8 @@ int lan9645x_port_setup(struct dsa_switch *ds, int port)
 		ANA_PORT_CFG_PORTID_VAL,
 		lan9645x, ANA_PORT_CFG(p->chip_port));
 
+	if (p->chip_port != lan9645x->npi)
+		lan9645x_vlan_set_hostmode(p);
+
 	return 0;
 }
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c
new file mode 100644
index 000000000000..c38e918a881d
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include "lan9645x_main.h"
+
+#define VLANACCESS_CMD_IDLE		0
+#define VLANACCESS_CMD_READ		1
+#define VLANACCESS_CMD_WRITE		2
+#define VLANACCESS_CMD_INIT		3
+
+struct lan9645x_vlan_port_info {
+	int untagged;
+	int tagged;
+	u16 untagged_vid;
+};
+
+/* Calculate VLAN state of a port, across all VLANS. */
+static void lan9645x_vlan_port_get_info(struct lan9645x *lan9645x, int port,
+					struct lan9645x_vlan_port_info *info)
+{
+	u16 vid;
+
+	info->untagged = 0;
+	info->tagged = 0;
+	info->untagged_vid = 0;
+
+	for (vid = 1; vid <= VLAN_MAX; vid++) {
+		struct lan9645x_vlan *v = &lan9645x->vlans[vid];
+
+		if (!(v->portmask & BIT(port)))
+			continue;
+
+		if (v->untagged & BIT(port)) {
+			info->untagged++;
+			info->untagged_vid = vid;
+		} else {
+			info->tagged++;
+		}
+
+		/* VLAN composition is invalid, so break early. */
+		if (info->untagged > 1 && info->tagged)
+			break;
+	}
+}
+
+static int lan9645x_vlan_wait_for_completion(struct lan9645x *lan9645x)
+{
+	u32 val;
+
+	return lan9645x_rd_poll_timeout(lan9645x, ANA_VLANACCESS, val,
+					ANA_VLANACCESS_VLAN_TBL_CMD_GET(val) ==
+					VLANACCESS_CMD_IDLE);
+}
+
+static int lan9645x_vlan_hw_wr(struct lan9645x *lan9645x, u16 vid)
+{
+	struct lan9645x_vlan *v = &lan9645x->vlans[vid];
+	bool cpu_dis = !(v->portmask & BIT(CPU_PORT));
+	u32 val;
+	int err;
+
+	val = ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(cpu_dis) |
+	      ANA_VLANTIDX_V_INDEX_SET(vid) |
+	      ANA_VLANTIDX_VLAN_SEC_FWD_ENA_SET(v->s_fwd_ena) |
+	      ANA_VLANTIDX_VLAN_FLOOD_DIS_SET(v->fld_dis) |
+	      ANA_VLANTIDX_VLAN_PRIV_VLAN_SET(v->prv_vlan) |
+	      ANA_VLANTIDX_VLAN_LEARN_DISABLED_SET(v->lrn_dis) |
+	      ANA_VLANTIDX_VLAN_MIRROR_SET(v->mir) |
+	      ANA_VLANTIDX_VLAN_SRC_CHK_SET(v->src_chk);
+
+	lan_wr(val, lan9645x, ANA_VLANTIDX);
+	lan_wr(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_SET(v->portmask),
+	       lan9645x, ANA_VLAN_PORT_MASK);
+	lan_wr(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_WRITE),
+	       lan9645x, ANA_VLANACCESS);
+
+	err = lan9645x_vlan_wait_for_completion(lan9645x);
+	if (err)
+		dev_err(lan9645x->dev, "Vlan set mask failed\n");
+
+	return err;
+}
+
+u16 lan9645x_vlan_unaware_pvid(bool is_bridged)
+{
+	return is_bridged ? UNAWARE_PVID : HOST_PVID;
+}
+
+static u16 lan9645x_vlan_port_get_pvid(struct lan9645x_port *port)
+{
+	bool is_bridged = lan9645x_port_is_bridged(port);
+
+	if (is_bridged && port->vlan_aware)
+		return port->pvid;
+	else
+		return lan9645x_vlan_unaware_pvid(is_bridged);
+}
+
+/* Dynamically choose the egress tagging mode based on the port vlan state:
+ *
+ * Standalone:
+ * TAG_NO_PVID_NO_UNAWARE with PORT_VID=HOST_PVID. This avoids leaking the
+ * internal HOST_PVID tag on ingress mirrored frames while leaving normal
+ * egress frames untagged.
+ *
+ * Bridged, VLAN-aware:
+ *  - N untagged, 0 tagged: TAG_DISABLED
+ *  - 1 untagged, N tagged: TAG_NO_PVID_NO_UNAWARE
+ *  - 0 untagged, N tagged: TAG_ALL
+ *
+ * Bridged, VLAN-unaware:
+ *   TAG_DISABLED
+ */
+static void
+lan9645x_vlan_port_apply_egress(struct lan9645x_port *p,
+				struct lan9645x_vlan_port_info *info)
+{
+	struct lan9645x *lan9645x = p->lan9645x;
+	enum lan9645x_vlan_port_tag tag_cfg;
+	u16 port_vid = UNAWARE_PVID;
+
+	if (!lan9645x_port_is_bridged(p)) {
+		tag_cfg = LAN9645X_TAG_NO_PVID_NO_UNAWARE;
+		port_vid = HOST_PVID;
+	} else if (p->vlan_aware) {
+		struct lan9645x_vlan_port_info _info;
+
+		if (!info) {
+			lan9645x_vlan_port_get_info(lan9645x, p->chip_port,
+						    &_info);
+			info = &_info;
+		}
+
+		if (info->untagged == 1 && info->tagged) {
+			tag_cfg = LAN9645X_TAG_NO_PVID_NO_UNAWARE;
+			port_vid = info->untagged_vid;
+		} else if (info->untagged) {
+			tag_cfg = LAN9645X_TAG_DISABLED;
+		} else {
+			tag_cfg = LAN9645X_TAG_ALL;
+		}
+	} else {
+		tag_cfg = LAN9645X_TAG_DISABLED;
+	}
+
+	/* TAG_TPID_CFG encoding:
+	 *
+	 * 0: Use 0x8100.
+	 * 1: Use 0x88A8.
+	 * 2: Use custom value from PORT_VLAN_CFG.PORT_TPID.
+	 * 3: Use PORT_VLAN_CFG.PORT_TPID, unless ingress tag was a C-tag
+	 *    (EtherType = 0x8100)
+	 *
+	 * Use 3 and PORT_VLAN_CFG.PORT_TPID=0x88a8 to ensure stags are not
+	 * rewritten to ctags on egress.
+	 */
+	lan_rmw(REW_TAG_CFG_TAG_TPID_CFG_SET(3) |
+		REW_TAG_CFG_TAG_CFG_SET(tag_cfg),
+		REW_TAG_CFG_TAG_TPID_CFG |
+		REW_TAG_CFG_TAG_CFG,
+		lan9645x, REW_TAG_CFG(p->chip_port));
+
+	lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021AD) |
+		REW_PORT_VLAN_CFG_PORT_VID_SET(port_vid),
+		REW_PORT_VLAN_CFG_PORT_TPID |
+		REW_PORT_VLAN_CFG_PORT_VID,
+		lan9645x, REW_PORT_VLAN_CFG(p->chip_port));
+}
+
+static void lan9645x_vlan_port_apply_ingress(struct lan9645x_port *p)
+{
+	struct lan9645x *lan9645x = p->lan9645x;
+	u16 pvid;
+	u32 val;
+
+	pvid = lan9645x_vlan_port_get_pvid(p);
+
+	/* Default vlan to classify for untagged frames (may be zero) */
+	val = ANA_VLAN_CFG_VLAN_VID_SET(pvid);
+	if (p->vlan_aware)
+		val |= ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
+		       ANA_VLAN_CFG_VLAN_POP_CNT_SET(1);
+
+	lan_rmw(val,
+		ANA_VLAN_CFG_VLAN_VID |
+		ANA_VLAN_CFG_VLAN_AWARE_ENA |
+		ANA_VLAN_CFG_VLAN_POP_CNT,
+		lan9645x, ANA_VLAN_CFG(p->chip_port));
+
+	/* Drop frames with multicast source address */
+	val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1);
+	if (p->vlan_aware && !pvid)
+		/* If port is vlan-aware and tagged, drop untagged and priority
+		 * tagged frames.
+		 */
+		val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) |
+		       ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) |
+		       ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1);
+
+	lan_wr(val, lan9645x, ANA_DROP_CFG(p->chip_port));
+}
+
+void lan9645x_vlan_port_apply(struct lan9645x_port *p)
+{
+	lan9645x_vlan_port_apply_ingress(p);
+	lan9645x_vlan_port_apply_egress(p, NULL);
+}
+
+static struct lan9645x_vlan *lan9645x_vlan_port_modify(struct lan9645x_port *p,
+						       u16 vid, bool pvid,
+						       bool untagged)
+{
+	struct lan9645x_vlan *v = &p->lan9645x->vlans[vid];
+
+	if (untagged)
+		v->untagged |= BIT(p->chip_port);
+	else
+		v->untagged &= ~BIT(p->chip_port);
+
+	if (pvid)
+		p->pvid = vid;
+	else if (p->pvid == vid)
+		p->pvid = 0;
+
+	return v;
+}
+
+static int lan9645x_vlan_cpu_add(struct lan9645x_port *p, u16 vid, bool pvid,
+				 bool untagged)
+{
+	struct lan9645x_vlan *v;
+
+	v = lan9645x_vlan_port_modify(p, vid, pvid, untagged);
+	v->portmask |= BIT(CPU_PORT) | BIT(p->chip_port);
+	lan9645x_vlan_hw_wr(p->lan9645x, vid);
+	lan9645x_vlan_port_apply_ingress(p);
+
+	return 0;
+}
+
+int lan9645x_vlan_port_add_vlan(struct lan9645x_port *p, u16 vid, bool pvid,
+				bool untagged, struct netlink_ext_ack *extack)
+{
+	struct lan9645x *lan9645x = p->lan9645x;
+	struct lan9645x_vlan_port_info info;
+	struct lan9645x_vlan old_vlan;
+	struct lan9645x_vlan *v;
+	u16 old_pvid;
+
+	/* Kernel VLAN core adds vid 0, which collides with our UNAWARE_PVID.
+	 * We handle priority tagged frames by other means.
+	 */
+	if (!vid)
+		return 0;
+
+	if (vid > VLAN_MAX) {
+		NL_SET_ERR_MSG_MOD(extack, "VLAN range 4094-4095 reserved.");
+		return -EBUSY;
+	}
+
+	if (p->chip_port == lan9645x->npi)
+		return lan9645x_vlan_cpu_add(p, vid, pvid, untagged);
+
+	old_vlan = lan9645x->vlans[vid];
+	old_pvid = p->pvid;
+
+	v = lan9645x_vlan_port_modify(p, vid, pvid, untagged);
+	v->portmask |= BIT(p->chip_port);
+
+	lan9645x_vlan_port_get_info(lan9645x, p->chip_port, &info);
+
+	if (info.untagged > 1 && info.tagged) {
+		*v = old_vlan;
+		p->pvid = old_pvid;
+		NL_SET_ERR_MSG_MOD(extack, "Only support 1 untagged port VLAN");
+		return -EBUSY;
+	}
+
+	lan9645x_vlan_hw_wr(lan9645x, vid);
+	lan9645x_vlan_port_apply_ingress(p);
+	lan9645x_vlan_port_apply_egress(p, &info);
+
+	return 0;
+}
+
+static int lan9645x_vlan_cpu_del(struct lan9645x_port *p, u16 vid)
+{
+	struct lan9645x_vlan *v;
+
+	v = lan9645x_vlan_port_modify(p, vid, false, false);
+	v->portmask &= ~BIT(CPU_PORT) & ~BIT(p->chip_port);
+	lan9645x_vlan_hw_wr(p->lan9645x, vid);
+	lan9645x_vlan_port_apply_ingress(p);
+
+	return 0;
+}
+
+int lan9645x_vlan_port_del_vlan(struct lan9645x_port *p, u16 vid)
+{
+	struct lan9645x *lan9645x = p->lan9645x;
+	struct lan9645x_vlan *v;
+
+	if (!vid)
+		return 0;
+
+	if (vid > VLAN_MAX)
+		return -EBUSY;
+
+	if (p->chip_port == lan9645x->npi)
+		return lan9645x_vlan_cpu_del(p, vid);
+
+	v = lan9645x_vlan_port_modify(p, vid, false, false);
+	v->portmask &= ~BIT(p->chip_port);
+	lan9645x_vlan_hw_wr(lan9645x, vid);
+	lan9645x_vlan_port_apply(p);
+
+	return 0;
+}
+
+void lan9645x_vlan_set_hostmode(struct lan9645x_port *p)
+{
+	p->vlan_aware = false;
+	lan9645x_vlan_port_apply(p);
+}
+
+int lan9645x_vlan_init(struct lan9645x *lan9645x)
+{
+	u32 all_phys_ports, all_ports;
+	u16 port, vid;
+	int err;
+
+	all_phys_ports = GENMASK(lan9645x->num_phys_ports - 1, 0);
+	all_ports = all_phys_ports | BIT(CPU_PORT);
+
+	/* Clear VLAN table, by default all ports are members of all VLANS */
+	lan_wr(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_INIT),
+	       lan9645x, ANA_VLANACCESS);
+
+	err = lan9645x_vlan_wait_for_completion(lan9645x);
+	if (err) {
+		dev_err(lan9645x->dev, "Vlan clear table failed\n");
+		return err;
+	}
+
+	for (vid = 1; vid < VLAN_N_VID; vid++) {
+		err = lan9645x_vlan_hw_wr(lan9645x, vid);
+		if (err)
+			return err;
+	}
+
+	/* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */
+	lan9645x->vlans[HOST_PVID].portmask = all_ports;
+	err = lan9645x_vlan_hw_wr(lan9645x, HOST_PVID);
+	if (err)
+		return err;
+
+	lan9645x->vlans[UNAWARE_PVID].portmask = all_ports;
+	err = lan9645x_vlan_hw_wr(lan9645x, UNAWARE_PVID);
+	if (err)
+		return err;
+
+	/* Configure the CPU port to be vlan aware */
+	lan_wr(ANA_VLAN_CFG_VLAN_VID_SET(UNAWARE_PVID) |
+	       ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
+	       ANA_VLAN_CFG_VLAN_POP_CNT_SET(1),
+	       lan9645x, ANA_VLAN_CFG(CPU_PORT));
+
+	/* Set vlan ingress filter mask to all ports */
+	lan_wr(all_ports, lan9645x, ANA_VLANMASK);
+
+	for (port = 0; port < lan9645x->num_phys_ports; port++) {
+		lan_wr(0, lan9645x, REW_PORT_VLAN_CFG(port));
+		lan_wr(0, lan9645x, REW_TAG_CFG(port));
+	}
+
+	return 0;
+}

-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next v3 3/9] net: dsa: lan9645x: add autogenerated register macros
From: Jens Emil Schulz Østergaard @ 2026-04-10 11:48 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com>

Add autogenerated register macros and update MAINTAINERS file. The
register macros are generated using the same tool we use for lan966x,
sparx5 and lan969x.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v3:
- No changes

Changes in v2:
- Add cpuq registers
---
 MAINTAINERS                                        |    1 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h | 1915 ++++++++++++++++++++
 2 files changed, 1916 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eeb67eed905c..ab660943fcb1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17284,6 +17284,7 @@ M:	UNGLinuxDriver@microchip.com
 L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
+F:	drivers/net/dsa/microchip/lan9645x/*
 F:	include/linux/dsa/lan9645x.h
 F:	net/dsa/tag_lan9645x.c
 
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h
new file mode 100644
index 000000000000..2353478a7520
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h
@@ -0,0 +1,1915 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (c) 2026 Microchip Technology Inc.
+ */
+
+/* This file is autogenerated by cml-utils 2026-03-16 11:57:35 +0100.
+ * Commit ID: dfbf5970b36c4f047cfac4f0ca6049d276354bd7
+ */
+
+#ifndef _LAN9645X_REGS_H_
+#define _LAN9645X_REGS_H_
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+
+enum lan9645x_target {
+	TARGET_AFI = 0,
+	TARGET_ANA = 1,
+	TARGET_CHIP_TOP = 2,
+	TARGET_DEV = 5,
+	TARGET_GCB = 16,
+	TARGET_HSIO = 17,
+	TARGET_QS = 26,
+	TARGET_QSYS = 27,
+	TARGET_REW = 28,
+	TARGET_SYS = 29,
+	NUM_TARGETS = 38
+};
+
+#define __REG(...)    __VA_ARGS__
+
+/*      AFI:PORT_TBL:PORT_FRM_OUT */
+#define AFI_PORT_FRM_OUT(g)       __REG(TARGET_AFI,\
+					0, 1, 6272, g, 11, 8, 0, 0, 1, 4)
+
+#define AFI_PORT_FRM_OUT_FRM_OUT_CNT             GENMASK(26, 16)
+#define AFI_PORT_FRM_OUT_FRM_OUT_CNT_SET(x)\
+	FIELD_PREP(AFI_PORT_FRM_OUT_FRM_OUT_CNT, x)
+#define AFI_PORT_FRM_OUT_FRM_OUT_CNT_GET(x)\
+	FIELD_GET(AFI_PORT_FRM_OUT_FRM_OUT_CNT, x)
+
+/*      AFI:PORT_TBL:PORT_CFG */
+#define AFI_PORT_CFG(g)           __REG(TARGET_AFI,\
+					0, 1, 6272, g, 11, 8, 4, 0, 1, 4)
+
+#define AFI_PORT_CFG_FRM_OUT_MAX                 GENMASK(9, 0)
+#define AFI_PORT_CFG_FRM_OUT_MAX_SET(x)\
+	FIELD_PREP(AFI_PORT_CFG_FRM_OUT_MAX, x)
+#define AFI_PORT_CFG_FRM_OUT_MAX_GET(x)\
+	FIELD_GET(AFI_PORT_CFG_FRM_OUT_MAX, x)
+
+#define AFI_PORT_CFG_FC_SKIP_TTI_INJ             BIT(16)
+#define AFI_PORT_CFG_FC_SKIP_TTI_INJ_SET(x)\
+	FIELD_PREP(AFI_PORT_CFG_FC_SKIP_TTI_INJ, x)
+#define AFI_PORT_CFG_FC_SKIP_TTI_INJ_GET(x)\
+	FIELD_GET(AFI_PORT_CFG_FC_SKIP_TTI_INJ, x)
+
+/*      ANA:ANA:ADVLEARN */
+#define ANA_ADVLEARN              __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 0, 0, 1, 4)
+
+#define ANA_ADVLEARN_VLAN_CHK                    BIT(0)
+#define ANA_ADVLEARN_VLAN_CHK_SET(x)\
+	FIELD_PREP(ANA_ADVLEARN_VLAN_CHK, x)
+#define ANA_ADVLEARN_VLAN_CHK_GET(x)\
+	FIELD_GET(ANA_ADVLEARN_VLAN_CHK, x)
+
+/*      ANA:ANA:VLANMASK */
+#define ANA_VLANMASK              __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 8, 0, 1, 4)
+
+#define ANA_VLANMASK_VLANMASK                    GENMASK(9, 0)
+#define ANA_VLANMASK_VLANMASK_SET(x)\
+	FIELD_PREP(ANA_VLANMASK_VLANMASK, x)
+#define ANA_VLANMASK_VLANMASK_GET(x)\
+	FIELD_GET(ANA_VLANMASK_VLANMASK, x)
+
+/*      ANA:ANA:ANAGEFIL */
+#define ANA_ANAGEFIL              __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 12, 0, 1, 4)
+
+#define ANA_ANAGEFIL_AGE_LOCKED                  BIT(20)
+#define ANA_ANAGEFIL_AGE_LOCKED_SET(x)\
+	FIELD_PREP(ANA_ANAGEFIL_AGE_LOCKED, x)
+#define ANA_ANAGEFIL_AGE_LOCKED_GET(x)\
+	FIELD_GET(ANA_ANAGEFIL_AGE_LOCKED, x)
+
+#define ANA_ANAGEFIL_PID_EN                      BIT(19)
+#define ANA_ANAGEFIL_PID_EN_SET(x)\
+	FIELD_PREP(ANA_ANAGEFIL_PID_EN, x)
+#define ANA_ANAGEFIL_PID_EN_GET(x)\
+	FIELD_GET(ANA_ANAGEFIL_PID_EN, x)
+
+#define ANA_ANAGEFIL_PID_VAL                     GENMASK(18, 14)
+#define ANA_ANAGEFIL_PID_VAL_SET(x)\
+	FIELD_PREP(ANA_ANAGEFIL_PID_VAL, x)
+#define ANA_ANAGEFIL_PID_VAL_GET(x)\
+	FIELD_GET(ANA_ANAGEFIL_PID_VAL, x)
+
+#define ANA_ANAGEFIL_VID_EN                      BIT(13)
+#define ANA_ANAGEFIL_VID_EN_SET(x)\
+	FIELD_PREP(ANA_ANAGEFIL_VID_EN, x)
+#define ANA_ANAGEFIL_VID_EN_GET(x)\
+	FIELD_GET(ANA_ANAGEFIL_VID_EN, x)
+
+#define ANA_ANAGEFIL_VID_VAL                     GENMASK(12, 0)
+#define ANA_ANAGEFIL_VID_VAL_SET(x)\
+	FIELD_PREP(ANA_ANAGEFIL_VID_VAL, x)
+#define ANA_ANAGEFIL_VID_VAL_GET(x)\
+	FIELD_GET(ANA_ANAGEFIL_VID_VAL, x)
+
+/*      ANA:ANA:AUTOAGE */
+#define ANA_AUTOAGE               __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 44, 0, 1, 4)
+
+#define ANA_AUTOAGE_AGE_FAST                     BIT(21)
+#define ANA_AUTOAGE_AGE_FAST_SET(x)\
+	FIELD_PREP(ANA_AUTOAGE_AGE_FAST, x)
+#define ANA_AUTOAGE_AGE_FAST_GET(x)\
+	FIELD_GET(ANA_AUTOAGE_AGE_FAST, x)
+
+#define ANA_AUTOAGE_AGE_PERIOD                   GENMASK(20, 1)
+#define ANA_AUTOAGE_AGE_PERIOD_SET(x)\
+	FIELD_PREP(ANA_AUTOAGE_AGE_PERIOD, x)
+#define ANA_AUTOAGE_AGE_PERIOD_GET(x)\
+	FIELD_GET(ANA_AUTOAGE_AGE_PERIOD, x)
+
+#define ANA_AUTOAGE_AUTOAGE_LOCKED               BIT(0)
+#define ANA_AUTOAGE_AUTOAGE_LOCKED_SET(x)\
+	FIELD_PREP(ANA_AUTOAGE_AUTOAGE_LOCKED, x)
+#define ANA_AUTOAGE_AUTOAGE_LOCKED_GET(x)\
+	FIELD_GET(ANA_AUTOAGE_AUTOAGE_LOCKED, x)
+
+/*      ANA:ANA:FLOODING */
+#define ANA_FLOODING(r)           __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 68, r, 8, 4)
+
+#define ANA_FLOODING_FLD_UNICAST                 GENMASK(17, 12)
+#define ANA_FLOODING_FLD_UNICAST_SET(x)\
+	FIELD_PREP(ANA_FLOODING_FLD_UNICAST, x)
+#define ANA_FLOODING_FLD_UNICAST_GET(x)\
+	FIELD_GET(ANA_FLOODING_FLD_UNICAST, x)
+
+#define ANA_FLOODING_FLD_BROADCAST               GENMASK(11, 6)
+#define ANA_FLOODING_FLD_BROADCAST_SET(x)\
+	FIELD_PREP(ANA_FLOODING_FLD_BROADCAST, x)
+#define ANA_FLOODING_FLD_BROADCAST_GET(x)\
+	FIELD_GET(ANA_FLOODING_FLD_BROADCAST, x)
+
+#define ANA_FLOODING_FLD_MULTICAST               GENMASK(5, 0)
+#define ANA_FLOODING_FLD_MULTICAST_SET(x)\
+	FIELD_PREP(ANA_FLOODING_FLD_MULTICAST, x)
+#define ANA_FLOODING_FLD_MULTICAST_GET(x)\
+	FIELD_GET(ANA_FLOODING_FLD_MULTICAST, x)
+
+/*      ANA:ANA:FLOODING_IPMC */
+#define ANA_FLOODING_IPMC         __REG(TARGET_ANA,\
+					0, 1, 27136, 0, 1, 284, 100, 0, 1, 4)
+
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL           GENMASK(23, 18)
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL_SET(x)\
+	FIELD_PREP(ANA_FLOODING_IPMC_FLD_MC4_CTRL, x)
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL_GET(x)\
+	FIELD_GET(ANA_FLOODING_IPMC_FLD_MC4_CTRL, x)
+
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA           GENMASK(17, 12)
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA_SET(x)\
+	FIELD_PREP(ANA_FLOODING_IPMC_FLD_MC4_DATA, x)
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA_GET(x)\
+	FIELD_GET(ANA_FLOODING_IPMC_FLD_MC4_DATA, x)
+
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL           GENMASK(11, 6)
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL_SET(x)\
+	FIELD_PREP(ANA_FLOODING_IPMC_FLD_MC6_CTRL, x)
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL_GET(x)\
+	FIELD_GET(ANA_FLOODING_IPMC_FLD_MC6_CTRL, x)
+
+#define ANA_FLOODING_IPMC_FLD_MC6_DATA           GENMASK(5, 0)
+#define ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(x)\
+	FIELD_PREP(ANA_FLOODING_IPMC_FLD_MC6_DATA, x)
+#define ANA_FLOODING_IPMC_FLD_MC6_DATA_GET(x)\
+	FIELD_GET(ANA_FLOODING_IPMC_FLD_MC6_DATA, x)
+
+/*      ANA:PGID:PGID */
+#define ANA_PGID(g)               __REG(TARGET_ANA,\
+					0, 1, 27648, g, 90, 8, 0, 0, 1, 4)
+
+#define ANA_PGID_PGID                            GENMASK(9, 0)
+#define ANA_PGID_PGID_SET(x)\
+	FIELD_PREP(ANA_PGID_PGID, x)
+#define ANA_PGID_PGID_GET(x)\
+	FIELD_GET(ANA_PGID_PGID, x)
+
+/*      ANA:PGID:PGID_CFG */
+#define ANA_PGID_CFG(g)           __REG(TARGET_ANA,\
+					0, 1, 27648, g, 90, 8, 4, 0, 1, 4)
+
+#define ANA_PGID_CFG_SAN_ENA                     BIT(4)
+#define ANA_PGID_CFG_SAN_ENA_SET(x)\
+	FIELD_PREP(ANA_PGID_CFG_SAN_ENA, x)
+#define ANA_PGID_CFG_SAN_ENA_GET(x)\
+	FIELD_GET(ANA_PGID_CFG_SAN_ENA, x)
+
+#define ANA_PGID_CFG_CPUQ_DST_PGID               GENMASK(3, 1)
+#define ANA_PGID_CFG_CPUQ_DST_PGID_SET(x)\
+	FIELD_PREP(ANA_PGID_CFG_CPUQ_DST_PGID, x)
+#define ANA_PGID_CFG_CPUQ_DST_PGID_GET(x)\
+	FIELD_GET(ANA_PGID_CFG_CPUQ_DST_PGID, x)
+
+#define ANA_PGID_CFG_OBEY_VLAN                   BIT(0)
+#define ANA_PGID_CFG_OBEY_VLAN_SET(x)\
+	FIELD_PREP(ANA_PGID_CFG_OBEY_VLAN, x)
+#define ANA_PGID_CFG_OBEY_VLAN_GET(x)\
+	FIELD_GET(ANA_PGID_CFG_OBEY_VLAN, x)
+
+/*      ANA:ANA_TABLES:MACHDATA */
+#define ANA_MACHDATA              __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 44, 0, 1, 4)
+
+#define ANA_MACHDATA_VID                         GENMASK(28, 16)
+#define ANA_MACHDATA_VID_SET(x)\
+	FIELD_PREP(ANA_MACHDATA_VID, x)
+#define ANA_MACHDATA_VID_GET(x)\
+	FIELD_GET(ANA_MACHDATA_VID, x)
+
+#define ANA_MACHDATA_MACHDATA                    GENMASK(15, 0)
+#define ANA_MACHDATA_MACHDATA_SET(x)\
+	FIELD_PREP(ANA_MACHDATA_MACHDATA, x)
+#define ANA_MACHDATA_MACHDATA_GET(x)\
+	FIELD_GET(ANA_MACHDATA_MACHDATA, x)
+
+/*      ANA:ANA_TABLES:MACLDATA */
+#define ANA_MACLDATA              __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 48, 0, 1, 4)
+
+/*      ANA:ANA_TABLES:MACACCESS */
+#define ANA_MACACCESS             __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 52, 0, 1, 4)
+
+#define ANA_MACACCESS_CHANGE2SW                  BIT(17)
+#define ANA_MACACCESS_CHANGE2SW_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_CHANGE2SW, x)
+#define ANA_MACACCESS_CHANGE2SW_GET(x)\
+	FIELD_GET(ANA_MACACCESS_CHANGE2SW, x)
+
+#define ANA_MACACCESS_MAC_CPU_COPY               BIT(16)
+#define ANA_MACACCESS_MAC_CPU_COPY_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_MAC_CPU_COPY, x)
+#define ANA_MACACCESS_MAC_CPU_COPY_GET(x)\
+	FIELD_GET(ANA_MACACCESS_MAC_CPU_COPY, x)
+
+#define ANA_MACACCESS_SRC_KILL                   BIT(15)
+#define ANA_MACACCESS_SRC_KILL_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_SRC_KILL, x)
+#define ANA_MACACCESS_SRC_KILL_GET(x)\
+	FIELD_GET(ANA_MACACCESS_SRC_KILL, x)
+
+#define ANA_MACACCESS_IGNORE_VLAN                BIT(14)
+#define ANA_MACACCESS_IGNORE_VLAN_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_IGNORE_VLAN, x)
+#define ANA_MACACCESS_IGNORE_VLAN_GET(x)\
+	FIELD_GET(ANA_MACACCESS_IGNORE_VLAN, x)
+
+#define ANA_MACACCESS_AGED_FLAG                  BIT(13)
+#define ANA_MACACCESS_AGED_FLAG_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_AGED_FLAG, x)
+#define ANA_MACACCESS_AGED_FLAG_GET(x)\
+	FIELD_GET(ANA_MACACCESS_AGED_FLAG, x)
+
+#define ANA_MACACCESS_VALID                      BIT(12)
+#define ANA_MACACCESS_VALID_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_VALID, x)
+#define ANA_MACACCESS_VALID_GET(x)\
+	FIELD_GET(ANA_MACACCESS_VALID, x)
+
+#define ANA_MACACCESS_ENTRYTYPE                  GENMASK(11, 10)
+#define ANA_MACACCESS_ENTRYTYPE_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_ENTRYTYPE, x)
+#define ANA_MACACCESS_ENTRYTYPE_GET(x)\
+	FIELD_GET(ANA_MACACCESS_ENTRYTYPE, x)
+
+#define ANA_MACACCESS_DEST_IDX                   GENMASK(9, 4)
+#define ANA_MACACCESS_DEST_IDX_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_DEST_IDX, x)
+#define ANA_MACACCESS_DEST_IDX_GET(x)\
+	FIELD_GET(ANA_MACACCESS_DEST_IDX, x)
+
+#define ANA_MACACCESS_MAC_TABLE_CMD              GENMASK(3, 0)
+#define ANA_MACACCESS_MAC_TABLE_CMD_SET(x)\
+	FIELD_PREP(ANA_MACACCESS_MAC_TABLE_CMD, x)
+#define ANA_MACACCESS_MAC_TABLE_CMD_GET(x)\
+	FIELD_GET(ANA_MACACCESS_MAC_TABLE_CMD, x)
+
+/*      ANA:ANA_TABLES:MACTINDX */
+#define ANA_MACTINDX              __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 56, 0, 1, 4)
+
+#define ANA_MACTINDX_BUCKET                      GENMASK(12, 11)
+#define ANA_MACTINDX_BUCKET_SET(x)\
+	FIELD_PREP(ANA_MACTINDX_BUCKET, x)
+#define ANA_MACTINDX_BUCKET_GET(x)\
+	FIELD_GET(ANA_MACTINDX_BUCKET, x)
+
+#define ANA_MACTINDX_M_INDEX                     GENMASK(10, 0)
+#define ANA_MACTINDX_M_INDEX_SET(x)\
+	FIELD_PREP(ANA_MACTINDX_M_INDEX, x)
+#define ANA_MACTINDX_M_INDEX_GET(x)\
+	FIELD_GET(ANA_MACTINDX_M_INDEX, x)
+
+/*      ANA:ANA_TABLES:VLAN_PORT_MASK */
+#define ANA_VLAN_PORT_MASK        __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 60, 0, 1, 4)
+
+#define ANA_VLAN_PORT_MASK_VLAN_PORT_MASK        GENMASK(9, 0)
+#define ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_SET(x)\
+	FIELD_PREP(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK, x)
+#define ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_GET(x)\
+	FIELD_GET(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK, x)
+
+/*      ANA:ANA_TABLES:VLANACCESS */
+#define ANA_VLANACCESS            __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 64, 0, 1, 4)
+
+#define ANA_VLANACCESS_VLAN_TBL_CMD              GENMASK(1, 0)
+#define ANA_VLANACCESS_VLAN_TBL_CMD_SET(x)\
+	FIELD_PREP(ANA_VLANACCESS_VLAN_TBL_CMD, x)
+#define ANA_VLANACCESS_VLAN_TBL_CMD_GET(x)\
+	FIELD_GET(ANA_VLANACCESS_VLAN_TBL_CMD, x)
+
+/*      ANA:ANA_TABLES:VLANTIDX */
+#define ANA_VLANTIDX              __REG(TARGET_ANA,\
+					0, 1, 23680, 0, 1, 128, 68, 0, 1, 4)
+
+#define ANA_VLANTIDX_VLAN_PGID_CPU_DIS           BIT(18)
+#define ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_PGID_CPU_DIS, x)
+#define ANA_VLANTIDX_VLAN_PGID_CPU_DIS_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_PGID_CPU_DIS, x)
+
+#define ANA_VLANTIDX_VLAN_SEC_FWD_ENA            BIT(17)
+#define ANA_VLANTIDX_VLAN_SEC_FWD_ENA_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_SEC_FWD_ENA, x)
+#define ANA_VLANTIDX_VLAN_SEC_FWD_ENA_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_SEC_FWD_ENA, x)
+
+#define ANA_VLANTIDX_VLAN_FLOOD_DIS              BIT(16)
+#define ANA_VLANTIDX_VLAN_FLOOD_DIS_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_FLOOD_DIS, x)
+#define ANA_VLANTIDX_VLAN_FLOOD_DIS_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_FLOOD_DIS, x)
+
+#define ANA_VLANTIDX_VLAN_PRIV_VLAN              BIT(15)
+#define ANA_VLANTIDX_VLAN_PRIV_VLAN_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_PRIV_VLAN, x)
+#define ANA_VLANTIDX_VLAN_PRIV_VLAN_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_PRIV_VLAN, x)
+
+#define ANA_VLANTIDX_VLAN_LEARN_DISABLED         BIT(14)
+#define ANA_VLANTIDX_VLAN_LEARN_DISABLED_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_LEARN_DISABLED, x)
+#define ANA_VLANTIDX_VLAN_LEARN_DISABLED_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_LEARN_DISABLED, x)
+
+#define ANA_VLANTIDX_VLAN_MIRROR                 BIT(13)
+#define ANA_VLANTIDX_VLAN_MIRROR_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_MIRROR, x)
+#define ANA_VLANTIDX_VLAN_MIRROR_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_MIRROR, x)
+
+#define ANA_VLANTIDX_VLAN_SRC_CHK                BIT(12)
+#define ANA_VLANTIDX_VLAN_SRC_CHK_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_VLAN_SRC_CHK, x)
+#define ANA_VLANTIDX_VLAN_SRC_CHK_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_VLAN_SRC_CHK, x)
+
+#define ANA_VLANTIDX_V_INDEX                     GENMASK(11, 0)
+#define ANA_VLANTIDX_V_INDEX_SET(x)\
+	FIELD_PREP(ANA_VLANTIDX_V_INDEX, x)
+#define ANA_VLANTIDX_V_INDEX_GET(x)\
+	FIELD_GET(ANA_VLANTIDX_V_INDEX, x)
+
+/*      ANA:PORT:VLAN_CFG */
+#define ANA_VLAN_CFG(g)           __REG(TARGET_ANA,\
+					0, 1, 24576, g, 10, 256, 0, 0, 1, 4)
+
+#define ANA_VLAN_CFG_VLAN_PFC_ENA                BIT(21)
+#define ANA_VLAN_CFG_VLAN_PFC_ENA_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_PFC_ENA, x)
+#define ANA_VLAN_CFG_VLAN_PFC_ENA_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_PFC_ENA, x)
+
+#define ANA_VLAN_CFG_VLAN_AWARE_ENA              BIT(20)
+#define ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_AWARE_ENA, x)
+#define ANA_VLAN_CFG_VLAN_AWARE_ENA_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_AWARE_ENA, x)
+
+#define ANA_VLAN_CFG_VLAN_POP_CNT                GENMASK(19, 18)
+#define ANA_VLAN_CFG_VLAN_POP_CNT_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_POP_CNT, x)
+#define ANA_VLAN_CFG_VLAN_POP_CNT_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_POP_CNT, x)
+
+#define ANA_VLAN_CFG_VLAN_INNER_TAG_ENA          BIT(17)
+#define ANA_VLAN_CFG_VLAN_INNER_TAG_ENA_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_INNER_TAG_ENA, x)
+#define ANA_VLAN_CFG_VLAN_INNER_TAG_ENA_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_INNER_TAG_ENA, x)
+
+#define ANA_VLAN_CFG_VLAN_TAG_TYPE               BIT(16)
+#define ANA_VLAN_CFG_VLAN_TAG_TYPE_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_TAG_TYPE, x)
+#define ANA_VLAN_CFG_VLAN_TAG_TYPE_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_TAG_TYPE, x)
+
+#define ANA_VLAN_CFG_VLAN_PCP                    GENMASK(15, 13)
+#define ANA_VLAN_CFG_VLAN_PCP_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_PCP, x)
+#define ANA_VLAN_CFG_VLAN_PCP_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_PCP, x)
+
+#define ANA_VLAN_CFG_VLAN_DEI                    BIT(12)
+#define ANA_VLAN_CFG_VLAN_DEI_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_DEI, x)
+#define ANA_VLAN_CFG_VLAN_DEI_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_DEI, x)
+
+#define ANA_VLAN_CFG_VLAN_VID                    GENMASK(11, 0)
+#define ANA_VLAN_CFG_VLAN_VID_SET(x)\
+	FIELD_PREP(ANA_VLAN_CFG_VLAN_VID, x)
+#define ANA_VLAN_CFG_VLAN_VID_GET(x)\
+	FIELD_GET(ANA_VLAN_CFG_VLAN_VID, x)
+
+/*      ANA:PORT:DROP_CFG */
+#define ANA_DROP_CFG(g)           __REG(TARGET_ANA,\
+					0, 1, 24576, g, 10, 256, 4, 0, 1, 4)
+
+#define ANA_DROP_CFG_DROP_UNTAGGED_ENA           BIT(6)
+#define ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_UNTAGGED_ENA, x)
+#define ANA_DROP_CFG_DROP_UNTAGGED_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_UNTAGGED_ENA, x)
+
+#define ANA_DROP_CFG_DROP_S_TAGGED_ENA           BIT(5)
+#define ANA_DROP_CFG_DROP_S_TAGGED_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_S_TAGGED_ENA, x)
+#define ANA_DROP_CFG_DROP_S_TAGGED_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_S_TAGGED_ENA, x)
+
+#define ANA_DROP_CFG_DROP_C_TAGGED_ENA           BIT(4)
+#define ANA_DROP_CFG_DROP_C_TAGGED_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_C_TAGGED_ENA, x)
+#define ANA_DROP_CFG_DROP_C_TAGGED_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_C_TAGGED_ENA, x)
+
+#define ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA      BIT(3)
+#define ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA, x)
+#define ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA, x)
+
+#define ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA      BIT(2)
+#define ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA, x)
+#define ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA, x)
+
+#define ANA_DROP_CFG_DROP_NULL_MAC_ENA           BIT(1)
+#define ANA_DROP_CFG_DROP_NULL_MAC_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_NULL_MAC_ENA, x)
+#define ANA_DROP_CFG_DROP_NULL_MAC_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_NULL_MAC_ENA, x)
+
+#define ANA_DROP_CFG_DROP_MC_SMAC_ENA            BIT(0)
+#define ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(x)\
+	FIELD_PREP(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x)
+#define ANA_DROP_CFG_DROP_MC_SMAC_ENA_GET(x)\
+	FIELD_GET(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x)
+
+/*      ANA:PORT:CPU_FWD_CFG */
+#define ANA_CPU_FWD_CFG(g)        __REG(TARGET_ANA,\
+					0, 1, 24576, g, 10, 256, 96, 0, 1, 4)
+
+#define ANA_CPU_FWD_CFG_NO_HSR_REDIR_ENA         BIT(9)
+#define ANA_CPU_FWD_CFG_NO_HSR_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_NO_HSR_REDIR_ENA, x)
+#define ANA_CPU_FWD_CFG_NO_HSR_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_NO_HSR_REDIR_ENA, x)
+
+#define ANA_CPU_FWD_CFG_SPV_COPY_ENA             BIT(8)
+#define ANA_CPU_FWD_CFG_SPV_COPY_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_SPV_COPY_ENA, x)
+#define ANA_CPU_FWD_CFG_SPV_COPY_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_SPV_COPY_ENA, x)
+
+#define ANA_CPU_FWD_CFG_VRAP_REDIR_ENA           BIT(7)
+#define ANA_CPU_FWD_CFG_VRAP_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_VRAP_REDIR_ENA, x)
+#define ANA_CPU_FWD_CFG_VRAP_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_VRAP_REDIR_ENA, x)
+
+#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA            BIT(6)
+#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x)
+#define ANA_CPU_FWD_CFG_MLD_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_MLD_REDIR_ENA, x)
+
+#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA           BIT(5)
+#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x)
+#define ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA, x)
+
+#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA       BIT(4)
+#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x)
+#define ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, x)
+
+#define ANA_CPU_FWD_CFG_SRC_COPY_ENA             BIT(3)
+#define ANA_CPU_FWD_CFG_SRC_COPY_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_SRC_COPY_ENA, x)
+#define ANA_CPU_FWD_CFG_SRC_COPY_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_SRC_COPY_ENA, x)
+
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_DROP_ENA       BIT(2)
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_DROP_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_ALLBRIDGE_DROP_ENA, x)
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_DROP_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_ALLBRIDGE_DROP_ENA, x)
+
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_REDIR_ENA      BIT(1)
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_ALLBRIDGE_REDIR_ENA, x)
+#define ANA_CPU_FWD_CFG_ALLBRIDGE_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_ALLBRIDGE_REDIR_ENA, x)
+
+#define ANA_CPU_FWD_CFG_OAM_ENA                  BIT(0)
+#define ANA_CPU_FWD_CFG_OAM_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_CFG_OAM_ENA, x)
+#define ANA_CPU_FWD_CFG_OAM_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_CFG_OAM_ENA, x)
+
+/*      ANA:PORT:CPU_FWD_BPDU_CFG */
+#define ANA_CPU_FWD_BPDU_CFG(g)   __REG(TARGET_ANA,\
+					0, 1, 24576, g, 10, 256, 100, 0, 1, 4)
+
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA       GENMASK(31, 16)
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA, x)
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA, x)
+
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA      GENMASK(15, 0)
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA_SET(x)\
+	FIELD_PREP(ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA, x)
+#define ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA_GET(x)\
+	FIELD_GET(ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA, x)
+
+/*      ANA:PORT:PORT_CFG */
+#define ANA_PORT_CFG(g)           __REG(TARGET_ANA,\
+					0, 1, 24576, g, 10, 256, 112, 0, 1, 4)
+
+#define ANA_PORT_CFG_SRC_MIRROR_ENA              BIT(13)
+#define ANA_PORT_CFG_SRC_MIRROR_ENA_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_SRC_MIRROR_ENA, x)
+#define ANA_PORT_CFG_SRC_MIRROR_ENA_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_SRC_MIRROR_ENA, x)
+
+#define ANA_PORT_CFG_LIMIT_DROP                  BIT(12)
+#define ANA_PORT_CFG_LIMIT_DROP_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LIMIT_DROP, x)
+#define ANA_PORT_CFG_LIMIT_DROP_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LIMIT_DROP, x)
+
+#define ANA_PORT_CFG_LIMIT_CPU                   BIT(11)
+#define ANA_PORT_CFG_LIMIT_CPU_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LIMIT_CPU, x)
+#define ANA_PORT_CFG_LIMIT_CPU_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LIMIT_CPU, x)
+
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_DROP        BIT(10)
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_DROP_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LOCKED_PORTMOVE_DROP, x)
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_DROP_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LOCKED_PORTMOVE_DROP, x)
+
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_CPU         BIT(9)
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_CPU_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LOCKED_PORTMOVE_CPU, x)
+#define ANA_PORT_CFG_LOCKED_PORTMOVE_CPU_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LOCKED_PORTMOVE_CPU, x)
+
+#define ANA_PORT_CFG_LEARNDROP                   BIT(8)
+#define ANA_PORT_CFG_LEARNDROP_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LEARNDROP, x)
+#define ANA_PORT_CFG_LEARNDROP_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LEARNDROP, x)
+
+#define ANA_PORT_CFG_LEARNCPU                    BIT(7)
+#define ANA_PORT_CFG_LEARNCPU_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LEARNCPU, x)
+#define ANA_PORT_CFG_LEARNCPU_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LEARNCPU, x)
+
+#define ANA_PORT_CFG_LEARNAUTO                   BIT(6)
+#define ANA_PORT_CFG_LEARNAUTO_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LEARNAUTO, x)
+#define ANA_PORT_CFG_LEARNAUTO_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LEARNAUTO, x)
+
+#define ANA_PORT_CFG_LEARN_ENA                   BIT(5)
+#define ANA_PORT_CFG_LEARN_ENA_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_LEARN_ENA, x)
+#define ANA_PORT_CFG_LEARN_ENA_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_LEARN_ENA, x)
+
+#define ANA_PORT_CFG_RECV_ENA                    BIT(4)
+#define ANA_PORT_CFG_RECV_ENA_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_RECV_ENA, x)
+#define ANA_PORT_CFG_RECV_ENA_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_RECV_ENA, x)
+
+#define ANA_PORT_CFG_PORTID_VAL                  GENMASK(3, 0)
+#define ANA_PORT_CFG_PORTID_VAL_SET(x)\
+	FIELD_PREP(ANA_PORT_CFG_PORTID_VAL, x)
+#define ANA_PORT_CFG_PORTID_VAL_GET(x)\
+	FIELD_GET(ANA_PORT_CFG_PORTID_VAL, x)
+
+/*      ANA:PFC:PFC_CFG */
+#define ANA_PFC_CFG(g)            __REG(TARGET_ANA,\
+					0, 1, 28672, g, 9, 64, 0, 0, 1, 4)
+
+#define ANA_PFC_CFG_RX_PFC_ENA                   GENMASK(9, 2)
+#define ANA_PFC_CFG_RX_PFC_ENA_SET(x)\
+	FIELD_PREP(ANA_PFC_CFG_RX_PFC_ENA, x)
+#define ANA_PFC_CFG_RX_PFC_ENA_GET(x)\
+	FIELD_GET(ANA_PFC_CFG_RX_PFC_ENA, x)
+
+#define ANA_PFC_CFG_FC_LINK_SPEED                GENMASK(1, 0)
+#define ANA_PFC_CFG_FC_LINK_SPEED_SET(x)\
+	FIELD_PREP(ANA_PFC_CFG_FC_LINK_SPEED, x)
+#define ANA_PFC_CFG_FC_LINK_SPEED_GET(x)\
+	FIELD_GET(ANA_PFC_CFG_FC_LINK_SPEED, x)
+
+/*      ANA:COMMON:AGGR_CFG */
+#define ANA_AGGR_CFG              __REG(TARGET_ANA,\
+					0, 1, 29248, 0, 1, 552, 0, 0, 1, 4)
+
+#define ANA_AGGR_CFG_AC_RND_ENA                  BIT(6)
+#define ANA_AGGR_CFG_AC_RND_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_RND_ENA, x)
+#define ANA_AGGR_CFG_AC_RND_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_RND_ENA, x)
+
+#define ANA_AGGR_CFG_AC_DMAC_ENA                 BIT(5)
+#define ANA_AGGR_CFG_AC_DMAC_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_DMAC_ENA, x)
+#define ANA_AGGR_CFG_AC_DMAC_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_DMAC_ENA, x)
+
+#define ANA_AGGR_CFG_AC_SMAC_ENA                 BIT(4)
+#define ANA_AGGR_CFG_AC_SMAC_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_SMAC_ENA, x)
+#define ANA_AGGR_CFG_AC_SMAC_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_SMAC_ENA, x)
+
+#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA         BIT(3)
+#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA, x)
+#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA, x)
+
+#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA           BIT(2)
+#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA, x)
+#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA, x)
+
+#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA           BIT(1)
+#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA, x)
+#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA, x)
+
+#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA           BIT(0)
+#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(x)\
+	FIELD_PREP(ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, x)
+#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_GET(x)\
+	FIELD_GET(ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, x)
+
+/*      ANA:COMMON:CPUQ_CFG */
+#define ANA_CPUQ_CFG              __REG(TARGET_ANA,\
+					0, 1, 29248, 0, 1, 552, 4, 0, 1, 4)
+
+#define ANA_CPUQ_CFG_CPUQ_MLD                    GENMASK(29, 27)
+#define ANA_CPUQ_CFG_CPUQ_MLD_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_MLD, x)
+#define ANA_CPUQ_CFG_CPUQ_MLD_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_MLD, x)
+
+#define ANA_CPUQ_CFG_CPUQ_IGMP                   GENMASK(26, 24)
+#define ANA_CPUQ_CFG_CPUQ_IGMP_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_IGMP, x)
+#define ANA_CPUQ_CFG_CPUQ_IGMP_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_IGMP, x)
+
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL              GENMASK(23, 21)
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_IPMC_CTRL, x)
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_IPMC_CTRL, x)
+
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE              GENMASK(20, 18)
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_ALLBRIDGE, x)
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_ALLBRIDGE, x)
+
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE        GENMASK(17, 15)
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE, x)
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE, x)
+
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY               GENMASK(14, 12)
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_SRC_COPY, x)
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_SRC_COPY, x)
+
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY               GENMASK(11, 9)
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_MAC_COPY, x)
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_MAC_COPY, x)
+
+#define ANA_CPUQ_CFG_CPUQ_LRN                    GENMASK(8, 6)
+#define ANA_CPUQ_CFG_CPUQ_LRN_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_LRN, x)
+#define ANA_CPUQ_CFG_CPUQ_LRN_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_LRN, x)
+
+#define ANA_CPUQ_CFG_CPUQ_MIRROR                 GENMASK(5, 3)
+#define ANA_CPUQ_CFG_CPUQ_MIRROR_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_MIRROR, x)
+#define ANA_CPUQ_CFG_CPUQ_MIRROR_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_MIRROR, x)
+
+#define ANA_CPUQ_CFG_CPUQ_SFLOW                  GENMASK(2, 0)
+#define ANA_CPUQ_CFG_CPUQ_SFLOW_SET(x)\
+	FIELD_PREP(ANA_CPUQ_CFG_CPUQ_SFLOW, x)
+#define ANA_CPUQ_CFG_CPUQ_SFLOW_GET(x)\
+	FIELD_GET(ANA_CPUQ_CFG_CPUQ_SFLOW, x)
+
+/*      CHIP_TOP:CUPHY_CFG:CUPHY_PORT_CFG */
+#define CHIP_TOP_CUPHY_PORT_CFG(r) __REG(TARGET_CHIP_TOP,\
+					0, 1, 12, 0, 1, 64, 20, r, 5, 4)
+
+#define CHIP_TOP_CUPHY_PORT_CFG_AUTO_SQUELCH_ENA BIT(7)
+#define CHIP_TOP_CUPHY_PORT_CFG_AUTO_SQUELCH_ENA_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_PORT_CFG_AUTO_SQUELCH_ENA, x)
+#define CHIP_TOP_CUPHY_PORT_CFG_AUTO_SQUELCH_ENA_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_PORT_CFG_AUTO_SQUELCH_ENA, x)
+
+#define CHIP_TOP_CUPHY_PORT_CFG_COMA_MODE        BIT(6)
+#define CHIP_TOP_CUPHY_PORT_CFG_COMA_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_PORT_CFG_COMA_MODE, x)
+#define CHIP_TOP_CUPHY_PORT_CFG_COMA_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_PORT_CFG_COMA_MODE, x)
+
+#define CHIP_TOP_CUPHY_PORT_CFG_MODE             GENMASK(5, 1)
+#define CHIP_TOP_CUPHY_PORT_CFG_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_PORT_CFG_MODE, x)
+#define CHIP_TOP_CUPHY_PORT_CFG_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_PORT_CFG_MODE, x)
+
+#define CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA      BIT(0)
+#define CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA, x)
+#define CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA, x)
+
+/*      CHIP_TOP:CUPHY_CFG:CUPHY_LED_CFG */
+#define CHIP_TOP_CUPHY_LED_CFG(r) __REG(TARGET_CHIP_TOP,\
+					0, 1, 12, 0, 1, 64, 40, r, 5, 4)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_ECO_DIS       BIT(11)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_ECO_DIS_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_ECO_DIS, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_ECO_DIS_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_ECO_DIS, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_EEE_MODE      BIT(10)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_EEE_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_EEE_MODE, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_EEE_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_EEE_MODE, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_MODE     GENMASK(9, 8)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_TEST_MODE, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_TEST_MODE, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_VAL      GENMASK(7, 6)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_VAL_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_TEST_VAL, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_TEST_VAL_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_TEST_VAL, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_POLARITY      GENMASK(5, 4)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_POLARITY_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_POLARITY, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_POLARITY_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_POLARITY, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_DRIVE_MODE    GENMASK(3, 2)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_DRIVE_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_DRIVE_MODE, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_DRIVE_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_DRIVE_MODE, x)
+
+#define CHIP_TOP_CUPHY_LED_CFG_LED_BLINK_MODE    GENMASK(1, 0)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_BLINK_MODE_SET(x)\
+	FIELD_PREP(CHIP_TOP_CUPHY_LED_CFG_LED_BLINK_MODE, x)
+#define CHIP_TOP_CUPHY_LED_CFG_LED_BLINK_MODE_GET(x)\
+	FIELD_GET(CHIP_TOP_CUPHY_LED_CFG_LED_BLINK_MODE, x)
+
+/*      DEV:PORT_MODE:CLOCK_CFG */
+#define DEV_CLOCK_CFG(t)          __REG(TARGET_DEV,\
+					t, 9, 0, 0, 1, 20, 0, 0, 1, 4)
+
+#define DEV_CLOCK_CFG_MAC_TX_RST                 BIT(7)
+#define DEV_CLOCK_CFG_MAC_TX_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_MAC_TX_RST, x)
+#define DEV_CLOCK_CFG_MAC_TX_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_MAC_TX_RST, x)
+
+#define DEV_CLOCK_CFG_MAC_RX_RST                 BIT(6)
+#define DEV_CLOCK_CFG_MAC_RX_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_MAC_RX_RST, x)
+#define DEV_CLOCK_CFG_MAC_RX_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_MAC_RX_RST, x)
+
+#define DEV_CLOCK_CFG_PCS_TX_RST                 BIT(5)
+#define DEV_CLOCK_CFG_PCS_TX_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_PCS_TX_RST, x)
+#define DEV_CLOCK_CFG_PCS_TX_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_PCS_TX_RST, x)
+
+#define DEV_CLOCK_CFG_PCS_RX_RST                 BIT(4)
+#define DEV_CLOCK_CFG_PCS_RX_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_PCS_RX_RST, x)
+#define DEV_CLOCK_CFG_PCS_RX_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_PCS_RX_RST, x)
+
+#define DEV_CLOCK_CFG_PORT_RST                   BIT(3)
+#define DEV_CLOCK_CFG_PORT_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_PORT_RST, x)
+#define DEV_CLOCK_CFG_PORT_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_PORT_RST, x)
+
+#define DEV_CLOCK_CFG_PHY_RST                    BIT(2)
+#define DEV_CLOCK_CFG_PHY_RST_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_PHY_RST, x)
+#define DEV_CLOCK_CFG_PHY_RST_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_PHY_RST, x)
+
+#define DEV_CLOCK_CFG_LINK_SPEED                 GENMASK(1, 0)
+#define DEV_CLOCK_CFG_LINK_SPEED_SET(x)\
+	FIELD_PREP(DEV_CLOCK_CFG_LINK_SPEED, x)
+#define DEV_CLOCK_CFG_LINK_SPEED_GET(x)\
+	FIELD_GET(DEV_CLOCK_CFG_LINK_SPEED, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_ENA_CFG */
+#define DEV_MAC_ENA_CFG(t)        __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 0, 0, 1, 4)
+
+#define DEV_MAC_ENA_CFG_RX_ENA                   BIT(4)
+#define DEV_MAC_ENA_CFG_RX_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_ENA_CFG_RX_ENA, x)
+#define DEV_MAC_ENA_CFG_RX_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_ENA_CFG_RX_ENA, x)
+
+#define DEV_MAC_ENA_CFG_TX_ENA                   BIT(0)
+#define DEV_MAC_ENA_CFG_TX_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_ENA_CFG_TX_ENA, x)
+#define DEV_MAC_ENA_CFG_TX_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_ENA_CFG_TX_ENA, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_MODE_CFG */
+#define DEV_MAC_MODE_CFG(t)       __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 4, 0, 1, 4)
+
+#define DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA        BIT(8)
+#define DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA, x)
+#define DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA, x)
+
+#define DEV_MAC_MODE_CFG_GIGA_MODE_ENA           BIT(4)
+#define DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_MODE_CFG_GIGA_MODE_ENA, x)
+#define DEV_MAC_MODE_CFG_GIGA_MODE_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_MODE_CFG_GIGA_MODE_ENA, x)
+
+#define DEV_MAC_MODE_CFG_FDX_ENA                 BIT(0)
+#define DEV_MAC_MODE_CFG_FDX_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_MODE_CFG_FDX_ENA, x)
+#define DEV_MAC_MODE_CFG_FDX_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_MODE_CFG_FDX_ENA, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_MAXLEN_CFG */
+#define DEV_MAC_MAXLEN_CFG(t)     __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 8, 0, 1, 4)
+
+#define DEV_MAC_MAXLEN_CFG_MAX_LEN               GENMASK(15, 0)
+#define DEV_MAC_MAXLEN_CFG_MAX_LEN_SET(x)\
+	FIELD_PREP(DEV_MAC_MAXLEN_CFG_MAX_LEN, x)
+#define DEV_MAC_MAXLEN_CFG_MAX_LEN_GET(x)\
+	FIELD_GET(DEV_MAC_MAXLEN_CFG_MAX_LEN, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_TAGS_CFG */
+#define DEV_MAC_TAGS_CFG(t)       __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 12, 0, 1, 4)
+
+#define DEV_MAC_TAGS_CFG_TAG_ID                  GENMASK(31, 16)
+#define DEV_MAC_TAGS_CFG_TAG_ID_SET(x)\
+	FIELD_PREP(DEV_MAC_TAGS_CFG_TAG_ID, x)
+#define DEV_MAC_TAGS_CFG_TAG_ID_GET(x)\
+	FIELD_GET(DEV_MAC_TAGS_CFG_TAG_ID, x)
+
+#define DEV_MAC_TAGS_CFG_PB_ENA                  BIT(1)
+#define DEV_MAC_TAGS_CFG_PB_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_TAGS_CFG_PB_ENA, x)
+#define DEV_MAC_TAGS_CFG_PB_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_TAGS_CFG_PB_ENA, x)
+
+#define DEV_MAC_TAGS_CFG_VLAN_AWR_ENA            BIT(0)
+#define DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA, x)
+#define DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA, x)
+
+#define DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA        BIT(2)
+#define DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x)
+#define DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_IFG_CFG */
+#define DEV_MAC_IFG_CFG(t)        __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 20, 0, 1, 4)
+
+#define DEV_MAC_IFG_CFG_OLD_IPG_CHECK            BIT(17)
+#define DEV_MAC_IFG_CFG_OLD_IPG_CHECK_SET(x)\
+	FIELD_PREP(DEV_MAC_IFG_CFG_OLD_IPG_CHECK, x)
+#define DEV_MAC_IFG_CFG_OLD_IPG_CHECK_GET(x)\
+	FIELD_GET(DEV_MAC_IFG_CFG_OLD_IPG_CHECK, x)
+
+#define DEV_MAC_IFG_CFG_REDUCED_TX_IFG           BIT(16)
+#define DEV_MAC_IFG_CFG_REDUCED_TX_IFG_SET(x)\
+	FIELD_PREP(DEV_MAC_IFG_CFG_REDUCED_TX_IFG, x)
+#define DEV_MAC_IFG_CFG_REDUCED_TX_IFG_GET(x)\
+	FIELD_GET(DEV_MAC_IFG_CFG_REDUCED_TX_IFG, x)
+
+#define DEV_MAC_IFG_CFG_TX_IFG                   GENMASK(12, 8)
+#define DEV_MAC_IFG_CFG_TX_IFG_SET(x)\
+	FIELD_PREP(DEV_MAC_IFG_CFG_TX_IFG, x)
+#define DEV_MAC_IFG_CFG_TX_IFG_GET(x)\
+	FIELD_GET(DEV_MAC_IFG_CFG_TX_IFG, x)
+
+#define DEV_MAC_IFG_CFG_RX_IFG2                  GENMASK(7, 4)
+#define DEV_MAC_IFG_CFG_RX_IFG2_SET(x)\
+	FIELD_PREP(DEV_MAC_IFG_CFG_RX_IFG2, x)
+#define DEV_MAC_IFG_CFG_RX_IFG2_GET(x)\
+	FIELD_GET(DEV_MAC_IFG_CFG_RX_IFG2, x)
+
+#define DEV_MAC_IFG_CFG_RX_IFG1                  GENMASK(3, 0)
+#define DEV_MAC_IFG_CFG_RX_IFG1_SET(x)\
+	FIELD_PREP(DEV_MAC_IFG_CFG_RX_IFG1, x)
+#define DEV_MAC_IFG_CFG_RX_IFG1_GET(x)\
+	FIELD_GET(DEV_MAC_IFG_CFG_RX_IFG1, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_HDX_CFG */
+#define DEV_MAC_HDX_CFG(t)        __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 24, 0, 1, 4)
+
+#define DEV_MAC_HDX_CFG_BYPASS_COL_SYNC          BIT(26)
+#define DEV_MAC_HDX_CFG_BYPASS_COL_SYNC_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_BYPASS_COL_SYNC, x)
+#define DEV_MAC_HDX_CFG_BYPASS_COL_SYNC_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_BYPASS_COL_SYNC, x)
+
+#define DEV_MAC_HDX_CFG_OB_ENA                   BIT(25)
+#define DEV_MAC_HDX_CFG_OB_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_OB_ENA, x)
+#define DEV_MAC_HDX_CFG_OB_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_OB_ENA, x)
+
+#define DEV_MAC_HDX_CFG_WEXC_DIS                 BIT(24)
+#define DEV_MAC_HDX_CFG_WEXC_DIS_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_WEXC_DIS, x)
+#define DEV_MAC_HDX_CFG_WEXC_DIS_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_WEXC_DIS, x)
+
+#define DEV_MAC_HDX_CFG_SEED                     GENMASK(23, 16)
+#define DEV_MAC_HDX_CFG_SEED_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_SEED, x)
+#define DEV_MAC_HDX_CFG_SEED_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_SEED, x)
+
+#define DEV_MAC_HDX_CFG_SEED_LOAD                BIT(12)
+#define DEV_MAC_HDX_CFG_SEED_LOAD_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_SEED_LOAD, x)
+#define DEV_MAC_HDX_CFG_SEED_LOAD_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_SEED_LOAD, x)
+
+#define DEV_MAC_HDX_CFG_RETRY_EXC_COL_ENA        BIT(8)
+#define DEV_MAC_HDX_CFG_RETRY_EXC_COL_ENA_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_RETRY_EXC_COL_ENA, x)
+#define DEV_MAC_HDX_CFG_RETRY_EXC_COL_ENA_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_RETRY_EXC_COL_ENA, x)
+
+#define DEV_MAC_HDX_CFG_LATE_COL_POS             GENMASK(6, 0)
+#define DEV_MAC_HDX_CFG_LATE_COL_POS_SET(x)\
+	FIELD_PREP(DEV_MAC_HDX_CFG_LATE_COL_POS, x)
+#define DEV_MAC_HDX_CFG_LATE_COL_POS_GET(x)\
+	FIELD_GET(DEV_MAC_HDX_CFG_LATE_COL_POS, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_FC_MAC_LOW_CFG */
+#define DEV_FC_MAC_LOW_CFG(t)     __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 32, 0, 1, 4)
+
+#define DEV_FC_MAC_LOW_CFG_MAC_LOW               GENMASK(23, 0)
+#define DEV_FC_MAC_LOW_CFG_MAC_LOW_SET(x)\
+	FIELD_PREP(DEV_FC_MAC_LOW_CFG_MAC_LOW, x)
+#define DEV_FC_MAC_LOW_CFG_MAC_LOW_GET(x)\
+	FIELD_GET(DEV_FC_MAC_LOW_CFG_MAC_LOW, x)
+
+/*      DEV:MAC_CFG_STATUS:MAC_FC_MAC_HIGH_CFG */
+#define DEV_FC_MAC_HIGH_CFG(t)    __REG(TARGET_DEV,\
+					t, 9, 20, 0, 1, 44, 36, 0, 1, 4)
+
+#define DEV_FC_MAC_HIGH_CFG_MAC_HIGH             GENMASK(23, 0)
+#define DEV_FC_MAC_HIGH_CFG_MAC_HIGH_SET(x)\
+	FIELD_PREP(DEV_FC_MAC_HIGH_CFG_MAC_HIGH, x)
+#define DEV_FC_MAC_HIGH_CFG_MAC_HIGH_GET(x)\
+	FIELD_GET(DEV_FC_MAC_HIGH_CFG_MAC_HIGH, x)
+
+/*      DEV:PCS1G_CFG_STATUS:PCS1G_CFG */
+#define DEV_PCS1G_CFG(t)          __REG(TARGET_DEV,\
+					t, 9, 64, 0, 1, 68, 0, 0, 1, 4)
+
+#define DEV_PCS1G_CFG_LINK_STATUS_TYPE           BIT(4)
+#define DEV_PCS1G_CFG_LINK_STATUS_TYPE_SET(x)\
+	FIELD_PREP(DEV_PCS1G_CFG_LINK_STATUS_TYPE, x)
+#define DEV_PCS1G_CFG_LINK_STATUS_TYPE_GET(x)\
+	FIELD_GET(DEV_PCS1G_CFG_LINK_STATUS_TYPE, x)
+
+#define DEV_PCS1G_CFG_AN_LINK_CTRL_ENA           BIT(1)
+#define DEV_PCS1G_CFG_AN_LINK_CTRL_ENA_SET(x)\
+	FIELD_PREP(DEV_PCS1G_CFG_AN_LINK_CTRL_ENA, x)
+#define DEV_PCS1G_CFG_AN_LINK_CTRL_ENA_GET(x)\
+	FIELD_GET(DEV_PCS1G_CFG_AN_LINK_CTRL_ENA, x)
+
+#define DEV_PCS1G_CFG_PCS_ENA                    BIT(0)
+#define DEV_PCS1G_CFG_PCS_ENA_SET(x)\
+	FIELD_PREP(DEV_PCS1G_CFG_PCS_ENA, x)
+#define DEV_PCS1G_CFG_PCS_ENA_GET(x)\
+	FIELD_GET(DEV_PCS1G_CFG_PCS_ENA, x)
+
+/*      DEV:PCS1G_CFG_STATUS:PCS1G_SD_CFG */
+#define DEV_PCS1G_SD_CFG(t)       __REG(TARGET_DEV,\
+					t, 9, 64, 0, 1, 68, 8, 0, 1, 4)
+
+#define DEV_PCS1G_SD_CFG_SD_SEL                  BIT(8)
+#define DEV_PCS1G_SD_CFG_SD_SEL_SET(x)\
+	FIELD_PREP(DEV_PCS1G_SD_CFG_SD_SEL, x)
+#define DEV_PCS1G_SD_CFG_SD_SEL_GET(x)\
+	FIELD_GET(DEV_PCS1G_SD_CFG_SD_SEL, x)
+
+#define DEV_PCS1G_SD_CFG_SD_POL                  BIT(4)
+#define DEV_PCS1G_SD_CFG_SD_POL_SET(x)\
+	FIELD_PREP(DEV_PCS1G_SD_CFG_SD_POL, x)
+#define DEV_PCS1G_SD_CFG_SD_POL_GET(x)\
+	FIELD_GET(DEV_PCS1G_SD_CFG_SD_POL, x)
+
+#define DEV_PCS1G_SD_CFG_SD_ENA                  BIT(0)
+#define DEV_PCS1G_SD_CFG_SD_ENA_SET(x)\
+	FIELD_PREP(DEV_PCS1G_SD_CFG_SD_ENA, x)
+#define DEV_PCS1G_SD_CFG_SD_ENA_GET(x)\
+	FIELD_GET(DEV_PCS1G_SD_CFG_SD_ENA, x)
+
+/*      DEVCPU_GCB:CHIP_REGS:FEAT_DISABLE */
+#define GCB_FEAT_DISABLE          __REG(TARGET_GCB,\
+					0, 1, 0, 0, 1, 28, 20, 0, 1, 4)
+
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_CFG_DIS     BIT(0)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_CFG_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_EEPROM_CFG_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_CFG_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_EEPROM_CFG_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_BOOT_DIS    BIT(1)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_BOOT_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_EEPROM_BOOT_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_BOOT_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_EEPROM_BOOT_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_FW_PATCH_DIS BIT(2)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_FW_PATCH_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_EEPROM_FW_PATCH_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_EEPROM_FW_PATCH_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_EEPROM_FW_PATCH_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_MMU_BACKDOOR_WR_DIS BIT(3)
+#define GCB_FEAT_DISABLE_FEAT_MMU_BACKDOOR_WR_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_MMU_BACKDOOR_WR_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_MMU_BACKDOOR_WR_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_MMU_BACKDOOR_WR_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_MGMT_IF_WR_DIS     BIT(4)
+#define GCB_FEAT_DISABLE_FEAT_MGMT_IF_WR_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_MGMT_IF_WR_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_MGMT_IF_WR_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_MGMT_IF_WR_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_DD_DIS             BIT(5)
+#define GCB_FEAT_DISABLE_FEAT_DD_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_DD_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_DD_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_DD_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_TSN_DIS            BIT(6)
+#define GCB_FEAT_DISABLE_FEAT_TSN_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_TSN_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_TSN_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_TSN_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_PTP_DIS            BIT(7)
+#define GCB_FEAT_DISABLE_FEAT_PTP_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_PTP_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_PTP_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_PTP_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_FRER_DIS           BIT(8)
+#define GCB_FEAT_DISABLE_FEAT_FRER_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_FRER_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_FRER_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_FRER_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS      GENMASK(14, 12)
+#define GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_DIS         GENMASK(16, 15)
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_NUM_CU_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_NUM_CU_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_RGMII_DIS          GENMASK(18, 17)
+#define GCB_FEAT_DISABLE_FEAT_RGMII_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_RGMII_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_RGMII_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_RGMII_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_SGMII_DIS          GENMASK(20, 19)
+#define GCB_FEAT_DISABLE_FEAT_SGMII_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_SGMII_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_SGMII_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_SGMII_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_QSGMII_DIS         BIT(21)
+#define GCB_FEAT_DISABLE_FEAT_QSGMII_DIS_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_QSGMII_DIS, x)
+#define GCB_FEAT_DISABLE_FEAT_QSGMII_DIS_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_QSGMII_DIS, x)
+
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_FIXED       BIT(22)
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_FIXED_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_NUM_CU_FIXED, x)
+#define GCB_FEAT_DISABLE_FEAT_NUM_CU_FIXED_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_NUM_CU_FIXED, x)
+
+#define GCB_FEAT_DISABLE_FEAT_WDT_ENABLED        BIT(25)
+#define GCB_FEAT_DISABLE_FEAT_WDT_ENABLED_SET(x)\
+	FIELD_PREP(GCB_FEAT_DISABLE_FEAT_WDT_ENABLED, x)
+#define GCB_FEAT_DISABLE_FEAT_WDT_ENABLED_GET(x)\
+	FIELD_GET(GCB_FEAT_DISABLE_FEAT_WDT_ENABLED, x)
+
+/*      HSIO:HW_CFGSTAT:HW_CFG */
+#define HSIO_HW_CFG               __REG(TARGET_HSIO,\
+					0, 1, 72, 0, 1, 44, 0, 0, 1, 4)
+
+#define HSIO_HW_CFG_RGMII_0_CFG                  BIT(10)
+#define HSIO_HW_CFG_RGMII_0_CFG_SET(x)\
+	FIELD_PREP(HSIO_HW_CFG_RGMII_0_CFG, x)
+#define HSIO_HW_CFG_RGMII_0_CFG_GET(x)\
+	FIELD_GET(HSIO_HW_CFG_RGMII_0_CFG, x)
+
+#define HSIO_HW_CFG_GMII_ENA                     GENMASK(9, 1)
+#define HSIO_HW_CFG_GMII_ENA_SET(x)\
+	FIELD_PREP(HSIO_HW_CFG_GMII_ENA, x)
+#define HSIO_HW_CFG_GMII_ENA_GET(x)\
+	FIELD_GET(HSIO_HW_CFG_GMII_ENA, x)
+
+#define HSIO_HW_CFG_QSGMII_ENA                   BIT(0)
+#define HSIO_HW_CFG_QSGMII_ENA_SET(x)\
+	FIELD_PREP(HSIO_HW_CFG_QSGMII_ENA, x)
+#define HSIO_HW_CFG_QSGMII_ENA_GET(x)\
+	FIELD_GET(HSIO_HW_CFG_QSGMII_ENA, x)
+
+/*      HSIO:HW_CFGSTAT:RGMII_CFG */
+#define HSIO_RGMII_CFG(r)         __REG(TARGET_HSIO,\
+					0, 1, 72, 0, 1, 44, 12, r, 2, 4)
+
+#define HSIO_RGMII_CFG_IB_RX_LINK_STATUS         BIT(15)
+#define HSIO_RGMII_CFG_IB_RX_LINK_STATUS_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_RX_LINK_STATUS, x)
+#define HSIO_RGMII_CFG_IB_RX_LINK_STATUS_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_RX_LINK_STATUS, x)
+
+#define HSIO_RGMII_CFG_IB_RX_DUPLEX              BIT(14)
+#define HSIO_RGMII_CFG_IB_RX_DUPLEX_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_RX_DUPLEX, x)
+#define HSIO_RGMII_CFG_IB_RX_DUPLEX_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_RX_DUPLEX, x)
+
+#define HSIO_RGMII_CFG_IB_RX_SPEED               GENMASK(13, 12)
+#define HSIO_RGMII_CFG_IB_RX_SPEED_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_RX_SPEED, x)
+#define HSIO_RGMII_CFG_IB_RX_SPEED_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_RX_SPEED, x)
+
+#define HSIO_RGMII_CFG_IB_TX_LINK_STATUS         BIT(11)
+#define HSIO_RGMII_CFG_IB_TX_LINK_STATUS_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_TX_LINK_STATUS, x)
+#define HSIO_RGMII_CFG_IB_TX_LINK_STATUS_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_TX_LINK_STATUS, x)
+
+#define HSIO_RGMII_CFG_IB_TX_FDX                 BIT(10)
+#define HSIO_RGMII_CFG_IB_TX_FDX_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_TX_FDX, x)
+#define HSIO_RGMII_CFG_IB_TX_FDX_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_TX_FDX, x)
+
+#define HSIO_RGMII_CFG_IB_TX_MII_SPD             BIT(9)
+#define HSIO_RGMII_CFG_IB_TX_MII_SPD_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_TX_MII_SPD, x)
+#define HSIO_RGMII_CFG_IB_TX_MII_SPD_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_TX_MII_SPD, x)
+
+#define HSIO_RGMII_CFG_IB_TX_SPD_1G              BIT(8)
+#define HSIO_RGMII_CFG_IB_TX_SPD_1G_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_TX_SPD_1G, x)
+#define HSIO_RGMII_CFG_IB_TX_SPD_1G_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_TX_SPD_1G, x)
+
+#define HSIO_RGMII_CFG_IB_TX_ENA                 BIT(7)
+#define HSIO_RGMII_CFG_IB_TX_ENA_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_TX_ENA, x)
+#define HSIO_RGMII_CFG_IB_TX_ENA_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_TX_ENA, x)
+
+#define HSIO_RGMII_CFG_IB_RX_ENA                 BIT(6)
+#define HSIO_RGMII_CFG_IB_RX_ENA_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_RX_ENA, x)
+#define HSIO_RGMII_CFG_IB_RX_ENA_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_RX_ENA, x)
+
+#define HSIO_RGMII_CFG_IB_ENA                    BIT(5)
+#define HSIO_RGMII_CFG_IB_ENA_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_IB_ENA, x)
+#define HSIO_RGMII_CFG_IB_ENA_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_IB_ENA, x)
+
+#define HSIO_RGMII_CFG_TX_CLK_CFG                GENMASK(4, 2)
+#define HSIO_RGMII_CFG_TX_CLK_CFG_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_TX_CLK_CFG, x)
+#define HSIO_RGMII_CFG_TX_CLK_CFG_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_TX_CLK_CFG, x)
+
+#define HSIO_RGMII_CFG_RGMII_TX_RST              BIT(1)
+#define HSIO_RGMII_CFG_RGMII_TX_RST_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_RGMII_TX_RST, x)
+#define HSIO_RGMII_CFG_RGMII_TX_RST_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_RGMII_TX_RST, x)
+
+#define HSIO_RGMII_CFG_RGMII_RX_RST              BIT(0)
+#define HSIO_RGMII_CFG_RGMII_RX_RST_SET(x)\
+	FIELD_PREP(HSIO_RGMII_CFG_RGMII_RX_RST, x)
+#define HSIO_RGMII_CFG_RGMII_RX_RST_GET(x)\
+	FIELD_GET(HSIO_RGMII_CFG_RGMII_RX_RST, x)
+
+/*      HSIO:HW_CFGSTAT:DLL_CFG */
+#define HSIO_DLL_CFG(r)           __REG(TARGET_HSIO,\
+					0, 1, 72, 0, 1, 44, 28, r, 4, 4)
+
+#define HSIO_DLL_CFG_DLL_CLK_ENA                 BIT(20)
+#define HSIO_DLL_CFG_DLL_CLK_ENA_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_DLL_CLK_ENA, x)
+#define HSIO_DLL_CFG_DLL_CLK_ENA_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_DLL_CLK_ENA, x)
+
+#define HSIO_DLL_CFG_BIST_PASS                   BIT(19)
+#define HSIO_DLL_CFG_BIST_PASS_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_BIST_PASS, x)
+#define HSIO_DLL_CFG_BIST_PASS_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_BIST_PASS, x)
+
+#define HSIO_DLL_CFG_BIST_END                    BIT(18)
+#define HSIO_DLL_CFG_BIST_END_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_BIST_END, x)
+#define HSIO_DLL_CFG_BIST_END_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_BIST_END, x)
+
+#define HSIO_DLL_CFG_BIST_START                  BIT(17)
+#define HSIO_DLL_CFG_BIST_START_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_BIST_START, x)
+#define HSIO_DLL_CFG_BIST_START_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_BIST_START, x)
+
+#define HSIO_DLL_CFG_TAP_SEL                     GENMASK(16, 10)
+#define HSIO_DLL_CFG_TAP_SEL_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_TAP_SEL, x)
+#define HSIO_DLL_CFG_TAP_SEL_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_TAP_SEL, x)
+
+#define HSIO_DLL_CFG_TAP_ADJ                     GENMASK(9, 3)
+#define HSIO_DLL_CFG_TAP_ADJ_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_TAP_ADJ, x)
+#define HSIO_DLL_CFG_TAP_ADJ_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_TAP_ADJ, x)
+
+#define HSIO_DLL_CFG_DELAY_ENA                   BIT(2)
+#define HSIO_DLL_CFG_DELAY_ENA_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_DELAY_ENA, x)
+#define HSIO_DLL_CFG_DELAY_ENA_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_DELAY_ENA, x)
+
+#define HSIO_DLL_CFG_DLL_ENA                     BIT(1)
+#define HSIO_DLL_CFG_DLL_ENA_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_DLL_ENA, x)
+#define HSIO_DLL_CFG_DLL_ENA_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_DLL_ENA, x)
+
+#define HSIO_DLL_CFG_DLL_RST                     BIT(0)
+#define HSIO_DLL_CFG_DLL_RST_SET(x)\
+	FIELD_PREP(HSIO_DLL_CFG_DLL_RST, x)
+#define HSIO_DLL_CFG_DLL_RST_GET(x)\
+	FIELD_GET(HSIO_DLL_CFG_DLL_RST, x)
+
+/*      DEVCPU_QS:XTR:XTR_FLUSH */
+#define QS_XTR_FLUSH              __REG(TARGET_QS,\
+					0, 1, 0, 0, 1, 36, 24, 0, 1, 4)
+
+#define QS_XTR_FLUSH_FLUSH                       GENMASK(1, 0)
+#define QS_XTR_FLUSH_FLUSH_SET(x)\
+	FIELD_PREP(QS_XTR_FLUSH_FLUSH, x)
+#define QS_XTR_FLUSH_FLUSH_GET(x)\
+	FIELD_GET(QS_XTR_FLUSH_FLUSH, x)
+
+/*      DEVCPU_QS:INJ:INJ_GRP_CFG */
+#define QS_INJ_GRP_CFG(r)         __REG(TARGET_QS,\
+					0, 1, 36, 0, 1, 40, 0, r, 2, 4)
+
+#define QS_INJ_GRP_CFG_MODE                      GENMASK(3, 2)
+#define QS_INJ_GRP_CFG_MODE_SET(x)\
+	FIELD_PREP(QS_INJ_GRP_CFG_MODE, x)
+#define QS_INJ_GRP_CFG_MODE_GET(x)\
+	FIELD_GET(QS_INJ_GRP_CFG_MODE, x)
+
+#define QS_INJ_GRP_CFG_BYTE_SWAP                 BIT(0)
+#define QS_INJ_GRP_CFG_BYTE_SWAP_SET(x)\
+	FIELD_PREP(QS_INJ_GRP_CFG_BYTE_SWAP, x)
+#define QS_INJ_GRP_CFG_BYTE_SWAP_GET(x)\
+	FIELD_GET(QS_INJ_GRP_CFG_BYTE_SWAP, x)
+
+/*      DEVCPU_QS:INJ:INJ_CTRL */
+#define QS_INJ_CTRL(r)            __REG(TARGET_QS,\
+					0, 1, 36, 0, 1, 40, 16, r, 2, 4)
+
+#define QS_INJ_CTRL_GAP_SIZE                     GENMASK(24, 21)
+#define QS_INJ_CTRL_GAP_SIZE_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_GAP_SIZE, x)
+#define QS_INJ_CTRL_GAP_SIZE_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_GAP_SIZE, x)
+
+#define QS_INJ_CTRL_ABORT                        BIT(20)
+#define QS_INJ_CTRL_ABORT_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_ABORT, x)
+#define QS_INJ_CTRL_ABORT_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_ABORT, x)
+
+#define QS_INJ_CTRL_EOF                          BIT(19)
+#define QS_INJ_CTRL_EOF_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_EOF, x)
+#define QS_INJ_CTRL_EOF_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_EOF, x)
+
+#define QS_INJ_CTRL_SOF                          BIT(18)
+#define QS_INJ_CTRL_SOF_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_SOF, x)
+#define QS_INJ_CTRL_SOF_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_SOF, x)
+
+#define QS_INJ_CTRL_VLD_BYTES                    GENMASK(17, 16)
+#define QS_INJ_CTRL_VLD_BYTES_SET(x)\
+	FIELD_PREP(QS_INJ_CTRL_VLD_BYTES, x)
+#define QS_INJ_CTRL_VLD_BYTES_GET(x)\
+	FIELD_GET(QS_INJ_CTRL_VLD_BYTES, x)
+
+/*      QSYS:SYSTEM:PORT_MODE */
+#define QSYS_PORT_MODE(r)         __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 0, r, 11, 4)
+
+#define QSYS_PORT_MODE_DEQUEUE_DIS               BIT(1)
+#define QSYS_PORT_MODE_DEQUEUE_DIS_SET(x)\
+	FIELD_PREP(QSYS_PORT_MODE_DEQUEUE_DIS, x)
+#define QSYS_PORT_MODE_DEQUEUE_DIS_GET(x)\
+	FIELD_GET(QSYS_PORT_MODE_DEQUEUE_DIS, x)
+
+#define QSYS_PORT_MODE_DEQUEUE_LATE              BIT(0)
+#define QSYS_PORT_MODE_DEQUEUE_LATE_SET(x)\
+	FIELD_PREP(QSYS_PORT_MODE_DEQUEUE_LATE, x)
+#define QSYS_PORT_MODE_DEQUEUE_LATE_GET(x)\
+	FIELD_GET(QSYS_PORT_MODE_DEQUEUE_LATE, x)
+
+/*      QSYS:SYSTEM:SWITCH_PORT_MODE */
+#define QSYS_SW_PORT_MODE(r)      __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 88, r, 10, 4)
+
+#define QSYS_SW_PORT_MODE_PORT_ENA               BIT(19)
+#define QSYS_SW_PORT_MODE_PORT_ENA_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_PORT_ENA, x)
+#define QSYS_SW_PORT_MODE_PORT_ENA_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_PORT_ENA, x)
+
+#define QSYS_SW_PORT_MODE_IDEQ_DIS               BIT(18)
+#define QSYS_SW_PORT_MODE_IDEQ_DIS_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_IDEQ_DIS, x)
+#define QSYS_SW_PORT_MODE_IDEQ_DIS_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_IDEQ_DIS, x)
+
+#define QSYS_SW_PORT_MODE_SCH_NEXT_CFG           GENMASK(17, 15)
+#define QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_SCH_NEXT_CFG, x)
+#define QSYS_SW_PORT_MODE_SCH_NEXT_CFG_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_SCH_NEXT_CFG, x)
+
+#define QSYS_SW_PORT_MODE_YEL_RSRVD              BIT(14)
+#define QSYS_SW_PORT_MODE_YEL_RSRVD_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_YEL_RSRVD, x)
+#define QSYS_SW_PORT_MODE_YEL_RSRVD_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_YEL_RSRVD, x)
+
+#define QSYS_SW_PORT_MODE_INGRESS_DROP_MODE      BIT(13)
+#define QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_INGRESS_DROP_MODE, x)
+#define QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_INGRESS_DROP_MODE, x)
+
+#define QSYS_SW_PORT_MODE_TX_PFC_ENA             GENMASK(12, 5)
+#define QSYS_SW_PORT_MODE_TX_PFC_ENA_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_TX_PFC_ENA, x)
+#define QSYS_SW_PORT_MODE_TX_PFC_ENA_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_TX_PFC_ENA, x)
+
+#define QSYS_SW_PORT_MODE_TX_PFC_MODE            BIT(4)
+#define QSYS_SW_PORT_MODE_TX_PFC_MODE_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_TX_PFC_MODE, x)
+#define QSYS_SW_PORT_MODE_TX_PFC_MODE_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_TX_PFC_MODE, x)
+
+#define QSYS_SW_PORT_MODE_FWD_TWOCYCLE_MODE      BIT(3)
+#define QSYS_SW_PORT_MODE_FWD_TWOCYCLE_MODE_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_FWD_TWOCYCLE_MODE, x)
+#define QSYS_SW_PORT_MODE_FWD_TWOCYCLE_MODE_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_FWD_TWOCYCLE_MODE, x)
+
+#define QSYS_SW_PORT_MODE_AGING_MODE             GENMASK(2, 1)
+#define QSYS_SW_PORT_MODE_AGING_MODE_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_AGING_MODE, x)
+#define QSYS_SW_PORT_MODE_AGING_MODE_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_AGING_MODE, x)
+
+#define QSYS_SW_PORT_MODE_HOL_PROTECTED          BIT(0)
+#define QSYS_SW_PORT_MODE_HOL_PROTECTED_SET(x)\
+	FIELD_PREP(QSYS_SW_PORT_MODE_HOL_PROTECTED, x)
+#define QSYS_SW_PORT_MODE_HOL_PROTECTED_GET(x)\
+	FIELD_GET(QSYS_SW_PORT_MODE_HOL_PROTECTED, x)
+
+/*      QSYS:SYSTEM:EGR_NO_SHARING */
+#define QSYS_EGR_NO_SHARING       __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 176, 0, 1, 4)
+
+#define QSYS_EGR_NO_SHARING_EGR_NO_SHARING       GENMASK(9, 0)
+#define QSYS_EGR_NO_SHARING_EGR_NO_SHARING_SET(x)\
+	FIELD_PREP(QSYS_EGR_NO_SHARING_EGR_NO_SHARING, x)
+#define QSYS_EGR_NO_SHARING_EGR_NO_SHARING_GET(x)\
+	FIELD_GET(QSYS_EGR_NO_SHARING_EGR_NO_SHARING, x)
+
+/*      QSYS:SYSTEM:SW_STATUS */
+#define QSYS_SW_STATUS(r)         __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 180, r, 10, 4)
+
+#define QSYS_SW_STATUS_EQ_AVAIL                  GENMASK(7, 0)
+#define QSYS_SW_STATUS_EQ_AVAIL_SET(x)\
+	FIELD_PREP(QSYS_SW_STATUS_EQ_AVAIL, x)
+#define QSYS_SW_STATUS_EQ_AVAIL_GET(x)\
+	FIELD_GET(QSYS_SW_STATUS_EQ_AVAIL, x)
+
+/*      QSYS:SYSTEM:EXT_CPU_CFG */
+#define QSYS_EXT_CPU_CFG          __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 220, 0, 1, 4)
+
+#define QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA        BIT(14)
+#define QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA_SET(x)\
+	FIELD_PREP(QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA, x)
+#define QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA_GET(x)\
+	FIELD_GET(QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA, x)
+
+#define QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA        BIT(13)
+#define QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA_SET(x)\
+	FIELD_PREP(QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA, x)
+#define QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA_GET(x)\
+	FIELD_GET(QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA, x)
+
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT            GENMASK(12, 8)
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT_SET(x)\
+	FIELD_PREP(QSYS_EXT_CPU_CFG_EXT_CPU_PORT, x)
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT_GET(x)\
+	FIELD_GET(QSYS_EXT_CPU_CFG_EXT_CPU_PORT, x)
+
+#define QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK            GENMASK(7, 0)
+#define QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_SET(x)\
+	FIELD_PREP(QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK, x)
+#define QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_GET(x)\
+	FIELD_GET(QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK, x)
+
+/*      QSYS:SYSTEM:CPU_GROUP_MAP */
+#define QSYS_CPU_GROUP_MAP        __REG(TARGET_QSYS,\
+					0, 1, 14336, 0, 1, 240, 224, 0, 1, 4)
+
+#define QSYS_CPU_GROUP_MAP_CPU_GROUP_MAP         GENMASK(7, 0)
+#define QSYS_CPU_GROUP_MAP_CPU_GROUP_MAP_SET(x)\
+	FIELD_PREP(QSYS_CPU_GROUP_MAP_CPU_GROUP_MAP, x)
+#define QSYS_CPU_GROUP_MAP_CPU_GROUP_MAP_GET(x)\
+	FIELD_GET(QSYS_CPU_GROUP_MAP_CPU_GROUP_MAP, x)
+
+/*      QSYS:RES_CTRL:RES_CFG */
+#define QSYS_RES_CFG(g)           __REG(TARGET_QSYS,\
+					0, 1, 16384, g, 1024, 8, 0, 0, 1, 4)
+
+#define QSYS_RES_CFG_WM_HIGH                     GENMASK(8, 0)
+#define QSYS_RES_CFG_WM_HIGH_SET(x)\
+	FIELD_PREP(QSYS_RES_CFG_WM_HIGH, x)
+#define QSYS_RES_CFG_WM_HIGH_GET(x)\
+	FIELD_GET(QSYS_RES_CFG_WM_HIGH, x)
+
+/*      QSYS:DROP_CFG:EGR_DROP_MODE */
+#define QSYS_EGR_DROP_MODE        __REG(TARGET_QSYS,\
+					0, 1, 12736, 0, 1, 8, 0, 0, 1, 4)
+
+#define QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE      GENMASK(9, 0)
+#define QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE_SET(x)\
+	FIELD_PREP(QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE, x)
+#define QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE_GET(x)\
+	FIELD_GET(QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE, x)
+
+/*      REW:PORT:PORT_VLAN_CFG */
+#define REW_PORT_VLAN_CFG(g)      __REG(TARGET_REW,\
+					0, 1, 4096, g, 11, 128, 0, 0, 1, 4)
+
+#define REW_PORT_VLAN_CFG_PORT_TPID              GENMASK(31, 16)
+#define REW_PORT_VLAN_CFG_PORT_TPID_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_TPID, x)
+#define REW_PORT_VLAN_CFG_PORT_TPID_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_TPID, x)
+
+#define REW_PORT_VLAN_CFG_PORT_DEI               BIT(15)
+#define REW_PORT_VLAN_CFG_PORT_DEI_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_DEI, x)
+#define REW_PORT_VLAN_CFG_PORT_DEI_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_DEI, x)
+
+#define REW_PORT_VLAN_CFG_PORT_PCP               GENMASK(14, 12)
+#define REW_PORT_VLAN_CFG_PORT_PCP_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_PCP, x)
+#define REW_PORT_VLAN_CFG_PORT_PCP_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_PCP, x)
+
+#define REW_PORT_VLAN_CFG_PORT_VID               GENMASK(11, 0)
+#define REW_PORT_VLAN_CFG_PORT_VID_SET(x)\
+	FIELD_PREP(REW_PORT_VLAN_CFG_PORT_VID, x)
+#define REW_PORT_VLAN_CFG_PORT_VID_GET(x)\
+	FIELD_GET(REW_PORT_VLAN_CFG_PORT_VID, x)
+
+/*      REW:PORT:TAG_CFG */
+#define REW_TAG_CFG(g)            __REG(TARGET_REW,\
+					0, 1, 4096, g, 11, 128, 4, 0, 1, 4)
+
+#define REW_TAG_CFG_TAG_CFG                      GENMASK(8, 7)
+#define REW_TAG_CFG_TAG_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CFG_TAG_CFG, x)
+#define REW_TAG_CFG_TAG_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CFG_TAG_CFG, x)
+
+#define REW_TAG_CFG_TAG_TPID_CFG                 GENMASK(6, 5)
+#define REW_TAG_CFG_TAG_TPID_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CFG_TAG_TPID_CFG, x)
+#define REW_TAG_CFG_TAG_TPID_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CFG_TAG_TPID_CFG, x)
+
+#define REW_TAG_CFG_TAG_VID_CFG                  BIT(4)
+#define REW_TAG_CFG_TAG_VID_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CFG_TAG_VID_CFG, x)
+#define REW_TAG_CFG_TAG_VID_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CFG_TAG_VID_CFG, x)
+
+#define REW_TAG_CFG_TAG_PCP_CFG                  GENMASK(3, 2)
+#define REW_TAG_CFG_TAG_PCP_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CFG_TAG_PCP_CFG, x)
+#define REW_TAG_CFG_TAG_PCP_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CFG_TAG_PCP_CFG, x)
+
+#define REW_TAG_CFG_TAG_DEI_CFG                  GENMASK(1, 0)
+#define REW_TAG_CFG_TAG_DEI_CFG_SET(x)\
+	FIELD_PREP(REW_TAG_CFG_TAG_DEI_CFG, x)
+#define REW_TAG_CFG_TAG_DEI_CFG_GET(x)\
+	FIELD_GET(REW_TAG_CFG_TAG_DEI_CFG, x)
+
+/*      REW:PORT:PORT_CFG */
+#define REW_PORT_CFG(g)           __REG(TARGET_REW,\
+					0, 1, 4096, g, 11, 128, 8, 0, 1, 4)
+
+#define REW_PORT_CFG_ES0_EN                      BIT(4)
+#define REW_PORT_CFG_ES0_EN_SET(x)\
+	FIELD_PREP(REW_PORT_CFG_ES0_EN, x)
+#define REW_PORT_CFG_ES0_EN_GET(x)\
+	FIELD_GET(REW_PORT_CFG_ES0_EN, x)
+
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG       GENMASK(3, 2)
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG_SET(x)\
+	FIELD_PREP(REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG, x)
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG_GET(x)\
+	FIELD_GET(REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG, x)
+
+#define REW_PORT_CFG_FCS_UPDATE_CPU_ENA          BIT(1)
+#define REW_PORT_CFG_FCS_UPDATE_CPU_ENA_SET(x)\
+	FIELD_PREP(REW_PORT_CFG_FCS_UPDATE_CPU_ENA, x)
+#define REW_PORT_CFG_FCS_UPDATE_CPU_ENA_GET(x)\
+	FIELD_GET(REW_PORT_CFG_FCS_UPDATE_CPU_ENA, x)
+
+#define REW_PORT_CFG_NO_REWRITE                  BIT(0)
+#define REW_PORT_CFG_NO_REWRITE_SET(x)\
+	FIELD_PREP(REW_PORT_CFG_NO_REWRITE, x)
+#define REW_PORT_CFG_NO_REWRITE_GET(x)\
+	FIELD_GET(REW_PORT_CFG_NO_REWRITE, x)
+
+/*      SYS:SYSTEM:RESET_CFG */
+#define SYS_RESET_CFG             __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 0, 0, 1, 4)
+
+#define SYS_RESET_CFG_CORE_ENA                   BIT(0)
+#define SYS_RESET_CFG_CORE_ENA_SET(x)\
+	FIELD_PREP(SYS_RESET_CFG_CORE_ENA, x)
+#define SYS_RESET_CFG_CORE_ENA_GET(x)\
+	FIELD_GET(SYS_RESET_CFG_CORE_ENA, x)
+
+/*      SYS:SYSTEM:PORT_MODE */
+#define SYS_PORT_MODE(r)          __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 48, r, 11, 4)
+
+#define SYS_PORT_MODE_PRP_LANID                  BIT(8)
+#define SYS_PORT_MODE_PRP_LANID_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_PRP_LANID, x)
+#define SYS_PORT_MODE_PRP_LANID_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_PRP_LANID, x)
+
+#define SYS_PORT_MODE_PRP_ENA                    BIT(7)
+#define SYS_PORT_MODE_PRP_ENA_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_PRP_ENA, x)
+#define SYS_PORT_MODE_PRP_ENA_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_PRP_ENA, x)
+
+#define SYS_PORT_MODE_INCL_INJ_HDR               GENMASK(6, 5)
+#define SYS_PORT_MODE_INCL_INJ_HDR_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_INCL_INJ_HDR, x)
+#define SYS_PORT_MODE_INCL_INJ_HDR_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_INCL_INJ_HDR, x)
+
+#define SYS_PORT_MODE_INCL_XTR_HDR               GENMASK(4, 3)
+#define SYS_PORT_MODE_INCL_XTR_HDR_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_INCL_XTR_HDR, x)
+#define SYS_PORT_MODE_INCL_XTR_HDR_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_INCL_XTR_HDR, x)
+
+#define SYS_PORT_MODE_INJ_HDR_ERR                BIT(2)
+#define SYS_PORT_MODE_INJ_HDR_ERR_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_INJ_HDR_ERR, x)
+#define SYS_PORT_MODE_INJ_HDR_ERR_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_INJ_HDR_ERR, x)
+
+#define SYS_PORT_MODE_PAD_DIS                    BIT(1)
+#define SYS_PORT_MODE_PAD_DIS_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_PAD_DIS, x)
+#define SYS_PORT_MODE_PAD_DIS_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_PAD_DIS, x)
+
+#define SYS_PORT_MODE_RTAG_CLEAR                 BIT(0)
+#define SYS_PORT_MODE_RTAG_CLEAR_SET(x)\
+	FIELD_PREP(SYS_PORT_MODE_RTAG_CLEAR, x)
+#define SYS_PORT_MODE_RTAG_CLEAR_GET(x)\
+	FIELD_GET(SYS_PORT_MODE_RTAG_CLEAR, x)
+
+/*      SYS:SYSTEM:FRONT_PORT_MODE */
+#define SYS_FRONT_PORT_MODE(r)    __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 92, r, 9, 4)
+
+#define SYS_FRONT_PORT_MODE_HDX_MODE             BIT(1)
+#define SYS_FRONT_PORT_MODE_HDX_MODE_SET(x)\
+	FIELD_PREP(SYS_FRONT_PORT_MODE_HDX_MODE, x)
+#define SYS_FRONT_PORT_MODE_HDX_MODE_GET(x)\
+	FIELD_GET(SYS_FRONT_PORT_MODE_HDX_MODE, x)
+
+#define SYS_FRONT_PORT_MODE_ADD_FRAG_SIZE        GENMASK(9, 8)
+#define SYS_FRONT_PORT_MODE_ADD_FRAG_SIZE_SET(x)\
+	FIELD_PREP(SYS_FRONT_PORT_MODE_ADD_FRAG_SIZE, x)
+#define SYS_FRONT_PORT_MODE_ADD_FRAG_SIZE_GET(x)\
+	FIELD_GET(SYS_FRONT_PORT_MODE_ADD_FRAG_SIZE, x)
+
+#define SYS_FRONT_PORT_MODE_DONT_WAIT_FOR_TS     BIT(0)
+#define SYS_FRONT_PORT_MODE_DONT_WAIT_FOR_TS_SET(x)\
+	FIELD_PREP(SYS_FRONT_PORT_MODE_DONT_WAIT_FOR_TS, x)
+#define SYS_FRONT_PORT_MODE_DONT_WAIT_FOR_TS_GET(x)\
+	FIELD_GET(SYS_FRONT_PORT_MODE_DONT_WAIT_FOR_TS, x)
+
+/*      SYS:SYSTEM:FRM_AGING */
+#define SYS_FRM_AGING             __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 128, 0, 1, 4)
+
+#define SYS_FRM_AGING_AGE_TX_ENA                 BIT(20)
+#define SYS_FRM_AGING_AGE_TX_ENA_SET(x)\
+	FIELD_PREP(SYS_FRM_AGING_AGE_TX_ENA, x)
+#define SYS_FRM_AGING_AGE_TX_ENA_GET(x)\
+	FIELD_GET(SYS_FRM_AGING_AGE_TX_ENA, x)
+
+#define SYS_FRM_AGING_MAX_AGE                    GENMASK(19, 0)
+#define SYS_FRM_AGING_MAX_AGE_SET(x)\
+	FIELD_PREP(SYS_FRM_AGING_MAX_AGE, x)
+#define SYS_FRM_AGING_MAX_AGE_GET(x)\
+	FIELD_GET(SYS_FRM_AGING_MAX_AGE, x)
+
+/*      SYS:SYSTEM:STAT_CFG */
+#define SYS_STAT_CFG              __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 132, 0, 1, 4)
+
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT             GENMASK(16, 10)
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT_SET(x)\
+	FIELD_PREP(SYS_STAT_CFG_STAT_CLEAR_SHOT, x)
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT_GET(x)\
+	FIELD_GET(SYS_STAT_CFG_STAT_CLEAR_SHOT, x)
+
+#define SYS_STAT_CFG_STAT_VIEW                   GENMASK(9, 0)
+#define SYS_STAT_CFG_STAT_VIEW_SET(x)\
+	FIELD_PREP(SYS_STAT_CFG_STAT_VIEW, x)
+#define SYS_STAT_CFG_STAT_VIEW_GET(x)\
+	FIELD_GET(SYS_STAT_CFG_STAT_VIEW, x)
+
+/*      SYS:SYSTEM:SW_STATUS */
+#define SYS_SW_STATUS(r)          __REG(TARGET_SYS,\
+					0, 1, 4160, 0, 1, 184, 136, r, 10, 4)
+
+#define SYS_SW_STATUS_PORT_RX_PAUSED             BIT(0)
+#define SYS_SW_STATUS_PORT_RX_PAUSED_SET(x)\
+	FIELD_PREP(SYS_SW_STATUS_PORT_RX_PAUSED, x)
+#define SYS_SW_STATUS_PORT_RX_PAUSED_GET(x)\
+	FIELD_GET(SYS_SW_STATUS_PORT_RX_PAUSED, x)
+
+/*      SYS:PAUSE_CFG:PAUSE_CFG */
+#define SYS_PAUSE_CFG(r)          __REG(TARGET_SYS,\
+					0, 1, 4344, 0, 1, 124, 0, r, 10, 4)
+
+#define SYS_PAUSE_CFG_PAUSE_START                GENMASK(18, 10)
+#define SYS_PAUSE_CFG_PAUSE_START_SET(x)\
+	FIELD_PREP(SYS_PAUSE_CFG_PAUSE_START, x)
+#define SYS_PAUSE_CFG_PAUSE_START_GET(x)\
+	FIELD_GET(SYS_PAUSE_CFG_PAUSE_START, x)
+
+#define SYS_PAUSE_CFG_PAUSE_STOP                 GENMASK(9, 1)
+#define SYS_PAUSE_CFG_PAUSE_STOP_SET(x)\
+	FIELD_PREP(SYS_PAUSE_CFG_PAUSE_STOP, x)
+#define SYS_PAUSE_CFG_PAUSE_STOP_GET(x)\
+	FIELD_GET(SYS_PAUSE_CFG_PAUSE_STOP, x)
+
+#define SYS_PAUSE_CFG_PAUSE_ENA                  BIT(0)
+#define SYS_PAUSE_CFG_PAUSE_ENA_SET(x)\
+	FIELD_PREP(SYS_PAUSE_CFG_PAUSE_ENA, x)
+#define SYS_PAUSE_CFG_PAUSE_ENA_GET(x)\
+	FIELD_GET(SYS_PAUSE_CFG_PAUSE_ENA, x)
+
+/*      SYS:PAUSE_CFG:ATOP */
+#define SYS_ATOP(r)               __REG(TARGET_SYS,\
+					0, 1, 4344, 0, 1, 124, 44, r, 10, 4)
+
+#define SYS_ATOP_ATOP                            GENMASK(8, 0)
+#define SYS_ATOP_ATOP_SET(x)\
+	FIELD_PREP(SYS_ATOP_ATOP, x)
+#define SYS_ATOP_ATOP_GET(x)\
+	FIELD_GET(SYS_ATOP_ATOP, x)
+
+/*      SYS:PAUSE_CFG:ATOP_TOT_CFG */
+#define SYS_ATOP_TOT_CFG          __REG(TARGET_SYS,\
+					0, 1, 4344, 0, 1, 124, 84, 0, 1, 4)
+
+#define SYS_ATOP_TOT_CFG_ATOP_TOT                GENMASK(8, 0)
+#define SYS_ATOP_TOT_CFG_ATOP_TOT_SET(x)\
+	FIELD_PREP(SYS_ATOP_TOT_CFG_ATOP_TOT, x)
+#define SYS_ATOP_TOT_CFG_ATOP_TOT_GET(x)\
+	FIELD_GET(SYS_ATOP_TOT_CFG_ATOP_TOT, x)
+
+/*      SYS:PAUSE_CFG:MAC_FC_CFG */
+#define SYS_MAC_FC_CFG(r)         __REG(TARGET_SYS,\
+					0, 1, 4344, 0, 1, 124, 88, r, 9, 4)
+
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED             GENMASK(27, 26)
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_FC_LINK_SPEED, x)
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_FC_LINK_SPEED, x)
+
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG            GENMASK(25, 20)
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_FC_LATENCY_CFG, x)
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_FC_LATENCY_CFG, x)
+
+#define SYS_MAC_FC_CFG_ZERO_PAUSE_ENA            BIT(18)
+#define SYS_MAC_FC_CFG_ZERO_PAUSE_ENA_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_ZERO_PAUSE_ENA, x)
+#define SYS_MAC_FC_CFG_ZERO_PAUSE_ENA_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_ZERO_PAUSE_ENA, x)
+
+#define SYS_MAC_FC_CFG_TX_FC_ENA                 BIT(17)
+#define SYS_MAC_FC_CFG_TX_FC_ENA_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_TX_FC_ENA, x)
+#define SYS_MAC_FC_CFG_TX_FC_ENA_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_TX_FC_ENA, x)
+
+#define SYS_MAC_FC_CFG_RX_FC_ENA                 BIT(16)
+#define SYS_MAC_FC_CFG_RX_FC_ENA_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_RX_FC_ENA, x)
+#define SYS_MAC_FC_CFG_RX_FC_ENA_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_RX_FC_ENA, x)
+
+#define SYS_MAC_FC_CFG_PAUSE_VAL_CFG             GENMASK(15, 0)
+#define SYS_MAC_FC_CFG_PAUSE_VAL_CFG_SET(x)\
+	FIELD_PREP(SYS_MAC_FC_CFG_PAUSE_VAL_CFG, x)
+#define SYS_MAC_FC_CFG_PAUSE_VAL_CFG_GET(x)\
+	FIELD_GET(SYS_MAC_FC_CFG_PAUSE_VAL_CFG, x)
+
+/*      SYS:STAT:CNT */
+#define SYS_CNT(g)                __REG(TARGET_SYS,\
+					0, 1, 0, g, 896, 4, 0, 0, 1, 4)
+
+/*      SYS:RAM_CTRL:RAM_INIT */
+#define SYS_RAM_INIT              __REG(TARGET_SYS,\
+					0, 1, 4492, 0, 1, 4, 0, 0, 1, 4)
+
+#define SYS_RAM_INIT_RAM_TEST_OPT                GENMASK(4, 2)
+#define SYS_RAM_INIT_RAM_TEST_OPT_SET(x)\
+	FIELD_PREP(SYS_RAM_INIT_RAM_TEST_OPT, x)
+#define SYS_RAM_INIT_RAM_TEST_OPT_GET(x)\
+	FIELD_GET(SYS_RAM_INIT_RAM_TEST_OPT, x)
+
+#define SYS_RAM_INIT_RAM_INIT                    BIT(1)
+#define SYS_RAM_INIT_RAM_INIT_SET(x)\
+	FIELD_PREP(SYS_RAM_INIT_RAM_INIT, x)
+#define SYS_RAM_INIT_RAM_INIT_GET(x)\
+	FIELD_GET(SYS_RAM_INIT_RAM_INIT, x)
+
+#define SYS_RAM_INIT_RAM_CFG_HOOK                BIT(0)
+#define SYS_RAM_INIT_RAM_CFG_HOOK_SET(x)\
+	FIELD_PREP(SYS_RAM_INIT_RAM_CFG_HOOK, x)
+#define SYS_RAM_INIT_RAM_CFG_HOOK_GET(x)\
+	FIELD_GET(SYS_RAM_INIT_RAM_CFG_HOOK, x)
+
+/*      SYS:PTPPORT:PTP_RXDLY_CFG */
+#define SYS_PTP_RXDLY_CFG(g)      __REG(TARGET_SYS,\
+					0, 1, 4512, g, 11, 28, 8, 0, 1, 4)
+
+#define SYS_PTP_RXDLY_CFG_PTP_RX_IO_DLY          GENMASK(23, 0)
+#define SYS_PTP_RXDLY_CFG_PTP_RX_IO_DLY_SET(x)\
+	FIELD_PREP(SYS_PTP_RXDLY_CFG_PTP_RX_IO_DLY, x)
+#define SYS_PTP_RXDLY_CFG_PTP_RX_IO_DLY_GET(x)\
+	FIELD_GET(SYS_PTP_RXDLY_CFG_PTP_RX_IO_DLY, x)
+
+/*      SYS:PTPPORT:PTP_TXDLY_CFG */
+#define SYS_PTP_TXDLY_CFG(g)      __REG(TARGET_SYS,\
+					0, 1, 4512, g, 11, 28, 12, 0, 1, 4)
+
+#define SYS_PTP_TXDLY_CFG_PTP_TX_IO_DLY          GENMASK(23, 0)
+#define SYS_PTP_TXDLY_CFG_PTP_TX_IO_DLY_SET(x)\
+	FIELD_PREP(SYS_PTP_TXDLY_CFG_PTP_TX_IO_DLY, x)
+#define SYS_PTP_TXDLY_CFG_PTP_TX_IO_DLY_GET(x)\
+	FIELD_GET(SYS_PTP_TXDLY_CFG_PTP_TX_IO_DLY, x)
+
+#endif /* _LAN9645X_REGS_H_ */

-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next v3 9/9] net: dsa: lan9645x: add port statistics
From: Jens Emil Schulz Østergaard @ 2026-04-10 11:48 UTC (permalink / raw)
  To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Woojung Huh,
	Russell King, Steen Hegelund, Daniel Machon
  Cc: linux-kernel, netdev, devicetree,
	Jens Emil Schulz Østergaard
In-Reply-To: <20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com>

Add statistics support for the port counters. Chip registers are 32 bit,
so this unit is responsible maintaining a 64bit software cache, and
updating it frequently to handle overflows in hardware.

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v3:
- No changes

Changes in v2:
- introduce spinlock sw_lock protecting software counters and region
  buffer.
- ran Ioana's selftest for standard counters
        drivers/net/hw/ethtool_std_stats.sh
  along with
        selftests/drivers/net/hw/ethtool_rmon.sh
  they pass except for software injected pause frames.
- remove strings/counters covered by standard counters from
  get_strings/get_ethtool_stats.
- fix proper use of 'src' in standard counters
- remove static region table, and use stats_prepare_regions for dynamic
  region calculation inspired by ocelot.
- fix queue leak in error path.
---
 drivers/net/dsa/microchip/lan9645x/Makefile        |   1 +
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.c |  86 ++
 drivers/net/dsa/microchip/lan9645x/lan9645x_main.h |   3 +
 .../net/dsa/microchip/lan9645x/lan9645x_stats.c    | 922 +++++++++++++++++++++
 .../net/dsa/microchip/lan9645x/lan9645x_stats.h    | 277 +++++++
 5 files changed, 1289 insertions(+)

diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile
index 2413d11fe849..cd994943c1c0 100644
--- a/drivers/net/dsa/microchip/lan9645x/Makefile
+++ b/drivers/net/dsa/microchip/lan9645x/Makefile
@@ -8,4 +8,5 @@ mchp-lan9645x-objs := \
 	lan9645x_npi.o \
 	lan9645x_phylink.o \
 	lan9645x_port.o \
+	lan9645x_stats.o \
 	lan9645x_vlan.o \
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
index 8f63729ff55d..738a01ee6e64 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
@@ -5,6 +5,7 @@
 #include <linux/platform_device.h>
 
 #include "lan9645x_main.h"
+#include "lan9645x_stats.h"
 
 static const char *lan9645x_resource_names[NUM_TARGETS + 1] = {
 	[TARGET_GCB]          = "gcb",
@@ -73,6 +74,7 @@ static void lan9645x_teardown(struct dsa_switch *ds)
 	lan9645x_npi_port_deinit(lan9645x, lan9645x->npi);
 	lan9645x_mac_deinit(lan9645x);
 	lan9645x_mdb_deinit(lan9645x);
+	lan9645x_stats_deinit(lan9645x);
 	mutex_destroy(&lan9645x->fwd_domain_lock);
 }
 
@@ -266,6 +268,12 @@ static int lan9645x_setup(struct dsa_switch *ds)
 	if (!lan9645x->owq)
 		return -ENOMEM;
 
+	err = lan9645x_stats_init(lan9645x);
+	if (err) {
+		dev_err(dev, "Failed to init stats.\n");
+		goto owq_destroy;
+	}
+
 	ds->mtu_enforcement_ingress = true;
 	ds->assisted_learning_on_cpu_port = true;
 	ds->fdb_isolation = true;
@@ -276,6 +284,10 @@ static int lan9645x_setup(struct dsa_switch *ds)
 		 lan9645x->num_phys_ports - lan9645x->num_port_dis);
 
 	return 0;
+
+owq_destroy:
+	destroy_workqueue(lan9645x->owq);
+	return err;
 }
 
 static void lan9645x_port_phylink_get_caps(struct dsa_switch *ds, int port,
@@ -737,6 +749,68 @@ static int lan9645x_mdb_del(struct dsa_switch *ds, int port,
 	return err;
 }
 
+static void lan9645x_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+				 uint8_t *data)
+{
+	lan9645x_stats_get_strings(ds->priv, port, stringset, data);
+}
+
+static void lan9645x_get_ethtool_stats(struct dsa_switch *ds, int port,
+				       uint64_t *data)
+{
+	lan9645x_stats_get_ethtool_stats(ds->priv, port, data);
+}
+
+static int lan9645x_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+	return lan9645x_stats_get_sset_count(ds->priv, port, sset);
+}
+
+static void lan9645x_get_eth_mac_stats(struct dsa_switch *ds, int port,
+				       struct ethtool_eth_mac_stats *mac_stats)
+{
+	lan9645x_stats_get_eth_mac_stats(ds->priv, port, mac_stats);
+}
+
+static void
+lan9645x_get_rmon_stats(struct dsa_switch *ds, int port,
+			struct ethtool_rmon_stats *rmon_stats,
+			const struct ethtool_rmon_hist_range **ranges)
+{
+	lan9645x_stats_get_rmon_stats(ds->priv, port, rmon_stats, ranges);
+}
+
+static void lan9645x_get_stats64(struct dsa_switch *ds, int port,
+				 struct rtnl_link_stats64 *s)
+{
+	lan9645x_stats_get_stats64(ds->priv, port, s);
+}
+
+static void lan9645x_get_pause_stats(struct dsa_switch *ds, int port,
+				     struct ethtool_pause_stats *pause_stats)
+{
+	lan9645x_stats_get_pause_stats(ds->priv, port, pause_stats);
+}
+
+static void lan9645x_get_mm_stats(struct dsa_switch *ds, int port,
+				  struct ethtool_mm_stats *stats)
+{
+	lan9645x_stats_get_mm_stats(ds->priv, port, stats);
+}
+
+static void lan9645x_get_eth_phy_stats(struct dsa_switch *ds, int port,
+				       struct ethtool_eth_phy_stats *phy_stats)
+{
+	lan9645x_stats_get_eth_phy_stats(ds->priv, port, phy_stats);
+}
+
+static void
+lan9645x_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+			    struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+	lan9645x_stats_get_eth_ctrl_stats(ds->priv, port, ctrl_stats);
+}
+
 static const struct dsa_switch_ops lan9645x_switch_ops = {
 	.get_tag_protocol		= lan9645x_get_tag_protocol,
 
@@ -774,6 +848,18 @@ static const struct dsa_switch_ops lan9645x_switch_ops = {
 	/* Multicast database */
 	.port_mdb_add			= lan9645x_mdb_add,
 	.port_mdb_del			= lan9645x_mdb_del,
+
+	/* Port statistics counters. */
+	.get_strings			= lan9645x_get_strings,
+	.get_ethtool_stats		= lan9645x_get_ethtool_stats,
+	.get_sset_count			= lan9645x_get_sset_count,
+	.get_eth_mac_stats		= lan9645x_get_eth_mac_stats,
+	.get_rmon_stats			= lan9645x_get_rmon_stats,
+	.get_stats64			= lan9645x_get_stats64,
+	.get_pause_stats		= lan9645x_get_pause_stats,
+	.get_mm_stats			= lan9645x_get_mm_stats,
+	.get_eth_phy_stats		= lan9645x_get_eth_phy_stats,
+	.get_eth_ctrl_stats		= lan9645x_get_eth_ctrl_stats,
 };
 
 static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x)
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
index 1c3c9899ed07..8d40eb1b6103 100644
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
@@ -226,6 +226,9 @@ struct lan9645x {
 	 */
 	struct mutex mdb_lock;
 
+	/* Statistics  */
+	struct lan9645x_stats *stats;
+
 	int num_port_dis;
 	bool dd_dis;
 	bool tsn_dis;
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c
new file mode 100644
index 000000000000..3e82a859f173
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "lan9645x_main.h"
+#include "lan9645x_stats.h"
+
+#define LAN9645X_STATS_CHECK_DELAY	(3 * HZ)
+
+static const u32 lan9645x_port_stats_layout[] = {
+	[SCNT_RX_OCT]              = 0x0,
+	[SCNT_RX_UC]               = 0x1,
+	[SCNT_RX_MC]               = 0x2,
+	[SCNT_RX_BC]               = 0x3,
+	[SCNT_RX_SHORT]            = 0x4,
+	[SCNT_RX_FRAG]             = 0x5,
+	[SCNT_RX_JABBER]           = 0x6,
+	[SCNT_RX_CRC]              = 0x7,
+	[SCNT_RX_SYMBOL_ERR]       = 0x8,
+	[SCNT_RX_SZ_64]            = 0x9,
+	[SCNT_RX_SZ_65_127]        = 0xa,
+	[SCNT_RX_SZ_128_255]       = 0xb,
+	[SCNT_RX_SZ_256_511]       = 0xc,
+	[SCNT_RX_SZ_512_1023]      = 0xd,
+	[SCNT_RX_SZ_1024_1526]     = 0xe,
+	[SCNT_RX_SZ_JUMBO]         = 0xf,
+	[SCNT_RX_PAUSE]            = 0x10,
+	[SCNT_RX_CONTROL]          = 0x11,
+	[SCNT_RX_LONG]             = 0x12,
+	[SCNT_RX_CAT_DROP]         = 0x13,
+	[SCNT_RX_RED_PRIO_0]       = 0x14,
+	[SCNT_RX_RED_PRIO_1]       = 0x15,
+	[SCNT_RX_RED_PRIO_2]       = 0x16,
+	[SCNT_RX_RED_PRIO_3]       = 0x17,
+	[SCNT_RX_RED_PRIO_4]       = 0x18,
+	[SCNT_RX_RED_PRIO_5]       = 0x19,
+	[SCNT_RX_RED_PRIO_6]       = 0x1a,
+	[SCNT_RX_RED_PRIO_7]       = 0x1b,
+	[SCNT_RX_YELLOW_PRIO_0]    = 0x1c,
+	[SCNT_RX_YELLOW_PRIO_1]    = 0x1d,
+	[SCNT_RX_YELLOW_PRIO_2]    = 0x1e,
+	[SCNT_RX_YELLOW_PRIO_3]    = 0x1f,
+	[SCNT_RX_YELLOW_PRIO_4]    = 0x20,
+	[SCNT_RX_YELLOW_PRIO_5]    = 0x21,
+	[SCNT_RX_YELLOW_PRIO_6]    = 0x22,
+	[SCNT_RX_YELLOW_PRIO_7]    = 0x23,
+	[SCNT_RX_GREEN_PRIO_0]     = 0x24,
+	[SCNT_RX_GREEN_PRIO_1]     = 0x25,
+	[SCNT_RX_GREEN_PRIO_2]     = 0x26,
+	[SCNT_RX_GREEN_PRIO_3]     = 0x27,
+	[SCNT_RX_GREEN_PRIO_4]     = 0x28,
+	[SCNT_RX_GREEN_PRIO_5]     = 0x29,
+	[SCNT_RX_GREEN_PRIO_6]     = 0x2a,
+	[SCNT_RX_GREEN_PRIO_7]     = 0x2b,
+	[SCNT_RX_ASSEMBLY_ERR]     = 0x2c,
+	[SCNT_RX_SMD_ERR]          = 0x2d,
+	[SCNT_RX_ASSEMBLY_OK]      = 0x2e,
+	[SCNT_RX_MERGE_FRAG]       = 0x2f,
+	[SCNT_RX_PMAC_OCT]         = 0x30,
+	[SCNT_RX_PMAC_UC]          = 0x31,
+	[SCNT_RX_PMAC_MC]          = 0x32,
+	[SCNT_RX_PMAC_BC]          = 0x33,
+	[SCNT_RX_PMAC_SHORT]       = 0x34,
+	[SCNT_RX_PMAC_FRAG]        = 0x35,
+	[SCNT_RX_PMAC_JABBER]      = 0x36,
+	[SCNT_RX_PMAC_CRC]         = 0x37,
+	[SCNT_RX_PMAC_SYMBOL_ERR]  = 0x38,
+	[SCNT_RX_PMAC_SZ_64]       = 0x39,
+	[SCNT_RX_PMAC_SZ_65_127]   = 0x3a,
+	[SCNT_RX_PMAC_SZ_128_255]  = 0x3b,
+	[SCNT_RX_PMAC_SZ_256_511]  = 0x3c,
+	[SCNT_RX_PMAC_SZ_512_1023] = 0x3d,
+	[SCNT_RX_PMAC_SZ_1024_1526] = 0x3e,
+	[SCNT_RX_PMAC_SZ_JUMBO]    = 0x3f,
+	[SCNT_RX_PMAC_PAUSE]       = 0x40,
+	[SCNT_RX_PMAC_CONTROL]     = 0x41,
+	[SCNT_RX_PMAC_LONG]        = 0x42,
+	[SCNT_TX_OCT]              = 0x80,
+	[SCNT_TX_UC]               = 0x81,
+	[SCNT_TX_MC]               = 0x82,
+	[SCNT_TX_BC]               = 0x83,
+	[SCNT_TX_COL]              = 0x84,
+	[SCNT_TX_DROP]             = 0x85,
+	[SCNT_TX_PAUSE]            = 0x86,
+	[SCNT_TX_SZ_64]            = 0x87,
+	[SCNT_TX_SZ_65_127]        = 0x88,
+	[SCNT_TX_SZ_128_255]       = 0x89,
+	[SCNT_TX_SZ_256_511]       = 0x8a,
+	[SCNT_TX_SZ_512_1023]      = 0x8b,
+	[SCNT_TX_SZ_1024_1526]     = 0x8c,
+	[SCNT_TX_SZ_JUMBO]         = 0x8d,
+	[SCNT_TX_YELLOW_PRIO_0]    = 0x8e,
+	[SCNT_TX_YELLOW_PRIO_1]    = 0x8f,
+	[SCNT_TX_YELLOW_PRIO_2]    = 0x90,
+	[SCNT_TX_YELLOW_PRIO_3]    = 0x91,
+	[SCNT_TX_YELLOW_PRIO_4]    = 0x92,
+	[SCNT_TX_YELLOW_PRIO_5]    = 0x93,
+	[SCNT_TX_YELLOW_PRIO_6]    = 0x94,
+	[SCNT_TX_YELLOW_PRIO_7]    = 0x95,
+	[SCNT_TX_GREEN_PRIO_0]     = 0x96,
+	[SCNT_TX_GREEN_PRIO_1]     = 0x97,
+	[SCNT_TX_GREEN_PRIO_2]     = 0x98,
+	[SCNT_TX_GREEN_PRIO_3]     = 0x99,
+	[SCNT_TX_GREEN_PRIO_4]     = 0x9a,
+	[SCNT_TX_GREEN_PRIO_5]     = 0x9b,
+	[SCNT_TX_GREEN_PRIO_6]     = 0x9c,
+	[SCNT_TX_GREEN_PRIO_7]     = 0x9d,
+	[SCNT_TX_AGED]             = 0x9e,
+	[SCNT_TX_LLCT]             = 0x9f,
+	[SCNT_TX_CT]               = 0xa0,
+	[SCNT_TX_BUFDROP]          = 0xa1,
+	[SCNT_TX_MM_HOLD]          = 0xa2,
+	[SCNT_TX_MERGE_FRAG]       = 0xa3,
+	[SCNT_TX_PMAC_OCT]         = 0xa4,
+	[SCNT_TX_PMAC_UC]          = 0xa5,
+	[SCNT_TX_PMAC_MC]          = 0xa6,
+	[SCNT_TX_PMAC_BC]          = 0xa7,
+	[SCNT_TX_PMAC_PAUSE]       = 0xa8,
+	[SCNT_TX_PMAC_SZ_64]       = 0xa9,
+	[SCNT_TX_PMAC_SZ_65_127]   = 0xaa,
+	[SCNT_TX_PMAC_SZ_128_255]  = 0xab,
+	[SCNT_TX_PMAC_SZ_256_511]  = 0xac,
+	[SCNT_TX_PMAC_SZ_512_1023] = 0xad,
+	[SCNT_TX_PMAC_SZ_1024_1526] = 0xae,
+	[SCNT_TX_PMAC_SZ_JUMBO]    = 0xaf,
+	[SCNT_DR_LOCAL]            = 0x100,
+	[SCNT_DR_TAIL]             = 0x101,
+	[SCNT_DR_YELLOW_PRIO_0]    = 0x102,
+	[SCNT_DR_YELLOW_PRIO_1]    = 0x103,
+	[SCNT_DR_YELLOW_PRIO_2]    = 0x104,
+	[SCNT_DR_YELLOW_PRIO_3]    = 0x105,
+	[SCNT_DR_YELLOW_PRIO_4]    = 0x106,
+	[SCNT_DR_YELLOW_PRIO_5]    = 0x107,
+	[SCNT_DR_YELLOW_PRIO_6]    = 0x108,
+	[SCNT_DR_YELLOW_PRIO_7]    = 0x109,
+	[SCNT_DR_GREEN_PRIO_0]     = 0x10a,
+	[SCNT_DR_GREEN_PRIO_1]     = 0x10b,
+	[SCNT_DR_GREEN_PRIO_2]     = 0x10c,
+	[SCNT_DR_GREEN_PRIO_3]     = 0x10d,
+	[SCNT_DR_GREEN_PRIO_4]     = 0x10e,
+	[SCNT_DR_GREEN_PRIO_5]     = 0x10f,
+	[SCNT_DR_GREEN_PRIO_6]     = 0x110,
+	[SCNT_DR_GREEN_PRIO_7]     = 0x111,
+};
+
+struct lan9645x_ethtool_stat {
+	char name[ETH_GSTRING_LEN];
+	u16 idx;
+};
+
+static const struct lan9645x_ethtool_stat lan9645x_port_ethtool_stats[] = {
+	{ "rx_uc",              SCNT_RX_UC },
+	{ "rx_cat_drop",        SCNT_RX_CAT_DROP },
+	{ "rx_red_prio_0",      SCNT_RX_RED_PRIO_0 },
+	{ "rx_red_prio_1",      SCNT_RX_RED_PRIO_1 },
+	{ "rx_red_prio_2",      SCNT_RX_RED_PRIO_2 },
+	{ "rx_red_prio_3",      SCNT_RX_RED_PRIO_3 },
+	{ "rx_red_prio_4",      SCNT_RX_RED_PRIO_4 },
+	{ "rx_red_prio_5",      SCNT_RX_RED_PRIO_5 },
+	{ "rx_red_prio_6",      SCNT_RX_RED_PRIO_6 },
+	{ "rx_red_prio_7",      SCNT_RX_RED_PRIO_7 },
+	{ "rx_yellow_prio_0",   SCNT_RX_YELLOW_PRIO_0 },
+	{ "rx_yellow_prio_1",   SCNT_RX_YELLOW_PRIO_1 },
+	{ "rx_yellow_prio_2",   SCNT_RX_YELLOW_PRIO_2 },
+	{ "rx_yellow_prio_3",   SCNT_RX_YELLOW_PRIO_3 },
+	{ "rx_yellow_prio_4",   SCNT_RX_YELLOW_PRIO_4 },
+	{ "rx_yellow_prio_5",   SCNT_RX_YELLOW_PRIO_5 },
+	{ "rx_yellow_prio_6",   SCNT_RX_YELLOW_PRIO_6 },
+	{ "rx_yellow_prio_7",   SCNT_RX_YELLOW_PRIO_7 },
+	{ "rx_green_prio_0",    SCNT_RX_GREEN_PRIO_0 },
+	{ "rx_green_prio_1",    SCNT_RX_GREEN_PRIO_1 },
+	{ "rx_green_prio_2",    SCNT_RX_GREEN_PRIO_2 },
+	{ "rx_green_prio_3",    SCNT_RX_GREEN_PRIO_3 },
+	{ "rx_green_prio_4",    SCNT_RX_GREEN_PRIO_4 },
+	{ "rx_green_prio_5",    SCNT_RX_GREEN_PRIO_5 },
+	{ "rx_green_prio_6",    SCNT_RX_GREEN_PRIO_6 },
+	{ "rx_green_prio_7",    SCNT_RX_GREEN_PRIO_7 },
+	{ "tx_uc",              SCNT_TX_UC },
+	{ "tx_drop",            SCNT_TX_DROP },
+	{ "tx_yellow_prio_0",   SCNT_TX_YELLOW_PRIO_0 },
+	{ "tx_yellow_prio_1",   SCNT_TX_YELLOW_PRIO_1 },
+	{ "tx_yellow_prio_2",   SCNT_TX_YELLOW_PRIO_2 },
+	{ "tx_yellow_prio_3",   SCNT_TX_YELLOW_PRIO_3 },
+	{ "tx_yellow_prio_4",   SCNT_TX_YELLOW_PRIO_4 },
+	{ "tx_yellow_prio_5",   SCNT_TX_YELLOW_PRIO_5 },
+	{ "tx_yellow_prio_6",   SCNT_TX_YELLOW_PRIO_6 },
+	{ "tx_yellow_prio_7",   SCNT_TX_YELLOW_PRIO_7 },
+	{ "tx_green_prio_0",    SCNT_TX_GREEN_PRIO_0 },
+	{ "tx_green_prio_1",    SCNT_TX_GREEN_PRIO_1 },
+	{ "tx_green_prio_2",    SCNT_TX_GREEN_PRIO_2 },
+	{ "tx_green_prio_3",    SCNT_TX_GREEN_PRIO_3 },
+	{ "tx_green_prio_4",    SCNT_TX_GREEN_PRIO_4 },
+	{ "tx_green_prio_5",    SCNT_TX_GREEN_PRIO_5 },
+	{ "tx_green_prio_6",    SCNT_TX_GREEN_PRIO_6 },
+	{ "tx_green_prio_7",    SCNT_TX_GREEN_PRIO_7 },
+	{ "tx_aged",            SCNT_TX_AGED },
+	{ "tx_bufdrop",         SCNT_TX_BUFDROP },
+	{ "dr_local",           SCNT_DR_LOCAL },
+	{ "dr_tail",            SCNT_DR_TAIL },
+	{ "dr_yellow_prio_0",   SCNT_DR_YELLOW_PRIO_0 },
+	{ "dr_yellow_prio_1",   SCNT_DR_YELLOW_PRIO_1 },
+	{ "dr_yellow_prio_2",   SCNT_DR_YELLOW_PRIO_2 },
+	{ "dr_yellow_prio_3",   SCNT_DR_YELLOW_PRIO_3 },
+	{ "dr_yellow_prio_4",   SCNT_DR_YELLOW_PRIO_4 },
+	{ "dr_yellow_prio_5",   SCNT_DR_YELLOW_PRIO_5 },
+	{ "dr_yellow_prio_6",   SCNT_DR_YELLOW_PRIO_6 },
+	{ "dr_yellow_prio_7",   SCNT_DR_YELLOW_PRIO_7 },
+	{ "dr_green_prio_0",    SCNT_DR_GREEN_PRIO_0 },
+	{ "dr_green_prio_1",    SCNT_DR_GREEN_PRIO_1 },
+	{ "dr_green_prio_2",    SCNT_DR_GREEN_PRIO_2 },
+	{ "dr_green_prio_3",    SCNT_DR_GREEN_PRIO_3 },
+	{ "dr_green_prio_4",    SCNT_DR_GREEN_PRIO_4 },
+	{ "dr_green_prio_5",    SCNT_DR_GREEN_PRIO_5 },
+	{ "dr_green_prio_6",    SCNT_DR_GREEN_PRIO_6 },
+	{ "dr_green_prio_7",    SCNT_DR_GREEN_PRIO_7 },
+};
+
+static const struct lan9645x_view_stats lan9645x_view_stat_cfgs[] = {
+	[LAN9645X_STAT_PORTS] = {
+		.name = "ports",
+		.type = LAN9645X_STAT_PORTS,
+		.layout = lan9645x_port_stats_layout,
+		.num_cnts = ARRAY_SIZE(lan9645x_port_stats_layout),
+		.num_indexes = NUM_PHYS_PORTS,
+	},
+};
+
+static int __lan9645x_stats_view_idx_hw_read(struct lan9645x *lan9645x,
+					     enum lan9645x_view_stat_type vtype,
+					     int idx)
+{
+	struct lan9645x_stat_region region;
+	struct lan9645x_view_stats *vstats;
+	u32 *region_buf;
+	int err;
+
+	lockdep_assert_held(&lan9645x->stats->hw_lock);
+
+	vstats = lan9645x_get_vstats(lan9645x, vtype);
+	if (!vstats || idx < 0 || idx >= vstats->num_indexes)
+		return -EINVAL;
+
+	lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(idx), lan9645x, SYS_STAT_CFG);
+
+	region_buf = &vstats->buf[vstats->num_cnts * idx];
+
+	/* Each region for this index contains counters which are at sequential
+	 * addresses, so we can use bulk reads to ease lock pressure a bit.
+	 */
+	for (int r = 0; r < vstats->num_regions; r++) {
+		region = vstats->regions[r];
+		err = lan_bulk_rd(&region_buf[region.cnts_base_idx], region.cnt,
+				  lan9645x, SYS_CNT(region.base_offset));
+		if (err) {
+			dev_err(lan9645x->dev,
+				"stats bulk read err vtype=%d idx=%d err=%d\n",
+				vtype, idx, err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void
+__lan9645x_stats_view_idx_transfer(struct lan9645x *lan9645x,
+				   enum lan9645x_view_stat_type vtype, int idx)
+{
+	struct lan9645x_view_stats *vstats;
+	u64 *idx_counters;
+	u32 *region_buf;
+	int cntr;
+
+	lockdep_assert_held(&lan9645x->stats->sw_lock);
+
+	vstats = lan9645x_get_vstats(lan9645x, vtype);
+	if (!vstats || idx < 0 || idx >= vstats->num_indexes)
+		return;
+
+	idx_counters = STATS_INDEX(vstats, idx);
+	region_buf = &vstats->buf[vstats->num_cnts * idx];
+
+	for (cntr = 0; cntr < vstats->num_cnts; cntr++)
+		lan9645x_stats_add_cnt(&idx_counters[cntr], region_buf[cntr]);
+}
+
+static void __lan9645x_stats_view_idx_update(struct lan9645x *lan9645x,
+					     enum lan9645x_view_stat_type vtype,
+					     int idx)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+
+	lockdep_assert_held(&s->hw_lock);
+
+	if (!__lan9645x_stats_view_idx_hw_read(lan9645x, vtype, idx)) {
+		spin_lock(&s->sw_lock);
+		__lan9645x_stats_view_idx_transfer(lan9645x, vtype, idx);
+		spin_unlock(&s->sw_lock);
+	}
+}
+
+static u64 *lan9645x_stats_view_idx_update(struct lan9645x *lan9645x,
+					   enum lan9645x_view_stat_type vtype,
+					   int idx)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+
+	mutex_lock(&s->hw_lock);
+	__lan9645x_stats_view_idx_update(lan9645x, vtype, idx);
+	mutex_unlock(&s->hw_lock);
+
+	return STAT_COUNTERS(lan9645x, vtype, idx);
+}
+
+static void lan9645x_stats_view_update(struct lan9645x *lan9645x,
+				       enum lan9645x_view_stat_type vtype)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	struct lan9645x_view_stats *vstats;
+	int idx;
+
+	vstats = lan9645x_get_vstats(lan9645x, vtype);
+	if (!vstats)
+		return;
+
+	switch (vtype) {
+	case LAN9645X_STAT_PORTS:
+		mutex_lock(&s->hw_lock);
+		for (idx = 0; idx < vstats->num_indexes; idx++) {
+			if (dsa_is_unused_port(lan9645x->ds, idx))
+				continue;
+			__lan9645x_stats_view_idx_update(lan9645x, vtype, idx);
+		}
+		mutex_unlock(&s->hw_lock);
+		return;
+	default:
+		return;
+	}
+}
+
+static void lan9645x_stats_update(struct lan9645x *lan9645x)
+{
+	for (int vtype = 0; vtype < LAN9645X_STAT_NUM; vtype++)
+		lan9645x_stats_view_update(lan9645x, vtype);
+}
+
+void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port,
+				u32 stringset, u8 *data)
+{
+	int i;
+
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(lan9645x_port_ethtool_stats); i++)
+		memcpy(data + i * ETH_GSTRING_LEN,
+		       lan9645x_port_ethtool_stats[i].name, ETH_GSTRING_LEN);
+}
+
+int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port, int sset)
+{
+	if (sset != ETH_SS_STATS)
+		return -EOPNOTSUPP;
+
+	return ARRAY_SIZE(lan9645x_port_ethtool_stats);
+}
+
+void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port,
+				      u64 *data)
+{
+	struct lan9645x_stats *stats = lan9645x->stats;
+	u64 *c;
+	int i;
+
+	c = lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port);
+
+	spin_lock(&stats->sw_lock);
+	for (i = 0; i < ARRAY_SIZE(lan9645x_port_ethtool_stats); i++)
+		*data++ = c[lan9645x_port_ethtool_stats[i].idx];
+	spin_unlock(&stats->sw_lock);
+}
+
+static u64 *lan9645x_stats_port_update(struct lan9645x *lan9645x, int port)
+{
+	return lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS,
+					      port);
+}
+
+void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port,
+				      struct ethtool_eth_mac_stats *m)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	switch (m->src) {
+	case ETHTOOL_MAC_STATS_SRC_EMAC:
+		m->FramesTransmittedOK = c[SCNT_TX_UC] +
+					 c[SCNT_TX_MC] +
+					 c[SCNT_TX_BC];
+		m->SingleCollisionFrames = c[SCNT_TX_COL];
+		m->FramesReceivedOK = c[SCNT_RX_UC] +
+				      c[SCNT_RX_MC] +
+				      c[SCNT_RX_BC];
+		m->FrameCheckSequenceErrors = c[SCNT_RX_CRC];
+		m->OctetsTransmittedOK = c[SCNT_TX_OCT];
+		m->OctetsReceivedOK = c[SCNT_RX_OCT];
+		m->MulticastFramesXmittedOK = c[SCNT_TX_MC];
+		m->BroadcastFramesXmittedOK = c[SCNT_TX_BC];
+		m->MulticastFramesReceivedOK = c[SCNT_RX_MC];
+		m->BroadcastFramesReceivedOK = c[SCNT_RX_BC];
+		m->InRangeLengthErrors = c[SCNT_RX_FRAG] +
+					 c[SCNT_RX_JABBER] +
+					 c[SCNT_RX_CRC];
+		m->OutOfRangeLengthField = c[SCNT_RX_SHORT] +
+					   c[SCNT_RX_LONG];
+		m->FrameTooLongErrors = c[SCNT_RX_LONG];
+		break;
+	case ETHTOOL_MAC_STATS_SRC_PMAC:
+		m->FramesTransmittedOK = c[SCNT_TX_PMAC_UC] +
+					 c[SCNT_TX_PMAC_MC] +
+					 c[SCNT_TX_PMAC_BC];
+		m->FramesReceivedOK = c[SCNT_RX_PMAC_UC] +
+				      c[SCNT_RX_PMAC_MC] +
+				      c[SCNT_RX_PMAC_BC];
+		m->FrameCheckSequenceErrors = c[SCNT_RX_PMAC_CRC];
+		m->OctetsTransmittedOK = c[SCNT_TX_PMAC_OCT];
+		m->OctetsReceivedOK = c[SCNT_RX_PMAC_OCT];
+		m->MulticastFramesXmittedOK = c[SCNT_TX_PMAC_MC];
+		m->BroadcastFramesXmittedOK = c[SCNT_TX_PMAC_BC];
+		m->MulticastFramesReceivedOK = c[SCNT_RX_PMAC_MC];
+		m->BroadcastFramesReceivedOK = c[SCNT_RX_PMAC_BC];
+		m->InRangeLengthErrors = c[SCNT_RX_PMAC_FRAG] +
+					 c[SCNT_RX_PMAC_JABBER] +
+					 c[SCNT_RX_PMAC_CRC];
+		m->OutOfRangeLengthField = c[SCNT_RX_PMAC_SHORT] +
+					   c[SCNT_RX_PMAC_LONG];
+		m->FrameTooLongErrors = c[SCNT_RX_PMAC_LONG];
+		break;
+	default:
+		m->FramesTransmittedOK = c[SCNT_TX_UC] +
+					 c[SCNT_TX_MC] +
+					 c[SCNT_TX_BC] +
+					 c[SCNT_TX_PMAC_UC] +
+					 c[SCNT_TX_PMAC_MC] +
+					 c[SCNT_TX_PMAC_BC];
+		m->SingleCollisionFrames = c[SCNT_TX_COL];
+		m->FramesReceivedOK = c[SCNT_RX_UC] +
+				      c[SCNT_RX_MC] +
+				      c[SCNT_RX_BC] +
+				      c[SCNT_RX_PMAC_UC] +
+				      c[SCNT_RX_PMAC_MC] +
+				      c[SCNT_RX_PMAC_BC];
+		m->FrameCheckSequenceErrors = c[SCNT_RX_CRC] +
+					      c[SCNT_RX_PMAC_CRC];
+		m->OctetsTransmittedOK = c[SCNT_TX_OCT] +
+					 c[SCNT_TX_PMAC_OCT];
+		m->OctetsReceivedOK = c[SCNT_RX_OCT] +
+				      c[SCNT_RX_PMAC_OCT];
+		m->MulticastFramesXmittedOK = c[SCNT_TX_MC] +
+					      c[SCNT_TX_PMAC_MC];
+		m->BroadcastFramesXmittedOK = c[SCNT_TX_BC] +
+					      c[SCNT_TX_PMAC_BC];
+		m->MulticastFramesReceivedOK = c[SCNT_RX_MC] +
+					       c[SCNT_RX_PMAC_MC];
+		m->BroadcastFramesReceivedOK = c[SCNT_RX_BC] +
+					       c[SCNT_RX_PMAC_BC];
+		m->InRangeLengthErrors = c[SCNT_RX_FRAG] +
+					 c[SCNT_RX_JABBER] +
+					 c[SCNT_RX_CRC] +
+					 c[SCNT_RX_PMAC_FRAG] +
+					 c[SCNT_RX_PMAC_JABBER] +
+					 c[SCNT_RX_PMAC_CRC];
+		m->OutOfRangeLengthField = c[SCNT_RX_SHORT] +
+					   c[SCNT_RX_LONG] +
+					   c[SCNT_RX_PMAC_SHORT] +
+					   c[SCNT_RX_PMAC_LONG];
+		m->FrameTooLongErrors = c[SCNT_RX_LONG] +
+					c[SCNT_RX_PMAC_LONG];
+		break;
+	}
+
+	spin_unlock(&s->sw_lock);
+}
+
+static const struct ethtool_rmon_hist_range lan9645x_rmon_ranges[] = {
+	{    0,     64 },
+	{   65,    127 },
+	{  128,    255 },
+	{  256,    511 },
+	{  512,   1023 },
+	{ 1024,   1526 },
+	{ 1527, 0xffff },
+	{}
+};
+
+void
+lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port,
+			      struct ethtool_rmon_stats *r,
+			      const struct ethtool_rmon_hist_range **ranges)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	switch (r->src) {
+	case ETHTOOL_MAC_STATS_SRC_EMAC:
+		r->undersize_pkts = c[SCNT_RX_SHORT];
+		r->oversize_pkts = c[SCNT_RX_LONG];
+		r->fragments = c[SCNT_RX_FRAG];
+		r->jabbers = c[SCNT_RX_JABBER];
+		r->hist[0] = c[SCNT_RX_SZ_64];
+		r->hist[1] = c[SCNT_RX_SZ_65_127];
+		r->hist[2] = c[SCNT_RX_SZ_128_255];
+		r->hist[3] = c[SCNT_RX_SZ_256_511];
+		r->hist[4] = c[SCNT_RX_SZ_512_1023];
+		r->hist[5] = c[SCNT_RX_SZ_1024_1526];
+		r->hist[6] = c[SCNT_RX_SZ_JUMBO];
+		r->hist_tx[0] = c[SCNT_TX_SZ_64];
+		r->hist_tx[1] = c[SCNT_TX_SZ_65_127];
+		r->hist_tx[2] = c[SCNT_TX_SZ_128_255];
+		r->hist_tx[3] = c[SCNT_TX_SZ_256_511];
+		r->hist_tx[4] = c[SCNT_TX_SZ_512_1023];
+		r->hist_tx[5] = c[SCNT_TX_SZ_1024_1526];
+		r->hist_tx[6] = c[SCNT_TX_SZ_JUMBO];
+		break;
+	case ETHTOOL_MAC_STATS_SRC_PMAC:
+		r->undersize_pkts = c[SCNT_RX_PMAC_SHORT];
+		r->oversize_pkts = c[SCNT_RX_PMAC_LONG];
+		r->fragments = c[SCNT_RX_PMAC_FRAG];
+		r->jabbers = c[SCNT_RX_PMAC_JABBER];
+		r->hist[0] = c[SCNT_RX_PMAC_SZ_64];
+		r->hist[1] = c[SCNT_RX_PMAC_SZ_65_127];
+		r->hist[2] = c[SCNT_RX_PMAC_SZ_128_255];
+		r->hist[3] = c[SCNT_RX_PMAC_SZ_256_511];
+		r->hist[4] = c[SCNT_RX_PMAC_SZ_512_1023];
+		r->hist[5] = c[SCNT_RX_PMAC_SZ_1024_1526];
+		r->hist[6] = c[SCNT_RX_PMAC_SZ_JUMBO];
+		r->hist_tx[0] = c[SCNT_TX_PMAC_SZ_64];
+		r->hist_tx[1] = c[SCNT_TX_PMAC_SZ_65_127];
+		r->hist_tx[2] = c[SCNT_TX_PMAC_SZ_128_255];
+		r->hist_tx[3] = c[SCNT_TX_PMAC_SZ_256_511];
+		r->hist_tx[4] = c[SCNT_TX_PMAC_SZ_512_1023];
+		r->hist_tx[5] = c[SCNT_TX_PMAC_SZ_1024_1526];
+		r->hist_tx[6] = c[SCNT_TX_PMAC_SZ_JUMBO];
+		break;
+	default:
+		r->undersize_pkts = c[SCNT_RX_SHORT] +
+				    c[SCNT_RX_PMAC_SHORT];
+		r->oversize_pkts = c[SCNT_RX_LONG] +
+				   c[SCNT_RX_PMAC_LONG];
+		r->fragments = c[SCNT_RX_FRAG] +
+			       c[SCNT_RX_PMAC_FRAG];
+		r->jabbers = c[SCNT_RX_JABBER] +
+			     c[SCNT_RX_PMAC_JABBER];
+		r->hist[0] = c[SCNT_RX_SZ_64] +
+			     c[SCNT_RX_PMAC_SZ_64];
+		r->hist[1] = c[SCNT_RX_SZ_65_127] +
+			     c[SCNT_RX_PMAC_SZ_65_127];
+		r->hist[2] = c[SCNT_RX_SZ_128_255] +
+			     c[SCNT_RX_PMAC_SZ_128_255];
+		r->hist[3] = c[SCNT_RX_SZ_256_511] +
+			     c[SCNT_RX_PMAC_SZ_256_511];
+		r->hist[4] = c[SCNT_RX_SZ_512_1023] +
+			     c[SCNT_RX_PMAC_SZ_512_1023];
+		r->hist[5] = c[SCNT_RX_SZ_1024_1526] +
+			     c[SCNT_RX_PMAC_SZ_1024_1526];
+		r->hist[6] = c[SCNT_RX_SZ_JUMBO] +
+			     c[SCNT_RX_PMAC_SZ_JUMBO];
+		r->hist_tx[0] = c[SCNT_TX_SZ_64] +
+				c[SCNT_TX_PMAC_SZ_64];
+		r->hist_tx[1] = c[SCNT_TX_SZ_65_127] +
+				c[SCNT_TX_PMAC_SZ_65_127];
+		r->hist_tx[2] = c[SCNT_TX_SZ_128_255] +
+				c[SCNT_TX_PMAC_SZ_128_255];
+		r->hist_tx[3] = c[SCNT_TX_SZ_256_511] +
+				c[SCNT_TX_PMAC_SZ_256_511];
+		r->hist_tx[4] = c[SCNT_TX_SZ_512_1023] +
+				c[SCNT_TX_PMAC_SZ_512_1023];
+		r->hist_tx[5] = c[SCNT_TX_SZ_1024_1526] +
+				c[SCNT_TX_PMAC_SZ_1024_1526];
+		r->hist_tx[6] = c[SCNT_TX_SZ_JUMBO] +
+				c[SCNT_TX_PMAC_SZ_JUMBO];
+		break;
+	}
+
+	spin_unlock(&s->sw_lock);
+
+	*ranges = lan9645x_rmon_ranges;
+}
+
+/* Called in atomic context */
+void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port,
+				struct rtnl_link_stats64 *stats)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port);
+
+	spin_lock(&s->sw_lock);
+
+	stats->rx_bytes = c[SCNT_RX_OCT] + c[SCNT_RX_PMAC_OCT];
+
+	stats->rx_packets = c[SCNT_RX_SHORT] +
+			    c[SCNT_RX_FRAG] +
+			    c[SCNT_RX_JABBER] +
+			    c[SCNT_RX_CRC] +
+			    c[SCNT_RX_SYMBOL_ERR] +
+			    c[SCNT_RX_SZ_64] +
+			    c[SCNT_RX_SZ_65_127] +
+			    c[SCNT_RX_SZ_128_255] +
+			    c[SCNT_RX_SZ_256_511] +
+			    c[SCNT_RX_SZ_512_1023] +
+			    c[SCNT_RX_SZ_1024_1526] +
+			    c[SCNT_RX_SZ_JUMBO] +
+			    c[SCNT_RX_LONG] +
+			    c[SCNT_RX_PMAC_SHORT] +
+			    c[SCNT_RX_PMAC_FRAG] +
+			    c[SCNT_RX_PMAC_JABBER] +
+			    c[SCNT_RX_PMAC_SZ_64] +
+			    c[SCNT_RX_PMAC_SZ_65_127] +
+			    c[SCNT_RX_PMAC_SZ_128_255] +
+			    c[SCNT_RX_PMAC_SZ_256_511] +
+			    c[SCNT_RX_PMAC_SZ_512_1023] +
+			    c[SCNT_RX_PMAC_SZ_1024_1526] +
+			    c[SCNT_RX_PMAC_SZ_JUMBO];
+
+	stats->multicast = c[SCNT_RX_MC] + c[SCNT_RX_PMAC_MC];
+
+	stats->rx_errors = c[SCNT_RX_SHORT] +
+			   c[SCNT_RX_FRAG] +
+			   c[SCNT_RX_JABBER] +
+			   c[SCNT_RX_CRC] +
+			   c[SCNT_RX_SYMBOL_ERR] +
+			   c[SCNT_RX_LONG] +
+			   c[SCNT_RX_PMAC_SHORT] +
+			   c[SCNT_RX_PMAC_FRAG] +
+			   c[SCNT_RX_PMAC_JABBER] +
+			   c[SCNT_RX_PMAC_CRC] +
+			   c[SCNT_RX_PMAC_SYMBOL_ERR] +
+			   c[SCNT_RX_PMAC_LONG];
+
+	stats->rx_dropped = c[SCNT_RX_LONG] +
+			    c[SCNT_DR_LOCAL] +
+			    c[SCNT_DR_TAIL] +
+			    c[SCNT_RX_CAT_DROP] +
+			    c[SCNT_RX_RED_PRIO_0] +
+			    c[SCNT_RX_RED_PRIO_1] +
+			    c[SCNT_RX_RED_PRIO_2] +
+			    c[SCNT_RX_RED_PRIO_3] +
+			    c[SCNT_RX_RED_PRIO_4] +
+			    c[SCNT_RX_RED_PRIO_5] +
+			    c[SCNT_RX_RED_PRIO_6] +
+			    c[SCNT_RX_RED_PRIO_7];
+
+	for (int i = 0; i < LAN9645X_NUM_TC; i++) {
+		stats->rx_dropped += c[SCNT_DR_YELLOW_PRIO_0 + i] +
+				     c[SCNT_DR_GREEN_PRIO_0 + i];
+	}
+
+	stats->tx_bytes = c[SCNT_TX_OCT] + c[SCNT_TX_PMAC_OCT];
+
+	stats->tx_packets = c[SCNT_TX_SZ_64] +
+			    c[SCNT_TX_SZ_65_127] +
+			    c[SCNT_TX_SZ_128_255] +
+			    c[SCNT_TX_SZ_256_511] +
+			    c[SCNT_TX_SZ_512_1023] +
+			    c[SCNT_TX_SZ_1024_1526] +
+			    c[SCNT_TX_SZ_JUMBO] +
+			    c[SCNT_TX_PMAC_SZ_64] +
+			    c[SCNT_TX_PMAC_SZ_65_127] +
+			    c[SCNT_TX_PMAC_SZ_128_255] +
+			    c[SCNT_TX_PMAC_SZ_256_511] +
+			    c[SCNT_TX_PMAC_SZ_512_1023] +
+			    c[SCNT_TX_PMAC_SZ_1024_1526] +
+			    c[SCNT_TX_PMAC_SZ_JUMBO];
+
+	stats->tx_dropped = c[SCNT_TX_DROP] + c[SCNT_TX_AGED];
+
+	stats->collisions = c[SCNT_TX_COL];
+
+	spin_unlock(&s->sw_lock);
+}
+
+void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port,
+				      struct ethtool_eth_phy_stats *p)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	switch (p->src) {
+	case ETHTOOL_MAC_STATS_SRC_EMAC:
+		p->SymbolErrorDuringCarrier = c[SCNT_RX_SYMBOL_ERR];
+		break;
+	case ETHTOOL_MAC_STATS_SRC_PMAC:
+		p->SymbolErrorDuringCarrier = c[SCNT_RX_PMAC_SYMBOL_ERR];
+		break;
+	default:
+		p->SymbolErrorDuringCarrier = c[SCNT_RX_SYMBOL_ERR] +
+					      c[SCNT_RX_PMAC_SYMBOL_ERR];
+		break;
+	}
+
+	spin_unlock(&s->sw_lock);
+}
+
+void
+lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port,
+				  struct ethtool_eth_ctrl_stats *ctrl)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	switch (ctrl->src) {
+	case ETHTOOL_MAC_STATS_SRC_EMAC:
+		ctrl->MACControlFramesReceived = c[SCNT_RX_CONTROL];
+		break;
+	case ETHTOOL_MAC_STATS_SRC_PMAC:
+		ctrl->MACControlFramesReceived = c[SCNT_RX_PMAC_CONTROL];
+		break;
+	default:
+		ctrl->MACControlFramesReceived = c[SCNT_RX_CONTROL] +
+						 c[SCNT_RX_PMAC_CONTROL];
+		break;
+	}
+
+	spin_unlock(&s->sw_lock);
+}
+
+void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port,
+				    struct ethtool_pause_stats *ps)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	switch (ps->src) {
+	case ETHTOOL_MAC_STATS_SRC_EMAC:
+		ps->tx_pause_frames = c[SCNT_TX_PAUSE];
+		ps->rx_pause_frames = c[SCNT_RX_PAUSE];
+		break;
+	case ETHTOOL_MAC_STATS_SRC_PMAC:
+		ps->tx_pause_frames = c[SCNT_TX_PMAC_PAUSE];
+		ps->rx_pause_frames = c[SCNT_RX_PMAC_PAUSE];
+		break;
+	default:
+		ps->tx_pause_frames = c[SCNT_TX_PAUSE] + c[SCNT_TX_PMAC_PAUSE];
+		ps->rx_pause_frames = c[SCNT_RX_PAUSE] + c[SCNT_RX_PMAC_PAUSE];
+		break;
+	}
+
+	spin_unlock(&s->sw_lock);
+}
+
+void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port,
+				 struct ethtool_mm_stats *stats)
+{
+	struct lan9645x_stats *s = lan9645x->stats;
+	u64 *c;
+
+	c = lan9645x_stats_port_update(lan9645x, port);
+
+	spin_lock(&s->sw_lock);
+
+	stats->MACMergeFrameAssErrorCount = c[SCNT_RX_ASSEMBLY_ERR];
+	stats->MACMergeFrameSmdErrorCount = c[SCNT_RX_SMD_ERR];
+	stats->MACMergeFrameAssOkCount = c[SCNT_RX_ASSEMBLY_OK];
+	stats->MACMergeFragCountRx = c[SCNT_RX_MERGE_FRAG];
+	stats->MACMergeFragCountTx = c[SCNT_TX_MERGE_FRAG];
+	stats->MACMergeHoldCount = c[SCNT_TX_MM_HOLD];
+
+	spin_unlock(&s->sw_lock);
+}
+
+static void lan9645x_check_stats_work(struct work_struct *work)
+{
+	struct delayed_work *del_work = to_delayed_work(work);
+	struct lan9645x_stats *stats;
+
+	stats = container_of(del_work, struct lan9645x_stats, work);
+
+	lan9645x_stats_update(stats->lan9645x);
+
+	queue_delayed_work(stats->queue, &stats->work,
+			   LAN9645X_STATS_CHECK_DELAY);
+}
+
+static int lan9645x_stats_prepare_regions(struct lan9645x *lan9645x,
+					  struct lan9645x_view_stats *vstat)
+{
+	struct lan9645x_stat_region *regions;
+	const u32 *layout = vstat->layout;
+	size_t num_regions = 1;
+	int i;
+
+	for (i = 1; i < vstat->num_cnts; i++)
+		if (layout[i] != layout[i - 1] + 1)
+			num_regions++;
+
+	regions = devm_kcalloc(lan9645x->dev, num_regions, sizeof(*regions),
+			       GFP_KERNEL);
+	if (!regions)
+		return -ENOMEM;
+
+	vstat->num_regions = num_regions;
+	vstat->regions = regions;
+
+	regions[0].base_offset = layout[0];
+	regions[0].cnts_base_idx = 0;
+	regions[0].cnt = 1;
+
+	for (i = 1, num_regions = 0; i < vstat->num_cnts; i++) {
+		if (layout[i] != layout[i - 1] + 1) {
+			num_regions++;
+			regions[num_regions].base_offset = layout[i];
+			regions[num_regions].cnts_base_idx = i;
+			regions[num_regions].cnt = 1;
+		} else {
+			regions[num_regions].cnt++;
+		}
+	}
+
+	return 0;
+}
+
+static int lan9645x_view_stat_init(struct lan9645x *lan9645x,
+				   struct lan9645x_view_stats *vstat,
+				   const struct lan9645x_view_stats *cfg)
+{
+	size_t total = cfg->num_cnts * cfg->num_indexes;
+	int err;
+
+	memcpy(vstat, cfg, sizeof(*cfg));
+
+	vstat->cnts = devm_kcalloc(lan9645x->dev, total, sizeof(u64),
+				   GFP_KERNEL);
+	if (!vstat->cnts)
+		return -ENOMEM;
+
+	vstat->buf = devm_kcalloc(lan9645x->dev, total, sizeof(u32),
+				  GFP_KERNEL);
+	if (!vstat->buf)
+		return -ENOMEM;
+
+	err = lan9645x_stats_prepare_regions(lan9645x, vstat);
+	if (err)
+		return err;
+
+	vstat->stats = lan9645x->stats;
+
+	return 0;
+}
+
+int lan9645x_stats_init(struct lan9645x *lan9645x)
+{
+	const struct lan9645x_view_stats *vs;
+	struct lan9645x_stats *stats;
+	int err, i;
+
+	lan9645x->stats = devm_kzalloc(lan9645x->dev, sizeof(*stats),
+				       GFP_KERNEL);
+	if (!lan9645x->stats)
+		return -ENOMEM;
+
+	stats = lan9645x->stats;
+	stats->lan9645x = lan9645x;
+
+	mutex_init(&stats->hw_lock);
+	spin_lock_init(&stats->sw_lock);
+
+	for (i = 0; i < ARRAY_SIZE(lan9645x_view_stat_cfgs); i++) {
+		vs = &lan9645x_view_stat_cfgs[i];
+
+		if (!vs->num_cnts)
+			continue;
+
+		err = lan9645x_view_stat_init(lan9645x, &stats->view[vs->type],
+					      vs);
+		if (err)
+			return err;
+	}
+
+	stats->queue = alloc_ordered_workqueue("%s-stats", 0,
+					       dev_name(lan9645x->dev));
+	if (!stats->queue)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&stats->work, lan9645x_check_stats_work);
+	queue_delayed_work(stats->queue, &stats->work,
+			   LAN9645X_STATS_CHECK_DELAY);
+
+	return 0;
+}
+
+void lan9645x_stats_deinit(struct lan9645x *lan9645x)
+{
+	cancel_delayed_work_sync(&lan9645x->stats->work);
+	destroy_workqueue(lan9645x->stats->queue);
+	mutex_destroy(&lan9645x->stats->hw_lock);
+	lan9645x->stats->queue = NULL;
+}
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h
new file mode 100644
index 000000000000..268f6ad18088
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#ifndef _LAN9645X_STATS_H_
+#define _LAN9645X_STATS_H_
+
+#include "lan9645x_main.h"
+
+#define STATS_INDEX(vstats, idx) (&(vstats)->cnts[(vstats)->num_cnts * (idx)])
+
+#define STAT_COUNTERS(lan9645x, type, idx) \
+	STATS_INDEX(lan9645x_get_vstats(lan9645x, type), idx)
+
+/* Counter indices into stat layout structs */
+#define SCNT_FRER_SID_IN_PKT             0
+#define SCNT_ISDX_GREEN_OCT              1
+#define SCNT_ISDX_GREEN_PKT              2
+#define SCNT_ISDX_YELLOW_OCT             3
+#define SCNT_ISDX_YELLOW_PKT             4
+#define SCNT_ISDX_RED_OCT                5
+#define SCNT_ISDX_RED_PKT                6
+#define SCNT_ISDX_DROP_GREEN_OCT         7
+#define SCNT_ISDX_DROP_GREEN_PKT         8
+#define SCNT_ISDX_DROP_YELLOW_OCT        9
+#define SCNT_ISDX_DROP_YELLOW_PKT        10
+
+#define SCNT_SF_MATCHING_FRAMES_COUNT    0
+#define SCNT_SF_NOT_PASSING_FRAMES_COUNT 1
+#define SCNT_SF_NOT_PASSING_SDU_COUNT    2
+#define SCNT_SF_RED_FRAMES_COUNT         3
+#define SCNT_SF_STREAM_BLOCK_COUNT       4
+
+#define SCNT_ESDX_GREEN_OCT              0
+#define SCNT_ESDX_GREEN_PKT              1
+#define SCNT_ESDX_YELLOW_OCT             2
+#define SCNT_ESDX_YELLOW_PKT             3
+
+#define SCNT_RX_OCT                      0
+#define SCNT_RX_UC                       1
+#define SCNT_RX_MC                       2
+#define SCNT_RX_BC                       3
+#define SCNT_RX_SHORT                    4
+#define SCNT_RX_FRAG                     5
+#define SCNT_RX_JABBER                   6
+#define SCNT_RX_CRC                      7
+#define SCNT_RX_SYMBOL_ERR               8
+#define SCNT_RX_SZ_64                    9
+#define SCNT_RX_SZ_65_127                10
+#define SCNT_RX_SZ_128_255               11
+#define SCNT_RX_SZ_256_511               12
+#define SCNT_RX_SZ_512_1023              13
+#define SCNT_RX_SZ_1024_1526             14
+#define SCNT_RX_SZ_JUMBO                 15
+#define SCNT_RX_PAUSE                    16
+#define SCNT_RX_CONTROL                  17
+#define SCNT_RX_LONG                     18
+#define SCNT_RX_CAT_DROP                 19
+#define SCNT_RX_RED_PRIO_0               20
+#define SCNT_RX_RED_PRIO_1               21
+#define SCNT_RX_RED_PRIO_2               22
+#define SCNT_RX_RED_PRIO_3               23
+#define SCNT_RX_RED_PRIO_4               24
+#define SCNT_RX_RED_PRIO_5               25
+#define SCNT_RX_RED_PRIO_6               26
+#define SCNT_RX_RED_PRIO_7               27
+#define SCNT_RX_YELLOW_PRIO_0            28
+#define SCNT_RX_YELLOW_PRIO_1            29
+#define SCNT_RX_YELLOW_PRIO_2            30
+#define SCNT_RX_YELLOW_PRIO_3            31
+#define SCNT_RX_YELLOW_PRIO_4            32
+#define SCNT_RX_YELLOW_PRIO_5            33
+#define SCNT_RX_YELLOW_PRIO_6            34
+#define SCNT_RX_YELLOW_PRIO_7            35
+#define SCNT_RX_GREEN_PRIO_0             36
+#define SCNT_RX_GREEN_PRIO_1             37
+#define SCNT_RX_GREEN_PRIO_2             38
+#define SCNT_RX_GREEN_PRIO_3             39
+#define SCNT_RX_GREEN_PRIO_4             40
+#define SCNT_RX_GREEN_PRIO_5             41
+#define SCNT_RX_GREEN_PRIO_6             42
+#define SCNT_RX_GREEN_PRIO_7             43
+#define SCNT_RX_ASSEMBLY_ERR             44
+#define SCNT_RX_SMD_ERR                  45
+#define SCNT_RX_ASSEMBLY_OK              46
+#define SCNT_RX_MERGE_FRAG               47
+#define SCNT_RX_PMAC_OCT                 48
+#define SCNT_RX_PMAC_UC                  49
+#define SCNT_RX_PMAC_MC                  50
+#define SCNT_RX_PMAC_BC                  51
+#define SCNT_RX_PMAC_SHORT               52
+#define SCNT_RX_PMAC_FRAG                53
+#define SCNT_RX_PMAC_JABBER              54
+#define SCNT_RX_PMAC_CRC                 55
+#define SCNT_RX_PMAC_SYMBOL_ERR          56
+#define SCNT_RX_PMAC_SZ_64               57
+#define SCNT_RX_PMAC_SZ_65_127           58
+#define SCNT_RX_PMAC_SZ_128_255          59
+#define SCNT_RX_PMAC_SZ_256_511          60
+#define SCNT_RX_PMAC_SZ_512_1023         61
+#define SCNT_RX_PMAC_SZ_1024_1526        62
+#define SCNT_RX_PMAC_SZ_JUMBO            63
+#define SCNT_RX_PMAC_PAUSE               64
+#define SCNT_RX_PMAC_CONTROL             65
+#define SCNT_RX_PMAC_LONG                66
+#define SCNT_TX_OCT                      67
+#define SCNT_TX_UC                       68
+#define SCNT_TX_MC                       69
+#define SCNT_TX_BC                       70
+#define SCNT_TX_COL                      71
+#define SCNT_TX_DROP                     72
+#define SCNT_TX_PAUSE                    73
+#define SCNT_TX_SZ_64                    74
+#define SCNT_TX_SZ_65_127                75
+#define SCNT_TX_SZ_128_255               76
+#define SCNT_TX_SZ_256_511               77
+#define SCNT_TX_SZ_512_1023              78
+#define SCNT_TX_SZ_1024_1526             79
+#define SCNT_TX_SZ_JUMBO                 80
+#define SCNT_TX_YELLOW_PRIO_0            81
+#define SCNT_TX_YELLOW_PRIO_1            82
+#define SCNT_TX_YELLOW_PRIO_2            83
+#define SCNT_TX_YELLOW_PRIO_3            84
+#define SCNT_TX_YELLOW_PRIO_4            85
+#define SCNT_TX_YELLOW_PRIO_5            86
+#define SCNT_TX_YELLOW_PRIO_6            87
+#define SCNT_TX_YELLOW_PRIO_7            88
+#define SCNT_TX_GREEN_PRIO_0             89
+#define SCNT_TX_GREEN_PRIO_1             90
+#define SCNT_TX_GREEN_PRIO_2             91
+#define SCNT_TX_GREEN_PRIO_3             92
+#define SCNT_TX_GREEN_PRIO_4             93
+#define SCNT_TX_GREEN_PRIO_5             94
+#define SCNT_TX_GREEN_PRIO_6             95
+#define SCNT_TX_GREEN_PRIO_7             96
+#define SCNT_TX_AGED                     97
+#define SCNT_TX_LLCT                     98
+#define SCNT_TX_CT                       99
+#define SCNT_TX_BUFDROP                  100
+#define SCNT_TX_MM_HOLD                  101
+#define SCNT_TX_MERGE_FRAG               102
+#define SCNT_TX_PMAC_OCT                 103
+#define SCNT_TX_PMAC_UC                  104
+#define SCNT_TX_PMAC_MC                  105
+#define SCNT_TX_PMAC_BC                  106
+#define SCNT_TX_PMAC_PAUSE               107
+#define SCNT_TX_PMAC_SZ_64               108
+#define SCNT_TX_PMAC_SZ_65_127           109
+#define SCNT_TX_PMAC_SZ_128_255          110
+#define SCNT_TX_PMAC_SZ_256_511          111
+#define SCNT_TX_PMAC_SZ_512_1023         112
+#define SCNT_TX_PMAC_SZ_1024_1526        113
+#define SCNT_TX_PMAC_SZ_JUMBO            114
+#define SCNT_DR_LOCAL                    115
+#define SCNT_DR_TAIL                     116
+#define SCNT_DR_YELLOW_PRIO_0            117
+#define SCNT_DR_YELLOW_PRIO_1            118
+#define SCNT_DR_YELLOW_PRIO_2            119
+#define SCNT_DR_YELLOW_PRIO_3            120
+#define SCNT_DR_YELLOW_PRIO_4            121
+#define SCNT_DR_YELLOW_PRIO_5            122
+#define SCNT_DR_YELLOW_PRIO_6            123
+#define SCNT_DR_YELLOW_PRIO_7            124
+#define SCNT_DR_GREEN_PRIO_0             125
+#define SCNT_DR_GREEN_PRIO_1             126
+#define SCNT_DR_GREEN_PRIO_2             127
+#define SCNT_DR_GREEN_PRIO_3             128
+#define SCNT_DR_GREEN_PRIO_4             129
+#define SCNT_DR_GREEN_PRIO_5             130
+#define SCNT_DR_GREEN_PRIO_6             131
+#define SCNT_DR_GREEN_PRIO_7             132
+
+enum lan9645x_view_stat_type {
+	LAN9645X_STAT_PORTS = 0,
+	LAN9645X_STAT_ISDX,
+	LAN9645X_STAT_ESDX,
+	LAN9645X_STAT_SFID,
+
+	LAN9645X_STAT_NUM,
+};
+
+struct lan9645x_stat_region {
+	u32 base_offset;
+	u32 cnt;
+	u32 cnts_base_idx;
+};
+
+/* Counters are organized by indices/views such as
+ *
+ * - physical ports
+ * - isdx
+ * - esdx
+ * - frer
+ * - sfid
+ *
+ * Each view contains regions, which is a linear address range of related
+ * stats. I.e. the ports index has RX, TX and Drop regions.
+ *
+ *
+ * and you have a given counter replicated per index.
+ */
+struct lan9645x_view_stats {
+	/* HW register offsets indexed by SCNT_*, used for bulk reading */
+	const u32 *layout;
+	/* Region description for this view, used for bulk reading */
+	struct lan9645x_stat_region *regions;
+	struct lan9645x_stats *stats;
+	char name[16];
+	/* 64bit software counters with the same addr layout hw */
+	u64 *cnts;
+	/* Buffer for bulk reading counter regions from hw */
+	u32 *buf;
+	/* Number of counters per index in view */
+	u32 num_cnts;
+	/* Number of indexes in view */
+	u32 num_indexes;
+	/* Number of counter regions with counters at sequential addresses */
+	size_t num_regions;
+	enum lan9645x_view_stat_type type;
+};
+
+struct lan9645x_stats {
+	struct lan9645x *lan9645x;
+	struct mutex hw_lock; /* lock r/w to stat registers and u32 buf */
+	spinlock_t sw_lock; /* lock access to u64 software counters */
+	struct delayed_work work;
+	struct workqueue_struct *queue;
+
+	struct lan9645x_view_stats view[LAN9645X_STAT_NUM];
+};
+
+static inline struct lan9645x_view_stats *
+lan9645x_get_vstats(struct lan9645x *lan9645x,
+		    enum lan9645x_view_stat_type type)
+{
+	if (WARN_ON(!(type < LAN9645X_STAT_NUM)))
+		return NULL;
+
+	return &lan9645x->stats->view[type];
+}
+
+/* Add a possibly wrapping 32 bit value to a 64 bit counter */
+static inline void lan9645x_stats_add_cnt(u64 *cnt, u32 val)
+{
+	if (val < (*cnt & U32_MAX))
+		*cnt += (u64)1 << 32; /* value has wrapped */
+
+	*cnt = (*cnt & ~(u64)U32_MAX) + val;
+}
+
+int lan9645x_stats_init(struct lan9645x *lan9645x);
+void lan9645x_stats_deinit(struct lan9645x *lan9645x);
+void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port,
+				u32 stringset, u8 *data);
+int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port,
+				  int sset);
+void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port,
+				      uint64_t *data);
+void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port,
+				      struct ethtool_eth_mac_stats *mac_stats);
+void
+lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port,
+			      struct ethtool_rmon_stats *rmon_stats,
+			      const struct ethtool_rmon_hist_range **ranges);
+void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port,
+				struct rtnl_link_stats64 *s);
+void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port,
+				 struct ethtool_mm_stats *stats);
+void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port,
+				    struct ethtool_pause_stats *ps);
+void
+lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port,
+				  struct ethtool_eth_ctrl_stats *ctrl_stats);
+void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port,
+				      struct ethtool_eth_phy_stats *phy_stats);
+
+#endif

-- 
2.52.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox