Add bus error, state change, lost message handling mechanism. Signed-off-by: Dong Aisheng <b29396@xxxxxxxxxxxxx> --- drivers/net/can/m_can.c | 271 +++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 240 insertions(+), 31 deletions(-) diff --git a/drivers/net/can/m_can.c b/drivers/net/can/m_can.c index 61e9a8e..e4aed71 100644 --- a/drivers/net/can/m_can.c +++ b/drivers/net/can/m_can.c @@ -83,6 +83,18 @@ enum m_can_reg { M_CAN_TXEFA = 0xf8, }; +/* m_can lec values */ +enum m_can_lec_type { + LEC_NO_ERROR = 0, + LEC_STUFF_ERROR, + LEC_FORM_ERROR, + LEC_ACK_ERROR, + LEC_BIT1_ERROR, + LEC_BIT0_ERROR, + LEC_CRC_ERROR, + LEC_UNUSED, +}; + /* CC Control Register(CCCR) */ #define CCCR_CCE BIT(1) #define CCCR_INIT BIT(0) @@ -97,6 +109,19 @@ enum m_can_reg { #define BTR_SJW_SHIFT 0 #define BTR_SJW_MASK 0xf +/* Error Counter Register(ECR) */ +#define ECR_RP BIT(15) +#define ECR_REC_SHIFT 8 +#define ECR_REC_MASK (0x7f << ECR_REC_SHIFT) +#define ECR_TEC_SHIFT 0 +#define ECR_TEC_MASK 0xff + +/* Protocol Status Register(PSR) */ +#define PSR_BO BIT(7) +#define PSR_EW BIT(6) +#define PSR_EP BIT(5) +#define PSR_LEC_MASK 0x7 + /* Interrupt Register(IR) */ #define IR_ALL_INT 0xffffffff #define IR_STE BIT(31) @@ -131,10 +156,11 @@ enum m_can_reg { #define IR_RF0F BIT(2) #define IR_RF0W BIT(1) #define IR_RF0N BIT(0) -#define IR_ERR_ALL (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \ - IR_WDI | IR_BO | IR_EW | IR_EP | IR_ELO | IR_BEU | \ - IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \ - IR_RF0L) +#define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) +#define IR_ERR_BUS (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \ + IR_WDI | IR_ELO | IR_BEU | IR_BEC | IR_TOO | IR_MRAF | \ + IR_TSW | IR_TEFL | IR_RF1L | IR_RF0L) +#define IR_ERR_ALL (IR_ERR_STATE | IR_ERR_BUS) /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ #define RXFC_FWM_OFF 24 @@ -320,12 +346,175 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) return num_rx_pkts; } +static int m_can_handle_lost_msg(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *frame; + + netdev_err(dev, "msg lost in rxf0\n"); + + skb = alloc_can_err_skb(dev, &frame); + if (unlikely(!skb)) + return 0; + + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_errors++; + stats->rx_over_errors++; + + netif_receive_skb(skb); + + return 1; +} + +static int m_can_handle_bus_err(struct net_device *dev, + enum m_can_lec_type lec_type) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* early exit if no lec update */ + if (lec_type == LEC_UNUSED) + return 0; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + /* + * check for 'last error code' which tells us the + * type of the last error to occur on the CAN bus + */ + priv->can.can_stats.bus_error++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + + switch (lec_type) { + case LEC_STUFF_ERROR: + netdev_dbg(dev, "stuff error\n"); + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + case LEC_FORM_ERROR: + netdev_dbg(dev, "form error\n"); + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case LEC_ACK_ERROR: + netdev_dbg(dev, "ack error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK | + CAN_ERR_PROT_LOC_ACK_DEL); + break; + case LEC_BIT1_ERROR: + netdev_dbg(dev, "bit1 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT1; + break; + case LEC_BIT0_ERROR: + netdev_dbg(dev, "bit0 error\n"); + cf->data[2] |= CAN_ERR_PROT_BIT0; + break; + case LEC_CRC_ERROR: + netdev_dbg(dev, "CRC error\n"); + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + break; + default: + break; + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int m_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct m_can_priv *priv = netdev_priv(dev); + unsigned int ecr; + + ecr = m_can_read(priv, M_CAN_ECR); + bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; + bec->txerr = ecr & ECR_TEC_MASK; + + return 0; +} + +static int m_can_handle_state_change(struct net_device *dev, + enum can_state new_state) +{ + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + struct can_berr_counter bec; + unsigned int ecr; + + /* propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + m_can_get_berr_counter(dev, &bec); + + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* error warning state */ + priv->can.can_stats.error_warning++; + priv->can.state = CAN_STATE_ERROR_WARNING; + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_ERROR_PASSIVE: + /* error passive state */ + priv->can.can_stats.error_passive++; + priv->can.state = CAN_STATE_ERROR_PASSIVE; + cf->can_id |= CAN_ERR_CRTL; + ecr = m_can_read(priv, M_CAN_ECR); + if (ecr & ECR_RP) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + if (bec.txerr > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + break; + case CAN_STATE_BUS_OFF: + /* bus-off state */ + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * disable all interrupts in bus-off mode to ensure that + * the CPU is not hogged down + */ + m_can_enable_all_interrupts(priv, false); + can_bus_off(dev); + break; + default: + break; + } + + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + static int m_can_poll(struct napi_struct *napi, int quota) { struct net_device *dev = napi->dev; struct m_can_priv *priv = netdev_priv(dev); u32 work_done = 0; - u32 irqstatus; + u32 irqstatus, psr; irqstatus = m_can_read(priv, M_CAN_IR); if (irqstatus) @@ -337,6 +526,48 @@ static int m_can_poll(struct napi_struct *napi, int quota) if (!irqstatus) goto end; + psr = m_can_read(priv, M_CAN_PSR); + if (irqstatus & IR_ERR_STATE) { + if ((psr & PSR_EW) && + (priv->can.state != CAN_STATE_ERROR_WARNING)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_WARNING); + } + + if ((psr & PSR_EP) && + (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_ERROR_PASSIVE); + } + + if ((psr & PSR_BO) && + (priv->can.state != CAN_STATE_BUS_OFF)) { + netdev_dbg(dev, "entered error warning state\n"); + work_done += m_can_handle_state_change(dev, + CAN_STATE_BUS_OFF); + } + } + + if (irqstatus & IR_ERR_BUS) { + if (irqstatus & IR_RF0L) + work_done += m_can_handle_lost_msg(dev); + + /* handle lec errors on the bus */ + if (psr & LEC_UNUSED) + work_done += m_can_handle_bus_err(dev, + psr & LEC_UNUSED); + + /* other unproccessed error interrupts */ + if (irqstatus & IR_WDI) + netdev_err(dev, "Message RAM Watchdog event due to missing READY\n"); + if (irqstatus & IR_TOO) + netdev_err(dev, "Timeout reached\n"); + if (irqstatus & IR_MRAF) + netdev_err(dev, "Message RAM access failure occurred\n"); + } + if (irqstatus & IR_RF0N) /* handle events corresponding to receive message objects */ work_done += m_can_do_rx_poll(dev, (quota - work_done)); @@ -369,31 +600,18 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (ir & IR_ALL_INT) m_can_write(priv, M_CAN_IR, ir); - if (ir & IR_ERR_ALL) { - netdev_dbg(dev, "bus error\n"); - /* TODO: handle bus error */ - } - - /* save irqstatus for later using */ - priv->irqstatus = ir; - /* * schedule NAPI in case of * - rx IRQ - * - state change IRQ(TODO) - * - bus error IRQ and bus error reporting (TODO) + * - state change IRQ + * - bus error IRQ and bus error reporting */ - if (ir & IR_RF0N) { + if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) { + priv->irqstatus = ir; m_can_enable_all_interrupts(priv, false); napi_schedule(&priv->napi); } - /* FIFO overflow */ - if (ir & IR_RF0L) { - dev->stats.rx_over_errors++; - dev->stats.rx_errors++; - } - /* transmission complete interrupt */ if (ir & IR_TC) { netdev_dbg(dev, "tx complete\n"); @@ -446,7 +664,6 @@ static int m_can_set_bittiming(struct net_device *dev) * - setup bittiming * - TODO: * 1) other working modes support like monitor, loopback... - * 2) lec error status report enable */ static void m_can_chip_config(struct net_device *dev) { @@ -515,14 +732,6 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode) return 0; } -static int m_can_get_berr_counter(const struct net_device *dev, - struct can_berr_counter *bec) -{ - /* TODO */ - - return 0; -} - static void free_m_can_dev(struct net_device *dev) { free_candev(dev); -- 1.7.8 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html