mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
Merge branch 'add-preliminary-netc-switch-support-for-i-mx94'
Wei Fang says:
====================
Add preliminary NETC switch support for i.MX94
i.MX94 NETC (v4.3) integrates 802.1Q Ethernet switch functionality, the
switch provides advanced QoS with 8 traffic classes and a full range of
TSN standards capabilities. It has 3 user ports and 1 CPU port, and the
CPU port is connected to an internal ENETC through the pseduo link, so
instead of a back-to-back MAC, the lightweight "pseudo MAC" is used at
both ends of the pseudo link to transfer Ethernet frames. The pseudo
link provides a zero-copy interface (no serialization delay) and lower
power (less logic and memory).
Like most Ethernet switches, the NETC switch also supports a proprietary
switch tag, is used to carry in-band metadata information about frames.
This in-band metadata information can include the source port from which
the frame was received, what was the reason why this frame got forwarded
to the entity, and for the entity to indicate the precise destination
port of a frame. The NETC switch tag is added to frames after the source
MAC address. There are three types of switch tags, and each type has 1
to 4 subtypes, more details are as follows.
Forward switch tag (Type = 0): Represents forwarded frames.
- SubType = 0 - Normal frame processing.
To_Port switch tag (Type = 1): Represents frames that are to be sent to
a specific switch port.
- SubType = 0. No request to perform timestamping.
- SubType = 1. Request to perform one-step timestamping.
- SubType = 2. Request to perform two-step timestamping.
- SubType = 3. Request to perform both one-step timestamping and
two-step timestamping.
To_Host switch tag (Type = 2): Represents frames redirected or copied to
the switch management port.
- SubType = 0. Received frames redirected or copied to the switch
management port.
- SubType = 1. Received frames redirected or copied to the switch
management port with captured timestamp at the switch port where
the frame was received.
- SubType = 2. Transmit timestamp response (two-step timestamping).
Currently, this patch set supports Forward tag, SubType 0 of To_Port tag
and SubType 0 of To_Host tag. More tags will be supported in the future.
In addition, the switch supports NETC Table Management Protocol (NTMP),
some switch functionality is controlled using control messages sent to
the hardware using BD ring interface with 32B descriptors similar to the
packet Transmit BD ring used on ENETC. This interface is referred to as
the command BD ring. This is used to configure functionality where the
underlying resources may be shared between different entities or being
too large to configure using direct registers.
For this patch set, we have supported the following tables through the
command BD ring interface.
FDB Table: It contains forwarding and/or filtering information about MAC
addresses. The FDB table is used for MAC learning lookups and MAC
forwarding lookups.
VLAN Filter Table: It contains configuration and control information for
each VLAN configured on the switch.
Buffer Pool Table: It contains buffer pool configuration and operational
information. Each entry corresponds to a buffer pool. Currently, we use
this table to implement flow control feature on each port.
Ingress Port Filter Table: It contains a set of filters each capable of
classifying incoming traffic using a mix of L2, L3, and L4 parsed and
arbitrary field data. We use this table to implement host flood support
to the switch port.
The switch also supports other tables, and we will add more advanced
features through them in the future.
====================
Link: https://patch.msgid.link/20260518082506.1318236-1-wei.fang@nxp.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
73366893d1
|
|
@ -28,7 +28,11 @@ properties:
|
|||
A two element list indicates which DSA cluster, and position within the
|
||||
cluster a switch takes. <0 0> is cluster 0, switch 0. <0 1> is cluster 0,
|
||||
switch 1. <1 0> is cluster 1, switch 0. A switch not part of any cluster
|
||||
(single device hanging off a CPU port) must not specify this property
|
||||
(single device hanging off a CPU port) does not usually need to specify
|
||||
this property, and then it becomes cluster 0, switch 0. For a topology
|
||||
aware switch, its switch index can be specified through this property,
|
||||
even if it is not part of any cluster. Also, topology-unaware switches
|
||||
must always be defined as index 0 of their cluster.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
additionalProperties: true
|
||||
|
|
|
|||
131
Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
Normal file
131
Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/dsa/nxp,netc-switch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NETC Switch family
|
||||
|
||||
description: >
|
||||
The NETC presents itself as a multi-function PCIe Root Complex Integrated
|
||||
Endpoint (RCiEP) and provides full 802.1Q Ethernet switch functionality,
|
||||
advanced QoS with 8 traffic classes and 4 drop resilience levels, and a
|
||||
full range of TSN standards capabilities.
|
||||
|
||||
The CPU port of the switch connects to an internal ENETC. The switch and
|
||||
the internal ENETC are fully integrated into the NETC IP, a back-to-back
|
||||
MAC is not required. Instead, a light-weight "pseudo MAC" provides the
|
||||
delineation between the switch and ENETC. This translates to lower power
|
||||
(less logic and memory) and lower delay (as there is no serialization
|
||||
delay across this link).
|
||||
|
||||
maintainers:
|
||||
- Wei Fang <wei.fang@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- pci1131,eef2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dsa,member:
|
||||
description: >
|
||||
The property indicates DSA cluster and switch index. For NETC switch,
|
||||
the valid range of the switch index is 1 ~ 7, the index is reflected
|
||||
in the switch tag as an indication of the switch ID where the frame
|
||||
originated. The value 0 is reserved for ENETC VEPA switch, whose ID
|
||||
is hardwired to zero.
|
||||
items:
|
||||
- true
|
||||
- minimum: 1
|
||||
maximum: 7
|
||||
|
||||
ethernet-ports:
|
||||
type: object
|
||||
patternProperties:
|
||||
"^ethernet-port@[0-9a-f]$":
|
||||
type: object
|
||||
$ref: dsa-port.yaml#
|
||||
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: MAC transmit/receive reference clock.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref
|
||||
|
||||
mdio:
|
||||
$ref: /schemas/net/mdio.yaml#
|
||||
unevaluatedProperties: false
|
||||
description:
|
||||
Optional child node for switch port, otherwise use NETC EMDIO.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- dsa,member
|
||||
- ethernet-ports
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/pci/pci-device.yaml
|
||||
- $ref: dsa.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
pcie {
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ethernet-switch@0,2 {
|
||||
compatible = "pci1131,eef2";
|
||||
reg = <0x200 0 0 0 0>;
|
||||
dsa,member = <0 1>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_switch>;
|
||||
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet-port@0 {
|
||||
reg = <0>;
|
||||
phy-handle = <ðphy0>;
|
||||
phy-mode = "mii";
|
||||
};
|
||||
|
||||
ethernet-port@1 {
|
||||
reg = <1>;
|
||||
phy-handle = <ðphy1>;
|
||||
phy-mode = "mii";
|
||||
};
|
||||
|
||||
ethernet-port@2 {
|
||||
reg = <2>;
|
||||
clocks = <&scmi_clk 103>;
|
||||
clock-names = "ref";
|
||||
phy-handle = <ðphy2>;
|
||||
phy-mode = "rgmii-id";
|
||||
};
|
||||
|
||||
ethernet-port@3 {
|
||||
reg = <3>;
|
||||
ethernet = <&enetc3>;
|
||||
phy-mode = "internal";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
11
MAINTAINERS
11
MAINTAINERS
|
|
@ -19301,6 +19301,17 @@ F: Documentation/devicetree/bindings/clock/*imx*
|
|||
F: drivers/clk/imx/
|
||||
F: include/dt-bindings/clock/*imx*
|
||||
|
||||
NXP NETC ETHERNET SWITCH DRIVER
|
||||
M: Wei Fang <wei.fang@nxp.com>
|
||||
R: Clark Wang <xiaoning.wang@nxp.com>
|
||||
L: imx@lists.linux.dev
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
|
||||
F: drivers/net/dsa/netc/
|
||||
F: include/linux/dsa/tag_netc.h
|
||||
F: net/dsa/tag_netc.c
|
||||
|
||||
NXP NETC TIMER PTP CLOCK DRIVER
|
||||
M: Wei Fang <wei.fang@nxp.com>
|
||||
M: Clark Wang <xiaoning.wang@nxp.com>
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ source "drivers/net/dsa/mv88e6xxx/Kconfig"
|
|||
|
||||
source "drivers/net/dsa/mxl862xx/Kconfig"
|
||||
|
||||
source "drivers/net/dsa/netc/Kconfig"
|
||||
|
||||
source "drivers/net/dsa/ocelot/Kconfig"
|
||||
|
||||
source "drivers/net/dsa/qca/Kconfig"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ obj-y += lantiq/
|
|||
obj-y += microchip/
|
||||
obj-y += mv88e6xxx/
|
||||
obj-y += mxl862xx/
|
||||
obj-y += netc/
|
||||
obj-y += ocelot/
|
||||
obj-y += qca/
|
||||
obj-y += realtek/
|
||||
|
|
|
|||
15
drivers/net/dsa/netc/Kconfig
Normal file
15
drivers/net/dsa/netc/Kconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config NET_DSA_NETC_SWITCH
|
||||
tristate "NXP NETC Ethernet switch support"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
depends on NET_DSA && PCI
|
||||
select NET_DSA_TAG_NETC
|
||||
select FSL_ENETC_MDIO
|
||||
select NXP_NTMP
|
||||
select NXP_NETC_LIB
|
||||
help
|
||||
This driver supports the NXP NETC Ethernet switch, which is embedded
|
||||
as a PCIe function of the NXP NETC IP. But note that this driver is
|
||||
only available for NETC v4.3 and later versions.
|
||||
|
||||
If compiled as module (M), the module name is nxp-netc-switch.
|
||||
3
drivers/net/dsa/netc/Makefile
Normal file
3
drivers/net/dsa/netc/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_NET_DSA_NETC_SWITCH) += nxp-netc-switch.o
|
||||
nxp-netc-switch-objs := netc_main.o netc_platform.o netc_ethtool.o
|
||||
291
drivers/net/dsa/netc/netc_ethtool.c
Normal file
291
drivers/net/dsa/netc/netc_ethtool.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* NXP NETC switch driver
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include "netc_switch.h"
|
||||
|
||||
static const struct ethtool_rmon_hist_range netc_rmon_ranges[] = {
|
||||
{ 64, 64 },
|
||||
{ 65, 127 },
|
||||
{ 128, 255 },
|
||||
{ 256, 511 },
|
||||
{ 512, 1023 },
|
||||
{ 1024, 1522 },
|
||||
{ 1523, NETC_MAX_FRAME_LEN },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct netc_port_stat netc_port_counters[] = {
|
||||
{ NETC_PTGSLACR, "port gate late arrival frames" },
|
||||
{ NETC_PSDFTCR, "port SDF transmit frames" },
|
||||
{ NETC_PSDFDDCR, "port SDF drop duplicate frames" },
|
||||
{ NETC_PRXDCR, "port rx discard frames" },
|
||||
{ NETC_PRXDCRRR, "port rx discard read-reset" },
|
||||
{ NETC_PRXDCRR0, "port rx discard reason 0" },
|
||||
{ NETC_PRXDCRR1, "port rx discard reason 1" },
|
||||
{ NETC_PTXDCR, "port tx discard frames" },
|
||||
{ NETC_BPDCR, "bridge port discard frames" },
|
||||
};
|
||||
|
||||
static const struct netc_port_stat netc_emac_counters[] = {
|
||||
{ NETC_PM_ROCT(0), "eMAC rx octets" },
|
||||
{ NETC_PM_RVLAN(0), "eMAC rx VLAN frames" },
|
||||
{ NETC_PM_RERR(0), "eMAC rx frame errors" },
|
||||
{ NETC_PM_RUCA(0), "eMAC rx unicast frames" },
|
||||
{ NETC_PM_RDRP(0), "eMAC rx dropped packets" },
|
||||
{ NETC_PM_RPKT(0), "eMAC rx packets" },
|
||||
{ NETC_PM_TOCT(0), "eMAC tx octets" },
|
||||
{ NETC_PM_TVLAN(0), "eMAC tx VLAN frames" },
|
||||
{ NETC_PM_TFCS(0), "eMAC tx FCS errors" },
|
||||
{ NETC_PM_TUCA(0), "eMAC tx unicast frames" },
|
||||
{ NETC_PM_TPKT(0), "eMAC tx packets" },
|
||||
{ NETC_PM_TUND(0), "eMAC tx undersized packets" },
|
||||
{ NETC_PM_TIOCT(0), "eMAC tx invalid octets" },
|
||||
};
|
||||
|
||||
static const struct netc_port_stat netc_pmac_counters[] = {
|
||||
{ NETC_PM_ROCT(1), "pMAC rx octets" },
|
||||
{ NETC_PM_RVLAN(1), "pMAC rx VLAN frames" },
|
||||
{ NETC_PM_RERR(1), "pMAC rx frame errors" },
|
||||
{ NETC_PM_RUCA(1), "pMAC rx unicast frames" },
|
||||
{ NETC_PM_RDRP(1), "pMAC rx dropped packets" },
|
||||
{ NETC_PM_RPKT(1), "pMAC rx packets" },
|
||||
{ NETC_PM_TOCT(1), "pMAC tx octets" },
|
||||
{ NETC_PM_TVLAN(1), "pMAC tx VLAN frames" },
|
||||
{ NETC_PM_TFCS(1), "pMAC tx FCS errors" },
|
||||
{ NETC_PM_TUCA(1), "pMAC tx unicast frames" },
|
||||
{ NETC_PM_TPKT(1), "pMAC tx packets" },
|
||||
{ NETC_PM_TUND(1), "pMAC tx undersized packets" },
|
||||
{ NETC_PM_TIOCT(1), "pMAC tx invalid octets" },
|
||||
};
|
||||
|
||||
static void netc_port_pause_stats(struct netc_port *np, int mac,
|
||||
struct ethtool_pause_stats *stats)
|
||||
{
|
||||
if (mac && !np->caps.pmac)
|
||||
return;
|
||||
|
||||
stats->tx_pause_frames = netc_port_rd64(np, NETC_PM_TXPF(mac));
|
||||
stats->rx_pause_frames = netc_port_rd64(np, NETC_PM_RXPF(mac));
|
||||
}
|
||||
|
||||
void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_pause_stats *pause_stats)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
struct net_device *ndev;
|
||||
|
||||
switch (pause_stats->src) {
|
||||
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
||||
netc_port_pause_stats(np, 0, pause_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
||||
netc_port_pause_stats(np, 1, pause_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
||||
ndev = dsa_to_port(ds, port)->user;
|
||||
ethtool_aggregate_pause_stats(ndev, pause_stats);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netc_port_rmon_stats(struct netc_port *np, int mac,
|
||||
struct ethtool_rmon_stats *stats)
|
||||
{
|
||||
if (mac && !np->caps.pmac)
|
||||
return;
|
||||
|
||||
stats->undersize_pkts = netc_port_rd64(np, NETC_PM_RUND(mac));
|
||||
stats->oversize_pkts = netc_port_rd64(np, NETC_PM_ROVR(mac));
|
||||
stats->fragments = netc_port_rd64(np, NETC_PM_RFRG(mac));
|
||||
stats->jabbers = netc_port_rd64(np, NETC_PM_RJBR(mac));
|
||||
|
||||
stats->hist[0] = netc_port_rd64(np, NETC_PM_R64(mac));
|
||||
stats->hist[1] = netc_port_rd64(np, NETC_PM_R127(mac));
|
||||
stats->hist[2] = netc_port_rd64(np, NETC_PM_R255(mac));
|
||||
stats->hist[3] = netc_port_rd64(np, NETC_PM_R511(mac));
|
||||
stats->hist[4] = netc_port_rd64(np, NETC_PM_R1023(mac));
|
||||
stats->hist[5] = netc_port_rd64(np, NETC_PM_R1522(mac));
|
||||
stats->hist[6] = netc_port_rd64(np, NETC_PM_R1523X(mac));
|
||||
|
||||
stats->hist_tx[0] = netc_port_rd64(np, NETC_PM_T64(mac));
|
||||
stats->hist_tx[1] = netc_port_rd64(np, NETC_PM_T127(mac));
|
||||
stats->hist_tx[2] = netc_port_rd64(np, NETC_PM_T255(mac));
|
||||
stats->hist_tx[3] = netc_port_rd64(np, NETC_PM_T511(mac));
|
||||
stats->hist_tx[4] = netc_port_rd64(np, NETC_PM_T1023(mac));
|
||||
stats->hist_tx[5] = netc_port_rd64(np, NETC_PM_T1522(mac));
|
||||
stats->hist_tx[6] = netc_port_rd64(np, NETC_PM_T1523X(mac));
|
||||
}
|
||||
|
||||
void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_rmon_stats *rmon_stats,
|
||||
const struct ethtool_rmon_hist_range **ranges)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
struct net_device *ndev;
|
||||
|
||||
*ranges = netc_rmon_ranges;
|
||||
|
||||
switch (rmon_stats->src) {
|
||||
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
||||
netc_port_rmon_stats(np, 0, rmon_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
||||
netc_port_rmon_stats(np, 1, rmon_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
||||
ndev = dsa_to_port(ds, port)->user;
|
||||
ethtool_aggregate_rmon_stats(ndev, rmon_stats);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netc_port_ctrl_stats(struct netc_port *np, int mac,
|
||||
struct ethtool_eth_ctrl_stats *stats)
|
||||
{
|
||||
if (mac && !np->caps.pmac)
|
||||
return;
|
||||
|
||||
stats->MACControlFramesTransmitted =
|
||||
netc_port_rd64(np, NETC_PM_TCNP(mac));
|
||||
stats->MACControlFramesReceived =
|
||||
netc_port_rd64(np, NETC_PM_RCNP(mac));
|
||||
}
|
||||
|
||||
void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_eth_ctrl_stats *ctrl_stats)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
struct net_device *ndev;
|
||||
|
||||
switch (ctrl_stats->src) {
|
||||
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
||||
netc_port_ctrl_stats(np, 0, ctrl_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
||||
netc_port_ctrl_stats(np, 1, ctrl_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
||||
ndev = dsa_to_port(ds, port)->user;
|
||||
ethtool_aggregate_ctrl_stats(ndev, ctrl_stats);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void netc_port_mac_stats(struct netc_port *np, int mac,
|
||||
struct ethtool_eth_mac_stats *stats)
|
||||
{
|
||||
if (mac && !np->caps.pmac)
|
||||
return;
|
||||
|
||||
stats->FramesTransmittedOK = netc_port_rd64(np, NETC_PM_TFRM(mac));
|
||||
stats->SingleCollisionFrames = netc_port_rd64(np, NETC_PM_TSCOL(mac));
|
||||
stats->MultipleCollisionFrames =
|
||||
netc_port_rd64(np, NETC_PM_TMCOL(mac));
|
||||
stats->FramesReceivedOK = netc_port_rd64(np, NETC_PM_RFRM(mac));
|
||||
stats->FrameCheckSequenceErrors =
|
||||
netc_port_rd64(np, NETC_PM_RFCS(mac));
|
||||
stats->AlignmentErrors = netc_port_rd64(np, NETC_PM_RALN(mac));
|
||||
stats->OctetsTransmittedOK = netc_port_rd64(np, NETC_PM_TEOCT(mac));
|
||||
stats->FramesWithDeferredXmissions =
|
||||
netc_port_rd64(np, NETC_PM_TDFR(mac));
|
||||
stats->LateCollisions = netc_port_rd64(np, NETC_PM_TLCOL(mac));
|
||||
stats->FramesAbortedDueToXSColls =
|
||||
netc_port_rd64(np, NETC_PM_TECOL(mac));
|
||||
stats->FramesLostDueToIntMACXmitError =
|
||||
netc_port_rd64(np, NETC_PM_TERR(mac));
|
||||
stats->OctetsReceivedOK = netc_port_rd64(np, NETC_PM_REOCT(mac));
|
||||
stats->FramesLostDueToIntMACRcvError =
|
||||
netc_port_rd64(np, NETC_PM_RDRNTP(mac));
|
||||
stats->MulticastFramesXmittedOK =
|
||||
netc_port_rd64(np, NETC_PM_TMCA(mac));
|
||||
stats->BroadcastFramesXmittedOK =
|
||||
netc_port_rd64(np, NETC_PM_TBCA(mac));
|
||||
stats->MulticastFramesReceivedOK =
|
||||
netc_port_rd64(np, NETC_PM_RMCA(mac));
|
||||
stats->BroadcastFramesReceivedOK =
|
||||
netc_port_rd64(np, NETC_PM_RBCA(mac));
|
||||
stats->FramesWithExcessiveDeferral =
|
||||
netc_port_rd64(np, NETC_PM_TEDFR(mac));
|
||||
}
|
||||
|
||||
void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_eth_mac_stats *mac_stats)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
struct net_device *ndev;
|
||||
|
||||
switch (mac_stats->src) {
|
||||
case ETHTOOL_MAC_STATS_SRC_EMAC:
|
||||
netc_port_mac_stats(np, 0, mac_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_PMAC:
|
||||
netc_port_mac_stats(np, 1, mac_stats);
|
||||
break;
|
||||
case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
|
||||
ndev = dsa_to_port(ds, port)->user;
|
||||
ethtool_aggregate_mac_stats(ndev, mac_stats);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
int size;
|
||||
|
||||
if (sset != ETH_SS_STATS)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
size = ARRAY_SIZE(netc_port_counters) +
|
||||
ARRAY_SIZE(netc_emac_counters);
|
||||
|
||||
if (np->caps.pmac)
|
||||
size += ARRAY_SIZE(netc_pmac_counters);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void netc_port_get_strings(struct dsa_switch *ds, int port,
|
||||
u32 sset, u8 *data)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
int i;
|
||||
|
||||
if (sset != ETH_SS_STATS)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
|
||||
ethtool_cpy(&data, netc_port_counters[i].name);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
|
||||
ethtool_cpy(&data, netc_emac_counters[i].name);
|
||||
|
||||
if (!np->caps.pmac)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
|
||||
ethtool_cpy(&data, netc_pmac_counters[i].name);
|
||||
}
|
||||
|
||||
void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
|
||||
{
|
||||
struct netc_port *np = NETC_PORT(ds, port);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++)
|
||||
*data++ = netc_port_rd(np, netc_port_counters[i].reg);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++)
|
||||
*data++ = netc_port_rd64(np, netc_emac_counters[i].reg);
|
||||
|
||||
if (!np->caps.pmac)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++)
|
||||
*data++ = netc_port_rd64(np, netc_pmac_counters[i].reg);
|
||||
}
|
||||
1585
drivers/net/dsa/netc/netc_main.c
Normal file
1585
drivers/net/dsa/netc/netc_main.c
Normal file
File diff suppressed because it is too large
Load Diff
87
drivers/net/dsa/netc/netc_platform.c
Normal file
87
drivers/net/dsa/netc/netc_platform.c
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* NXP NETC switch driver
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#include "netc_switch.h"
|
||||
|
||||
struct netc_switch_platform {
|
||||
u16 revision;
|
||||
const struct netc_switch_info *info;
|
||||
};
|
||||
|
||||
static void imx94_switch_phylink_get_caps(int port,
|
||||
struct phylink_config *config)
|
||||
{
|
||||
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_1000FD;
|
||||
|
||||
switch (port) {
|
||||
case 0 ... 1:
|
||||
__set_bit(PHY_INTERFACE_MODE_SGMII,
|
||||
config->supported_interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
|
||||
config->supported_interfaces);
|
||||
config->mac_capabilities |= MAC_2500FD;
|
||||
fallthrough;
|
||||
case 2:
|
||||
config->mac_capabilities |= MAC_10 | MAC_100;
|
||||
__set_bit(PHY_INTERFACE_MODE_MII,
|
||||
config->supported_interfaces);
|
||||
__set_bit(PHY_INTERFACE_MODE_RMII,
|
||||
config->supported_interfaces);
|
||||
/* Port 0 and 1 do not support REVMII */
|
||||
if (port == 2)
|
||||
__set_bit(PHY_INTERFACE_MODE_REVMII,
|
||||
config->supported_interfaces);
|
||||
|
||||
phy_interface_set_rgmii(config->supported_interfaces);
|
||||
break;
|
||||
case 3: /* CPU port */
|
||||
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
|
||||
config->supported_interfaces);
|
||||
config->mac_capabilities |= MAC_10FD | MAC_100FD |
|
||||
MAC_2500FD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct netc_switch_info imx94_info = {
|
||||
.num_ports = 4,
|
||||
.phylink_get_caps = imx94_switch_phylink_get_caps,
|
||||
};
|
||||
|
||||
static const struct netc_switch_platform netc_platforms[] = {
|
||||
{ .revision = NETC_SWITCH_REV_4_3, .info = &imx94_info, },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct netc_switch_info *
|
||||
netc_switch_get_info(struct netc_switch *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Matching based on IP revision */
|
||||
for (i = 0; i < ARRAY_SIZE(netc_platforms); i++) {
|
||||
if (priv->revision == netc_platforms[i].revision)
|
||||
return netc_platforms[i].info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int netc_switch_platform_probe(struct netc_switch *priv)
|
||||
{
|
||||
const struct netc_switch_info *info = netc_switch_get_info(priv);
|
||||
|
||||
if (!info) {
|
||||
dev_err(priv->dev, "Cannot find switch platform info\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->info = info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
173
drivers/net/dsa/netc/netc_switch.h
Normal file
173
drivers/net/dsa/netc/netc_switch.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NETC_SWITCH_H
|
||||
#define _NETC_SWITCH_H
|
||||
|
||||
#include <linux/dsa/tag_netc.h>
|
||||
#include <linux/fsl/netc_global.h>
|
||||
#include <linux/fsl/ntmp.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "netc_switch_hw.h"
|
||||
|
||||
#define NETC_REGS_BAR 0
|
||||
#define NETC_REGS_SIZE 0x80000
|
||||
#define NETC_MSIX_TBL_BAR 2
|
||||
#define NETC_REGS_PORT_BASE 0x4000
|
||||
/* register block size per port */
|
||||
#define NETC_REGS_PORT_SIZE 0x4000
|
||||
#define PORT_IOBASE(p) (NETC_REGS_PORT_SIZE * (p))
|
||||
#define NETC_REGS_GLOBAL_BASE 0x70000
|
||||
|
||||
#define NETC_SWITCH_REV_4_3 0x0403
|
||||
|
||||
#define NETC_TC_NUM 8
|
||||
#define NETC_CBDR_NUM 2
|
||||
#define NETC_IPV_NUM 8
|
||||
|
||||
#define NETC_MAX_FRAME_LEN 9600
|
||||
|
||||
#define NETC_STANDALONE_PVID 0
|
||||
|
||||
/* Threshold format: MANT (bits 11:4) * 2^EXP (bits 3:0)
|
||||
* Unit: Memory words (average of 20 bytes each)
|
||||
* NETC_BP_THRESH = 0x8c3, MANT = 0x8c, EXP = 3. Threshold: 1120 words
|
||||
* NETC_FC_THRESH_ON = 0x733, MANT = 0x73, EXP = 3. Threshold: 920 words
|
||||
* NETC_FC_THRESH_OFF = 0x263, MANT = 0x26, EXP = 3. Threshold: 304 words
|
||||
*/
|
||||
#define NETC_BP_THRESH 0x8c3
|
||||
#define NETC_FC_THRESH_ON 0x733
|
||||
#define NETC_FC_THRESH_OFF 0x263
|
||||
|
||||
/* PAUSE quanta: 0xFFFF = 65535 quanta (each quanta = 512 bit times) */
|
||||
#define NETC_PAUSE_QUANTA 0xFFFF
|
||||
/* PAUSE refresh threshold: send refresh when timer reaches this value */
|
||||
#define NETC_PAUSE_THRESH 0x7FFF
|
||||
|
||||
struct netc_switch;
|
||||
|
||||
struct netc_switch_info {
|
||||
u32 num_ports;
|
||||
void (*phylink_get_caps)(int port, struct phylink_config *config);
|
||||
};
|
||||
|
||||
struct netc_port_caps {
|
||||
u32 half_duplex:1; /* indicates whether the port support half-duplex */
|
||||
u32 pmac:1; /* indicates whether the port has preemption MAC */
|
||||
u32 pseudo_link:1;
|
||||
};
|
||||
|
||||
enum netc_host_reason {
|
||||
/* Software defined host reasons */
|
||||
NETC_HR_HOST_FLOOD = 8,
|
||||
};
|
||||
|
||||
struct netc_port {
|
||||
void __iomem *iobase;
|
||||
struct netc_switch *switch_priv;
|
||||
struct netc_port_caps caps;
|
||||
struct dsa_port *dp;
|
||||
struct clk *ref_clk; /* RGMII/RMII reference clock */
|
||||
struct mii_bus *emdio;
|
||||
|
||||
u16 enable:1;
|
||||
u16 uc:1;
|
||||
u16 mc:1;
|
||||
struct ipft_entry_data *host_flood;
|
||||
};
|
||||
|
||||
struct netc_switch_regs {
|
||||
void __iomem *base;
|
||||
void __iomem *port;
|
||||
void __iomem *global;
|
||||
};
|
||||
|
||||
struct netc_fdb_entry {
|
||||
u32 entry_id;
|
||||
struct fdbt_cfge_data cfge;
|
||||
struct fdbt_keye_data keye;
|
||||
struct hlist_node node;
|
||||
};
|
||||
|
||||
struct netc_port_stat {
|
||||
int reg;
|
||||
char name[ETH_GSTRING_LEN] __nonstring;
|
||||
};
|
||||
|
||||
struct netc_switch {
|
||||
struct pci_dev *pdev;
|
||||
struct device *dev;
|
||||
struct dsa_switch *ds;
|
||||
u16 revision;
|
||||
|
||||
const struct netc_switch_info *info;
|
||||
struct netc_switch_regs regs;
|
||||
struct netc_port **ports;
|
||||
|
||||
struct ntmp_user ntmp;
|
||||
struct hlist_head fdb_list;
|
||||
struct mutex fdbt_lock; /* FDB table lock */
|
||||
|
||||
/* Switch hardware capabilities */
|
||||
u32 htmcapr_num_words;
|
||||
u32 num_bp;
|
||||
|
||||
struct bpt_cfge_data *bpt_list;
|
||||
};
|
||||
|
||||
#define NETC_PRIV(ds) ((struct netc_switch *)((ds)->priv))
|
||||
#define NETC_PORT(ds, port_id) (NETC_PRIV(ds)->ports[(port_id)])
|
||||
|
||||
/* Write/Read Switch base registers */
|
||||
#define netc_base_rd(r, o) netc_read((r)->base + (o))
|
||||
#define netc_base_wr(r, o, v) netc_write((r)->base + (o), v)
|
||||
|
||||
/* Write/Read registers of Switch Port (including pseudo MAC port) */
|
||||
#define netc_port_rd(p, o) netc_read((p)->iobase + (o))
|
||||
#define netc_port_rd64(p, o) netc_read64((p)->iobase + (o))
|
||||
#define netc_port_wr(p, o, v) netc_write((p)->iobase + (o), v)
|
||||
|
||||
/* Write/Read Switch global registers */
|
||||
#define netc_glb_rd(r, o) netc_read((r)->global + (o))
|
||||
#define netc_glb_wr(r, o, v) netc_write((r)->global + (o), v)
|
||||
|
||||
static inline bool is_netc_pseudo_port(struct netc_port *np)
|
||||
{
|
||||
return np->caps.pseudo_link;
|
||||
}
|
||||
|
||||
static inline void netc_add_fdb_entry(struct netc_switch *priv,
|
||||
struct netc_fdb_entry *entry)
|
||||
{
|
||||
hlist_add_head(&entry->node, &priv->fdb_list);
|
||||
}
|
||||
|
||||
static inline void netc_del_fdb_entry(struct netc_fdb_entry *entry)
|
||||
{
|
||||
hlist_del(&entry->node);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
int netc_switch_platform_probe(struct netc_switch *priv);
|
||||
|
||||
/* ethtool APIs */
|
||||
void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_pause_stats *pause_stats);
|
||||
void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_rmon_stats *rmon_stats,
|
||||
const struct ethtool_rmon_hist_range **ranges);
|
||||
void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_eth_ctrl_stats *ctrl_stats);
|
||||
void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
|
||||
struct ethtool_eth_mac_stats *mac_stats);
|
||||
int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset);
|
||||
void netc_port_get_strings(struct dsa_switch *ds, int port,
|
||||
u32 sset, u8 *data);
|
||||
void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data);
|
||||
|
||||
#endif
|
||||
366
drivers/net/dsa/netc/netc_switch_hw.h
Normal file
366
drivers/net/dsa/netc/netc_switch_hw.h
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#ifndef _NETC_SWITCH_HW_H
|
||||
#define _NETC_SWITCH_HW_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define NETC_SWITCH_VENDOR_ID 0x1131
|
||||
#define NETC_SWITCH_DEVICE_ID 0xeef2
|
||||
|
||||
/* Definition of Switch base registers */
|
||||
#define NETC_BPCAPR 0x0008
|
||||
#define BPCAPR_NUM_BP GENMASK(7, 0)
|
||||
|
||||
#define NETC_PBPMCR0 0x0400
|
||||
#define NETC_PBPMCR1 0x0404
|
||||
|
||||
#define NETC_CBDRMR(a) (0x0800 + (a) * 0x30)
|
||||
#define NETC_CBDRBAR0(a) (0x0810 + (a) * 0x30)
|
||||
#define NETC_CBDRBAR1(a) (0x0814 + (a) * 0x30)
|
||||
#define NETC_CBDRPIR(a) (0x0818 + (a) * 0x30)
|
||||
#define NETC_CBDRCIR(a) (0x081c + (a) * 0x30)
|
||||
#define NETC_CBDRLENR(a) (0x0820 + (a) * 0x30)
|
||||
|
||||
#define NETC_SWCR 0x1018
|
||||
#define SWCR_SWID GENMASK(2, 0)
|
||||
|
||||
#define NETC_DOSL2CR 0x1220
|
||||
#define DOSL2CR_SAMEADDR BIT(0)
|
||||
#define DOSL2CR_MSAMCC BIT(1)
|
||||
|
||||
#define NETC_DOSL3CR 0x1224
|
||||
#define DOSL3CR_SAMEADDR BIT(0)
|
||||
#define DOSL3CR_IPSAMCC BIT(1)
|
||||
|
||||
/* Hash table memory capability register, the memory is shared by
|
||||
* the following tables:
|
||||
*
|
||||
* - Ingress Stream Identification table
|
||||
* - Ingress Stream Filter table
|
||||
* - VLAN Filter table
|
||||
* - FDB table
|
||||
* - L2 IPv4 Multicast Filter table
|
||||
*
|
||||
* Each hash table entry is one word in size.
|
||||
*/
|
||||
#define NETC_HTMCAPR 0x1900
|
||||
#define HTMCAPR_NUM_WORDS GENMASK(15, 0)
|
||||
|
||||
#define NETC_VFHTDECR1 0x2014
|
||||
#define NETC_VFHTDECR2 0x2018
|
||||
#define VFHTDECR2_ET_PORT(a) BIT((a))
|
||||
#define VFHTDECR2_MLO GENMASK(26, 24)
|
||||
#define VFHTDECR2_MFO GENMASK(28, 27)
|
||||
|
||||
/* Definition of Switch port registers */
|
||||
#define NETC_PCAPR 0x0000
|
||||
#define PCAPR_LINK_TYPE BIT(4)
|
||||
#define PCAPR_NUM_TC GENMASK(15, 12)
|
||||
#define PCAPR_NUM_Q GENMASK(19, 16)
|
||||
#define PCAPR_NUM_CG GENMASK(27, 24)
|
||||
#define PCAPR_TGS BIT(28)
|
||||
#define PCAPR_CBS BIT(29)
|
||||
|
||||
#define NETC_PMCAPR 0x0004
|
||||
#define PMCAPR_HD BIT(8)
|
||||
#define PMCAPR_FP GENMASK(10, 9)
|
||||
#define FP_SUPPORT 2
|
||||
|
||||
#define NETC_PCR 0x0010
|
||||
#define PCR_HDR_FMT BIT(0)
|
||||
#define PCR_NS_TAG_PORT BIT(3)
|
||||
#define PCR_L2DOSE BIT(4)
|
||||
#define PCR_L3DOSE BIT(5)
|
||||
#define PCR_TIMER_CS BIT(8)
|
||||
#define PCR_PSPEED GENMASK(29, 16)
|
||||
#define PSPEED_SET_VAL(s) FIELD_PREP(PCR_PSPEED, ((s) / 10 - 1))
|
||||
|
||||
#define NETC_PQOSMR 0x0054
|
||||
#define PQOSMR_VS BIT(0)
|
||||
#define PQOSMR_VE BIT(1)
|
||||
#define PQOSMR_DDR GENMASK(3, 2)
|
||||
#define PQOSMR_DIPV GENMASK(6, 4)
|
||||
#define PQOSMR_VQMP GENMASK(19, 16)
|
||||
#define PQOSMR_QVMP GENMASK(23, 20)
|
||||
|
||||
#define NETC_PIPFCR 0x0084
|
||||
#define PIPFCR_EN BIT(0)
|
||||
|
||||
#define NETC_POR 0x100
|
||||
#define POR_TXDIS BIT(0)
|
||||
#define POR_RXDIS BIT(1)
|
||||
|
||||
#define NETC_PSR 0x104
|
||||
#define PSR_TX_BUSY BIT(0)
|
||||
#define PSR_RX_BUSY BIT(1)
|
||||
|
||||
#define NETC_PTGSLACR 0x130
|
||||
|
||||
#define NETC_PRXDCR 0x1c0
|
||||
#define NETC_PRXDCRRR 0x1c4
|
||||
#define NETC_PRXDCRR0 0x1c8
|
||||
#define NETC_PRXDCRR1 0x1cc
|
||||
#define NETC_PTXDCR 0x1e0
|
||||
|
||||
#define NETC_PTCTMSDUR(a) (0x208 + (a) * 0x20)
|
||||
#define PTCTMSDUR_MAXSDU GENMASK(15, 0)
|
||||
#define PTCTMSDUR_SDU_TYPE GENMASK(17, 16)
|
||||
#define SDU_TYPE_PPDU 0
|
||||
#define SDU_TYPE_MPDU 1
|
||||
#define SDU_TYPE_MSDU 2
|
||||
|
||||
#define NETC_PSDFTCR 0x4c4
|
||||
#define NETC_PSDFDDCR 0x4c8
|
||||
|
||||
#define NETC_BPCR 0x500
|
||||
#define BPCR_DYN_LIMIT GENMASK(15, 0)
|
||||
#define BPCR_MLO GENMASK(22, 20)
|
||||
#define BPCR_UUCASTE BIT(24)
|
||||
#define BPCR_UMCASTE BIT(25)
|
||||
#define BPCR_MCASTE BIT(26)
|
||||
#define BPCR_BCASTE BIT(27)
|
||||
#define BPCR_STAMVD BIT(28)
|
||||
#define BPCR_SRCPRND BIT(29)
|
||||
|
||||
/* MAC learning options, see BPCR[MLO], VFHTDECR2[MLO] and
|
||||
* VLAN Filter Table CFGE_DATA[MLO]
|
||||
*/
|
||||
enum netc_mlo {
|
||||
MLO_NOT_OVERRIDE = 0,
|
||||
MLO_DISABLE,
|
||||
MLO_HW,
|
||||
MLO_SW_SEC,
|
||||
MLO_SW_UNSEC,
|
||||
MLO_DISABLE_SMAC,
|
||||
};
|
||||
|
||||
/* MAC forwarding options, see VFHTDECR2[MFO] and VLAN
|
||||
* Filter Table CFGE_DATA[MFO]
|
||||
*/
|
||||
enum netc_mfo {
|
||||
MFO_NO_FDB_LOOKUP = 1,
|
||||
MFO_NO_MATCH_FLOOD,
|
||||
MFO_NO_MATCH_DISCARD,
|
||||
};
|
||||
|
||||
#define NETC_BPDVR 0x510
|
||||
#define BPDVR_VID GENMASK(11, 0)
|
||||
#define BPDVR_DEI BIT(12)
|
||||
#define BPDVR_PCP GENMASK(15, 13)
|
||||
#define BPDVR_TPID BIT(16)
|
||||
#define BPDVR_RXTAGA GENMASK(23, 20)
|
||||
#define BPDVR_RXVAM BIT(24)
|
||||
#define BPDVR_TXTAGA GENMASK(26, 25)
|
||||
|
||||
#define NETC_BPSTGSR 0x520
|
||||
|
||||
enum netc_stg_stage {
|
||||
NETC_STG_STATE_DISABLED = 0,
|
||||
NETC_STG_STATE_LEARNING,
|
||||
NETC_STG_STATE_FORWARDING,
|
||||
};
|
||||
|
||||
#define NETC_BPDCR 0x580
|
||||
|
||||
/* Definition of Switch ethernet MAC port registers */
|
||||
#define NETC_PMAC_OFFSET 0x400
|
||||
#define NETC_PM_CMD_CFG(a) (0x1008 + (a) * 0x400)
|
||||
#define PM_CMD_CFG_TX_EN BIT(0)
|
||||
#define PM_CMD_CFG_RX_EN BIT(1)
|
||||
#define PM_CMD_CFG_PAUSE_IGN BIT(8)
|
||||
|
||||
#define NETC_PM_MAXFRM(a) (0x1014 + (a) * 0x400)
|
||||
#define PM_MAXFRAM GENMASK(15, 0)
|
||||
|
||||
#define NETC_PM_IEVENT(a) (0x1040 + (a) * 0x400)
|
||||
#define PM_IEVENT_TX_EMPTY BIT(5)
|
||||
#define PM_IEVENT_RX_EMPTY BIT(6)
|
||||
|
||||
#define NETC_PM_PAUSE_QUANTA(a) (0x1054 + (a) * 0x400)
|
||||
#define NETC_PM_PAUSE_THRESH(a) (0x1064 + (a) * 0x400)
|
||||
|
||||
#define NETC_PM_IF_MODE(a) (0x1300 + (a) * 0x400)
|
||||
#define PM_IF_MODE_IFMODE GENMASK(2, 0)
|
||||
#define IFMODE_MII 1
|
||||
#define IFMODE_RMII 3
|
||||
#define IFMODE_RGMII 4
|
||||
#define IFMODE_SGMII 5
|
||||
#define PM_IF_MODE_REVMII BIT(3)
|
||||
#define PM_IF_MODE_M10 BIT(4)
|
||||
#define PM_IF_MODE_HD BIT(6)
|
||||
#define PM_IF_MODE_SSP GENMASK(14, 13)
|
||||
#define SSP_100M 0
|
||||
#define SSP_10M 1
|
||||
#define SSP_1G 2
|
||||
|
||||
/* Port MAC 0/1 Receive Ethernet Octets Counter */
|
||||
#define NETC_PM_REOCT(a) (0x1100 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Octets Counter */
|
||||
#define NETC_PM_ROCT(a) (0x1108 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Alignment Error Counter Register */
|
||||
#define NETC_PM_RALN(a) (0x1110 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Valid Pause Frame Counter */
|
||||
#define NETC_PM_RXPF(a) (0x1118 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Frame Counter */
|
||||
#define NETC_PM_RFRM(a) (0x1120 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
|
||||
#define NETC_PM_RFCS(a) (0x1128 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive VLAN Frame Counter */
|
||||
#define NETC_PM_RVLAN(a) (0x1130 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Frame Error Counter */
|
||||
#define NETC_PM_RERR(a) (0x1138 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Unicast Frame Counter */
|
||||
#define NETC_PM_RUCA(a) (0x1140 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Multicast Frame Counter */
|
||||
#define NETC_PM_RMCA(a) (0x1148 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Broadcast Frame Counter */
|
||||
#define NETC_PM_RBCA(a) (0x1150 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Dropped Packets Counter */
|
||||
#define NETC_PM_RDRP(a) (0x1158 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Packets Counter */
|
||||
#define NETC_PM_RPKT(a) (0x1160 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Undersized Packet Counter */
|
||||
#define NETC_PM_RUND(a) (0x1168 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 64-Octet Packet Counter */
|
||||
#define NETC_PM_R64(a) (0x1170 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 65 to 127-Octet Packet Counter */
|
||||
#define NETC_PM_R127(a) (0x1178 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 128 to 255-Octet Packet Counter */
|
||||
#define NETC_PM_R255(a) (0x1180 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 256 to 511-Octet Packet Counter */
|
||||
#define NETC_PM_R511(a) (0x1188 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 512 to 1023-Octet Packet Counter */
|
||||
#define NETC_PM_R1023(a) (0x1190 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 1024 to 1522-Octet Packet Counter */
|
||||
#define NETC_PM_R1522(a) (0x1198 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive 1523 to Max-Octet Packet Counter */
|
||||
#define NETC_PM_R1523X(a) (0x11a0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Oversized Packet Counter */
|
||||
#define NETC_PM_ROVR(a) (0x11a8 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Jabber Packet Counter */
|
||||
#define NETC_PM_RJBR(a) (0x11b0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Fragment Packet Counter */
|
||||
#define NETC_PM_RFRG(a) (0x11b8 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Control Packet Counter */
|
||||
#define NETC_PM_RCNP(a) (0x11c0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Receive Dropped Not Truncated Packets Counter */
|
||||
#define NETC_PM_RDRNTP(a) (0x11c8 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Ethernet Octets Counter */
|
||||
#define NETC_PM_TEOCT(a) (0x1200 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Octets Counter */
|
||||
#define NETC_PM_TOCT(a) (0x1208 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Excessive Deferral Packet Counter */
|
||||
#define NETC_PM_TEDFR(a) (0x1210 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Valid Pause Frame Counter */
|
||||
#define NETC_PM_TXPF(a) (0x1218 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Frame Counter */
|
||||
#define NETC_PM_TFRM(a) (0x1220 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */
|
||||
#define NETC_PM_TFCS(a) (0x1228 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit VLAN Frame Counter */
|
||||
#define NETC_PM_TVLAN(a) (0x1230 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Frame Error Counter */
|
||||
#define NETC_PM_TERR(a) (0x1238 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Unicast Frame Counter */
|
||||
#define NETC_PM_TUCA(a) (0x1240 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Multicast Frame Counter */
|
||||
#define NETC_PM_TMCA(a) (0x1248 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Broadcast Frame Counter */
|
||||
#define NETC_PM_TBCA(a) (0x1250 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Packets Counter */
|
||||
#define NETC_PM_TPKT(a) (0x1260 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Undersized Packet Counter */
|
||||
#define NETC_PM_TUND(a) (0x1268 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 64-Octet Packet Counter */
|
||||
#define NETC_PM_T64(a) (0x1270 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 65 to 127-Octet Packet Counter */
|
||||
#define NETC_PM_T127(a) (0x1278 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 128 to 255-Octet Packet Counter */
|
||||
#define NETC_PM_T255(a) (0x1280 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 256 to 511-Octet Packet Counter */
|
||||
#define NETC_PM_T511(a) (0x1288 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 512 to 1023-Octet Packet Counter */
|
||||
#define NETC_PM_T1023(a) (0x1290 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 1024 to 1522-Octet Packet Counter */
|
||||
#define NETC_PM_T1522(a) (0x1298 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit 1523 to TX_MTU-Octet Packet Counter */
|
||||
#define NETC_PM_T1523X(a) (0x12a0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Control Packet Counter */
|
||||
#define NETC_PM_TCNP(a) (0x12c0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Deferred Packet Counter */
|
||||
#define NETC_PM_TDFR(a) (0x12d0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Multiple Collisions Counter */
|
||||
#define NETC_PM_TMCOL(a) (0x12d8 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Single Collision */
|
||||
#define NETC_PM_TSCOL(a) (0x12e0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Late Collision Counter */
|
||||
#define NETC_PM_TLCOL(a) (0x12e8 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Excessive Collisions Counter */
|
||||
#define NETC_PM_TECOL(a) (0x12f0 + (a) * 0x400)
|
||||
|
||||
/* Port MAC 0/1 Transmit Invalid Octets Counter */
|
||||
#define NETC_PM_TIOCT(a) (0x12f8 + (a) * 0x400)
|
||||
|
||||
#define NETC_PEMDIOCR 0x1c00
|
||||
#define NETC_EMDIO_BASE NETC_PEMDIOCR
|
||||
|
||||
/* Definition of global registers (read only) */
|
||||
#define NETC_IPBRR0 0x0bf8
|
||||
#define IPBRR0_IP_REV GENMASK(15, 0)
|
||||
|
||||
#endif
|
||||
|
|
@ -261,40 +261,108 @@ static int imx94_link_config(struct netc_blk_ctrl *priv,
|
|||
}
|
||||
|
||||
static int imx94_enetc_link_config(struct netc_blk_ctrl *priv,
|
||||
struct device_node *np)
|
||||
struct device_node *np,
|
||||
bool *enetc0_en)
|
||||
{
|
||||
int link_id = imx94_enetc_get_link_id(np);
|
||||
|
||||
if (link_id < 0)
|
||||
return link_id;
|
||||
|
||||
if (link_id == IMX94_ENETC0_LINK && of_device_is_available(np))
|
||||
*enetc0_en = true;
|
||||
|
||||
return imx94_link_config(priv, np, link_id);
|
||||
}
|
||||
|
||||
static int imx94_switch_link_config(struct netc_blk_ctrl *priv,
|
||||
struct device_node *np,
|
||||
bool *swp2_en)
|
||||
{
|
||||
struct device_node *ports;
|
||||
u32 port_id;
|
||||
int err = 0;
|
||||
|
||||
ports = of_get_child_by_name(np, "ethernet-ports");
|
||||
if (!ports)
|
||||
return -ENODEV;
|
||||
|
||||
/* The switch may be owned by a guest OS, in this case, the switch
|
||||
* node in the host OS will be disabled, but we still hope that the
|
||||
* host OS could do some configurations for the switch, as the
|
||||
* netc_blk_ctrl is owned by host OS. So of_device_is_available()
|
||||
* is not needed here.
|
||||
*/
|
||||
for_each_available_child_of_node_scoped(ports, child) {
|
||||
if (of_property_read_u32(child, "reg", &port_id) < 0) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (port_id) {
|
||||
case 0 ... 2: /* External ports */
|
||||
err = imx94_link_config(priv, child, port_id);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
if (port_id == 2)
|
||||
*swp2_en = true;
|
||||
|
||||
break;
|
||||
case 3: /* CPU port */
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
of_node_put(ports);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int imx94_netcmix_init(struct platform_device *pdev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
bool enetc0_en = false, swp2_en = false;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
for_each_child_of_node_scoped(child, gchild) {
|
||||
if (!of_device_is_compatible(gchild, "pci1131,e101"))
|
||||
continue;
|
||||
|
||||
err = imx94_enetc_link_config(priv, gchild);
|
||||
if (err)
|
||||
return err;
|
||||
if (of_device_is_compatible(gchild, "pci1131,e101")) {
|
||||
err = imx94_enetc_link_config(priv, gchild,
|
||||
&enetc0_en);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (of_device_is_compatible(gchild,
|
||||
"pci1131,eef2")) {
|
||||
err = imx94_switch_link_config(priv, gchild,
|
||||
&swp2_en);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ENETC 0 and switch port 2 share the same parallel interface.
|
||||
* Currently, the switch is not supported, so this interface is
|
||||
* used by ENETC 0 by default.
|
||||
if (enetc0_en && swp2_en) {
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot enable swp2 and enetc0 at the same time\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* ENETC 0 and switch port 2 share the same parallel interface, they
|
||||
* cannot be enabled at the same time. The interface is set for the
|
||||
* ENETC 0 by default unless the switch port 2 is enabled in the DTS.
|
||||
*/
|
||||
val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL);
|
||||
val |= MAC2_MAC3_SEL;
|
||||
if (!swp2_en)
|
||||
val |= MAC2_MAC3_SEL;
|
||||
else
|
||||
val &= ~MAC2_MAC3_SEL;
|
||||
netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val);
|
||||
|
||||
return 0;
|
||||
|
|
@ -610,6 +678,78 @@ static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int imx94_ierb_enetc_init(struct netc_blk_ctrl *priv,
|
||||
struct device_node *np,
|
||||
u32 phy_mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = imx94_enetc_update_tid(priv, np);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return imx94_enetc_mdio_phyaddr_config(priv, np, phy_mask);
|
||||
}
|
||||
|
||||
static int imx94_switch_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
|
||||
struct device_node *np,
|
||||
u32 port_id, u32 phy_mask)
|
||||
{
|
||||
int addr;
|
||||
|
||||
/* The switch has 3 external ports at most */
|
||||
if (port_id > 2)
|
||||
return 0;
|
||||
|
||||
addr = netc_get_phy_addr(np);
|
||||
if (addr < 0) {
|
||||
if (addr == -ENODEV)
|
||||
return 0;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
if (phy_mask & BIT(addr)) {
|
||||
dev_err(&priv->pdev->dev,
|
||||
"Found same PHY address in EMDIO and switch node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
netc_reg_write(priv->ierb, IERB_LBCR(port_id),
|
||||
LBCR_MDIO_PHYAD_PRTAD(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx94_ierb_switch_init(struct netc_blk_ctrl *priv,
|
||||
struct device_node *np,
|
||||
u32 phy_mask)
|
||||
{
|
||||
struct device_node *ports;
|
||||
u32 port_id;
|
||||
int err = 0;
|
||||
|
||||
ports = of_get_child_by_name(np, "ethernet-ports");
|
||||
if (!ports)
|
||||
return -ENODEV;
|
||||
|
||||
for_each_available_child_of_node_scoped(ports, child) {
|
||||
err = of_property_read_u32(child, "reg", &port_id);
|
||||
if (err)
|
||||
goto end;
|
||||
|
||||
err = imx94_switch_mdio_phyaddr_config(priv, child,
|
||||
port_id, phy_mask);
|
||||
if (err)
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
of_node_put(ports);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int imx94_ierb_init(struct platform_device *pdev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
|
||||
|
|
@ -625,17 +765,18 @@ static int imx94_ierb_init(struct platform_device *pdev)
|
|||
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
for_each_child_of_node_scoped(child, gchild) {
|
||||
if (!of_device_is_compatible(gchild, "pci1131,e101"))
|
||||
continue;
|
||||
|
||||
err = imx94_enetc_update_tid(priv, gchild);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = imx94_enetc_mdio_phyaddr_config(priv, gchild,
|
||||
phy_mask);
|
||||
if (err)
|
||||
return err;
|
||||
if (of_device_is_compatible(gchild, "pci1131,e101")) {
|
||||
err = imx94_ierb_enetc_init(priv, gchild,
|
||||
phy_mask);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (of_device_is_compatible(gchild,
|
||||
"pci1131,eef2")) {
|
||||
err = imx94_ierb_switch_init(priv, gchild,
|
||||
phy_mask);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* NETC NTMP (NETC Table Management Protocol) 2.0 Library
|
||||
* Copyright 2025 NXP
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
|
|
@ -21,11 +21,23 @@
|
|||
/* Define NTMP Table ID */
|
||||
#define NTMP_MAFT_ID 1
|
||||
#define NTMP_RSST_ID 3
|
||||
#define NTMP_IPFT_ID 13
|
||||
#define NTMP_FDBT_ID 15
|
||||
#define NTMP_VFT_ID 18
|
||||
#define NTMP_BPT_ID 41
|
||||
|
||||
/* Generic Update Actions for most tables */
|
||||
#define NTMP_GEN_UA_CFGEU BIT(0)
|
||||
#define NTMP_GEN_UA_STSEU BIT(1)
|
||||
|
||||
/* Specific Update Actions for some tables */
|
||||
#define BPT_UA_BPSEU BIT(1)
|
||||
|
||||
/* Query Action: 0: Full query. 1: Query entry ID, the fields after entry
|
||||
* ID are not returned.
|
||||
*/
|
||||
#define NTMP_QA_ENTRY_ID 1
|
||||
|
||||
#define NTMP_ENTRY_ID_SIZE 4
|
||||
#define RSST_ENTRY_NUM 64
|
||||
#define RSST_STSE_DATA_SIZE(n) ((n) * 8)
|
||||
|
|
@ -134,11 +146,16 @@ static void ntmp_clean_cbdr(struct netc_cbdr *cbdr)
|
|||
static void ntmp_select_and_lock_cbdr(struct ntmp_user *user,
|
||||
struct netc_cbdr **cbdr)
|
||||
{
|
||||
/* Currently only ENETC is supported, and it has only one command
|
||||
* BD ring.
|
||||
*/
|
||||
*cbdr = &user->ring[0];
|
||||
for (int i = 0; i < user->cbdr_num; i++) {
|
||||
*cbdr = &user->ring[i];
|
||||
if (mutex_trylock(&(*cbdr)->ring_lock))
|
||||
return;
|
||||
}
|
||||
|
||||
/* If all command BD rings are locked, we need to select one of
|
||||
* them and wait for it.
|
||||
*/
|
||||
*cbdr = &user->ring[raw_smp_processor_id() % user->cbdr_num];
|
||||
mutex_lock(&(*cbdr)->ring_lock);
|
||||
}
|
||||
|
||||
|
|
@ -260,6 +277,14 @@ static const char *ntmp_table_name(int tbl_id)
|
|||
return "MAC Address Filter Table";
|
||||
case NTMP_RSST_ID:
|
||||
return "RSS Table";
|
||||
case NTMP_IPFT_ID:
|
||||
return "Ingress Port Filter Table";
|
||||
case NTMP_FDBT_ID:
|
||||
return "FDB Table";
|
||||
case NTMP_VFT_ID:
|
||||
return "VLAN Filter Table";
|
||||
case NTMP_BPT_ID:
|
||||
return "Buffer Pool Table";
|
||||
default:
|
||||
return "Unknown Table";
|
||||
}
|
||||
|
|
@ -496,5 +521,361 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_rsst_query_entry);
|
||||
|
||||
/**
|
||||
* ntmp_ipft_add_entry - add an entry into the ingress port filter table
|
||||
* @user: target ntmp_user struct
|
||||
* @entry: the entry data, entry->cfge (configuration element data) and
|
||||
* entry->keye (key element data) are used as input. Since the entry ID
|
||||
* is assigned by the hardware, so entry->entry_id is a returned value
|
||||
* for the driver to use, the driver can update/delete/query the entry
|
||||
* based on the entry_id.
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_ipft_add_entry(struct ntmp_user *user,
|
||||
struct ipft_entry_data *entry)
|
||||
{
|
||||
struct ipft_resp_query *resp;
|
||||
struct ipft_req_ua *req;
|
||||
struct netc_swcbd swcbd;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
u32 len;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*resp);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Note that NTMP_GEN_UA_STSEU is used to reset the statistics of
|
||||
* the entry. The STSE_DATA is not present in the request data for
|
||||
* 'Add' operation.
|
||||
*/
|
||||
ntmp_fill_crd(&req->crd, user->tbl.ipft_ver, NTMP_QA_ENTRY_ID,
|
||||
NTMP_GEN_UA_CFGEU | NTMP_GEN_UA_STSEU);
|
||||
req->ak.keye = entry->keye;
|
||||
req->cfge = entry->cfge;
|
||||
|
||||
len = NTMP_LEN(sizeof(*req), swcbd.size);
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_IPFT_ID,
|
||||
NTMP_CMD_AQ, NTMP_AM_TERNARY_KEY);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err) {
|
||||
dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
|
||||
ntmp_table_name(NTMP_IPFT_ID), ERR_PTR(err));
|
||||
|
||||
goto unlock_cbdr;
|
||||
}
|
||||
|
||||
resp = (struct ipft_resp_query *)req;
|
||||
entry->entry_id = le32_to_cpu(resp->entry_id);
|
||||
|
||||
unlock_cbdr:
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_ipft_add_entry);
|
||||
|
||||
/**
|
||||
* ntmp_ipft_delete_entry - delete a specified ingress port filter table entry
|
||||
* @user: target ntmp_user struct
|
||||
* @entry_id: the specified ID of the ingress port filter table entry
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id)
|
||||
{
|
||||
u32 req_len = sizeof(struct ipft_req_qd);
|
||||
|
||||
return ntmp_delete_entry_by_id(user, NTMP_IPFT_ID,
|
||||
user->tbl.ipft_ver,
|
||||
entry_id, req_len,
|
||||
NTMP_STATUS_RESP_LEN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_ipft_delete_entry);
|
||||
|
||||
/**
|
||||
* ntmp_fdbt_add_entry - add an entry into the FDB table
|
||||
* @user: target ntmp_user struct
|
||||
* @entry_id: returned value, the entry ID of the new added entry
|
||||
* @keye: key element data
|
||||
* @cfge: configuration element data
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
|
||||
const struct fdbt_keye_data *keye,
|
||||
const struct fdbt_cfge_data *cfge)
|
||||
{
|
||||
struct fdbt_resp_query *resp;
|
||||
struct fdbt_req_ua *req;
|
||||
struct netc_swcbd swcbd;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
u32 len;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*req);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Request data */
|
||||
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, NTMP_QA_ENTRY_ID,
|
||||
NTMP_GEN_UA_CFGEU);
|
||||
req->ak.exact.keye = *keye;
|
||||
req->cfge = *cfge;
|
||||
|
||||
len = NTMP_LEN(swcbd.size, sizeof(*resp));
|
||||
/* The entry ID is allotted by hardware, so we need to perform
|
||||
* a query action after the add action to get the entry ID from
|
||||
* hardware.
|
||||
*/
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
||||
NTMP_CMD_AQ, NTMP_AM_EXACT_KEY);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err) {
|
||||
dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
|
||||
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
||||
goto unlock_cbdr;
|
||||
}
|
||||
|
||||
if (entry_id) {
|
||||
resp = (struct fdbt_resp_query *)req;
|
||||
*entry_id = le32_to_cpu(resp->entry_id);
|
||||
}
|
||||
|
||||
unlock_cbdr:
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_fdbt_add_entry);
|
||||
|
||||
/**
|
||||
* ntmp_fdbt_update_entry - update the configuration element data of the
|
||||
* specified FDB entry
|
||||
* @user: target ntmp_user struct
|
||||
* @entry_id: the specified entry ID of the FDB table
|
||||
* @cfge: configuration element data
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
|
||||
const struct fdbt_cfge_data *cfge)
|
||||
{
|
||||
struct fdbt_req_ua *req;
|
||||
struct netc_swcbd swcbd;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
u32 len;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*req);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Request data */
|
||||
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, NTMP_GEN_UA_CFGEU);
|
||||
req->ak.eid.entry_id = cpu_to_le32(entry_id);
|
||||
req->cfge = *cfge;
|
||||
|
||||
/* Request header */
|
||||
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
||||
NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err)
|
||||
dev_err(user->dev, "Failed to update %s entry, err: %pe\n",
|
||||
ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
|
||||
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_fdbt_update_entry);
|
||||
|
||||
/**
|
||||
* ntmp_fdbt_delete_entry - delete the specified FDB entry
|
||||
* @user: target ntmp_user struct
|
||||
* @entry_id: the specified ID of the FDB entry
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id)
|
||||
{
|
||||
u32 req_len = sizeof(struct fdbt_req_qd);
|
||||
|
||||
return ntmp_delete_entry_by_id(user, NTMP_FDBT_ID,
|
||||
user->tbl.fdbt_ver,
|
||||
entry_id, req_len,
|
||||
NTMP_STATUS_RESP_LEN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_entry);
|
||||
|
||||
/**
|
||||
* ntmp_fdbt_search_port_entry - Search the FDB entry on the specified
|
||||
* port based on RESUME_ENTRY_ID
|
||||
* @user: target ntmp_user struct
|
||||
* @port: the specified switch port ID
|
||||
* @resume_entry_id: it is both an input and an output. As an input, it
|
||||
* represents the FDB entry ID to be searched. If it is a NULL entry ID,
|
||||
* it indicates that the first FDB entry for that port is being searched.
|
||||
* As an output, it represents the next FDB entry ID to be searched.
|
||||
* @entry: returned value, the response data of the searched FDB entry
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
|
||||
u32 *resume_entry_id,
|
||||
struct fdbt_entry_data *entry)
|
||||
{
|
||||
struct fdbt_resp_query *resp;
|
||||
struct fdbt_req_qd *req;
|
||||
struct netc_swcbd swcbd;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
u32 len;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*req);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Request data */
|
||||
ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
|
||||
req->ak.search.resume_eid = cpu_to_le32(*resume_entry_id);
|
||||
req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port));
|
||||
/* Match CFGE_DATA[PORT_BITMAP] field */
|
||||
req->ak.search.cfge_mc = FDBT_CFGE_MC_PORT_BITMAP;
|
||||
|
||||
/* Request header */
|
||||
len = NTMP_LEN(swcbd.size, sizeof(*resp));
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
|
||||
NTMP_CMD_QUERY, NTMP_AM_SEARCH);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err) {
|
||||
dev_err(user->dev,
|
||||
"Failed to search %s entry on port %d, err: %pe\n",
|
||||
ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err));
|
||||
goto unlock_cbdr;
|
||||
}
|
||||
|
||||
if (!cbd.resp_hdr.num_matched) {
|
||||
entry->entry_id = NTMP_NULL_ENTRY_ID;
|
||||
*resume_entry_id = NTMP_NULL_ENTRY_ID;
|
||||
goto unlock_cbdr;
|
||||
}
|
||||
|
||||
resp = (struct fdbt_resp_query *)req;
|
||||
*resume_entry_id = le32_to_cpu(resp->status);
|
||||
entry->entry_id = le32_to_cpu(resp->entry_id);
|
||||
entry->keye = resp->keye;
|
||||
entry->cfge = resp->cfge;
|
||||
entry->acte = resp->acte;
|
||||
|
||||
unlock_cbdr:
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry);
|
||||
|
||||
/**
|
||||
* ntmp_vft_add_entry - add an entry into the VLAN filter table
|
||||
* @user: target ntmp_user struct
|
||||
* @vid: VLAN ID
|
||||
* @cfge: configuration element data
|
||||
*
|
||||
* Return: 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
|
||||
const struct vft_cfge_data *cfge)
|
||||
{
|
||||
struct netc_swcbd swcbd;
|
||||
struct vft_req_ua *req;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
u32 len;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*req);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Request data */
|
||||
ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0,
|
||||
NTMP_GEN_UA_CFGEU);
|
||||
req->ak.exact.vid = cpu_to_le16(vid);
|
||||
req->cfge = *cfge;
|
||||
|
||||
/* Request header */
|
||||
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID,
|
||||
NTMP_CMD_ADD, NTMP_AM_EXACT_KEY);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err)
|
||||
dev_err(user->dev,
|
||||
"Failed to add %s entry, vid: %u, err: %pe\n",
|
||||
ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
|
||||
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
|
||||
|
||||
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
|
||||
const struct bpt_cfge_data *cfge)
|
||||
{
|
||||
struct bpt_req_update *req;
|
||||
struct netc_swcbd swcbd;
|
||||
struct netc_cbdr *cbdr;
|
||||
union netc_cbd cbd;
|
||||
int err;
|
||||
|
||||
swcbd.size = sizeof(*req);
|
||||
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Note that BPT_UA_BPSEU is used to update the BPSE_DATA of the entry,
|
||||
* which is maintained by the hardware. The BPSE_DATA is not present in
|
||||
* the request data for 'Update' operation.
|
||||
*/
|
||||
ntmp_fill_crd_eid(&req->rbe, user->tbl.bpt_ver, 0,
|
||||
NTMP_GEN_UA_CFGEU | BPT_UA_BPSEU, entry_id);
|
||||
req->cfge = *cfge;
|
||||
ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
|
||||
NTMP_BPT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
|
||||
|
||||
ntmp_select_and_lock_cbdr(user, &cbdr);
|
||||
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
|
||||
if (err)
|
||||
dev_err(user->dev,
|
||||
"Failed to update %s entry 0x%x, err: %pe\n",
|
||||
ntmp_table_name(NTMP_BPT_ID), entry_id, ERR_PTR(err));
|
||||
|
||||
ntmp_unlock_cbdr(cbdr);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ntmp_bpt_update_entry);
|
||||
|
||||
MODULE_DESCRIPTION("NXP NETC Library");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/*
|
||||
* NTMP table request and response data buffer formats
|
||||
* Copyright 2025 NXP
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#ifndef __NTMP_PRIVATE_H
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/fsl/ntmp.h>
|
||||
|
||||
#define NTMP_EID_REQ_LEN 8
|
||||
#define NTMP_STATUS_RESP_LEN 4
|
||||
#define NETC_CBDR_BD_NUM 256
|
||||
#define NETC_CBDRCIR_INDEX GENMASK(9, 0)
|
||||
#define NETC_CBDRCIR_SBE BIT(31)
|
||||
|
|
@ -30,6 +31,7 @@ union netc_cbd {
|
|||
#define NTMP_CMD_QUERY BIT(2)
|
||||
#define NTMP_CMD_ADD BIT(3)
|
||||
#define NTMP_CMD_QU (NTMP_CMD_QUERY | NTMP_CMD_UPDATE)
|
||||
#define NTMP_CMD_AQ (NTMP_CMD_ADD | NTMP_CMD_QUERY)
|
||||
u8 access_method;
|
||||
#define NTMP_ACCESS_METHOD GENMASK(7, 4)
|
||||
#define NTMP_AM_ENTRY_ID 0
|
||||
|
|
@ -97,4 +99,122 @@ struct rsst_req_update {
|
|||
u8 groups[];
|
||||
};
|
||||
|
||||
/* Ingress Port Filter Table Response Data Buffer Format of Query action */
|
||||
struct ipft_resp_query {
|
||||
__le32 status;
|
||||
__le32 entry_id;
|
||||
struct ipft_keye_data keye;
|
||||
__le64 match_count; /* STSE_DATA */
|
||||
struct ipft_cfge_data cfge;
|
||||
} __packed;
|
||||
|
||||
struct ipft_ak_eid {
|
||||
__le32 entry_id;
|
||||
__le32 resv[52];
|
||||
};
|
||||
|
||||
union ipft_access_key {
|
||||
struct ipft_ak_eid eid;
|
||||
struct ipft_keye_data keye;
|
||||
};
|
||||
|
||||
/* Ingress Port Filter Table Request Data Buffer Format of Update and
|
||||
* Add actions
|
||||
*/
|
||||
struct ipft_req_ua {
|
||||
struct ntmp_cmn_req_data crd;
|
||||
union ipft_access_key ak;
|
||||
struct ipft_cfge_data cfge;
|
||||
};
|
||||
|
||||
/* Ingress Port Filter Table Request Data Buffer Format of Query and
|
||||
* Delete actions
|
||||
*/
|
||||
struct ipft_req_qd {
|
||||
struct ntmp_req_by_eid rbe;
|
||||
__le32 resv[52];
|
||||
};
|
||||
|
||||
/* Access Key Format of FDB Table */
|
||||
struct fdbt_ak_eid {
|
||||
__le32 entry_id;
|
||||
__le32 resv[7];
|
||||
};
|
||||
|
||||
struct fdbt_ak_exact {
|
||||
struct fdbt_keye_data keye;
|
||||
__le32 resv[5];
|
||||
};
|
||||
|
||||
struct fdbt_ak_search {
|
||||
__le32 resume_eid;
|
||||
struct fdbt_keye_data keye;
|
||||
struct fdbt_cfge_data cfge;
|
||||
u8 acte;
|
||||
u8 keye_mc;
|
||||
#define FDBT_KEYE_MAC GENMASK(1, 0)
|
||||
u8 cfge_mc;
|
||||
#define FDBT_CFGE_MC GENMASK(2, 0)
|
||||
#define FDBT_CFGE_MC_ANY 0
|
||||
#define FDBT_CFGE_MC_DYNAMIC 1
|
||||
#define FDBT_CFGE_MC_PORT_BITMAP 2
|
||||
#define FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP 3
|
||||
u8 acte_mc;
|
||||
#define FDBT_ACTE_MC BIT(0)
|
||||
};
|
||||
|
||||
union fdbt_access_key {
|
||||
struct fdbt_ak_eid eid;
|
||||
struct fdbt_ak_exact exact;
|
||||
struct fdbt_ak_search search;
|
||||
};
|
||||
|
||||
/* FDB Table Request Data Buffer Format of Update and Add actions */
|
||||
struct fdbt_req_ua {
|
||||
struct ntmp_cmn_req_data crd;
|
||||
union fdbt_access_key ak;
|
||||
struct fdbt_cfge_data cfge;
|
||||
};
|
||||
|
||||
/* FDB Table Request Data Buffer Format of Query and Delete actions */
|
||||
struct fdbt_req_qd {
|
||||
struct ntmp_cmn_req_data crd;
|
||||
union fdbt_access_key ak;
|
||||
};
|
||||
|
||||
/* FDB Table Response Data Buffer Format of Query action */
|
||||
struct fdbt_resp_query {
|
||||
__le32 status;
|
||||
__le32 entry_id;
|
||||
struct fdbt_keye_data keye;
|
||||
struct fdbt_cfge_data cfge;
|
||||
u8 acte;
|
||||
u8 resv[3];
|
||||
};
|
||||
|
||||
/* Access Key Format of VLAN Filter Table */
|
||||
struct vft_ak_exact {
|
||||
__le16 vid; /* bit0~11: VLAN ID, other bits are reserved */
|
||||
__le16 resv;
|
||||
};
|
||||
|
||||
union vft_access_key {
|
||||
__le32 entry_id; /* entry_id match */
|
||||
struct vft_ak_exact exact;
|
||||
__le32 resume_entry_id; /* search */
|
||||
};
|
||||
|
||||
/* VLAN Filter Table Request Data Buffer Format of Update and Add actions */
|
||||
struct vft_req_ua {
|
||||
struct ntmp_cmn_req_data crd;
|
||||
union vft_access_key ak;
|
||||
struct vft_cfge_data cfge;
|
||||
};
|
||||
|
||||
/* Buffer Pool Table Request Data Buffer Format of Update action */
|
||||
struct bpt_req_update {
|
||||
struct ntmp_req_by_eid rbe;
|
||||
struct bpt_cfge_data cfge;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
14
include/linux/dsa/tag_netc.h
Normal file
14
include/linux/dsa/tag_netc.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#ifndef __NET_DSA_TAG_NETC_H
|
||||
#define __NET_DSA_TAG_NETC_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/dsa.h>
|
||||
|
||||
#define NETC_TAG_MAX_LEN 14
|
||||
|
||||
#endif
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#define __NETC_GLOBAL_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
|
||||
static inline u32 netc_read(void __iomem *reg)
|
||||
{
|
||||
|
|
@ -16,4 +17,9 @@ static inline void netc_write(void __iomem *reg, u32 val)
|
|||
iowrite32(val, reg);
|
||||
}
|
||||
|
||||
static inline u64 netc_read64(void __iomem *reg)
|
||||
{
|
||||
return ioread64(reg);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
||||
/* Copyright 2025 NXP */
|
||||
/* Copyright 2025-2026 NXP */
|
||||
#ifndef __NETC_NTMP_H
|
||||
#define __NETC_NTMP_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#define NTMP_NULL_ENTRY_ID 0xffffffffU
|
||||
#define IPFT_MAX_PLD_LEN 24
|
||||
|
||||
struct maft_keye_data {
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
__le16 resv;
|
||||
|
|
@ -29,6 +32,10 @@ struct netc_cbdr_regs {
|
|||
struct netc_tbl_vers {
|
||||
u8 maft_ver;
|
||||
u8 rsst_ver;
|
||||
u8 fdbt_ver;
|
||||
u8 vft_ver;
|
||||
u8 bpt_ver;
|
||||
u8 ipft_ver;
|
||||
};
|
||||
|
||||
struct netc_swcbd {
|
||||
|
|
@ -68,6 +75,167 @@ struct maft_entry_data {
|
|||
struct maft_cfge_data cfge;
|
||||
};
|
||||
|
||||
struct ipft_pld_byte {
|
||||
u8 data;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct ipft_keye_data {
|
||||
__le16 precedence;
|
||||
__le16 resv0[3];
|
||||
__le16 frm_attr_flags;
|
||||
#define IPFT_FAF_OVLAN BIT(2)
|
||||
#define IPFT_FAF_IVLAN BIT(3)
|
||||
#define IPFT_FAF_IP_HDR BIT(7)
|
||||
#define IPFT_FAF_IP_VER6 BIT(8)
|
||||
#define IPFT_FAF_L4_CODE GENMASK(11, 10)
|
||||
#define IPFT_FAF_TCP_HDR 1
|
||||
#define IPFT_FAF_UDP_HDR 2
|
||||
#define IPFT_FAF_SCTP_HDR 3
|
||||
#define IPFT_FAF_WOL_MAGIC BIT(12)
|
||||
__le16 frm_attr_flags_mask;
|
||||
__le16 dscp;
|
||||
#define IPFT_DSCP GENMASK(5, 0)
|
||||
#define IPFT_DSCP_MASK GENMASK(11, 6)
|
||||
#define IPFT_DSCP_MASK_ALL 0x3f
|
||||
__le16 src_port; /* This field is reserved for ENETC */
|
||||
#define IPFT_SRC_PORT GENMASK(4, 0)
|
||||
#define IPFT_SRC_PORT_MASK GENMASK(9, 5)
|
||||
#define IPFT_SRC_PORT_MASK_ALL 0x1f
|
||||
__be16 outer_vlan_tci;
|
||||
__be16 outer_vlan_tci_mask;
|
||||
u8 dmac[ETH_ALEN];
|
||||
u8 dmac_mask[ETH_ALEN];
|
||||
u8 smac[ETH_ALEN];
|
||||
u8 smac_mask[ETH_ALEN];
|
||||
__be16 inner_vlan_tci;
|
||||
__be16 inner_vlan_tci_mask;
|
||||
__be16 ethertype;
|
||||
__be16 ethertype_mask;
|
||||
u8 ip_protocol;
|
||||
u8 ip_protocol_mask;
|
||||
__le16 resv1[7];
|
||||
__be32 ip_src[4];
|
||||
__le32 resv2[2];
|
||||
__be32 ip_src_mask[4];
|
||||
__be16 l4_src_port;
|
||||
__be16 l4_src_port_mask;
|
||||
__le32 resv3;
|
||||
__be32 ip_dst[4];
|
||||
__le32 resv4[2];
|
||||
__be32 ip_dst_mask[4];
|
||||
__be16 l4_dst_port;
|
||||
__be16 l4_dst_port_mask;
|
||||
__le32 resv5;
|
||||
struct ipft_pld_byte byte[IPFT_MAX_PLD_LEN];
|
||||
};
|
||||
|
||||
struct ipft_cfge_data {
|
||||
__le32 cfg;
|
||||
#define IPFT_IPV GENMASK(3, 0)
|
||||
#define IPFT_OIPV BIT(4)
|
||||
#define IPFT_DR GENMASK(6, 5)
|
||||
#define IPFT_ODR BIT(7)
|
||||
#define IPFT_FLTFA GENMASK(10, 8)
|
||||
#define IPFT_FLTFA_DISCARD 0
|
||||
#define IPFT_FLTFA_PERMIT 1
|
||||
/* Redirect is only for switch */
|
||||
#define IPFT_FLTFA_REDIRECT 2
|
||||
#define IPFT_IMIRE BIT(11)
|
||||
#define IPFT_WOLTE BIT(12)
|
||||
#define IPFT_FLTA GENMASK(14, 13)
|
||||
#define IPFT_FLTA_RP 1
|
||||
#define IPFT_FLTA_IS 2
|
||||
#define IPFT_FLTA_SI_BITMAP 3
|
||||
#define IPFT_RPR GENMASK(16, 15)
|
||||
#define IPFT_CTD BIT(17)
|
||||
#define IPFT_HR GENMASK(21, 18)
|
||||
#define IPFT_TIMECAPE BIT(22)
|
||||
#define IPFT_RRT BIT(23)
|
||||
#define IPFT_BL2F BIT(24)
|
||||
#define IPFT_EVMEID GENMASK(31, 28)
|
||||
__le32 flta_tgt;
|
||||
};
|
||||
|
||||
struct ipft_entry_data {
|
||||
u32 entry_id; /* hardware assigns entry ID */
|
||||
struct ipft_keye_data keye;
|
||||
struct ipft_cfge_data cfge;
|
||||
};
|
||||
|
||||
struct fdbt_keye_data {
|
||||
u8 mac_addr[ETH_ALEN]; /* big-endian */
|
||||
__le16 resv0;
|
||||
__le16 fid;
|
||||
#define FDBT_FID GENMASK(11, 0)
|
||||
__le16 resv1;
|
||||
};
|
||||
|
||||
struct fdbt_cfge_data {
|
||||
__le32 port_bitmap;
|
||||
#define FDBT_PORT_BITMAP GENMASK(23, 0)
|
||||
__le32 cfg;
|
||||
#define FDBT_OETEID GENMASK(1, 0)
|
||||
#define FDBT_EPORT GENMASK(6, 2)
|
||||
#define FDBT_IMIRE BIT(7)
|
||||
#define FDBT_CTD GENMASK(10, 9)
|
||||
#define FDBT_DYNAMIC BIT(11)
|
||||
#define FDBT_TIMECAPE BIT(12)
|
||||
__le32 et_eid;
|
||||
};
|
||||
|
||||
struct fdbt_entry_data {
|
||||
u32 entry_id;
|
||||
struct fdbt_keye_data keye;
|
||||
struct fdbt_cfge_data cfge;
|
||||
u8 acte;
|
||||
#define FDBT_ACT_CNT GENMASK(6, 0)
|
||||
#define FDBT_ACT_FLAG BIT(7)
|
||||
};
|
||||
|
||||
struct vft_cfge_data {
|
||||
__le32 bitmap_stg;
|
||||
#define VFT_PORT_MEMBERSHIP GENMASK(23, 0)
|
||||
#define VFT_STG_ID_MASK GENMASK(27, 24)
|
||||
#define VFT_STG_ID(g) FIELD_PREP(VFT_STG_ID_MASK, (g))
|
||||
__le16 fid;
|
||||
#define VFT_FID GENMASK(11, 0)
|
||||
__le16 cfg;
|
||||
#define VFT_MLO GENMASK(2, 0)
|
||||
#define VFT_MFO GENMASK(4, 3)
|
||||
#define VFT_IPMFE BIT(6)
|
||||
#define VFT_IPMFLE BIT(7)
|
||||
#define VFT_PGA BIT(8)
|
||||
#define VFT_SFDA BIT(10)
|
||||
#define VFT_OSFDA BIT(11)
|
||||
#define VFT_FDBAFSS BIT(12)
|
||||
__le32 eta_port_bitmap;
|
||||
#define VFT_ETA_PORT_BITMAP GENMASK(23, 0)
|
||||
__le32 et_eid;
|
||||
};
|
||||
|
||||
struct bpt_bpse_data {
|
||||
__le32 amount_used;
|
||||
__le32 amount_used_hwm;
|
||||
u8 bpd_fc_state;
|
||||
#define BPT_FC_STATE BIT(0)
|
||||
#define BPT_BPD BIT(1)
|
||||
} __packed;
|
||||
|
||||
struct bpt_cfge_data {
|
||||
u8 fccfg_sbpen;
|
||||
#define BPT_FC_CFG GENMASK(2, 1)
|
||||
#define BPT_FC_CFG_EN_BPFC 1
|
||||
u8 pfc_vector;
|
||||
__le16 max_thresh;
|
||||
__le16 fc_on_thresh;
|
||||
__le16 fc_off_thresh;
|
||||
__le16 sbp_thresh;
|
||||
__le16 resv;
|
||||
__le32 sbp_eid;
|
||||
__le32 fc_ports;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_NXP_NETC_LIB)
|
||||
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
|
||||
const struct netc_cbdr_regs *regs);
|
||||
|
|
@ -83,6 +251,22 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
|
|||
int count);
|
||||
int ntmp_rsst_query_entry(struct ntmp_user *user,
|
||||
u32 *table, int count);
|
||||
int ntmp_ipft_add_entry(struct ntmp_user *user,
|
||||
struct ipft_entry_data *entry);
|
||||
int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id);
|
||||
int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
|
||||
const struct fdbt_keye_data *keye,
|
||||
const struct fdbt_cfge_data *cfge);
|
||||
int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
|
||||
const struct fdbt_cfge_data *cfge);
|
||||
int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id);
|
||||
int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
|
||||
u32 *resume_entry_id,
|
||||
struct fdbt_entry_data *entry);
|
||||
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
|
||||
const struct vft_cfge_data *cfge);
|
||||
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
|
||||
const struct bpt_cfge_data *cfge);
|
||||
#else
|
||||
static inline int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
|
||||
const struct netc_cbdr_regs *regs)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ struct tc_action;
|
|||
#define DSA_TAG_PROTO_YT921X_VALUE 30
|
||||
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
|
||||
#define DSA_TAG_PROTO_MXL862_VALUE 32
|
||||
#define DSA_TAG_PROTO_NETC_VALUE 33
|
||||
|
||||
enum dsa_tag_protocol {
|
||||
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
|
||||
|
|
@ -93,6 +94,7 @@ enum dsa_tag_protocol {
|
|||
DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE,
|
||||
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
|
||||
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
|
||||
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
|
||||
};
|
||||
|
||||
struct dsa_switch;
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@
|
|||
#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
|
||||
#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */
|
||||
#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
|
||||
#define ETH_P_NXP_NETC 0xFD3A /* NXP NETC DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
|
||||
|
||||
#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value
|
||||
* then the frame is Ethernet II. Else it is 802.3 */
|
||||
|
|
|
|||
|
|
@ -125,6 +125,16 @@ 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_NETC
|
||||
tristate "Tag driver for NXP NETC switches"
|
||||
help
|
||||
Say Y or M if you want to enable support for the NXP Switch Tag (NST),
|
||||
as implemented by NXP NETC switches having version 4.3 or later. The
|
||||
switch tag is a proprietary header added to frames after the source
|
||||
MAC address, it has 3 types and each type has different subtypes, so
|
||||
its length depends on the type and subtype of the tag, the maximum
|
||||
length is 14 bytes.
|
||||
|
||||
config NET_DSA_TAG_OCELOT
|
||||
tristate "Tag driver for Ocelot family of switches, using NPI port"
|
||||
select PACKING
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
|
|||
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
|
||||
obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
|
||||
|
|
|
|||
214
net/dsa/tag_netc.c
Normal file
214
net/dsa/tag_netc.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2025-2026 NXP
|
||||
*/
|
||||
|
||||
#include <linux/dsa/tag_netc.h>
|
||||
|
||||
#include "tag.h"
|
||||
|
||||
#define NETC_NAME "nxp_netc"
|
||||
|
||||
/* Forward NXP switch tag */
|
||||
#define NETC_TAG_FORWARD 0
|
||||
|
||||
/* To_Port NXP switch tag */
|
||||
#define NETC_TAG_TO_PORT 1
|
||||
/* SubType0: No request to perform timestamping */
|
||||
#define NETC_TAG_TP_SUBTYPE0 0
|
||||
|
||||
/* To_Host NXP switch tag */
|
||||
#define NETC_TAG_TO_HOST 2
|
||||
/* SubType0: frames redirected or copied to CPU port */
|
||||
#define NETC_TAG_TH_SUBTYPE0 0
|
||||
/* SubType1: frames redirected or copied to CPU port with timestamp */
|
||||
#define NETC_TAG_TH_SUBTYPE1 1
|
||||
/* SubType2: Transmit timestamp response (two-step timestamping) */
|
||||
#define NETC_TAG_TH_SUBTYPE2 2
|
||||
|
||||
/* NETC switch tag lengths */
|
||||
#define NETC_TAG_FORWARD_LEN 6
|
||||
#define NETC_TAG_TP_SUBTYPE0_LEN 6
|
||||
#define NETC_TAG_TH_SUBTYPE0_LEN 6
|
||||
#define NETC_TAG_TH_SUBTYPE1_LEN 14
|
||||
#define NETC_TAG_TH_SUBTYPE2_LEN 14
|
||||
#define NETC_TAG_CMN_LEN 5
|
||||
|
||||
#define NETC_TAG_SUBTYPE GENMASK(3, 0)
|
||||
#define NETC_TAG_TYPE GENMASK(7, 4)
|
||||
#define NETC_TAG_QV BIT(0)
|
||||
#define NETC_TAG_IPV GENMASK(4, 2)
|
||||
#define NETC_TAG_SWITCH GENMASK(2, 0)
|
||||
#define NETC_TAG_PORT GENMASK(7, 3)
|
||||
|
||||
struct netc_tag_cmn {
|
||||
__be16 tpid;
|
||||
u8 type;
|
||||
u8 qos;
|
||||
u8 switch_port;
|
||||
} __packed;
|
||||
|
||||
static void netc_fill_common_tag(struct netc_tag_cmn *tag, u8 type,
|
||||
u8 subtype, u8 sw_id, u8 port, u8 ipv)
|
||||
{
|
||||
tag->tpid = htons(ETH_P_NXP_NETC);
|
||||
tag->type = FIELD_PREP(NETC_TAG_TYPE, type) |
|
||||
FIELD_PREP(NETC_TAG_SUBTYPE, subtype);
|
||||
tag->qos = NETC_TAG_QV | FIELD_PREP(NETC_TAG_IPV, ipv);
|
||||
tag->switch_port = FIELD_PREP(NETC_TAG_SWITCH, sw_id) |
|
||||
FIELD_PREP(NETC_TAG_PORT, port);
|
||||
}
|
||||
|
||||
static void *netc_fill_common_tp_tag(struct sk_buff *skb,
|
||||
struct net_device *ndev,
|
||||
u8 subtype, int tag_len)
|
||||
{
|
||||
struct dsa_port *dp = dsa_user_to_port(ndev);
|
||||
u16 queue = skb_get_queue_mapping(skb);
|
||||
s8 ipv = netdev_txq_to_tc(ndev, queue);
|
||||
void *tag;
|
||||
|
||||
if (unlikely(ipv < 0))
|
||||
ipv = 0;
|
||||
|
||||
skb_push(skb, tag_len);
|
||||
dsa_alloc_etype_header(skb, tag_len);
|
||||
|
||||
tag = dsa_etype_header_pos_tx(skb);
|
||||
memset(tag + NETC_TAG_CMN_LEN, 0, tag_len - NETC_TAG_CMN_LEN);
|
||||
/* As 'dsa,member' is a required property for NETC switch, the member
|
||||
* is used to specify the switch ID (thus the hardware switch ID and
|
||||
* the software switch ID are consistent), its range is 1 ~ 7. The
|
||||
* NETC switch driver will check this value, and if it is invalid,
|
||||
* the switch driver will fail the probe.
|
||||
* In addition, according to the nxp,netc-switch.yaml doc, the port
|
||||
* index will not be greater than 0xf.
|
||||
*/
|
||||
netc_fill_common_tag(tag, NETC_TAG_TO_PORT, subtype,
|
||||
dp->ds->index, dp->index, ipv);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void netc_fill_tp_tag_subtype0(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
netc_fill_common_tp_tag(skb, ndev, NETC_TAG_TP_SUBTYPE0,
|
||||
NETC_TAG_TP_SUBTYPE0_LEN);
|
||||
}
|
||||
|
||||
/* Currently only support To_Port tag, subtype 0 */
|
||||
static struct sk_buff *netc_xmit(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
netc_fill_tp_tag_subtype0(skb, ndev);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int netc_get_rx_tag_len(int type, int subtype)
|
||||
{
|
||||
/* Only NETC_TAG_TO_HOST and NETC_TAG_FORWARD are expected in RX,
|
||||
* NETC_TAG_TO_PORT is a TX switch tag that does not exist in RX.
|
||||
*/
|
||||
if (type == NETC_TAG_TO_HOST) {
|
||||
if (subtype == NETC_TAG_TH_SUBTYPE1)
|
||||
return NETC_TAG_TH_SUBTYPE1_LEN;
|
||||
else if (subtype == NETC_TAG_TH_SUBTYPE2)
|
||||
return NETC_TAG_TH_SUBTYPE2_LEN;
|
||||
else
|
||||
return NETC_TAG_TH_SUBTYPE0_LEN;
|
||||
}
|
||||
|
||||
return NETC_TAG_FORWARD_LEN;
|
||||
}
|
||||
|
||||
static struct sk_buff *netc_rcv(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
struct netc_tag_cmn *tag_cmn;
|
||||
int tag_len, sw_id, port;
|
||||
int type, subtype;
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN)))
|
||||
return NULL;
|
||||
|
||||
tag_cmn = dsa_etype_header_pos_rx(skb);
|
||||
if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) {
|
||||
dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n",
|
||||
ntohs(tag_cmn->tpid));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tag_cmn->qos & NETC_TAG_QV)
|
||||
skb->priority = FIELD_GET(NETC_TAG_IPV, tag_cmn->qos);
|
||||
|
||||
sw_id = FIELD_GET(NETC_TAG_SWITCH, tag_cmn->switch_port);
|
||||
/* ENETC VEPA switch ID (0) is not supported yet */
|
||||
if (!sw_id) {
|
||||
dev_warn_ratelimited(&ndev->dev,
|
||||
"VEPA switch ID is not supported yet\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port);
|
||||
skb->dev = dsa_conduit_find_user(ndev, sw_id, port);
|
||||
if (!skb->dev)
|
||||
return NULL;
|
||||
|
||||
type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
|
||||
subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
|
||||
if (type == NETC_TAG_FORWARD) {
|
||||
dsa_default_offload_fwd_mark(skb);
|
||||
} else if (type == NETC_TAG_TO_HOST) {
|
||||
/* Currently only subtype0 supported */
|
||||
if (subtype != NETC_TAG_TH_SUBTYPE0)
|
||||
return NULL;
|
||||
} else {
|
||||
dev_warn_ratelimited(&ndev->dev,
|
||||
"Unexpected tag type %d\n", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove Switch tag from the frame */
|
||||
tag_len = netc_get_rx_tag_len(type, subtype);
|
||||
skb_pull_rcsum(skb, tag_len);
|
||||
dsa_strip_etype_header(skb, tag_len);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto,
|
||||
int *offset)
|
||||
{
|
||||
struct netc_tag_cmn *tag_cmn = (struct netc_tag_cmn *)(skb->data - 2);
|
||||
int subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
|
||||
int type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
|
||||
int tag_len = netc_get_rx_tag_len(type, subtype);
|
||||
|
||||
/* The RX minimum frame length of the NETC switch port is 64 bytes,
|
||||
* and the frame is received by the ENETC driver. From the hardware
|
||||
* perspective, the receive buffer of RX BD is at least 128 bytes,
|
||||
* so the switch tag header is guaranteed to be in the linear region
|
||||
* of the skb.
|
||||
*/
|
||||
*offset = tag_len;
|
||||
*proto = ((__be16 *)skb->data)[(tag_len / 2) - 1];
|
||||
}
|
||||
|
||||
static const struct dsa_device_ops netc_netdev_ops = {
|
||||
.name = NETC_NAME,
|
||||
.proto = DSA_TAG_PROTO_NETC,
|
||||
.xmit = netc_xmit,
|
||||
.rcv = netc_rcv,
|
||||
.needed_headroom = NETC_TAG_MAX_LEN,
|
||||
.flow_dissect = netc_flow_dissect,
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("DSA tag driver for NXP NETC switch family");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_NAME);
|
||||
module_dsa_tag_driver(netc_netdev_ops);
|
||||
Loading…
Reference in New Issue
Block a user