diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c
index 6bcc474..43cdd55 100644
--- a/drivers/net/can/rcar/rcar_canfd.c
+++ b/drivers/net/can/rcar/rcar_canfd.c
@@ -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;
