--- /dev/null
+/*
+ * Xilinx TSN PTP header
+ *
+ * Copyright (C) 2017 Xilinx, Inc.
+ *
+ * Author: Syed S <syeds@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _TSN_PTP_H_
+#define _TSN_PTP_H_
+
+#define PTP_HW_TSTAMP_SIZE 8 /* 64 bit timestamp */
+#define PTP_RX_HWBUF_SIZE 256
+#define PTP_RX_FRAME_SIZE 252
+#define PTP_HW_TSTAMP_OFFSET (PTP_RX_HWBUF_SIZE - PTP_HW_TSTAMP_SIZE)
+
+#define PTP_TYPE_SYNC 0x0
+#define PTP_TYPE_FOLLOW_UP 0x8
+#define PTP_TYPE_PDELAYREQ 0x2
+#define PTP_TYPE_PDELAYRESP 0x3
+#define PTP_TYPE_PDELAYRESP_FOLLOW_UP 0xA
+#define PTP_TYPE_ANNOUNCE 0xB
+#define PTP_TYPE_SIGNALING 0xC
+
+#define PTP_TX_CONTROL_OFFSET 0x00012000 /**< Tx PTP Control Reg */
+#define PTP_RX_CONTROL_OFFSET 0x00012004 /**< Rx PTP Control Reg */
+#define RX_FILTER_CONTROL 0x00012008 /**< Rx Filter Ctrl Reg */
+
+#define PTP_RX_BASE_OFFSET 0x00010000
+#define PTP_RX_CONTROL_OFFSET 0x00012004 /**< Rx PTP Control Reg */
+#define PTP_RX_PACKET_FIELD_MASK 0x00000F00
+#define PTP_RX_PACKET_CLEAR 0x00000001
+
+#define PTP_TX_BUFFER_OFFSET(index) (0x00011000 + (index) * 0x100)
+
+#define PTP_TX_SYNC_OFFSET 0x00011000
+#define PTP_TX_FOLLOW_UP_OFFSET 0x00011100
+#define PTP_TX_PDELAYREQ_OFFSET 0x00011200
+#define PTP_TX_PDELAYRESP_OFFSET 0x00011300
+#define PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET 0x00011400
+#define PTP_TX_ANNOUNCE_OFFSET 0x00011500
+#define PTP_TX_SIGNALING_OFFSET 0x00011600
+#define PTP_TX_GENERIC_OFFSET 0x00011700
+#define PTP_TX_SEND_SYNC_FRAME_MASK 0x00000001
+#define PTP_TX_SEND_FOLLOWUP_FRAME_MASK 0x00000002
+#define PTP_TX_SEND_PDELAYREQ_FRAME_MASK 0x00000004
+#define PTP_TX_SEND_PDELAYRESP_FRAME_MASK 0x00000008
+#define PTP_TX_SEND_PDELAYRESPFOLLOWUP_FRAME_MASK 0x00000010
+#define PTP_TX_SEND_ANNOUNCE_FRAME_MASK 0x00000020
+#define PTP_TX_SEND_FRAME6_BIT_MASK 0x00000040
+#define PTP_TX_SEND_FRAME7_BIT_MASK 0x00000080
+#define PTP_TX_FRAME_WAITING_MASK 0x0000ff00
+#define PTP_TX_FRAME_WAITING_SHIFT 8
+#define PTP_TX_WAIT_SYNC_FRAME_MASK 0x00000100
+#define PTP_TX_WAIT_FOLLOWUP_FRAME_MASK 0x00000200
+#define PTP_TX_WAIT_PDELAYREQ_FRAME_MASK 0x00000400
+#define PTP_TX_WAIT_PDELAYRESP_FRAME_MASK 0x00000800
+#define PTP_TX_WAIT_PDELAYRESPFOLLOWUP_FRAME_MASK 0x00001000
+#define PTP_TX_WAIT_ANNOUNCE_FRAME_MASK 0x00002000
+#define PTP_TX_WAIT_FRAME6_BIT_MASK 0x00004000
+#define PTP_TX_WAIT_FRAME7_BIT_MASK 0x00008000
+#define PTP_TX_WAIT_ALL_FRAMES_MASK 0x0000FF00
+#define PTP_TX_PACKET_FIELD_MASK 0x00070000
+#define PTP_TX_PACKET_FIELD_SHIFT 16
+
+int axienet_ptp_xmit(struct sk_buff *skb, struct net_device *ndev);
+irqreturn_t axienet_ptp_rx_irq(int irq, void *_ndev);
+irqreturn_t axienet_ptp_tx_irq(int irq, void *_ndev);
+
+#endif
--- /dev/null
+/*
+ * Xilinx FPGA Xilinx TSN PTP protocol clock Controller module.
+ *
+ * Copyright (c) 2017 Xilinx Pvt., Ltd
+ *
+ * Author: Syed S <syeds@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include "xilinx_tsn_timer.h"
+
+struct xlnx_ptp_timer {
+ struct device *dev;
+ void __iomem *baseaddr;
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_clock_info;
+ spinlock_t reg_lock; /* ptp timer lock */
+ int irq;
+ int pps_enable;
+ int countpulse;
+};
+
+static void xlnx_tod_read(struct xlnx_ptp_timer *timer, struct timespec64 *ts)
+{
+ u32 sec, nsec;
+
+ nsec = in_be32(timer->baseaddr + XTIMER1588_CURRENT_RTC_NS);
+ sec = in_be32(timer->baseaddr + XTIMER1588_CURRENT_RTC_SEC_L);
+
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+}
+
+static void xlnx_rtc_offset_write(struct xlnx_ptp_timer *timer,
+ const struct timespec64 *ts)
+{
+ pr_debug("%s: sec: %ld nsec: %ld\n", __func__, ts->tv_sec, ts->tv_nsec);
+
+ out_be32((timer->baseaddr + XTIMER1588_RTC_OFFSET_SEC_H), 0);
+ out_be32((timer->baseaddr + XTIMER1588_RTC_OFFSET_SEC_L),
+ (ts->tv_sec));
+ out_be32((timer->baseaddr + XTIMER1588_RTC_OFFSET_NS), ts->tv_nsec);
+}
+
+static void xlnx_rtc_offset_read(struct xlnx_ptp_timer *timer,
+ struct timespec64 *ts)
+{
+ ts->tv_sec = in_be32(timer->baseaddr + XTIMER1588_RTC_OFFSET_SEC_L);
+ ts->tv_nsec = in_be32(timer->baseaddr + XTIMER1588_RTC_OFFSET_NS);
+}
+
+/* PTP clock operations
+ */
+static int xlnx_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ struct xlnx_ptp_timer *timer = container_of(ptp, struct xlnx_ptp_timer,
+ ptp_clock_info);
+
+ int neg_adj = 0;
+ u64 freq;
+ u32 diff, incval;
+
+ /* This number should be replaced by a call to get the frequency
+ * from the device-tree. Currently assumes 125MHz
+ */
+ incval = 0x800000;
+ /* for 156.25 MHZ Ref clk the value is incval = 0x800000; */
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+
+ freq = incval;
+ freq *= ppb;
+ diff = div_u64(freq, 1000000000ULL);
+
+ pr_debug("%s: adj: %d ppb: %d\n", __func__, diff, ppb);
+
+ incval = neg_adj ? (incval - diff) : (incval + diff);
+ out_be32((timer->baseaddr + XTIMER1588_RTC_INCREMENT), incval);
+ return 0;
+}
+
+static int xlnx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ unsigned long flags;
+ struct xlnx_ptp_timer *timer = container_of(ptp, struct xlnx_ptp_timer,
+ ptp_clock_info);
+ struct timespec64 now, then = ns_to_timespec64(delta);
+
+ spin_lock_irqsave(&timer->reg_lock, flags);
+
+ xlnx_rtc_offset_read(timer, &now);
+
+ now = timespec64_add(now, then);
+
+ xlnx_rtc_offset_write(timer, (const struct timespec64 *)&now);
+ spin_unlock_irqrestore(&timer->reg_lock, flags);
+
+ return 0;
+}
+
+static int xlnx_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+ unsigned long flags;
+ struct xlnx_ptp_timer *timer = container_of(ptp, struct xlnx_ptp_timer,
+ ptp_clock_info);
+ spin_lock_irqsave(&timer->reg_lock, flags);
+
+ xlnx_tod_read(timer, ts);
+
+ spin_unlock_irqrestore(&timer->reg_lock, flags);
+ return 0;
+}
+
+/**
+ * xlnx_ptp_settime - Set the current time on the hardware clock
+ * @ptp: ptp clock structure
+ * @ts: timespec64 containing the new time for the cycle counter
+ *
+ * Return: 0 in all cases.
+ *
+ * The seconds register is written first, then the nanosecond
+ * The hardware loads the entire new value when a nanosecond register
+ * is written
+ **/
+static int xlnx_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct xlnx_ptp_timer *timer = container_of(ptp, struct xlnx_ptp_timer,
+ ptp_clock_info);
+ struct timespec64 delta, tod;
+ struct timespec64 offset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&timer->reg_lock, flags);
+
+ /* First zero the offset */
+ offset.tv_sec = 0;
+ offset.tv_nsec = 0;
+ xlnx_rtc_offset_write(timer, &offset);
+
+ /* Get the current timer value */
+ xlnx_tod_read(timer, &tod);
+
+ /* Subtract the current reported time from our desired time */
+ delta = timespec64_sub(*ts, tod);
+
+ /* Don't write a negative offset */
+ if (delta.tv_sec <= 0) {
+ delta.tv_sec = 0;
+ if (delta.tv_nsec < 0)
+ delta.tv_nsec = 0;
+ }
+
+ xlnx_rtc_offset_write(timer, &delta);
+ spin_unlock_irqrestore(&timer->reg_lock, flags);
+ return 0;
+}
+
+static int xlnx_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct xlnx_ptp_timer *timer = container_of(ptp, struct xlnx_ptp_timer,
+ ptp_clock_info);
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_PPS:
+ timer->pps_enable = 1;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info xlnx_ptp_clock_info = {
+ .owner = THIS_MODULE,
+ .name = "Xilinx Timer",
+ .max_adj = 999999999,
+ .n_ext_ts = 0,
+ .pps = 1,
+ .adjfreq = xlnx_ptp_adjfreq,
+ .adjtime = xlnx_ptp_adjtime,
+ .gettime64 = xlnx_ptp_gettime,
+ .settime64 = xlnx_ptp_settime,
+ .enable = xlnx_ptp_enable,
+};
+
+/* module operations */
+
+/**
+ * xlnx_ptp_timer_isr - Interrupt Service Routine
+ * @irq: IRQ number
+ * @priv: pointer to the timer structure
+ *
+ * Returns: IRQ_HANDLED for all cases
+ *
+ * Handles the timer interrupt. The timer interrupt fires 128 times per
+ * secound. When our count reaches 128 emit a PTP_CLOCK_PPS event
+ */
+static irqreturn_t xlnx_ptp_timer_isr(int irq, void *priv)
+{
+ struct xlnx_ptp_timer *timer = (struct xlnx_ptp_timer *)priv;
+ struct ptp_clock_event event;
+
+ event.type = PTP_CLOCK_PPS;
+ ++timer->countpulse;
+ if (timer->countpulse >= PULSESIN1PPS) {
+ timer->countpulse = 0;
+ if ((timer->ptp_clock) && (timer->pps_enable))
+ ptp_clock_event(timer->ptp_clock, &event);
+ }
+ out_be32((timer->baseaddr + XTIMER1588_INTERRUPT),
+ (1 << XTIMER1588_INT_SHIFT));
+
+ return IRQ_HANDLED;
+}
+
+int axienet_ptp_timer_remove(void *priv)
+{
+ struct xlnx_ptp_timer *timer = (struct xlnx_ptp_timer *)priv;
+
+ free_irq(timer->irq, (void *)timer);
+
+ axienet_phc_index = -1;
+ if (timer->ptp_clock) {
+ ptp_clock_unregister(timer->ptp_clock);
+ timer->ptp_clock = NULL;
+ }
+ kfree(timer);
+ return 0;
+}
+
+int axienet_get_phc_index(void *priv)
+{
+ struct xlnx_ptp_timer *timer = (struct xlnx_ptp_timer *)priv;
+
+ if (timer->ptp_clock)
+ return ptp_clock_index(timer->ptp_clock);
+ else
+ return -1;
+}
+
+void *axienet_ptp_timer_probe(void __iomem *base, struct platform_device *pdev)
+{
+ struct xlnx_ptp_timer *timer;
+ struct timespec64 ts;
+ int err = 0;
+
+ timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+ if (!timer)
+ return NULL;
+
+ timer->baseaddr = base;
+
+ timer->irq = platform_get_irq_byname(pdev, "rtc_irq");
+
+ spin_lock_init(&timer->reg_lock);
+
+ timer->ptp_clock_info = xlnx_ptp_clock_info;
+
+ timer->ptp_clock = ptp_clock_register(&timer->ptp_clock_info,
+ &pdev->dev);
+
+ if (IS_ERR(timer->ptp_clock)) {
+ err = PTR_ERR(timer->ptp_clock);
+ pr_debug("Failed to register ptp clock\n");
+ goto out;
+ }
+
+ axienet_phc_index = ptp_clock_index(timer->ptp_clock);
+
+ ts = ktime_to_timespec64(ktime_get_real());
+
+ xlnx_ptp_settime(&timer->ptp_clock_info, &ts);
+
+ /* Enable interrupts */
+ err = request_irq(timer->irq,
+ xlnx_ptp_timer_isr,
+ 0,
+ "ptp_rtc",
+ (void *)timer);
+ if (err)
+ goto err_irq;
+
+ return timer;
+
+err_irq:
+ ptp_clock_unregister(timer->ptp_clock);
+out:
+ timer->ptp_clock = NULL;
+ return NULL;
+}
--- /dev/null
+/*
+ * Xilinx FPGA Xilinx TSN PTP transfer protocol module.
+ *
+ * Copyright (c) 2017 Xilinx Pvt., Ltd
+ *
+ * Author: Syed S <syeds@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "xilinx_axienet.h"
+#include "xilinx_tsn_ptp.h"
+#include "xilinx_tsn_timer.h"
+#include <linux/ptp_classify.h>
+
+#define PTP_ONE_SECOND 1000000000 /**< Value in ns */
+
+#define msg_type_string(type) \
+ ((type) == PTP_TYPE_SYNC) ? "SYNC" : \
+ ((type) == PTP_TYPE_FOLLOW_UP) ? "FOLLOW_UP" : \
+ ((type) == PTP_TYPE_PDELAYREQ) ? "PDELAY_REQ" : \
+ ((type) == PTP_TYPE_PDELAYRESP) ? "PDELAY_RESP" : \
+ ((type) == PTP_TYPE_PDELAYRESP_FOLLOW_UP) ? "PDELAY_RESP_FOLLOW_UP" : \
+ ((type) == PTP_TYPE_ANNOUNCE) ? "ANNOUNCE" : \
+ "UNKNOWN"
+
+/**
+ * memcpy_fromio_32 - copy ptp buffer from HW
+ * @lp: Pointer to axienet local structure
+ * @offset: Offset in the PTP buffer
+ * @data: Destination buffer
+ * @len: Len to copy
+ *
+ * This functions copies the data from PTP buffer to destination data buffer
+ */
+static void memcpy_fromio_32(struct axienet_local *lp,
+ unsigned long offset, u8 *data, size_t len)
+{
+ while (len >= 4) {
+ *(u32 *)data = axienet_ior(lp, offset);
+ len -= 4;
+ offset += 4;
+ data += 4;
+ }
+
+ if (len > 0) {
+ u32 leftover = axienet_ior(lp, offset);
+ u8 *src = (u8 *)&leftover;
+
+ while (len) {
+ *data++ = *src++;
+ len--;
+ }
+ }
+}
+
+/**
+ * memcpy_toio_32 - copy ptp buffer from HW
+ * @lp: Pointer to axienet local structure
+ * @offset: Offset in the PTP buffer
+ * @data: Source data
+ * @len: Len to copy
+ *
+ * This functions copies the source data to desination ptp buffer
+ */
+static void memcpy_toio_32(struct axienet_local *lp,
+ unsigned long offset, u8 *data, size_t len)
+{
+ while (len >= 4) {
+ axienet_iow(lp, offset, *(u32 *)data);
+ len -= 4;
+ offset += 4;
+ data += 4;
+ }
+
+ if (len > 0) {
+ u32 leftover = 0;
+ u8 *dest = (u8 *)&leftover;
+
+ while (len) {
+ *dest++ = *data++;
+ len--;
+ }
+ axienet_iow(lp, offset, leftover);
+ }
+}
+
+/**
+ * axienet_ptp_xmit - xmit skb using PTP HW
+ * @skb: sk_buff pointer that contains data to be Txed.
+ * @ndev: Pointer to net_device structure.
+ *
+ * Return: NETDEV_TX_OK, on success
+ * NETDEV_TX_BUSY, if any of the descriptors are not free
+ *
+ * This function is called to transmit a PTP skb. The function uses
+ * the free PTP TX buffer entry and sends the frame
+ */
+int axienet_ptp_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ u8 msg_type;
+ struct axienet_local *lp = netdev_priv(ndev);
+ unsigned long flags;
+ u8 tx_frame_waiting;
+ u8 free_index;
+
+ msg_type = *(u8 *)(skb->data + ETH_HLEN);
+
+ pr_debug(" -->XMIT: protocol: %x message: %s frame_len: %d\n",
+ skb->protocol,
+ msg_type_string(msg_type & 0xf), skb->len);
+
+ tx_frame_waiting = (axienet_ior(lp, PTP_TX_CONTROL_OFFSET) &
+ PTP_TX_FRAME_WAITING_MASK) >>
+ PTP_TX_FRAME_WAITING_SHIFT;
+
+ /* we reached last frame */
+ if (tx_frame_waiting & (1 << 7)) {
+ if (!netif_queue_stopped(ndev))
+ netif_stop_queue(ndev);
+ pr_debug("tx_frame_waiting: %d\n", tx_frame_waiting);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* go to next available slot */
+ free_index = fls(tx_frame_waiting);
+
+ /* write the len */
+ axienet_iow(lp, PTP_TX_BUFFER_OFFSET(free_index), skb->len);
+ memcpy_toio_32(lp, (PTP_TX_BUFFER_OFFSET(free_index) + 8),
+ skb->data, skb->len);
+
+ /* send the frame */
+ axienet_iow(lp, PTP_TX_CONTROL_OFFSET, (1 << free_index));
+
+ spin_lock_irqsave(&lp->ptp_tx_lock, flags);
+ skb->cb[0] = free_index;
+ skb_queue_tail(&lp->ptp_txq, skb_get(skb));
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ skb_tx_timestamp(skb);
+ spin_unlock_irqrestore(&lp->ptp_tx_lock, flags);
+
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * axienet_set_timestamp - timestamp skb with HW timestamp
+ * @lp: Pointer to axienet local structure
+ * @hwtstamps: Pointer to skb timestamp structure
+ * @offset: offset of the timestamp in the PTP buffer
+ *
+ * Return: None.
+ *
+ */
+static void axienet_set_timestamp(struct axienet_local *lp,
+ struct skb_shared_hwtstamps *hwtstamps,
+ unsigned int offset)
+{
+ u32 captured_ns;
+ u32 captured_sec;
+
+ captured_ns = axienet_ior(lp, offset + 4);
+ captured_sec = axienet_ior(lp, offset);
+
+ /* Upper 32 bits contain s, lower 32 bits contain ns. */
+ hwtstamps->hwtstamp = ktime_set(captured_sec,
+ captured_ns);
+}
+
+/**
+ * axienet_ptp_recv - receive ptp buffer in skb from HW
+ * @ndev: Pointer to net_device structure.
+ *
+ * This function is called from the ptp rx isr. It allocates skb, and
+ * copies the ptp rx buffer data to it and calls netif_rx for further
+ * processing.
+ *
+ */
+static void axienet_ptp_recv(struct net_device *ndev)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ unsigned long ptp_frame_base_addr = 0;
+ struct sk_buff *skb;
+ u16 msg_len;
+ u8 msg_type;
+
+ pr_debug("%s:\n ", __func__);
+
+ while (((lp->ptp_rx_hw_pointer & 0xf) !=
+ (lp->ptp_rx_sw_pointer & 0xf))) {
+ skb = netdev_alloc_skb(ndev, PTP_RX_FRAME_SIZE);
+
+ lp->ptp_rx_sw_pointer += 1;
+
+ ptp_frame_base_addr = PTP_RX_BASE_OFFSET +
+ ((lp->ptp_rx_sw_pointer & 0xf) *
+ PTP_RX_HWBUF_SIZE);
+
+ memset(skb->data, 0x0, PTP_RX_FRAME_SIZE);
+
+ memcpy_fromio_32(lp, ptp_frame_base_addr, skb->data,
+ PTP_RX_FRAME_SIZE);
+
+ msg_type = *(u8 *)(skb->data + ETH_HLEN) & 0xf;
+ msg_len = *(u16 *)(skb->data + ETH_HLEN + 2);
+
+ skb_put(skb, ntohs(msg_len) + ETH_HLEN);
+
+ skb->protocol = eth_type_trans(skb, ndev);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ pr_debug(" -->RECV: protocol: %x message: %s frame_len: %d\n",
+ skb->protocol, msg_type_string(msg_type & 0xf),
+ skb->len);
+ axienet_set_timestamp(lp, skb_hwtstamps(skb),
+ (ptp_frame_base_addr
+ + PTP_HW_TSTAMP_OFFSET));
+
+ netif_rx(skb);
+ }
+}
+
+/**
+ * axienet_ptp_rx_irq - PTP RX ISR handler
+ * @irq: irq number
+ * @_ndev: net_device pointer
+ *
+ * Return: IRQ_HANDLED for all cases.
+ */
+irqreturn_t axienet_ptp_rx_irq(int irq, void *_ndev)
+{
+ struct net_device *ndev = _ndev;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ pr_debug("%s: received\n ", __func__);
+ lp->ptp_rx_hw_pointer = (axienet_ior(lp, PTP_RX_CONTROL_OFFSET)
+ & PTP_RX_PACKET_FIELD_MASK) >> 8;
+
+ axienet_ptp_recv(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * axienet_tx_tstamp - timestamp skb on trasmit path
+ * @work: Pointer to work_struct structure
+ *
+ * This adds TX timestamp to skb
+ */
+void axienet_tx_tstamp(struct work_struct *work)
+{
+ struct axienet_local *lp = container_of(work, struct axienet_local,
+ tx_tstamp_work);
+ struct skb_shared_hwtstamps hwtstamps;
+ struct sk_buff *skb;
+ unsigned long ts_reg_offset;
+ unsigned long flags;
+ u8 tx_packet;
+ u8 index;
+
+ memset(&hwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+
+ spin_lock_irqsave(&lp->ptp_tx_lock, flags);
+
+ tx_packet = (axienet_ior(lp, PTP_TX_CONTROL_OFFSET) &
+ PTP_TX_PACKET_FIELD_MASK) >>
+ PTP_TX_PACKET_FIELD_SHIFT;
+
+ while ((skb = __skb_dequeue(&lp->ptp_txq)) != NULL) {
+ index = skb->cb[0];
+
+ /* dequeued packet yet to be xmited? */
+ if (index > tx_packet) {
+ /* enqueue it back and break */
+ skb_queue_tail(&lp->ptp_txq, skb);
+ break;
+ }
+ /* time stamp reg offset */
+ ts_reg_offset = PTP_TX_BUFFER_OFFSET(index) +
+ PTP_HW_TSTAMP_OFFSET;
+
+ axienet_set_timestamp(lp, &hwtstamps, ts_reg_offset);
+
+ skb_tstamp_tx(skb, &hwtstamps);
+
+ dev_kfree_skb_any(skb);
+ }
+
+ spin_unlock_irqrestore(&lp->ptp_tx_lock, flags);
+}
+
+/**
+ * axienet_ptp_tx_irq - PTP TX irq handler
+ * @irq: irq number
+ * @_ndev: net_device pointer
+ *
+ * Return: IRQ_HANDLED for all cases.
+ *
+ */
+irqreturn_t axienet_ptp_tx_irq(int irq, void *_ndev)
+{
+ struct net_device *ndev = _ndev;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ pr_debug("%s: got tx interrupt\n", __func__);
+
+ /* read ctrl register to clear the interrupt */
+ axienet_ior(lp, PTP_TX_CONTROL_OFFSET);
+
+ schedule_work(&lp->tx_tstamp_work);
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
--- /dev/null
+/*
+ * Xilinx FPGA Xilinx TSN timer module header.
+ *
+ * Copyright (c) 2017 Xilinx Pvt., Ltd
+ *
+ * Author: Syed S <syeds@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _XILINX_TSN_H_
+#define _XILINX_TSN_H_
+
+#include <linux/platform_device.h>
+
+#define XAE_RTC_OFFSET 0x12800
+/* RTC Nanoseconds Field Offset Register */
+#define XTIMER1588_RTC_OFFSET_NS 0x00000
+/* RTC Seconds Field Offset Register - Low */
+#define XTIMER1588_RTC_OFFSET_SEC_L 0x00008
+/* RTC Seconds Field Offset Register - High */
+#define XTIMER1588_RTC_OFFSET_SEC_H 0x0000C
+/* RTC Increment */
+#define XTIMER1588_RTC_INCREMENT 0x00010
+/* Current TOD Nanoseconds - RO */
+#define XTIMER1588_CURRENT_RTC_NS 0x00014
+/* Current TOD Seconds -Low RO */
+#define XTIMER1588_CURRENT_RTC_SEC_L 0x00018
+/* Current TOD Seconds -High RO */
+#define XTIMER1588_CURRENT_RTC_SEC_H 0x0001C
+#define XTIMER1588_SYNTONIZED_NS 0x0002C
+#define XTIMER1588_SYNTONIZED_SEC_L 0x00030
+#define XTIMER1588_SYNTONIZED_SEC_H 0x00034
+/* Write to Bit 0 to clear the interrupt */
+#define XTIMER1588_INTERRUPT 0x00020
+/* 8kHz Pulse Offset Register */
+#define XTIMER1588_8KPULSE 0x00024
+/* Correction Field - Low */
+#define XTIMER1588_CF_L 0x0002C
+/* Correction Field - Low */
+#define XTIMER1588_CF_H 0x00030
+
+#define XTIMER1588_RTC_MASK ((1 << 26) - 1)
+#define XTIMER1588_INT_SHIFT 0
+#define NANOSECOND_BITS 20
+#define NANOSECOND_MASK ((1 << NANOSECOND_BITS) - 1)
+#define SECOND_MASK ((1 << (32 - NANOSECOND_BITS)) - 1)
+#define XTIMER1588_RTC_INCREMENT_SHIFT 20
+#define PULSESIN1PPS 128
+
+/* Read/Write access to the registers */
+#ifndef out_be32
+#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_ARCH_ZYNQMP)
+#define in_be32(offset) __raw_readl(offset)
+#define out_be32(offset, val) __raw_writel(val, offset)
+#endif
+#endif
+
+/* The tsn ptp module will set this variable */
+extern int axienet_phc_index;
+
+void *axienet_ptp_timer_probe(void __iomem *base,
+ struct platform_device *pdev);
+int axienet_ptp_timer_remove(void *priv);
+int axienet_get_phc_index(void *priv);
+#endif