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,8 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
# Copyright (c) 2019-2020 Marvell International Ltd.
obj-$(CONFIG_QEDE) := qede.o
qede-y := qede_main.o qede_fp.o qede_filter.o qede_ethtool.o qede_ptp.o
qede-$(CONFIG_DCB) += qede_dcbnl.o
qede-$(CONFIG_QED_RDMA) += qede_rdma.o
@@ -0,0 +1,609 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qede NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QEDE_H_
#define _QEDE_H_
#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <linux/interrupt.h>
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/bpf.h>
#include <net/xdp.h>
#include <linux/qed/qede_rdma.h>
#include <linux/io.h>
#ifdef CONFIG_RFS_ACCEL
#include <linux/cpu_rmap.h>
#endif
#include <linux/qed/common_hsi.h>
#include <linux/qed/eth_common.h>
#include <linux/qed/qed_if.h>
#include <linux/qed/qed_chain.h>
#include <linux/qed/qed_eth_if.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h>
#define DRV_MODULE_SYM qede
struct qede_stats_common {
u64 no_buff_discards;
u64 packet_too_big_discard;
u64 ttl0_discard;
u64 rx_ucast_bytes;
u64 rx_mcast_bytes;
u64 rx_bcast_bytes;
u64 rx_ucast_pkts;
u64 rx_mcast_pkts;
u64 rx_bcast_pkts;
u64 mftag_filter_discards;
u64 mac_filter_discards;
u64 gft_filter_drop;
u64 tx_ucast_bytes;
u64 tx_mcast_bytes;
u64 tx_bcast_bytes;
u64 tx_ucast_pkts;
u64 tx_mcast_pkts;
u64 tx_bcast_pkts;
u64 tx_err_drop_pkts;
u64 coalesced_pkts;
u64 coalesced_events;
u64 coalesced_aborts_num;
u64 non_coalesced_pkts;
u64 coalesced_bytes;
u64 link_change_count;
u64 ptp_skip_txts;
/* port */
u64 rx_64_byte_packets;
u64 rx_65_to_127_byte_packets;
u64 rx_128_to_255_byte_packets;
u64 rx_256_to_511_byte_packets;
u64 rx_512_to_1023_byte_packets;
u64 rx_1024_to_1518_byte_packets;
u64 rx_crc_errors;
u64 rx_mac_crtl_frames;
u64 rx_pause_frames;
u64 rx_pfc_frames;
u64 rx_align_errors;
u64 rx_carrier_errors;
u64 rx_oversize_packets;
u64 rx_jabbers;
u64 rx_undersize_packets;
u64 rx_fragments;
u64 tx_64_byte_packets;
u64 tx_65_to_127_byte_packets;
u64 tx_128_to_255_byte_packets;
u64 tx_256_to_511_byte_packets;
u64 tx_512_to_1023_byte_packets;
u64 tx_1024_to_1518_byte_packets;
u64 tx_pause_frames;
u64 tx_pfc_frames;
u64 brb_truncates;
u64 brb_discards;
u64 tx_mac_ctrl_frames;
};
struct qede_stats_bb {
u64 rx_1519_to_1522_byte_packets;
u64 rx_1519_to_2047_byte_packets;
u64 rx_2048_to_4095_byte_packets;
u64 rx_4096_to_9216_byte_packets;
u64 rx_9217_to_16383_byte_packets;
u64 tx_1519_to_2047_byte_packets;
u64 tx_2048_to_4095_byte_packets;
u64 tx_4096_to_9216_byte_packets;
u64 tx_9217_to_16383_byte_packets;
u64 tx_lpi_entry_count;
u64 tx_total_collisions;
};
struct qede_stats_ah {
u64 rx_1519_to_max_byte_packets;
u64 tx_1519_to_max_byte_packets;
};
struct qede_stats {
struct qede_stats_common common;
union {
struct qede_stats_bb bb;
struct qede_stats_ah ah;
};
};
struct qede_vlan {
struct list_head list;
u16 vid;
bool configured;
};
struct qede_rdma_dev {
struct qedr_dev *qedr_dev;
struct list_head entry;
struct list_head rdma_event_list;
struct workqueue_struct *rdma_wq;
struct kref refcnt;
struct completion event_comp;
bool exp_recovery;
};
struct qede_ptp;
#define QEDE_RFS_MAX_FLTR 256
enum qede_flags_bit {
QEDE_FLAGS_IS_VF = 0,
QEDE_FLAGS_LINK_REQUESTED,
QEDE_FLAGS_PTP_TX_IN_PRORGESS,
QEDE_FLAGS_TX_TIMESTAMPING_EN
};
#define QEDE_DUMP_MAX_ARGS 4
enum qede_dump_cmd {
QEDE_DUMP_CMD_NONE = 0,
QEDE_DUMP_CMD_NVM_CFG,
QEDE_DUMP_CMD_GRCDUMP,
QEDE_DUMP_CMD_MAX
};
struct qede_dump_info {
enum qede_dump_cmd cmd;
u8 num_args;
u32 args[QEDE_DUMP_MAX_ARGS];
};
struct qede_coalesce {
bool isvalid;
u16 rxc;
u16 txc;
};
struct qede_dev {
struct qed_dev *cdev;
struct net_device *ndev;
struct pci_dev *pdev;
struct devlink *devlink;
u32 dp_module;
u8 dp_level;
unsigned long flags;
#define IS_VF(edev) test_bit(QEDE_FLAGS_IS_VF, \
&(edev)->flags)
const struct qed_eth_ops *ops;
struct qede_ptp *ptp;
u64 ptp_skip_txts;
struct qed_dev_eth_info dev_info;
#define QEDE_MAX_RSS_CNT(edev) ((edev)->dev_info.num_queues)
#define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues)
#define QEDE_IS_BB(edev) \
((edev)->dev_info.common.dev_type == QED_DEV_TYPE_BB)
#define QEDE_IS_AH(edev) \
((edev)->dev_info.common.dev_type == QED_DEV_TYPE_AH)
struct qede_fastpath *fp_array;
struct qede_coalesce *coal_entry;
u8 req_num_tx;
u8 fp_num_tx;
u8 req_num_rx;
u8 fp_num_rx;
u16 req_queues;
u16 num_queues;
u16 total_xdp_queues;
#define QEDE_QUEUE_CNT(edev) ((edev)->num_queues)
#define QEDE_RSS_COUNT(edev) ((edev)->num_queues - (edev)->fp_num_tx)
#define QEDE_RX_QUEUE_IDX(edev, i) (i)
#define QEDE_TSS_COUNT(edev) ((edev)->num_queues - (edev)->fp_num_rx)
struct qed_int_info int_info;
/* Smaller private variant of the RTNL lock */
struct mutex qede_lock;
u32 state; /* Protected by qede_lock */
u16 rx_buf_size;
u32 rx_copybreak;
/* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */
#define ETH_OVERHEAD (ETH_HLEN + 8 + 8)
/* Max supported alignment is 256 (8 shift)
* minimal alignment shift 6 is optimal for 57xxx HW performance
*/
#define QEDE_RX_ALIGN_SHIFT max(6, min(8, L1_CACHE_SHIFT))
/* We assume skb_build() uses sizeof(struct skb_shared_info) bytes
* at the end of skb->data, to avoid wasting a full cache line.
* This reduces memory use (skb->truesize).
*/
#define QEDE_FW_RX_ALIGN_END \
max_t(u64, 1UL << QEDE_RX_ALIGN_SHIFT, \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
struct qede_stats stats;
/* Bitfield to track initialized RSS params */
u32 rss_params_inited;
#define QEDE_RSS_INDIR_INITED BIT(0)
#define QEDE_RSS_KEY_INITED BIT(1)
#define QEDE_RSS_CAPS_INITED BIT(2)
u16 rss_ind_table[128];
u32 rss_key[10];
u8 rss_caps;
/* Both must be a power of two */
u16 q_num_rx_buffers;
u16 q_num_tx_buffers;
bool gro_disable;
struct list_head vlan_list;
u16 configured_vlans;
u16 non_configured_vlans;
bool accept_any_vlan;
struct delayed_work sp_task;
unsigned long sp_flags;
u16 vxlan_dst_port;
u16 geneve_dst_port;
struct qede_arfs *arfs;
bool wol_enabled;
struct qede_rdma_dev rdma_info;
struct bpf_prog *xdp_prog;
enum qed_hw_err_type last_err_type;
unsigned long err_flags;
#define QEDE_ERR_IS_HANDLED 31
#define QEDE_ERR_ATTN_CLR_EN 0
#define QEDE_ERR_GET_DBG_INFO 1
#define QEDE_ERR_IS_RECOVERABLE 2
#define QEDE_ERR_WARN 3
struct qede_dump_info dump_info;
struct delayed_work periodic_task;
unsigned long stats_coal_ticks;
u32 stats_coal_usecs;
spinlock_t stats_lock; /* lock for vport stats access */
};
enum QEDE_STATE {
QEDE_STATE_CLOSED,
QEDE_STATE_OPEN,
QEDE_STATE_RECOVERY,
};
#define HILO_U64(hi, lo) ((((u64)(hi)) << 32) + (lo))
#define MAX_NUM_TC 8
#define MAX_NUM_PRI 8
/* The driver supports the new build_skb() API:
* RX ring buffer contains pointer to kmalloc() data only,
* skb are built only after the frame was DMA-ed.
*/
struct sw_rx_data {
struct page *data;
dma_addr_t mapping;
unsigned int page_offset;
};
enum qede_agg_state {
QEDE_AGG_STATE_NONE = 0,
QEDE_AGG_STATE_START = 1,
QEDE_AGG_STATE_ERROR = 2
};
struct qede_agg_info {
/* rx_buf is a data buffer that can be placed / consumed from rx bd
* chain. It has two purposes: We will preallocate the data buffer
* for each aggregation when we open the interface and will place this
* buffer on the rx-bd-ring when we receive TPA_START. We don't want
* to be in a state where allocation fails, as we can't reuse the
* consumer buffer in the rx-chain since FW may still be writing to it
* (since header needs to be modified for TPA).
* The second purpose is to keep a pointer to the bd buffer during
* aggregation.
*/
struct sw_rx_data buffer;
struct sk_buff *skb;
/* We need some structs from the start cookie until termination */
u16 vlan_tag;
bool tpa_start_fail;
u8 state;
u8 frag_id;
u8 tunnel_type;
};
struct qede_rx_queue {
__le16 *hw_cons_ptr;
void __iomem *hw_rxq_prod_addr;
/* Required for the allocation of replacement buffers */
struct device *dev;
struct bpf_prog *xdp_prog;
u16 sw_rx_cons;
u16 sw_rx_prod;
u16 filled_buffers;
u8 data_direction;
u8 rxq_id;
/* Used once per each NAPI run */
u16 num_rx_buffers;
u16 rx_headroom;
u32 rx_buf_size;
u32 rx_buf_seg_size;
struct sw_rx_data *sw_rx_ring;
struct qed_chain rx_bd_ring;
struct qed_chain rx_comp_ring ____cacheline_aligned;
/* GRO */
struct qede_agg_info tpa_info[ETH_TPA_MAX_AGGS_NUM];
/* Used once per each NAPI run */
u64 rcv_pkts;
u64 rx_hw_errors;
u64 rx_alloc_errors;
u64 rx_ip_frags;
u64 xdp_no_pass;
void *handle;
struct xdp_rxq_info xdp_rxq;
};
union db_prod {
struct eth_db_data data;
u32 raw;
};
struct sw_tx_bd {
struct sk_buff *skb;
u8 flags;
/* Set on the first BD descriptor when there is a split BD */
#define QEDE_TSO_SPLIT_BD BIT(0)
};
struct sw_tx_xdp {
struct page *page;
struct xdp_frame *xdpf;
dma_addr_t mapping;
};
struct qede_tx_queue {
u8 is_xdp;
bool is_legacy;
u16 sw_tx_cons;
u16 sw_tx_prod;
u16 num_tx_buffers; /* Slowpath only */
u64 xmit_pkts;
u64 stopped_cnt;
u64 tx_mem_alloc_err;
__le16 *hw_cons_ptr;
/* Needed for the mapping of packets */
struct device *dev;
void __iomem *doorbell_addr;
union db_prod tx_db;
/* Spinlock for XDP queues in case of XDP_REDIRECT */
spinlock_t xdp_tx_lock;
int index; /* Slowpath only */
#define QEDE_TXQ_XDP_TO_IDX(edev, txq) ((txq)->index - \
QEDE_MAX_TSS_CNT(edev))
#define QEDE_TXQ_IDX_TO_XDP(edev, idx) ((idx) + QEDE_MAX_TSS_CNT(edev))
#define QEDE_NDEV_TXQ_ID_TO_FP_ID(edev, idx) ((edev)->fp_num_rx + \
((idx) % QEDE_TSS_COUNT(edev)))
#define QEDE_NDEV_TXQ_ID_TO_TXQ_COS(edev, idx) ((idx) / QEDE_TSS_COUNT(edev))
#define QEDE_TXQ_TO_NDEV_TXQ_ID(edev, txq) ((QEDE_TSS_COUNT(edev) * \
(txq)->cos) + (txq)->index)
#define QEDE_NDEV_TXQ_ID_TO_TXQ(edev, idx) \
(&((edev)->fp_array[QEDE_NDEV_TXQ_ID_TO_FP_ID(edev, idx)].txq \
[QEDE_NDEV_TXQ_ID_TO_TXQ_COS(edev, idx)]))
#define QEDE_FP_TC0_TXQ(fp) (&((fp)->txq[0]))
/* Regular Tx requires skb + metadata for release purpose,
* while XDP requires the pages and the mapped address.
*/
union {
struct sw_tx_bd *skbs;
struct sw_tx_xdp *xdp;
} sw_tx_ring;
struct qed_chain tx_pbl;
/* Slowpath; Should be kept in end [unless missing padding] */
void *handle;
u16 cos;
u16 ndev_txq_id;
};
#define BD_UNMAP_ADDR(bd) HILO_U64(le32_to_cpu((bd)->addr.hi), \
le32_to_cpu((bd)->addr.lo))
#define BD_SET_UNMAP_ADDR_LEN(bd, maddr, len) \
do { \
(bd)->addr.hi = cpu_to_le32(upper_32_bits(maddr)); \
(bd)->addr.lo = cpu_to_le32(lower_32_bits(maddr)); \
(bd)->nbytes = cpu_to_le16(len); \
} while (0)
#define BD_UNMAP_LEN(bd) (le16_to_cpu((bd)->nbytes))
struct qede_fastpath {
struct qede_dev *edev;
u8 type;
#define QEDE_FASTPATH_TX BIT(0)
#define QEDE_FASTPATH_RX BIT(1)
#define QEDE_FASTPATH_XDP BIT(2)
#define QEDE_FASTPATH_COMBINED (QEDE_FASTPATH_TX | QEDE_FASTPATH_RX)
u8 id;
u8 xdp_xmit;
#define QEDE_XDP_TX BIT(0)
#define QEDE_XDP_REDIRECT BIT(1)
struct napi_struct napi;
struct qed_sb_info *sb_info;
struct qede_rx_queue *rxq;
struct qede_tx_queue *txq;
struct qede_tx_queue *xdp_tx;
char name[IFNAMSIZ + 8];
};
/* Debug print definitions */
#define DP_NAME(edev) netdev_name((edev)->ndev)
#define XMIT_PLAIN 0
#define XMIT_L4_CSUM BIT(0)
#define XMIT_LSO BIT(1)
#define XMIT_ENC BIT(2)
#define XMIT_ENC_GSO_L4_CSUM BIT(3)
#define QEDE_CSUM_ERROR BIT(0)
#define QEDE_CSUM_UNNECESSARY BIT(1)
#define QEDE_TUNN_CSUM_UNNECESSARY BIT(2)
#define QEDE_SP_RECOVERY 0
#define QEDE_SP_RX_MODE 1
#define QEDE_SP_RSVD1 2
#define QEDE_SP_RSVD2 3
#define QEDE_SP_HW_ERR 4
#define QEDE_SP_ARFS_CONFIG 5
#define QEDE_SP_AER 7
#define QEDE_SP_DISABLE 8
#ifdef CONFIG_RFS_ACCEL
int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
u16 rxq_index, u32 flow_id);
#define QEDE_SP_TASK_POLL_DELAY (5 * HZ)
#endif
void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr);
void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev);
void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc);
void qede_free_arfs(struct qede_dev *edev);
int qede_alloc_arfs(struct qede_dev *edev);
int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info);
int qede_delete_flow_filter(struct qede_dev *edev, u64 cookie);
int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd);
int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info,
u32 *rule_locs);
int qede_get_arfs_filter_count(struct qede_dev *edev);
struct qede_reload_args {
void (*func)(struct qede_dev *edev, struct qede_reload_args *args);
union {
netdev_features_t features;
struct bpf_prog *new_prog;
u16 mtu;
} u;
};
/* Datapath functions definition */
netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int qede_xdp_transmit(struct net_device *dev, int n_frames,
struct xdp_frame **frames, u32 flags);
u16 qede_select_queue(struct net_device *dev, struct sk_buff *skb,
struct net_device *sb_dev);
netdev_features_t qede_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
int qede_alloc_rx_buffer(struct qede_rx_queue *rxq, bool allow_lazy);
int qede_free_tx_pkt(struct qede_dev *edev,
struct qede_tx_queue *txq, int *len);
int qede_poll(struct napi_struct *napi, int budget);
irqreturn_t qede_msix_fp_int(int irq, void *fp_cookie);
/* Filtering function definitions */
void qede_force_mac(void *dev, u8 *mac, bool forced);
void qede_udp_ports_update(void *dev, u16 vxlan_port, u16 geneve_port);
int qede_set_mac_addr(struct net_device *ndev, void *p);
int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid);
int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid);
void qede_vlan_mark_nonconfigured(struct qede_dev *edev);
int qede_configure_vlan_filters(struct qede_dev *edev);
netdev_features_t qede_fix_features(struct net_device *dev,
netdev_features_t features);
int qede_set_features(struct net_device *dev, netdev_features_t features);
void qede_set_rx_mode(struct net_device *ndev);
void qede_config_rx_mode(struct net_device *ndev);
void qede_fill_rss_params(struct qede_dev *edev,
struct qed_update_vport_rss_params *rss, u8 *update);
int qede_xdp(struct net_device *dev, struct netdev_bpf *xdp);
#ifdef CONFIG_DCB
void qede_set_dcbnl_ops(struct net_device *ndev);
#endif
void qede_config_debug(uint debug, u32 *p_dp_module, u8 *p_dp_level);
void qede_set_ethtool_ops(struct net_device *netdev);
void qede_set_udp_tunnels(struct qede_dev *edev);
void qede_reload(struct qede_dev *edev,
struct qede_reload_args *args, bool is_locked);
int qede_change_mtu(struct net_device *dev, int new_mtu);
void qede_fill_by_demand_stats(struct qede_dev *edev);
void __qede_lock(struct qede_dev *edev);
void __qede_unlock(struct qede_dev *edev);
bool qede_has_rx_work(struct qede_rx_queue *rxq);
int qede_txq_has_work(struct qede_tx_queue *txq);
void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, u8 count);
void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq);
int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto,
struct flow_cls_offload *f);
void qede_forced_speed_maps_init(void);
int qede_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack);
int qede_set_per_coalesce(struct net_device *dev, u32 queue,
struct ethtool_coalesce *coal);
#define RX_RING_SIZE_POW 13
#define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW))
#define NUM_RX_BDS_MAX (RX_RING_SIZE - 1)
#define NUM_RX_BDS_MIN 128
#define NUM_RX_BDS_KDUMP_MIN 63
#define NUM_RX_BDS_DEF ((u16)BIT(10) - 1)
#define TX_RING_SIZE_POW 13
#define TX_RING_SIZE ((u16)BIT(TX_RING_SIZE_POW))
#define NUM_TX_BDS_MAX (TX_RING_SIZE - 1)
#define NUM_TX_BDS_MIN 128
#define NUM_TX_BDS_KDUMP_MIN 63
#define NUM_TX_BDS_DEF NUM_TX_BDS_MAX
#define QEDE_MIN_PKT_LEN 64
#define QEDE_RX_HDR_SIZE 256
#define QEDE_MAX_JUMBO_PACKET_SIZE 9600
#define for_each_queue(i) for (i = 0; i < edev->num_queues; i++)
#define for_each_cos_in_txq(edev, var) \
for ((var) = 0; (var) < (edev)->dev_info.num_tc; (var)++)
#endif /* _QEDE_H_ */
@@ -0,0 +1,350 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qede NIC Driver
* Copyright (c) 2015 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <net/dcbnl.h>
#include "qede.h"
static u8 qede_dcbnl_getstate(struct net_device *netdev)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getstate(edev->cdev);
}
static u8 qede_dcbnl_setstate(struct net_device *netdev, u8 state)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setstate(edev->cdev, state);
}
static void qede_dcbnl_getpermhwaddr(struct net_device *netdev,
u8 *perm_addr)
{
memcpy(perm_addr, netdev->dev_addr, netdev->addr_len);
}
static void qede_dcbnl_getpgtccfgtx(struct net_device *netdev, int prio,
u8 *prio_type, u8 *pgid, u8 *bw_pct,
u8 *up_map)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->getpgtccfgtx(edev->cdev, prio, prio_type,
pgid, bw_pct, up_map);
}
static void qede_dcbnl_getpgbwgcfgtx(struct net_device *netdev,
int pgid, u8 *bw_pct)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->getpgbwgcfgtx(edev->cdev, pgid, bw_pct);
}
static void qede_dcbnl_getpgtccfgrx(struct net_device *netdev, int prio,
u8 *prio_type, u8 *pgid, u8 *bw_pct,
u8 *up_map)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->getpgtccfgrx(edev->cdev, prio, prio_type, pgid, bw_pct,
up_map);
}
static void qede_dcbnl_getpgbwgcfgrx(struct net_device *netdev,
int pgid, u8 *bw_pct)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->getpgbwgcfgrx(edev->cdev, pgid, bw_pct);
}
static void qede_dcbnl_getpfccfg(struct net_device *netdev, int prio,
u8 *setting)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->getpfccfg(edev->cdev, prio, setting);
}
static void qede_dcbnl_setpfccfg(struct net_device *netdev, int prio,
u8 setting)
{
struct qede_dev *edev = netdev_priv(netdev);
edev->ops->dcb->setpfccfg(edev->cdev, prio, setting);
}
static u8 qede_dcbnl_getcap(struct net_device *netdev, int capid, u8 *cap)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getcap(edev->cdev, capid, cap);
}
static int qede_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getnumtcs(edev->cdev, tcid, num);
}
static u8 qede_dcbnl_getpfcstate(struct net_device *netdev)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getpfcstate(edev->cdev);
}
static int qede_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getapp(edev->cdev, idtype, id);
}
static u8 qede_dcbnl_getdcbx(struct net_device *netdev)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getdcbx(edev->cdev);
}
static void qede_dcbnl_setpgtccfgtx(struct net_device *netdev, int prio,
u8 pri_type, u8 pgid, u8 bw_pct, u8 up_map)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setpgtccfgtx(edev->cdev, prio, pri_type, pgid,
bw_pct, up_map);
}
static void qede_dcbnl_setpgtccfgrx(struct net_device *netdev, int prio,
u8 pri_type, u8 pgid, u8 bw_pct, u8 up_map)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setpgtccfgrx(edev->cdev, prio, pri_type, pgid,
bw_pct, up_map);
}
static void qede_dcbnl_setpgbwgcfgtx(struct net_device *netdev, int pgid,
u8 bw_pct)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setpgbwgcfgtx(edev->cdev, pgid, bw_pct);
}
static void qede_dcbnl_setpgbwgcfgrx(struct net_device *netdev, int pgid,
u8 bw_pct)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setpgbwgcfgrx(edev->cdev, pgid, bw_pct);
}
static u8 qede_dcbnl_setall(struct net_device *netdev)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setall(edev->cdev);
}
static int qede_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setnumtcs(edev->cdev, tcid, num);
}
static void qede_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setpfcstate(edev->cdev, state);
}
static int qede_dcbnl_setapp(struct net_device *netdev, u8 idtype, u16 idval,
u8 up)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setapp(edev->cdev, idtype, idval, up);
}
static u8 qede_dcbnl_setdcbx(struct net_device *netdev, u8 state)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setdcbx(edev->cdev, state);
}
static u8 qede_dcbnl_getfeatcfg(struct net_device *netdev, int featid,
u8 *flags)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->getfeatcfg(edev->cdev, featid, flags);
}
static u8 qede_dcbnl_setfeatcfg(struct net_device *netdev, int featid, u8 flags)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->setfeatcfg(edev->cdev, featid, flags);
}
static int qede_dcbnl_peer_getappinfo(struct net_device *netdev,
struct dcb_peer_app_info *info,
u16 *count)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->peer_getappinfo(edev->cdev, info, count);
}
static int qede_dcbnl_peer_getapptable(struct net_device *netdev,
struct dcb_app *app)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->peer_getapptable(edev->cdev, app);
}
static int qede_dcbnl_cee_peer_getpfc(struct net_device *netdev,
struct cee_pfc *pfc)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->cee_peer_getpfc(edev->cdev, pfc);
}
static int qede_dcbnl_cee_peer_getpg(struct net_device *netdev,
struct cee_pg *pg)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->cee_peer_getpg(edev->cdev, pg);
}
static int qede_dcbnl_ieee_getpfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_getpfc(edev->cdev, pfc);
}
static int qede_dcbnl_ieee_setpfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_setpfc(edev->cdev, pfc);
}
static int qede_dcbnl_ieee_getets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_getets(edev->cdev, ets);
}
static int qede_dcbnl_ieee_setets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_setets(edev->cdev, ets);
}
static int qede_dcbnl_ieee_getapp(struct net_device *netdev,
struct dcb_app *app)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_getapp(edev->cdev, app);
}
static int qede_dcbnl_ieee_setapp(struct net_device *netdev,
struct dcb_app *app)
{
struct qede_dev *edev = netdev_priv(netdev);
int err;
err = dcb_ieee_setapp(netdev, app);
if (err)
return err;
return edev->ops->dcb->ieee_setapp(edev->cdev, app);
}
static int qede_dcbnl_ieee_peer_getpfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_peer_getpfc(edev->cdev, pfc);
}
static int qede_dcbnl_ieee_peer_getets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct qede_dev *edev = netdev_priv(netdev);
return edev->ops->dcb->ieee_peer_getets(edev->cdev, ets);
}
static const struct dcbnl_rtnl_ops qede_dcbnl_ops = {
.ieee_getpfc = qede_dcbnl_ieee_getpfc,
.ieee_setpfc = qede_dcbnl_ieee_setpfc,
.ieee_getets = qede_dcbnl_ieee_getets,
.ieee_setets = qede_dcbnl_ieee_setets,
.ieee_getapp = qede_dcbnl_ieee_getapp,
.ieee_setapp = qede_dcbnl_ieee_setapp,
.ieee_peer_getpfc = qede_dcbnl_ieee_peer_getpfc,
.ieee_peer_getets = qede_dcbnl_ieee_peer_getets,
.getstate = qede_dcbnl_getstate,
.setstate = qede_dcbnl_setstate,
.getpermhwaddr = qede_dcbnl_getpermhwaddr,
.getpgtccfgtx = qede_dcbnl_getpgtccfgtx,
.getpgbwgcfgtx = qede_dcbnl_getpgbwgcfgtx,
.getpgtccfgrx = qede_dcbnl_getpgtccfgrx,
.getpgbwgcfgrx = qede_dcbnl_getpgbwgcfgrx,
.getpfccfg = qede_dcbnl_getpfccfg,
.setpfccfg = qede_dcbnl_setpfccfg,
.getcap = qede_dcbnl_getcap,
.getnumtcs = qede_dcbnl_getnumtcs,
.getpfcstate = qede_dcbnl_getpfcstate,
.getapp = qede_dcbnl_getapp,
.getdcbx = qede_dcbnl_getdcbx,
.setpgtccfgtx = qede_dcbnl_setpgtccfgtx,
.setpgtccfgrx = qede_dcbnl_setpgtccfgrx,
.setpgbwgcfgtx = qede_dcbnl_setpgbwgcfgtx,
.setpgbwgcfgrx = qede_dcbnl_setpgbwgcfgrx,
.setall = qede_dcbnl_setall,
.setnumtcs = qede_dcbnl_setnumtcs,
.setpfcstate = qede_dcbnl_setpfcstate,
.setapp = qede_dcbnl_setapp,
.setdcbx = qede_dcbnl_setdcbx,
.setfeatcfg = qede_dcbnl_setfeatcfg,
.getfeatcfg = qede_dcbnl_getfeatcfg,
.peer_getappinfo = qede_dcbnl_peer_getappinfo,
.peer_getapptable = qede_dcbnl_peer_getapptable,
.cee_peer_getpfc = qede_dcbnl_cee_peer_getpfc,
.cee_peer_getpg = qede_dcbnl_cee_peer_getpg,
};
void qede_set_dcbnl_ops(struct net_device *dev)
{
dev->dcbnl_ops = &qede_dcbnl_ops;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,561 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qede NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include "qede_ptp.h"
#define QEDE_PTP_TX_TIMEOUT (2 * HZ)
struct qede_ptp {
const struct qed_eth_ptp_ops *ops;
struct ptp_clock_info clock_info;
struct cyclecounter cc;
struct timecounter tc;
struct ptp_clock *clock;
struct work_struct work;
unsigned long ptp_tx_start;
struct qede_dev *edev;
struct sk_buff *tx_skb;
/* ptp spinlock is used for protecting the cycle/time counter fields
* and, also for serializing the qed PTP API invocations.
*/
spinlock_t lock;
bool hw_ts_ioctl_called;
u16 tx_type;
u16 rx_filter;
};
/**
* qede_ptp_adjfine() - Adjust the frequency of the PTP cycle counter.
*
* @info: The PTP clock info structure.
* @scaled_ppm: Scaled parts per million adjustment from base.
*
* Scaled parts per million is ppm with a 16-bit binary fractional field.
*
* Return: Zero on success, negative errno otherwise.
*/
static int qede_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
{
struct qede_ptp *ptp = container_of(info, struct qede_ptp, clock_info);
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
struct qede_dev *edev = ptp->edev;
int rc;
__qede_lock(edev);
if (edev->state == QEDE_STATE_OPEN) {
spin_lock_bh(&ptp->lock);
rc = ptp->ops->adjfreq(edev->cdev, ppb);
spin_unlock_bh(&ptp->lock);
} else {
DP_ERR(edev, "PTP adjfine called while interface is down\n");
rc = -EFAULT;
}
__qede_unlock(edev);
return rc;
}
static int qede_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
{
struct qede_dev *edev;
struct qede_ptp *ptp;
ptp = container_of(info, struct qede_ptp, clock_info);
edev = ptp->edev;
DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP adjtime called, delta = %llx\n",
delta);
spin_lock_bh(&ptp->lock);
timecounter_adjtime(&ptp->tc, delta);
spin_unlock_bh(&ptp->lock);
return 0;
}
static int qede_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
{
struct qede_dev *edev;
struct qede_ptp *ptp;
u64 ns;
ptp = container_of(info, struct qede_ptp, clock_info);
edev = ptp->edev;
spin_lock_bh(&ptp->lock);
ns = timecounter_read(&ptp->tc);
spin_unlock_bh(&ptp->lock);
DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP gettime called, ns = %llu\n", ns);
*ts = ns_to_timespec64(ns);
return 0;
}
static int qede_ptp_settime(struct ptp_clock_info *info,
const struct timespec64 *ts)
{
struct qede_dev *edev;
struct qede_ptp *ptp;
u64 ns;
ptp = container_of(info, struct qede_ptp, clock_info);
edev = ptp->edev;
ns = timespec64_to_ns(ts);
DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP settime called, ns = %llu\n", ns);
/* Re-init the timecounter */
spin_lock_bh(&ptp->lock);
timecounter_init(&ptp->tc, &ptp->cc, ns);
spin_unlock_bh(&ptp->lock);
return 0;
}
/* Enable (or disable) ancillary features of the phc subsystem */
static int qede_ptp_ancillary_feature_enable(struct ptp_clock_info *info,
struct ptp_clock_request *rq,
int on)
{
struct qede_dev *edev;
struct qede_ptp *ptp;
ptp = container_of(info, struct qede_ptp, clock_info);
edev = ptp->edev;
DP_ERR(edev, "PHC ancillary features are not supported\n");
return -ENOTSUPP;
}
static void qede_ptp_task(struct work_struct *work)
{
struct skb_shared_hwtstamps shhwtstamps;
struct qede_dev *edev;
struct qede_ptp *ptp;
u64 timestamp, ns;
bool timedout;
int rc;
ptp = container_of(work, struct qede_ptp, work);
edev = ptp->edev;
timedout = time_is_before_jiffies(ptp->ptp_tx_start +
QEDE_PTP_TX_TIMEOUT);
/* Read Tx timestamp registers */
spin_lock_bh(&ptp->lock);
rc = ptp->ops->read_tx_ts(edev->cdev, &timestamp);
spin_unlock_bh(&ptp->lock);
if (rc) {
if (unlikely(timedout)) {
DP_INFO(edev, "Tx timestamp is not recorded\n");
dev_kfree_skb_any(ptp->tx_skb);
ptp->tx_skb = NULL;
clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS,
&edev->flags);
edev->ptp_skip_txts++;
} else {
/* Reschedule to keep checking for a valid TS value */
schedule_work(&ptp->work);
}
return;
}
ns = timecounter_cyc2time(&ptp->tc, timestamp);
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
shhwtstamps.hwtstamp = ns_to_ktime(ns);
skb_tstamp_tx(ptp->tx_skb, &shhwtstamps);
dev_kfree_skb_any(ptp->tx_skb);
ptp->tx_skb = NULL;
clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags);
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Tx timestamp, timestamp cycles = %llu, ns = %llu\n",
timestamp, ns);
}
/* Read the PHC. This API is invoked with ptp_lock held. */
static u64 qede_ptp_read_cc(struct cyclecounter *cc)
{
struct qede_dev *edev;
struct qede_ptp *ptp;
u64 phc_cycles;
int rc;
ptp = container_of(cc, struct qede_ptp, cc);
edev = ptp->edev;
rc = ptp->ops->read_cc(edev->cdev, &phc_cycles);
if (rc)
WARN_ONCE(1, "PHC read err %d\n", rc);
DP_VERBOSE(edev, QED_MSG_DEBUG, "PHC read cycles = %llu\n", phc_cycles);
return phc_cycles;
}
static void qede_ptp_cfg_filters(struct qede_dev *edev)
{
enum qed_ptp_hwtstamp_tx_type tx_type = QED_PTP_HWTSTAMP_TX_ON;
enum qed_ptp_filter_type rx_filter = QED_PTP_FILTER_NONE;
struct qede_ptp *ptp = edev->ptp;
if (!ptp->hw_ts_ioctl_called) {
DP_INFO(edev, "TS IOCTL not called\n");
return;
}
switch (ptp->tx_type) {
case HWTSTAMP_TX_ON:
set_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
tx_type = QED_PTP_HWTSTAMP_TX_ON;
break;
case HWTSTAMP_TX_OFF:
clear_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
tx_type = QED_PTP_HWTSTAMP_TX_OFF;
break;
}
spin_lock_bh(&ptp->lock);
switch (ptp->rx_filter) {
case HWTSTAMP_FILTER_NONE:
rx_filter = QED_PTP_FILTER_NONE;
break;
case HWTSTAMP_FILTER_ALL:
case HWTSTAMP_FILTER_SOME:
case HWTSTAMP_FILTER_NTP_ALL:
ptp->rx_filter = HWTSTAMP_FILTER_NONE;
rx_filter = QED_PTP_FILTER_ALL;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
rx_filter = QED_PTP_FILTER_V1_L4_EVENT;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
/* Initialize PTP detection for UDP/IPv4 events */
rx_filter = QED_PTP_FILTER_V1_L4_GEN;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
rx_filter = QED_PTP_FILTER_V2_L4_EVENT;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
/* Initialize PTP detection for UDP/IPv4 or UDP/IPv6 events */
rx_filter = QED_PTP_FILTER_V2_L4_GEN;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
rx_filter = QED_PTP_FILTER_V2_L2_EVENT;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
/* Initialize PTP detection L2 events */
rx_filter = QED_PTP_FILTER_V2_L2_GEN;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
rx_filter = QED_PTP_FILTER_V2_EVENT;
break;
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
/* Initialize PTP detection L2, UDP/IPv4 or UDP/IPv6 events */
rx_filter = QED_PTP_FILTER_V2_GEN;
break;
}
ptp->ops->cfg_filters(edev->cdev, rx_filter, tx_type);
spin_unlock_bh(&ptp->lock);
}
int qede_hwtstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct qede_dev *edev = netdev_priv(netdev);
struct qede_ptp *ptp;
if (!netif_running(netdev)) {
NL_SET_ERR_MSG_MOD(extack, "Device is down");
return -EAGAIN;
}
ptp = edev->ptp;
if (!ptp) {
NL_SET_ERR_MSG_MOD(extack, "HW timestamping is not supported");
return -EIO;
}
DP_VERBOSE(edev, QED_MSG_DEBUG,
"HWTSTAMP SET: Requested tx_type = %d, requested rx_filters = %d\n",
config->tx_type, config->rx_filter);
switch (config->tx_type) {
case HWTSTAMP_TX_ON:
case HWTSTAMP_TX_OFF:
break;
default:
NL_SET_ERR_MSG_MOD(extack,
"One-step timestamping is not supported");
return -ERANGE;
}
ptp->hw_ts_ioctl_called = 1;
ptp->tx_type = config->tx_type;
ptp->rx_filter = config->rx_filter;
qede_ptp_cfg_filters(edev);
config->rx_filter = ptp->rx_filter;
return 0;
}
int qede_hwtstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config)
{
struct qede_dev *edev = netdev_priv(netdev);
struct qede_ptp *ptp;
ptp = edev->ptp;
if (!ptp)
return -EIO;
config->tx_type = ptp->tx_type;
config->rx_filter = ptp->rx_filter;
return 0;
}
int qede_ptp_get_ts_info(struct qede_dev *edev, struct kernel_ethtool_ts_info *info)
{
struct qede_ptp *ptp = edev->ptp;
if (!ptp) {
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
return 0;
}
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if (ptp->clock)
info->phc_index = ptp_clock_index(ptp->clock);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ);
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
return 0;
}
void qede_ptp_disable(struct qede_dev *edev)
{
struct qede_ptp *ptp;
ptp = edev->ptp;
if (!ptp)
return;
if (ptp->clock) {
ptp_clock_unregister(ptp->clock);
ptp->clock = NULL;
}
/* Cancel PTP work queue. Should be done after the Tx queues are
* drained to prevent additional scheduling.
*/
cancel_work_sync(&ptp->work);
if (ptp->tx_skb) {
dev_kfree_skb_any(ptp->tx_skb);
ptp->tx_skb = NULL;
clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags);
}
/* Disable PTP in HW */
spin_lock_bh(&ptp->lock);
ptp->ops->disable(edev->cdev);
spin_unlock_bh(&ptp->lock);
kfree(ptp);
edev->ptp = NULL;
}
static int qede_ptp_init(struct qede_dev *edev)
{
struct qede_ptp *ptp;
int rc;
ptp = edev->ptp;
if (!ptp)
return -EINVAL;
spin_lock_init(&ptp->lock);
/* Configure PTP in HW */
rc = ptp->ops->enable(edev->cdev);
if (rc) {
DP_INFO(edev, "PTP HW enable failed\n");
return rc;
}
/* Init work queue for Tx timestamping */
INIT_WORK(&ptp->work, qede_ptp_task);
/* Init cyclecounter and timecounter */
memset(&ptp->cc, 0, sizeof(ptp->cc));
ptp->cc.read = qede_ptp_read_cc;
ptp->cc.mask = CYCLECOUNTER_MASK(64);
ptp->cc.shift = 0;
ptp->cc.mult = 1;
timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real()));
return 0;
}
int qede_ptp_enable(struct qede_dev *edev)
{
struct qede_ptp *ptp;
int rc;
ptp = kzalloc_obj(*ptp);
if (!ptp) {
DP_INFO(edev, "Failed to allocate struct for PTP\n");
return -ENOMEM;
}
ptp->edev = edev;
ptp->ops = edev->ops->ptp;
if (!ptp->ops) {
DP_INFO(edev, "PTP enable failed\n");
rc = -EIO;
goto err1;
}
edev->ptp = ptp;
rc = qede_ptp_init(edev);
if (rc)
goto err1;
qede_ptp_cfg_filters(edev);
/* Fill the ptp_clock_info struct and register PTP clock */
ptp->clock_info.owner = THIS_MODULE;
snprintf(ptp->clock_info.name, 16, "%s", edev->ndev->name);
ptp->clock_info.max_adj = QED_MAX_PHC_DRIFT_PPB;
ptp->clock_info.n_alarm = 0;
ptp->clock_info.n_ext_ts = 0;
ptp->clock_info.n_per_out = 0;
ptp->clock_info.pps = 0;
ptp->clock_info.adjfine = qede_ptp_adjfine;
ptp->clock_info.adjtime = qede_ptp_adjtime;
ptp->clock_info.gettime64 = qede_ptp_gettime;
ptp->clock_info.settime64 = qede_ptp_settime;
ptp->clock_info.enable = qede_ptp_ancillary_feature_enable;
ptp->clock = ptp_clock_register(&ptp->clock_info, &edev->pdev->dev);
if (IS_ERR(ptp->clock)) {
DP_ERR(edev, "PTP clock registration failed\n");
qede_ptp_disable(edev);
rc = -EINVAL;
goto err2;
}
return 0;
err1:
kfree(ptp);
err2:
edev->ptp = NULL;
return rc;
}
void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb)
{
struct qede_ptp *ptp;
ptp = edev->ptp;
if (!ptp)
return;
if (test_and_set_bit_lock(QEDE_FLAGS_PTP_TX_IN_PRORGESS,
&edev->flags)) {
DP_VERBOSE(edev, QED_MSG_DEBUG, "Timestamping in progress\n");
edev->ptp_skip_txts++;
return;
}
if (unlikely(!test_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags))) {
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Tx timestamping was not enabled, this pkt will not be timestamped\n");
clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags);
edev->ptp_skip_txts++;
} else if (unlikely(ptp->tx_skb)) {
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Device supports a single outstanding pkt to ts, It will not be ts\n");
clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags);
edev->ptp_skip_txts++;
} else {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
/* schedule check for Tx timestamp */
ptp->tx_skb = skb_get(skb);
ptp->ptp_tx_start = jiffies;
schedule_work(&ptp->work);
}
}
void qede_ptp_rx_ts(struct qede_dev *edev, struct sk_buff *skb)
{
struct qede_ptp *ptp;
u64 timestamp, ns;
int rc;
ptp = edev->ptp;
if (!ptp)
return;
spin_lock_bh(&ptp->lock);
rc = ptp->ops->read_rx_ts(edev->cdev, &timestamp);
if (rc) {
spin_unlock_bh(&ptp->lock);
DP_INFO(edev, "Invalid Rx timestamp\n");
return;
}
ns = timecounter_cyc2time(&ptp->tc, timestamp);
spin_unlock_bh(&ptp->lock);
skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Rx timestamp, timestamp cycles = %llu, ns = %llu\n",
timestamp, ns);
}
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qede NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QEDE_PTP_H_
#define _QEDE_PTP_H_
#include <linux/ptp_clock_kernel.h>
#include <linux/net_tstamp.h>
#include <linux/timecounter.h>
#include "qede.h"
void qede_ptp_rx_ts(struct qede_dev *edev, struct sk_buff *skb);
void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb);
int qede_hwtstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config);
int qede_hwtstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
void qede_ptp_disable(struct qede_dev *edev);
int qede_ptp_enable(struct qede_dev *edev);
int qede_ptp_get_ts_info(struct qede_dev *edev, struct kernel_ethtool_ts_info *ts);
static inline void qede_ptp_record_rx_ts(struct qede_dev *edev,
union eth_rx_cqe *cqe,
struct sk_buff *skb)
{
/* Check if this packet was timestamped */
if (unlikely(le16_to_cpu(cqe->fast_path_regular.pars_flags.flags) &
(1 << PARSING_AND_ERR_FLAGS_TIMESTAMPRECORDED_SHIFT))) {
if (likely(le16_to_cpu(cqe->fast_path_regular.pars_flags.flags)
& (1 << PARSING_AND_ERR_FLAGS_TIMESYNCPKT_SHIFT))) {
qede_ptp_rx_ts(edev, skb);
} else {
DP_INFO(edev,
"Timestamp recorded for non PTP packets\n");
}
}
}
#endif /* _QEDE_PTP_H_ */
@@ -0,0 +1,355 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qedr NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/qed/qede_rdma.h>
#include "qede.h"
static struct qedr_driver *qedr_drv;
static LIST_HEAD(qedr_dev_list);
static DEFINE_MUTEX(qedr_dev_list_lock);
bool qede_rdma_supported(struct qede_dev *dev)
{
return dev->dev_info.common.rdma_supported;
}
static void _qede_rdma_dev_add(struct qede_dev *edev)
{
if (!qedr_drv)
return;
/* Leftovers from previous error recovery */
edev->rdma_info.exp_recovery = false;
edev->rdma_info.qedr_dev = qedr_drv->add(edev->cdev, edev->pdev,
edev->ndev);
}
static int qede_rdma_create_wq(struct qede_dev *edev)
{
INIT_LIST_HEAD(&edev->rdma_info.rdma_event_list);
kref_init(&edev->rdma_info.refcnt);
init_completion(&edev->rdma_info.event_comp);
edev->rdma_info.rdma_wq = create_singlethread_workqueue("rdma_wq");
if (!edev->rdma_info.rdma_wq) {
DP_NOTICE(edev, "qedr: Could not create workqueue\n");
return -ENOMEM;
}
return 0;
}
static void qede_rdma_cleanup_event(struct qede_dev *edev)
{
struct list_head *head = &edev->rdma_info.rdma_event_list;
struct qede_rdma_event_work *event_node;
flush_workqueue(edev->rdma_info.rdma_wq);
while (!list_empty(head)) {
event_node = list_entry(head->next, struct qede_rdma_event_work,
list);
cancel_work_sync(&event_node->work);
list_del(&event_node->list);
kfree(event_node);
}
}
static void qede_rdma_complete_event(struct kref *ref)
{
struct qede_rdma_dev *rdma_dev =
container_of(ref, struct qede_rdma_dev, refcnt);
/* no more events will be added after this */
complete(&rdma_dev->event_comp);
}
static void qede_rdma_destroy_wq(struct qede_dev *edev)
{
/* Avoid race with add_event flow, make sure it finishes before
* we start accessing the list and cleaning up the work
*/
kref_put(&edev->rdma_info.refcnt, qede_rdma_complete_event);
wait_for_completion(&edev->rdma_info.event_comp);
qede_rdma_cleanup_event(edev);
destroy_workqueue(edev->rdma_info.rdma_wq);
edev->rdma_info.rdma_wq = NULL;
}
int qede_rdma_dev_add(struct qede_dev *edev, bool recovery)
{
int rc;
if (!qede_rdma_supported(edev))
return 0;
/* Cannot start qedr while recovering since it wasn't fully stopped */
if (recovery)
return 0;
rc = qede_rdma_create_wq(edev);
if (rc)
return rc;
INIT_LIST_HEAD(&edev->rdma_info.entry);
mutex_lock(&qedr_dev_list_lock);
list_add_tail(&edev->rdma_info.entry, &qedr_dev_list);
_qede_rdma_dev_add(edev);
mutex_unlock(&qedr_dev_list_lock);
return rc;
}
static void _qede_rdma_dev_remove(struct qede_dev *edev)
{
if (qedr_drv && qedr_drv->remove && edev->rdma_info.qedr_dev)
qedr_drv->remove(edev->rdma_info.qedr_dev);
}
void qede_rdma_dev_remove(struct qede_dev *edev, bool recovery)
{
if (!qede_rdma_supported(edev))
return;
/* Cannot remove qedr while recovering since it wasn't fully stopped */
if (!recovery) {
qede_rdma_destroy_wq(edev);
mutex_lock(&qedr_dev_list_lock);
if (!edev->rdma_info.exp_recovery)
_qede_rdma_dev_remove(edev);
edev->rdma_info.qedr_dev = NULL;
list_del(&edev->rdma_info.entry);
mutex_unlock(&qedr_dev_list_lock);
} else {
if (!edev->rdma_info.exp_recovery) {
mutex_lock(&qedr_dev_list_lock);
_qede_rdma_dev_remove(edev);
mutex_unlock(&qedr_dev_list_lock);
}
edev->rdma_info.exp_recovery = true;
}
}
static void _qede_rdma_dev_open(struct qede_dev *edev)
{
if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_UP);
}
static void qede_rdma_dev_open(struct qede_dev *edev)
{
if (!qede_rdma_supported(edev))
return;
mutex_lock(&qedr_dev_list_lock);
_qede_rdma_dev_open(edev);
mutex_unlock(&qedr_dev_list_lock);
}
static void _qede_rdma_dev_close(struct qede_dev *edev)
{
if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_DOWN);
}
static void qede_rdma_dev_close(struct qede_dev *edev)
{
if (!qede_rdma_supported(edev))
return;
mutex_lock(&qedr_dev_list_lock);
_qede_rdma_dev_close(edev);
mutex_unlock(&qedr_dev_list_lock);
}
static void qede_rdma_dev_shutdown(struct qede_dev *edev)
{
if (!qede_rdma_supported(edev))
return;
mutex_lock(&qedr_dev_list_lock);
if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_CLOSE);
mutex_unlock(&qedr_dev_list_lock);
}
int qede_rdma_register_driver(struct qedr_driver *drv)
{
struct qede_dev *edev;
u8 qedr_counter = 0;
mutex_lock(&qedr_dev_list_lock);
if (qedr_drv) {
mutex_unlock(&qedr_dev_list_lock);
return -EINVAL;
}
qedr_drv = drv;
list_for_each_entry(edev, &qedr_dev_list, rdma_info.entry) {
struct net_device *ndev;
qedr_counter++;
_qede_rdma_dev_add(edev);
ndev = edev->ndev;
if (netif_running(ndev) && netif_oper_up(ndev))
_qede_rdma_dev_open(edev);
}
mutex_unlock(&qedr_dev_list_lock);
pr_notice("qedr: discovered and registered %d RDMA funcs\n",
qedr_counter);
return 0;
}
EXPORT_SYMBOL(qede_rdma_register_driver);
void qede_rdma_unregister_driver(struct qedr_driver *drv)
{
struct qede_dev *edev;
mutex_lock(&qedr_dev_list_lock);
list_for_each_entry(edev, &qedr_dev_list, rdma_info.entry) {
/* If device has experienced recovery it was already removed */
if (edev->rdma_info.qedr_dev && !edev->rdma_info.exp_recovery)
_qede_rdma_dev_remove(edev);
}
qedr_drv = NULL;
mutex_unlock(&qedr_dev_list_lock);
}
EXPORT_SYMBOL(qede_rdma_unregister_driver);
static void qede_rdma_changeaddr(struct qede_dev *edev)
{
if (!qede_rdma_supported(edev))
return;
if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_CHANGE_ADDR);
}
static void qede_rdma_change_mtu(struct qede_dev *edev)
{
if (qede_rdma_supported(edev)) {
if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
qedr_drv->notify(edev->rdma_info.qedr_dev,
QEDE_CHANGE_MTU);
}
}
static struct qede_rdma_event_work *
qede_rdma_get_free_event_node(struct qede_dev *edev)
{
struct qede_rdma_event_work *event_node = NULL;
bool found = false;
list_for_each_entry(event_node, &edev->rdma_info.rdma_event_list,
list) {
if (!work_pending(&event_node->work)) {
found = true;
break;
}
}
if (!found) {
event_node = kzalloc_obj(*event_node, GFP_ATOMIC);
if (!event_node) {
DP_NOTICE(edev,
"qedr: Could not allocate memory for rdma work\n");
return NULL;
}
list_add_tail(&event_node->list,
&edev->rdma_info.rdma_event_list);
}
return event_node;
}
static void qede_rdma_handle_event(struct work_struct *work)
{
struct qede_rdma_event_work *event_node;
enum qede_rdma_event event;
struct qede_dev *edev;
event_node = container_of(work, struct qede_rdma_event_work, work);
event = event_node->event;
edev = event_node->ptr;
switch (event) {
case QEDE_UP:
qede_rdma_dev_open(edev);
break;
case QEDE_DOWN:
qede_rdma_dev_close(edev);
break;
case QEDE_CLOSE:
qede_rdma_dev_shutdown(edev);
break;
case QEDE_CHANGE_ADDR:
qede_rdma_changeaddr(edev);
break;
case QEDE_CHANGE_MTU:
qede_rdma_change_mtu(edev);
break;
default:
DP_NOTICE(edev, "Invalid rdma event %d", event);
}
}
static void qede_rdma_add_event(struct qede_dev *edev,
enum qede_rdma_event event)
{
struct qede_rdma_event_work *event_node;
/* If a recovery was experienced avoid adding the event */
if (edev->rdma_info.exp_recovery)
return;
if (!edev->rdma_info.qedr_dev || !edev->rdma_info.rdma_wq)
return;
/* We don't want the cleanup flow to start while we're allocating and
* scheduling the work
*/
if (!kref_get_unless_zero(&edev->rdma_info.refcnt))
return; /* already being destroyed */
event_node = qede_rdma_get_free_event_node(edev);
if (!event_node)
goto out;
event_node->event = event;
event_node->ptr = edev;
INIT_WORK(&event_node->work, qede_rdma_handle_event);
queue_work(edev->rdma_info.rdma_wq, &event_node->work);
out:
kref_put(&edev->rdma_info.refcnt, qede_rdma_complete_event);
}
void qede_rdma_dev_event_open(struct qede_dev *edev)
{
qede_rdma_add_event(edev, QEDE_UP);
}
void qede_rdma_dev_event_close(struct qede_dev *edev)
{
qede_rdma_add_event(edev, QEDE_DOWN);
}
void qede_rdma_event_changeaddr(struct qede_dev *edev)
{
qede_rdma_add_event(edev, QEDE_CHANGE_ADDR);
}
void qede_rdma_event_change_mtu(struct qede_dev *edev)
{
qede_rdma_add_event(edev, QEDE_CHANGE_MTU);
}