Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,132 @@
# SPDX-License-Identifier: GPL-2.0
config FSL_ENETC_CORE
tristate
select NXP_NETC_LIB if NXP_NTMP
help
This module supports common functionality between the PF and VF
drivers for the NXP ENETC controller.
If compiled as module (M), the module name is fsl-enetc-core.
config NXP_ENETC_PF_COMMON
tristate
select CRC_ITU_T
help
This module supports common functionality between drivers of
different versions of NXP ENETC PF controllers.
If compiled as module (M), the module name is nxp-enetc-pf-common.
config NXP_NETC_LIB
tristate
help
This module provides common functionalities for both ENETC and NETC
Switch, such as NETC Table Management Protocol (NTMP) 2.0, common tc
flower and debugfs interfaces and so on.
config NXP_NTMP
bool
config FSL_ENETC
tristate "ENETC PF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_IERB
select FSL_ENETC_MDIO
select NXP_ENETC_PF_COMMON
select PHYLINK
select PCS_LYNX
select DIMLIB
help
This driver supports NXP ENETC gigabit ethernet controller PCIe
physical function (PF) devices, managing ENETC Ports at a privileged
level.
If compiled as module (M), the module name is fsl-enetc.
config NXP_ENETC4
tristate "ENETC4 PF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_MDIO
select NXP_ENETC_PF_COMMON
select NXP_NTMP
select PHYLINK
select DIMLIB
help
This driver supports NXP ENETC devices with major revision 4. ENETC is
as the NIC functionality in NETC, it supports virtualization/isolation
based on PCIe Single Root IO Virtualization (SR-IOV) and a full range
of TSN standards and NIC offload capabilities.
If compiled as module (M), the module name is nxp-enetc4.
config FSL_ENETC_VF
tristate "ENETC VF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_MDIO
select PHYLINK
select DIMLIB
select CRC_ITU_T
help
This driver supports NXP ENETC gigabit ethernet controller PCIe
virtual function (VF) devices enabled by the ENETC PF driver.
If compiled as module (M), the module name is fsl-enetc-vf.
config FSL_ENETC_IERB
tristate "ENETC IERB driver"
help
This driver configures the Integrated Endpoint Register Block on NXP
LS1028A.
If compiled as module (M), the module name is fsl-enetc-ierb.
config FSL_ENETC_MDIO
tristate "ENETC MDIO driver"
depends on PCI && PHYLIB
help
This driver supports NXP ENETC Central MDIO controller as a PCIe
physical function (PF) device.
If compiled as module (M), the module name is fsl-enetc-mdio.
config FSL_ENETC_PTP_CLOCK
tristate "ENETC PTP clock driver"
depends on PTP_1588_CLOCK_QORIQ && (FSL_ENETC || FSL_ENETC_VF)
default y
help
This driver adds support for using the ENETC 1588 timer
as a PTP clock. This clock is only useful if your PTP
programs are getting hardware time stamps on the PTP Ethernet
packets using the SO_TIMESTAMPING API.
If compiled as module (M), the module name is fsl-enetc-ptp.
config FSL_ENETC_QOS
bool "ENETC hardware Time-sensitive Network support"
depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS)
help
There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
/802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
enable/disable from user space via Qos commands(tc). In the kernel
side, it can be loaded by Qos driver. Currently, it is only support
taprio(802.1Qbv) and Credit Based Shaper(802.1Qbu).
config NXP_NETC_BLK_CTRL
tristate "NETC blocks control driver"
help
This driver configures Integrated Endpoint Register Block (IERB) and
Privileged Register Block (PRB) of NETC. For i.MX platforms, it also
includes the configuration of NETCMIX block.
The IERB contains registers that are used for pre-boot initialization,
debug, and non-customer configuration. The PRB controls global reset
and global error handling for NETC. The NETCMIX block is mainly used
to set MII protocol and PCS protocol of the links, it also contains
settings for some other functions.
If compiled as module (M), the module name is nxp-netc-blk-ctrl.
@@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FSL_ENETC_CORE) += fsl-enetc-core.o
fsl-enetc-core-y := enetc.o enetc_cbdr.o enetc_ethtool.o
obj-$(CONFIG_NXP_ENETC_PF_COMMON) += nxp-enetc-pf-common.o
nxp-enetc-pf-common-y := enetc_pf_common.o
nxp-enetc-pf-common-$(CONFIG_PCI_IOV) += enetc_msg.o
obj-$(CONFIG_NXP_NETC_LIB) += nxp-netc-lib.o
nxp-netc-lib-y := ntmp.o
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-y := enetc_pf.o
fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
obj-$(CONFIG_NXP_ENETC4) += nxp-enetc4.o
nxp-enetc4-y := enetc4_pf.o
nxp-enetc4-$(CONFIG_DEBUG_FS) += enetc4_debugfs.o
obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
fsl-enetc-vf-y := enetc_vf.o
obj-$(CONFIG_FSL_ENETC_IERB) += fsl-enetc-ierb.o
fsl-enetc-ierb-y := enetc_ierb.o
obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o
fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
obj-$(CONFIG_FSL_ENETC_PTP_CLOCK) += fsl-enetc-ptp.o
fsl-enetc-ptp-y := enetc_ptp.o
obj-$(CONFIG_NXP_NETC_BLK_CTRL) += nxp-netc-blk-ctrl.o
nxp-netc-blk-ctrl-y := netc_blk_ctrl.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,699 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2017-2019 NXP */
#include <linux/timer.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/dma-mapping.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/fsl/ntmp.h>
#include <linux/if_vlan.h>
#include <linux/phylink.h>
#include <linux/dim.h>
#include <net/xdp.h>
#include "enetc_hw.h"
#include "enetc4_hw.h"
#include "enetc_mailbox.h"
#define ENETC_MAC_MAXFRM_SIZE 9600
#define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \
(ETH_FCS_LEN + ETH_HLEN + VLAN_HLEN))
#define ENETC_CBD_DATA_MEM_ALIGN 64
#define ENETC_MADDR_HASH_TBL_SZ 64
enum enetc_mac_addr_type {UC, MC, MADDR_TYPE};
struct enetc_mac_filter {
union {
char mac_addr[ETH_ALEN];
DECLARE_BITMAP(mac_hash_table, ENETC_MADDR_HASH_TBL_SZ);
};
int mac_addr_cnt;
};
struct enetc_tx_swbd {
union {
struct sk_buff *skb;
struct xdp_frame *xdp_frame;
};
dma_addr_t dma;
struct page *page; /* valid only if is_xdp_tx */
u16 page_offset; /* valid only if is_xdp_tx */
u16 len;
enum dma_data_direction dir;
u8 is_dma_page:1;
u8 check_wb:1;
u8 do_twostep_tstamp:1;
u8 is_eof:1;
u8 is_xdp_tx:1;
u8 is_xdp_redirect:1;
u8 qbv_en:1;
};
struct enetc_skb_cb {
u8 flag;
bool udp;
u16 correction_off;
u16 origin_tstamp_off;
};
#define ENETC_SKB_CB(skb) ((struct enetc_skb_cb *)((skb)->cb))
struct enetc_lso_t {
bool ipv6;
bool tcp;
u8 l3_hdr_len;
u8 hdr_len; /* LSO header length */
u8 l3_start;
u16 lso_seg_size;
int total_len; /* total data length, not include LSO header */
};
#define ENETC_LSO_MAX_DATA_LEN SZ_256K
#define ENETC_RX_MAXFRM_SIZE ENETC_MAC_MAXFRM_SIZE
#define ENETC_RXB_TRUESIZE (PAGE_SIZE >> 1)
#define ENETC_RXB_PAD NET_SKB_PAD /* add extra space if needed */
#define ENETC_RXB_DMA_SIZE \
min(SKB_WITH_OVERHEAD(ENETC_RXB_TRUESIZE) - ENETC_RXB_PAD, 0xffff)
#define ENETC_RXB_DMA_SIZE_XDP \
min(SKB_WITH_OVERHEAD(ENETC_RXB_TRUESIZE) - XDP_PACKET_HEADROOM, 0xffff)
struct enetc_rx_swbd {
dma_addr_t dma;
struct page *page;
u16 page_offset;
enum dma_data_direction dir;
u16 len;
};
/* ENETC overhead: optional extension BD + 1 BD gap */
#define ENETC_TXBDS_NEEDED(val) ((val) + 2)
/* For LS1028A, max # of chained Tx BDs is 15, including head and
* extension BD.
*/
#define ENETC_MAX_SKB_FRAGS 13
/* For ENETC v4 and later versions, max # of chained Tx BDs is 63,
* including head and extension BD, but the range of MAX_SKB_FRAGS
* is 17 ~ 45, so set ENETC4_MAX_SKB_FRAGS to MAX_SKB_FRAGS.
*/
#define ENETC4_MAX_SKB_FRAGS MAX_SKB_FRAGS
#define ENETC_TXBDS_MAX_NEEDED(x) ENETC_TXBDS_NEEDED((x) + 1)
struct enetc_ring_stats {
unsigned long packets;
unsigned long bytes;
unsigned long rx_alloc_errs;
unsigned long xdp_drops;
unsigned long xdp_tx;
unsigned long xdp_tx_drops;
unsigned long xdp_redirect;
unsigned long xdp_redirect_failures;
unsigned long recycles;
unsigned long recycle_failures;
unsigned long win_drop;
};
struct enetc_xdp_data {
struct xdp_rxq_info rxq;
struct bpf_prog *prog;
int xdp_tx_in_flight;
};
#define ENETC_RX_RING_DEFAULT_SIZE 2048
#define ENETC_TX_RING_DEFAULT_SIZE 2048
#define ENETC_DEFAULT_TX_WORK (ENETC_TX_RING_DEFAULT_SIZE / 2)
struct enetc_bdr_resource {
/* Input arguments saved for teardown */
struct device *dev; /* for DMA mapping */
size_t bd_count;
size_t bd_size;
/* Resource proper */
void *bd_base; /* points to Rx or Tx BD ring */
dma_addr_t bd_dma_base;
union {
struct enetc_tx_swbd *tx_swbd;
struct enetc_rx_swbd *rx_swbd;
};
char *tso_headers;
dma_addr_t tso_headers_dma;
};
struct enetc_bdr {
struct device *dev; /* for DMA mapping */
struct net_device *ndev;
void *bd_base; /* points to Rx or Tx BD ring */
union {
void __iomem *tpir;
void __iomem *rcir;
};
u16 index;
u16 prio;
int bd_count; /* # of BDs */
int next_to_use;
int next_to_clean;
union {
struct enetc_tx_swbd *tx_swbd;
struct enetc_rx_swbd *rx_swbd;
};
union {
void __iomem *tcir; /* Tx */
int next_to_alloc; /* Rx */
};
void __iomem *idr; /* Interrupt Detect Register pointer */
int buffer_offset;
struct enetc_xdp_data xdp;
struct enetc_ring_stats stats;
dma_addr_t bd_dma_base;
u8 tsd_enable; /* Time specific departure */
bool ext_en; /* enable h/w descriptor extensions */
/* DMA buffer for TSO headers */
char *tso_headers;
dma_addr_t tso_headers_dma;
} ____cacheline_aligned_in_smp;
static inline void enetc_bdr_idx_inc(struct enetc_bdr *bdr, int *i)
{
if (unlikely(++*i == bdr->bd_count))
*i = 0;
}
static inline int enetc_bd_unused(struct enetc_bdr *bdr)
{
if (bdr->next_to_clean > bdr->next_to_use)
return bdr->next_to_clean - bdr->next_to_use - 1;
return bdr->bd_count + bdr->next_to_clean - bdr->next_to_use - 1;
}
static inline int enetc_swbd_unused(struct enetc_bdr *bdr)
{
if (bdr->next_to_clean > bdr->next_to_alloc)
return bdr->next_to_clean - bdr->next_to_alloc - 1;
return bdr->bd_count + bdr->next_to_clean - bdr->next_to_alloc - 1;
}
/* Control BD ring */
#define ENETC_CBDR_DEFAULT_SIZE 64
struct enetc_cbdr {
void *bd_base; /* points to Rx or Tx BD ring */
void __iomem *pir;
void __iomem *cir;
void __iomem *mr; /* mode register */
int bd_count; /* # of BDs */
int next_to_use;
int next_to_clean;
dma_addr_t bd_dma_base;
struct device *dma_dev;
};
#define ENETC_TXBD(BDR, i) (&(((union enetc_tx_bd *)((BDR).bd_base))[i]))
static inline union enetc_rx_bd *enetc_rxbd(struct enetc_bdr *rx_ring, int i)
{
int hw_idx = i;
if (rx_ring->ext_en)
hw_idx = 2 * i;
return &(((union enetc_rx_bd *)rx_ring->bd_base)[hw_idx]);
}
static inline void enetc_rxbd_next(struct enetc_bdr *rx_ring,
union enetc_rx_bd **old_rxbd, int *old_index)
{
union enetc_rx_bd *new_rxbd = *old_rxbd;
int new_index = *old_index;
new_rxbd++;
if (rx_ring->ext_en)
new_rxbd++;
if (unlikely(++new_index == rx_ring->bd_count)) {
new_rxbd = rx_ring->bd_base;
new_index = 0;
}
*old_rxbd = new_rxbd;
*old_index = new_index;
}
static inline union enetc_rx_bd *enetc_rxbd_ext(union enetc_rx_bd *rxbd)
{
return ++rxbd;
}
#define ENETC_REV1 0x1
#define ENETC_REV4 0x4
enum enetc_errata {
ENETC_ERR_VLAN_ISOL = BIT(0),
ENETC_ERR_UCMCSWP = BIT(1),
};
#define ENETC_SI_F_PSFP BIT(0)
#define ENETC_SI_F_QBV BIT(1)
#define ENETC_SI_F_QBU BIT(2)
#define ENETC_SI_F_LSO BIT(3)
#define ENETC_SI_F_PPM BIT(4) /* pseudo MAC */
struct enetc_drvdata {
u32 pmac_offset; /* Only valid for PSI which supports 802.1Qbu */
u8 tx_csum:1;
u8 max_frags;
u64 sysclk_freq;
const struct ethtool_ops *eth_ops;
};
struct enetc_platform_info {
u16 revision;
u16 dev_id;
const struct enetc_drvdata *data;
};
struct enetc_si;
/*
* This structure defines the some common hooks for ENETC PSI and VSI.
* In addition, since VSI only uses the struct enetc_si as its private
* driver data, so this structure also define some hooks specifically
* for VSI. For VSI-specific hooks, the format is ‘vf_*()’.
*/
struct enetc_si_ops {
int (*get_rss_table)(struct enetc_si *si, u32 *table, int count);
int (*set_rss_table)(struct enetc_si *si, const u32 *table, int count);
int (*setup_cbdr)(struct enetc_si *si);
void (*teardown_cbdr)(struct enetc_si *si);
};
/* PCI IEP device data */
struct enetc_si {
struct pci_dev *pdev;
struct enetc_hw hw;
enum enetc_errata errata;
struct net_device *ndev; /* back ref. */
union {
struct enetc_cbdr cbd_ring; /* Only ENETC 1.0 */
struct ntmp_user ntmp_user; /* ENETC 4.1 and later */
};
int num_rx_rings; /* how many rings are available in the SI */
int num_tx_rings;
int num_fs_entries;
int num_rss; /* number of RSS buckets */
unsigned short pad;
u16 revision;
int hw_features;
const struct enetc_drvdata *drvdata;
const struct enetc_si_ops *ops;
struct workqueue_struct *workqueue;
struct work_struct rx_mode_task;
struct dentry *debugfs_root;
struct enetc_msg_swbd msg; /* Only valid for VSI */
};
#define ENETC_SI_ALIGN 32
static inline bool is_enetc_rev1(struct enetc_si *si)
{
return si->pdev->revision == ENETC_REV1;
}
static inline void *enetc_si_priv(const struct enetc_si *si)
{
return (char *)si + ALIGN(sizeof(struct enetc_si), ENETC_SI_ALIGN);
}
static inline bool enetc_si_is_pf(struct enetc_si *si)
{
return !!(si->hw.port);
}
static inline int enetc_pf_to_port(struct pci_dev *pf_pdev)
{
switch (pf_pdev->devfn) {
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
case 6:
return 3;
default:
return -1;
}
}
static inline bool enetc_is_pseudo_mac(struct enetc_si *si)
{
return si->hw_features & ENETC_SI_F_PPM;
}
#define ENETC_MAX_NUM_TXQS 8
#define ENETC_INT_NAME_MAX (IFNAMSIZ + 8)
struct enetc_int_vector {
void __iomem *rbier;
void __iomem *tbier_base;
void __iomem *ricr1;
unsigned long tx_rings_map;
int count_tx_rings;
u32 rx_ictt;
u16 comp_cnt;
bool rx_dim_en, rx_napi_work;
struct napi_struct napi ____cacheline_aligned_in_smp;
struct dim rx_dim ____cacheline_aligned_in_smp;
char name[ENETC_INT_NAME_MAX];
struct enetc_bdr rx_ring;
struct enetc_bdr tx_ring[] __counted_by(count_tx_rings);
} ____cacheline_aligned_in_smp;
struct enetc_cls_rule {
struct ethtool_rx_flow_spec fs;
int used;
};
#define ENETC_MAX_BDR_INT 6 /* fixed to max # of available cpus */
struct psfp_cap {
u32 max_streamid;
u32 max_psfp_filter;
u32 max_psfp_gate;
u32 max_psfp_gatelist;
u32 max_psfp_meter;
};
#define ENETC_F_TX_TSTAMP_MASK 0xff
enum enetc_active_offloads {
/* 8 bits reserved for TX timestamp types (hwtstamp_tx_types) */
ENETC_F_TX_TSTAMP = BIT(0),
ENETC_F_TX_ONESTEP_SYNC_TSTAMP = BIT(1),
ENETC_F_RX_TSTAMP = BIT(8),
ENETC_F_QBV = BIT(9),
ENETC_F_QCI = BIT(10),
ENETC_F_QBU = BIT(11),
ENETC_F_TXCSUM = BIT(12),
ENETC_F_LSO = BIT(13),
};
enum enetc_flags_bit {
ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS = 0,
ENETC_TX_DOWN,
};
/* interrupt coalescing modes */
enum enetc_ic_mode {
/* one interrupt per frame */
ENETC_IC_NONE = 0,
/* activated when int coalescing time is set to a non-0 value */
ENETC_IC_RX_MANUAL = BIT(0),
ENETC_IC_TX_MANUAL = BIT(1),
/* use dynamic interrupt moderation */
ENETC_IC_RX_ADAPTIVE = BIT(2),
};
#define ENETC_RXIC_PKTTHR min_t(u32, 256, ENETC_RX_RING_DEFAULT_SIZE / 2)
#define ENETC_TXIC_PKTTHR min_t(u32, 128, ENETC_TX_RING_DEFAULT_SIZE / 2)
struct enetc_ndev_priv {
struct net_device *ndev;
struct device *dev; /* dma-mapping device */
struct enetc_si *si;
int bdr_int_num; /* number of Rx/Tx ring interrupts */
struct enetc_int_vector *int_vector[ENETC_MAX_BDR_INT];
u16 num_rx_rings, num_tx_rings;
u16 rx_bd_count, tx_bd_count;
u16 msg_enable;
u8 preemptible_tcs;
u8 max_frags; /* The maximum number of BDs for fragments */
enum enetc_active_offloads active_offloads;
u32 speed; /* store speed for compare update pspeed */
struct enetc_bdr **xdp_tx_ring;
struct enetc_bdr *tx_ring[16];
struct enetc_bdr *rx_ring[16];
const struct enetc_bdr_resource *tx_res;
const struct enetc_bdr_resource *rx_res;
struct enetc_cls_rule *cls_rules;
struct psfp_cap psfp_cap;
/* Minimum number of TX queues required by the network stack */
unsigned int min_num_stack_tx_queues;
struct phylink *phylink;
int ic_mode;
u32 tx_ictt;
struct bpf_prog *xdp_prog;
unsigned long flags;
struct work_struct tx_onestep_tstamp;
struct sk_buff_head tx_skbs;
/* Serialize access to MAC Merge state between ethtool requests
* and link state updates
*/
struct mutex mm_lock;
struct clk *ref_clk; /* RGMII/RMII reference clock */
u64 sysclk_freq; /* NETC system clock frequency */
};
#define ENETC_CBD(R, i) (&(((struct enetc_cbd *)((R).bd_base))[i]))
#define ENETC_CBDR_TIMEOUT 1000 /* usecs */
/* SI common */
u32 enetc_port_mac_rd(struct enetc_si *si, u32 reg);
void enetc_port_mac_wr(struct enetc_si *si, u32 reg, u32 val);
int enetc_pci_probe(struct pci_dev *pdev, const char *name, int sizeof_priv);
void enetc_pci_remove(struct pci_dev *pdev);
int enetc_alloc_msix(struct enetc_ndev_priv *priv);
void enetc_free_msix(struct enetc_ndev_priv *priv);
void enetc_get_si_caps(struct enetc_si *si);
void enetc_init_si_rings_params(struct enetc_ndev_priv *priv);
int enetc_alloc_si_resources(struct enetc_ndev_priv *priv);
void enetc_free_si_resources(struct enetc_ndev_priv *priv);
int enetc_configure_si(struct enetc_ndev_priv *priv);
int enetc_get_driver_data(struct enetc_si *si);
void enetc_add_mac_addr_ht_filter(struct enetc_mac_filter *filter,
const unsigned char *addr);
void enetc_reset_mac_addr_filter(struct enetc_mac_filter *filter);
int enetc_open(struct net_device *ndev);
int enetc_close(struct net_device *ndev);
void enetc_start(struct net_device *ndev);
void enetc_stop(struct net_device *ndev);
netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev);
struct net_device_stats *enetc_get_stats(struct net_device *ndev);
void enetc_set_features(struct net_device *ndev, netdev_features_t features);
int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd);
int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data);
void enetc_reset_tc_mqprio(struct net_device *ndev);
int enetc_setup_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
struct xdp_frame **frames, u32 flags);
int enetc_hwtstamp_get(struct net_device *ndev,
struct kernel_hwtstamp_config *config);
int enetc_hwtstamp_set(struct net_device *ndev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
/* ethtool */
extern const struct ethtool_ops enetc_pf_ethtool_ops;
extern const struct ethtool_ops enetc4_pf_ethtool_ops;
extern const struct ethtool_ops enetc_vf_ethtool_ops;
extern const struct ethtool_ops enetc4_ppm_ethtool_ops;
void enetc_set_ethtool_ops(struct net_device *ndev);
void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link);
void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv);
/* control buffer descriptor ring (CBDR) */
int enetc_setup_cbdr(struct enetc_si *si);
void enetc_teardown_cbdr(struct enetc_si *si);
int enetc4_setup_cbdr(struct enetc_si *si);
void enetc4_teardown_cbdr(struct enetc_si *si);
int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
char *mac_addr, int si_map);
int enetc_clear_mac_flt_entry(struct enetc_si *si, int index);
int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
int index);
void enetc_set_rss_key(struct enetc_si *si, const u8 *bytes);
int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count);
int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count);
int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
int enetc4_get_rss_table(struct enetc_si *si, u32 *table, int count);
int enetc4_set_rss_table(struct enetc_si *si, const u32 *table, int count);
static inline void *enetc_cbd_alloc_data_mem(struct enetc_si *si,
struct enetc_cbd *cbd,
int size, dma_addr_t *dma,
void **data_align)
{
struct enetc_cbdr *ring = &si->cbd_ring;
dma_addr_t dma_align;
void *data;
data = dma_alloc_coherent(ring->dma_dev,
size + ENETC_CBD_DATA_MEM_ALIGN,
dma, GFP_KERNEL);
if (!data) {
dev_err(ring->dma_dev, "CBD alloc data memory failed!\n");
return NULL;
}
dma_align = ALIGN(*dma, ENETC_CBD_DATA_MEM_ALIGN);
*data_align = PTR_ALIGN(data, ENETC_CBD_DATA_MEM_ALIGN);
cbd->addr[0] = cpu_to_le32(lower_32_bits(dma_align));
cbd->addr[1] = cpu_to_le32(upper_32_bits(dma_align));
cbd->length = cpu_to_le16(size);
return data;
}
static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size,
void *data, dma_addr_t *dma)
{
struct enetc_cbdr *ring = &si->cbd_ring;
dma_free_coherent(ring->dma_dev, size + ENETC_CBD_DATA_MEM_ALIGN,
data, *dma);
}
void enetc_reset_ptcmsdur(struct enetc_hw *hw);
void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu);
static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si)
{
if (is_enetc_rev1(si))
return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
return IS_ENABLED(CONFIG_PTP_NETC_V4_TIMER);
}
#ifdef CONFIG_FSL_ENETC_QOS
int enetc_qos_query_caps(struct net_device *ndev, void *type_data);
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed);
int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data);
int enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv);
int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data);
int enetc_psfp_init(struct enetc_ndev_priv *priv);
int enetc_psfp_clean(struct enetc_ndev_priv *priv);
int enetc_set_psfp(struct net_device *ndev, bool en);
static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
u32 reg;
reg = enetc_port_rd(hw, ENETC_PSIDCAPR);
priv->psfp_cap.max_streamid = reg & ENETC_PSIDCAPR_MSK;
/* Port stream filter capability */
reg = enetc_port_rd(hw, ENETC_PSFCAPR);
priv->psfp_cap.max_psfp_filter = reg & ENETC_PSFCAPR_MSK;
/* Port stream gate capability */
reg = enetc_port_rd(hw, ENETC_PSGCAPR);
priv->psfp_cap.max_psfp_gate = (reg & ENETC_PSGCAPR_SGIT_MSK);
priv->psfp_cap.max_psfp_gatelist = (reg & ENETC_PSGCAPR_GCL_MSK) >> 16;
/* Port flow meter capability */
reg = enetc_port_rd(hw, ENETC_PFMCAPR);
priv->psfp_cap.max_psfp_meter = reg & ENETC_PFMCAPR_MSK;
}
static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
int err;
enetc_get_max_cap(priv);
err = enetc_psfp_init(priv);
if (err)
return err;
enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) |
ENETC_PPSFPMR_PSFPEN | ENETC_PPSFPMR_VS |
ENETC_PPSFPMR_PVC | ENETC_PPSFPMR_PVZC);
return 0;
}
static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv)
{
struct enetc_hw *hw = &priv->si->hw;
int err;
err = enetc_psfp_clean(priv);
if (err)
return err;
enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) &
~ENETC_PPSFPMR_PSFPEN & ~ENETC_PPSFPMR_VS &
~ENETC_PPSFPMR_PVC & ~ENETC_PPSFPMR_PVZC);
memset(&priv->psfp_cap, 0, sizeof(struct psfp_cap));
return 0;
}
#else
#define enetc_qos_query_caps(ndev, type_data) -EOPNOTSUPP
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(priv, speed) (void)0
#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#define enetc_setup_tc_txtime(ndev, type_data) -EOPNOTSUPP
#define enetc_setup_tc_psfp(ndev, type_data) -EOPNOTSUPP
#define enetc_setup_tc_block_cb NULL
#define enetc_get_max_cap(p) \
memset(&((p)->psfp_cap), 0, sizeof(struct psfp_cap))
static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv)
{
return 0;
}
static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv)
{
return 0;
}
static inline int enetc_set_psfp(struct net_device *ndev, bool en)
{
return 0;
}
#endif
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0+
/* Copyright 2025 NXP */
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/string_choices.h>
#include "enetc_pf.h"
#include "enetc4_debugfs.h"
static void enetc_show_si_mac_hash_filter(struct seq_file *s, int i)
{
struct enetc_si *si = s->private;
struct enetc_hw *hw = &si->hw;
u32 hash_h, hash_l;
hash_l = enetc_port_rd(hw, ENETC4_PSIUMHFR0(i));
hash_h = enetc_port_rd(hw, ENETC4_PSIUMHFR1(i));
seq_printf(s, "SI %d unicast MAC hash filter: 0x%08x%08x\n",
i, hash_h, hash_l);
hash_l = enetc_port_rd(hw, ENETC4_PSIMMHFR0(i));
hash_h = enetc_port_rd(hw, ENETC4_PSIMMHFR1(i));
seq_printf(s, "SI %d multicast MAC hash filter: 0x%08x%08x\n",
i, hash_h, hash_l);
}
static int enetc_mac_filter_show(struct seq_file *s, void *data)
{
struct enetc_si *si = s->private;
struct enetc_hw *hw = &si->hw;
struct maft_entry_data maft;
struct enetc_pf *pf;
int i, err, num_si;
u32 val;
pf = enetc_si_priv(si);
num_si = pf->caps.num_vsi + 1;
val = enetc_port_rd(hw, ENETC4_PSIPMMR);
for (i = 0; i < num_si; i++) {
seq_printf(s, "SI %d Unicast Promiscuous mode: %s\n", i,
str_enabled_disabled(PSIPMMR_SI_MAC_UP(i) & val));
seq_printf(s, "SI %d Multicast Promiscuous mode: %s\n", i,
str_enabled_disabled(PSIPMMR_SI_MAC_MP(i) & val));
}
/* MAC hash filter table */
for (i = 0; i < num_si; i++)
enetc_show_si_mac_hash_filter(s, i);
if (!pf->num_mfe)
return 0;
/* MAC address filter table */
seq_puts(s, "MAC address filter table\n");
for (i = 0; i < pf->num_mfe; i++) {
memset(&maft, 0, sizeof(maft));
err = ntmp_maft_query_entry(&si->ntmp_user, i, &maft);
if (err)
return err;
seq_printf(s, "Entry %d, MAC: %pM, SI bitmap: 0x%04x\n", i,
maft.keye.mac_addr, le16_to_cpu(maft.cfge.si_bitmap));
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(enetc_mac_filter);
void enetc_create_debugfs(struct enetc_si *si)
{
struct net_device *ndev = si->ndev;
struct dentry *root;
root = debugfs_create_dir(netdev_name(ndev), NULL);
if (IS_ERR(root))
return;
si->debugfs_root = root;
debugfs_create_file("mac_filter", 0444, root, si, &enetc_mac_filter_fops);
}
void enetc_remove_debugfs(struct enetc_si *si)
{
debugfs_remove(si->debugfs_root);
si->debugfs_root = NULL;
}
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2025 NXP */
#ifndef __ENETC4_DEBUGFS_H
#define __ENETC4_DEBUGFS_H
#if IS_ENABLED(CONFIG_DEBUG_FS)
void enetc_create_debugfs(struct enetc_si *si);
void enetc_remove_debugfs(struct enetc_si *si);
#else
static inline void enetc_create_debugfs(struct enetc_si *si)
{
}
static inline void enetc_remove_debugfs(struct enetc_si *si)
{
}
#endif
#endif
@@ -0,0 +1,435 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* This header file defines the register offsets and bit fields
* of ENETC4 PF and VFs. Note that the same registers as ENETC
* version 1.0 are defined in the enetc_hw.h file.
*
* Copyright 2024 NXP
*/
#ifndef __ENETC4_HW_H_
#define __ENETC4_HW_H_
#define NXP_ENETC_VENDOR_ID 0x1131
#define NXP_ENETC_PF_DEV_ID 0xe101
#define NXP_ENETC_PPM_DEV_ID 0xe110
/**********************Station interface registers************************/
/* Station interface LSO segmentation flag mask register 0/1 */
#define ENETC4_SILSOSFMR0 0x1300
#define SILSOSFMR0_TCP_MID_SEG GENMASK(27, 16)
#define SILSOSFMR0_TCP_1ST_SEG GENMASK(11, 0)
#define SILSOSFMR0_VAL_SET(first, mid) (FIELD_PREP(SILSOSFMR0_TCP_MID_SEG, mid) | \
FIELD_PREP(SILSOSFMR0_TCP_1ST_SEG, first))
#define ENETC4_SILSOSFMR1 0x1304
#define SILSOSFMR1_TCP_LAST_SEG GENMASK(11, 0)
#define ENETC4_TCP_FLAGS_FIN BIT(0)
#define ENETC4_TCP_FLAGS_SYN BIT(1)
#define ENETC4_TCP_FLAGS_RST BIT(2)
#define ENETC4_TCP_FLAGS_PSH BIT(3)
#define ENETC4_TCP_FLAGS_ACK BIT(4)
#define ENETC4_TCP_FLAGS_URG BIT(5)
#define ENETC4_TCP_FLAGS_ECE BIT(6)
#define ENETC4_TCP_FLAGS_CWR BIT(7)
#define ENETC4_TCP_FLAGS_NS BIT(8)
/* According to tso_build_hdr(), clear all special flags for not last packet. */
#define ENETC4_TCP_NL_SEG_FLAGS_DMASK (ENETC4_TCP_FLAGS_FIN | \
ENETC4_TCP_FLAGS_RST | ENETC4_TCP_FLAGS_PSH)
/***************************ENETC port registers**************************/
#define ENETC4_ECAPR0 0x0
#define ECAPR0_RFS BIT(2)
#define ECAPR0_TSD BIT(5)
#define ECAPR0_RSS BIT(8)
#define ECAPR0_RSC BIT(9)
#define ECAPR0_LSO BIT(10)
#define ECAPR0_WO BIT(13)
#define ENETC4_ECAPR1 0x4
#define ECAPR1_NUM_TCS GENMASK(6, 4)
#define ECAPR1_NUM_MCH GENMASK(9, 8)
#define ECAPR1_NUM_UCH GENMASK(11, 10)
#define ECAPR1_NUM_MSIX GENMASK(22, 12)
#define ECAPR1_NUM_VSI GENMASK(27, 24)
#define ECAPR1_NUM_IPV BIT(31)
#define ENETC4_ECAPR2 0x8
#define ECAPR2_NUM_TX_BDR GENMASK(9, 0)
#define ECAPR2_NUM_RX_BDR GENMASK(25, 16)
#define ENETC4_PMR 0x10
#define PMR_SI_EN(a) BIT((16 + (a)))
/* Port Pause ON/OFF threshold register */
#define ENETC4_PPAUONTR 0x108
#define ENETC4_PPAUOFFTR 0x10c
/* Port ingress congestion DRa (a=0,1,2,3) discard count register */
#define ENETC4_PICDRDCR(a) ((a) * 0x10 + 0x140)
/* Port Station interface promiscuous MAC mode register */
#define ENETC4_PSIPMMR 0x200
#define PSIPMMR_SI_MAC_UP(a) BIT(a) /* a = SI index */
#define PSIPMMR_SI_MAC_MP(a) BIT((a) + 16)
/* Port Station interface promiscuous VLAN mode register */
#define ENETC4_PSIPVMR 0x204
/* Port broadcast frames dropped due to MAC filtering register */
#define ENETC4_PBFDSIR 0x208
/* Port frame drop MAC source address pruning register */
#define ENETC4_PFDMSAPR 0x20c
/* Port RSS key register n. n = 0,1,2,...,9 */
#define ENETC4_PRSSKR(n) ((n) * 0x4 + 0x250)
/* Port station interface MAC address filtering capability register */
#define ENETC4_PSIMAFCAPR 0x280
#define PSIMAFCAPR_NUM_MAC_AFTE GENMASK(11, 0)
/* Port unicast frames dropped due to MAC filtering register */
#define ENETC4_PUFDMFR 0x284
/* Port multicast frames dropped due to MAC filtering register */
#define ENETC4_PMFDMFR 0x288
/* Port station interface VLAN filtering capability register */
#define ENETC4_PSIVLANFCAPR 0x2c0
#define PSIVLANFCAPR_NUM_VLAN_FTE GENMASK(11, 0)
/* Port station interface VLAN filtering mode register */
#define ENETC4_PSIVLANFMR 0x2c4
#define PSIVLANFMR_VS BIT(0)
/* Port unicast frames dropped VLAN filtering register */
#define ENETC4_PUFDVFR 0x2d0
/* Port multicast frames dropped VLAN filtering register */
#define ENETC4_PMFDVFR 0x2d4
/* Port broadcast frames dropped VLAN filtering register */
#define ENETC4_PBFDVFR 0x2d8
/* Port Station interface a primary MAC address registers */
#define ENETC4_PSIPMAR0(a) ((a) * 0x80 + 0x2000)
#define ENETC4_PSIPMAR1(a) ((a) * 0x80 + 0x2004)
/* Port station interface a configuration register 0/2 */
#define ENETC4_PSICFGR0(a) ((a) * 0x80 + 0x2010)
#define PSICFGR0_VASE BIT(13)
#define PSICFGR0_ASE BIT(15)
#define PSICFGR0_ANTI_SPOOFING (PSICFGR0_VASE | PSICFGR0_ASE)
#define ENETC4_PSICFGR2(a) ((a) * 0x80 + 0x2018)
#define PSICFGR2_NUM_MSIX GENMASK(5, 0)
/* Port station interface a unicast MAC hash filter register 0/1 */
#define ENETC4_PSIUMHFR0(a) ((a) * 0x80 + 0x2050)
#define ENETC4_PSIUMHFR1(a) ((a) * 0x80 + 0x2054)
/* Port station interface a multicast MAC hash filter register 0/1 */
#define ENETC4_PSIMMHFR0(a) ((a) * 0x80 + 0x2058)
#define ENETC4_PSIMMHFR1(a) ((a) * 0x80 + 0x205c)
/* Port station interface a VLAN hash filter register 0/1 */
#define ENETC4_PSIVHFR0(a) ((a) * 0x80 + 0x2060)
#define ENETC4_PSIVHFR1(a) ((a) * 0x80 + 0x2064)
#define ENETC4_PMCAPR 0x4004
#define PMCAPR_HD BIT(8)
#define PMCAPR_FP GENMASK(10, 9)
/* Port capability register */
#define ENETC4_PCAPR 0x4000
#define PCAPR_LINK_TYPE BIT(4)
/* Port configuration register */
#define ENETC4_PCR 0x4010
#define PCR_HDR_FMT BIT(0)
#define PCR_L2DOSE BIT(4)
#define PCR_TIMER_CS BIT(8)
#define PCR_PSPEED GENMASK(29, 16)
#define PCR_PSPEED_VAL(speed) (((speed) / 10 - 1) << 16)
/* Port MAC address register 0/1 */
#define ENETC4_PMAR0 0x4020
#define ENETC4_PMAR1 0x4024
/* Port operational register */
#define ENETC4_POR 0x4100
#define POR_TXDIS BIT(0)
#define POR_RXDIS BIT(1)
/* Port status register */
#define ENETC4_PSR 0x4104
#define PSR_RX_BUSY BIT(1)
/* Port Rx discard count register */
#define ENETC4_PRXDCR 0x41c0
/* Port Rx discard count read-reset register */
#define ENETC4_PRXDCRRR 0x41c4
/* Port Rx discard count reason register 0 */
#define ENETC4_PRXDCRR0 0x41c8
/* Port Rx discard count reason register 1 */
#define ENETC4_PRXDCRR1 0x41cc
/* Port traffic class a transmit maximum SDU register */
#define ENETC4_PTCTMSDUR(a) ((a) * 0x20 + 0x4208)
#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 ENETC4_PMAC_OFFSET 0x400
#define ENETC4_PM_CMD_CFG(mac) (0x5008 + (mac) * 0x400)
#define PM_CMD_CFG_TX_EN BIT(0)
#define PM_CMD_CFG_RX_EN BIT(1)
#define PM_CMD_CFG_PAUSE_FWD BIT(7)
#define PM_CMD_CFG_PAUSE_IGN BIT(8)
#define PM_CMD_CFG_TX_ADDR_INS BIT(9)
#define PM_CMD_CFG_LOOP_EN BIT(10)
#define PM_CMD_CFG_LPBK_MODE GENMASK(12, 11)
#define LPBCK_MODE_EXT_TX_CLK 0
#define LPBCK_MODE_MAC_LEVEL 1
#define LPBCK_MODE_INT_TX_CLK 2
#define PM_CMD_CFG_CNT_FRM_EN BIT(13)
#define PM_CMD_CFG_TXP BIT(15)
#define PM_CMD_CFG_SEND_IDLE BIT(16)
#define PM_CMD_CFG_HD_FCEN BIT(18)
#define PM_CMD_CFG_SFD BIT(21)
#define PM_CMD_CFG_TX_FLUSH BIT(22)
#define PM_CMD_CFG_TX_LOWP_EN BIT(23)
#define PM_CMD_CFG_RX_LOWP_EMPTY BIT(24)
#define PM_CMD_CFG_SWR BIT(26)
#define PM_CMD_CFG_TS_MODE BIT(30)
#define PM_CMD_CFG_MG BIT(31)
/* Port MAC 0/1 Maximum Frame Length Register */
#define ENETC4_PM_MAXFRM(mac) (0x5014 + (mac) * 0x400)
/* Port internal MDIO base address, use to access PCS */
#define ENETC4_PM_IMDIO_BASE 0x5030
/* Port MAC 0/1 Interrupt Event Register */
#define ENETC4_PM_IEVENT(mac) (0x5040 + (mac) * 0x400)
#define PM_IEVENT_TX_EMPTY BIT(5)
#define PM_IEVENT_RX_EMPTY BIT(6)
/* Port MAC 0/1 Pause Quanta Register */
#define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400)
/* Port MAC 0/1 Pause Quanta Threshold Register */
#define ENETC4_PM_PAUSE_THRESH(mac) (0x5064 + (mac) * 0x400)
#define ENETC4_PM_SINGLE_STEP(mac) (0x50c0 + (mac) * 0x400)
#define PM_SINGLE_STEP_CH BIT(6)
#define PM_SINGLE_STEP_OFFSET GENMASK(15, 7)
#define PM_SINGLE_STEP_OFFSET_SET(o) FIELD_PREP(PM_SINGLE_STEP_OFFSET, o)
#define PM_SINGLE_STEP_EN BIT(31)
/* Port MAC 0/1 Receive Ethernet Octets Counter */
#define ENETC4_PM_REOCT(mac) (0x5100 + (mac) * 0x400)
/* Port MAC 0/1 Receive Octets Counter */
#define ENETC4_PM_ROCT(mac) (0x5108 + (mac) * 0x400)
/* Port MAC 0/1 Receive Alignment Error Counter Register */
#define ENETC4_PM_RALN(mac) (0x5110 + (mac) * 0x400)
/* Port MAC 0/1 Receive Valid Pause Frame Counter */
#define ENETC4_PM_RXPF(mac) (0x5118 + (mac) * 0x400)
/* Port MAC 0/1 Receive Frame Counter */
#define ENETC4_PM_RFRM(mac) (0x5120 + (mac) * 0x400)
/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */
#define ENETC4_PM_RFCS(mac) (0x5128 + (mac) * 0x400)
/* Port MAC 0/1 Receive VLAN Frame Counter */
#define ENETC4_PM_RVLAN(mac) (0x5130 + (mac) * 0x400)
/* Port MAC 0/1 Receive Frame Error Counter */
#define ENETC4_PM_RERR(mac) (0x5138 + (mac) * 0x400)
/* Port MAC 0/1 Receive Unicast Frame Counter */
#define ENETC4_PM_RUCA(mac) (0x5140 + (mac) * 0x400)
/* Port MAC 0/1 Receive Multicast Frame Counter */
#define ENETC4_PM_RMCA(mac) (0x5148 + (mac) * 0x400)
/* Port MAC 0/1 Receive Broadcast Frame Counter */
#define ENETC4_PM_RBCA(mac) (0x5150 + (mac) * 0x400)
/* Port MAC 0/1 Receive Dropped Packets Counter */
#define ENETC4_PM_RDRP(mac) (0x5158 + (mac) * 0x400)
/* Port MAC 0/1 Receive Packets Counter */
#define ENETC4_PM_RPKT(mac) (0x5160 + (mac) * 0x400)
/* Port MAC 0/1 Receive Undersized Packet Counter */
#define ENETC4_PM_RUND(mac) (0x5168 + (mac) * 0x400)
/* Port MAC 0/1 Receive 64-Octet Packet Counter */
#define ENETC4_PM_R64(mac) (0x5170 + (mac) * 0x400)
/* Port MAC 0/1 Receive 65 to 127-Octet Packet Counter */
#define ENETC4_PM_R127(mac) (0x5178 + (mac) * 0x400)
/* Port MAC 0/1 Receive 128 to 255-Octet Packet Counter */
#define ENETC4_PM_R255(mac) (0x5180 + (mac) * 0x400)
/* Port MAC 0/1 Receive 256 to 511-Octet Packet Counter */
#define ENETC4_PM_R511(mac) (0x5188 + (mac) * 0x400)
/* Port MAC 0/1 Receive 512 to 1023-Octet Packet Counter */
#define ENETC4_PM_R1023(mac) (0x5190 + (mac) * 0x400)
/* Port MAC 0/1 Receive 1024 to 1522-Octet Packet Counter */
#define ENETC4_PM_R1522(mac) (0x5198 + (mac) * 0x400)
/* Port MAC 0/1 Receive 1523 to Max-Octet Packet Counter */
#define ENETC4_PM_R1523X(mac) (0x51a0 + (mac) * 0x400)
/* Port MAC 0/1 Receive Oversized Packet Counter */
#define ENETC4_PM_ROVR(mac) (0x51a8 + (mac) * 0x400)
/* Port MAC 0/1 Receive Jabber Packet Counter */
#define ENETC4_PM_RJBR(mac) (0x51b0 + (mac) * 0x400)
/* Port MAC 0/1 Receive Fragment Packet Counter */
#define ENETC4_PM_RFRG(mac) (0x51b8 + (mac) * 0x400)
/* Port MAC 0/1 Receive Control Packet Counter */
#define ENETC4_PM_RCNP(mac) (0x51c0 + (mac) * 0x400)
/* Port MAC 0/1 Receive Dropped Not Truncated Packets Counter */
#define ENETC4_PM_RDRNTP(mac) (0x51c8 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Ethernet Octets Counter */
#define ENETC4_PM_TEOCT(mac) (0x5200 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Octets Counter */
#define ENETC4_PM_TOCT(mac) (0x5208 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Valid Pause Frame Counter */
#define ENETC4_PM_TXPF(mac) (0x5218 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Frame Counter */
#define ENETC4_PM_TFRM(mac) (0x5220 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */
#define ENETC4_PM_TFCS(mac) (0x5228 + (mac) * 0x400)
/* Port MAC 0/1 Transmit VLAN Frame Counter */
#define ENETC4_PM_TVLAN(mac) (0x5230 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Frame Error Counter */
#define ENETC4_PM_TERR(mac) (0x5238 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Unicast Frame Counter */
#define ENETC4_PM_TUCA(mac) (0x5240 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Multicast Frame Counter */
#define ENETC4_PM_TMCA(mac) (0x5248 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Broadcast Frame Counter */
#define ENETC4_PM_TBCA(mac) (0x5250 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Packets Counter */
#define ENETC4_PM_TPKT(mac) (0x5260 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Undersized Packet Counter */
#define ENETC4_PM_TUND(mac) (0x5268 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 64-Octet Packet Counter */
#define ENETC4_PM_T64(mac) (0x5270 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 65 to 127-Octet Packet Counter */
#define ENETC4_PM_T127(mac) (0x5278 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 128 to 255-Octet Packet Counter */
#define ENETC4_PM_T255(mac) (0x5280 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 256 to 511-Octet Packet Counter */
#define ENETC4_PM_T511(mac) (0x5288 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 512 to 1023-Octet Packet Counter */
#define ENETC4_PM_T1023(mac) (0x5290 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 1024 to 1522-Octet Packet Counter */
#define ENETC4_PM_T1522(mac) (0x5298 + (mac) * 0x400)
/* Port MAC 0/1 Transmit 1523 to TX_MTU-Octet Packet Counter */
#define ENETC4_PM_T1523X(mac) (0x52a0 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Control Packet Counter */
#define ENETC4_PM_TCNP(mac) (0x52c0 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Deferred Packet Counter */
#define ENETC4_PM_TDFR(mac) (0x52d0 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Multiple Collisions Counter */
#define ENETC4_PM_TMCOL(mac) (0x52d8 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Single Collision */
#define ENETC4_PM_TSCOL(mac) (0x52e0 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Late Collision Counter */
#define ENETC4_PM_TLCOL(mac) (0x52e8 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Excessive Collisions Counter */
#define ENETC4_PM_TECOL(mac) (0x52f0 + (mac) * 0x400)
/* Port MAC 0/1 Transmit Invalid Octets Counter */
#define ENETC4_PM_TIOCT(mac) (0x52f8 + (mac) * 0x400)
/* Port MAC 0 Interface Mode Control Register */
#define ENETC4_PM_IF_MODE(mac) (0x5300 + (mac) * 0x400)
#define PM_IF_MODE_IFMODE GENMASK(2, 0)
#define IFMODE_XGMII 0
#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
#define PM_IF_MODE_ENA BIT(15)
/* Port external MDIO Base address, use to access off-chip PHY */
#define ENETC4_EMDIO_BASE 0x5c00
/**********************ENETC Pseudo MAC port registers************************/
/* Port pseudo MAC receive octets counter (64-bit) */
#define ENETC4_PPMROCR 0x5080
/* Port pseudo MAC receive unicast frame counter register (64-bit) */
#define ENETC4_PPMRUFCR 0x5088
/* Port pseudo MAC receive multicast frame counter register (64-bit) */
#define ENETC4_PPMRMFCR 0x5090
/* Port pseudo MAC receive broadcast frame counter register (64-bit) */
#define ENETC4_PPMRBFCR 0x5098
/* Port pseudo MAC transmit octets counter (64-bit) */
#define ENETC4_PPMTOCR 0x50c0
/* Port pseudo MAC transmit unicast frame counter register (64-bit) */
#define ENETC4_PPMTUFCR 0x50c8
/* Port pseudo MAC transmit multicast frame counter register (64-bit) */
#define ENETC4_PPMTMFCR 0x50d0
/* Port pseudo MAC transmit broadcast frame counter register (64-bit) */
#define ENETC4_PPMTBFCR 0x50d8
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,308 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
#include "enetc.h"
int enetc_setup_cbdr(struct enetc_si *si)
{
int size = ENETC_CBDR_DEFAULT_SIZE * sizeof(struct enetc_cbd);
struct enetc_cbdr *cbdr = &si->cbd_ring;
struct device *dev = &si->pdev->dev;
struct enetc_hw *hw = &si->hw;
cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base,
GFP_KERNEL);
if (!cbdr->bd_base)
return -ENOMEM;
/* h/w requires 128B alignment */
if (!IS_ALIGNED(cbdr->bd_dma_base, 128)) {
dma_free_coherent(dev, size, cbdr->bd_base,
cbdr->bd_dma_base);
return -EINVAL;
}
cbdr->next_to_clean = 0;
cbdr->next_to_use = 0;
cbdr->dma_dev = dev;
cbdr->bd_count = ENETC_CBDR_DEFAULT_SIZE;
cbdr->pir = hw->reg + ENETC_SICBDRPIR;
cbdr->cir = hw->reg + ENETC_SICBDRCIR;
cbdr->mr = hw->reg + ENETC_SICBDRMR;
/* set CBDR cache attributes */
enetc_wr(hw, ENETC_SICAR2,
ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
enetc_wr(hw, ENETC_SICBDRBAR0, lower_32_bits(cbdr->bd_dma_base));
enetc_wr(hw, ENETC_SICBDRBAR1, upper_32_bits(cbdr->bd_dma_base));
enetc_wr(hw, ENETC_SICBDRLENR, ENETC_RTBLENR_LEN(cbdr->bd_count));
enetc_wr_reg(cbdr->pir, cbdr->next_to_clean);
enetc_wr_reg(cbdr->cir, cbdr->next_to_use);
/* enable ring */
enetc_wr_reg(cbdr->mr, BIT(31));
return 0;
}
EXPORT_SYMBOL_GPL(enetc_setup_cbdr);
void enetc_teardown_cbdr(struct enetc_si *si)
{
struct enetc_cbdr *cbdr = &si->cbd_ring;
int size;
/* disable ring */
enetc_wr_reg(cbdr->mr, 0);
size = cbdr->bd_count * sizeof(struct enetc_cbd);
dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base,
cbdr->bd_dma_base);
cbdr->bd_base = NULL;
cbdr->dma_dev = NULL;
}
EXPORT_SYMBOL_GPL(enetc_teardown_cbdr);
int enetc4_setup_cbdr(struct enetc_si *si)
{
struct ntmp_user *user = &si->ntmp_user;
struct device *dev = &si->pdev->dev;
struct enetc_hw *hw = &si->hw;
struct netc_cbdr_regs regs;
user->cbdr_num = 1;
user->dev = dev;
user->ring = devm_kcalloc(dev, user->cbdr_num,
sizeof(struct netc_cbdr), GFP_KERNEL);
if (!user->ring)
return -ENOMEM;
regs.pir = hw->reg + ENETC_SICBDRPIR;
regs.cir = hw->reg + ENETC_SICBDRCIR;
regs.mr = hw->reg + ENETC_SICBDRMR;
regs.bar0 = hw->reg + ENETC_SICBDRBAR0;
regs.bar1 = hw->reg + ENETC_SICBDRBAR1;
regs.lenr = hw->reg + ENETC_SICBDRLENR;
return ntmp_init_cbdr(user->ring, dev, &regs);
}
EXPORT_SYMBOL_GPL(enetc4_setup_cbdr);
void enetc4_teardown_cbdr(struct enetc_si *si)
{
struct ntmp_user *user = &si->ntmp_user;
ntmp_free_cbdr(user->ring);
user->dev = NULL;
}
EXPORT_SYMBOL_GPL(enetc4_teardown_cbdr);
static void enetc_clean_cbdr(struct enetc_cbdr *ring)
{
struct enetc_cbd *dest_cbd;
int i, status;
i = ring->next_to_clean;
while (enetc_rd_reg(ring->cir) != i) {
dest_cbd = ENETC_CBD(*ring, i);
status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;
if (status)
dev_warn(ring->dma_dev, "CMD err %04x for cmd %04x\n",
status, dest_cbd->cmd);
memset(dest_cbd, 0, sizeof(*dest_cbd));
i = (i + 1) % ring->bd_count;
}
ring->next_to_clean = i;
}
static int enetc_cbd_unused(struct enetc_cbdr *r)
{
return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %
r->bd_count;
}
int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
{
struct enetc_cbdr *ring = &si->cbd_ring;
int timeout = ENETC_CBDR_TIMEOUT;
struct enetc_cbd *dest_cbd;
int i;
if (unlikely(!ring->bd_base))
return -EIO;
if (unlikely(!enetc_cbd_unused(ring)))
enetc_clean_cbdr(ring);
i = ring->next_to_use;
dest_cbd = ENETC_CBD(*ring, i);
/* copy command to the ring */
*dest_cbd = *cbd;
i = (i + 1) % ring->bd_count;
ring->next_to_use = i;
/* let H/W know BD ring has been updated */
enetc_wr_reg(ring->pir, i);
do {
if (enetc_rd_reg(ring->cir) == i)
break;
udelay(10); /* cannot sleep, rtnl_lock() */
timeout -= 10;
} while (timeout);
if (!timeout)
return -EBUSY;
/* CBD may writeback data, feedback up level */
*cbd = *dest_cbd;
enetc_clean_cbdr(ring);
return 0;
}
EXPORT_SYMBOL_GPL(enetc_send_cmd);
int enetc_clear_mac_flt_entry(struct enetc_si *si, int index)
{
struct enetc_cbd cbd;
memset(&cbd, 0, sizeof(cbd));
cbd.cls = 1;
cbd.status_flags = ENETC_CBD_FLAGS_SF;
cbd.index = cpu_to_le16(index);
return enetc_send_cmd(si, &cbd);
}
EXPORT_SYMBOL_GPL(enetc_clear_mac_flt_entry);
int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
char *mac_addr, int si_map)
{
struct enetc_cbd cbd;
u32 upper;
u16 lower;
memset(&cbd, 0, sizeof(cbd));
/* fill up the "set" descriptor */
cbd.cls = 1;
cbd.status_flags = ENETC_CBD_FLAGS_SF;
cbd.index = cpu_to_le16(index);
cbd.opt[3] = cpu_to_le32(si_map);
/* enable entry */
cbd.opt[0] = cpu_to_le32(BIT(31));
upper = *(const u32 *)mac_addr;
lower = *(const u16 *)(mac_addr + 4);
cbd.addr[0] = cpu_to_le32(upper);
cbd.addr[1] = cpu_to_le32(lower);
return enetc_send_cmd(si, &cbd);
}
EXPORT_SYMBOL_GPL(enetc_set_mac_flt_entry);
/* Set entry in RFS table */
int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
int index)
{
struct enetc_cbdr *ring = &si->cbd_ring;
struct enetc_cbd cbd = {.cmd = 0};
void *tmp, *tmp_align;
dma_addr_t dma;
int err;
/* fill up the "set" descriptor */
cbd.cmd = 0;
cbd.cls = 4;
cbd.index = cpu_to_le16(index);
cbd.opt[3] = cpu_to_le32(0); /* SI */
tmp = enetc_cbd_alloc_data_mem(si, &cbd, sizeof(*rfse),
&dma, &tmp_align);
if (!tmp)
return -ENOMEM;
memcpy(tmp_align, rfse, sizeof(*rfse));
err = enetc_send_cmd(si, &cbd);
if (err)
dev_err(ring->dma_dev, "FS entry add failed (%d)!", err);
enetc_cbd_free_data_mem(si, sizeof(*rfse), tmp, &dma);
return err;
}
EXPORT_SYMBOL_GPL(enetc_set_fs_entry);
static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count,
bool read)
{
struct enetc_cbdr *ring = &si->cbd_ring;
struct enetc_cbd cbd = {.cmd = 0};
u8 *tmp, *tmp_align;
dma_addr_t dma;
int err, i;
if (count < ENETC_CBD_DATA_MEM_ALIGN)
/* HW only takes in a full 64 entry table */
return -EINVAL;
tmp = enetc_cbd_alloc_data_mem(si, &cbd, count,
&dma, (void *)&tmp_align);
if (!tmp)
return -ENOMEM;
if (!read)
for (i = 0; i < count; i++)
tmp_align[i] = (u8)(table[i]);
/* fill up the descriptor */
cbd.cmd = read ? 2 : 1;
cbd.cls = 3;
err = enetc_send_cmd(si, &cbd);
if (err)
dev_err(ring->dma_dev, "RSS cmd failed (%d)!", err);
if (read)
for (i = 0; i < count; i++)
table[i] = tmp_align[i];
enetc_cbd_free_data_mem(si, count, tmp, &dma);
return err;
}
/* Get RSS table */
int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count)
{
return enetc_cmd_rss_table(si, table, count, true);
}
EXPORT_SYMBOL_GPL(enetc_get_rss_table);
/* Set RSS table */
int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count)
{
return enetc_cmd_rss_table(si, (u32 *)table, count, false);
}
EXPORT_SYMBOL_GPL(enetc_set_rss_table);
int enetc4_get_rss_table(struct enetc_si *si, u32 *table, int count)
{
return ntmp_rsst_query_entry(&si->ntmp_user, table, count);
}
EXPORT_SYMBOL_GPL(enetc4_get_rss_table);
int enetc4_set_rss_table(struct enetc_si *si, const u32 *table, int count)
{
return ntmp_rsst_update_entry(&si->ntmp_user, table, count);
}
EXPORT_SYMBOL_GPL(enetc4_set_rss_table);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2021 NXP
*
* The Integrated Endpoint Register Block (IERB) is configured by pre-boot
* software and is supposed to be to ENETC what a NVRAM is to a 'real' PCIe
* card. Upon FLR, values from the IERB are transferred to the ENETC PFs, and
* are read-only in the PF memory space.
*
* This driver fixes up the power-on reset values for the ENETC shared FIFO,
* such that the TX and RX allocations are sufficient for jumbo frames, and
* that intelligent FIFO dropping is enabled before the internal data
* structures are corrupted.
*
* Even though not all ports might be used on a given board, we are not
* concerned with partitioning the FIFO, because the default values configure
* no strict reservations, so the entire FIFO can be used by the RX of a single
* port, or the TX of a single port.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include "enetc.h"
#include "enetc_ierb.h"
/* IERB registers */
#define ENETC_IERB_TXMBAR(port) (((port) * 0x100) + 0x8080)
#define ENETC_IERB_RXMBER(port) (((port) * 0x100) + 0x8090)
#define ENETC_IERB_RXMBLR(port) (((port) * 0x100) + 0x8094)
#define ENETC_IERB_RXBCR(port) (((port) * 0x100) + 0x80a0)
#define ENETC_IERB_TXBCR(port) (((port) * 0x100) + 0x80a8)
#define ENETC_IERB_FMBDTR 0xa000
#define ENETC_RESERVED_FOR_ICM 1024
struct enetc_ierb {
void __iomem *regs;
};
static void enetc_ierb_write(struct enetc_ierb *ierb, u32 offset, u32 val)
{
iowrite32(val, ierb->regs + offset);
}
int enetc_ierb_register_pf(struct platform_device *pdev,
struct pci_dev *pf_pdev)
{
struct enetc_ierb *ierb = platform_get_drvdata(pdev);
int port = enetc_pf_to_port(pf_pdev);
u16 tx_credit, rx_credit, tx_alloc;
if (port < 0)
return -ENODEV;
if (!ierb)
return -EPROBE_DEFER;
/* By default, it is recommended to set the Host Transfer Agent
* per port transmit byte credit to "1000 + max_frame_size/2".
* The power-on reset value (1800 bytes) is rounded up to the nearest
* 100 assuming a maximum frame size of 1536 bytes.
*/
tx_credit = roundup(1000 + ENETC_MAC_MAXFRM_SIZE / 2, 100);
/* Internal memory allocated for transmit buffering is guaranteed but
* not reserved; i.e. if the total transmit allocation is not used,
* then the unused portion is not left idle, it can be used for receive
* buffering but it will be reclaimed, if required, from receive by
* intelligently dropping already stored receive frames in the internal
* memory to ensure that the transmit allocation is respected.
*
* PaTXMBAR must be set to a value larger than
* PaTXBCR + 2 * max_frame_size + 32
* if frame preemption is not enabled, or to
* 2 * PaTXBCR + 2 * p_max_frame_size (pMAC maximum frame size) +
* 2 * np_max_frame_size (eMAC maximum frame size) + 64
* if frame preemption is enabled.
*/
tx_alloc = roundup(2 * tx_credit + 4 * ENETC_MAC_MAXFRM_SIZE + 64, 16);
/* Initial credits, in units of 8 bytes, to the Ingress Congestion
* Manager for the maximum amount of bytes the port is allocated for
* pending traffic.
* It is recommended to set the initial credits to 2 times the maximum
* frame size (2 frames of maximum size).
*/
rx_credit = DIV_ROUND_UP(ENETC_MAC_MAXFRM_SIZE * 2, 8);
enetc_ierb_write(ierb, ENETC_IERB_TXBCR(port), tx_credit);
enetc_ierb_write(ierb, ENETC_IERB_TXMBAR(port), tx_alloc);
enetc_ierb_write(ierb, ENETC_IERB_RXBCR(port), rx_credit);
return 0;
}
EXPORT_SYMBOL(enetc_ierb_register_pf);
static int enetc_ierb_probe(struct platform_device *pdev)
{
struct enetc_ierb *ierb;
void __iomem *regs;
ierb = devm_kzalloc(&pdev->dev, sizeof(*ierb), GFP_KERNEL);
if (!ierb)
return -ENOMEM;
regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(regs))
return PTR_ERR(regs);
ierb->regs = regs;
/* Free buffer depletion threshold in bytes.
* This sets the minimum amount of free buffer memory that should be
* maintained in the datapath sub system, and when the amount of free
* buffer memory falls below this threshold, a depletion indication is
* asserted, which may trigger "intelligent drop" frame releases from
* the ingress queues in the ICM.
* It is recommended to set the free buffer depletion threshold to 1024
* bytes, since the ICM needs some FIFO memory for its own use.
*/
enetc_ierb_write(ierb, ENETC_IERB_FMBDTR, ENETC_RESERVED_FOR_ICM);
platform_set_drvdata(pdev, ierb);
return 0;
}
static const struct of_device_id enetc_ierb_match[] = {
{ .compatible = "fsl,ls1028a-enetc-ierb", },
{},
};
MODULE_DEVICE_TABLE(of, enetc_ierb_match);
static struct platform_driver enetc_ierb_driver = {
.driver = {
.name = "fsl-enetc-ierb",
.of_match_table = enetc_ierb_match,
},
.probe = enetc_ierb_probe,
};
module_platform_driver(enetc_ierb_driver);
MODULE_DESCRIPTION("NXP ENETC IERB");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2021 NXP */
#include <linux/pci.h>
#include <linux/platform_device.h>
#if IS_ENABLED(CONFIG_FSL_ENETC_IERB)
int enetc_ierb_register_pf(struct platform_device *pdev,
struct pci_dev *pf_pdev);
#else
static inline int enetc_ierb_register_pf(struct platform_device *pdev,
struct pci_dev *pf_pdev)
{
return -EOPNOTSUPP;
}
#endif
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* Copyright 2025-2026 NXP
*
* The VSI-to-PSI message generic format:
*
* OFFSET 0 16 24 31
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x0 | CRC16 (big-endian) | CLASS ID | CMD ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x4 | PROTO VER | LEN | RESV | COOKIE| RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x8 | RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0xc | RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x10 | |
* 0x14 | |
* 0x18 | Message Body |
* 0x1c | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x20 | |
* ~ | Extended Message Body: LEN x 32B |
* 0x3e0 | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Field Descriptions:
* CRC16 (16-bit): Big endian, CRC16 CCITT-FALSE algorithm, It provides the
* equivalent data integrity check functionality as the FCS for standard
* Ethernet frames.
*
* CLASS ID (8-bit) and CMD ID (8-bit): These are 8-bit fields identifying
* the command class and the class-specific operations supported. For more
* details, please refer to the definitions of the relevant class ID and
* cmd ID in this document.
*
* PROTO VER (8-bit): Supported VSI-PSI command protocol version. Currently
* only support version 0. To be incremented for future protocol extensions.
*
* LEN (8-bit): Extended message body length in increments of 32B. The upper
* limit is given by the physical implementation of the NETC VSI-PSI Messaging
* mechanism that supports message sizes of up to 1024B (including headers),
* that are multiple of 32B.
*
* COOKIE (4-bit): Optional parameter, which, if not 0, indicates that the
* command should be execute asynchronously on PSI side. If COOKIE is not 0
* and the command cannot be executed instantly on the PSI side (it would
* take longer time to complete), the PSI may enqueue the request in a command
* queue of up to 15 entries per VSI and, later after command execution, the
* PSI returns the COOKIE to VSI as part of an asynchronous notification
* message that indicates the command completion status. If COOKIE is 0 then
* the command is considered as blocking, the PSI will wait for the execution
* of the command to complete before updating the PSIMSGRR[MC] field with the
* corresponding return code.
*
* The PSI-to-VSI message generic format:
* 0 4 8 12 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | COOKIE | CLASS CODE | CLASS ID |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* The PSI to VSI message format is mapped to the following PSI message
* registers/fields, depending on use case:
* 1) PSI_RX_control: PSIMSGRR[MC] - for VSI command return code messages
* (blocking requests), and
* 2) PSI_TX_control: PSIMSGSR[MC] - for PSI to VSI notification messages
* (async mode)
*
* Note that for some GET messages, there is no COOKIE field, and the CLASS
* CODE field is expanded to 8 bits.
*/
#ifndef __ENETC_MAILBOX_H
#define __ENETC_MAILBOX_H
#include <linux/crc-itu-t.h>
#define ENETC_CRC_INIT 0xffff
#define ENETC_MSG_ALIGN 32
/* s indicates the size of the message */
#define ENETC_MSG_EXT_BODY_LEN(s) ((s) / ENETC_MSG_ALIGN - 1)
/* l indicates the extended body len (LEN field) of the message */
#define ENETC_MSG_SIZE(l) (((l) + 1) * ENETC_MSG_ALIGN)
/* The cookie filed of VSI-to-PSI message */
#define ENETC_VF_MSG_COOKIE GENMASK(3, 0)
/* The fileds of PSI-to-VSI message, the message is only 16-bit */
#define ENETC_PF_MSG_COOKIE GENMASK(3, 0)
#define ENETC_PF_MSG_CLASS_CODE GENMASK(7, 4)
/* Extend the class code to 8-bit for GET messages without COOKIE */
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
enum enetc_msg_class_id {
/* Class ID for PSI-to-VSI messages */
ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1,
ENETC_MSG_CLASS_ID_PERMISSION_DENY,
ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT,
ENETC_MSG_CLASS_ID_PSI_BUSY,
ENETC_MSG_CLASS_ID_CRC_ERROR,
ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT,
ENETC_MSG_CLASS_ID_INVALID_MSG_LEN,
ENETC_MSG_CLASS_ID_CMD_TIMEOUT,
ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED,
ENETC_MSG_CLASS_ID_CMD_FAIL, /* Generic error code for failure */
ENETC_MSG_CLASS_ID_CMD_DEFERRED = 0xf,
/* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */
ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20,
ENETC_MSG_CLASS_ID_IP_REVISION = 0xf0,
};
enum enetc_msg_mac_filter_cmd_id {
ENETC_MSG_SET_PRIMARY_MAC,
};
enum enetc_msg_ip_revision_cmd_id {
ENETC_MSG_GET_IP_MN = 1,
};
/* Class-specific error return codes of MAC filter */
enum enetc_mac_filter_class_code {
ENETC_MF_CLASS_CODE_INVALID_MAC,
};
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
int size;
};
/* The generic VSI-to-PSI message header */
struct enetc_msg_header {
__be16 crc16;
u8 class_id;
u8 cmd_id;
u8 proto_ver;
u8 len;
u8 resv0;
u8 cookie;
u8 resv2[8];
};
struct enetc_mac_addr {
u8 addr[ETH_ALEN]; /* Network byte order */
};
/* Message format of class_id 0x20 for exact MAC filter.
* cmd_id 0x0: set primary MAC
* cmd_id 0x1: Add entries to MAC address filter table
* cmd_id 0x2: Delete entries from MAC address filter table
* Note that cmd_id 0x1 and 0x2 are not supported yet.
*/
struct enetc_msg_mac_exact_filter {
struct enetc_msg_header hdr;
u8 mac_cnt; /* No need to set for cmd_id 0 */
u8 resv[3];
struct enetc_mac_addr mac[];
};
/* The generic message format applies to the following messages:
* Get IP revision message, class_id 0xf0.
* cmd_id 1: get IP minor revision
*/
struct enetc_msg_generic {
struct enetc_msg_header hdr;
u8 resv[16];
};
#endif
@@ -0,0 +1,236 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */
#include <linux/fsl/enetc_mdio.h>
#include <linux/mdio.h>
#include <linux/of_mdio.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include "enetc_pf.h"
#define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */
#define ENETC_MDIO_CTL 0x4 /* MDIO control */
#define ENETC_MDIO_DATA 0x8 /* MDIO data */
#define ENETC_MDIO_ADDR 0xc /* MDIO address */
#define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8)
#define MDIO_CFG_BSY BIT(0)
#define MDIO_CFG_RD_ER BIT(1)
#define MDIO_CFG_HOLD(x) (((x) << 2) & GENMASK(4, 2))
#define MDIO_CFG_ENC45 BIT(6)
/* external MDIO only - driven on neg MDC edge */
#define MDIO_CFG_NEG BIT(23)
#define ENETC_EMDIO_CFG \
(MDIO_CFG_HOLD(2) | \
MDIO_CFG_CLKDIV(258) | \
MDIO_CFG_NEG)
#define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f)
#define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5)
#define MDIO_CTL_READ BIT(15)
static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
{
return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
}
static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
u32 val)
{
enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
}
static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv)
{
return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY;
}
static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
{
bool is_busy;
return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv,
is_busy, !is_busy, 10, 10 * 1000);
}
int enetc_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,
u16 value)
{
struct enetc_mdio_priv *mdio_priv = bus->priv;
u32 mdio_ctl, mdio_cfg;
u16 dev_addr;
int ret;
mdio_cfg = ENETC_EMDIO_CFG;
dev_addr = regnum & 0x1f;
mdio_cfg &= ~MDIO_CFG_ENC45;
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* set port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
/* write the value */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(enetc_mdio_write_c22);
int enetc_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
int regnum, u16 value)
{
struct enetc_mdio_priv *mdio_priv = bus->priv;
u32 mdio_ctl, mdio_cfg;
int ret;
mdio_cfg = ENETC_EMDIO_CFG;
mdio_cfg |= MDIO_CFG_ENC45;
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* set port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
/* set the register address */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* write the value */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(enetc_mdio_write_c45);
int enetc_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
{
struct enetc_mdio_priv *mdio_priv = bus->priv;
u32 mdio_ctl, mdio_cfg;
u16 dev_addr, value;
int ret;
mdio_cfg = ENETC_EMDIO_CFG;
dev_addr = regnum & 0x1f;
mdio_cfg &= ~MDIO_CFG_ENC45;
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* set port and device addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
/* initiate the read */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* return all Fs if nothing was there */
if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
dev_dbg(&bus->dev,
"Error while reading PHY%d reg at %d.%d\n",
phy_id, dev_addr, regnum);
return 0xffff;
}
value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
return value;
}
EXPORT_SYMBOL_GPL(enetc_mdio_read_c22);
int enetc_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr,
int regnum)
{
struct enetc_mdio_priv *mdio_priv = bus->priv;
u32 mdio_ctl, mdio_cfg;
u16 value;
int ret;
mdio_cfg = ENETC_EMDIO_CFG;
mdio_cfg |= MDIO_CFG_ENC45;
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* set port and device addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
/* set the register address */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* initiate the read */
enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
ret = enetc_mdio_wait_complete(mdio_priv);
if (ret)
return ret;
/* return all Fs if nothing was there */
if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
dev_dbg(&bus->dev,
"Error while reading PHY%d reg at %d.%d\n",
phy_id, dev_addr, regnum);
return 0xffff;
}
value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
return value;
}
EXPORT_SYMBOL_GPL(enetc_mdio_read_c45);
struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
{
struct enetc_hw *hw;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return ERR_PTR(-ENOMEM);
hw->port = port_regs;
return hw;
}
EXPORT_SYMBOL_GPL(enetc_hw_alloc);
/* Lock for MDIO access errata on LS1028A */
DEFINE_RWLOCK(enetc_mdio_lock);
EXPORT_SYMBOL_GPL(enetc_mdio_lock);
@@ -0,0 +1,377 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
#include "enetc_pf_common.h"
#define ENETC_PF_MSG_SUCCESS FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
ENETC_MSG_CLASS_ID_CMD_SUCCESS)
#define ENETC_PF_MSG_NOTSUPP FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT)
static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
{
struct enetc_hw *hw = &pf->si->hw;
u32 psiier;
psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
/* disable MR int source(s) */
enetc_wr(hw, ENETC_PSIIER, psiier);
}
static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
{
struct enetc_hw *hw = &pf->si->hw;
u32 psiier;
psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
enetc_wr(hw, ENETC_PSIIER, psiier);
}
static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
{
struct enetc_si *si = (struct enetc_si *)data;
struct enetc_pf *pf = enetc_si_priv(si);
enetc_msg_disable_mr_int(pf);
schedule_work(&pf->msg_task);
return IRQ_HANDLED;
}
/* Messaging */
static bool enetc_msg_check_crc16(void *msg_addr, u32 msg_size)
{
u32 data_size = msg_size - 2;
u8 *data_buf = msg_addr + 2;
u16 verify_val;
verify_val = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size);
verify_val = crc_itu_t(verify_val, msg_addr, 2);
if (verify_val)
return false;
return true;
}
static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
void *vf_msg)
{
struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
struct enetc_msg_mac_exact_filter *msg = vf_msg;
struct device *dev = &pf->si->pdev->dev;
char *addr = msg->mac[0].addr;
if (!is_valid_ether_addr(addr)) {
dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
vf_id);
return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_MAC_FILTER) |
FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
ENETC_MF_CLASS_CODE_INVALID_MAC));
}
mutex_lock(&vf_state->lock);
if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
mutex_unlock(&vf_state->lock);
dev_err_ratelimited(dev,
"VF%d attempted to override PF set MAC\n",
vf_id);
return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED);
}
enetc_set_si_hw_addr(pf, vf_id + 1, addr);
mutex_unlock(&vf_state->lock);
return ENETC_PF_MSG_SUCCESS;
}
static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
void *vf_msg)
{
struct enetc_msg_header *msg_hdr = vf_msg;
switch (msg_hdr->cmd_id) {
case ENETC_MSG_SET_PRIMARY_MAC:
return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg);
default:
return ENETC_PF_MSG_NOTSUPP;
}
}
static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
{
struct enetc_msg_header *msg_hdr = vf_msg;
switch (msg_hdr->cmd_id) {
case ENETC_MSG_GET_IP_MN:
return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_IP_REVISION) |
FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8,
pf->si->revision));
default:
return ENETC_PF_MSG_NOTSUPP;
}
}
static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
u16 *pf_msg)
{
struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id];
struct enetc_msg_header *msg_hdr = msg_swbd->vaddr;
u32 msg_size = ENETC_MSG_SIZE(msg_hdr->len);
struct device *dev = &pf->si->pdev->dev;
u8 *msg;
if (msg_size > ENETC_DEFAULT_MSG_SIZE) {
dev_err_ratelimited(dev,
"Invalid message size: %u\n", msg_size);
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_INVALID_MSG_LEN);
return;
}
/* To prevent malicious VF from tampering with the original data by
* sending new messages after passing the check, the DMA buffer data
* is copied to the msg buffer before validation.
*/
msg = kzalloc_objs(*msg, msg_size);
if (!msg) {
dev_err_ratelimited(dev,
"Failed to allocate message buffer\n");
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CMD_FAIL);
return;
}
memcpy(msg, msg_swbd->vaddr, msg_size);
if (!enetc_msg_check_crc16(msg, msg_size)) {
dev_err_ratelimited(dev, "VSI to PSI Message CRC16 error\n");
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CRC_ERROR);
goto free_msg;
}
/* Default to not supported */
*pf_msg = ENETC_PF_MSG_NOTSUPP;
msg_hdr = (struct enetc_msg_header *)msg;
/* Currently, asynchronous actions are not supported */
if (FIELD_GET(ENETC_VF_MSG_COOKIE, msg_hdr->cookie)) {
dev_err_ratelimited(dev,
"Cookie field is not supported yet\n");
goto free_msg;
}
/* Currently only support protocol version 0 */
if (msg_hdr->proto_ver) {
dev_err_ratelimited(dev, "Unsupported protocol version %u\n",
msg_hdr->proto_ver);
goto free_msg;
}
/* The new messages are currently only supported on ENETC v4. If v1
* requires them, the current restriction can be lifted.
*/
if (is_enetc_rev1(pf->si) &&
!(msg_hdr->class_id == ENETC_MSG_CLASS_ID_MAC_FILTER &&
msg_hdr->cmd_id == ENETC_MSG_SET_PRIMARY_MAC)) {
dev_err_ratelimited(dev, "Unsupported message for ENETC v1\n");
goto free_msg;
}
switch (msg_hdr->class_id) {
case ENETC_MSG_CLASS_ID_MAC_FILTER:
*pf_msg = enetc_msg_handle_mac_filter(pf, vf_id, msg);
break;
case ENETC_MSG_CLASS_ID_IP_REVISION:
*pf_msg = enetc_msg_handle_ip_revision(pf, msg);
break;
default:
dev_err_ratelimited(dev,
"Unsupported message class ID: 0x%x\n",
msg_hdr->class_id);
}
free_msg:
kfree(msg);
}
static void enetc_msg_task(struct work_struct *work)
{
struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
struct enetc_hw *hw = &pf->si->hw;
u32 mr_status;
int i;
mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
(enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
if (!mr_status)
goto out;
for (i = 0; i < pf->num_vfs; i++) {
u32 psimsgrr;
u16 msg_code;
if (!(ENETC_PSIMR_BIT(i) & mr_status))
continue;
enetc_msg_handle_rxmsg(pf, i, &msg_code);
/* w1c to clear the corresponding VF MR bit */
enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));
psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
}
out:
enetc_msg_enable_mr_int(pf);
}
/* Init */
static int enetc_msg_alloc_mbx(struct enetc_si *si, int idx)
{
struct enetc_pf *pf = enetc_si_priv(si);
struct device *dev = &si->pdev->dev;
struct enetc_hw *hw = &si->hw;
struct enetc_msg_swbd *msg;
u32 val;
msg = &pf->rxmsg[idx];
/* allocate and set receive buffer */
msg->size = ENETC_DEFAULT_MSG_SIZE;
msg->vaddr = dma_alloc_coherent(dev, msg->size, &msg->dma,
GFP_KERNEL);
if (!msg->vaddr) {
dev_err(dev, "msg: fail to alloc dma buffer of size: %d\n",
msg->size);
return -ENOMEM;
}
/* set multiple of 32 bytes */
val = lower_32_bits(msg->dma);
enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), val);
val = upper_32_bits(msg->dma);
enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), val);
return 0;
}
static void enetc_msg_free_mbx(struct enetc_si *si, int idx)
{
struct enetc_pf *pf = enetc_si_priv(si);
struct enetc_hw *hw = &si->hw;
struct enetc_msg_swbd *msg;
enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
msg = &pf->rxmsg[idx];
dma_free_coherent(&si->pdev->dev, msg->size, msg->vaddr, msg->dma);
memset(msg, 0, sizeof(*msg));
}
static int enetc_msg_psi_init(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
int vector, i, err;
for (i = 0; i < pf->num_vfs; i++) {
err = enetc_msg_alloc_mbx(si, i);
if (err)
goto free_mbx;
}
/* initialize PSI mailbox */
INIT_WORK(&pf->msg_task, enetc_msg_task);
/* register message passing interrupt handler */
snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
si->ndev->name);
vector = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX);
err = request_irq(vector, enetc_msg_psi_msix, 0, pf->msg_int_name, si);
if (err) {
dev_err(&si->pdev->dev,
"PSI messaging: request_irq() failed!\n");
goto free_mbx;
}
/* set one IRQ entry for PSI message receive notification (SI int) */
enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
/* enable MR interrupts */
enetc_msg_enable_mr_int(pf);
return 0;
free_mbx:
for (i--; i >= 0; i--)
enetc_msg_free_mbx(si, i);
return err;
}
static void enetc_msg_psi_free(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
int i;
/* disable MR interrupts */
enetc_msg_disable_mr_int(pf);
/* de-register message passing interrupt handler */
free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
cancel_work_sync(&pf->msg_task);
/* MR interrupts may be re-enabled by workqueue */
enetc_msg_disable_mr_int(pf);
for (i = 0; i < pf->num_vfs; i++)
enetc_msg_free_mbx(si, i);
}
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_pf *pf = enetc_si_priv(si);
int err;
if (!num_vfs) {
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
} else {
pf->num_vfs = num_vfs;
err = enetc_msg_psi_init(pf);
if (err) {
dev_err(&pdev->dev, "enetc_msg_psi_init (%d)\n", err);
goto err_msg_psi;
}
err = pci_enable_sriov(pdev, num_vfs);
if (err) {
dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err);
goto err_en_sriov;
}
}
return num_vfs;
err_en_sriov:
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
return err;
}
EXPORT_SYMBOL_GPL(enetc_sriov_configure);
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */
#include <linux/fsl/enetc_mdio.h>
#include <linux/of_mdio.h>
#include "enetc_pf.h"
#define NETC_EMDIO_VEN_ID 0x1131
#define NETC_EMDIO_DEV_ID 0xee00
#define ENETC_MDIO_DEV_ID 0xee01
#define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO"
#define ENETC_MDIO_BUS_NAME ENETC_MDIO_DEV_NAME " Bus"
#define ENETC_MDIO_DRV_NAME ENETC_MDIO_DEV_NAME " driver"
DEFINE_STATIC_KEY_FALSE(enetc_has_err050089);
EXPORT_SYMBOL_GPL(enetc_has_err050089);
static void enetc_emdio_enable_err050089(struct pci_dev *pdev)
{
if (pdev->vendor == PCI_VENDOR_ID_FREESCALE &&
pdev->device == ENETC_MDIO_DEV_ID) {
static_branch_inc(&enetc_has_err050089);
dev_info(&pdev->dev, "Enabled ERR050089 workaround\n");
}
}
static void enetc_emdio_disable_err050089(struct pci_dev *pdev)
{
if (pdev->vendor == PCI_VENDOR_ID_FREESCALE &&
pdev->device == ENETC_MDIO_DEV_ID) {
static_branch_dec(&enetc_has_err050089);
if (!static_key_enabled(&enetc_has_err050089.key))
dev_info(&pdev->dev, "Disabled ERR050089 workaround\n");
}
}
static int enetc_pci_mdio_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct enetc_mdio_priv *mdio_priv;
struct device *dev = &pdev->dev;
void __iomem *port_regs;
struct enetc_hw *hw;
struct mii_bus *bus;
int err;
port_regs = pci_iomap(pdev, 0, 0);
if (!port_regs) {
dev_err(dev, "iomap failed\n");
err = -ENXIO;
goto err_ioremap;
}
hw = enetc_hw_alloc(dev, port_regs);
if (IS_ERR(hw)) {
err = PTR_ERR(hw);
goto err_hw_alloc;
}
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus) {
err = -ENOMEM;
goto err_mdiobus_alloc;
}
bus->name = ENETC_MDIO_BUS_NAME;
bus->read = enetc_mdio_read_c22;
bus->write = enetc_mdio_write_c22;
bus->read_c45 = enetc_mdio_read_c45;
bus->write_c45 = enetc_mdio_write_c45;
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = hw;
mdio_priv->mdio_base = ENETC_EMDIO_BASE;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
pcie_flr(pdev);
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(dev, "device enable failed\n");
goto err_pci_enable;
}
err = pci_request_region(pdev, 0, KBUILD_MODNAME);
if (err) {
dev_err(dev, "pci_request_region failed\n");
goto err_pci_mem_reg;
}
enetc_emdio_enable_err050089(pdev);
err = of_mdiobus_register(bus, dev->of_node);
if (err)
goto err_mdiobus_reg;
pci_set_drvdata(pdev, bus);
return 0;
err_mdiobus_reg:
enetc_emdio_disable_err050089(pdev);
pci_release_region(pdev, 0);
err_pci_mem_reg:
pci_disable_device(pdev);
err_pci_enable:
err_mdiobus_alloc:
err_hw_alloc:
iounmap(port_regs);
err_ioremap:
return err;
}
static void enetc_pci_mdio_remove(struct pci_dev *pdev)
{
struct mii_bus *bus = pci_get_drvdata(pdev);
struct enetc_mdio_priv *mdio_priv;
mdiobus_unregister(bus);
enetc_emdio_disable_err050089(pdev);
mdio_priv = bus->priv;
iounmap(mdio_priv->hw->port);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
}
static const struct pci_device_id enetc_pci_mdio_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_MDIO_DEV_ID) },
{ PCI_DEVICE(NETC_EMDIO_VEN_ID, NETC_EMDIO_DEV_ID) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc_pci_mdio_id_table);
static struct pci_driver enetc_pci_mdio_driver = {
.name = KBUILD_MODNAME,
.id_table = enetc_pci_mdio_id_table,
.probe = enetc_pci_mdio_probe,
.remove = enetc_pci_mdio_remove,
};
module_pci_driver(enetc_pci_mdio_driver);
MODULE_DESCRIPTION(ENETC_MDIO_DRV_NAME);
MODULE_LICENSE("Dual BSD/GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2017-2019 NXP */
#include "enetc.h"
#include <linux/phylink.h>
#define ENETC_PF_NUM_RINGS 8
#define ENETC_VLAN_HT_SIZE 64
enum enetc_vf_flags {
ENETC_VF_FLAG_PF_SET_MAC = BIT(0),
};
struct enetc_vf_state {
struct mutex lock; /* Prevent concurrent access */
enum enetc_vf_flags flags;
};
struct enetc_port_caps {
u32 half_duplex:1;
int num_vsi;
int num_msix;
int num_rx_bdr;
int num_tx_bdr;
int mac_filter_num;
};
struct enetc_pf;
struct enetc_pf_ops {
void (*set_si_primary_mac)(struct enetc_hw *hw, int si, const u8 *addr);
void (*get_si_primary_mac)(struct enetc_hw *hw, int si, u8 *addr);
struct phylink_pcs *(*create_pcs)(struct enetc_pf *pf, struct mii_bus *bus);
void (*destroy_pcs)(struct phylink_pcs *pcs);
int (*enable_psfp)(struct enetc_ndev_priv *priv);
};
struct enetc_pf {
struct enetc_si *si;
int num_vfs; /* number of active VFs, after sriov_init */
int total_vfs; /* max number of VFs, set for PF at probe */
struct enetc_vf_state *vf_state;
struct enetc_mac_filter mac_filter[MADDR_TYPE];
struct enetc_msg_swbd *rxmsg;
struct work_struct msg_task;
char msg_int_name[ENETC_INT_NAME_MAX];
char vlan_promisc_simap; /* bitmap of SIs in VLAN promisc mode */
DECLARE_BITMAP(vlan_ht_filter, ENETC_VLAN_HT_SIZE);
DECLARE_BITMAP(active_vlans, VLAN_N_VID);
struct mii_bus *mdio; /* saved for cleanup */
struct mii_bus *imdio;
struct phylink_pcs *pcs;
phy_interface_t if_mode;
struct phylink_config phylink_config;
struct enetc_port_caps caps;
const struct enetc_pf_ops *ops;
int num_mfe; /* number of mac address filter table entries */
};
#define phylink_to_enetc_pf(config) \
container_of((config), struct enetc_pf, phylink_config)
@@ -0,0 +1,466 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2024 NXP */
#include <linux/fsl/enetc_mdio.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include "enetc_pf_common.h"
void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr)
{
struct enetc_hw *hw = &pf->si->hw;
pf->ops->set_si_primary_mac(hw, si, mac_addr);
}
EXPORT_SYMBOL_GPL(enetc_set_si_hw_addr);
static void enetc_get_si_hw_addr(struct enetc_pf *pf, int si, u8 *mac_addr)
{
struct enetc_hw *hw = &pf->si->hw;
pf->ops->get_si_primary_mac(hw, si, mac_addr);
}
int enetc_pf_set_mac_addr(struct net_device *ndev, void *addr)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
struct sockaddr *saddr = addr;
if (!is_valid_ether_addr(saddr->sa_data))
return -EADDRNOTAVAIL;
eth_hw_addr_set(ndev, saddr->sa_data);
enetc_set_si_hw_addr(pf, 0, saddr->sa_data);
return 0;
}
EXPORT_SYMBOL_GPL(enetc_pf_set_mac_addr);
static int enetc_setup_mac_address(struct device_node *np, struct enetc_pf *pf,
int si)
{
struct device *dev = &pf->si->pdev->dev;
u8 mac_addr[ETH_ALEN] = { 0 };
int err;
/* (1) try to get the MAC address from the device tree */
if (np) {
err = of_get_mac_address(np, mac_addr);
if (err == -EPROBE_DEFER)
return err;
}
/* (2) bootloader supplied MAC address */
if (is_zero_ether_addr(mac_addr))
enetc_get_si_hw_addr(pf, si, mac_addr);
/* (3) choose a random one */
if (is_zero_ether_addr(mac_addr)) {
eth_random_addr(mac_addr);
dev_info(dev, "no MAC address specified for SI%d, using %pM\n",
si, mac_addr);
}
enetc_set_si_hw_addr(pf, si, mac_addr);
return 0;
}
int enetc_setup_mac_addresses(struct device_node *np, struct enetc_pf *pf)
{
int err, i;
/* The PF might take its MAC from the device tree */
err = enetc_setup_mac_address(np, pf, 0);
if (err)
return err;
for (i = 0; i < pf->total_vfs; i++) {
err = enetc_setup_mac_address(NULL, pf, i + 1);
if (err)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(enetc_setup_mac_addresses);
void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
const struct net_device_ops *ndev_ops)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(si);
SET_NETDEV_DEV(ndev, &si->pdev->dev);
priv->ndev = ndev;
priv->si = si;
priv->dev = &si->pdev->dev;
si->ndev = ndev;
priv->msg_enable = (NETIF_MSG_WOL << 1) - 1;
priv->sysclk_freq = si->drvdata->sysclk_freq;
priv->max_frags = si->drvdata->max_frags;
ndev->netdev_ops = ndev_ops;
enetc_set_ethtool_ops(ndev);
ndev->watchdog_timeo = 5 * HZ;
ndev->max_mtu = ENETC_MAX_MTU;
ndev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
ndev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
ndev->vlan_features = NETIF_F_SG | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_TSO6;
ndev->priv_flags |= IFF_UNICAST_FLT;
if (si->drvdata->tx_csum)
priv->active_offloads |= ENETC_F_TXCSUM;
if (si->hw_features & ENETC_SI_F_LSO)
priv->active_offloads |= ENETC_F_LSO;
if (si->num_rss) {
ndev->hw_features |= NETIF_F_RXHASH;
ndev->features |= NETIF_F_RXHASH;
}
if (!enetc_is_pseudo_mac(si))
ndev->hw_features |= NETIF_F_LOOPBACK;
/* TODO: currently, i.MX95 ENETC driver does not support advanced features */
if (!is_enetc_rev1(si))
goto end;
ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_NDO_XMIT | NETDEV_XDP_ACT_RX_SG |
NETDEV_XDP_ACT_NDO_XMIT_SG;
if (si->hw_features & ENETC_SI_F_PSFP && pf->ops->enable_psfp &&
!pf->ops->enable_psfp(priv)) {
priv->active_offloads |= ENETC_F_QCI;
ndev->features |= NETIF_F_HW_TC;
ndev->hw_features |= NETIF_F_HW_TC;
}
end:
/* pick up primary MAC address from SI */
enetc_load_primary_mac_addr(&si->hw, ndev);
}
EXPORT_SYMBOL_GPL(enetc_pf_netdev_setup);
static int enetc_mdio_probe(struct enetc_pf *pf, struct device_node *np)
{
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv;
struct mii_bus *bus;
int err;
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = "Freescale ENETC MDIO Bus";
bus->read = enetc_mdio_read_c22;
bus->write = enetc_mdio_write_c22;
bus->read_c45 = enetc_mdio_read_c45;
bus->write_c45 = enetc_mdio_write_c45;
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
if (is_enetc_rev1(pf->si))
mdio_priv->mdio_base = ENETC_EMDIO_BASE;
else
mdio_priv->mdio_base = ENETC4_EMDIO_BASE;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
err = of_mdiobus_register(bus, np);
if (err)
return dev_err_probe(dev, err, "cannot register MDIO bus\n");
pf->mdio = bus;
return 0;
}
static void enetc_mdio_remove(struct enetc_pf *pf)
{
if (pf->mdio)
mdiobus_unregister(pf->mdio);
}
static int enetc_imdio_create(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv;
struct phylink_pcs *phylink_pcs;
struct mii_bus *bus;
int err;
if (!pf->ops->create_pcs) {
dev_err(dev, "Creating PCS is not supported\n");
return -EOPNOTSUPP;
}
bus = mdiobus_alloc_size(sizeof(*mdio_priv));
if (!bus)
return -ENOMEM;
bus->name = "Freescale ENETC internal MDIO Bus";
bus->read = enetc_mdio_read_c22;
bus->write = enetc_mdio_write_c22;
bus->read_c45 = enetc_mdio_read_c45;
bus->write_c45 = enetc_mdio_write_c45;
bus->parent = dev;
bus->phy_mask = ~0;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
if (is_enetc_rev1(pf->si))
mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE;
else
mdio_priv->mdio_base = ENETC4_PM_IMDIO_BASE;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
err = mdiobus_register(bus);
if (err) {
dev_err(dev, "cannot register internal MDIO bus (%d)\n", err);
goto free_mdio_bus;
}
phylink_pcs = pf->ops->create_pcs(pf, bus);
if (IS_ERR(phylink_pcs)) {
err = PTR_ERR(phylink_pcs);
dev_err(dev, "cannot create lynx pcs (%d)\n", err);
goto unregister_mdiobus;
}
pf->imdio = bus;
pf->pcs = phylink_pcs;
return 0;
unregister_mdiobus:
mdiobus_unregister(bus);
free_mdio_bus:
mdiobus_free(bus);
return err;
}
static void enetc_imdio_remove(struct enetc_pf *pf)
{
if (pf->pcs && pf->ops->destroy_pcs)
pf->ops->destroy_pcs(pf->pcs);
if (pf->imdio) {
mdiobus_unregister(pf->imdio);
mdiobus_free(pf->imdio);
}
}
static bool enetc_port_has_pcs(struct enetc_pf *pf)
{
return (pf->if_mode == PHY_INTERFACE_MODE_SGMII ||
pf->if_mode == PHY_INTERFACE_MODE_1000BASEX ||
pf->if_mode == PHY_INTERFACE_MODE_2500BASEX ||
pf->if_mode == PHY_INTERFACE_MODE_USXGMII);
}
int enetc_mdiobus_create(struct enetc_pf *pf, struct device_node *node)
{
struct device_node *mdio_np;
int err;
mdio_np = of_get_child_by_name(node, "mdio");
if (mdio_np) {
err = enetc_mdio_probe(pf, mdio_np);
of_node_put(mdio_np);
if (err)
return err;
}
if (enetc_port_has_pcs(pf)) {
err = enetc_imdio_create(pf);
if (err) {
enetc_mdio_remove(pf);
return err;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(enetc_mdiobus_create);
void enetc_mdiobus_destroy(struct enetc_pf *pf)
{
enetc_mdio_remove(pf);
enetc_imdio_remove(pf);
}
EXPORT_SYMBOL_GPL(enetc_mdiobus_destroy);
int enetc_phylink_create(struct enetc_ndev_priv *priv, struct device_node *node,
const struct phylink_mac_ops *ops)
{
struct enetc_pf *pf = enetc_si_priv(priv->si);
struct phylink *phylink;
int err;
pf->phylink_config.dev = &priv->ndev->dev;
pf->phylink_config.type = PHYLINK_NETDEV;
pf->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
pf->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_SGMII,
pf->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
pf->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
pf->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_USXGMII,
pf->phylink_config.supported_interfaces);
phy_interface_set_rgmii(pf->phylink_config.supported_interfaces);
phylink = phylink_create(&pf->phylink_config, of_fwnode_handle(node),
pf->if_mode, ops);
if (IS_ERR(phylink)) {
err = PTR_ERR(phylink);
return err;
}
priv->phylink = phylink;
return 0;
}
EXPORT_SYMBOL_GPL(enetc_phylink_create);
void enetc_phylink_destroy(struct enetc_ndev_priv *priv)
{
phylink_destroy(priv->phylink);
}
EXPORT_SYMBOL_GPL(enetc_phylink_destroy);
void enetc_set_default_rss_key(struct enetc_pf *pf)
{
u8 hash_key[ENETC_RSSHASH_KEY_SIZE] = {0};
/* set up hash key */
get_random_bytes(hash_key, ENETC_RSSHASH_KEY_SIZE);
enetc_set_rss_key(pf->si, hash_key);
}
EXPORT_SYMBOL_GPL(enetc_set_default_rss_key);
static int enetc_vid_hash_idx(unsigned int vid)
{
int res = 0;
int i;
for (i = 0; i < 6; i++)
res |= (hweight8(vid & (BIT(i) | BIT(i + 6))) & 0x1) << i;
return res;
}
static void enetc_refresh_vlan_ht_filter(struct enetc_pf *pf)
{
int i;
bitmap_zero(pf->vlan_ht_filter, ENETC_VLAN_HT_SIZE);
for_each_set_bit(i, pf->active_vlans, VLAN_N_VID) {
int hidx = enetc_vid_hash_idx(i);
__set_bit(hidx, pf->vlan_ht_filter);
}
}
static void enetc_set_si_vlan_ht_filter(struct enetc_si *si,
int si_id, u64 hash)
{
struct enetc_hw *hw = &si->hw;
int high_reg_off, low_reg_off;
if (is_enetc_rev1(si)) {
low_reg_off = ENETC_PSIVHFR0(si_id);
high_reg_off = ENETC_PSIVHFR1(si_id);
} else {
low_reg_off = ENETC4_PSIVHFR0(si_id);
high_reg_off = ENETC4_PSIVHFR1(si_id);
}
enetc_port_wr(hw, low_reg_off, lower_32_bits(hash));
enetc_port_wr(hw, high_reg_off, upper_32_bits(hash));
}
int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
int idx;
__set_bit(vid, pf->active_vlans);
idx = enetc_vid_hash_idx(vid);
if (!__test_and_set_bit(idx, pf->vlan_ht_filter))
enetc_set_si_vlan_ht_filter(pf->si, 0, *pf->vlan_ht_filter);
return 0;
}
EXPORT_SYMBOL_GPL(enetc_vlan_rx_add_vid);
int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
if (__test_and_clear_bit(vid, pf->active_vlans)) {
enetc_refresh_vlan_ht_filter(pf);
enetc_set_si_vlan_ht_filter(pf->si, 0, *pf->vlan_ht_filter);
}
return 0;
}
EXPORT_SYMBOL_GPL(enetc_vlan_rx_del_vid);
int enetc_init_sriov_resources(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
pf->total_vfs = pci_sriov_get_totalvfs(pf->si->pdev);
if (!pf->total_vfs)
return 0;
pf->rxmsg = devm_kcalloc(dev, pf->total_vfs,
sizeof(struct enetc_msg_swbd),
GFP_KERNEL);
if (!pf->rxmsg)
return -ENOMEM;
pf->vf_state = devm_kcalloc(dev, pf->total_vfs,
sizeof(struct enetc_vf_state),
GFP_KERNEL);
if (!pf->vf_state)
return -ENOMEM;
for (int i = 0; i < pf->total_vfs; i++)
mutex_init(&pf->vf_state[i].lock);
return 0;
}
EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* Copyright 2024 NXP */
#include "enetc_pf.h"
void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr);
int enetc_pf_set_mac_addr(struct net_device *ndev, void *addr);
int enetc_setup_mac_addresses(struct device_node *np, struct enetc_pf *pf);
void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
const struct net_device_ops *ndev_ops);
int enetc_mdiobus_create(struct enetc_pf *pf, struct device_node *node);
void enetc_mdiobus_destroy(struct enetc_pf *pf);
int enetc_phylink_create(struct enetc_ndev_priv *priv, struct device_node *node,
const struct phylink_mac_ops *ops);
void enetc_phylink_destroy(struct enetc_ndev_priv *priv);
void enetc_set_default_rss_key(struct enetc_pf *pf);
int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid);
int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid);
int enetc_init_sriov_resources(struct enetc_pf *pf);
static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
{
return enetc_global_rd(hw, ENETC_G_EIPBRR0) & EIPBRR0_REVISION;
}
#if IS_ENABLED(CONFIG_PCI_IOV)
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs);
#else
static inline int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
return 0;
}
#endif
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2019 NXP */
#include <linux/module.h>
#include <linux/of.h>
#include <linux/fsl/ptp_qoriq.h>
#include "enetc.h"
static struct ptp_clock_info enetc_ptp_caps = {
.owner = THIS_MODULE,
.name = "ENETC PTP clock",
.max_adj = 512000,
.n_alarm = 0,
.n_ext_ts = 2,
.n_per_out = 0,
.n_pins = 0,
.pps = 1,
.adjfine = ptp_qoriq_adjfine,
.adjtime = ptp_qoriq_adjtime,
.gettime64 = ptp_qoriq_gettime,
.settime64 = ptp_qoriq_settime,
.enable = ptp_qoriq_enable,
};
static int enetc_ptp_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct ptp_qoriq *ptp_qoriq;
void __iomem *base;
int err, len, n;
if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
dev_info(&pdev->dev, "device is disabled, skipping\n");
return -ENODEV;
}
err = pci_enable_device_mem(pdev);
if (err)
return dev_err_probe(&pdev->dev, err, "device enable failed\n");
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_err(&pdev->dev, "DMA configuration failed: 0x%x\n", err);
goto err_dma;
}
err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev, "pci_request_regions failed err=%d\n", err);
goto err_pci_mem_reg;
}
pci_set_master(pdev);
ptp_qoriq = kzalloc_obj(*ptp_qoriq);
if (!ptp_qoriq) {
err = -ENOMEM;
goto err_alloc_ptp;
}
len = pci_resource_len(pdev, ENETC_BAR_REGS);
base = ioremap(pci_resource_start(pdev, ENETC_BAR_REGS), len);
if (!base) {
err = -ENXIO;
dev_err(&pdev->dev, "ioremap() failed\n");
goto err_ioremap;
}
/* Allocate 1 interrupt */
n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
if (n != 1) {
err = -EPERM;
goto err_irq_vectors;
}
ptp_qoriq->irq = pci_irq_vector(pdev, 0);
err = request_irq(ptp_qoriq->irq, ptp_qoriq_isr, 0, DRIVER, ptp_qoriq);
if (err) {
dev_err(&pdev->dev, "request_irq() failed!\n");
goto err_irq;
}
ptp_qoriq->dev = &pdev->dev;
err = ptp_qoriq_init(ptp_qoriq, base, &enetc_ptp_caps);
if (err)
goto err_no_clock;
pci_set_drvdata(pdev, ptp_qoriq);
return 0;
err_no_clock:
free_irq(ptp_qoriq->irq, ptp_qoriq);
err_irq:
pci_free_irq_vectors(pdev);
err_irq_vectors:
iounmap(base);
err_ioremap:
kfree(ptp_qoriq);
err_alloc_ptp:
pci_release_mem_regions(pdev);
err_pci_mem_reg:
err_dma:
pci_disable_device(pdev);
return err;
}
static void enetc_ptp_remove(struct pci_dev *pdev)
{
struct ptp_qoriq *ptp_qoriq = pci_get_drvdata(pdev);
ptp_qoriq_free(ptp_qoriq);
pci_free_irq_vectors(pdev);
kfree(ptp_qoriq);
pci_release_mem_regions(pdev);
pci_disable_device(pdev);
}
static const struct pci_device_id enetc_ptp_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PTP) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc_ptp_id_table);
static struct pci_driver enetc_ptp_driver = {
.name = KBUILD_MODNAME,
.id_table = enetc_ptp_id_table,
.probe = enetc_ptp_probe,
.remove = enetc_ptp_remove,
};
module_pci_driver(enetc_ptp_driver);
MODULE_DESCRIPTION("ENETC PTP clock driver");
MODULE_LICENSE("Dual BSD/GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,418 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
#include <linux/iopoll.h>
#include <linux/module.h>
#include "enetc.h"
#define ENETC_DRV_NAME_STR "ENETC VF driver"
/* Note: This function should be called after filling the message body,
* because the CRC16 needs to be calculated after all the data has been
* filled.
*/
static void enetc_msg_fill_common_hdr(struct enetc_msg_swbd *msg_swbd,
u8 class_id, u8 cmd_id, u8 proto_ver,
u8 cookie)
{
struct enetc_msg_header *hdr = msg_swbd->vaddr;
u8 *data_buf = ((u8 *)msg_swbd->vaddr) + 2; /* skip crc16 field */
u32 data_size = msg_swbd->size - 2;
u16 crc16;
hdr->class_id = class_id;
hdr->cmd_id = cmd_id;
hdr->len = ENETC_MSG_EXT_BODY_LEN(msg_swbd->size);
hdr->proto_ver = proto_ver;
hdr->cookie = FIELD_PREP(ENETC_VF_MSG_COOKIE, cookie);
crc16 = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size);
hdr->crc16 = htons(crc16);
}
static void enetc_msg_vsi_write_msg(struct enetc_hw *hw,
struct enetc_msg_swbd *msg)
{
u32 val;
val = enetc_vsi_set_msize(msg->size) | lower_32_bits(msg->dma);
enetc_wr(hw, ENETC_VSIMSGSNDAR1, upper_32_bits(msg->dma));
enetc_wr(hw, ENETC_VSIMSGSNDAR0, val);
}
static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg)
{
if (msg->vaddr) {
dma_free_coherent(dev, msg->size, msg->vaddr, msg->dma);
msg->vaddr = NULL;
}
}
static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
{
struct device *dev = &si->pdev->dev;
u32 vsimsgsr;
u16 pf_msg;
int err;
/* The VSI mailbox may be busy if last message was not yet processed
* by PSI. So need to check the mailbox status before sending.
*/
vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR);
if (vsimsgsr & ENETC_VSIMSGSR_MB) {
/* It is safe to free the DMA buffer here, the caller does
* not access the DMA buffer if enetc_msg_vsi_send() fails.
*/
enetc_msg_dma_free(dev, msg);
dev_err(dev, "VSI mailbox is busy\n");
return -EIO;
}
/* Free the DMA buffer of the last message */
enetc_msg_dma_free(dev, &si->msg);
si->msg = *msg;
enetc_msg_vsi_write_msg(&si->hw, msg);
err = read_poll_timeout(enetc_rd, vsimsgsr,
!(vsimsgsr & ENETC_VSIMSGSR_MB),
1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
if (err) {
dev_err(dev, "VSI mailbox timeout\n");
return err;
}
/* check for message delivery error */
if (vsimsgsr & ENETC_VSIMSGSR_MS) {
dev_err(dev, "Transfer error when copying the data\n");
return -EIO;
}
pf_msg = ENETC_SIMSGSR_GET_MC(vsimsgsr);
/* Check the user-defined completion status. */
if (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg) !=
ENETC_MSG_CLASS_ID_CMD_SUCCESS) {
switch (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg)) {
case ENETC_MSG_CLASS_ID_PERMISSION_DENY:
/* Intentionally returning early to prevent excessive
* error logs due to permission issues.
*/
return -EACCES;
case ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT:
case ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT:
err = -EOPNOTSUPP;
break;
case ENETC_MSG_CLASS_ID_PSI_BUSY:
err = -EBUSY;
break;
case ENETC_MSG_CLASS_ID_CMD_TIMEOUT:
err = -ETIME;
break;
case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN:
case ENETC_MSG_CLASS_ID_MAC_FILTER:
err = -EINVAL;
break;
case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED:
err = -EPERM;
break;
case ENETC_MSG_CLASS_ID_IP_REVISION:
err = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg);
break;
case ENETC_MSG_CLASS_ID_CMD_FAIL:
case ENETC_MSG_CLASS_ID_CRC_ERROR:
case ENETC_MSG_CLASS_ID_CMD_DEFERRED:
default:
err = -EIO;
}
}
if (err < 0)
dev_err(dev, "Return error code from PSI: 0x%04x\n", pf_msg);
return err;
}
static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
struct sockaddr *saddr)
{
struct enetc_msg_mac_exact_filter *msg;
struct enetc_msg_swbd msg_swbd;
u32 msg_size;
msg_size = struct_size(msg, mac, 1);
msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN);
msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size,
&msg_swbd.dma, GFP_KERNEL);
if (!msg_swbd.vaddr)
return -ENOMEM;
msg = (struct enetc_msg_mac_exact_filter *)msg_swbd.vaddr;
memcpy(&msg->mac[0].addr, saddr->sa_data, ETH_ALEN);
enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
ENETC_MSG_SET_PRIMARY_MAC, 0, 0);
/* send the command and wait */
return enetc_msg_vsi_send(priv->si, &msg_swbd);
}
static int enetc_vf_get_ip_minor_revision(struct enetc_si *si)
{
struct device *dev = &si->pdev->dev;
struct enetc_msg_swbd msg_swbd;
msg_swbd.size = ALIGN(sizeof(struct enetc_msg_generic),
ENETC_MSG_ALIGN);
msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size,
&msg_swbd.dma, GFP_KERNEL);
if (!msg_swbd.vaddr)
return -ENOMEM;
enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_IP_REVISION,
ENETC_MSG_GET_IP_MN, 0, 0);
return enetc_msg_vsi_send(si, &msg_swbd);
}
static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct sockaddr *saddr = addr;
int err;
if (!is_valid_ether_addr(saddr->sa_data))
return -EADDRNOTAVAIL;
err = enetc_msg_vsi_set_primary_mac_addr(priv, saddr);
if (err)
return err;
eth_hw_addr_set(ndev, saddr->sa_data);
return 0;
}
static int enetc_vf_set_features(struct net_device *ndev,
netdev_features_t features)
{
enetc_set_features(ndev, features);
return 0;
}
static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void *type_data)
{
switch (type) {
case TC_SETUP_QDISC_MQPRIO:
return enetc_setup_tc_mqprio(ndev, type_data);
default:
return -EOPNOTSUPP;
}
}
/* Probing/ Init */
static const struct net_device_ops enetc_ndev_ops = {
.ndo_open = enetc_open,
.ndo_stop = enetc_close,
.ndo_start_xmit = enetc_xmit,
.ndo_get_stats = enetc_get_stats,
.ndo_set_mac_address = enetc_vf_set_mac_addr,
.ndo_set_features = enetc_vf_set_features,
.ndo_eth_ioctl = enetc_ioctl,
.ndo_setup_tc = enetc_vf_setup_tc,
.ndo_hwtstamp_get = enetc_hwtstamp_get,
.ndo_hwtstamp_set = enetc_hwtstamp_set,
};
static void enetc_vf_get_revision(struct enetc_si *si)
{
int ip_mn;
if (is_enetc_rev1(si)) {
si->revision = ENETC_REV_1_0;
return;
}
ip_mn = enetc_vf_get_ip_minor_revision(si);
if (ip_mn >= 0) {
si->revision = (si->pdev->revision << 8) | ip_mn;
return;
}
si->revision = ENETC_REV_4_1;
dev_info(&si->pdev->dev,
"Failed to get revision, use compatible revision: 0x%04x\n",
si->revision);
}
static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
const struct net_device_ops *ndev_ops)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
SET_NETDEV_DEV(ndev, &si->pdev->dev);
priv->ndev = ndev;
priv->si = si;
priv->dev = &si->pdev->dev;
si->ndev = ndev;
priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1;
priv->sysclk_freq = si->drvdata->sysclk_freq;
priv->max_frags = si->drvdata->max_frags;
ndev->netdev_ops = ndev_ops;
enetc_set_ethtool_ops(ndev);
ndev->watchdog_timeo = 5 * HZ;
ndev->max_mtu = ENETC_MAX_MTU;
ndev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
ndev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
ndev->vlan_features = NETIF_F_SG | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_TSO6;
if (si->num_rss) {
ndev->hw_features |= NETIF_F_RXHASH;
ndev->features |= NETIF_F_RXHASH;
}
/* pick up primary MAC address from SI */
enetc_load_primary_mac_addr(&si->hw, ndev);
}
static const struct enetc_si_ops enetc_vsi_ops = {
.get_rss_table = enetc_get_rss_table,
.set_rss_table = enetc_set_rss_table,
.setup_cbdr = enetc_setup_cbdr,
.teardown_cbdr = enetc_teardown_cbdr,
};
static int enetc_vf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct enetc_ndev_priv *priv;
struct enetc_msg_swbd msg;
struct net_device *ndev;
struct enetc_si *si;
int err;
err = enetc_pci_probe(pdev, KBUILD_MODNAME, 0);
if (err)
return dev_err_probe(&pdev->dev, err, "PCI probing failed\n");
si = pci_get_drvdata(pdev);
enetc_vf_get_revision(si);
si->ops = &enetc_vsi_ops;
err = enetc_get_driver_data(si);
if (err) {
dev_err_probe(&pdev->dev, err,
"Could not get VF driver data\n");
goto err_get_driver_data;
}
enetc_get_si_caps(si);
ndev = alloc_etherdev_mq(sizeof(*priv), ENETC_MAX_NUM_TXQS);
if (!ndev) {
err = -ENOMEM;
dev_err(&pdev->dev, "netdev creation failed\n");
goto err_alloc_netdev;
}
enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops);
priv = netdev_priv(ndev);
enetc_init_si_rings_params(priv);
err = si->ops->setup_cbdr(si);
if (err)
goto err_setup_cbdr;
err = enetc_alloc_si_resources(priv);
if (err) {
dev_err(&pdev->dev, "SI resource alloc failed\n");
goto err_alloc_si_res;
}
err = enetc_configure_si(priv);
if (err) {
dev_err(&pdev->dev, "Failed to configure SI\n");
goto err_config_si;
}
err = enetc_alloc_msix(priv);
if (err) {
dev_err(&pdev->dev, "MSIX alloc failed\n");
goto err_alloc_msix;
}
err = register_netdev(ndev);
if (err)
goto err_reg_netdev;
netif_carrier_off(ndev);
return 0;
err_reg_netdev:
enetc_free_msix(priv);
err_config_si:
err_alloc_msix:
enetc_free_si_resources(priv);
err_alloc_si_res:
si->ops->teardown_cbdr(si);
err_setup_cbdr:
si->ndev = NULL;
free_netdev(ndev);
err_alloc_netdev:
err_get_driver_data:
msg = si->msg;
enetc_pci_remove(pdev);
enetc_msg_dma_free(&pdev->dev, &msg);
return err;
}
static void enetc_vf_remove(struct pci_dev *pdev)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_ndev_priv *priv;
struct enetc_msg_swbd msg;
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
enetc_free_msix(priv);
enetc_free_si_resources(priv);
si->ops->teardown_cbdr(si);
free_netdev(si->ndev);
msg = si->msg;
enetc_pci_remove(pdev);
enetc_msg_dma_free(&pdev->dev, &msg);
}
static const struct pci_device_id enetc_vf_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_VF) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc_vf_id_table);
static struct pci_driver enetc_vf_driver = {
.name = KBUILD_MODNAME,
.id_table = enetc_vf_id_table,
.probe = enetc_vf_probe,
.remove = enetc_vf_remove,
};
module_pci_driver(enetc_vf_driver);
MODULE_DESCRIPTION(ENETC_DRV_NAME_STR);
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,988 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* NXP NETC Blocks Control Driver
*
* Copyright 2024 NXP
*
* This driver is used for pre-initialization of NETC, such as PCS and MII
* protocols, LDID, warm reset, etc. Therefore, all NETC device drivers can
* only be probed after the netc-blk-crtl driver has completed initialization.
* In addition, when the system enters suspend mode, IERB, PRB, and NETCMIX
* will be powered off, except for WOL. Therefore, when the system resumes,
* these blocks need to be reinitialized.
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/fsl/netc_global.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
/* NETCMIX registers */
#define IMX95_CFG_LINK_IO_VAR 0x0
#define IO_VAR_16FF_16G_SERDES 0x1
#define IO_VAR(port, var) (((var) & 0xf) << ((port) << 2))
#define IMX95_CFG_LINK_MII_PROT 0x4
#define CFG_LINK_MII_PORT_0 GENMASK(3, 0)
#define CFG_LINK_MII_PORT_1 GENMASK(7, 4)
#define MII_PROT_MII 0x0
#define MII_PROT_RMII 0x1
#define MII_PROT_RGMII 0x2
#define MII_PROT_SERIAL 0x3
#define MII_PROT(port, prot) (((prot) & 0xf) << ((port) << 2))
#define IMX95_CFG_LINK_PCS_PROT(a) (0x8 + (a) * 4)
#define PCS_PROT_1G_SGMII BIT(0)
#define PCS_PROT_2500M_SGMII BIT(1)
#define PCS_PROT_XFI BIT(3)
#define PCS_PROT_SFI BIT(4)
#define PCS_PROT_10G_SXGMII BIT(6)
#define IMX94_EXT_PIN_CONTROL 0x10
#define MAC2_MAC3_SEL BIT(1)
#define IMX94_NETC_LINK_CFG(a) (0x4c + (a) * 4)
#define NETC_LINK_CFG_MII_PROT GENMASK(3, 0)
#define NETC_LINK_CFG_IO_VAR GENMASK(19, 16)
/* NETC privileged register block register */
#define PRB_NETCRR 0x100
#define NETCRR_SR BIT(0)
#define NETCRR_LOCK BIT(1)
#define PRB_NETCSR 0x104
#define NETCSR_ERROR BIT(0)
#define NETCSR_STATE BIT(1)
/* NETC integrated endpoint register block register */
#define IERB_EMDIOFAUXR 0x344
#define IERB_T0FAUXR 0x444
#define IERB_ETBCR(a) (0x300c + 0x100 * (a))
#define IERB_LBCR(a) (0x1010 + 0x40 * (a))
#define LBCR_MDIO_PHYAD_PRTAD(addr) (((addr) & 0x1f) << 8)
#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a))
#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a))
#define FAUXR_LDID GENMASK(3, 0)
/* Platform information */
#define IMX95_ENETC0_BUS_DEVFN 0x0
#define IMX95_ENETC1_BUS_DEVFN 0x40
#define IMX95_ENETC2_BUS_DEVFN 0x80
#define IMX94_ENETC0_BUS_DEVFN 0x100
#define IMX94_ENETC1_BUS_DEVFN 0x140
#define IMX94_ENETC2_BUS_DEVFN 0x180
#define IMX94_TIMER0_BUS_DEVFN 0x1
#define IMX94_TIMER1_BUS_DEVFN 0x101
#define IMX94_TIMER2_BUS_DEVFN 0x181
#define IMX94_ENETC0_LINK 3
#define IMX94_ENETC1_LINK 4
#define IMX94_ENETC2_LINK 5
#define NETC_ENETC_ID(a) (a)
#define NETC_TIMER_ID(a) (a)
/* Flags for different platforms */
#define NETC_HAS_NETCMIX BIT(0)
struct netc_devinfo {
u32 flags;
int (*netcmix_init)(struct platform_device *pdev);
int (*ierb_init)(struct platform_device *pdev);
};
struct netc_blk_ctrl {
void __iomem *prb;
void __iomem *ierb;
void __iomem *netcmix;
const struct netc_devinfo *devinfo;
struct platform_device *pdev;
struct dentry *debugfs_root;
};
static void netc_reg_write(void __iomem *base, u32 offset, u32 val)
{
netc_write(base + offset, val);
}
static u32 netc_reg_read(void __iomem *base, u32 offset)
{
return netc_read(base + offset);
}
static int netc_of_pci_get_bus_devfn(struct device_node *np)
{
u32 reg[5];
int error;
error = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg));
if (error)
return error;
return (reg[0] >> 8) & 0xffff;
}
static int netc_get_link_mii_protocol(phy_interface_t interface)
{
switch (interface) {
case PHY_INTERFACE_MODE_MII:
return MII_PROT_MII;
case PHY_INTERFACE_MODE_RMII:
return MII_PROT_RMII;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
return MII_PROT_RGMII;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_XGMII:
case PHY_INTERFACE_MODE_USXGMII:
return MII_PROT_SERIAL;
default:
return -EINVAL;
}
}
static int imx95_netcmix_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
phy_interface_t interface;
int bus_devfn, mii_proto;
u32 val;
int err;
/* Default setting of MII protocol */
val = MII_PROT(0, MII_PROT_RGMII) | MII_PROT(1, MII_PROT_RGMII) |
MII_PROT(2, MII_PROT_SERIAL);
/* Update the link MII protocol through parsing phy-mode */
for_each_available_child_of_node_scoped(np, child) {
for_each_available_child_of_node_scoped(child, gchild) {
if (!of_device_is_compatible(gchild, "pci1131,e101"))
continue;
bus_devfn = netc_of_pci_get_bus_devfn(gchild);
if (bus_devfn < 0)
return -EINVAL;
if (bus_devfn == IMX95_ENETC2_BUS_DEVFN)
continue;
err = of_get_phy_mode(gchild, &interface);
if (err)
continue;
mii_proto = netc_get_link_mii_protocol(interface);
if (mii_proto < 0)
return -EINVAL;
switch (bus_devfn) {
case IMX95_ENETC0_BUS_DEVFN:
val = u32_replace_bits(val, mii_proto,
CFG_LINK_MII_PORT_0);
break;
case IMX95_ENETC1_BUS_DEVFN:
val = u32_replace_bits(val, mii_proto,
CFG_LINK_MII_PORT_1);
break;
default:
return -EINVAL;
}
}
}
/* Configure Link I/O variant */
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_IO_VAR,
IO_VAR(2, IO_VAR_16FF_16G_SERDES));
/* Configure Link 2 PCS protocol */
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_PCS_PROT(2),
PCS_PROT_10G_SXGMII);
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_MII_PROT, val);
return 0;
}
static int imx94_enetc_get_link_id(struct device_node *np)
{
int bus_devfn = netc_of_pci_get_bus_devfn(np);
/* Parse ENETC link number */
switch (bus_devfn) {
case IMX94_ENETC0_BUS_DEVFN:
return IMX94_ENETC0_LINK;
case IMX94_ENETC1_BUS_DEVFN:
return IMX94_ENETC1_LINK;
case IMX94_ENETC2_BUS_DEVFN:
return IMX94_ENETC2_LINK;
default:
return -EINVAL;
}
}
static int imx94_link_config(struct netc_blk_ctrl *priv,
struct device_node *np, int link_id)
{
phy_interface_t interface;
int mii_proto;
u32 val;
/* The node may be disabled and does not have a 'phy-mode'
* or 'phy-connection-type' property.
*/
if (of_get_phy_mode(np, &interface))
return 0;
mii_proto = netc_get_link_mii_protocol(interface);
if (mii_proto < 0)
return mii_proto;
val = mii_proto & NETC_LINK_CFG_MII_PROT;
if (val == MII_PROT_SERIAL)
val = u32_replace_bits(val, IO_VAR_16FF_16G_SERDES,
NETC_LINK_CFG_IO_VAR);
netc_reg_write(priv->netcmix, IMX94_NETC_LINK_CFG(link_id), val);
return 0;
}
static int imx94_enetc_link_config(struct netc_blk_ctrl *priv,
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")) {
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;
}
}
}
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);
if (!swp2_en)
val |= MAC2_MAC3_SEL;
else
val &= ~MAC2_MAC3_SEL;
netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val);
return 0;
}
static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
{
return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
}
static int netc_lock_ierb(struct netc_blk_ctrl *priv)
{
u32 val;
netc_reg_write(priv->prb, PRB_NETCRR, NETCRR_LOCK);
return read_poll_timeout(netc_reg_read, val, !(val & NETCSR_STATE),
100, 2000, false, priv->prb, PRB_NETCSR);
}
static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv)
{
u32 val;
netc_reg_write(priv->prb, PRB_NETCRR, 0);
return read_poll_timeout(netc_reg_read, val, !(val & NETCRR_LOCK),
1000, 100000, true, priv->prb, PRB_NETCRR);
}
static int netc_get_phy_addr(struct device_node *np)
{
struct device_node *mdio_node, *phy_node;
u32 addr = 0;
int err = 0;
mdio_node = of_get_child_by_name(np, "mdio");
if (!mdio_node)
return -ENODEV;
phy_node = of_get_next_child(mdio_node, NULL);
if (!phy_node) {
err = -ENODEV;
goto of_put_mdio_node;
}
err = of_property_read_u32(phy_node, "reg", &addr);
if (err)
goto of_put_phy_node;
if (addr >= PHY_MAX_ADDR)
err = -EINVAL;
of_put_phy_node:
of_node_put(phy_node);
of_put_mdio_node:
of_node_put(mdio_node);
return err ? err : addr;
}
static int netc_parse_emdio_phy_mask(struct device_node *np, u32 *phy_mask)
{
u32 mask = 0;
for_each_child_of_node_scoped(np, child) {
u32 addr;
int err;
err = of_property_read_u32(child, "reg", &addr);
if (err)
return err;
if (addr >= PHY_MAX_ADDR)
return -EINVAL;
mask |= BIT(addr);
}
*phy_mask = mask;
return 0;
}
static int netc_get_emdio_phy_mask(struct device_node *np, u32 *phy_mask)
{
for_each_child_of_node_scoped(np, child) {
for_each_child_of_node_scoped(child, gchild) {
if (!of_device_is_compatible(gchild, "pci1131,ee00"))
continue;
return netc_parse_emdio_phy_mask(gchild, phy_mask);
}
}
return 0;
}
static int imx95_enetc_mdio_phyaddr_config(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int bus_devfn, addr, err;
u32 phy_mask = 0;
err = netc_get_emdio_phy_mask(np, &phy_mask);
if (err) {
dev_err(dev, "Failed to get PHY address mask\n");
return err;
}
/* Update the port EMDIO PHY address through parsing phy properties.
* This is needed when using the port EMDIO but it's harmless when
* using the central EMDIO. So apply it on all cases.
*/
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;
bus_devfn = netc_of_pci_get_bus_devfn(gchild);
if (bus_devfn < 0) {
dev_err(dev, "Failed to get BDF number\n");
return bus_devfn;
}
addr = netc_get_phy_addr(gchild);
if (addr < 0) {
if (addr == -ENODEV)
continue;
dev_err(dev, "Failed to get PHY address\n");
return addr;
}
if (phy_mask & BIT(addr)) {
dev_err(dev,
"Find same PHY address in EMDIO and ENETC node\n");
return -EINVAL;
}
switch (bus_devfn) {
case IMX95_ENETC0_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(0),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
case IMX95_ENETC1_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(1),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
case IMX95_ENETC2_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(2),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
default:
break;
}
}
}
return 0;
}
static int imx95_ierb_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
/* EMDIO : No MSI-X intterupt */
netc_reg_write(priv->ierb, IERB_EMDIOFAUXR, 0);
/* ENETC0 PF */
netc_reg_write(priv->ierb, IERB_EFAUXR(0), 0);
/* ENETC0 VF0 */
netc_reg_write(priv->ierb, IERB_VFAUXR(0), 1);
/* ENETC0 VF1 */
netc_reg_write(priv->ierb, IERB_VFAUXR(1), 2);
/* ENETC1 PF */
netc_reg_write(priv->ierb, IERB_EFAUXR(1), 3);
/* ENETC1 VF0 */
netc_reg_write(priv->ierb, IERB_VFAUXR(2), 5);
/* ENETC1 VF1 */
netc_reg_write(priv->ierb, IERB_VFAUXR(3), 6);
/* ENETC2 PF */
netc_reg_write(priv->ierb, IERB_EFAUXR(2), 4);
/* ENETC2 VF0 */
netc_reg_write(priv->ierb, IERB_VFAUXR(4), 5);
/* ENETC2 VF1 */
netc_reg_write(priv->ierb, IERB_VFAUXR(5), 6);
/* NETC TIMER */
netc_reg_write(priv->ierb, IERB_T0FAUXR, 7);
return imx95_enetc_mdio_phyaddr_config(pdev);
}
static int imx94_get_enetc_id(struct device_node *np)
{
int bus_devfn = netc_of_pci_get_bus_devfn(np);
/* Parse ENETC offset */
switch (bus_devfn) {
case IMX94_ENETC0_BUS_DEVFN:
return NETC_ENETC_ID(0);
case IMX94_ENETC1_BUS_DEVFN:
return NETC_ENETC_ID(1);
case IMX94_ENETC2_BUS_DEVFN:
return NETC_ENETC_ID(2);
default:
return -EINVAL;
}
}
static int imx94_get_timer_id(struct device_node *np)
{
int bus_devfn = netc_of_pci_get_bus_devfn(np);
/* Parse NETC PTP timer ID, the timer0 is on bus 0,
* the timer 1 and timer2 is on bus 1.
*/
switch (bus_devfn) {
case IMX94_TIMER0_BUS_DEVFN:
return NETC_TIMER_ID(0);
case IMX94_TIMER1_BUS_DEVFN:
return NETC_TIMER_ID(1);
case IMX94_TIMER2_BUS_DEVFN:
return NETC_TIMER_ID(2);
default:
return -EINVAL;
}
}
static int imx94_enetc_update_tid(struct netc_blk_ctrl *priv,
struct device_node *np)
{
struct device *dev = &priv->pdev->dev;
struct device_node *timer_np;
int eid, tid;
eid = imx94_get_enetc_id(np);
if (eid < 0) {
dev_err(dev, "Failed to get ENETC ID\n");
return eid;
}
timer_np = of_parse_phandle(np, "ptp-timer", 0);
if (!timer_np) {
/* If 'ptp-timer' is not present, the timer1 is the default
* timer of all standalone ENETCs, which is on the same PCIe
* bus as these ENETCs.
*/
tid = NETC_TIMER_ID(1);
goto end;
}
tid = imx94_get_timer_id(timer_np);
of_node_put(timer_np);
if (tid < 0) {
dev_err(dev, "Failed to get NETC Timer ID\n");
return tid;
}
end:
netc_reg_write(priv->ierb, IERB_ETBCR(eid), tid);
return 0;
}
static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
struct device_node *np,
u32 phy_mask)
{
struct device *dev = &priv->pdev->dev;
int bus_devfn, addr;
bus_devfn = netc_of_pci_get_bus_devfn(np);
if (bus_devfn < 0) {
dev_err(dev, "Failed to get BDF number\n");
return bus_devfn;
}
addr = netc_get_phy_addr(np);
if (addr < 0) {
if (addr == -ENODEV)
return 0;
dev_err(dev, "Failed to get PHY address\n");
return addr;
}
if (phy_mask & BIT(addr)) {
dev_err(dev,
"Find same PHY address in EMDIO and ENETC node\n");
return -EINVAL;
}
switch (bus_devfn) {
case IMX94_ENETC0_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC0_LINK),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
case IMX94_ENETC1_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC1_LINK),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
case IMX94_ENETC2_BUS_DEVFN:
netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC2_LINK),
LBCR_MDIO_PHYAD_PRTAD(addr));
break;
default:
break;
}
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);
struct device_node *np = pdev->dev.of_node;
u32 phy_mask = 0;
int err;
err = netc_get_emdio_phy_mask(np, &phy_mask);
if (err) {
dev_err(&pdev->dev, "Failed to get PHY address mask\n");
return 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")) {
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;
}
}
}
return 0;
}
static int netc_ierb_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
const struct netc_devinfo *devinfo = priv->devinfo;
int err;
if (netc_ierb_is_locked(priv)) {
err = netc_unlock_ierb_with_warm_reset(priv);
if (err) {
dev_err(&pdev->dev, "Unlock IERB failed.\n");
return err;
}
}
if (devinfo->ierb_init) {
err = devinfo->ierb_init(pdev);
if (err)
return err;
}
err = netc_lock_ierb(priv);
if (err) {
dev_err(&pdev->dev, "Lock IERB failed.\n");
return err;
}
return 0;
}
#if IS_ENABLED(CONFIG_DEBUG_FS)
static int netc_prb_show(struct seq_file *s, void *data)
{
struct netc_blk_ctrl *priv = s->private;
u32 val;
val = netc_reg_read(priv->prb, PRB_NETCRR);
seq_printf(s, "[PRB NETCRR] Lock:%d SR:%d\n",
(val & NETCRR_LOCK) ? 1 : 0,
(val & NETCRR_SR) ? 1 : 0);
val = netc_reg_read(priv->prb, PRB_NETCSR);
seq_printf(s, "[PRB NETCSR] State:%d Error:%d\n",
(val & NETCSR_STATE) ? 1 : 0,
(val & NETCSR_ERROR) ? 1 : 0);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(netc_prb);
static void netc_blk_ctrl_create_debugfs(struct netc_blk_ctrl *priv)
{
struct dentry *root;
root = debugfs_create_dir("netc_blk_ctrl", NULL);
if (IS_ERR(root))
return;
priv->debugfs_root = root;
debugfs_create_file("prb", 0444, root, priv, &netc_prb_fops);
}
static void netc_blk_ctrl_remove_debugfs(struct netc_blk_ctrl *priv)
{
debugfs_remove_recursive(priv->debugfs_root);
priv->debugfs_root = NULL;
}
#else
static void netc_blk_ctrl_create_debugfs(struct netc_blk_ctrl *priv)
{
}
static void netc_blk_ctrl_remove_debugfs(struct netc_blk_ctrl *priv)
{
}
#endif
static int netc_prb_check_error(struct netc_blk_ctrl *priv)
{
if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR)
return -1;
return 0;
}
static const struct netc_devinfo imx95_devinfo = {
.flags = NETC_HAS_NETCMIX,
.netcmix_init = imx95_netcmix_init,
.ierb_init = imx95_ierb_init,
};
static const struct netc_devinfo imx94_devinfo = {
.flags = NETC_HAS_NETCMIX,
.netcmix_init = imx94_netcmix_init,
.ierb_init = imx94_ierb_init,
};
static const struct of_device_id netc_blk_ctrl_match[] = {
{ .compatible = "nxp,imx95-netc-blk-ctrl", .data = &imx95_devinfo },
{ .compatible = "nxp,imx94-netc-blk-ctrl", .data = &imx94_devinfo },
{},
};
MODULE_DEVICE_TABLE(of, netc_blk_ctrl_match);
static int netc_blk_ctrl_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
const struct netc_devinfo *devinfo;
struct device *dev = &pdev->dev;
const struct of_device_id *id;
struct netc_blk_ctrl *priv;
struct clk *ipg_clk;
void __iomem *regs;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->pdev = pdev;
ipg_clk = devm_clk_get_optional_enabled(dev, "ipg");
if (IS_ERR(ipg_clk))
return dev_err_probe(dev, PTR_ERR(ipg_clk),
"Set ipg clock failed\n");
id = of_match_device(netc_blk_ctrl_match, dev);
if (!id)
return dev_err_probe(dev, -EINVAL, "Cannot match device\n");
devinfo = (struct netc_devinfo *)id->data;
if (!devinfo)
return dev_err_probe(dev, -EINVAL, "No device information\n");
priv->devinfo = devinfo;
regs = devm_platform_ioremap_resource_byname(pdev, "ierb");
if (IS_ERR(regs))
return dev_err_probe(dev, PTR_ERR(regs),
"Missing IERB resource\n");
priv->ierb = regs;
regs = devm_platform_ioremap_resource_byname(pdev, "prb");
if (IS_ERR(regs))
return dev_err_probe(dev, PTR_ERR(regs),
"Missing PRB resource\n");
priv->prb = regs;
if (devinfo->flags & NETC_HAS_NETCMIX) {
regs = devm_platform_ioremap_resource_byname(pdev, "netcmix");
if (IS_ERR(regs))
return dev_err_probe(dev, PTR_ERR(regs),
"Missing NETCMIX resource\n");
priv->netcmix = regs;
}
platform_set_drvdata(pdev, priv);
if (devinfo->netcmix_init) {
err = devinfo->netcmix_init(pdev);
if (err)
return dev_err_probe(dev, err,
"Initializing NETCMIX failed\n");
}
err = netc_ierb_init(pdev);
if (err)
return dev_err_probe(dev, err, "Initializing IERB failed\n");
if (netc_prb_check_error(priv) < 0)
dev_warn(dev, "The current IERB configuration is invalid\n");
netc_blk_ctrl_create_debugfs(priv);
err = of_platform_populate(node, NULL, NULL, dev);
if (err) {
netc_blk_ctrl_remove_debugfs(priv);
return dev_err_probe(dev, err, "of_platform_populate failed\n");
}
return 0;
}
static void netc_blk_ctrl_remove(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
of_platform_depopulate(&pdev->dev);
netc_blk_ctrl_remove_debugfs(priv);
}
static struct platform_driver netc_blk_ctrl_driver = {
.driver = {
.name = "nxp-netc-blk-ctrl",
.of_match_table = netc_blk_ctrl_match,
},
.probe = netc_blk_ctrl_probe,
.remove = netc_blk_ctrl_remove,
};
module_platform_driver(netc_blk_ctrl_driver);
MODULE_DESCRIPTION("NXP NETC Blocks Control Driver");
MODULE_LICENSE("Dual BSD/GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,234 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* NTMP table request and response data buffer formats
* Copyright 2025-2026 NXP
*/
#ifndef __NTMP_PRIVATE_H
#define __NTMP_PRIVATE_H
#include <linux/bitfield.h>
#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)
#define NETC_CBDR_CLEAN_WORK 16
union netc_cbd {
struct {
__le64 addr;
__le32 len;
#define NTMP_RESP_LEN GENMASK(19, 0)
#define NTMP_REQ_LEN GENMASK(31, 20)
#define NTMP_LEN(req, resp) (FIELD_PREP(NTMP_REQ_LEN, (req)) | \
((resp) & NTMP_RESP_LEN))
u8 cmd;
#define NTMP_CMD_DELETE BIT(0)
#define NTMP_CMD_UPDATE BIT(1)
#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
#define NTMP_AM_EXACT_KEY 1
#define NTMP_AM_SEARCH 2
#define NTMP_AM_TERNARY_KEY 3
u8 table_id;
u8 ver_cci_rr;
#define NTMP_HDR_VERSION GENMASK(5, 0)
#define NTMP_HDR_VER2 2
#define NTMP_CCI BIT(6)
#define NTMP_RR BIT(7)
__le32 resv[3];
__le32 npf;
#define NTMP_NPF BIT(15)
} req_hdr; /* NTMP Request Message Header Format */
struct {
__le32 resv0[3];
__le16 num_matched;
__le16 error_rr;
#define NTMP_RESP_ERROR GENMASK(11, 0)
#define NTMP_RESP_RR BIT(15)
__le32 resv1[4];
} resp_hdr; /* NTMP Response Message Header Format */
};
struct ntmp_cmn_req_data {
__le16 update_act;
u8 dbg_opt;
u8 tblv_qact;
#define NTMP_QUERY_ACT GENMASK(3, 0)
#define NTMP_TBL_VER GENMASK(7, 4)
#define NTMP_TBLV_QACT(v, a) (FIELD_PREP(NTMP_TBL_VER, (v)) | \
((a) & NTMP_QUERY_ACT))
};
struct ntmp_cmn_resp_query {
__le32 entry_id;
};
/* Generic structure for request data by entry ID */
struct ntmp_req_by_eid {
struct ntmp_cmn_req_data crd;
__le32 entry_id;
};
/* MAC Address Filter Table Request Data Buffer Format of Add action */
struct maft_req_add {
struct ntmp_req_by_eid rbe;
struct maft_keye_data keye;
struct maft_cfge_data cfge;
};
/* MAC Address Filter Table Response Data Buffer Format of Query action */
struct maft_resp_query {
__le32 entry_id;
struct maft_keye_data keye;
struct maft_cfge_data cfge;
};
/* RSS Table Request Data Buffer Format of Update action */
struct rsst_req_update {
struct ntmp_req_by_eid rbe;
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;
};
/* VLAN Filter Table Request Data Buffer Format of Query and Delete actions */
struct vft_req_qd {
struct ntmp_cmn_req_data crd;
union vft_access_key ak;
};
/* Egress Treatment Table Request Data Buffer Format of Update and Add
* actions
*/
struct ett_req_ua {
struct ntmp_req_by_eid rbe;
struct ett_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