From 665bf9feb627d2dd13cc60d03a611b9e0e6510e5 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Tue, 11 Mar 2014 23:41:44 -0500 Subject: [PATCH] wcxb: Reset TDM engine on IO errors. There are error conditions that the firmware can detect but cannot recover from without help from the driver. When firmware detects these conditions the DESC_IO_ERROR bit will be set in the descriptor header to signal that the driver should reset the TDM engine on the card. Since this is not due to failure of the host to service the interrupt in time, it does not make sense to increase latency when these conditions are detected. Internal-Issue-ID: DAHDI-1087 Signed-off-by: Shaun Ruffell --- drivers/dahdi/wcxb.c | 34 +++++++++++++++++++++++++++++++--- drivers/dahdi/wcxb.h | 1 + 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/dahdi/wcxb.c b/drivers/dahdi/wcxb.c index d02023b..6e770fb 100644 --- a/drivers/dahdi/wcxb.c +++ b/drivers/dahdi/wcxb.c @@ -62,6 +62,7 @@ #define DRING_SIZE_MASK (DRING_SIZE-1) #define DESC_EOR (1 << 0) #define DESC_INT (1 << 1) +#define DESC_IO_ERROR (1 << 30) #define DESC_OWN (1 << 31) #define DESC_DEFAULT_STATUS 0xdeadbeef #define DMA_CHAN_SIZE 128 @@ -357,10 +358,28 @@ static void _wcxb_reset_dring(struct wcxb *xb) static void wcxb_handle_dma(struct wcxb *xb) { struct wcxb_meta_desc *mdesc; + struct wcxb_hw_desc *tail = &(xb->hw_dring[xb->dma_tail]); - while (!(xb->hw_dring[xb->dma_tail].control & cpu_to_be32(DESC_OWN))) { + while (!(tail->control & cpu_to_be32(DESC_OWN))) { u_char *frame; + if (tail->control & cpu_to_be32(DESC_IO_ERROR)) { + u32 ier; + unsigned long flags; + + /* The firmware detected an error condition on the bus. + * Force an underrun by disabling the descriptor + * complete interrupt. When the driver processes the + * underrun it will reset the TDM engine. */ + xb->flags.io_error = 1; + + spin_lock_irqsave(&xb->lock, flags); + ier = ioread32be(xb->membase + IER); + iowrite32be(ier & ~DESC_COMPLETE, xb->membase + IER); + spin_unlock_irqrestore(&xb->lock, flags); + return; + } + mdesc = &xb->meta_dring[xb->dma_tail]; frame = mdesc->rx_buf_virt; @@ -368,6 +387,7 @@ static void wcxb_handle_dma(struct wcxb *xb) xb->dma_tail = (xb->dma_tail == xb->latency-1) ? 0 : xb->dma_tail + 1; + tail = &(xb->hw_dring[xb->dma_tail]); mdesc = &xb->meta_dring[xb->dma_head]; frame = mdesc->tx_buf_virt; @@ -403,10 +423,18 @@ static irqreturn_t _wcxb_isr(int irq, void *dev_id) if (xb->ops->handle_error) xb->ops->handle_error(xb); - /* bump latency */ spin_lock(&xb->lock); - if (!xb->flags.latency_locked) { + if (xb->flags.io_error) { + /* Since an IO error is not necessarily because + * the host could not keep up, we do not want to + * bump the latency. */ + xb->flags.io_error = 0; + dev_warn(&xb->pdev->dev, + "IO error reported by firmware.\n"); + } else if (!xb->flags.latency_locked) { + /* bump latency */ + xb->latency = min(xb->latency + 1, xb->max_latency); #ifdef HAVE_RATELIMIT diff --git a/drivers/dahdi/wcxb.h b/drivers/dahdi/wcxb.h index af7828b..c7d54d5 100644 --- a/drivers/dahdi/wcxb.h +++ b/drivers/dahdi/wcxb.h @@ -61,6 +61,7 @@ struct wcxb { #ifdef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE u32 is_pcie:1; #endif + u32 io_error:1; } flags; void __iomem *membase; struct wcxb_meta_desc *meta_dring;