diff options
-rw-r--r-- | drivers/net/can/m_can/m_can.c | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 75e7490c4299..02c5795b7393 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -778,6 +778,43 @@ static inline bool is_lec_err(u32 psr) return psr && (psr != LEC_UNUSED); } +static inline bool m_can_is_protocol_err(u32 irqstatus) +{ + return irqstatus & IR_ERR_LEC_31X; +} + +static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus) +{ + struct net_device_stats *stats = &dev->stats; + struct m_can_classdev *cdev = netdev_priv(dev); + struct can_frame *cf; + struct sk_buff *skb; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + + /* update tx error stats since there is protocol error */ + stats->tx_errors++; + + /* update arbitration lost status */ + if (cdev->version >= 31 && (irqstatus & IR_PEA)) { + netdev_dbg(dev, "Protocol error in Arbitration fail\n"); + cdev->can.can_stats.arbitration_lost++; + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] |= CAN_ERR_LOSTARB_UNSPEC; + } + } + + if (unlikely(!skb)) { + netdev_dbg(dev, "allocation of skb failed\n"); + return 0; + } + netif_receive_skb(skb); + + return 1; +} + static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, u32 psr) { @@ -792,6 +829,11 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus, is_lec_err(psr)) work_done += m_can_handle_lec_err(dev, psr & LEC_UNUSED); + /* handle protocol errors in arbitration phase */ + if ((cdev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) && + m_can_is_protocol_err(irqstatus)) + work_done += m_can_handle_protocol_error(dev, irqstatus); + /* other unproccessed error interrupts */ m_can_handle_other_err(dev, irqstatus); |