Commit f5015a59 authored by Russell King's avatar Russell King Committed by David S. Miller

net: mvpp2: ptp: add support for transmit timestamping

Add support for timestamping transmit packets.  We allocate SYNC
messages to queue 1, every other message to queue 0.
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ce3497e2
......@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include <net/flow_offload.h>
......@@ -463,8 +464,10 @@
#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7)
#define MVPP22_GMAC_INT_SUM_STAT 0xa0
#define MVPP22_GMAC_INT_SUM_STAT_INTERNAL BIT(1)
#define MVPP22_GMAC_INT_SUM_STAT_PTP BIT(2)
#define MVPP22_GMAC_INT_SUM_MASK 0xa4
#define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1)
#define MVPP22_GMAC_INT_SUM_MASK_PTP BIT(2)
/* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0,
* relative to port->base.
......@@ -492,9 +495,11 @@
#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13)
#define MVPP22_XLG_EXT_INT_STAT 0x158
#define MVPP22_XLG_EXT_INT_STAT_XLG BIT(1)
#define MVPP22_XLG_EXT_INT_STAT_PTP BIT(7)
#define MVPP22_XLG_EXT_INT_MASK 0x15c
#define MVPP22_XLG_EXT_INT_MASK_XLG BIT(1)
#define MVPP22_XLG_EXT_INT_MASK_GIG BIT(2)
#define MVPP22_XLG_EXT_INT_MASK_PTP BIT(7)
#define MVPP22_XLG_CTRL4_REG 0x184
#define MVPP22_XLG_CTRL4_FWD_FC BIT(5)
#define MVPP22_XLG_CTRL4_FWD_PFC BIT(6)
......@@ -598,7 +603,11 @@
/* PTP registers. PPv2.2 only */
#define MVPP22_PTP_BASE(port) (0x7800 + (port * 0x1000))
#define MVPP22_PTP_INT_CAUSE 0x00
#define MVPP22_PTP_INT_CAUSE_QUEUE1 BIT(6)
#define MVPP22_PTP_INT_CAUSE_QUEUE0 BIT(5)
#define MVPP22_PTP_INT_MASK 0x04
#define MVPP22_PTP_INT_MASK_QUEUE1 BIT(6)
#define MVPP22_PTP_INT_MASK_QUEUE0 BIT(5)
#define MVPP22_PTP_GCR 0x08
#define MVPP22_PTP_GCR_RX_RESET BIT(13)
#define MVPP22_PTP_GCR_TX_RESET BIT(1)
......@@ -796,6 +805,43 @@ enum mvpp2_prs_l3_cast {
MVPP2_PRS_L3_BROAD_CAST
};
/* PTP descriptor constants. The low bits of the descriptor are stored
* separately from the high bits.
*/
#define MVPP22_PTP_DESC_MASK_LOW 0xfff
/* PTPAction */
enum mvpp22_ptp_action {
MVPP22_PTP_ACTION_NONE = 0,
MVPP22_PTP_ACTION_FORWARD = 1,
MVPP22_PTP_ACTION_CAPTURE = 3,
/* The following have not been verified */
MVPP22_PTP_ACTION_ADDTIME = 4,
MVPP22_PTP_ACTION_ADDCORRECTEDTIME = 5,
MVPP22_PTP_ACTION_CAPTUREADDTIME = 6,
MVPP22_PTP_ACTION_CAPTUREADDCORRECTEDTIME = 7,
MVPP22_PTP_ACTION_ADDINGRESSTIME = 8,
MVPP22_PTP_ACTION_CAPTUREADDINGRESSTIME = 9,
MVPP22_PTP_ACTION_CAPTUREINGRESSTIME = 10,
};
/* PTPPacketFormat */
enum mvpp22_ptp_packet_format {
MVPP22_PTP_PKT_FMT_PTPV2 = 0,
MVPP22_PTP_PKT_FMT_PTPV1 = 1,
MVPP22_PTP_PKT_FMT_Y1731 = 2,
MVPP22_PTP_PKT_FMT_NTPTS = 3,
MVPP22_PTP_PKT_FMT_NTPRX = 4,
MVPP22_PTP_PKT_FMT_NTPTX = 5,
MVPP22_PTP_PKT_FMT_TWAMP = 6,
};
#define MVPP22_PTP_ACTION(x) (((x) & 15) << 0)
#define MVPP22_PTP_PACKETFORMAT(x) (((x) & 7) << 4)
#define MVPP22_PTP_MACTIMESTAMPINGEN BIT(11)
#define MVPP22_PTP_TIMESTAMPENTRYID(x) (((x) & 31) << 12)
#define MVPP22_PTP_TIMESTAMPQUEUESELECT BIT(18)
/* BM constants */
#define MVPP2_BM_JUMBO_BUF_NUM 512
#define MVPP2_BM_LONG_BUF_NUM 1024
......@@ -1014,6 +1060,11 @@ struct mvpp2_ethtool_fs {
struct ethtool_rxnfc rxnfc;
};
struct mvpp2_hwtstamp_queue {
struct sk_buff *skb[32];
u8 next;
};
struct mvpp2_port {
u8 id;
......@@ -1100,6 +1151,8 @@ struct mvpp2_port {
bool hwtstamp;
bool rx_hwtstamp;
enum hwtstamp_tx_types tx_hwtstamp_type;
struct mvpp2_hwtstamp_queue tx_hwtstamp_queue[2];
};
/* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the
......@@ -1168,7 +1221,8 @@ struct mvpp22_tx_desc {
u8 packet_offset;
u8 phys_txq;
__le16 data_size;
__le64 reserved1;
__le32 ptp_descriptor;
__le32 reserved2;
__le64 buf_dma_addr_ptp;
__le64 buf_cookie_misc;
};
......
......@@ -28,6 +28,7 @@
#include <linux/phy.h>
#include <linux/phylink.h>
#include <linux/phy/phy.h>
#include <linux/ptp_classify.h>
#include <linux/clk.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
......@@ -1379,6 +1380,10 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
{
u32 val;
mvpp2_modify(port->base + MVPP22_GMAC_INT_SUM_MASK,
MVPP22_GMAC_INT_SUM_MASK_PTP,
MVPP22_GMAC_INT_SUM_MASK_PTP);
if (port->phylink ||
phy_interface_mode_is_rgmii(port->phy_interface) ||
phy_interface_mode_is_8023z(port->phy_interface) ||
......@@ -1392,6 +1397,10 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
val = readl(port->base + MVPP22_XLG_INT_MASK);
val |= MVPP22_XLG_INT_MASK_LINK;
writel(val, port->base + MVPP22_XLG_INT_MASK);
mvpp2_modify(port->base + MVPP22_XLG_EXT_INT_MASK,
MVPP22_XLG_EXT_INT_MASK_PTP,
MVPP22_XLG_EXT_INT_MASK_PTP);
}
mvpp22_gop_unmask_irq(port);
......@@ -2974,6 +2983,56 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void mvpp2_isr_handle_ptp_queue(struct mvpp2_port *port, int nq)
{
struct skb_shared_hwtstamps shhwtstamps;
struct mvpp2_hwtstamp_queue *queue;
struct sk_buff *skb;
void __iomem *ptp_q;
unsigned int id;
u32 r0, r1, r2;
ptp_q = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
if (nq)
ptp_q += MVPP22_PTP_TX_Q1_R0 - MVPP22_PTP_TX_Q0_R0;
queue = &port->tx_hwtstamp_queue[nq];
while (1) {
r0 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R0) & 0xffff;
if (!r0)
break;
r1 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R1) & 0xffff;
r2 = readl_relaxed(ptp_q + MVPP22_PTP_TX_Q0_R2) & 0xffff;
id = (r0 >> 1) & 31;
skb = queue->skb[id];
queue->skb[id] = NULL;
if (skb) {
u32 ts = r2 << 19 | r1 << 3 | r0 >> 13;
mvpp22_tai_tstamp(port->priv->tai, ts, &shhwtstamps);
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
}
}
static void mvpp2_isr_handle_ptp(struct mvpp2_port *port)
{
void __iomem *ptp;
u32 val;
ptp = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
val = readl(ptp + MVPP22_PTP_INT_CAUSE);
if (val & MVPP22_PTP_INT_CAUSE_QUEUE0)
mvpp2_isr_handle_ptp_queue(port, 0);
if (val & MVPP22_PTP_INT_CAUSE_QUEUE1)
mvpp2_isr_handle_ptp_queue(port, 1);
}
static void mvpp2_isr_handle_link(struct mvpp2_port *port, bool link)
{
struct net_device *dev = port->dev;
......@@ -3049,6 +3108,8 @@ static irqreturn_t mvpp2_port_isr(int irq, void *dev_id)
val = readl(port->base + MVPP22_XLG_EXT_INT_STAT);
if (val & MVPP22_XLG_EXT_INT_STAT_XLG)
mvpp2_isr_handle_xlg(port);
if (val & MVPP22_XLG_EXT_INT_STAT_PTP)
mvpp2_isr_handle_ptp(port);
} else {
/* If it's not the XLG, we must be using the GMAC.
* Check the summary status.
......@@ -3056,6 +3117,8 @@ static irqreturn_t mvpp2_port_isr(int irq, void *dev_id)
val = readl(port->base + MVPP22_GMAC_INT_SUM_STAT);
if (val & MVPP22_GMAC_INT_SUM_STAT_INTERNAL)
mvpp2_isr_handle_gmac_internal(port);
if (val & MVPP22_GMAC_INT_SUM_STAT_PTP)
mvpp2_isr_handle_ptp(port);
}
mvpp22_gop_unmask_irq(port);
......@@ -3610,6 +3673,92 @@ tx_desc_unmap_put(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
mvpp2_txq_desc_put(txq);
}
static void mvpp2_txdesc_clear_ptp(struct mvpp2_port *port,
struct mvpp2_tx_desc *desc)
{
/* We only need to clear the low bits */
if (port->priv->hw_version != MVPP21)
desc->pp22.ptp_descriptor &=
cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
}
static bool mvpp2_tx_hw_tstamp(struct mvpp2_port *port,
struct mvpp2_tx_desc *tx_desc,
struct sk_buff *skb)
{
struct mvpp2_hwtstamp_queue *queue;
unsigned int mtype, type, i;
struct ptp_header *hdr;
u64 ptpdesc;
if (port->priv->hw_version == MVPP21 ||
port->tx_hwtstamp_type == HWTSTAMP_TX_OFF)
return false;
type = ptp_classify_raw(skb);
if (!type)
return false;
hdr = ptp_parse_header(skb, type);
if (!hdr)
return false;
ptpdesc = MVPP22_PTP_MACTIMESTAMPINGEN |
MVPP22_PTP_ACTION_CAPTURE;
queue = &port->tx_hwtstamp_queue[0];
switch (type & PTP_CLASS_VMASK) {
case PTP_CLASS_V1:
ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV1);
break;
case PTP_CLASS_V2:
ptpdesc |= MVPP22_PTP_PACKETFORMAT(MVPP22_PTP_PKT_FMT_PTPV2);
mtype = hdr->tsmt & 15;
/* Direct PTP Sync messages to queue 1 */
if (mtype == 0) {
ptpdesc |= MVPP22_PTP_TIMESTAMPQUEUESELECT;
queue = &port->tx_hwtstamp_queue[1];
}
break;
}
/* Take a reference on the skb and insert into our queue */
i = queue->next;
queue->next = (i + 1) & 31;
if (queue->skb[i])
dev_kfree_skb_any(queue->skb[i]);
queue->skb[i] = skb_get(skb);
ptpdesc |= MVPP22_PTP_TIMESTAMPENTRYID(i);
/*
* 3:0 - PTPAction
* 6:4 - PTPPacketFormat
* 7 - PTP_CF_WraparoundCheckEn
* 9:8 - IngressTimestampSeconds[1:0]
* 10 - Reserved
* 11 - MACTimestampingEn
* 17:12 - PTP_TimestampQueueEntryID[5:0]
* 18 - PTPTimestampQueueSelect
* 19 - UDPChecksumUpdateEn
* 27:20 - TimestampOffset
* PTP, NTPTransmit, OWAMP/TWAMP - L3 to PTP header
* NTPTs, Y.1731 - L3 to timestamp entry
* 35:28 - UDP Checksum Offset
*
* stored in tx descriptor bits 75:64 (11:0) and 191:168 (35:12)
*/
tx_desc->pp22.ptp_descriptor &=
cpu_to_le32(~MVPP22_PTP_DESC_MASK_LOW);
tx_desc->pp22.ptp_descriptor |=
cpu_to_le32(ptpdesc & MVPP22_PTP_DESC_MASK_LOW);
tx_desc->pp22.buf_dma_addr_ptp &= cpu_to_le64(~0xffffff0000000000ULL);
tx_desc->pp22.buf_dma_addr_ptp |= cpu_to_le64((ptpdesc >> 12) << 40);
return true;
}
/* Handle tx fragmentation processing */
static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb,
struct mvpp2_tx_queue *aggr_txq,
......@@ -3626,6 +3775,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb,
void *addr = skb_frag_address(frag);
tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
mvpp2_txdesc_clear_ptp(port, tx_desc);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, skb_frag_size(frag));
......@@ -3675,6 +3825,7 @@ static inline void mvpp2_tso_put_hdr(struct sk_buff *skb,
struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
dma_addr_t addr;
mvpp2_txdesc_clear_ptp(port, tx_desc);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, hdr_sz);
......@@ -3699,6 +3850,7 @@ static inline int mvpp2_tso_put_data(struct sk_buff *skb,
struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
dma_addr_t buf_dma_addr;
mvpp2_txdesc_clear_ptp(port, tx_desc);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, sz);
......@@ -3815,6 +3967,9 @@ static netdev_tx_t mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
/* Get a descriptor for the first part of the packet */
tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
!mvpp2_tx_hw_tstamp(port, tx_desc, skb))
mvpp2_txdesc_clear_ptp(port, tx_desc);
mvpp2_txdesc_txq_set(port, tx_desc, txq->id);
mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb));
......@@ -4574,6 +4729,7 @@ static int mvpp2_set_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
{
struct hwtstamp_config config;
void __iomem *ptp;
u32 gcr, int_mask;
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
......@@ -4581,30 +4737,51 @@ static int mvpp2_set_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
if (config.flags)
return -EINVAL;
if (config.tx_type != HWTSTAMP_TX_OFF)
if (config.tx_type != HWTSTAMP_TX_OFF &&
config.tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
ptp = port->priv->iface_base + MVPP22_PTP_BASE(port->gop_id);
int_mask = gcr = 0;
if (config.tx_type != HWTSTAMP_TX_OFF) {
gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_TX_RESET;
int_mask |= MVPP22_PTP_INT_MASK_QUEUE1 |
MVPP22_PTP_INT_MASK_QUEUE0;
}
/* It seems we must also release the TX reset when enabling the TSU */
if (config.rx_filter != HWTSTAMP_FILTER_NONE)
gcr |= MVPP22_PTP_GCR_TSU_ENABLE | MVPP22_PTP_GCR_RX_RESET |
MVPP22_PTP_GCR_TX_RESET;
if (gcr & MVPP22_PTP_GCR_TSU_ENABLE)
mvpp22_tai_start(port->priv->tai);
if (config.rx_filter != HWTSTAMP_FILTER_NONE) {
config.rx_filter = HWTSTAMP_FILTER_ALL;
mvpp22_tai_start(port->priv->tai);
mvpp2_modify(ptp + MVPP22_PTP_GCR,
MVPP22_PTP_GCR_RX_RESET |
MVPP22_PTP_GCR_TX_RESET |
MVPP22_PTP_GCR_TSU_ENABLE,
MVPP22_PTP_GCR_RX_RESET |
MVPP22_PTP_GCR_TX_RESET |
MVPP22_PTP_GCR_TSU_ENABLE);
MVPP22_PTP_GCR_TSU_ENABLE, gcr);
port->rx_hwtstamp = true;
} else {
port->rx_hwtstamp = false;
mvpp2_modify(ptp + MVPP22_PTP_GCR,
MVPP22_PTP_GCR_RX_RESET |
MVPP22_PTP_GCR_TX_RESET |
MVPP22_PTP_GCR_TSU_ENABLE, 0);
mvpp22_tai_stop(port->priv->tai);
MVPP22_PTP_GCR_TSU_ENABLE, gcr);
}
mvpp2_modify(ptp + MVPP22_PTP_INT_MASK,
MVPP22_PTP_INT_MASK_QUEUE1 |
MVPP22_PTP_INT_MASK_QUEUE0, int_mask);
if (!(gcr & MVPP22_PTP_GCR_TSU_ENABLE))
mvpp22_tai_stop(port->priv->tai);
port->tx_hwtstamp_type = config.tx_type;
if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
return -EFAULT;
......@@ -4617,7 +4794,7 @@ static int mvpp2_get_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
memset(&config, 0, sizeof(config));
config.tx_type = HWTSTAMP_TX_OFF;
config.tx_type = port->tx_hwtstamp_type;
config.rx_filter = port->rx_hwtstamp ?
HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
......@@ -4639,9 +4816,11 @@ static int mvpp2_ethtool_get_ts_info(struct net_device *dev,
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF);
info->tx_types = BIT(HWTSTAMP_TX_OFF) |
BIT(HWTSTAMP_TX_ON);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_ALL);
......
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