* [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family
@ 2026-11-03 12:31 Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 1/4] net: dsa: add tag driver for LAN9645X Jens Emil Schulz Østergaard
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Jens Emil Schulz Østergaard @ 2026-11-03 12:31 UTC (permalink / raw)
To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Woojung Huh, Russell King,
Steen Hegelund, Daniel Machon, Geert Uytterhoeven, Magnus Damm,
Eric Dumazet, Richard Cochran, Eric Dumazet
Cc: linux-kernel, netdev, devicetree, linux-renesas-soc,
Jens Emil Schulz Østergaard
This series provides the Microchip LAN9645X Switch driver.
The LAN9645x is a family of chips with ethernet switch functionality and
multiple peripheral functions. The switch delivers up to 9 ethernet
ports and 12 Gbps switching bandwidth.
The switch chip has 5 integrated copper PHYs, support for 2x RGMII
interfaces, 2x SGMII and one QSGMII interface.
The switch chip is from the same design architecture family as ocelot
and lan966x, and the driver reflects this similarity. However, LAN9645x
does not have an internal CPU in any package, and must be driven
externally. For register IO it supports interfaces such as SPI, I2C and
MDIO.
The chip supports a variety of network features such as
* Mactable for MDB/FDB functionality
* Bridge forwarding offload
* VLAN-aware bridging
* IGMP/MLD snooping
* Link aggregation
* PTP timestamping
* FRER (802.1CB)
* Media Redundancy Protocol
* Parallel Redundancy and High-Availability Seamless Redundancy
(HSR/PRP) in DANH/DANP mode
* Per stream filtering and policing
* Shapers such as Credit Based Shaping and Time Aware Shaing
* Frame preemption
* A TCAM (VCAP) for line-rate frame processing
The LAN9645x family consists of the following SKUs:
LAN96455F
LAN96457F
LAN96459F
LAN96455S
LAN96457S
LAN96459S
The difference between the SKUs is the number of supported ports (5, 7
or 9) and features supported. The F subfamily supports HSR/PRP and TSN,
while the S subfamily does not.
The intended way to bind this driver is using a parent MFD driver,
responsible for the register IO protocol, and distributing regmaps to
child devices. The goal is to use the same approach as the MFD driver in
drivers/mfd/ocelot-spi.c, which also resets the chip before it
instantiates its children.
This driver expects to request named regmaps from a parent device. This
approach is similar to the DSA driver
drivers/net/dsa/ocelot/ocelot_ext.c
which supports being driven by an external CPU via SPI with parent
device drivers/mfd/ocelot-spi.c.
The MFD driver will come in a later series, because there are
requirements on the number of child devices before a driver qualifies as
a MFD device.
Development is done using the LAN966x as a host CPU, running the lan966x
swichdev driver, using the EVB-LAN9668 EDS2 board.
The datasheet is available here:
https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/DataSheets/LAN9645xF-Data-Sheet-DS00006065.pdf
This series adds support for standalone ports. Bridges and VLAN uppers
on the ports are handled in software.
More support will be added at a later stage. Here is a tentative plan of
future patches for this DSA driver:
* Add bridge forwarding and FDB offloading.
* Add multicast forwarding and MDB offloading.
* Add VLAN-aware bridge offloading.
* Add port statistics.
* Add LAG support.
* Add TC matchall mirror support.
* Add TC matchall police support.
* Add DCB/qos support.
* Add simple TC support: mqprio, cbs, tbf, ebf.
* Add TC flower filter support.
* Add HSR/PRP offloading support.
* Add PTP support.
* Add TC taprio support.
For completeness I include tentative plan of planned patches for
LAN9645x peripherals:
* Extend pinctrl-ocelot for LAN9645x:
https://lore.kernel.org/linux-gpio/20260119-pinctrl_ocelot_extend_support_for_lan9645x-v1-0-1228155ed0ee@microchip.com/
* Add driver for internal PHY:
https://lore.kernel.org/netdev/20260123-phy_micrel_add_support_for_lan9645x_internal_phy-v1-1-8484b1a5a7fd@microchip.com/
* MFD driver for managing register IO protocol and child device
initialization.
* Extend pinctrl-microchip-sgpio for LAN9645x support.
* Extend i2c_designware for LAN9645x support.
* Add driver for outbound interrupt controller.
* Add serdes driver for lan9645x.
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v16:
- Individual patches mention specific v16 changes
- Reduce the series to standalone ports. The vlan, mac table, mdb,
bridge and port statistics patches are dropped, and will be sent in a
follow-up series.
- tag: restore a popped VLAN tag whenever the IFH reports one, without
depending on dp->vlan_filtering, which DSA updates only after the
hardware VLAN configuration has changed.
- basic: drop the ds flags and defines only used by the dropped patches.
- Link to v15: https://lore.kernel.org/r/20261015-dsa_lan9645x_switch_driver_base-v15-0-562f316f78b9@microchip.com
Changes in v15:
- Individual patches mention specific v15 changes
- basic: on link down, assert MAC_TX_RST and MAC_RX_RST instead of
flushing the queues and asserting PORT_RST, which can hang the port
when the MAC cannot transmit the frames still queued.
- vlan: refuse VLAN filtering on bridges with a vlan_protocol other than
802.1Q, and only apply VLAN awareness while a port is bridged, so a
join after a failed one is configured correctly.
- Link to v14: https://lore.kernel.org/r/20261008-dsa_lan9645x_switch_driver_base-v14-0-3d2a72661213@microchip.com
Changes in v14:
- Individual patches mention specific v14 changes
- tag: classify untagged and priority tagged frames to VID 0 on xmit,
pull the VLAN header before reading it, restore the VLAN tag on rcv
whenever one was popped, and add a .flow_dissect callback that
accounts for popped VLAN tags.
- bindings: use the dt-bindings: net: dsa: subject prefix, and describe
all switch register targets and the analyzer and PTP interrupts.
- vlan: use TAG_NO_UNAWARE on ports with only tagged VLANs.
- mac table: keep a CPU port host entry while another database uses the
same MAC table key.
- mdb: keep the PGID reference when a mac table write fails while
widening a group, drop it on a failed narrow that keeps the same PGID,
and keep a CPU port host entry while another database uses the same
mac table key.
- bridge: only enable learning on bridged ports, start a joining port
disabled so it does not learn before its STP state is applied, and
honour host flood requests from bridged ports.
- stats: return 0 from get_sset_count() for other string sets, leave
RMON fragments unset, and describe the pMAC counters correctly.
- Link to v13: https://lore.kernel.org/r/20260929-dsa_lan9645x_switch_driver_base-v13-0-827c2d3617f2@microchip.com
Changes in v13:
- Individual patches mention specific v13 changes
- bindings: Drop the commit message paragraph citing
Documentation/networking/phy.rst.
- basic: only redirect the CPU extraction queues to the NPI port while
its link is up, and flush the CPU port module on NPI link up.
- Rebased on net-next, bumping DSA_TAG_PROTO_LAN9645X_VALUE to 37
- Link to v12: https://lore.kernel.org/r/20260908-dsa_lan9645x_switch_driver_base-v12-0-2d6aa59350cd@microchip.com
Changes in v12:
- reorder the series so bridge support lands after vlan, mac table and
mdb support. port_fast_age() now exists before the STP state handling
that needs it, and host address filtering is in place before bridging.
- include the CPU port module in the flood masks from the start, and
remove it again in the bridge patch where port_set_host_flood() takes
over on demand host flooding. Previously the host could not receive
unicast until the mac table patch.
- vlan: serialize the VLAN table with fwd_domain_lock, and describe the
untagged VLAN, TPID and C-component conformance limitations.
- tag driver: move an out-of-band VLAN tag into the payload before
reading it, instead of relying on the conduit never advertising a VLAN
TX offload.
- tag driver: restore the classified VLAN as a C-tag rather than following
the IFH tag type, so a terminated frame lands on the VID the hardware
forwarded it on.
- basic: drop the tail drop watermark setup, which programmed a
condition that could not trigger.
- mdb: handle PGID exhaustion on both the add and delete paths, rather
than leaving a departed port in the group.
- mdb: classify IPv4 multicast on the 01:00:5E prefix alone. Hardware does
not apply the 23-bit rule, so some groups were programmed as entries it
never matches.
- stats: give the hardware counters and the software shadow a common
baseline, fix some rtnl_link_stats64 fields, and stop reporting the
three eth-mac fields the hardware cannot measure.
- stats: stop the counter poller in .shutdown, which kept reading
registers after the parent had been shut down.
- Link to v11: https://lore.kernel.org/r/20260805-dsa_lan9645x_switch_driver_base-v11-0-007ebc983a0a@microchip.com
Changes in v11:
- Update comment about vlan tag hwaccel use in tag driver.
- Bump DSA_TAG_PROTO value in include/net/dsa.h.
- stats: add comment about calling contexts of sw_lock in
lan9645x_stats_get_stats64
- Link to v10: https://lore.kernel.org/r/20260713-dsa_lan9645x_switch_driver_base-v10-0-a4886a08fb15@microchip.com
Changes in v10:
- Individual patches mention specific v10 changes
- Update tag driver after skb ownership model change in DSA taggers.
- Link to v9: https://lore.kernel.org/r/20260708-dsa_lan9645x_switch_driver_base-v9-0-0d1512a326d7@microchip.com
Changes in v9:
- Individual patches mention specific v9 changes
- Drop RGMII muxing to port 4.
- Link to v8: https://lore.kernel.org/r/20260702-dsa_lan9645x_switch_driver_base-v8-0-90228d8bba58@microchip.com
Changes in v8:
- Individual patches mention specific v8 changes
- Do not use the name CPU_PORT for the chips internal CPU port module,
as it collides with the DSA CPU port (the NPI front port). Drop the
CPU_PORT macro and reference the module as lan9645x->num_phys_ports,
mirroring ocelot/felix.
- Reword the port map and related comments to distinguish the chip CPU
port modules (indices 9-10) from the DSA CPU port.
- Add a port_mux_lock mutex to serialize port mux arbitration in
the phylink mac_prepare path.
- Link to v7: https://lore.kernel.org/r/20260603-dsa_lan9645x_switch_driver_base-v7-0-b2f90e676707@microchip.com
Changes in v7:
- Individual patches mention specific v7 changes
- Do not mark the BPDU range 01:80:C2:00:00:0X as offloaded in the tag driver.
- Refactor cpu queue based frame classification to use categories default, trap
and copy, which mirrors usage instead of being based on frame types.
- Add registers ANA:COMMON:CPUQ_8021_CFG for bpdu cpu queue configuration.
- Use cpu queue LAN9645X_CPUQ_TRAP for bpdu frames.
- Refactor IGMP/MLD/IPMC_CTRL to use new LAN9645X_CPUQ_DEF,
LAN9645X_CPUQ_TRAP and LAN9645X_CPUQ_COPY queues.
- Add __aligned(2) to mac variable in lan9645x_mdb_update_dest.
- Link to v6: https://lore.kernel.org/r/20260527-dsa_lan9645x_switch_driver_base-v6-0-4d409ae64f3c@microchip.com
Changes in v6:
- Individual patches mention specific v6 changes
- Rebased on net-next, bumping DSA_TAG_PROTO_LAN9645X_VALUE to 34
- Link to v5: https://lore.kernel.org/r/20260518-dsa_lan9645x_switch_driver_base-v5-0-968fbf34ffa3@microchip.com
Changes in v5:
- Individual patches mention specific v5 changes
- Undo offset fix in postpull_rcsum. The original logic was correct for
CHECKSUM_COMPLETE host NICs
- Use __always_inline in lan9645x_ifh_{get,set}
- remove double space after = in set_merge_mask
- remove unused fields dd_dis and tsn_dis, and add SKU supported port validation
during setup
- phylink: remove MAC_2500FD
- phylink: add comment about empty supported_interfaces for port 5-6.
- phylink: fix 2:1 rgmii port muxing for port module 4 and 7 to be fully
dynamic and validate requested mux settings.
- phylink: add comment about 2:1 rgmii port muxing for port module 4
and 7.
- rx/tx-internal-delay-ps checked against supported 2ns value
- init lan9645x->npi = -1 at probe and check port < 0 in npi_deinit
- use ds->ageing_time_max
- use packed p->host_flood_req for atomic r/w
- fix typo in set_ageing_time comment
- include lan9645x->bridge deref under lock in brige_join
- switch -EBUSY to -EINVAL for vlan add/del in the reserved range.
- remove reserved HSR vlan
- lan9645x_mac_init returns error on table init timeout
- add comment about skipping LOCKED entries on fdb dump
- skip igmp/mld redir for npi port
- make lan9645x_stats_init void
- add SCNT_TX_BUFDROP to tx_dropped
- change rmon range {0,64} -> {64, 64}. Runt frames counted elsewhere.
- remove rx_crc, rx_symbol_err from rx_packets, as they are already
counted in SZ_* buckets.
- add defensive cancel_delayed_work_sync in stats_free
- Link to v4: https://lore.kernel.org/r/20260430-dsa_lan9645x_switch_driver_base-v4-0-f1b6005fa8b7@microchip.com
Changes in v4:
- v3 was deferred, but I made some changes based on the Sashiko review
- Individual patches mention specific v4 changes
- Fix offset in postpull_rcsum so prefix eth header is cleared, not
actual eth header, so tag driver works with CHECKSUM_COMPLETE host
NICs
- Fix untagged rx on vlan aware port with pvid
- Add comment to QSYS_RES_CFG configuration
- Phylink_mac_prepare: fix to make sure we can dynamically change rgmii
on port 4
- Move ports allocation to probe
- tag_npi_setup: reject cascaded setups
- Skip WARN_ON in lan9645x_to_port
- set_host_flood changed to per port work to coalesce values and skip
atomic allocations
- Fix clear HOST_PVID vlan membership when a port joins a bridge.
- Explicit default value write to tag type register for untagged frames
- Use lan_rmw for ANA_DROP_CFG
- Add comment for error path in lan9645x_vlan_hw_wr
- Remove mac_entries list and just do direct IO to mac table from
fdb_add/fdb_del.
- Clean up fresh mdb when hw mac table write fails.
- Remove rx_uc and tx_uc from ethtool stats list, as they are derivable
from the eth-mac group
- Split stats_init into stats_alloc and stats_init, use alloc in probe
and init in dsa_setup
- Link to v3: https://lore.kernel.org/r/20260410-dsa_lan9645x_switch_driver_base-v3-0-aadc8595306d@microchip.com
Changes in v3:
- Individual patches mention specific v3 changes.
- Add guard before vlan_remove_tag on xmit
- Add pskb_may_pull checks on rx
- Remove additionalProperties: true in bindings
- Remove unnecessary | from description in bindings
- Change top level $ref to dsa.yaml#/$defs/ethernet-ports
- Use ethernet-ports and ethernet-port
- Move ethernet-ports under properties instead of patternProperties
- Move unevaluatedProperties: false after $ref
- Update bindings example to use ethernet-ports and ethernet-port
- Move DEV_MAC_TAGS_CFG to port setup, instead of vlan config, so vlan
overhead is always included in port frame maxlen calculation.
- Remove code disabling ipv6 on conduit
- Use of_property_read_u32 for {rx,tx}-internal-delay-ps
- Use dsa_user_ports(ds) instead of
GENMASK(lan9645x->num_phys_ports - 1, 0) as base flood mask.
- Add comment explaining obey vlan
- Allow disabling aging with explicit zero parameters.
- Fix non-forwarding STP states in bridge fwd calculation.
- Restore host flood state on bridge leave.
- Avoid mac_entry dealloc when mac table writes fail.
- Avoid mdb_entry dealloc when mac table writes fail.
- Dealloc mac_entries on deinit.
- Dealloc mdb_entries on deinit.
- Link to v2: https://lore.kernel.org/r/20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com
Changes in v2:
- Individual patches have specific v2 changes.
- Ran DSA, and std counters, selftests, which prompted several changes.
The following selftests pass, except for some expected failures:
- bridge_vlan_aware.sh
- bridge_vlan_unaware.sh
- bridge_vlan_mcast.sh
- no_forwarding.sh
- bridge_mdb.sh
- bridge_mld.sh
- test_fdb_stress_test.sh
- .../drivers/net/hw/ethtool_rmon.sh
- .../drivers/net/hw/ethtool_std_stats.sh (from Ioana's series)
- Added new patch for MDB management, as this was required for selftests.
- Added port_set_host_flood to enable unknown traffic to standalone during
promisc/ALL_MULTI (selftests).
- Remove the dubugfs.
- Link to v1: https://lore.kernel.org/r/20260303-dsa_lan9645x_switch_driver_base-v1-0-bff8ca1396f5@microchip.com
---
Jens Emil Schulz Østergaard (4):
net: dsa: add tag driver for LAN9645X
dt-bindings: net: dsa: add Microchip LAN9645x switch
net: dsa: lan9645x: add autogenerated register macros
net: dsa: lan9645x: add basic dsa driver for LAN9645X
.../net/dsa/microchip,lan96455s-switch.yaml | 208 +++
MAINTAINERS | 10 +
drivers/net/dsa/Kconfig | 2 +
drivers/net/dsa/microchip/Makefile | 1 +
drivers/net/dsa/microchip/lan9645x/Kconfig | 12 +
drivers/net/dsa/microchip/lan9645x/Makefile | 8 +
drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 453 +++++
drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 321 ++++
drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c | 116 ++
.../net/dsa/microchip/lan9645x/lan9645x_phylink.c | 316 ++++
drivers/net/dsa/microchip/lan9645x/lan9645x_port.c | 166 ++
drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h | 1932 ++++++++++++++++++++
include/linux/dsa/lan9645x.h | 54 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 11 +
net/dsa/Makefile | 1 +
net/dsa/tag_lan9645x.c | 513 ++++++
17 files changed, 4126 insertions(+)
---
base-commit: 4dd16e31135ea8437a720222df34e8745f385d6d
change-id: 20260210-dsa_lan9645x_switch_driver_base-312bbfc37edb
Best regards,
--
Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v16 1/4] net: dsa: add tag driver for LAN9645X
2026-11-03 12:31 [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
@ 2026-11-03 12:31 ` Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 2/4] dt-bindings: net: dsa: add Microchip LAN9645x switch Jens Emil Schulz Østergaard
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jens Emil Schulz Østergaard @ 2026-11-03 12:31 UTC (permalink / raw)
To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Woojung Huh, Russell King,
Steen Hegelund, Daniel Machon, Geert Uytterhoeven, Magnus Damm,
Eric Dumazet, Richard Cochran, Eric Dumazet
Cc: linux-kernel, netdev, devicetree, linux-renesas-soc,
Jens Emil Schulz Østergaard
Add tag driver for LAN9645x using a front port as CPU port. This mode
is called an NPI port in the datasheet.
Use long prefix on extraction (RX) and no prefix on injection (TX). A
long prefix on extraction helps get through the conduit port on host
side, since it will see a broadcast MAC.
The LAN9645x chip is in the same design architecture family as ocelot
and lan966x. The tagging protocol has the same structure as these chips,
but the particular fields are different or have different sizes.
Therefore, this tag driver is similar to tag_ocelot.c, but the
differences in fields makes it hard to reuse.
LAN9645x supports 3 different tag formats for extraction/injection of
frames from a CPU port: long prefix, short prefix and no prefix.
The tag is prepended to the frame. The critical data for the chip is
contained in an internal frame header (IFH) which is 28 bytes. The
prefix formats look like this:
Long prefix (16 bytes) + IFH:
- DMAC = 0xffffffffffff on extraction.
- SMAC = 0xfeffffffffff on extraction.
- ETYPE = 0x8880
- payload = 0x0011
- IFH
Short prefix (4 bytes) + IFH:
- 0x8880
- 0x0011
- IFH
No prefix:
- IFH
The format can be configured asymmetrically on RX and TX.
The IFH get/set functions are declared as inline. All the field
constants are compile-time known, so when these calls are inlined
efficient code is generated with branches pruned and loops unrolled.
During testing it was observed that without explicit inlining GCC would
have trouble inlining the functions, which hurt performance.
A frame extracted from a VLAN-aware port arrives without its VLAN tag.
REW_PORT_CFG.NO_REWRITE is clear on the NPI port, so the rewriter applies
the classified VLAN_POP_CNT, and the classified VID reaches the CPU only
through the IFH. The tagger restores it as a hwaccel tag when the IFH
reports that a tag was popped. An untagged frame is classified to the
port pvid without a pop and is delivered untagged, which the bridge also
classifies to the pvid.
The restored tag is always a C-tag, not the TPID the IFH TAG_TYPE reports.
Both TPIDs are recognized as VLAN tags unconditionally, with no per port
control to treat one as untagged customer data, so an S-tag is already
consumed as the VLAN tag before extraction. Restoring 802.1AD would make
the bridge push the tag back into the payload and reclassify the frame to
the port pvid, placing it on a different VID than the one the hardware
forwarded it on. Normalizing to a C-tag keeps the terminated path
consistent with the forwarded path, at the cost of not preserving the
S-tag TPID towards the CPU. This assumes a bridge vlan_protocol of
802.1Q, which is the only protocol the driver offloads.
On injection the IFH carries the classified VID used by the rewriter
to decide whether to add a port tag. On a VLAN-aware port, a VLAN tag
with a non-zero VID is moved from the payload into the IFH. Untagged
and priority tagged frames are injected with classified VID 0 and left
as they are, so the rewriter adds no tag to them.
Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v16:
- Restore the popped VLAN tag whenever the IFH reports a pop, without
checking dsa_port_is_vlan_filtering().
- Mention the real ethernet header in the skb_postpull_rcsum() comment.
Changes in v14:
- Classify untagged and priority tagged frames to VID 0 on xmit instead
of the bridge pvid, and leave priority tags in the payload.
- Restore the VLAN tag on rcv whenever the rewriter popped one, instead
of only when the VID differs from the port pvid.
- Add a .flow_dissect callback that accounts for the gap left by VLAN
tags popped on extraction. The generic dissector assumes the tag length
is needed_headroom.
- Pull the VLAN header with pskb_may_pull() before reading it on xmit,
so a tag in paged data is handled like a linear one, and drop the
frame if the pull fails.
Changes in v12:
- Move an out-of-band VLAN tag into the payload with
__vlan_hwaccel_push_inside() before reading it, rather than relying on
the conduit never advertising NETIF_F_HW_VLAN_CTAG_TX or
NETIF_F_HW_VLAN_STAG_TX in vlan_features.
- Return the skb from lan9645x_xmit_get_vlan_info(), since
__vlan_hwaccel_push_inside() may reallocate and frees the skb on
failure.
- Annotate the IFH field defines with injection/extraction
applicability
- Drop IFH_TIMESTAMP because it is 38 bits wide. Rely on NS/SUB_NS
subfields.
- Document how HW derives the DMAC offset from pop_cnt and etype_ofs,
and drop frames whose encoding only a tag push could produce.
- Drop unncessary IFH_SRCPORT on injection.
- Drop the WARN_ON_ONCE() when the source port does not resolve to a
user port.
- Move the IFH field position and size defines from
include/linux/dsa/lan9645x.h into net/dsa/tag_lan9645x.c.
- Rename LAN9645X_IFH_LEN to LAN9645X_IFH_LEN_BYTES, so it does not read
as the position of the IFH LEN field.
- Use skb_vlan_eth_hdr() in lan9645x_xmit_get_vlan_info().
- Drop BTM_MSK() TOP_MSK() helpers for GENMASK().
- Rename set_merge_mask() to lan9645x_ifh_merge_byte().
- Name the vendor in the Kconfig prompt, spell the part LAN9645x, and
move the entry to its sorted position after NET_DSA_TAG_KSZ.
- Clamp the classified QoS class to the width of IFH_QOS_CLASS. A
skb->priority above 7 aliased down onto an unrelated class.
- Comment why needed_headroom covers the extraction prefix.
- Restore the classified VLAN as a C-tag rather than following the IFH
TAG_TYPE, so a terminated S-tagged frame lands on the same VID the
hardware forwards it on.
Changes in v11:
- Update comment about vlan tag hwaccel use in tag driver.
- Bump DSA_TAG_PROTO value in include/net/dsa.h.
Changes in v10:
- Update tag driver after skb ownership model change in DSA taggers.
Explicitly freeing the skb at rcv/xmit return points.
Changes in v8:
- Drop the cpu_port local and use ds->num_ports directly as the IFH
source port (the CPU port module index).
- Reword the reflection comment to refer to "the CPU".
Changes in v7:
- Introduce cpu queue based frame classification, and refactor to the
categories default, trap and copy, which mirrors usage instead of
being based on frame types.
Changes in v6:
- rebased on net-next, bumping DSA_TAG_PROTO_LAN9645X_VALUE to 34
Changes in v5:
- Undo offset fix in postpull_rcsum. The original logic was correct for
CHECKSUM_COMPLETE host NICs
- Use __always_inline in lan9645x_ifh_{get,set}
- remove double space after = in set_merge_mask
Changes in v4:
- Fix offset in postpull_rcsum so prefix eth header is cleared, not
actual eth header, so tag driver works with CHECKSUM_COMPLETE host
NICs
- Fix untagged rx on vlan aware port with pvid
Changes in v3:
- guard vlan_remove_tag behind skb_headlen(skb) >= VLAN_ETH_HLEN on xmit
- add pskb_may_pull checks in rx path
Changes in v2:
- sorting in net/dsa/Kconfig
- sorting in net/dsa/Makefile
- remove default zero promisc_on_conduit
- move functions to to .c file
- add justification for inline usage to commit message
- add __skb_put_padto on xmit path
- fix hwaccel_put_tag
---
MAINTAINERS | 8 +
include/linux/dsa/lan9645x.h | 54 +++++
include/net/dsa.h | 2 +
net/dsa/Kconfig | 11 +
net/dsa/Makefile | 1 +
net/dsa/tag_lan9645x.c | 513 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 589 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 09713dc4c6a7..2305052c3a5e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18184,6 +18184,14 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/phy/microchip_t1.c
+MICROCHIP LAN9645X ETHERNET SWITCH DRIVER
+M: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
+M: UNGLinuxDriver@microchip.com
+L: netdev@vger.kernel.org
+S: Maintained
+F: include/linux/dsa/lan9645x.h
+F: net/dsa/tag_lan9645x.c
+
MICROCHIP LAN966X ETHERNET DRIVER
M: Horatiu Vultur <horatiu.vultur@microchip.com>
M: UNGLinuxDriver@microchip.com
diff --git a/include/linux/dsa/lan9645x.h b/include/linux/dsa/lan9645x.h
new file mode 100644
index 000000000000..e774aa137092
--- /dev/null
+++ b/include/linux/dsa/lan9645x.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#ifndef _NET_DSA_TAG_LAN9645X_H_
+#define _NET_DSA_TAG_LAN9645X_H_
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+/* LAN9645x supports 3 different formats on an NPI port, long prefix, short
+ * prefix and no prefix. The format can be configured asymmetrically on RX and
+ * TX. We use long prefix on extraction (RX), and no prefix on injection.
+ * The long prefix on extraction helps get through the conduit port on host
+ * side, since it will see a broadcast MAC.
+ *
+ * The internal frame header (IFH) is 28 bytes.
+ *
+ * Long prefix, 16 bytes + IFH:
+ * - DMAC = 0xFFFFFFFFFFFF on extraction.
+ * - SMAC = 0xFEFFFFFFFFFF on extraction.
+ * - ETYPE = 0x8880
+ * - payload = 0x0011
+ * - IFH
+ *
+ * Short prefix, 4 bytes + IFH:
+ * - 0x8880
+ * - 0x0011
+ * - IFH
+ *
+ * No prefix:
+ * - IFH
+ *
+ */
+#define LAN9645X_IFH_TAG_TYPE_C 0
+#define LAN9645X_IFH_TAG_TYPE_S 1
+#define LAN9645X_IFH_LEN_U32 7
+#define LAN9645X_IFH_LEN_BYTES (LAN9645X_IFH_LEN_U32 * sizeof(u32))
+#define LAN9645X_IFH_BITS (LAN9645X_IFH_LEN_BYTES * BITS_PER_BYTE)
+#define LAN9645X_LONG_PREFIX_LEN 16
+#define LAN9645X_TOTAL_TAG_LEN \
+ (LAN9645X_LONG_PREFIX_LEN + LAN9645X_IFH_LEN_BYTES)
+
+/* Chip has 8 cpu queues. The cpu queues used by a frame are passed as a mask in
+ * the IFH on extraction. We use this to avoid classifying BPDU, IGMP and MLD
+ * frames in the tag driver.
+ */
+enum {
+ LAN9645X_CPUQ_DEF = 0,
+ LAN9645X_CPUQ_TRAP = 1,
+ LAN9645X_CPUQ_COPY = 2,
+};
+
+#endif /* _NET_DSA_TAG_LAN9645X_H_ */
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 5d12191b6f6f..09f3043d57f0 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -62,6 +62,7 @@ struct tc_action;
#define DSA_TAG_PROTO_KSZ8463_VALUE 34
#define DSA_TAG_PROTO_MT7628_VALUE 35
#define DSA_TAG_PROTO_KS8995_VALUE 36
+#define DSA_TAG_PROTO_LAN9645X_VALUE 37
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -101,6 +102,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
DSA_TAG_PROTO_MT7628 = DSA_TAG_PROTO_MT7628_VALUE,
DSA_TAG_PROTO_KS8995 = DSA_TAG_PROTO_KS8995_VALUE,
+ DSA_TAG_PROTO_LAN9645X = DSA_TAG_PROTO_LAN9645X_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 4f44bf3ede23..bdeb60af764b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -137,6 +137,17 @@ config NET_DSA_TAG_KSZ
Say Y if you want to enable support for tagging frames for the
Microchip 8795/937x/9477/9893 families of switches.
+config NET_DSA_TAG_LAN9645X
+ tristate "Tag driver for Microchip LAN9645x switches"
+ help
+ Say Y or M if you want to enable NPI tagging for the Microchip
+ LAN9645x switches. In this mode, the frames over the Ethernet CPU
+ port are prepended with a hardware-defined injection/extraction frame
+ header. On injection a 28 byte internal frame header (IFH) is used.
+ On extraction a 16 byte prefix is prepended before the internal frame
+ header. This prefix starts with a broadcast MAC, to ease passage
+ through the host side RX filter.
+
config NET_DSA_TAG_NETC
tristate "Tag driver for NXP NETC switches"
help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 1f9cc30e9988..3cd53124448d 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
obj-$(CONFIG_NET_DSA_TAG_KS8995) += tag_ks8995.o
obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
+obj-$(CONFIG_NET_DSA_TAG_LAN9645X) += tag_lan9645x.o
obj-$(CONFIG_NET_DSA_TAG_MT7628) += tag_mt7628.o
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
diff --git a/net/dsa/tag_lan9645x.c b/net/dsa/tag_lan9645x.c
new file mode 100644
index 000000000000..9b373bac91e9
--- /dev/null
+++ b/net/dsa/tag_lan9645x.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/dsa/lan9645x.h>
+
+#include "tag.h"
+
+#define LAN9645X_NAME "lan9645x"
+
+/* The internal frame header (IFH) is 28 bytes, and the fields are documented
+ * below. Some fields are only used on either injection or extraction.
+ *
+ * Injection header
+ */
+#define IFH_INJ_TIMESTAMP 192
+#define IFH_BYPASS 191
+#define IFH_MASQ 190
+/* Extraction header */
+#define IFH_TIMESTAMP_NS 194
+#define IFH_TIMESTAMP_SUBNS 186
+/* Injection header */
+#define IFH_MASQ_PORT 186
+#define IFH_RCT_INJ 185
+/* Extraction header */
+#define IFH_LEN 171
+#define IFH_WRDMODE 169
+/* Extraction/Injection header */
+#define IFH_RTAGD 167
+/* Extraction header */
+#define IFH_CUTTHRU 166
+/* Extraction/Injection header */
+#define IFH_REW_CMD 156
+#define IFH_REW_OAM 155
+#define IFH_PDU_TYPE 151
+#define IFH_FCS_UPD 150
+#define IFH_DP 149
+/* Reserved */
+#define IFH_RTE_INB_UPDATE 148
+/* Extraction/Injection header */
+#define IFH_POP_CNT 146
+#define IFH_ETYPE_OFS 144
+/* Extraction header */
+#define IFH_SRCPORT 140
+/* Extraction/Injection header */
+#define IFH_SEQ_NUM 120
+#define IFH_TAG_TYPE 119
+#define IFH_TCI 103
+#define IFH_DSCP 97
+#define IFH_QOS_CLASS 94
+#define IFH_CPUQ 86
+/* Extraction header */
+#define IFH_LEARN_FLAGS 84
+/* Extraction/Injection header */
+#define IFH_SFLOW_ID 80
+#define IFH_ACL_HIT 79
+#define IFH_ACL_IDX 73
+#define IFH_ISDX 65
+#define IFH_DSTS 55
+/* Extraction header */
+#define IFH_FLOOD 53
+/* Extraction/Injection header */
+#define IFH_SEQ_OP 51
+#define IFH_IPV 48
+/* Injection header */
+#define IFH_AFI 47
+/* Reserved */
+#define IFH_RTP_ID 37
+#define IFH_RTP_SUBID 36
+#define IFH_PN_DATA_STATUS 28
+#define IFH_PN_TRANSF_STATUS_ZERO 27
+#define IFH_PN_CC 11
+/* Extraction/Injection header */
+#define IFH_DUPL_DISC_ENA 10
+/* Extraction header */
+#define IFH_RCT_AVAIL 9
+
+#define IFH_INJ_TIMESTAMP_SZ 32
+#define IFH_BYPASS_SZ 1
+#define IFH_MASQ_SZ 1
+#define IFH_TIMESTAMP_NS_SZ 30
+#define IFH_TIMESTAMP_SUBNS_SZ 8
+#define IFH_MASQ_PORT_SZ 4
+#define IFH_RCT_INJ_SZ 1
+#define IFH_LEN_SZ 14
+#define IFH_WRDMODE_SZ 2
+#define IFH_RTAGD_SZ 2
+#define IFH_CUTTHRU_SZ 1
+#define IFH_REW_CMD_SZ 10
+#define IFH_REW_OAM_SZ 1
+#define IFH_PDU_TYPE_SZ 4
+#define IFH_FCS_UPD_SZ 1
+#define IFH_DP_SZ 1
+#define IFH_RTE_INB_UPDATE_SZ 1
+#define IFH_POP_CNT_SZ 2
+#define IFH_ETYPE_OFS_SZ 2
+#define IFH_SRCPORT_SZ 4
+#define IFH_SEQ_NUM_SZ 16
+#define IFH_TAG_TYPE_SZ 1
+#define IFH_TCI_SZ 16
+#define IFH_DSCP_SZ 6
+#define IFH_QOS_CLASS_SZ 3
+#define IFH_CPUQ_SZ 8
+#define IFH_LEARN_FLAGS_SZ 2
+#define IFH_SFLOW_ID_SZ 4
+#define IFH_ACL_HIT_SZ 1
+#define IFH_ACL_IDX_SZ 6
+#define IFH_ISDX_SZ 8
+#define IFH_DSTS_SZ 10
+#define IFH_FLOOD_SZ 2
+#define IFH_SEQ_OP_SZ 2
+#define IFH_IPV_SZ 3
+#define IFH_AFI_SZ 1
+#define IFH_RTP_ID_SZ 10
+#define IFH_RTP_SUBID_SZ 1
+#define IFH_PN_DATA_STATUS_SZ 8
+#define IFH_PN_TRANSF_STATUS_ZERO_SZ 1
+#define IFH_PN_CC_SZ 16
+#define IFH_DUPL_DISC_ENA_SZ 1
+#define IFH_RCT_AVAIL_SZ 1
+
+static __always_inline void lan9645x_ifh_merge_byte(u8 *dst, u8 src, u8 mask)
+{
+ *dst = *dst ^ ((*dst ^ src) & mask);
+}
+
+/* The internal frame header (IFH) is a big-endian 28 byte unpadded bit array.
+ * Frames can be prepended with an IFH on injection and extraction. There
+ * are two field layouts, one for extraction and one for injection.
+ *
+ * IFH bits go from high to low, for instance
+ * ifh[0] = [223:216]
+ * ifh[27] = [7:0]
+ *
+ * Here is an example of setting a value starting at bit 13 of bit length 17.
+ *
+ * val = 0x1ff
+ * pos = 13
+ * length = 17
+ *
+ *
+ * IFH[] 0 23 24 25 26 27
+ *
+ * end_u8 start_u8
+ * +--------+----------------+--------+--------+--------+--------+--------+
+ * | | | | | | | |
+ * IFH | | .... | | vvvvvvvvvvvvvvvvvvv | |
+ * | | | | | | | | | |
+ * +--------+----------------+--------+--+-----+--------+--+-----+--------+
+ * Bits 223 39 32 31| 24 23 16 15| 8 7 0
+ * | |
+ * | |
+ * | |
+ * v v
+ * end = 29 pos = 13
+ * end_rem = 5 pos_rem = 5
+ * end_u8 = 3 start_u8 = 1
+ * GENMASK(5, 0) = 0x3f GENMASK(7, 5) = 0xe0
+ *
+ *
+ * In end_u8 and start_u8 we must merge the existing IFH byte with the new
+ * value. In the 'middle' bytes of the value we can overwrite the corresponding
+ * IFH byte.
+ */
+static __always_inline void lan9645x_ifh_set(u8 *ifh, u32 val, size_t pos,
+ size_t length)
+{
+ size_t end = (pos + length) - 1;
+ size_t end_rem = end & 0x7;
+ size_t pos_rem = pos & 0x7;
+ size_t start_u8 = pos >> 3;
+ size_t end_u8 = end >> 3;
+ u8 end_mask, start_mask;
+ size_t vshift;
+ u8 *ptr;
+
+ BUILD_BUG_ON_MSG(length > 32, "IFH field size wider than 32.");
+ BUILD_BUG_ON_MSG(length == 0, "IFH field size of 0.");
+ BUILD_BUG_ON_MSG(pos + length > LAN9645X_IFH_BITS,
+ "IFH field overflows IFH");
+
+ end_mask = GENMASK(end_rem, 0);
+ start_mask = GENMASK(7, pos_rem);
+
+ ptr = &ifh[LAN9645X_IFH_LEN_BYTES - 1 - end_u8];
+
+ if (end_u8 == start_u8)
+ return lan9645x_ifh_merge_byte(ptr, val << pos_rem,
+ end_mask & start_mask);
+
+ vshift = length - end_rem - 1;
+ lan9645x_ifh_merge_byte(ptr++, val >> vshift, end_mask);
+
+ for (size_t j = 1; j < end_u8 - start_u8; j++) {
+ vshift -= 8;
+ *ptr++ = val >> vshift;
+ }
+
+ lan9645x_ifh_merge_byte(ptr, val << pos_rem, start_mask);
+}
+
+static __always_inline u32 lan9645x_ifh_get(const u8 *ifh, size_t pos,
+ size_t length)
+{
+ size_t end = (pos + length) - 1;
+ size_t end_rem = end & 0x7;
+ size_t pos_rem = pos & 0x7;
+ size_t start_u8 = pos >> 3;
+ size_t end_u8 = end >> 3;
+ u8 end_mask, start_mask;
+ const u8 *ptr;
+ u32 val;
+
+ BUILD_BUG_ON_MSG(length > 32, "IFH field size wider than 32.");
+ BUILD_BUG_ON_MSG(length == 0, "IFH field size of 0.");
+ BUILD_BUG_ON_MSG(pos + length > LAN9645X_IFH_BITS,
+ "IFH field overflows IFH");
+
+ end_mask = GENMASK(end_rem, 0);
+ start_mask = GENMASK(7, pos_rem);
+
+ ptr = &ifh[LAN9645X_IFH_LEN_BYTES - 1 - end_u8];
+
+ if (end_u8 == start_u8)
+ return (*ptr & end_mask & start_mask) >> pos_rem;
+
+ val = *ptr++ & end_mask;
+
+ for (size_t j = 1; j < end_u8 - start_u8; j++)
+ val = val << 8 | *ptr++;
+
+ return val << (8 - pos_rem) | (*ptr & start_mask) >> pos_rem;
+}
+
+static struct sk_buff *lan9645x_xmit_get_vlan_info(struct sk_buff *skb,
+ struct net_device *br,
+ u32 *vlan_tci,
+ u32 *tag_type)
+{
+ struct vlan_ethhdr *hdr;
+ u16 proto, tci;
+
+ /* If the VLAN tag is in the hwaccel area, move it to the payload so
+ * that both cases are handled uniformly below, and so that the conduit
+ * cannot insert it into the middle of the IFH we are about to prepend.
+ */
+ if (unlikely(skb_vlan_tag_present(skb))) {
+ skb = __vlan_hwaccel_push_inside(skb);
+ if (!skb)
+ return NULL;
+ }
+
+ if (!br || !br_vlan_enabled(br)) {
+ *vlan_tci = 0;
+ *tag_type = LAN9645X_IFH_TAG_TYPE_C;
+ return skb;
+ }
+
+ br_vlan_get_proto(br, &proto);
+
+ *vlan_tci = 0;
+ if (skb->len >= VLAN_ETH_HLEN) {
+ if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN))) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ hdr = skb_vlan_eth_hdr(skb);
+ if (ntohs(hdr->h_vlan_proto) == proto &&
+ ntohs(hdr->h_vlan_TCI) & VLAN_VID_MASK) {
+ vlan_remove_tag(skb, &tci);
+ *vlan_tci = tci;
+ }
+ }
+
+ *tag_type = (proto != ETH_P_8021Q) ? LAN9645X_IFH_TAG_TYPE_S :
+ LAN9645X_IFH_TAG_TYPE_C;
+
+ return skb;
+}
+
+static void lan9645x_offload_fwd_mark(struct sk_buff *skb, u32 cpuq)
+{
+ /* Trapped frames must be forwarded by the stack. */
+ if (cpuq & BIT(LAN9645X_CPUQ_TRAP)) {
+ skb->offload_fwd_mark = 0;
+ return;
+ }
+
+ dsa_default_offload_fwd_mark(skb);
+}
+
+static struct sk_buff *lan9645x_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct dsa_port *dp = dsa_user_to_port(ndev);
+ u32 vlan_tci, tag_type;
+ u32 qos_class;
+ void *ifh;
+
+ skb = lan9645x_xmit_get_vlan_info(skb, dsa_port_bridge_dev_get(dp),
+ &vlan_tci, &tag_type);
+ if (!skb)
+ return NULL;
+
+ /* We need to make sure frame has the proper size after IFH is stripped
+ * by hw.
+ */
+ if (skb_put_padto(skb, ETH_ZLEN))
+ return NULL;
+
+ qos_class = netdev_get_num_tc(ndev) ?
+ netdev_get_prio_tc_map(ndev, skb->priority) :
+ skb->priority;
+ qos_class = min_t(u32, qos_class, GENMASK(IFH_QOS_CLASS_SZ - 1, 0));
+
+ /* Make room for IFH */
+ ifh = skb_push(skb, LAN9645X_IFH_LEN_BYTES);
+ memset(ifh, 0, LAN9645X_IFH_LEN_BYTES);
+
+ lan9645x_ifh_set(ifh, 1, IFH_BYPASS, IFH_BYPASS_SZ);
+ lan9645x_ifh_set(ifh, tag_type, IFH_TAG_TYPE, IFH_TAG_TYPE_SZ);
+ lan9645x_ifh_set(ifh, vlan_tci, IFH_TCI, IFH_TCI_SZ);
+ lan9645x_ifh_set(ifh, qos_class, IFH_QOS_CLASS, IFH_QOS_CLASS_SZ);
+ lan9645x_ifh_set(ifh, BIT(dp->index), IFH_DSTS, IFH_DSTS_SZ);
+
+ return skb;
+}
+
+static int lan9645x_ifh_gap_len(const u8 *ifh)
+{
+ u32 popcnt = lan9645x_ifh_get(ifh, IFH_POP_CNT, IFH_POP_CNT_SZ);
+ u32 etype_ofs = lan9645x_ifh_get(ifh, IFH_ETYPE_OFS, IFH_ETYPE_OFS_SZ);
+
+ /* Tag pushing is disabled on the NPI port via REW_TAG_CFG, so if this
+ * fires REW_TAG_CFG is misconfigured.
+ */
+ if (popcnt == 1 ||
+ (popcnt == 0 && etype_ofs > 0))
+ return -EINVAL;
+
+ /* Since REW_PORT_CFG_NO_REWRITE=0 is required on the NPI port, we need
+ * to account for any tags popped by the hardware, as that will leave a
+ * gap between the IFH and DMAC. Tag pushing is disabled.
+ *
+ * The IFH fields do not have intuitive values. This is how HW does the
+ * calculation:
+ *
+ * DMAC_DT = (ifh.pop_cnt == 0 && ifh.etype_ofs == 0) ? 4 : ifh.pop_cnt
+ * DMAC_OFFSET = TAG_SIZE + 4*(DMAC_DT - 2)
+ *
+ * With tag pushing disabled we have either
+ *
+ * popcnt=0 and etype_ofs=0 => 2x pop
+ * popcnt=3 and etype_ofs=* => 1x pop
+ * popcnt=2 and etype_ofs=* => no pop
+ *
+ * The remaining combinations indicate a push and will not occur.
+ */
+ if (popcnt == 0 && etype_ofs == 0)
+ return 2 * VLAN_HLEN;
+ if (popcnt == 3)
+ return VLAN_HLEN;
+
+ return 0;
+}
+
+static struct sk_buff *lan9645x_rcv(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ u32 src_port, qos_class, vlan_tci, cpuq;
+ int ifh_gap_len;
+ u8 *ifh;
+
+ /* Conduit already consumed DMAC,SMAC,ETYPE from long prefix. Go back
+ * to beginning of frame.
+ */
+ skb_push(skb, ETH_HLEN);
+
+ if (unlikely(!pskb_may_pull(skb, LAN9645X_TOTAL_TAG_LEN))) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* IFH starts after our long prefix */
+ ifh = skb_pull(skb, LAN9645X_LONG_PREFIX_LEN);
+
+ src_port = lan9645x_ifh_get(ifh, IFH_SRCPORT, IFH_SRCPORT_SZ);
+ vlan_tci = lan9645x_ifh_get(ifh, IFH_TCI, IFH_TCI_SZ);
+ qos_class = lan9645x_ifh_get(ifh, IFH_QOS_CLASS, IFH_QOS_CLASS_SZ);
+ cpuq = lan9645x_ifh_get(ifh, IFH_CPUQ, IFH_CPUQ_SZ);
+
+ ifh_gap_len = lan9645x_ifh_gap_len(ifh);
+ if (ifh_gap_len < 0) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Set skb->data at start of real header */
+ skb_pull(skb, LAN9645X_IFH_LEN_BYTES);
+
+ if (unlikely(!pskb_may_pull(skb, ifh_gap_len + ETH_HLEN))) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ skb_pull(skb, ifh_gap_len);
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, ETH_HLEN);
+ skb_reset_mac_len(skb);
+
+ /* Reset skb->data past the actual ethernet header. */
+ skb_pull(skb, ETH_HLEN);
+
+ /* We must deliver the skb so skb->csum only covers the data beyond the
+ * real ethernet header. The fake ethernet header in the prefix is
+ * not part of skb->csum already. We must subtract what remains of the
+ * prefix, the ifh, the gap and the real ethernet header, which
+ * dsa_switch_rcv() pushes back without updating skb->csum. The start
+ * is derived from the current skb->data rather than saved on entry,
+ * because the pskb_may_pull() calls above may have reallocated
+ * skb->head.
+ */
+ skb_postpull_rcsum(skb,
+ skb->data - LAN9645X_TOTAL_TAG_LEN - ifh_gap_len,
+ LAN9645X_TOTAL_TAG_LEN + ifh_gap_len);
+
+ skb->dev = dsa_conduit_find_user(ndev, 0, src_port);
+ if (!skb->dev) {
+ /* Reflection is disabled for frames from the tag driver itself,
+ * however it is possible that a frame sent directly on the
+ * conduit gets reflected, so we drop it here.
+ */
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ lan9645x_offload_fwd_mark(skb, cpuq);
+
+ skb->priority = qos_class;
+
+ /* While we have REW_PORT_CFG_NO_REWRITE=0 on the NPI port, we still
+ * disable port VLAN tag pushing with REW_TAG_CFG. A frame ingressing
+ * on a vlan aware port, which is forwarded to the CPU, will not carry
+ * vlan info in the frame data, because the tag is popped. The
+ * classified VID is only communicated via the IFH, never in the
+ * payload. We therefore restore it via hwaccel and must not pop an
+ * in-band tag here.
+ */
+ if (ifh_gap_len) {
+ /* The tag is restored as a C-tag, not as the TAG_TYPE the IFH
+ * reports. The classifier recognizes both TPIDs as VLAN tags,
+ * so an S-tag has already been used for classification by the
+ * time we get here. Restoring it as 802.1AD would make the
+ * bridge push it back into the payload and reclassify the frame
+ * to the port pvid, on a different VID than the one the
+ * hardware forwarded it on.
+ */
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
+ }
+
+ return skb;
+}
+
+static void lan9645x_flow_dissect(const struct sk_buff *skb, __be16 *proto,
+ int *offset)
+{
+ u8 ifh_buf[LAN9645X_IFH_LEN_BYTES];
+ const __be16 *encap_proto;
+ __be16 proto_buf;
+ const u8 *ifh;
+ int tag_len;
+
+ *proto = 0;
+ *offset = 0;
+
+ ifh = skb_header_pointer(skb, LAN9645X_LONG_PREFIX_LEN - ETH_HLEN,
+ LAN9645X_IFH_LEN_BYTES, ifh_buf);
+ if (!ifh)
+ return;
+
+ tag_len = lan9645x_ifh_gap_len(ifh);
+ if (tag_len < 0)
+ return;
+
+ tag_len += LAN9645X_TOTAL_TAG_LEN;
+
+ encap_proto = skb_header_pointer(skb, tag_len - sizeof(proto_buf),
+ sizeof(proto_buf), &proto_buf);
+ if (!encap_proto)
+ return;
+
+ *proto = *encap_proto;
+ *offset = tag_len;
+}
+
+static const struct dsa_device_ops lan9645x_netdev_ops = {
+ .name = LAN9645X_NAME,
+ .proto = DSA_TAG_PROTO_LAN9645X,
+ .xmit = lan9645x_xmit,
+ .rcv = lan9645x_rcv,
+ .flow_dissect = lan9645x_flow_dissect,
+ /* Covers the extraction prefix too, since dsa_tag_protocol_overhead()
+ * sizes the conduit MTU from this.
+ */
+ .needed_headroom = LAN9645X_TOTAL_TAG_LEN,
+};
+
+MODULE_DESCRIPTION("DSA tag driver for LAN9645x family of switches, using NPI port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN9645X, LAN9645X_NAME);
+
+module_dsa_tag_driver(lan9645x_netdev_ops);
--
2.52.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v16 2/4] dt-bindings: net: dsa: add Microchip LAN9645x switch
2026-11-03 12:31 [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 1/4] net: dsa: add tag driver for LAN9645X Jens Emil Schulz Østergaard
@ 2026-11-03 12:31 ` Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 3/4] net: dsa: lan9645x: add autogenerated register macros Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 4/4] net: dsa: lan9645x: add basic dsa driver for LAN9645X Jens Emil Schulz Østergaard
3 siblings, 0 replies; 5+ messages in thread
From: Jens Emil Schulz Østergaard @ 2026-11-03 12:31 UTC (permalink / raw)
To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Woojung Huh, Russell King,
Steen Hegelund, Daniel Machon, Geert Uytterhoeven, Magnus Damm,
Eric Dumazet, Richard Cochran, Eric Dumazet
Cc: linux-kernel, netdev, devicetree, linux-renesas-soc,
Jens Emil Schulz Østergaard
Add bindings for LAN9645X switch. We use a fallback compatible for the
smallest SKU microchip,lan96455s-switch. The last digit of the part number
is the number of usable front ports and the letter is the feature set.
The switch is a child of an externally controlled parent that hands out
one regmap per register target, so reg and reg-names describe the target
layout the way mscc,vsc7514-switch.yaml does for the equivalent
mscc,vsc7512-switch node. That binding, and the mfd parent it hangs off
in mscc,ocelot.yaml, is the model this driver and its parent follow.
Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
Changes in v14:
- Use the dt-bindings: net: dsa: subject prefix and do not repeat
"bindings" in the subject.
- Describe all switch register targets in reg, and the analyzer and
PTP interrupts.
Changes in v13:
- Remove section quoting Documentation/networking/phy.rst from commit
message.
- Remove rx/tx-internal-delay-ps from example.
Changes in v12:
- Remove Rob's Reviewed-by
- Match both the port@ and ethernet-port@ spellings, so the per port
constraints apply. dsa.yaml accepts either, and the local pattern only
matched the long form.
- Allow 0 as well as 2000 for rx/tx-internal-delay-ps, matching what the
driver accepts, and restrict them to those two values on RGMII ports.
- Constrain the ethernet-ports node the way renesas,rzn1-a5psw.yaml does,
with additionalProperties instead of a local dsa-port.yaml $ref.
- Put the example properties in canonical order, so dt-check-style
--mode=strict is clean.
- Describe the switch register targets in reg, with a matching reg-names,
following mscc,vsc7514-switch.yaml.
- Document the default of rx/tx-internal-delay-ps, as
microchip,lan937x.yaml does.
- Describe the SKU naming in the binding description.
Changes in v5:
- No changes.
Changes in v4:
- No changes.
Changes in v3:
- remove additionalProperties: true
- remove unnecessary | from description
- change top level $ref to dsa.yaml#/$defs/ethernet-ports
- use ethernet-ports and ethernet-port
- move ethernet-ports under properties instead of patternProperties
- move unevaluatedProperties: false after $ref
- update example to use ethernet-ports and ethernet-port
Changes in v2:
- rename file to microchip,lan96455s-switch.yaml
- remove led vendor property
- add {rx,tx}-internal-delay-ps for rgmii delay
- remove labels from example
- remove container node from example
---
.../net/dsa/microchip,lan96455s-switch.yaml | 208 +++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 209 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
new file mode 100644
index 000000000000..517bc4c00de3
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/microchip,lan96455s-switch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip LAN9645x Ethernet switch
+
+maintainers:
+ - Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
+
+description: |
+ The LAN9645x switch is a multi-port Gigabit AVB/TSN Ethernet switch with
+ five integrated 10/100/1000Base-T PHYs. In addition to the integrated PHYs,
+ it supports up to 2 RGMII/RMII, up to 2 BASE-X/SERDES/2.5GBASE-X and one
+ Quad-SGMII interfaces.
+
+ The part number encodes the SKU. The last digit is the number of usable
+ front ports, 5, 7 or 9. The letter is the feature set, f for full and s for
+ standard, where full adds HSR, PRP, TAS, frame preemption and PSFP.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - microchip,lan96455s-switch
+ - items:
+ - enum:
+ - microchip,lan96455f-switch
+ - microchip,lan96457f-switch
+ - microchip,lan96459f-switch
+ - microchip,lan96457s-switch
+ - microchip,lan96459s-switch
+ - const: microchip,lan96455s-switch
+
+ reg:
+ items:
+ - description: General configuration block target
+ - description:
+ CPU device queue system target, the register based frame injection
+ and extraction interface
+ - description: Precision Time Protocol target
+ - description: Chip top level target
+ - description: Time aware shaper target
+ - description: Rewriter target
+ - description: VCAP ES0 target, egress stage 0 classification
+ - description: OAM MEP target
+ - description: VCAP S1 target, ingress stage 1 classification
+ - description: VCAP S2 target, ingress stage 2 classification
+ - description: Switching engine target
+ - description: HSIO target
+ - description: Port 0 device target
+ - description: Port 1 device target
+ - description: Port 2 device target
+ - description: Port 3 device target
+ - description: Port 4 device target
+ - description: Port 5 device target
+ - description: Port 6 device target
+ - description: Port 7 device target
+ - description: Port 8 device target
+ - description: Queue system target
+ - description: Automatic frame injector target
+ - description: Analyzer target
+
+ reg-names:
+ items:
+ - const: gcb
+ - const: qs
+ - const: ptp
+ - const: chip_top
+ - const: tas
+ - const: rew
+ - const: vcap0
+ - const: mep
+ - const: vcap1
+ - const: vcap2
+ - const: sys
+ - const: hsio
+ - const: dev0
+ - const: dev1
+ - const: dev2
+ - const: dev3
+ - const: dev4
+ - const: dev5
+ - const: dev6
+ - const: dev7
+ - const: dev8
+ - const: qsys
+ - const: afi
+ - const: ana
+
+ interrupts:
+ items:
+ - description: PTP timestamp ready interrupt
+ - description: PTP synchronization interrupt from the PTP pins
+ - description: Analyzer interrupt
+
+ interrupt-names:
+ items:
+ - const: ptp
+ - const: ptp-ext
+ - const: ana
+
+ ethernet-ports:
+ type: object
+ additionalProperties: true
+ patternProperties:
+ "^(ethernet-)?port@[0-8]$":
+ type: object
+ description: Ethernet switch ports
+ additionalProperties: true
+
+ allOf:
+ - if:
+ properties:
+ phy-mode:
+ contains:
+ enum:
+ - rgmii
+ - rgmii-rxid
+ - rgmii-txid
+ - rgmii-id
+ then:
+ properties:
+ rx-internal-delay-ps:
+ $ref: "#/$defs/internal-delay-ps"
+ tx-internal-delay-ps:
+ $ref: "#/$defs/internal-delay-ps"
+
+$ref: dsa.yaml#/$defs/ethernet-ports
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - ethernet-ports
+
+unevaluatedProperties: false
+
+$defs:
+ internal-delay-ps:
+ description:
+ Disable the delay line using 0 ps, or enable the 2000 ps delay. The
+ delay line is not tunable, so no other phase can be selected.
+ enum: [0, 2000]
+ default: 0
+
+examples:
+ - |
+ ethernet-switch@4000 {
+ compatible = "microchip,lan96459f-switch", "microchip,lan96455s-switch";
+ reg = <0x4000 0x244>, <0x8000 0x4c>, <0xc000 0x1f0>,
+ <0x10000 0x134>, <0x14000 0x2c8>, <0x18000 0x1910>,
+ <0x1c000 0x468>, <0x20000 0x1480>, <0x24000 0x468>,
+ <0x28000 0x468>, <0x2c000 0x12d4>, <0x30000 0xe4>,
+ <0x38000 0xb8>, <0x3c000 0xb8>, <0x40000 0xb8>,
+ <0x44000 0xb8>, <0x48000 0xb8>, <0x4c000 0xb8>,
+ <0x50000 0xb8>, <0x54000 0xb8>, <0x58000 0xb8>,
+ <0x100000 0xa000>, <0x120000 0x1998>, <0x140000 0x7468>;
+ reg-names = "gcb", "qs", "ptp", "chip_top", "tas", "rew", "vcap0",
+ "mep", "vcap1", "vcap2", "sys", "hsio", "dev0", "dev1",
+ "dev2", "dev3", "dev4", "dev5", "dev6", "dev7", "dev8",
+ "qsys", "afi", "ana";
+ interrupts = <27>, <26>, <17>;
+ interrupt-names = "ptp", "ptp-ext", "ana";
+
+ ethernet-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-port@0 {
+ reg = <0>;
+ phy-handle = <&cuphy0>;
+ phy-mode = "gmii";
+ };
+
+ ethernet-port@1 {
+ reg = <1>;
+ phy-handle = <&cuphy1>;
+ phy-mode = "gmii";
+ };
+
+ ethernet-port@2 {
+ reg = <2>;
+ phy-handle = <&cuphy2>;
+ phy-mode = "gmii";
+ };
+
+ ethernet-port@3 {
+ reg = <3>;
+ phy-handle = <&cuphy3>;
+ phy-mode = "gmii";
+ };
+
+ ethernet-port@7 {
+ reg = <7>;
+ ethernet = <&cpu_host_port>;
+ phy-mode = "rgmii";
+
+ fixed-link {
+ full-duplex;
+ pause;
+ speed = <1000>;
+ };
+ };
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 2305052c3a5e..5bc2bbb308d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18189,6 +18189,7 @@ M: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
M: UNGLinuxDriver@microchip.com
L: netdev@vger.kernel.org
S: Maintained
+F: Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
F: include/linux/dsa/lan9645x.h
F: net/dsa/tag_lan9645x.c
--
2.52.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v16 3/4] net: dsa: lan9645x: add autogenerated register macros
2026-11-03 12:31 [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 1/4] net: dsa: add tag driver for LAN9645X Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 2/4] dt-bindings: net: dsa: add Microchip LAN9645x switch Jens Emil Schulz Østergaard
@ 2026-11-03 12:31 ` Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 4/4] net: dsa: lan9645x: add basic dsa driver for LAN9645X Jens Emil Schulz Østergaard
3 siblings, 0 replies; 5+ messages in thread
From: Jens Emil Schulz Østergaard @ 2026-11-03 12:31 UTC (permalink / raw)
To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Woojung Huh, Russell King,
Steen Hegelund, Daniel Machon, Geert Uytterhoeven, Magnus Damm,
Eric Dumazet, Richard Cochran, Eric Dumazet
Cc: linux-kernel, netdev, devicetree, linux-renesas-soc,
Jens Emil Schulz Østergaard
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 v13:
- Add QS:XTR:XTR_GRP_CFG for extraction mode configuration.
Changes in v12:
- Regenerate without the unused AFI target.
Changes in v7:
- Add ANA:COMMON:CPUQ_8021_CFG for bpdu cpu queue configuration.
Changes in v3:
- No changes
Changes in v2:
- Add cpuq registers
---
MAINTAINERS | 1 +
drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h | 1932 ++++++++++++++++++++
2 files changed, 1933 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 5bc2bbb308d1..581f895bb618 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18190,6 +18190,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..0414c7239103
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_regs.h
@@ -0,0 +1,1932 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (c) 2026 Microchip Technology Inc.
+ */
+
+/* This file is autogenerated by cml-utils 2026-09-28 15:07:47 +0200.
+ * Commit ID: 725d258adf97fd69178153a51b7fc60f2a4b5988
+ */
+
+#ifndef _LAN9645X_REGS_H_
+#define _LAN9645X_REGS_H_
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+
+enum lan9645x_target {
+ 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__
+
+/* 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)
+
+/* ANA:COMMON:CPUQ_8021_CFG */
+#define ANA_CPUQ_8021_CFG(r) __REG(TARGET_ANA,\
+ 0, 1, 29248, 0, 1, 552, 12, r, 16, 4)
+
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL GENMASK(8, 6)
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL_SET(x)\
+ FIELD_PREP(ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL, x)
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL_GET(x)\
+ FIELD_GET(ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL, x)
+
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL GENMASK(5, 3)
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL_SET(x)\
+ FIELD_PREP(ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL, x)
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL_GET(x)\
+ FIELD_GET(ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL, x)
+
+#define ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL GENMASK(2, 0)
+#define ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL_SET(x)\
+ FIELD_PREP(ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL, x)
+#define ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL_GET(x)\
+ FIELD_GET(ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL, 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_GRP_CFG */
+#define QS_XTR_GRP_CFG(r) __REG(TARGET_QS,\
+ 0, 1, 0, 0, 1, 36, 0, r, 2, 4)
+
+#define QS_XTR_GRP_CFG_MODE GENMASK(3, 2)
+#define QS_XTR_GRP_CFG_MODE_SET(x)\
+ FIELD_PREP(QS_XTR_GRP_CFG_MODE, x)
+#define QS_XTR_GRP_CFG_MODE_GET(x)\
+ FIELD_GET(QS_XTR_GRP_CFG_MODE, x)
+
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS BIT(1)
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS_SET(x)\
+ FIELD_PREP(QS_XTR_GRP_CFG_STATUS_WORD_POS, x)
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS_GET(x)\
+ FIELD_GET(QS_XTR_GRP_CFG_STATUS_WORD_POS, x)
+
+#define QS_XTR_GRP_CFG_BYTE_SWAP BIT(0)
+#define QS_XTR_GRP_CFG_BYTE_SWAP_SET(x)\
+ FIELD_PREP(QS_XTR_GRP_CFG_BYTE_SWAP, x)
+#define QS_XTR_GRP_CFG_BYTE_SWAP_GET(x)\
+ FIELD_GET(QS_XTR_GRP_CFG_BYTE_SWAP, 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 [flat|nested] 5+ messages in thread
* [PATCH net-next v16 4/4] net: dsa: lan9645x: add basic dsa driver for LAN9645X
2026-11-03 12:31 [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
` (2 preceding siblings ...)
2026-11-03 12:31 ` [PATCH net-next v16 3/4] net: dsa: lan9645x: add autogenerated register macros Jens Emil Schulz Østergaard
@ 2026-11-03 12:31 ` Jens Emil Schulz Østergaard
3 siblings, 0 replies; 5+ messages in thread
From: Jens Emil Schulz Østergaard @ 2026-11-03 12:31 UTC (permalink / raw)
To: UNGLinuxDriver, Andrew Lunn, Vladimir Oltean, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Woojung Huh, Russell King,
Steen Hegelund, Daniel Machon, Geert Uytterhoeven, Magnus Damm,
Eric Dumazet, Richard Cochran, Eric Dumazet
Cc: linux-kernel, netdev, devicetree, linux-renesas-soc,
Jens Emil Schulz Østergaard
Add the LAN9645X basic DSA driver with initialization, parent regmap
requests, port module initialization for NPI, CPU ports and front ports,
and phylink integration for MAC side configuration.
The ports operate as standalone ports. Bridges and VLAN uppers on the
ports are handled in software.
This patch only wires up the internal CuPHYs (ports 0-4) and the RGMII
paths for ports 7 and 8. The supported SerDes-based modes: SGMII,
QSGMII, 1000BASE-X and 2500BASE-X on ports 5-8 are deferred, and support
will be added in follow-up patches. Port 5-6 have no usable interface
mode in this patch, and should not be declared in DT until SerDes
support lands.
If port 5 or 6 is declared in DT as a user port, phylink_create()
rejects the empty supported_interfaces and dsa_tree_setup_ports() falls
back to setting the port up as unused, so the switch still probes.
DSA logs an error including the port number. Declared as a CPU port
there is no such fallback, and the probe fails, and our driver logs an
error including the port number.
The intended way to bind this driver is using a parent MFD driver,
responsible for the register IO protocol, and distributing regmaps to
child devices. The goal is to use the same approach as the MFD driver in
drivers/mfd/ocelot-spi.c, which also resets the chip before it
instantiates its children.
This driver expects to request named regmaps from a parent device. This
approach is similar to the DSA driver
drivers/net/dsa/ocelot/ocelot_ext.c
which supports being driven by an external CPU via SPI with parent
device drivers/mfd/ocelot-spi.c.
The MFD driver will come in a later series, because there are
requirements on the number of child devices before a driver qualifies as
a MFD device.
Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>
---
Changes in v16:
- Drop ds->assisted_learning_on_cpu_port, ds->fdb_isolation and
ds->max_num_bridges, the reserved VLAN IDs, the general purpose PGID
range, PGID_MRP and lan_bulk_rd() which the dropped patches used.
- Describe the standalone port operation in the commit message.
Changes in v15:
- Link down now asserts MAC_TX_RST and MAC_RX_RST instead of flushing
the queues and asserting PORT_RST, which can hang the port when the
MAC cannot transmit the frames still queued.
Changes in v13:
- Only redirect the CPU extraction queues to the NPI port while the
NPI port link is up. This avoids queuing CPU frames to the NPI port
while its MAC is held in reset.
- Configure CPU extraction group 0 for manual extraction, instead of
the default where the VRAP engine owns it, so QSYS_SW_STATUS.EQ_AVAIL
of the CPU port module can be polled when flushing it on NPI link up.
Changes in v12:
- Drop lan9645x_port_set_tail_drop_wm(). This tail dropping config
is only active under cut through forwarding, which we never enable.
- lan9645x_reset_switch(): skip the memory init when the switch core is
already enabled. We expect parent device to reset before probe, but if
it does not, we must skip memory init if switch core is already
enabled.
- Drop unused shared_queue_sz field and LAN9645X_BUFFER_MEMORY define.
- Set ds->max_num_bridges = 1 next to ds->fdb_isolation. We support a
single offloaded bridge, so let the core enforce it.
- Reject an NPI port with no usable interface mode.
- Kconfig: select REGMAP.
- Include the CPU port module in the unicast and multicast flood masks.
Host address filtering needs the fdb and mdb operations, and unknown
traffic needs port_set_host_flood(), so until bridge support lands the
host depends on flooding to receive anything.
- Describe in the commit what happens if port 5 or 6 is declared in DT.
- Drop unused register writes from lan9645x_port_cpu_init().
- Update PGID table comment.
- Drop the unused PGID_AGGR_NUM, LAN9645X_ISDX_MAX, LAN9645X_ESDX_MAX and
LAN9645X_SFID_MAX defines, along with PGID_ENTRIES.
- Follow the LAN9645X_IFH_LEN rename in the tag driver.
- Drop ds->dscp_prio_mapping_is_global. It is only consulted after
port_add_dscp_prio() or port_del_dscp_prio() succeeds, neither of which
this series implements.
- Program QSYS_RES_CFG through lan9645x_wm_enc().
- Return -ENOMEM directly, not via dev_err_probe(), and add the missing
newlines to the remaining probe messages.
- lan9645x_npi_port_deinit(): restore the NPI bit in the CPU port module
source PGID that lan9645x_npi_port_init() cleared.
- Spell the part LAN9645x in the Kconfig prompt, the help text and
MODULE_DESCRIPTION.
- Drop the unread NULL sentinel from lan9645x_resource_names[].
- Resolve the regmap and the register address once in
lan9645x_rd_poll_timeout() and lan9645x_rd_poll_slow().
- lan9645x_get_max_mtu(): drop the NPI case. DSA only asks for the user
port limit and adds the tag overhead back itself when it sizes the
conduit MTU.
- lan9645x_phylink_mac_prepare(): drop the port range check. Ports 5 and 6
get no phylink instance, and ds->num_ports bounds the rest.
- lan9645x_phylink_mac_link_up(): drop the unreachable SPEED_2500 case,
and the DEV_CLOCK_CFG.LINK_SPEED fixup it forced.
lan9645x_phylink_get_caps() does not advertise MAC_2500FD.
- Fix DEV_MAC_IFG_CFG.TX_IFG, which had its full and half duplex values
swapped.
- Drop the unused afi target regmap request.
- Name the first per port regmap dev0 rather than dev, so the nine read
uniformly.
- Bound the ANA_PGID_CFG.OBEY_VLAN loop at PGID_AGGR.
- lan9645x_phylink_mac_prepare(): drop the HSIO_HW_CFG.RGMII_0_CFG write.
The field resets to DEV7 and nothing else writes it.
- lan9645x_phylink_port_down(): restore QSYS_SW_PORT_MODE.AGING_MODE to 2
after the flush, which is the reset value, rather than 1. Mode 1 exempts
frames from the CPU interface from the transmit ageing that
lan9645x_setup() enables, and every port sat in it from probe.
Changes in v9:
- Drop the DEV4 RGMII muxing support.
Changes in v8:
- Remove the CPU_PORT macro, which named the chips internal CPU port
module and collided with the DSA CPU port terminology. Reference the
module as lan9645x->num_phys_ports.
- Rework the port map comment to distinguish the chip CPU port modules
(indices 9-10) from the DSA CPU port (the NPI front port).
- Derive PGID_GP_START from NUM_PHYS_PORTS instead of CPU_PORT.
- Add a port_mux_lock mutex to serialize port mux arbitration in
lan9645x_phylink_mac_prepare().
Changes in v7:
- use cpu queue LAN9645X_CPUQ_TRAP for bpdu frames
Changes in v5:
- remove dd_dis and tsn_dis, and add SKU supported port validation
during setup
- phylink: remove MAC_2500FD
- phylink: add comment about empty supported_interfaces for port 5-6.
- phylink: fix 2:1 rgmii port muxing for port module 4 and 7 to be fully
dynamic and validate requested mux settings.
- phylink: add comment about 2:1 rgmii port muxing for port module 4
and 7.
- rx/tx-internal-delay-ps checked against support 2ns value
- update commit message
- init lan9645x->npi = -1 at probe and check port < 0 in npi_deinit
Changes in v4:
- add comment to QSYS_RES_CFG configuration
- phylink_mac_prepare: fix to make sure we can dynamically change rgmii
on port 4
- move ports allocation to probe
- tag_npi_setup: reject cascaded setups
- skip WARN_ON in lan9645x_to_port
Changes in v3:
- move DEV_MAC_TAGS_CFG config to port setup, for vlan overhead in port
frame maxlen
- remove code disabling ipv6 on conduit
- use of_property_read_u32 for {rx,tx}-internal-delay-ps
- use dsa_user_ports(ds) instead of
GENMASK(lan9645x->num_phys_ports - 1, 0) as base flood mask.
- update obey vlan comment
Changes in v2:
- source Kconfig from drivers/net/dsa/Kconfig
- sorting in Kconfig and Makefiles
- remove unused struct fields
- remote path delays
- use port_setup and dp->dn instead of DTS parsing
- phylink: split rgmii setup into dll and speed config
- phylink: remove pcs/sgmii/qsgmii related code
- phylink: simplify mac_prepare
- phylink: remove phylink_ops wrappers
- phylink: remove unrelated config from link up
- phylink: reorder functions according to phylink call order
---
drivers/net/dsa/Kconfig | 2 +
drivers/net/dsa/microchip/Makefile | 1 +
drivers/net/dsa/microchip/lan9645x/Kconfig | 12 +
drivers/net/dsa/microchip/lan9645x/Makefile | 8 +
drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 453 +++++++++++++++++++++
drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 321 +++++++++++++++
drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c | 116 ++++++
.../net/dsa/microchip/lan9645x/lan9645x_phylink.c | 316 ++++++++++++++
drivers/net/dsa/microchip/lan9645x/lan9645x_port.c | 166 ++++++++
9 files changed, 1395 insertions(+)
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index fe8cd5338fda..d62f0cccf13d 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -82,6 +82,8 @@ config NET_DSA_MV88E6060
source "drivers/net/dsa/microchip/Kconfig"
+source "drivers/net/dsa/microchip/lan9645x/Kconfig"
+
source "drivers/net/dsa/motorcomm/Kconfig"
source "drivers/net/dsa/mv88e6xxx/Kconfig"
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 9347cfb3d0b5..e75f17888f75 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -12,3 +12,4 @@ endif
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_LAN9645X) += lan9645x/
diff --git a/drivers/net/dsa/microchip/lan9645x/Kconfig b/drivers/net/dsa/microchip/lan9645x/Kconfig
new file mode 100644
index 000000000000..4e3e0d2f5846
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_MICROCHIP_LAN9645X
+ tristate "Microchip LAN9645x switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_LAN9645X
+ select REGMAP
+ help
+ This driver adds DSA support for Microchip LAN9645x switch chips.
+ The lan9645x switch is a multi-port Gigabit AVB/TSN Ethernet Switch
+ with five integrated 10/100/1000Base-T PHYs. In addition to the
+ integrated PHYs, it supports up to 2 RGMII/RMII, up to 2
+ BASE-X/SERDES/2.5GBASE-X and one Quad-SGMII/Quad-USGMII interfaces.
diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile
new file mode 100644
index 000000000000..7cc0ae0ada40
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_NET_DSA_MICROCHIP_LAN9645X) += mchp-lan9645x.o
+
+mchp-lan9645x-objs := \
+ lan9645x_main.o \
+ lan9645x_npi.o \
+ lan9645x_phylink.o \
+ lan9645x_port.o \
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
new file mode 100644
index 000000000000..f91e82534335
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/platform_device.h>
+
+#include "lan9645x_main.h"
+
+static const char *lan9645x_resource_names[NUM_TARGETS] = {
+ [TARGET_GCB] = "gcb",
+ [TARGET_QS] = "qs",
+ [TARGET_CHIP_TOP] = "chip_top",
+ [TARGET_REW] = "rew",
+ [TARGET_SYS] = "sys",
+ [TARGET_HSIO] = "hsio",
+ [TARGET_DEV + 0] = "dev0",
+ [TARGET_DEV + 1] = "dev1",
+ [TARGET_DEV + 2] = "dev2",
+ [TARGET_DEV + 3] = "dev3",
+ [TARGET_DEV + 4] = "dev4",
+ [TARGET_DEV + 5] = "dev5",
+ [TARGET_DEV + 6] = "dev6",
+ [TARGET_DEV + 7] = "dev7",
+ [TARGET_DEV + 8] = "dev8",
+ [TARGET_QSYS] = "qsys",
+ [TARGET_ANA] = "ana",
+};
+
+static int lan9645x_tag_npi_setup(struct dsa_switch *ds)
+{
+ struct dsa_port *dp, *first_cpu_dp = NULL;
+ struct lan9645x *lan9645x = ds->priv;
+ struct phylink_config c = {};
+
+ dsa_switch_for_each_user_port(dp, ds) {
+ if (dp->cpu_dp->ds != ds) {
+ dev_err(ds->dev,
+ "NPI port on a remote switch is not supported\n");
+ return -EINVAL;
+ }
+
+ if (first_cpu_dp && dp->cpu_dp != first_cpu_dp) {
+ dev_err(ds->dev, "Multiple NPI ports not supported\n");
+ return -EINVAL;
+ }
+
+ first_cpu_dp = dp->cpu_dp;
+ }
+
+ if (!first_cpu_dp)
+ return -EINVAL;
+
+ lan9645x_phylink_get_caps(lan9645x, first_cpu_dp->index, &c);
+ if (phy_interface_empty(c.supported_interfaces)) {
+ dev_err(ds->dev,
+ "Port %d has no usable interface mode, cannot be NPI port\n",
+ first_cpu_dp->index);
+ return -EINVAL;
+ }
+
+ lan9645x_npi_port_init(lan9645x, first_cpu_dp);
+
+ return 0;
+}
+
+static enum dsa_tag_protocol lan9645x_get_tag_protocol(struct dsa_switch *ds,
+ int port,
+ enum dsa_tag_protocol tp)
+{
+ return DSA_TAG_PROTO_LAN9645X;
+}
+
+static void lan9645x_teardown(struct dsa_switch *ds)
+{
+ struct lan9645x *lan9645x = ds->priv;
+
+ lan9645x_npi_port_deinit(lan9645x, lan9645x->npi);
+}
+
+static int lan9645x_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+ return lan9645x_port_set_maxlen(ds->priv, port, new_mtu);
+}
+
+static int lan9645x_get_max_mtu(struct dsa_switch *ds, int port)
+{
+ /* Actual MAC max MTU is around 16KB. We set 10000 - overhead which
+ * should be sufficient for all jumbo frames. Larger frames can cause
+ * problems especially with flow control, since we only have 160K queue
+ * buffer.
+ *
+ * DSA only asks for the user port limit, and adds the tag overhead
+ * back when it sizes the conduit MTU, so the CPU port is not a case
+ * here.
+ */
+ return 10000 - ETH_HLEN - ETH_FCS_LEN;
+}
+
+static int lan9645x_reset_switch(struct lan9645x *lan9645x)
+{
+ u32 val = 0;
+ int err;
+
+ /* The parent must reset the whole chip before instantiating its
+ * children, so the switch core is disabled when we get here. The
+ * sequence below must not run more than once: initializing the
+ * memories on a running core corrupts pointers inside the queue
+ * system, which eventually fills up and stops passing traffic. If the
+ * core is already enabled the chip was not reset, so skip it.
+ */
+ if (SYS_RESET_CFG_CORE_ENA_GET(lan_rd(lan9645x, SYS_RESET_CFG)))
+ return 0;
+
+ lan_wr(SYS_RESET_CFG_CORE_ENA_SET(0), lan9645x, SYS_RESET_CFG);
+ lan_wr(SYS_RAM_INIT_RAM_INIT_SET(1), lan9645x, SYS_RAM_INIT);
+ err = lan9645x_rd_poll_timeout(lan9645x, SYS_RAM_INIT, val,
+ SYS_RAM_INIT_RAM_INIT_GET(val) == 0);
+ if (err) {
+ dev_err(lan9645x->dev, "Failed to init chip RAM.\n");
+ return err;
+ }
+ lan_wr(SYS_RESET_CFG_CORE_ENA_SET(1), lan9645x, SYS_RESET_CFG);
+
+ return 0;
+}
+
+static int lan9645x_setup(struct dsa_switch *ds)
+{
+ struct lan9645x *lan9645x = ds->priv;
+ struct device *dev = lan9645x->dev;
+ u32 front_ports, res_wm;
+ int supported, used = 0;
+ struct dsa_port *dp;
+ int err;
+
+ lan9645x->num_phys_ports = ds->num_ports;
+ front_ports = dsa_user_ports(ds);
+ supported = lan9645x->num_phys_ports - lan9645x->num_port_dis;
+
+ dsa_switch_for_each_available_port(dp, ds)
+ used++;
+
+ if (used > supported) {
+ dev_err(ds->dev,
+ "%d physical ports in use in DT, SKU supports at most %d\n",
+ used, supported);
+ return -EINVAL;
+ }
+
+ err = lan9645x_reset_switch(lan9645x);
+ if (err)
+ return err;
+
+ err = lan9645x_tag_npi_setup(ds);
+ if (err) {
+ dev_err(dev, "Failed to setup NPI port.\n");
+ return err;
+ }
+
+ /* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */
+ lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA |
+ ANA_AGGR_CFG_AC_DMAC_ENA,
+ lan9645x, ANA_AGGR_CFG);
+
+ /* Flush queues */
+ lan_wr(GENMASK(1, 0), lan9645x, QS_XTR_FLUSH);
+
+ /* Allow to drain */
+ usleep_range(1000, 2000);
+
+ /* All Queues normal */
+ lan_wr(0x0, lan9645x, QS_XTR_FLUSH);
+
+ /* Set MAC age time to default value, the entry is aged after
+ * 2 * AGE_PERIOD
+ */
+ lan_wr(ANA_AUTOAGE_AGE_PERIOD_SET(BR_DEFAULT_AGEING_TIME / 2 / HZ),
+ lan9645x, ANA_AUTOAGE);
+
+ /* Disable learning for frames discarded by VLAN ingress filtering */
+ lan_rmw(ANA_ADVLEARN_VLAN_CHK_SET(1),
+ ANA_ADVLEARN_VLAN_CHK,
+ lan9645x, ANA_ADVLEARN);
+
+ /* Queue system frame ageing. We target 2s ageing.
+ *
+ * Register unit is 1024 cycles.
+ *
+ * ASIC: 165.625 Mhz ~ 6.0377 ns period
+ *
+ * 1024 * 6.0377 ns =~ 6182 ns
+ * val = 2000000000ns / 6182ns
+ */
+ lan_wr(SYS_FRM_AGING_AGE_TX_ENA_SET(1) |
+ SYS_FRM_AGING_MAX_AGE_SET((2000000000 / 6182)),
+ lan9645x, SYS_FRM_AGING);
+
+ /* Setup flooding PGIDs for IPv4/IPv6 multicast. Control and dataplane
+ * use the same masks. Control frames are redirected to CPU, and
+ * the network stack is responsible for forwarding these.
+ * The dataplane is forwarding according to the offloaded MDB entries.
+ */
+ lan_wr(ANA_FLOODING_IPMC_FLD_MC4_DATA_SET(PGID_MCIPV4) |
+ ANA_FLOODING_IPMC_FLD_MC4_CTRL_SET(PGID_MC) |
+ ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(PGID_MCIPV6) |
+ ANA_FLOODING_IPMC_FLD_MC6_CTRL_SET(PGID_MC),
+ lan9645x, ANA_FLOODING_IPMC);
+
+ for (int prio = 0; prio < NUM_PRIO_QUEUES; ++prio)
+ lan_wr(ANA_FLOODING_FLD_MULTICAST_SET(PGID_MC) |
+ ANA_FLOODING_FLD_UNICAST_SET(PGID_UC) |
+ ANA_FLOODING_FLD_BROADCAST_SET(PGID_BC),
+ lan9645x, ANA_FLOODING(prio));
+
+ /* Allow VLAN table to control whether cpu copy from the pgid table is
+ * enabled. Only the destination analysis resolves a CPU copy, so the
+ * aggregation and source regions are left at their reset value.
+ */
+ for (int i = 0; i < PGID_AGGR; ++i)
+ lan_wr(ANA_PGID_CFG_OBEY_VLAN_SET(1),
+ lan9645x, ANA_PGID_CFG(i));
+
+ /* Disable bridging by default */
+ for (int p = 0; p < lan9645x->num_phys_ports; p++) {
+ lan_wr(0, lan9645x, ANA_PGID(PGID_SRC + p));
+
+ /* Do not forward BPDU frames to the front ports and trap them
+ * to CPU
+ */
+ lan_wr(ANA_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA,
+ lan9645x, ANA_CPU_FWD_BPDU_CFG(p));
+ }
+
+ for (int i = 0; i < 16; i++) {
+ /* The register instance number corresponds to the
+ * address of the extracted frame. For instance:
+ * CPUQ_8021_CFG[4].CPUQ_BPDU_VAL
+ * is the cpu extraction queue used for BPDU frames
+ * with address 01-80-C2-00-00-04
+ */
+ lan_rmw(ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL_SET(LAN9645X_CPUQ_TRAP),
+ ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL,
+ lan9645x, ANA_CPUQ_8021_CFG(i));
+ }
+
+ /* Reserve 1664 bytes, 26 buffer cells, per (port, prio) for source
+ * tracking (resource 0, indices 0..95) and destination tracking
+ * (resource 2, indices 512..607). These are access watermarks, not
+ * pre-allocations: a flow draws from its reservation first, then
+ * from the shared pool. Keeping the reservation above a max-size
+ * Ethernet frame prevents a single frame from spilling into the
+ * shared pool, and cause pause frames to be emitted without actual
+ * congestion.
+ */
+ res_wm = QSYS_RES_CFG_WM_HIGH_SET(lan9645x_wm_enc(QS_SRC_BUF_RSV));
+ for (int i = 0; i <= QSYS_Q_RSRV; ++i) {
+ lan_wr(res_wm, lan9645x, QSYS_RES_CFG(i));
+ lan_wr(res_wm, lan9645x, QSYS_RES_CFG(512 + i));
+ }
+
+ lan9645x_port_cpu_init(lan9645x);
+
+ /* Multicast to cpu and all front ports */
+ lan_wr(BIT(lan9645x->num_phys_ports) | front_ports, lan9645x,
+ ANA_PGID(PGID_MC));
+
+ /* IP multicast to cpu and all front ports */
+ lan_wr(BIT(lan9645x->num_phys_ports) | front_ports, lan9645x,
+ ANA_PGID(PGID_MCIPV4));
+ lan_wr(BIT(lan9645x->num_phys_ports) | front_ports, lan9645x,
+ ANA_PGID(PGID_MCIPV6));
+
+ /* Unicast to cpu and all front ports */
+ lan_wr(BIT(lan9645x->num_phys_ports) | front_ports, lan9645x,
+ ANA_PGID(PGID_UC));
+
+ /* Broadcast to cpu and all front ports */
+ lan_wr(BIT(lan9645x->num_phys_ports) | front_ports, lan9645x,
+ ANA_PGID(PGID_BC));
+
+ ds->mtu_enforcement_ingress = true;
+
+ dev_info(lan9645x->dev,
+ "SKU features: max_ports=%d\n",
+ lan9645x->num_phys_ports - lan9645x->num_port_dis);
+
+ return 0;
+}
+
+static void lan9645x_port_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ lan9645x_phylink_get_caps(ds->priv, port, config);
+}
+
+static const struct dsa_switch_ops lan9645x_switch_ops = {
+ .get_tag_protocol = lan9645x_get_tag_protocol,
+
+ .setup = lan9645x_setup,
+ .teardown = lan9645x_teardown,
+ .port_setup = lan9645x_port_setup,
+
+ /* Phylink integration */
+ .phylink_get_caps = lan9645x_port_phylink_get_caps,
+
+ /* MTU */
+ .port_change_mtu = lan9645x_change_mtu,
+ .port_max_mtu = lan9645x_get_max_mtu,
+};
+
+static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x)
+{
+ const char *resource_name;
+ struct regmap *tgt_map;
+
+ for (int i = 0; i < NUM_TARGETS; i++) {
+ resource_name = lan9645x_resource_names[i];
+ if (!resource_name)
+ continue;
+
+ tgt_map = dev_get_regmap(lan9645x->dev->parent, resource_name);
+ if (IS_ERR_OR_NULL(tgt_map)) {
+ dev_err(lan9645x->dev, "Failed to get regmap=%s\n",
+ resource_name);
+ return -ENODEV;
+ }
+
+ lan9645x->rmap[i] = tgt_map;
+ }
+
+ return 0;
+}
+
+static void lan9645x_set_feat_dis(struct lan9645x *lan9645x)
+{
+ u32 feat_dis;
+
+ /* The features which can be physically disabled on some SKUs are:
+ * 1) Number of ports can be 5, 7 or 9. Any ports can be used, the chip
+ * tracks how many are active.
+ * 2) HSR/PRP. The duplicate discard table can be disabled.
+ * 3) TAS, frame preemption and PSFP can be disabled.
+ */
+ feat_dis = lan_rd(lan9645x, GCB_FEAT_DISABLE);
+
+ lan9645x->num_port_dis =
+ GCB_FEAT_DISABLE_FEAT_NUM_PORTS_DIS_GET(feat_dis);
+}
+
+static int lan9645x_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lan9645x *lan9645x;
+ struct dsa_switch *ds;
+ int err = 0;
+
+ lan9645x = devm_kzalloc(dev, sizeof(*lan9645x), GFP_KERNEL);
+ if (!lan9645x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, lan9645x);
+ lan9645x->dev = dev;
+
+ err = lan9645x_request_target_regmaps(lan9645x);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to request regmaps\n");
+
+ ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+ if (!ds)
+ return -ENOMEM;
+
+ lan9645x->ports = devm_kcalloc(lan9645x->dev, NUM_PHYS_PORTS,
+ sizeof(struct lan9645x_port *),
+ GFP_KERNEL);
+ if (!lan9645x->ports)
+ return -ENOMEM;
+
+ for (int port = 0; port < NUM_PHYS_PORTS; port++) {
+ struct lan9645x_port *p;
+
+ p = devm_kzalloc(lan9645x->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->lan9645x = lan9645x;
+ p->chip_port = port;
+ lan9645x->ports[port] = p;
+ }
+
+ ds->dev = dev;
+ ds->num_ports = NUM_PHYS_PORTS;
+ ds->num_tx_queues = NUM_PRIO_QUEUES;
+
+ ds->ops = &lan9645x_switch_ops;
+ ds->phylink_mac_ops = &lan9645x_phylink_mac_ops;
+ ds->priv = lan9645x;
+
+ lan9645x->ds = ds;
+ lan9645x->npi = -1;
+
+ lan9645x_set_feat_dis(lan9645x);
+
+ err = dsa_register_switch(ds);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register DSA switch\n");
+
+ return 0;
+}
+
+static void lan9645x_remove(struct platform_device *pdev)
+{
+ struct lan9645x *lan9645x = dev_get_drvdata(&pdev->dev);
+
+ if (!lan9645x)
+ return;
+
+ /* Calls lan9645x DSA .teardown */
+ dsa_unregister_switch(lan9645x->ds);
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static void lan9645x_shutdown(struct platform_device *pdev)
+{
+ struct lan9645x *lan9645x = dev_get_drvdata(&pdev->dev);
+
+ if (!lan9645x)
+ return;
+
+ dsa_switch_shutdown(lan9645x->ds);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct of_device_id lan9645x_switch_of_match[] = {
+ { .compatible = "microchip,lan96455s-switch" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lan9645x_switch_of_match);
+
+static struct platform_driver lan9645x_switch_driver = {
+ .driver = {
+ .name = "lan96455s-switch",
+ .of_match_table = lan9645x_switch_of_match,
+ },
+ .probe = lan9645x_probe,
+ .remove = lan9645x_remove,
+ .shutdown = lan9645x_shutdown,
+};
+module_platform_driver(lan9645x_switch_driver);
+
+MODULE_DESCRIPTION("LAN9645x Switch Driver");
+MODULE_AUTHOR("Jens Emil Schulz Østergaard <jensemil.schulzostergaard@microchip.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
new file mode 100644
index 000000000000..60acb5aec296
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#ifndef __LAN9645X_MAIN_H__
+#define __LAN9645X_MAIN_H__
+
+#include <linux/dsa/lan9645x.h>
+#include <linux/if_bridge.h>
+#include <linux/regmap.h>
+#include <net/dsa.h>
+
+#include "lan9645x_regs.h"
+
+/* Port modules 0-8 are front (user) ports. The chip additionally has two
+ * logical CPU port modules at indices 9 and 10. These are not the DSA CPU port.
+ * The CPU port modules are logical ports in the chip intended for management.
+ *
+ * The frame delivery mechanism can vary: direct register injection/extraction,
+ * or a front port can be used as the management port, called a Node Processor
+ * Interface (NPI) in the datasheet.
+ *
+ * LAN9645X uses the NPI approach, so the DSA CPU port is a front port
+ * (see lan9645x->npi) configured as NPI port.
+ *
+ * Therefore the CPU datapath has two port module indices of interest,
+ * lan9645x->npi and the cpu port module at index 9.
+ */
+#define NUM_PHYS_PORTS 9
+
+#define NUM_PRIO_QUEUES 8
+
+#define QS_SRC_BUF_RSV 1664
+
+/* Reserved amount for (SRC, PRIO) at index 8*SRC + PRIO
+ * See QSYS:RES_CTRL[*]:RES_CFG description
+ */
+#define QSYS_Q_RSRV 95
+
+/* Port Group Identifiers (PGID) are port-masks applied to all frames.
+ *
+ * The forwarding engine outputs two masks for the forwarding decision, DEST and
+ * CPUQ. DEST determines which front ports will receive a frame, and CPUQ
+ * whether the frame is forwarded to the CPU.
+ *
+ * The replicated registers are organized like so in HW:
+ *
+ * 0-63: Destination analysis.
+ * 64-79: Aggregation analysis
+ * 80-(80+10-1): Source port analysis
+ *
+ * Destination: Destination PGIDs are the result of mac table lookups or
+ * flooding decisions. The resolved destination PGID will feed into DEST and set
+ * the bit for ANA_PGID_CFG.CPUQ_DST_PGID in CPUQ, if the CPU port module bit is
+ * set in the PGID. This is how PGID_CPU and host flooding work.
+ *
+ * The first NUM_PHYS_PORTS destination PGIDs can not be used freely, because
+ * dynamic learning writes mac table entries with DEST_IDX taken from the
+ * ingress ports ANA_PORT_CFG.PORTID_VAL. They come out of reset as BIT(i)
+ * for that reason.
+ *
+ * PGID NUM_PHYS_PORTS..63 come out of reset as zero and have no reserved
+ * role. We use them for L2 Multicast and flooding (see FLOODING and
+ * FLOODING_IPMC) and reserve a few at the end of the range. The CPU port module
+ * has no reserved destination PGID. It does have a reserved source PGID at
+ * PGID_SRC + NUM_PHYS_PORTS.
+ *
+ * Aggregation: Each frame receives a link aggregation code from the analyzer,
+ * based on a configurable algorithm. This code picks out one of the 16
+ * aggregation PGIDs to mask out ports in DEST. If no aggregation is configured,
+ * these are all-ones. The CPU port module bit is inert here.
+ *
+ * Source: Control which ports a given source port can forward to. A frame that
+ * is received on port n, uses entry PGID_SRC + n to mask out DEST.
+ * The default values are all bits set except for the index itself
+ * (no loopback). The CPU port module bit is inert here.
+ */
+
+#define PGID_AGGR 64
+#define PGID_SRC 80
+
+/* Reserved PGIDs. */
+#define PGID_CPU (PGID_AGGR - 6)
+#define PGID_UC (PGID_AGGR - 5)
+#define PGID_BC (PGID_AGGR - 4)
+#define PGID_MC (PGID_AGGR - 3)
+#define PGID_MCIPV4 (PGID_AGGR - 2)
+#define PGID_MCIPV6 (PGID_AGGR - 1)
+
+/* Flooding PGIDS:
+ * PGID_UC
+ * PGID_MC*
+ * PGID_BC
+ */
+
+#define GWM_MULTIPLIER_BIT BIT(8)
+#define LAN9645X_BUFFER_CELL_SZ 64
+
+#define RD_SLEEP_US 3
+#define RD_SLEEPTIMEOUT_US 100000
+#define SLOW_RD_SLEEP_US 1000
+#define SLOW_RD_SLEEPTIMEOUT_US 4000000
+
+/* regmap_read_poll_timeout() re-evaluates its map and address arguments on
+ * every iteration, so resolve both once up front.
+ */
+#define lan9645x_rd_poll_timeout(_lan9645x, _reg_macro, _val, _cond) \
+({ \
+ struct regmap *__map = lan_rmap((_lan9645x), _reg_macro); \
+ u32 __addr = lan_rel_addr(_reg_macro); \
+ \
+ regmap_read_poll_timeout(__map, __addr, (_val), (_cond), \
+ RD_SLEEP_US, RD_SLEEPTIMEOUT_US); \
+})
+
+#define lan9645x_rd_poll_slow(_lan9645x, _reg_macro, _val, _cond) \
+({ \
+ struct regmap *__map = lan_rmap((_lan9645x), _reg_macro); \
+ u32 __addr = lan_rel_addr(_reg_macro); \
+ \
+ regmap_read_poll_timeout(__map, __addr, (_val), (_cond), \
+ SLOW_RD_SLEEP_US, \
+ SLOW_RD_SLEEPTIMEOUT_US); \
+})
+
+/* NPI port prefix config encoding
+ *
+ * 0: No CPU extraction header (normal frames)
+ * 1: CPU extraction header without prefix
+ * 2: CPU extraction header with short prefix
+ * 3: CPU extraction header with long prefix
+ */
+enum lan9645x_tag_prefix {
+ LAN9645X_TAG_PREFIX_DISABLED = 0,
+ LAN9645X_TAG_PREFIX_NONE = 1,
+ LAN9645X_TAG_PREFIX_SHORT = 2,
+ LAN9645X_TAG_PREFIX_LONG = 3,
+};
+
+enum {
+ LAN9645X_SPEED_DISABLED = 0,
+ LAN9645X_SPEED_10 = 1,
+ LAN9645X_SPEED_100 = 2,
+ LAN9645X_SPEED_1000 = 3,
+ LAN9645X_SPEED_2500 = 4,
+};
+
+/* Rewriter VLAN port tagging encoding for REW:PORT[0-10]:TAG_CFG.TAG_CFG
+ *
+ * 0: Port tagging disabled.
+ * 1: Tag all frames, except when VID=PORT_VLAN_CFG.PORT_VID or VID=0.
+ * 2: Tag all frames, except when VID=0.
+ * 3: Tag all frames.
+ */
+enum lan9645x_vlan_port_tag {
+ LAN9645X_TAG_DISABLED = 0,
+ LAN9645X_TAG_NO_PVID_NO_UNAWARE = 1,
+ LAN9645X_TAG_NO_UNAWARE = 2,
+ LAN9645X_TAG_ALL = 3,
+};
+
+struct lan9645x {
+ struct device *dev;
+ struct dsa_switch *ds;
+ struct regmap *rmap[NUM_TARGETS];
+
+ /* NPI chip_port */
+ int npi;
+
+ u8 num_phys_ports;
+ struct lan9645x_port **ports;
+
+ int num_port_dis;
+};
+
+struct lan9645x_port {
+ struct lan9645x *lan9645x;
+
+ u8 chip_port;
+
+ bool rx_internal_delay;
+ bool tx_internal_delay;
+};
+
+extern const struct phylink_mac_ops lan9645x_phylink_mac_ops;
+
+/* PFC_CFG.FC_LINK_SPEED encoding */
+static inline int lan9645x_speed_fc_enc(int speed)
+{
+ switch (speed) {
+ case LAN9645X_SPEED_10:
+ return 3;
+ case LAN9645X_SPEED_100:
+ return 2;
+ case LAN9645X_SPEED_1000:
+ return 1;
+ case LAN9645X_SPEED_2500:
+ return 0;
+ default:
+ WARN_ON_ONCE(1);
+ return 1;
+ }
+}
+
+/* Watermark encode. See QSYS:RES_CTRL[*]:RES_CFG.WM_HIGH for details.
+ * Returns lowest encoded number which will fit request/ is larger than request.
+ * Or the maximum representable value, if request is too large.
+ */
+static inline u32 lan9645x_wm_enc(u32 value)
+{
+ value = DIV_ROUND_UP(value, LAN9645X_BUFFER_CELL_SZ);
+
+ if (value >= GWM_MULTIPLIER_BIT) {
+ value = DIV_ROUND_UP(value, 16);
+ if (value >= GWM_MULTIPLIER_BIT)
+ value = (GWM_MULTIPLIER_BIT - 1);
+ value |= GWM_MULTIPLIER_BIT;
+ }
+
+ return value;
+}
+
+static inline struct lan9645x_port *lan9645x_to_port(struct lan9645x *lan9645x,
+ int port)
+{
+ return lan9645x->ports[port];
+}
+
+static inline struct regmap *lan_tgt2rmap(struct lan9645x *lan9645x,
+ enum lan9645x_target t, int tinst)
+{
+ return lan9645x->rmap[t + tinst];
+}
+
+static inline u32 __lan_rel_addr(int gbase, int ginst, int gcnt,
+ int gwidth, int raddr, int rinst,
+ int rcnt, int rwidth)
+{
+ WARN_ON(ginst >= gcnt);
+ WARN_ON(rinst >= rcnt);
+ return gbase + ginst * gwidth + raddr + rinst * rwidth;
+}
+
+/* Get register address relative to target instance */
+static inline u32 lan_rel_addr(enum lan9645x_target t, int tinst, int tcnt,
+ int gbase, int ginst, int gcnt, int gwidth,
+ int raddr, int rinst, int rcnt, int rwidth)
+{
+ WARN_ON(tinst >= tcnt);
+ return __lan_rel_addr(gbase, ginst, gcnt, gwidth, raddr, rinst,
+ rcnt, rwidth);
+}
+
+static inline u32 lan_rd(struct lan9645x *lan9645x, enum lan9645x_target t,
+ int tinst, int tcnt, int gbase, int ginst,
+ int gcnt, int gwidth, int raddr, int rinst,
+ int rcnt, int rwidth)
+{
+ u32 addr, val = 0;
+
+ addr = lan_rel_addr(t, tinst, tcnt, gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+
+ WARN_ON_ONCE(regmap_read(lan_tgt2rmap(lan9645x, t, tinst), addr, &val));
+
+ return val;
+}
+
+static inline struct regmap *lan_rmap(struct lan9645x *lan9645x,
+ enum lan9645x_target t, int tinst,
+ int tcnt, int gbase, int ginst,
+ int gcnt, int gwidth, int raddr,
+ int rinst, int rcnt, int rwidth)
+{
+ return lan_tgt2rmap(lan9645x, t, tinst);
+}
+
+static inline void lan_wr(u32 val, struct lan9645x *lan9645x,
+ enum lan9645x_target t, int tinst, int tcnt,
+ int gbase, int ginst, int gcnt, int gwidth,
+ int raddr, int rinst, int rcnt, int rwidth)
+{
+ u32 addr;
+
+ addr = lan_rel_addr(t, tinst, tcnt, gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+
+ WARN_ON_ONCE(regmap_write(lan_tgt2rmap(lan9645x, t, tinst), addr, val));
+}
+
+static inline void lan_rmw(u32 val, u32 mask, struct lan9645x *lan9645x,
+ enum lan9645x_target t, int tinst, int tcnt,
+ int gbase, int ginst, int gcnt, int gwidth,
+ int raddr, int rinst, int rcnt, int rwidth)
+{
+ u32 addr;
+
+ addr = lan_rel_addr(t, tinst, tcnt, gbase, ginst, gcnt, gwidth,
+ raddr, rinst, rcnt, rwidth);
+
+ WARN_ON_ONCE(regmap_update_bits(lan_tgt2rmap(lan9645x, t, tinst),
+ addr, mask, val));
+}
+
+/* lan9645x_npi.c */
+void lan9645x_npi_port_init(struct lan9645x *lan9645x,
+ struct dsa_port *cpu_port);
+void lan9645x_npi_port_deinit(struct lan9645x *lan9645x, int port);
+void lan9645x_npi_cpuq_redirect(struct lan9645x *lan9645x, bool enable);
+
+/* lan9645x_port.c */
+int lan9645x_port_setup(struct dsa_switch *ds, int port);
+int lan9645x_port_set_maxlen(struct lan9645x *lan9645x, int port, size_t sdu);
+void lan9645x_port_cpu_init(struct lan9645x *lan9645x);
+
+/* lan9645x_phylink.c */
+void lan9645x_phylink_get_caps(struct lan9645x *lan9645x, int port,
+ struct phylink_config *c);
+void lan9645x_phylink_port_down(struct lan9645x *lan9645x, int port);
+
+#endif /* __LAN9645X_MAIN_H__ */
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c
new file mode 100644
index 000000000000..945fc026b3ca
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include "lan9645x_main.h"
+
+void lan9645x_npi_port_init(struct lan9645x *lan9645x,
+ struct dsa_port *cpu_port)
+{
+ int port = cpu_port->index;
+ struct lan9645x_port *p;
+
+ p = lan9645x_to_port(lan9645x, port);
+ lan9645x->npi = port;
+
+ dev_dbg(lan9645x->dev, "NPI port=%d\n", port);
+
+ /* Never send injected frames back to the CPU. CPU queues are redirected
+ * to the NPI port on link up, see lan9645x_npi_cpuq_redirect().
+ */
+ lan_wr(QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_SET(0) |
+ QSYS_EXT_CPU_CFG_EXT_CPU_PORT_SET(p->chip_port) |
+ QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA_SET(1) |
+ QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA_SET(1),
+ lan9645x, QSYS_EXT_CPU_CFG);
+
+ /* Configure IFH prefix mode for NPI port. We can not use an injection
+ * prefix, because it requires all frames sent on the port to contain
+ * the prefix. Frames without the prefix would get stuck in the queue
+ * system rendering the port unusable. Since we do not control
+ * what is sent to the NPI port, no prefix is our only option.
+ */
+ lan_rmw(SYS_PORT_MODE_INCL_XTR_HDR_SET(LAN9645X_TAG_PREFIX_LONG) |
+ SYS_PORT_MODE_INCL_INJ_HDR_SET(LAN9645X_TAG_PREFIX_NONE),
+ SYS_PORT_MODE_INCL_XTR_HDR |
+ SYS_PORT_MODE_INCL_INJ_HDR,
+ lan9645x,
+ SYS_PORT_MODE(p->chip_port));
+
+ /* Rewriting and extraction with IFH does not play nice together. A VLAN
+ * tag pushed into the frame by REW will cause 4 bytes at the end of the
+ * extraction header to be overwritten with the top 4 bytes of the DMAC.
+ *
+ * We can not use REW_PORT_CFG_NO_REWRITE=1 as that disabled RTAGD
+ * setting in the IFH
+ */
+ lan_rmw(REW_TAG_CFG_TAG_CFG_SET(LAN9645X_TAG_DISABLED),
+ REW_TAG_CFG_TAG_CFG, lan9645x, REW_TAG_CFG(p->chip_port));
+
+ /* Clear rewriter port vid */
+ lan_wr(0, lan9645x, REW_PORT_VLAN_CFG(p->chip_port));
+
+ /* Make sure frames with src_port=<CPU port module> are not reflected
+ * back via the NPI port. This could happen if a frame is flooded for
+ * instance. The *_CPU_KILL_ENA flags above only have an effect when a
+ * frame is output due to a CPU forwarding decision such as trapping or
+ * cpu copy.
+ */
+ lan_rmw(0, BIT(port), lan9645x,
+ ANA_PGID(PGID_SRC + lan9645x->num_phys_ports));
+}
+
+/* Toggle CPU queue redirect to the NPI port, flushing the CPU port module on
+ * enable
+ */
+void lan9645x_npi_cpuq_redirect(struct lan9645x *lan9645x, bool enable)
+{
+ int cpu_port = lan9645x->num_phys_ports;
+ u32 val;
+ int err;
+
+ lan_rmw(QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_SET(enable ? GENMASK(7, 0) : 0),
+ QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK,
+ lan9645x, QSYS_EXT_CPU_CFG);
+
+ if (!enable)
+ return;
+
+ lan_wr(QS_XTR_FLUSH_FLUSH_SET(BIT(0)), lan9645x, QS_XTR_FLUSH);
+
+ err = lan9645x_rd_poll_slow(lan9645x, QSYS_SW_STATUS(cpu_port), val,
+ !QSYS_SW_STATUS_EQ_AVAIL_GET(val));
+ if (err)
+ dev_err(lan9645x->dev, "CPU port %d flush timeout\n", cpu_port);
+
+ lan_wr(QS_XTR_FLUSH_FLUSH_SET(0), lan9645x, QS_XTR_FLUSH);
+}
+
+void lan9645x_npi_port_deinit(struct lan9645x *lan9645x, int port)
+{
+ u32 no_cpu_port = FIELD_MAX(QSYS_EXT_CPU_CFG_EXT_CPU_PORT);
+ struct lan9645x_port *p;
+
+ if (port < 0)
+ return;
+
+ lan9645x->npi = -1;
+ p = lan9645x_to_port(lan9645x, port);
+
+ lan_wr(QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_SET(0) |
+ QSYS_EXT_CPU_CFG_EXT_CPU_PORT_SET(no_cpu_port) |
+ QSYS_EXT_CPU_CFG_EXT_CPU_KILL_ENA_SET(1) |
+ QSYS_EXT_CPU_CFG_INT_CPU_KILL_ENA_SET(1),
+ lan9645x, QSYS_EXT_CPU_CFG);
+
+ lan_rmw(SYS_PORT_MODE_INCL_XTR_HDR_SET(LAN9645X_TAG_PREFIX_DISABLED) |
+ SYS_PORT_MODE_INCL_INJ_HDR_SET(LAN9645X_TAG_PREFIX_DISABLED),
+ SYS_PORT_MODE_INCL_XTR_HDR |
+ SYS_PORT_MODE_INCL_INJ_HDR,
+ lan9645x,
+ SYS_PORT_MODE(p->chip_port));
+
+ lan_rmw(BIT(p->chip_port),
+ BIT(p->chip_port), lan9645x,
+ ANA_PGID(PGID_SRC + lan9645x->num_phys_ports));
+}
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c
new file mode 100644
index 000000000000..4104071d48bd
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/phy.h>
+
+#include "lan9645x_main.h"
+
+/* Port 7 is RGMII_0 and port 8 is RGMII_1 */
+#define RGMII_IDX(port) ((port) == 8 ? 1 : 0)
+
+void lan9645x_phylink_get_caps(struct lan9645x *lan9645x, int port,
+ struct phylink_config *c)
+{
+ c->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+ MAC_100 | MAC_1000FD;
+
+ switch (port) {
+ case 0 ... 4:
+ __set_bit(PHY_INTERFACE_MODE_GMII, c->supported_interfaces);
+ break;
+ case 5 ... 6:
+ /* SerDes ports: QSGMII/SGMII/1000BASEX/2500BASEX modes
+ * require PCS support which is not yet implemented. With an
+ * empty supported_interfaces, phylink_create() fails and
+ * dsa_tree_setup_ports() retries the port as unused, so a
+ * user port declared in DT is only disabled. A CPU port has
+ * no such fallback and fails the probe, which
+ * lan9645x_tag_npi_setup() rejects explicitly.
+ */
+ break;
+ case 7 ... 8:
+ /* QSGMII mode on ports 7-8 requires SerDes PCS support,
+ * which is not yet implemented.
+ */
+ phy_interface_set_rgmii(c->supported_interfaces);
+ break;
+ default:
+ break;
+ }
+}
+
+static void lan9645x_rgmii_set_speed(struct lan9645x *lan9645x, int port,
+ int speed)
+{
+ u8 tx_clk;
+
+ tx_clk = speed == SPEED_1000 ? 1 :
+ speed == SPEED_100 ? 2 :
+ speed == SPEED_10 ? 3 : 0;
+
+ lan_rmw(HSIO_RGMII_CFG_RGMII_RX_RST_SET(0) |
+ HSIO_RGMII_CFG_RGMII_TX_RST_SET(0) |
+ HSIO_RGMII_CFG_TX_CLK_CFG_SET(tx_clk),
+ HSIO_RGMII_CFG_RGMII_RX_RST |
+ HSIO_RGMII_CFG_RGMII_TX_RST |
+ HSIO_RGMII_CFG_TX_CLK_CFG,
+ lan9645x, HSIO_RGMII_CFG(RGMII_IDX(port)));
+}
+
+static void lan9645x_rgmii_dll_config(struct lan9645x_port *p)
+{
+ u32 rx_idx, tx_idx;
+
+ /* DLL register layout:
+ * (N*2): RGMII_N_RX
+ * (N*2)+1: RGMII_N_TX
+ */
+ rx_idx = RGMII_IDX(p->chip_port) * 2;
+ tx_idx = RGMII_IDX(p->chip_port) * 2 + 1;
+
+ /* Enable DLL in RGMII clock paths, deassert DLL reset, and start the
+ * delay tune FSM.
+ */
+ lan_rmw(HSIO_DLL_CFG_DLL_CLK_ENA_SET(1) |
+ HSIO_DLL_CFG_DLL_RST_SET(0) |
+ HSIO_DLL_CFG_DLL_ENA_SET(p->rx_internal_delay) |
+ HSIO_DLL_CFG_DELAY_ENA_SET(p->rx_internal_delay),
+ HSIO_DLL_CFG_DLL_CLK_ENA |
+ HSIO_DLL_CFG_DLL_RST |
+ HSIO_DLL_CFG_DLL_ENA |
+ HSIO_DLL_CFG_DELAY_ENA,
+ p->lan9645x, HSIO_DLL_CFG(rx_idx));
+
+ lan_rmw(HSIO_DLL_CFG_DLL_CLK_ENA_SET(1) |
+ HSIO_DLL_CFG_DLL_RST_SET(0) |
+ HSIO_DLL_CFG_DLL_ENA_SET(p->tx_internal_delay) |
+ HSIO_DLL_CFG_DELAY_ENA_SET(p->tx_internal_delay),
+ HSIO_DLL_CFG_DLL_CLK_ENA |
+ HSIO_DLL_CFG_DLL_RST |
+ HSIO_DLL_CFG_DLL_ENA |
+ HSIO_DLL_CFG_DELAY_ENA,
+ p->lan9645x, HSIO_DLL_CFG(tx_idx));
+}
+
+static struct lan9645x_port *
+lan9645x_phylink_config_to_port(struct phylink_config *config)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+
+ return lan9645x_to_port(dp->ds->priv, dp->index);
+}
+
+static int lan9645x_phylink_mac_prepare(struct phylink_config *config,
+ unsigned int mode,
+ phy_interface_t iface)
+{
+ struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
+ struct lan9645x *lan9645x = p->lan9645x;
+ int port = p->chip_port;
+
+ lan_rmw(HSIO_HW_CFG_GMII_ENA_SET(BIT(port)),
+ HSIO_HW_CFG_GMII_ENA_SET(BIT(port)), lan9645x, HSIO_HW_CFG);
+
+ return 0;
+}
+
+static void lan9645x_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
+
+ if (phy_interface_mode_is_rgmii(state->interface))
+ lan9645x_rgmii_dll_config(p);
+}
+
+static bool lan9645x_port_is_cuphy(struct lan9645x *lan9645x, int port,
+ phy_interface_t interface)
+{
+ return port >= 0 && port <= 4 && interface == PHY_INTERFACE_MODE_GMII;
+}
+
+void lan9645x_phylink_port_down(struct lan9645x *lan9645x, int port)
+{
+ struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
+
+ /* Disable traffic being sent to or from switch port */
+ lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0),
+ QSYS_SW_PORT_MODE_PORT_ENA,
+ lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
+
+ /* With MAC_TX_RST and MAC_RX_RST asserted, the queue system treats
+ * the MAC as accepting every frame, so the frames queued to the port
+ * are discarded also when the MAC cannot transmit them, for example
+ * when the PHY is powered down or the MAC is held by a received pause
+ * frame. PORT_RST resets the FIFOs between the system and MAC clock
+ * domains, and asserting it while they still hold frames hangs the
+ * port, so it is kept deasserted.
+ */
+ lan_rmw(DEV_CLOCK_CFG_MAC_TX_RST_SET(1) |
+ DEV_CLOCK_CFG_MAC_RX_RST_SET(1) |
+ DEV_CLOCK_CFG_PORT_RST_SET(0),
+ DEV_CLOCK_CFG_MAC_TX_RST |
+ DEV_CLOCK_CFG_MAC_RX_RST |
+ DEV_CLOCK_CFG_PORT_RST,
+ lan9645x, DEV_CLOCK_CFG(p->chip_port));
+}
+
+static void lan9645x_phylink_mac_link_down(struct phylink_config *config,
+ unsigned int link_an_mode,
+ phy_interface_t interface)
+{
+ struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
+ struct lan9645x *lan9645x = p->lan9645x;
+
+ /* Stop forwarding all CPU queues to the NPI port before the port is put
+ * in reset
+ */
+ if (p->chip_port == lan9645x->npi)
+ lan9645x_npi_cpuq_redirect(lan9645x, false);
+
+ lan9645x_phylink_port_down(lan9645x, p->chip_port);
+}
+
+static void lan9645x_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
+ unsigned int link_an_mode,
+ phy_interface_t interface, int speed,
+ int duplex, bool tx_pause,
+ bool rx_pause)
+{
+ struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
+ int rx_ifg1, rx_ifg2, tx_ifg, gtx_clk = 0;
+ struct lan9645x *lan9645x = p->lan9645x;
+ int gspeed = LAN9645X_SPEED_DISABLED;
+ int port = p->chip_port;
+ int mode = 0;
+ int fc_spd;
+ bool fdx;
+
+ /* Configure RGMII TX clock for the negotiated speed */
+ if (phy_interface_mode_is_rgmii(interface))
+ lan9645x_rgmii_set_speed(lan9645x, port, speed);
+
+ fdx = duplex == DUPLEX_FULL || speed == SPEED_1000;
+
+ if (fdx)
+ mode |= DEV_MAC_MODE_CFG_FDX_ENA_SET(1);
+
+ tx_ifg = fdx ? 0x6 : 0x5;
+ rx_ifg2 = 0x2;
+
+ switch (speed) {
+ case SPEED_10:
+ rx_ifg1 = 0x2;
+ gspeed = LAN9645X_SPEED_10;
+ break;
+ case SPEED_100:
+ rx_ifg1 = 0x1;
+ gspeed = LAN9645X_SPEED_100;
+ break;
+ case SPEED_1000:
+ gspeed = LAN9645X_SPEED_1000;
+ mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1);
+ rx_ifg1 = 0x1;
+ gtx_clk = 1;
+ break;
+ default:
+ dev_err(lan9645x->dev, "Unsupported speed on port %d: %d\n",
+ p->chip_port, speed);
+ return;
+ }
+
+ fc_spd = lan9645x_speed_fc_enc(gspeed);
+
+ lan_rmw(mode,
+ DEV_MAC_MODE_CFG_FDX_ENA |
+ DEV_MAC_MODE_CFG_GIGA_MODE_ENA,
+ lan9645x, DEV_MAC_MODE_CFG(p->chip_port));
+
+ lan_rmw(DEV_MAC_IFG_CFG_TX_IFG_SET(tx_ifg) |
+ DEV_MAC_IFG_CFG_RX_IFG1_SET(rx_ifg1) |
+ DEV_MAC_IFG_CFG_RX_IFG2_SET(rx_ifg2),
+ DEV_MAC_IFG_CFG_TX_IFG |
+ DEV_MAC_IFG_CFG_RX_IFG1 |
+ DEV_MAC_IFG_CFG_RX_IFG2,
+ lan9645x, DEV_MAC_IFG_CFG(p->chip_port));
+
+ if (lan9645x_port_is_cuphy(lan9645x, port, interface)) {
+ lan_rmw(CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA_SET(gtx_clk),
+ CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA, lan9645x,
+ CHIP_TOP_CUPHY_PORT_CFG(p->chip_port));
+ }
+
+ lan_rmw(SYS_PAUSE_CFG_PAUSE_ENA_SET(1),
+ SYS_PAUSE_CFG_PAUSE_ENA,
+ lan9645x, SYS_PAUSE_CFG(p->chip_port));
+
+ /* Flow control */
+ lan_rmw(SYS_MAC_FC_CFG_FC_LINK_SPEED_SET(fc_spd) |
+ SYS_MAC_FC_CFG_FC_LATENCY_CFG_SET(0x7) |
+ SYS_MAC_FC_CFG_ZERO_PAUSE_ENA_SET(1) |
+ SYS_MAC_FC_CFG_PAUSE_VAL_CFG_SET(0xffff) |
+ SYS_MAC_FC_CFG_RX_FC_ENA_SET(rx_pause ? 1 : 0) |
+ SYS_MAC_FC_CFG_TX_FC_ENA_SET(tx_pause ? 1 : 0),
+ SYS_MAC_FC_CFG_FC_LINK_SPEED |
+ SYS_MAC_FC_CFG_FC_LATENCY_CFG |
+ SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
+ SYS_MAC_FC_CFG_PAUSE_VAL_CFG |
+ SYS_MAC_FC_CFG_RX_FC_ENA |
+ SYS_MAC_FC_CFG_TX_FC_ENA,
+ lan9645x, SYS_MAC_FC_CFG(p->chip_port));
+
+ /* Enable MAC module */
+ lan_wr(DEV_MAC_ENA_CFG_RX_ENA_SET(1) |
+ DEV_MAC_ENA_CFG_TX_ENA_SET(1),
+ lan9645x, DEV_MAC_ENA_CFG(p->chip_port));
+
+ /* Take out the clock from reset. Note this write will set all these
+ * fields to zero:
+ *
+ * DEV_CLOCK_CFG[*].MAC_TX_RST
+ * DEV_CLOCK_CFG[*].MAC_RX_RST
+ * DEV_CLOCK_CFG[*].PCS_TX_RST
+ * DEV_CLOCK_CFG[*].PCS_RX_RST
+ * DEV_CLOCK_CFG[*].PORT_RST
+ * DEV_CLOCK_CFG[*].PHY_RST
+ *
+ * Note link_down will assert MAC_RX_RST and MAC_TX_RST, so
+ * we are effectively taking the mac tx/rx clocks out of reset.
+ *
+ * This linkspeed field has a slightly different encoding from others:
+ *
+ * - 0 is no-link
+ * - 1 is both 2500/1000
+ * - 2 is 100mbit
+ * - 3 is 10mbit
+ *
+ */
+ lan_wr(DEV_CLOCK_CFG_LINK_SPEED_SET(fc_spd),
+ lan9645x,
+ DEV_CLOCK_CFG(p->chip_port));
+
+ /* Core: Enable port for frame transfer */
+ lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1) |
+ QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(1) |
+ QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(1) |
+ QSYS_SW_PORT_MODE_TX_PFC_ENA_SET(0),
+ QSYS_SW_PORT_MODE_PORT_ENA |
+ QSYS_SW_PORT_MODE_SCH_NEXT_CFG |
+ QSYS_SW_PORT_MODE_INGRESS_DROP_MODE |
+ QSYS_SW_PORT_MODE_TX_PFC_ENA,
+ lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
+
+ /* Forward all CPU queues to the NPI port */
+ if (port == lan9645x->npi)
+ lan9645x_npi_cpuq_redirect(lan9645x, true);
+}
+
+const struct phylink_mac_ops lan9645x_phylink_mac_ops = {
+ .mac_prepare = lan9645x_phylink_mac_prepare,
+ .mac_config = lan9645x_phylink_mac_config,
+ .mac_link_down = lan9645x_phylink_mac_link_down,
+ .mac_link_up = lan9645x_phylink_mac_link_up,
+};
diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
new file mode 100644
index 000000000000..4d2d922bacba
--- /dev/null
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Microchip Technology Inc.
+ */
+
+#include <linux/of_net.h>
+
+#include "lan9645x_main.h"
+
+void lan9645x_port_cpu_init(struct lan9645x *lan9645x)
+{
+ u32 cpu_module_mask = BIT(lan9645x->num_phys_ports);
+
+ /* Map the 8 CPU extraction queues to the CPU port module (datasheet is
+ * wrong)
+ */
+ lan_wr(0, lan9645x, QSYS_CPU_GROUP_MAP);
+
+ /* Configure first cpu port for manual extraction. */
+ lan_rmw(QS_XTR_GRP_CFG_MODE_SET(1),
+ QS_XTR_GRP_CFG_MODE,
+ lan9645x, QS_XTR_GRP_CFG(0));
+
+ /* The CPU will only use its reserved buffer in the shared queue system
+ * and none of the shared buffer space, therefore we disable resource
+ * sharing in egress direction. We must not disable resource sharing in
+ * the ingress direction, because some traffic test scenarios require
+ * loads of buffer memory for frames initiated by the CPU.
+ */
+ lan_rmw(QSYS_EGR_NO_SHARING_EGR_NO_SHARING_SET(cpu_module_mask),
+ QSYS_EGR_NO_SHARING_EGR_NO_SHARING_SET(cpu_module_mask),
+ lan9645x, QSYS_EGR_NO_SHARING);
+
+ /* The CPU should also discard frames forwarded to it if it has run
+ * out of the reserved buffer space. Otherwise they will be held back
+ * in the ingress queues with potential head-of-line blocking effects.
+ */
+ lan_rmw(QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE_SET(cpu_module_mask),
+ QSYS_EGR_DROP_MODE_EGRESS_DROP_MODE_SET(cpu_module_mask),
+ lan9645x, QSYS_EGR_DROP_MODE);
+
+ lan_wr(cpu_module_mask, lan9645x, ANA_PGID(PGID_CPU));
+
+ /* Enable switching to/from cpu port. Keep default aging-mode. */
+ lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1) |
+ QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(1) |
+ QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(1),
+ QSYS_SW_PORT_MODE_PORT_ENA |
+ QSYS_SW_PORT_MODE_SCH_NEXT_CFG |
+ QSYS_SW_PORT_MODE_INGRESS_DROP_MODE,
+ lan9645x, QSYS_SW_PORT_MODE(lan9645x->num_phys_ports));
+}
+
+/* VLAN tag overhead is handled by DEV_MAC_TAGS_CFG */
+int lan9645x_port_set_maxlen(struct lan9645x *lan9645x, int port, size_t sdu)
+{
+ struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
+ int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN;
+
+ if (port == lan9645x->npi) {
+ maxlen += LAN9645X_IFH_LEN_BYTES;
+ maxlen += LAN9645X_LONG_PREFIX_LEN;
+ }
+
+ lan_wr(DEV_MAC_MAXLEN_CFG_MAX_LEN_SET(maxlen), lan9645x,
+ DEV_MAC_MAXLEN_CFG(p->chip_port));
+
+ /* Set Pause WM hysteresis */
+ lan_rmw(SYS_PAUSE_CFG_PAUSE_STOP_SET(lan9645x_wm_enc(4 * maxlen)) |
+ SYS_PAUSE_CFG_PAUSE_START_SET(lan9645x_wm_enc(6 * maxlen)),
+ SYS_PAUSE_CFG_PAUSE_START |
+ SYS_PAUSE_CFG_PAUSE_STOP,
+ lan9645x,
+ SYS_PAUSE_CFG(p->chip_port));
+
+ return 0;
+}
+
+static void lan9645x_parse_mac_delay(struct lan9645x *lan9645x, int port,
+ struct device_node *dn, const char *name,
+ bool *out)
+{
+ u32 val;
+
+ if (of_property_read_u32(dn, name, &val))
+ return;
+
+ if (val != 0 && val != 2000) {
+ dev_warn(lan9645x->dev,
+ "port %d: %s only supports a 2ns delay (on/off), got %u ps\n",
+ port, name, val);
+ return;
+ }
+
+ *out = val == 2000;
+}
+
+int lan9645x_port_setup(struct dsa_switch *ds, int port)
+{
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct lan9645x *lan9645x = ds->priv;
+ struct lan9645x_port *p;
+
+ p = lan9645x_to_port(lan9645x, port);
+
+ if (dp->dn) {
+ lan9645x_parse_mac_delay(lan9645x, port, dp->dn,
+ "rx-internal-delay-ps",
+ &p->rx_internal_delay);
+
+ lan9645x_parse_mac_delay(lan9645x, port, dp->dn,
+ "tx-internal-delay-ps",
+ &p->tx_internal_delay);
+ }
+
+ /* Disable learning on port */
+ lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(0),
+ ANA_PORT_CFG_LEARN_ENA,
+ lan9645x, ANA_PORT_CFG(p->chip_port));
+
+ lan9645x_port_set_maxlen(lan9645x, port, ETH_DATA_LEN);
+
+ /* Load HDX backoff seed (fixed per-port, one-shot strobe) */
+ lan_rmw(DEV_MAC_HDX_CFG_SEED_SET(p->chip_port) |
+ DEV_MAC_HDX_CFG_SEED_LOAD_SET(1),
+ DEV_MAC_HDX_CFG_SEED |
+ DEV_MAC_HDX_CFG_SEED_LOAD, lan9645x,
+ DEV_MAC_HDX_CFG(p->chip_port));
+
+ lan_rmw(DEV_MAC_HDX_CFG_SEED_LOAD_SET(0),
+ DEV_MAC_HDX_CFG_SEED_LOAD, lan9645x,
+ DEV_MAC_HDX_CFG(p->chip_port));
+
+ /* Set SMAC of Pause frame (00:00:00:00:00:00) */
+ lan_wr(0, lan9645x, DEV_FC_MAC_LOW_CFG(p->chip_port));
+ lan_wr(0, lan9645x, DEV_FC_MAC_HIGH_CFG(p->chip_port));
+
+ lan9645x_phylink_port_down(lan9645x, port);
+
+ /* Drop frames with multicast source address */
+ lan_rmw(ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1),
+ ANA_DROP_CFG_DROP_MC_SMAC_ENA, lan9645x,
+ ANA_DROP_CFG(p->chip_port));
+
+ lan_rmw(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(1) |
+ DEV_MAC_TAGS_CFG_PB_ENA_SET(1) |
+ DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(1) |
+ DEV_MAC_TAGS_CFG_TAG_ID_SET(ETH_P_8021AD),
+ DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
+ DEV_MAC_TAGS_CFG_PB_ENA |
+ DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA |
+ DEV_MAC_TAGS_CFG_TAG_ID,
+ lan9645x, DEV_MAC_TAGS_CFG(p->chip_port));
+
+ /* Enable receiving frames on the port, and activate auto-learning of
+ * MAC addresses. LEARNAUTO is ignored when LEARN_ENA=0.
+ */
+ lan_rmw(ANA_PORT_CFG_LEARNAUTO_SET(1) |
+ ANA_PORT_CFG_RECV_ENA_SET(1) |
+ ANA_PORT_CFG_PORTID_VAL_SET(p->chip_port),
+ ANA_PORT_CFG_LEARNAUTO |
+ ANA_PORT_CFG_RECV_ENA |
+ ANA_PORT_CFG_PORTID_VAL,
+ lan9645x, ANA_PORT_CFG(p->chip_port));
+
+ return 0;
+}
--
2.52.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-03 12:32 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 12:31 [PATCH net-next v16 0/4] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 1/4] net: dsa: add tag driver for LAN9645X Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 2/4] dt-bindings: net: dsa: add Microchip LAN9645x switch Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 3/4] net: dsa: lan9645x: add autogenerated register macros Jens Emil Schulz Østergaard
2026-11-03 12:31 ` [PATCH net-next v16 4/4] net: dsa: lan9645x: add basic dsa driver for LAN9645X Jens Emil Schulz Østergaard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®