diff --git a/doc/spec-sw.in b/doc/spec-sw.in index 29da5437f297c44c3260df9cdcba0714095fa380..567ae5ab98c42ac8475a7539ff9d7d9e874dcbbf 100644 --- a/doc/spec-sw.in +++ b/doc/spec-sw.in @@ -33,13 +33,13 @@ @setchapternewpage off -@set update-month December 2012 +@set update-month April 2013 @finalout @titlepage @title SPEC Software Support -@subtitle Version 2.2 (@value{update-month}) +@c @subtitle Version 2.2 (@value{update-month}) @subtitle A driver for the SPEC card and its FMC modules @author Alessandro Rubini for CERN (BE-CO-HT) @end titlepage @@ -977,6 +977,39 @@ Example uses of the tool follow: wr-dio-cmd wr0 mode Ii--0 @end example +@c ========================================================================== +@node Timestamping Fast Input Signals +@section Timestamping Fast Input Signals + +When timestamping pulses in the @i{simple-DIO} mezzanine board, an +interrupt is generated to notify the driver that a new timestamp is +pending. On recent computers this works reliably up to more than +100kHz, but clearly there is a point where the system locks up, +because it spends all of its time in interrupt handling. + +This problem is transient: as soon as you remove the offending cable +the system recovers. However, you need a 10MHz input signal if you +want to run your SPEC device to be a White Rabbit @i{grandmaster}. In +order to support that, the driver disables DIO interrupts when the +time spent in interrupt management exceeds 80% of the total time, +averaged over one thousand interrupt events. Ethernet interrupts +are not affected. The fact is reported by a kernel message, using +the PCI address of the card that triggered the problem. + +@example + spec 0000:04:00.0: DIO irq takes > 80% CPU time: disabling +@end example + +This choice allows stamping your pulse trains up to a few dozen +kilohertz and still be able to feed higher frequencies without manual +intervention. However, after DIO interrupts are disabled, the only +way to re-enable them is removing and reloading the device driver. + +@b{Note}: if you run two SPEC cards, and one is fed with high frequency +pulses, it may happen that interrupts are disabled on both boards. +The safeguard is currently not very refined, as it was implemented in +a hurry. + @c ========================================================================== @node WR-DIO Pulse per Second @section WR-DIO Pulse per Second diff --git a/kernel/wr-nic-dio.c b/kernel/wr-nic-dio.c index bb776583ff6265a081537d4150f20e674ef50991..ed7fd3cb396b274df86b903930f7cf28360412be 100644 --- a/kernel/wr-nic-dio.c +++ b/kernel/wr-nic-dio.c @@ -20,6 +20,7 @@ #include "spec-nic.h" #include "wr_nic/wr-nic.h" #include "wr-dio.h" +#include "wbgen-regs/vic-regs.h" #ifdef DIO_STAT #define wrn_stat 1 @@ -411,9 +412,12 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc) { struct platform_device *pdev = fmc->mezzanine_data; struct wrn_drvdata *drvdata = pdev->dev.platform_data; + struct VIC_WB __iomem *vic = drvdata->vic_base; struct DIO_WB __iomem *dio = drvdata->wrdio_base; void __iomem *base = drvdata->wrdio_base; struct dio_device *d = drvdata->mezzanine_data; + static ktime_t t_ini, t_end; + static int rate_avg; struct dio_channel *c; struct timespec *ts; struct regmap *map; @@ -427,6 +431,29 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc) return IRQ_NONE; } + /* Protect against interrupts taking 100% of cpu time */ + if (ktime_to_ns(t_end)) { + int rate; + u64 offtime, ontime; + + ontime = ktime_to_ns(t_end) - ktime_to_ns(t_ini); + t_ini = ktime_get(); + offtime = ktime_to_ns(t_ini) - ktime_to_ns(t_end); + + /* avoid __udivdi3 */ + if (offtime > 100 * ontime) + rate = 0; + else + rate = ((int)ontime * 100) / (int)offtime; + + rate_avg = (rate_avg * 1023 + rate) / 1024; + if (rate_avg > 80) { + dev_warn(fmc->hwdev, "DIO irq takes > 80%% CPU time: " + "disabling\n"); + writel(WRN_VIC_MASK_DIO, &vic->IDR); + } + } + mask = readl(&dio->EIC_ISR) & WRN_DIO_IRQ_MASK; /* Three indexes: channel, channel-mask, channel pointer */ @@ -466,6 +493,7 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc) } wake_up_interruptible(&c->q); } + t_end = ktime_get(); return IRQ_HANDLED; }