Commit ab3f2f8d authored by Alessandro Rubini's avatar Alessandro Rubini

kernel/wr_nic: major cleanup of tx timestamping

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent b242b877
...@@ -193,7 +193,7 @@ static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -193,7 +193,7 @@ static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* The timestamp has not been collected: silently discard it */ /* The timestamp has not been collected: silently discard it */
} }
wrn->skb_desc[desc].skb = skb; /* Save for tx irq and stamping */ wrn->skb_desc[desc].skb = skb; /* Save for tx irq and stamping */
wrn->skb_desc[desc].id = id; /* Save for tx irq and stamping */ wrn->skb_desc[desc].frame_id = id; /* Save for tx irq and stamping */
//netif_stop_queue(dev); /* Queue stopped until tx is over (FIXME?) */ //netif_stop_queue(dev); /* Queue stopped until tx is over (FIXME?) */
...@@ -471,7 +471,7 @@ static void wrn_tx_interrupt(struct wrn_dev *wrn) ...@@ -471,7 +471,7 @@ static void wrn_tx_interrupt(struct wrn_dev *wrn)
/* hardware timestamping is enabled */ /* hardware timestamping is enabled */
info->tx_flags |= SKBTX_IN_PROGRESS; info->tx_flags |= SKBTX_IN_PROGRESS;
pr_debug("%s: %i -- in progress\n", __func__, __LINE__); pr_debug("%s: %i -- in progress\n", __func__, __LINE__);
wrn_tstamp_find_skb(wrn, i); wrn_tx_tstamp_skb(wrn, i);
/* It has been freed if found; otherwise keep it */ /* It has been freed if found; otherwise keep it */
} else { } else {
dev_kfree_skb_irq(skb); dev_kfree_skb_irq(skb);
......
...@@ -15,40 +15,29 @@ ...@@ -15,40 +15,29 @@
#include "wr-nic.h" #include "wr-nic.h"
/* This looks for an skb in the already-received stamp list */ /* This checks if we already received the timestamp interrupt */
void wrn_tstamp_find_skb(struct wrn_dev *wrn, int desc) void wrn_tx_tstamp_skb(struct wrn_dev *wrn, int desc)
{ {
struct skb_shared_hwtstamps *hwts; struct skb_shared_hwtstamps *hwts;
struct sk_buff *skb = wrn->skb_desc[desc].skb; struct wrn_desc_pending *d = wrn->skb_desc + desc;
struct sk_buff *skb = d->skb;
struct timespec ts; struct timespec ts;
int id = wrn->skb_desc[desc].id;
u32 counter_ppsg; /* PPS generator nanosecond counter */ u32 counter_ppsg; /* PPS generator nanosecond counter */
u32 utc; u32 utc;
int i; /* FIXME: use list for faster access */
for(i = 0; i < WRN_TS_BUF_SIZE; i++) if (!wrn->skb_desc[desc].valid)
if(wrn->ts_buf[i].valid && wrn->ts_buf[i].frame_id == id)
break;
if (i == WRN_TS_BUF_SIZE) {
pr_debug("%s: not found\n", __func__);
return; return;
}
pr_debug("%s: found\n", __func__);
/* so we found the skb, do the timestamping magic */ /* already reported by hardware: do the timestamping magic */
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc); wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
/* The timestamp nanoseconds value is closer to the end of previous second, but the UTC time /* We may be at the beginning og the next second */
read from PPSG is at the beginning of the next second: adjust UTC seconds to avoid 1 sec if(counter_ppsg < d->cycles)
"jump" */
if(counter_ppsg < REFCLK_FREQ/4 && wrn->ts_buf[i].ts > 3*REFCLK_FREQ/4)
utc--; utc--;
ts.tv_sec = (s32)utc & 0x7fffffff; ts.tv_sec = (s32)utc & 0x7fffffff;
ts.tv_nsec = wrn->ts_buf[i].ts * NSEC_PER_TICK; ts.tv_nsec = d->cycles * NSEC_PER_TICK;
if (! (wrn->ts_buf[i].valid & TS_INVALID)) if (! (d->valid & TS_INVALID)) {
{
hwts = skb_hwtstamps(skb); hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts); hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts); skb_tstamp_tx(skb, hwts);
...@@ -56,70 +45,50 @@ void wrn_tstamp_find_skb(struct wrn_dev *wrn, int desc) ...@@ -56,70 +45,50 @@ void wrn_tstamp_find_skb(struct wrn_dev *wrn, int desc)
dev_kfree_skb_irq(skb); dev_kfree_skb_irq(skb);
/* release both the descriptor and the tstamp entry */ /* release both the descriptor and the tstamp entry */
wrn->skb_desc[desc].skb = 0; d->skb = 0;
wrn->ts_buf[i].valid = 0; d->valid = 0;
} }
/* This function records the timestamp in a list -- called from interrupt */ /* This function, called by txtsu records the timestamp for the descriptor */
static int record_tstamp(struct wrn_dev *wrn, u32 tsval, u32 idreg, u32 r2) static int record_tstamp(struct wrn_dev *wrn, u32 tsval, u32 idreg, u32 r2)
{ {
int port_id = TXTSU_TSF_R1_PID_R(idreg);
int frame_id = TXTSU_TSF_R1_FID_R(idreg); int frame_id = TXTSU_TSF_R1_FID_R(idreg);
int ts_incorrect = r2 & TXTSU_TSF_R2_INCORRECT; int ts_incorrect = r2 & TXTSU_TSF_R2_INCORRECT;
struct skb_shared_hwtstamps *hwts; struct skb_shared_hwtstamps *hwts;
struct timespec ts; struct timespec ts;
struct sk_buff *skb; struct sk_buff *skb;
u32 utc, counter_ppsg; /* PPS generator nanosecond counter */ u32 utc, counter_ppsg; /* PPS generator nanosecond counter */
int i; /* FIXME: use list for faster access */ int i;
if (0) /* Find the skb in the descriptor array */
printk("%s: Got TS: %x pid %d fid %d\n", __func__,
tsval, port_id, frame_id);
/* First of all look if the skb is already pending */
for (i = 0; i < WRN_NR_DESC; i++) for (i = 0; i < WRN_NR_DESC; i++)
if (wrn->skb_desc[i].skb && wrn->skb_desc[i].id == frame_id) if (wrn->skb_desc[i].skb
&& wrn->skb_desc[i].frame_id == frame_id)
break; break;
if (i < WRN_NR_DESC) { if (i == WRN_NR_DESC) {
/*printk("%s: found\n", __func__);*/ /* Not found: Must be a PTP frame sent from the SPEC! */
skb = wrn->skb_desc[i].skb; return 0;
}
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc); skb = wrn->skb_desc[i].skb;
if(counter_ppsg < (tsval & 0xfffffff)) wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
utc--;
ts.tv_sec = (s32)utc & 0x7fffffff; if(counter_ppsg < (tsval & 0xfffffff))
ts.tv_nsec = (tsval & 0xfffffff) * NSEC_PER_TICK; utc--;
/* Provide the timestamp for the userland only if we're 100% sure about its correctness */ ts.tv_sec = (s32)utc & 0x7fffffff;
if (!ts_incorrect) ts.tv_nsec = (tsval & 0xfffffff) * NSEC_PER_TICK;
{
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts);
}
dev_kfree_skb_irq(skb);
wrn->skb_desc[i].skb = 0;
return 0;
}
/* Otherwise, save it to the list, in an empty slot */
for(i = 0; i < WRN_TS_BUF_SIZE; i++)
if(!wrn->ts_buf[i].valid)
break;
if (i == WRN_TS_BUF_SIZE) { /* Provide the timestamp only if 100% sure about its correctness */
pr_debug("%s: ENOMEM\n", __func__); if (!ts_incorrect) {
return -ENOMEM; hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts);
} }
pr_debug("%s: save to slot %i\n", __func__, i); dev_kfree_skb_irq(skb);
wrn->ts_buf[i].ts = tsval; wrn->skb_desc[i].skb = 0;
wrn->ts_buf[i].port_id = port_id;
wrn->ts_buf[i].frame_id = frame_id;
wrn->ts_buf[i].valid = TS_PRESENT;
if (ts_incorrect)
wrn->ts_buf[i].valid |= TS_INVALID;
return 0; return 0;
} }
...@@ -135,11 +104,7 @@ irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id) ...@@ -135,11 +104,7 @@ irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id)
r1 = readl(&regs->TSF_R1); r1 = readl(&regs->TSF_R1);
r2 = readl(&regs->TSF_R2); r2 = readl(&regs->TSF_R2);
if(record_tstamp(wrn, r0, r1, r2) < 0) { record_tstamp(wrn, r0, r1, r2);
printk("%s: ENOMEM in the TS buffer. Disabling TX stamping.\n",
__func__);
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IDR);
}
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_ISR); /* ack irq */ writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_ISR); /* ack irq */
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -196,7 +161,6 @@ int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -196,7 +161,6 @@ int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
void wrn_tstamp_init(struct wrn_dev *wrn) void wrn_tstamp_init(struct wrn_dev *wrn)
{ {
memset(wrn->ts_buf, 0, sizeof(wrn->ts_buf));
/* enable TXTSU irq */ /* enable TXTSU irq */
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IER); writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IER);
} }
......
...@@ -55,27 +55,21 @@ ...@@ -55,27 +55,21 @@
#define WRN_IRQ_NAMES {"wr-nic", "wr-tstamp"} #define WRN_IRQ_NAMES {"wr-nic", "wr-tstamp"}
#define WRN_IRQ_HANDLERS {wrn_interrupt, wrn_tstamp_interrupt} #define WRN_IRQ_HANDLERS {wrn_interrupt, wrn_tstamp_interrupt}
#define WRN_TS_BUF_SIZE 1024 /* array of timestamp structures */
struct wrn_ep; /* Defined later */ struct wrn_ep; /* Defined later */
/* A timestamping structure to keep information for user-space */ /* We must remember skb, id and tstamp for each pending descriptor, */
struct wrn_tx_tstamp { struct wrn_desc_pending {
struct sk_buff *skb;
u8 valid; u8 valid;
u8 port_id; u8 port_id;
u16 frame_id; u16 frame_id;
u32 ts; u32 cycles;
}; };
/* bits for "valid" field */ /* bits for "valid" field */
#define TS_PRESENT 1 #define TS_PRESENT 1
#define TS_INVALID 2 /* as reported by hw: we return 0 as timestamp */ #define TS_INVALID 2 /* as reported by hw: we return 0 as timestamp */
/* We must remember both skb and id for each pending descriptor */
struct wrn_desc_pending {
struct sk_buff *skb;
u32 id; /* only 16 bits, actually */
};
/* /*
* This is the main data structure for our NIC device. As for locking, * 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. * the rule is that _either_ the wrn _or_ the endpoint is locked. Not both.
...@@ -101,7 +95,6 @@ struct wrn_dev { ...@@ -101,7 +95,6 @@ struct wrn_dev {
int id; int id;
struct net_device *dev[WRN_NR_ENDPOINTS]; struct net_device *dev[WRN_NR_ENDPOINTS];
struct wrn_tx_tstamp ts_buf[WRN_TS_BUF_SIZE];
/* FIXME: all dev fields must be verified */ /* FIXME: all dev fields must be verified */
...@@ -237,7 +230,7 @@ extern int wrn_endpoint_probe(struct net_device *netdev); ...@@ -237,7 +230,7 @@ extern int wrn_endpoint_probe(struct net_device *netdev);
extern void wrn_endpoint_remove(struct net_device *netdev); extern void wrn_endpoint_remove(struct net_device *netdev);
/* Following functions from timestamp.c */ /* Following functions from timestamp.c */
extern void wrn_tstamp_find_skb(struct wrn_dev *wrn, int i); extern void wrn_tx_tstamp_skb(struct wrn_dev *wrn, int desc);
extern int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); extern int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id); extern irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id);
extern void wrn_tstamp_init(struct wrn_dev *wrn); extern void wrn_tstamp_init(struct wrn_dev *wrn);
......
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