Commit 87cf7879 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Alessandro Rubini

nic: major change to add timestamp support

This patch adds the timestamping engine, by adding fields to
the core struct, an array for temporary storage of tstamp information
and fixes a few bug in register management
parent c0e8bd6c
......@@ -117,6 +117,8 @@ static int __devinit wrn_probe(struct platform_device *pdev)
goto out;
wrn->regs = wrn->bases[WRN_BLOCK_NIC];
wrn->txtsu_regs = wrn->bases[WRN_BLOCK_TSTAMP];
wrn->ppsg_regs = wrn->bases[WRN_BLOCK_PPSG];
wrn->txd = ((void *)wrn->regs) + 0x80; /* was: TX1_D1 */
wrn->rxd = ((void *)wrn->regs) + 0x100; /* was: RX1_D1 */
wrn->databuf = (void *)wrn->regs + offsetof(struct NIC_WB, MEM);
......
......@@ -129,13 +129,15 @@ static void __wrn_tx_desc(struct wrn_ep *ep, int desc,
__wrn_copy_out(ptr, data, len);
/* TX register 3: mask of endpoints (FIXME: broadcast) */
writel(ep->ep_number, &tx->tx3);
//printk("EP Num: %d\n", ep->ep_number);
writel(1<<ep->ep_number, &tx->tx3);
/* TX register 2: ofset and length */
writel(offset | (len << 16), &tx->tx2);
/* TX register 1: id and masks */
writel(NIC_TX1_D1_PAD_E | NIC_TX1_D1_READY | (id << 16),
writel((len < 46 ? NIC_TX1_D1_PAD_E : 0) | NIC_TX1_D1_READY | (id << 16),
&tx->tx1);
}
......@@ -264,8 +266,13 @@ static void __wrn_rx_descriptor(struct wrn_dev *wrn, int desc)
struct wrn_endpoint *ep;
struct sk_buff *skb;
struct wrn_rxd __iomem *rx;
u32 r1, r2, r3;
u32 r1, r2, r3, offset;
int epnum, off, len;
u32 ts_r, ts_f;
struct skb_shared_hwtstamps *hwts;
u32 counter_ppsg; /* PPS generator nanosecond counter */
u32 utc;
s32 cntr_diff;
rx = wrn->rxd + desc;
r1 = readl(&rx->rx1);
......@@ -274,7 +281,28 @@ static void __wrn_rx_descriptor(struct wrn_dev *wrn, int desc)
pr_debug("%s: %i: %08x %08x %08x\n", __func__, desc, r1, r2, r3);
/* So, this descriptor is not empty. Get the port (ep) */
epnum = NIC_RX1_D1_PORT_R(r1);
offset = __wrn_desc_offset(wrn, WRN_DDIR_RX, desc);
if (r1 & NIC_RX1_D1_GOT_TS) {
/*
* check if the packet has an RX OOB block
* if not, we can't determine the orginating port
* [sorry for confusion with the flag name - it should be
* GOT_OOB. I'll fix it later -- Tom]
*/
epnum = NIC_RX1_D1_PORT_R(r1);
ts_r = NIC_RX1_D2_TS_R_R(r2);
ts_f = NIC_RX1_D2_TS_F_R(r2);
} else {
printk("No RX OOB? Something's seriously fkd....\n");
writel( (2000 << 16) | offset, &rx->rx3);
writel(NIC_RX1_D1_EMPTY, &rx->rx1); /* FIXME: count as error */
return ;
}
dev = wrn->dev[epnum];
ep = netdev_priv(dev);
......@@ -287,7 +315,35 @@ static void __wrn_rx_descriptor(struct wrn_dev *wrn, int desc)
/* FIXME: handle allocation failure */
skb_reserve(skb, 2);
__wrn_copy_in(skb_put(skb, len), wrn->databuf + off, len);
/*
* reload the descriptor length (it was modified by the NIC
* during reception of the packet)
*/
writel( (2000 << 16) | offset, &rx->rx3);
writel(NIC_RX1_D1_EMPTY, &rx->rx1);
/* RX timestamping part */
hwts = skb_hwtstamps(skb);
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
if(counter_ppsg < ts_r)
utc--;
hwts->hwtstamp.tv.sec = (s32)utc & 0x7fffffff;
cntr_diff = (ts_r & 0xf) - ts_f;
/* the bit says the rising edge cnter is 1tick ahead */
if(cntr_diff == 1 || cntr_diff == (-0xf))
hwts->hwtstamp.tv.sec |= 0x80000000;
hwts->hwtstamp.tv.nsec = ts_r * 8; /* scale to nanoseconds */
pr_debug("Timestamp: %d:%d, ahead = %d\n",
hwts->hwtstamp.tv.sec & 0x7fffffff,
hwts->hwtstamp.tv.nsec & 0x7fffffff,
hwts->hwtstamp.tv.sec & 0x80000000 ? 1 :0);
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
/* FIXME: these stats don't go to the right place */
......
/*
* Pulse per second management for White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/sockios.h>
#include <linux/net_tstamp.h>
#include "wr-nic.h"
void wrn_ppsg_read_time(struct wrn_dev *wrn, u32 *fine_counter, u32 *utc)
{
u32 utc1, utc2, cnt;
utc1 = readl(&wrn->ppsg_regs->CNTR_UTCLO);
cnt = readl(&wrn->ppsg_regs->CNTR_NSEC);
utc2 = readl(&wrn->ppsg_regs->CNTR_UTCLO);
if (utc2 != utc1)
cnt = readl(&wrn->ppsg_regs->CNTR_NSEC);
*utc = utc2;
*fine_counter = cnt;
}
......@@ -51,7 +51,18 @@
#define WRN_IRQ_NAMES {"wr-nic"}
#define WRN_IRQ_HANDLERS {wrn_interrupt}
#define WRN_TS_BUF_SIZE 1024 /* array of timestamp structures */
struct wrn_ep; /* Defined later */
/* A timestamping structure to keep information for user-space */
struct wrn_tx_tstamp {
u8 valid;
u8 port_id;
u16 frame_id;
u32 ts;
};
/*
* This is the main data structure for our NIC device. As for locking,
* the rule is that _either_ the wrn _or_ the endpoint is locked. Not both.
......@@ -61,6 +72,9 @@ struct wrn_dev {
void __iomem *bases[WRN_NBLOCKS];
struct NIC_WB __iomem *regs; /* shorthand for NIC-block registers */
struct TXTSU_WB __iomem *txtsu_regs; /* ... and the same for TXTSU */
struct PPSG_WB __iomem *ppsg_regs; /* ... */
spinlock_t lock;
struct wrn_txd __iomem *txd;
struct wrn_rxd __iomem *rxd;
......@@ -73,7 +87,7 @@ struct wrn_dev {
int id;
struct net_device *dev[WRN_NR_ENDPOINTS];
struct wrn_tx_tstamp ts_buf[WRN_TS_BUF_SIZE];
/* FIXME: all dev fields must be verified */
......@@ -205,6 +219,6 @@ extern irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id);
/* Following functions from dmtd.c */
extern int wrn_phase_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern int wrn_calib_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern void wrn_ppsg_read_time(struct wrn_dev *wrn, u32 *fine_cnt, u32 *utc);
#endif /* __WR_NIC_H__ */
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