[PATCH 05/13] tile: support PTP using the tilegx mPIPE (IEEE 1588)

From: Chris Metcalf
Date: Tue Jul 23 2013 - 16:21:50 EST


Signed-off-by: Chris Metcalf <cmetcalf@xxxxxxxxxx>
---
arch/tile/gxio/iorpc_mpipe.c | 19 ++++
arch/tile/include/gxio/iorpc_mpipe.h | 4 +
arch/tile/include/gxio/mpipe.h | 13 +++
drivers/net/ethernet/tile/Makefile | 1 +
drivers/net/ethernet/tile/tilegx.c | 56 +++++++++
drivers/net/ethernet/tile/tilegx_ptp.c | 202 +++++++++++++++++++++++++++++++++
drivers/ptp/Kconfig | 10 ++
7 files changed, 305 insertions(+)
create mode 100644 drivers/net/ethernet/tile/tilegx_ptp.c

diff --git a/arch/tile/gxio/iorpc_mpipe.c b/arch/tile/gxio/iorpc_mpipe.c
index 31b87bf..ad48e71 100644
--- a/arch/tile/gxio/iorpc_mpipe.c
+++ b/arch/tile/gxio/iorpc_mpipe.c
@@ -454,6 +454,25 @@ int gxio_mpipe_adjust_timestamp_aux(gxio_mpipe_context_t * context,

EXPORT_SYMBOL(gxio_mpipe_adjust_timestamp_aux);

+struct adjust_timestamp_freq_param {
+ int32_t ppb;
+};
+
+int gxio_mpipe_adjust_timestamp_freq(gxio_mpipe_context_t * context,
+ int32_t ppb)
+{
+ struct adjust_timestamp_freq_param temp;
+ struct adjust_timestamp_freq_param *params = &temp;
+
+ params->ppb = ppb;
+
+ return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params,
+ sizeof(*params),
+ GXIO_MPIPE_OP_ADJUST_TIMESTAMP_FREQ);
+}
+
+EXPORT_SYMBOL(gxio_mpipe_adjust_timestamp_freq);
+
struct arm_pollfd_param {
union iorpc_pollfd pollfd;
};
diff --git a/arch/tile/include/gxio/iorpc_mpipe.h b/arch/tile/include/gxio/iorpc_mpipe.h
index 9d50fce..6961ec2 100644
--- a/arch/tile/include/gxio/iorpc_mpipe.h
+++ b/arch/tile/include/gxio/iorpc_mpipe.h
@@ -48,6 +48,7 @@
#define GXIO_MPIPE_OP_GET_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x121e)
#define GXIO_MPIPE_OP_SET_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x121f)
#define GXIO_MPIPE_OP_ADJUST_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1220)
+#define GXIO_MPIPE_OP_ADJUST_TIMESTAMP_FREQ IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1222)
#define GXIO_MPIPE_OP_ARM_POLLFD IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9000)
#define GXIO_MPIPE_OP_CLOSE_POLLFD IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9001)
#define GXIO_MPIPE_OP_GET_MMIO_BASE IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000)
@@ -124,6 +125,9 @@ int gxio_mpipe_set_timestamp_aux(gxio_mpipe_context_t * context, uint64_t sec,
int gxio_mpipe_adjust_timestamp_aux(gxio_mpipe_context_t * context,
int64_t nsec);

+int gxio_mpipe_adjust_timestamp_freq(gxio_mpipe_context_t * context,
+ int32_t ppb);
+
int gxio_mpipe_arm_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie);

int gxio_mpipe_close_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie);
diff --git a/arch/tile/include/gxio/mpipe.h b/arch/tile/include/gxio/mpipe.h
index b74f470..57f5ca2 100644
--- a/arch/tile/include/gxio/mpipe.h
+++ b/arch/tile/include/gxio/mpipe.h
@@ -1733,4 +1733,17 @@ extern int gxio_mpipe_set_timestamp(gxio_mpipe_context_t *context,
extern int gxio_mpipe_adjust_timestamp(gxio_mpipe_context_t *context,
int64_t delta);

+/* Adjust the mPIPE timestamp clock frequency.
+ *
+ * @param context An initialized mPIPE context.
+ * @param ppb A 32-bits signed PPB(Parts Per Billion) value to adjust.
+ * The absolute value of ppb must be less than or equal to 1000000000,
+ * and should be larger then 30000, otherwise just ignored because of
+ * the clock precision restriction.
+ * @return If the call was successful, zero; otherwise, a negative error
+ * code.
+ */
+extern int gxio_mpipe_adjust_timestamp_freq(gxio_mpipe_context_t *context,
+ int32_t ppb);
+
#endif /* !_GXIO_MPIPE_H_ */
diff --git a/drivers/net/ethernet/tile/Makefile b/drivers/net/ethernet/tile/Makefile
index 0ef9eef..e2df77e 100644
--- a/drivers/net/ethernet/tile/Makefile
+++ b/drivers/net/ethernet/tile/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_TILE_NET) += tile_net.o
ifdef CONFIG_TILEGX
tile_net-y := tilegx.o
+obj-$(CONFIG_PTP_1588_CLOCK_TILEGX) += tilegx_ptp.o
else
tile_net-y := tilepro.o
endif
diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c
index f3c2d03..3d4406c 100644
--- a/drivers/net/ethernet/tile/tilegx.c
+++ b/drivers/net/ethernet/tile/tilegx.c
@@ -389,6 +389,39 @@ oops:
pr_notice("Tile %d still needs some buffers\n", info->my_cpu);
}

