can: rcar_canfd: Add back-to-error-active support
As per Wolfgang G, all new drivers should support decreasing state transition(back-to-error-active). This patch adds this support. This driver configures the controller to halt on bus-off entry. Hence, when in error states less than bus off state, the TEC/REC counters are checked for lower state transition eligibility and action. Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
		
							parent
							
								
									6f4c2eea35
								
							
						
					
					
						commit
						926f10359a
					
				| @ -917,16 +917,17 @@ static void rcar_canfd_global_error(struct net_device *ndev) | ||||
| 	rcar_canfd_write(priv->base, RCANFD_GERFL, 0); | ||||
| } | ||||
| 
 | ||||
| static void rcar_canfd_error(struct net_device *ndev) | ||||
| static void rcar_canfd_error(struct net_device *ndev, u32 cerfl, | ||||
| 			     u16 txerr, u16 rxerr) | ||||
| { | ||||
| 	struct rcar_canfd_channel *priv = netdev_priv(ndev); | ||||
| 	struct net_device_stats *stats = &ndev->stats; | ||||
| 	struct can_frame *cf; | ||||
| 	struct sk_buff *skb; | ||||
| 	u32 cerfl, csts; | ||||
| 	u32 txerr = 0, rxerr = 0; | ||||
| 	u32 ch = priv->channel; | ||||
| 
 | ||||
| 	netdev_dbg(ndev, "ch erfl %x txerr %u rxerr %u\n", cerfl, txerr, rxerr); | ||||
| 
 | ||||
| 	/* Propagate the error condition to the CAN stack */ | ||||
| 	skb = alloc_can_err_skb(ndev, &cf); | ||||
| 	if (!skb) { | ||||
| @ -934,15 +935,7 @@ static void rcar_canfd_error(struct net_device *ndev) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Channel error interrupt */ | ||||
| 	cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); | ||||
| 	csts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); | ||||
| 	txerr = RCANFD_CSTS_TECCNT(csts); | ||||
| 	rxerr = RCANFD_CSTS_RECCNT(csts); | ||||
| 
 | ||||
| 	netdev_dbg(ndev, "ch erfl %x sts %x txerr %u rxerr %u\n", | ||||
| 		   cerfl, csts, txerr, rxerr); | ||||
| 
 | ||||
| 	/* Channel error interrupts */ | ||||
| 	if (cerfl & RCANFD_CERFL_BEF) { | ||||
| 		netdev_dbg(ndev, "Bus error\n"); | ||||
| 		cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; | ||||
| @ -1032,8 +1025,9 @@ static void rcar_canfd_error(struct net_device *ndev) | ||||
| 		cf->data[2] |= CAN_ERR_PROT_OVERLOAD; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clear all channel error interrupts */ | ||||
| 	rcar_canfd_write(priv->base, RCANFD_CERFL(ch), 0); | ||||
| 	/* Clear channel error interrupts that are handled */ | ||||
| 	rcar_canfd_write(priv->base, RCANFD_CERFL(ch), | ||||
| 			 RCANFD_CERFL_ERR(~cerfl)); | ||||
| 	stats->rx_packets++; | ||||
| 	stats->rx_bytes += cf->can_dlc; | ||||
| 	netif_rx(skb); | ||||
| @ -1098,12 +1092,12 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) | ||||
| 
 | ||||
| 		/* Global error interrupts */ | ||||
| 		gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); | ||||
| 		if (RCANFD_GERFL_ERR(gpriv, gerfl)) | ||||
| 		if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl))) | ||||
| 			rcar_canfd_global_error(ndev); | ||||
| 
 | ||||
| 		/* Handle Rx interrupts */ | ||||
| 		sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); | ||||
| 		if (sts & RCANFD_RFSTS_RFIF) { | ||||
| 		if (likely(sts & RCANFD_RFSTS_RFIF)) { | ||||
| 			if (napi_schedule_prep(&priv->napi)) { | ||||
| 				/* Disable Rx FIFO interrupts */ | ||||
| 				rcar_canfd_clear_bit(priv->base, | ||||
| @ -1116,12 +1110,46 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static void rcar_canfd_state_change(struct net_device *ndev, | ||||
| 				    u16 txerr, u16 rxerr) | ||||
| { | ||||
| 	struct rcar_canfd_channel *priv = netdev_priv(ndev); | ||||
| 	struct net_device_stats *stats = &ndev->stats; | ||||
| 	enum can_state rx_state, tx_state, state = priv->can.state; | ||||
| 	struct can_frame *cf; | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	/* Handle transition from error to normal states */ | ||||
| 	if (txerr < 96 && rxerr < 96) | ||||
| 		state = CAN_STATE_ERROR_ACTIVE; | ||||
| 	else if (txerr < 128 && rxerr < 128) | ||||
| 		state = CAN_STATE_ERROR_WARNING; | ||||
| 
 | ||||
| 	if (state != priv->can.state) { | ||||
| 		netdev_dbg(ndev, "state: new %d, old %d: txerr %u, rxerr %u\n", | ||||
| 			   state, priv->can.state, txerr, rxerr); | ||||
| 		skb = alloc_can_err_skb(ndev, &cf); | ||||
| 		if (!skb) { | ||||
| 			stats->rx_dropped++; | ||||
| 			return; | ||||
| 		} | ||||
| 		tx_state = txerr >= rxerr ? state : 0; | ||||
| 		rx_state = txerr <= rxerr ? state : 0; | ||||
| 
 | ||||
| 		can_change_state(ndev, cf, tx_state, rx_state); | ||||
| 		stats->rx_packets++; | ||||
| 		stats->rx_bytes += cf->can_dlc; | ||||
| 		netif_rx(skb); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) | ||||
| { | ||||
| 	struct rcar_canfd_global *gpriv = dev_id; | ||||
| 	struct net_device *ndev; | ||||
| 	struct rcar_canfd_channel *priv; | ||||
| 	u32 sts, cerfl, ch; | ||||
| 	u32 sts, ch, cerfl; | ||||
| 	u16 txerr, rxerr; | ||||
| 
 | ||||
| 	/* Common FIFO is a per channel resource */ | ||||
| 	for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { | ||||
| @ -1130,13 +1158,21 @@ static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) | ||||
| 
 | ||||
| 		/* Channel error interrupts */ | ||||
| 		cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); | ||||
| 		if (RCANFD_CERFL_ERR(cerfl)) | ||||
| 			rcar_canfd_error(ndev); | ||||
| 		sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); | ||||
| 		txerr = RCANFD_CSTS_TECCNT(sts); | ||||
| 		rxerr = RCANFD_CSTS_RECCNT(sts); | ||||
| 		if (unlikely(RCANFD_CERFL_ERR(cerfl))) | ||||
| 			rcar_canfd_error(ndev, cerfl, txerr, rxerr); | ||||
| 
 | ||||
| 		/* Handle state change to lower states */ | ||||
| 		if (unlikely((priv->can.state != CAN_STATE_ERROR_ACTIVE) && | ||||
| 			     (priv->can.state != CAN_STATE_BUS_OFF))) | ||||
| 			rcar_canfd_state_change(ndev, txerr, rxerr); | ||||
| 
 | ||||
| 		/* Handle Tx interrupts */ | ||||
| 		sts = rcar_canfd_read(priv->base, | ||||
| 				      RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); | ||||
| 		if (sts & RCANFD_CFSTS_CFTXIF) | ||||
| 		if (likely(sts & RCANFD_CFSTS_CFTXIF)) | ||||
| 			rcar_canfd_tx_done(ndev); | ||||
| 	} | ||||
| 	return IRQ_HANDLED; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user