aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@bitwise.fi>2017-02-07 10:01:14 -0500
committerMarc Kleine-Budde <mkl@pengutronix.de>2018-07-23 08:34:45 -0400
commit32852c561bffd613d4ed7ec464b1e03e1b7b6c5c (patch)
tree32a2004c5eee9c55ad99924a5250224fa76d767e
parent2574fe54515ed3487405de329e4e9f13d7098c10 (diff)
can: xilinx_can: fix RX loop if RXNEMP is asserted without RXOK
If the device gets into a state where RXNEMP (RX FIFO not empty) interrupt is asserted without RXOK (new frame received successfully) interrupt being asserted, xcan_rx_poll() will continue to try to clear RXNEMP without actually reading frames from RX FIFO. If the RX FIFO is not empty, the interrupt will not be cleared and napi_schedule() will just be called again. This situation can occur when: (a) xcan_rx() returns without reading RX FIFO due to an error condition. The code tries to clear both RXOK and RXNEMP but RXNEMP will not clear due to a frame still being in the FIFO. The frame will never be read from the FIFO as RXOK is no longer set. (b) A frame is received between xcan_rx_poll() reading interrupt status and clearing RXOK. RXOK will be cleared, but RXNEMP will again remain set as the new message is still in the FIFO. I'm able to trigger case (b) by flooding the bus with frames under load. There does not seem to be any benefit in using both RXNEMP and RXOK in the way the driver does, and the polling example in the reference manual (UG585 v1.10 18.3.7 Read Messages from RxFIFO) also says that either RXOK or RXNEMP can be used for detecting incoming messages. Fix the issue and simplify the RX processing by only using RXNEMP without RXOK. Tested with the integrated CAN on Zynq-7000 SoC. Fixes: b1201e44f50b ("can: xilinx CAN controller support") Signed-off-by: Anssi Hannula <anssi.hannula@bitwise.fi> Cc: <stable@vger.kernel.org> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r--drivers/net/can/xilinx_can.c18
1 files changed, 5 insertions, 13 deletions
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 389a9603db8c..1bda47aa62f5 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -101,7 +101,7 @@ enum xcan_reg {
101#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\ 101#define XCAN_INTR_ALL (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
102 XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \ 102 XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
103 XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \ 103 XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
104 XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK) 104 XCAN_IXR_ARBLST_MASK)
105 105
106/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */ 106/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
107#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */ 107#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
@@ -708,15 +708,7 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota)
708 708
709 isr = priv->read_reg(priv, XCAN_ISR_OFFSET); 709 isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
710 while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) { 710 while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
711 if (isr & XCAN_IXR_RXOK_MASK) { 711 work_done += xcan_rx(ndev);
712 priv->write_reg(priv, XCAN_ICR_OFFSET,
713 XCAN_IXR_RXOK_MASK);
714 work_done += xcan_rx(ndev);
715 } else {
716 priv->write_reg(priv, XCAN_ICR_OFFSET,
717 XCAN_IXR_RXNEMP_MASK);
718 break;
719 }
720 priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK); 712 priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
721 isr = priv->read_reg(priv, XCAN_ISR_OFFSET); 713 isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
722 } 714 }
@@ -727,7 +719,7 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota)
727 if (work_done < quota) { 719 if (work_done < quota) {
728 napi_complete_done(napi, work_done); 720 napi_complete_done(napi, work_done);
729 ier = priv->read_reg(priv, XCAN_IER_OFFSET); 721 ier = priv->read_reg(priv, XCAN_IER_OFFSET);
730 ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK); 722 ier |= XCAN_IXR_RXNEMP_MASK;
731 priv->write_reg(priv, XCAN_IER_OFFSET, ier); 723 priv->write_reg(priv, XCAN_IER_OFFSET, ier);
732 } 724 }
733 return work_done; 725 return work_done;
@@ -799,9 +791,9 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
799 } 791 }
800 792
801 /* Check for the type of receive interrupt and Processing it */ 793 /* Check for the type of receive interrupt and Processing it */
802 if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) { 794 if (isr & XCAN_IXR_RXNEMP_MASK) {
803 ier = priv->read_reg(priv, XCAN_IER_OFFSET); 795 ier = priv->read_reg(priv, XCAN_IER_OFFSET);
804 ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK); 796 ier &= ~XCAN_IXR_RXNEMP_MASK;
805 priv->write_reg(priv, XCAN_IER_OFFSET, ier); 797 priv->write_reg(priv, XCAN_IER_OFFSET, ier);
806 napi_schedule(&priv->napi); 798 napi_schedule(&priv->napi);
807 } 799 }