Commit 04a18399 authored by Egor Pomozov's avatar Egor Pomozov Committed by David S. Miller

net: aquantia: implement data PTP datapath

Here we do alloc/free IRQs for PTP rings.
We also implement processing of PTP packets on TX and RX sides.
Signed-off-by: default avatarEgor Pomozov <epomozov@marvell.com>
Co-developed-by: default avatarSergey Samoilenko <sergey.samoilenko@aquantia.com>
Signed-off-by: default avatarSergey Samoilenko <sergey.samoilenko@aquantia.com>
Co-developed-by: default avatarDmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Signed-off-by: default avatarDmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Signed-off-by: default avatarIgor Russkikh <igor.russkikh@aquantia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 61cc502e
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* aQuantia Corporation Network Driver
* Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
* Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
*/
/* File aq_cfg.h: Definition of configuration parameters and constants. */
......@@ -27,7 +27,7 @@
#define AQ_CFG_INTERRUPT_MODERATION_USEC_MAX (0x1FF * 2)
#define AQ_CFG_IRQ_MASK 0x1FFU
#define AQ_CFG_IRQ_MASK 0x3FFU
#define AQ_CFG_VECS_MAX 8U
#define AQ_CFG_TCS_MAX 8U
......
......@@ -244,6 +244,12 @@ struct aq_hw_ops {
int (*hw_rx_tc_mode_get)(struct aq_hw_s *self, u32 *tc_mode);
int (*hw_ring_hwts_rx_fill)(struct aq_hw_s *self,
struct aq_ring_s *aq_ring);
int (*hw_ring_hwts_rx_receive)(struct aq_hw_s *self,
struct aq_ring_s *ring);
void (*hw_get_ptp_ts)(struct aq_hw_s *self, u64 *stamp);
int (*hw_adj_clock_freq)(struct aq_hw_s *self, s32 delta);
......@@ -252,6 +258,12 @@ struct aq_hw_ops {
int (*hw_set_sys_clock)(struct aq_hw_s *self, u64 time, u64 ts);
u16 (*rx_extract_ts)(struct aq_hw_s *self, u8 *p, unsigned int len,
u64 *timestamp);
int (*extract_hwts)(struct aq_hw_s *self, u8 *p, unsigned int len,
u64 *timestamp);
int (*hw_set_fc)(struct aq_hw_s *self, u32 fc, u32 tc);
};
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* aQuantia Corporation Network Driver
* Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
* Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
*/
/* File aq_main.c: Main file for aQuantia Linux driver. */
......@@ -10,10 +10,13 @@
#include "aq_nic.h"
#include "aq_pci_func.h"
#include "aq_ethtool.h"
#include "aq_ptp.h"
#include "aq_filters.h"
#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/udp.h>
MODULE_LICENSE("GPL v2");
MODULE_VERSION(AQ_CFG_DRV_VERSION);
......@@ -93,6 +96,24 @@ static int aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct aq_nic_s *aq_nic = netdev_priv(ndev);
if (unlikely(aq_utils_obj_test(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP))) {
/* Hardware adds the Timestamp for PTPv2 802.AS1
* and PTPv2 IPv4 UDP.
* We have to push even general 320 port messages to the ptp
* queue explicitly. This is a limitation of current firmware
* and hardware PTP design of the chip. Otherwise ptp stream
* will fail to sync
*/
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
unlikely((ip_hdr(skb)->version == 4) &&
(ip_hdr(skb)->protocol == IPPROTO_UDP) &&
((udp_hdr(skb)->dest == htons(319)) ||
(udp_hdr(skb)->dest == htons(320)))) ||
unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588)))
return aq_ptp_xmit(aq_nic, skb);
}
skb_tx_timestamp(skb);
return aq_nic_xmit(aq_nic, skb);
}
......
......@@ -146,8 +146,11 @@ static int aq_nic_update_link_status(struct aq_nic_s *self)
self->aq_hw->aq_link_status.mbps);
aq_nic_update_interrupt_moderation_settings(self);
if (self->aq_ptp)
if (self->aq_ptp) {
aq_ptp_clock_init(self);
aq_ptp_tm_offset_set(self,
self->aq_hw->aq_link_status.mbps);
}
/* Driver has to update flow control settings on RX block
* on any link event.
......@@ -196,6 +199,8 @@ static void aq_nic_service_task(struct work_struct *work)
service_task);
int err;
aq_ptp_service_task(self);
if (aq_utils_obj_test(&self->flags, AQ_NIC_FLAGS_IS_NOT_READY))
return;
......@@ -408,6 +413,10 @@ int aq_nic_start(struct aq_nic_s *self)
goto err_exit;
}
err = aq_ptp_irq_alloc(self);
if (err < 0)
goto err_exit;
if (self->aq_nic_cfg.link_irq_vec) {
int irqvec = pci_irq_vector(self->pdev,
self->aq_nic_cfg.link_irq_vec);
......@@ -440,9 +449,8 @@ int aq_nic_start(struct aq_nic_s *self)
return err;
}
static unsigned int aq_nic_map_skb(struct aq_nic_s *self,
struct sk_buff *skb,
struct aq_ring_s *ring)
unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb,
struct aq_ring_s *ring)
{
unsigned int ret = 0U;
unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
......@@ -973,6 +981,8 @@ int aq_nic_stop(struct aq_nic_s *self)
else
aq_pci_func_free_irqs(self);
aq_ptp_irq_free(self);
for (i = 0U, aq_vec = self->aq_vec[0];
self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i])
aq_vec_stop(aq_vec);
......
......@@ -54,6 +54,7 @@ struct aq_nic_cfg_s {
#define AQ_NIC_FLAG_STOPPING 0x00000008U
#define AQ_NIC_FLAG_RESETTING 0x00000010U
#define AQ_NIC_FLAG_CLOSING 0x00000020U
#define AQ_NIC_PTP_DPATH_UP 0x02000000U
#define AQ_NIC_LINK_DOWN 0x04000000U
#define AQ_NIC_FLAG_ERR_UNPLUG 0x40000000U
#define AQ_NIC_FLAG_ERR_HW 0x80000000U
......@@ -129,6 +130,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self);
int aq_nic_ndev_register(struct aq_nic_s *self);
void aq_nic_ndev_free(struct aq_nic_s *self);
int aq_nic_start(struct aq_nic_s *self);
unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb,
struct aq_ring_s *ring);
int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb);
int aq_nic_get_regs(struct aq_nic_s *self, struct ethtool_regs *regs, void *p);
int aq_nic_get_regs_count(struct aq_nic_s *self);
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* aQuantia Corporation Network Driver
* Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
* Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
*/
/* File aq_pci_func.c: Definition of PCI functions. */
......@@ -269,6 +269,9 @@ static int aq_pci_probe(struct pci_dev *pdev,
numvecs = min((u8)AQ_CFG_VECS_DEF,
aq_nic_get_cfg(self)->aq_hw_caps->msix_irqs);
numvecs = min(numvecs, num_online_cpus());
/* Request IRQ vector for PTP */
numvecs += 1;
numvecs += AQ_HW_SERVICE_IRQS;
/*enable interrupts */
#if !AQ_CFG_FORCE_LEGACY_INT
......
......@@ -17,6 +17,9 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec);
void aq_ptp_unregister(struct aq_nic_s *aq_nic);
void aq_ptp_free(struct aq_nic_s *aq_nic);
int aq_ptp_irq_alloc(struct aq_nic_s *aq_nic);
void aq_ptp_irq_free(struct aq_nic_s *aq_nic);
int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic);
void aq_ptp_ring_free(struct aq_nic_s *aq_nic);
......@@ -25,6 +28,22 @@ int aq_ptp_ring_start(struct aq_nic_s *aq_nic);
void aq_ptp_ring_stop(struct aq_nic_s *aq_nic);
void aq_ptp_ring_deinit(struct aq_nic_s *aq_nic);
void aq_ptp_service_task(struct aq_nic_s *aq_nic);
void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps);
void aq_ptp_clock_init(struct aq_nic_s *aq_nic);
/* Traffic processing functions */
int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb);
void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp);
/* Return either ring is belong to PTP or not*/
bool aq_ptp_ring(struct aq_nic_s *aq_nic, struct aq_ring_s *ring);
u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, struct sk_buff *skb, u8 *p,
unsigned int len);
struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp);
#endif /* AQ_PTP_H */
......@@ -10,6 +10,7 @@
#include "aq_nic.h"
#include "aq_hw.h"
#include "aq_hw_utils.h"
#include "aq_ptp.h"
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
......@@ -314,6 +315,7 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
self->sw_head = aq_ring_next_dx(self, self->sw_head),
--budget, ++(*work_done)) {
struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
bool is_ptp_ring = aq_ptp_ring(self->aq_nic, self);
struct aq_ring_buff_s *buff_ = NULL;
struct sk_buff *skb = NULL;
unsigned int next_ = 0U;
......@@ -378,6 +380,11 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
err = -ENOMEM;
goto err_exit;
}
if (is_ptp_ring)
buff->len -=
aq_ptp_extract_ts(self->aq_nic, skb,
aq_buf_vaddr(&buff->rxdata),
buff->len);
skb_put(skb, buff->len);
page_ref_inc(buff->rxdata.page);
} else {
......@@ -386,6 +393,11 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
err = -ENOMEM;
goto err_exit;
}
if (is_ptp_ring)
buff->len -=
aq_ptp_extract_ts(self->aq_nic, skb,
aq_buf_vaddr(&buff->rxdata),
buff->len);
hdr_len = buff->len;
if (hdr_len > AQ_CFG_RX_HDR_SIZE)
......@@ -445,8 +457,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
skb_set_hash(skb, buff->rss_hash,
buff->is_hash_l4 ? PKT_HASH_TYPE_L4 :
PKT_HASH_TYPE_NONE);
skb_record_rx_queue(skb, self->idx);
/* Send all PTP traffic to 0 queue */
skb_record_rx_queue(skb, is_ptp_ring ? 0 : self->idx);
++self->stats.rx.packets;
self->stats.rx.bytes += skb->len;
......@@ -458,6 +470,21 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
return err;
}
void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic)
{
while (self->sw_head != self->hw_head) {
u64 ns;
aq_nic->aq_hw_ops->extract_hwts(aq_nic->aq_hw,
self->dx_ring +
(self->sw_head * self->dx_size),
self->dx_size, &ns);
aq_ptp_tx_hwtstamp(aq_nic, ns);
self->sw_head = aq_ring_next_dx(self, self->sw_head);
}
}
int aq_ring_rx_fill(struct aq_ring_s *self)
{
unsigned int page_order = self->page_order;
......
......@@ -177,5 +177,6 @@ int aq_ring_rx_fill(struct aq_ring_s *self);
struct aq_ring_s *aq_ring_hwts_rx_alloc(struct aq_ring_s *self,
struct aq_nic_s *aq_nic, unsigned int idx,
unsigned int size, unsigned int dx_size);
void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic);
#endif /* AQ_RING_H */
......@@ -682,6 +682,46 @@ static int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self,
return aq_hw_err_from_flags(self);
}
static int hw_atl_b0_hw_ring_hwts_rx_fill(struct aq_hw_s *self,
struct aq_ring_s *ring)
{
unsigned int i;
for (i = aq_ring_avail_dx(ring); i--;
ring->sw_tail = aq_ring_next_dx(ring, ring->sw_tail)) {
struct hw_atl_rxd_s *rxd =
(struct hw_atl_rxd_s *)
&ring->dx_ring[ring->sw_tail * HW_ATL_B0_RXD_SIZE];
rxd->buf_addr = ring->dx_ring_pa + ring->size * ring->dx_size;
rxd->hdr_addr = 0U;
}
/* Make sure descriptors are updated before bump tail*/
wmb();
hw_atl_reg_rx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx);
return aq_hw_err_from_flags(self);
}
static int hw_atl_b0_hw_ring_hwts_rx_receive(struct aq_hw_s *self,
struct aq_ring_s *ring)
{
while (ring->hw_head != ring->sw_tail) {
struct hw_atl_rxd_hwts_wb_s *hwts_wb =
(struct hw_atl_rxd_hwts_wb_s *)
(ring->dx_ring + (ring->hw_head * HW_ATL_B0_RXD_SIZE));
/* RxD is not done */
if (!(hwts_wb->sec_lw0 & 0x1U))
break;
ring->hw_head = aq_ring_next_dx(ring, ring->hw_head);
}
return aq_hw_err_from_flags(self);
}
static int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
struct aq_ring_s *ring)
{
......@@ -1133,6 +1173,61 @@ static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb)
return self->aq_fw_ops->send_fw_request(self, &fwreq, size);
}
static u16 hw_atl_b0_rx_extract_ts(struct aq_hw_s *self, u8 *p,
unsigned int len, u64 *timestamp)
{
unsigned int offset = 14;
struct ethhdr *eth;
u64 sec;
u8 *ptr;
u32 ns;
if (len <= offset || !timestamp)
return 0;
/* The TIMESTAMP in the end of package has following format:
* (big-endian)
* struct {
* uint64_t sec;
* uint32_t ns;
* uint16_t stream_id;
* };
*/
ptr = p + (len - offset);
memcpy(&sec, ptr, sizeof(sec));
ptr += sizeof(sec);
memcpy(&ns, ptr, sizeof(ns));
sec = be64_to_cpu(sec) & 0xffffffffffffllu;
ns = be32_to_cpu(ns);
*timestamp = sec * NSEC_PER_SEC + ns + self->ptp_clk_offset;
eth = (struct ethhdr *)p;
return (eth->h_proto == htons(ETH_P_1588)) ? 12 : 14;
}
static int hw_atl_b0_extract_hwts(struct aq_hw_s *self, u8 *p, unsigned int len,
u64 *timestamp)
{
struct hw_atl_rxd_hwts_wb_s *hwts_wb = (struct hw_atl_rxd_hwts_wb_s *)p;
u64 tmp, sec, ns;
sec = 0;
tmp = (hwts_wb->sec_lw0 >> 2) & 0x3ff;
sec += tmp;
tmp = (u64)((hwts_wb->sec_lw1 >> 16) & 0xffff) << 10;
sec += tmp;
tmp = (u64)(hwts_wb->sec_hw & 0xfff) << 26;
sec += tmp;
tmp = (u64)((hwts_wb->sec_hw >> 22) & 0x3ff) << 38;
sec += tmp;
ns = sec * NSEC_PER_SEC + hwts_wb->ns;
if (timestamp)
*timestamp = ns + self->ptp_clk_offset;
return 0;
}
static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self,
struct aq_rx_filter_l3l4 *data)
{
......@@ -1309,11 +1404,16 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
.hw_tx_tc_mode_get = hw_atl_b0_tx_tc_mode_get,
.hw_rx_tc_mode_get = hw_atl_b0_rx_tc_mode_get,
.hw_ring_hwts_rx_fill = hw_atl_b0_hw_ring_hwts_rx_fill,
.hw_ring_hwts_rx_receive = hw_atl_b0_hw_ring_hwts_rx_receive,
.hw_get_ptp_ts = hw_atl_b0_get_ptp_ts,
.hw_adj_sys_clock = hw_atl_b0_adj_sys_clock,
.hw_set_sys_clock = hw_atl_b0_set_sys_clock,
.hw_adj_clock_freq = hw_atl_b0_adj_clock_freq,
.rx_extract_ts = hw_atl_b0_rx_extract_ts,
.extract_hwts = hw_atl_b0_extract_hwts,
.hw_set_offload = hw_atl_b0_hw_offload_set,
.hw_set_fc = hw_atl_b0_set_fc,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment