diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index 51de7cbc579e..98c7ac4dd1f9 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -94,6 +94,9 @@ struct xilly_endpoint { __iomem void *registers; int fatal_error; + bool allow_isr; + spinlock_t allow_isr_lock; + struct mutex register_mutex; wait_queue_head_t ep_wait; diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 90c5b3d4e517..7189c6d97e7d 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -73,6 +73,8 @@ static struct workqueue_struct *xillybus_wq; * * rd_spinlock does the same with rd_*_buf_idx, rd_empty and end_offset. * + * allow_isr_lock protects allow_isr. + * * register_mutex is endpoint-specific, and is held when non-atomic * register operations are performed. wr_mutex and rd_mutex may be * held when register_mutex is taken, but none of the spinlocks. Note that @@ -85,7 +87,8 @@ static struct workqueue_struct *xillybus_wq; * Only interruptible blocking is allowed on mutexes and wait queues. * * All in all, the locking order goes (with skips allowed, of course): - * wr_mutex -> rd_mutex -> register_mutex -> wr_spinlock -> rd_spinlock + * wr_mutex -> rd_mutex -> register_mutex -> + * allow_isr_lock -> wr_spinlock -> rd_spinlock */ static void malformed_message(struct xilly_endpoint *endpoint, u32 *buf) @@ -120,6 +123,13 @@ irqreturn_t xillybus_isr(int irq, void *data) unsigned int msg_channel, msg_bufno, msg_data, msg_dir; struct xilly_channel *channel; + guard(spinlock)(&ep->allow_isr_lock); + + if (!ep->allow_isr) { + dev_err_ratelimited(ep->dev, "Unexpected interrupt! Something is wrong with the hardware.\n"); + return IRQ_NONE; + } + buf = ep->msgbuf_addr; buf_size = ep->msg_buf_size/sizeof(u32); @@ -138,6 +148,7 @@ irqreturn_t xillybus_isr(int irq, void *data) if (++ep->failed_messages > 10) { dev_err(ep->dev, "Lost sync with interrupt messages. Stopping.\n"); + return IRQ_NONE; } else { dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr, @@ -284,6 +295,19 @@ irqreturn_t xillybus_isr(int irq, void *data) } EXPORT_SYMBOL(xillybus_isr); +/* + * xilly_allow_isr() is similar to enabling / disabling the interrupt, + * with the difference that if an interrupt is issued while ep->allow_isr + * is false, this is visible in the kernel log. + */ + +static void xilly_allow_isr(struct xilly_endpoint *ep, bool newstate) +{ + guard(spinlock_irqsave)(&ep->allow_isr_lock); + + ep->allow_isr = newstate; +} + /* * A few trivial memory management functions. * NOTE: These functions are used only on probe and remove, and therefore @@ -652,6 +676,8 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint) channel->wr_sleepy = 1; + xilly_allow_isr(endpoint, true); + iowrite32(1 | (3 << 24), /* Opcode 3 for channel 0 = Send IDT */ endpoint->registers + fpga_buf_ctrl_reg); @@ -660,6 +686,8 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint) (!channel->wr_sleepy), XILLY_TIMEOUT); + xilly_allow_isr(endpoint, false); + if (t <= 0) { dev_err(endpoint->dev, "Failed to obtain IDT. Aborting.\n"); @@ -1838,6 +1866,9 @@ struct xilly_endpoint *xillybus_init_endpoint(struct device *dev) endpoint->failed_messages = 0; endpoint->fatal_error = 0; + endpoint->allow_isr = false; + spin_lock_init(&endpoint->allow_isr_lock); + init_waitqueue_head(&endpoint->ep_wait); mutex_init(&endpoint->register_mutex); @@ -1850,6 +1881,9 @@ static int xilly_quiesce(struct xilly_endpoint *endpoint) long t; endpoint->idtlen = -1; + endpoint->num_channels = 0; + + xilly_allow_isr(endpoint, true); iowrite32((u32) (endpoint->dma_using_dac & 0x0001), endpoint->registers + fpga_dma_control_reg); @@ -1857,6 +1891,9 @@ static int xilly_quiesce(struct xilly_endpoint *endpoint) t = wait_event_interruptible_timeout(endpoint->ep_wait, (endpoint->idtlen >= 0), XILLY_TIMEOUT); + + xilly_allow_isr(endpoint, false); + if (t <= 0) { dev_err(endpoint->dev, "Failed to quiesce the device on exit.\n"); @@ -1910,6 +1947,8 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) endpoint->idtlen = -1; + xilly_allow_isr(endpoint, true); + /* * Set DMA 32/64 bit mode, quiesce the device (?!) and get IDT * buffer size. @@ -1920,6 +1959,9 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) t = wait_event_interruptible_timeout(endpoint->ep_wait, (endpoint->idtlen >= 0), XILLY_TIMEOUT); + + xilly_allow_isr(endpoint, false); + if (t <= 0) { dev_err(endpoint->dev, "No response from FPGA. Aborting.\n"); return -ENODEV; @@ -1946,6 +1988,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) if (rc) goto failed_idt; + /* xilly_obtain_idt() allows and then disallows the ISR */ rc = xilly_obtain_idt(endpoint); if (rc) goto failed_idt; @@ -1964,6 +2007,8 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) if (rc) goto failed_idt; + xilly_allow_isr(endpoint, true); + rc = xillybus_init_chrdev(dev, &xillybus_fops, endpoint->owner, endpoint, idt_handle.names,