+/* Get RX timestamp, and store it in the skb. */
+static void tile_rx_timestamp(struct sk_buff *skb, gxio_mpipe_idesc_t *idesc)
+{
+#ifdef CONFIG_PTP_1588_CLOCK_TILEGX
+ struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
+
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ shhwtstamps->hwtstamp = ktime_set(idesc->time_stamp_sec,
+ idesc->time_stamp_ns);
+#endif
+}
+
+/* Get TX timestamp, and store it in the skb. */
+static void tile_tx_timestamp(struct sk_buff *skb)
+{
+#ifdef CONFIG_PTP_1588_CLOCK_TILEGX
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct skb_shared_info *shtx;
+ struct timespec ts;
+
+ shtx = skb_shinfo(skb);
+ if (likely((shtx->tx_flags & SKBTX_HW_TSTAMP) == 0))
+ return;
+
+ shtx->tx_flags |= SKBTX_IN_PROGRESS;
+
+ gxio_mpipe_get_timestamp(&context, &ts);
+ memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+ shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
+ skb_tstamp_tx(skb, &shhwtstamps);
+#endif
+}
+
static inline bool filter_packet(struct net_device *dev, void *buf)
{
/* Filter packets received before we're up. */
@@ -419,6 +452,9 @@ static void tile_net_receive_skb(struct net_device *dev, struct sk_buff *skb,
if (idesc->cs && idesc->csum_seed_val == 0xFFFF)
skb->ip_summed = CHECKSUM_UNNECESSARY;

+ /* Get RX timestamp from idesc. */
+ tile_rx_timestamp(skb, idesc);
+
netif_receive_skb(skb);

/* Update stats. */
@@ -987,6 +1023,7 @@ static int tile_net_init_mpipe(struct net_device *dev)
int i, num_buffers, rc;
int cpu;
int first_ring, ring;
+ struct timespec ts;
int network_cpus_count = cpus_weight(network_cpus_map);

if (!hash_default) {
@@ -1000,6 +1037,10 @@ static int tile_net_init_mpipe(struct net_device *dev)
return -EIO;
}

+ /* Sync mPIPE's timestamp up with Linux system time. */
+ getnstimeofday(&ts);
+ gxio_mpipe_set_timestamp(&context, &ts);
+
/* Set up the buffer stacks. */
num_buffers =
network_cpus_count * (IQUEUE_ENTRIES + TILE_NET_BATCH);
@@ -1284,6 +1325,12 @@ static int tile_net_stop(struct net_device *dev)
return 0;
}

+gxio_mpipe_context_t *get_mpipe_context(int index)
+{
+ return ingress_irq < 0 ? NULL : &context;
+}
+EXPORT_SYMBOL_GPL(get_mpipe_context);
+
/* Determine the VA for a fragment. */
static inline void *tile_net_frag_buf(skb_frag_t *f)
{
@@ -1693,6 +1740,9 @@ static int tile_net_tx(struct sk_buff *skb, struct net_device *dev)
for (i = 0; i < num_edescs; i++)
gxio_mpipe_equeue_put_at(equeue, edescs[i], slot++);

+ /* Store TX timestamp if needed. */
+ tile_tx_timestamp(skb);
+
/* Add a completion record. */
add_comp(equeue, comps, slot - 1, skb);

@@ -1727,6 +1777,12 @@ static void tile_net_tx_timeout(struct net_device *dev)
/* Ioctl commands. */
static int tile_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
+#ifdef CONFIG_PTP_1588_CLOCK_TILEGX
+ if (cmd == SIOCSHWTSTAMP) {
+ /* Hardware timestamping was enabled by default. */
+ return 0;
+ }
+#endif
return -EOPNOTSUPP;
}

diff --git a/drivers/net/ethernet/tile/tilegx_ptp.c b/drivers/net/ethernet/tile/tilegx_ptp.c
new file mode 100644
index 0000000..a188463
--- /dev/null
+++ b/drivers/net/ethernet/tile/tilegx_ptp.c
@@ -0,0 +1,202 @@
+/*
+ * PTP 1588 clock using the TILE-Gx.
+ *
+ * Copyright 2013 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * This source code is derived from ptp_ixp46x.c wrote by Richard Cochran.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include <gxio/mpipe.h>
+#include <gxio/iorpc_mpipe.h>
+
+#define GBE_LINK_NR 4
+
+/* nanoseconds will be incremented each clock cycle. */
+#define GBE_TIMER_INCREMENT 8
+
+
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_DESCRIPTION("PTP clock using the TILE-Gx");
+MODULE_LICENSE("GPL");
+
+
+struct mpipe_clock {
+ struct ptp_clock *ptp_clock;
+ gxio_mpipe_context_t *context;
+ struct ptp_clock_info caps;
+ struct mutex lock;
+};
+
+static struct mpipe_clock mpipe_clock;
+
+extern gxio_mpipe_context_t *get_mpipe_context(int index);
+
+/*
+ * Check if the context of mpipe device is valid.
+ */
+static inline int mpipe_context_check(struct mpipe_clock *clock)
+{
+ if (!clock->context) {
+ clock->context = get_mpipe_context(0);
+ if (!clock->context) {
+ pr_debug("Invalid mPIPE context.\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * PTP clock operations.
+ */
+
+static int ptp_mpipe_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ int ret = 0;
+ struct mpipe_clock *clock = container_of(ptp, struct mpipe_clock, caps);
+
+ mutex_lock(&clock->lock);
+ if (mpipe_context_check(clock)) {
+ mutex_unlock(&clock->lock);
+ return -EIO;
+ }
+
+ if (gxio_mpipe_adjust_timestamp_freq(clock->context, ppb))
+ ret = -EINVAL;
+
+ mutex_unlock(&clock->lock);
+ return ret;
+}
+
+static int ptp_mpipe_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ int ret;
+ struct mpipe_clock *clock = container_of(ptp, struct mpipe_clock, caps);
+
+ mutex_lock(&clock->lock);
+
+ if (mpipe_context_check(clock)) {
+ mutex_unlock(&clock->lock);
+ return -EIO;
+ }
+
+ ret = gxio_mpipe_adjust_timestamp(clock->context, delta);
+
+ /* Convert a gxio error code to a Linux error code. */
+ if (ret < 0)
+ ret = -EBUSY;
+
+ mutex_unlock(&clock->lock);
+
+ return ret;
+}
+
+static int ptp_mpipe_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ int ret;
+ struct mpipe_clock *clock = container_of(ptp, struct mpipe_clock, caps);
+
+ mutex_lock(&clock->lock);
+
+ if (mpipe_context_check(clock)) {
+ mutex_unlock(&clock->lock);
+ return -EIO;
+ }
+
+ ret = gxio_mpipe_get_timestamp(clock->context, ts);
+
+ /* Convert a gxio error code to a Linux error code. */
+ if (ret < 0)
+ ret = -EBUSY;
+
+ mutex_unlock(&clock->lock);
+
+ return ret;
+}
+
+static int ptp_mpipe_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ int ret;
+ struct mpipe_clock *clock = container_of(ptp, struct mpipe_clock, caps);
+
+ mutex_lock(&clock->lock);
+
+ if (mpipe_context_check(clock)) {
+ mutex_unlock(&clock->lock);
+ return -EIO;
+ }
+
+ ret = gxio_mpipe_set_timestamp(clock->context, ts);
+
+ /* Convert a gxio error code to a Linux error code. */
+ if (ret < 0)
+ ret = -EBUSY;
+
+ mutex_unlock(&clock->lock);
+
+ return ret;
+}
+
+static struct ptp_clock_info ptp_mpipe_caps = {
+ .owner = THIS_MODULE,
+ .name = "mPIPE ptp timer",
+ .max_adj = 512000,
+ .n_ext_ts = 0,
+ .pps = 0,
+ .adjfreq = ptp_mpipe_adjfreq,
+ .adjtime = ptp_mpipe_adjtime,
+ .gettime = ptp_mpipe_gettime,
+ .settime = ptp_mpipe_settime,
+};
+
+
+static void __exit ptp_tilegx_exit(void)
+{
+ ptp_clock_unregister(mpipe_clock.ptp_clock);
+ mutex_destroy(&mpipe_clock.lock);
+}
+
+
+static int __init ptp_tilegx_init(void)
+{
+ int err = 0;
+
+ mpipe_clock.context = NULL;
+ mpipe_clock.caps = ptp_mpipe_caps;
+ mutex_init(&mpipe_clock.lock);
+
+ mpipe_clock.ptp_clock = ptp_clock_register(&mpipe_clock.caps, NULL);
+ if (IS_ERR(mpipe_clock.ptp_clock)) {
+ err = PTR_ERR(mpipe_clock.ptp_clock);
+ pr_debug("Register ptp clock fail: %d\n", err);
+ }
+
+ return err;
+}
+
+module_init(ptp_tilegx_init);
+module_exit(ptp_tilegx_exit);
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 5be73ba..255ed1a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -87,4 +87,14 @@ config PTP_1588_CLOCK_PCH
To compile this driver as a module, choose M here: the module
will be called ptp_pch.

+config PTP_1588_CLOCK_TILEGX
+ tristate "Tilera TILE-Gx mPIPE as PTP clock"
+ select PTP_1588_CLOCK
+ depends on TILEGX
+ help
+ This driver adds support for using the mPIPE as a PTP
+ clock. This clock is only useful if your PTP programs are
+ getting hardware time stamps on the PTP Ethernet packets
+ using the SO_TIMESTAMPING API.
+
endmenu
--
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/