diff options
author | Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com> | 2016-06-22 08:31:47 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2016-06-23 05:23:49 -0400 |
commit | 926f10359a69fa82f0ec022f3b54ca8a05ea8440 (patch) | |
tree | 6fa6237d5fa3de26bc712da9bc326ce9ec34c537 | |
parent | 6f4c2eea353809fb85386d5ce17a30e37042847d (diff) |
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>
-rw-r--r-- | drivers/net/can/rcar/rcar_canfd.c | 76 |
1 files changed, 56 insertions, 20 deletions
diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 6bcc47408423..43cdd5544b0c 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) | |||
917 | rcar_canfd_write(priv->base, RCANFD_GERFL, 0); | 917 | rcar_canfd_write(priv->base, RCANFD_GERFL, 0); |
918 | } | 918 | } |
919 | 919 | ||
920 | static void rcar_canfd_error(struct net_device *ndev) | 920 | static void rcar_canfd_error(struct net_device *ndev, u32 cerfl, |
921 | u16 txerr, u16 rxerr) | ||
921 | { | 922 | { |
922 | struct rcar_canfd_channel *priv = netdev_priv(ndev); | 923 | struct rcar_canfd_channel *priv = netdev_priv(ndev); |
923 | struct net_device_stats *stats = &ndev->stats; | 924 | struct net_device_stats *stats = &ndev->stats; |
924 | struct can_frame *cf; | 925 | struct can_frame *cf; |
925 | struct sk_buff *skb; | 926 | struct sk_buff *skb; |
926 | u32 cerfl, csts; | ||
927 | u32 txerr = 0, rxerr = 0; | ||
928 | u32 ch = priv->channel; | 927 | u32 ch = priv->channel; |
929 | 928 | ||
929 | netdev_dbg(ndev, "ch erfl %x txerr %u rxerr %u\n", cerfl, txerr, rxerr); | ||
930 | |||
930 | /* Propagate the error condition to the CAN stack */ | 931 | /* Propagate the error condition to the CAN stack */ |
931 | skb = alloc_can_err_skb(ndev, &cf); | 932 | skb = alloc_can_err_skb(ndev, &cf); |
932 | if (!skb) { | 933 | if (!skb) { |
@@ -934,15 +935,7 @@ static void rcar_canfd_error(struct net_device *ndev) | |||
934 | return; | 935 | return; |
935 | } | 936 | } |
936 | 937 | ||
937 | /* Channel error interrupt */ | 938 | /* Channel error interrupts */ |
938 | cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); | ||
939 | csts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); | ||
940 | txerr = RCANFD_CSTS_TECCNT(csts); | ||
941 | rxerr = RCANFD_CSTS_RECCNT(csts); | ||
942 | |||
943 | netdev_dbg(ndev, "ch erfl %x sts %x txerr %u rxerr %u\n", | ||
944 | cerfl, csts, txerr, rxerr); | ||
945 | |||
946 | if (cerfl & RCANFD_CERFL_BEF) { | 939 | if (cerfl & RCANFD_CERFL_BEF) { |
947 | netdev_dbg(ndev, "Bus error\n"); | 940 | netdev_dbg(ndev, "Bus error\n"); |
948 | cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; | 941 | cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; |
@@ -1032,8 +1025,9 @@ static void rcar_canfd_error(struct net_device *ndev) | |||
1032 | cf->data[2] |= CAN_ERR_PROT_OVERLOAD; | 1025 | cf->data[2] |= CAN_ERR_PROT_OVERLOAD; |
1033 | } | 1026 | } |
1034 | 1027 | ||
1035 | /* Clear all channel error interrupts */ | 1028 | /* Clear channel error interrupts that are handled */ |
1036 | rcar_canfd_write(priv->base, RCANFD_CERFL(ch), 0); | 1029 | rcar_canfd_write(priv->base, RCANFD_CERFL(ch), |
1030 | RCANFD_CERFL_ERR(~cerfl)); | ||
1037 | stats->rx_packets++; | 1031 | stats->rx_packets++; |
1038 | stats->rx_bytes += cf->can_dlc; | 1032 | stats->rx_bytes += cf->can_dlc; |
1039 | netif_rx(skb); | 1033 | netif_rx(skb); |
@@ -1098,12 +1092,12 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) | |||
1098 | 1092 | ||
1099 | /* Global error interrupts */ | 1093 | /* Global error interrupts */ |
1100 | gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); | 1094 | gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL); |
1101 | if (RCANFD_GERFL_ERR(gpriv, gerfl)) | 1095 | if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl))) |
1102 | rcar_canfd_global_error(ndev); | 1096 | rcar_canfd_global_error(ndev); |
1103 | 1097 | ||
1104 | /* Handle Rx interrupts */ | 1098 | /* Handle Rx interrupts */ |
1105 | sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); | 1099 | sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx)); |
1106 | if (sts & RCANFD_RFSTS_RFIF) { | 1100 | if (likely(sts & RCANFD_RFSTS_RFIF)) { |
1107 | if (napi_schedule_prep(&priv->napi)) { | 1101 | if (napi_schedule_prep(&priv->napi)) { |
1108 | /* Disable Rx FIFO interrupts */ | 1102 | /* Disable Rx FIFO interrupts */ |
1109 | rcar_canfd_clear_bit(priv->base, | 1103 | rcar_canfd_clear_bit(priv->base, |
@@ -1116,12 +1110,46 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id) | |||
1116 | return IRQ_HANDLED; | 1110 | return IRQ_HANDLED; |
1117 | } | 1111 | } |
1118 | 1112 | ||
1113 | static void rcar_canfd_state_change(struct net_device *ndev, | ||
1114 | u16 txerr, u16 rxerr) | ||
1115 | { | ||
1116 | struct rcar_canfd_channel *priv = netdev_priv(ndev); | ||
1117 | struct net_device_stats *stats = &ndev->stats; | ||
1118 | enum can_state rx_state, tx_state, state = priv->can.state; | ||
1119 | struct can_frame *cf; | ||
1120 | struct sk_buff *skb; | ||
1121 | |||
1122 | /* Handle transition from error to normal states */ | ||
1123 | if (txerr < 96 && rxerr < 96) | ||
1124 | state = CAN_STATE_ERROR_ACTIVE; | ||
1125 | else if (txerr < 128 && rxerr < 128) | ||
1126 | state = CAN_STATE_ERROR_WARNING; | ||
1127 | |||
1128 | if (state != priv->can.state) { | ||
1129 | netdev_dbg(ndev, "state: new %d, old %d: txerr %u, rxerr %u\n", | ||
1130 | state, priv->can.state, txerr, rxerr); | ||
1131 | skb = alloc_can_err_skb(ndev, &cf); | ||
1132 | if (!skb) { | ||
1133 | stats->rx_dropped++; | ||
1134 | return; | ||
1135 | } | ||
1136 | tx_state = txerr >= rxerr ? state : 0; | ||
1137 | rx_state = txerr <= rxerr ? state : 0; | ||
1138 | |||
1139 | can_change_state(ndev, cf, tx_state, rx_state); | ||
1140 | stats->rx_packets++; | ||
1141 | stats->rx_bytes += cf->can_dlc; | ||
1142 | netif_rx(skb); | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1119 | static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) | 1146 | static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id) |
1120 | { | 1147 | { |
1121 | struct rcar_canfd_global *gpriv = dev_id; | 1148 | struct rcar_canfd_global *gpriv = dev_id; |
1122 | struct net_device *ndev; | 1149 | struct net_device *ndev; |
1123 | struct rcar_canfd_channel *priv; | 1150 | struct rcar_canfd_channel *priv; |
1124 | u32 sts, cerfl, ch; | 1151 | u32 sts, ch, cerfl; |
1152 | u16 txerr, rxerr; | ||
1125 | 1153 | ||
1126 | /* Common FIFO is a per channel resource */ | 1154 | /* Common FIFO is a per channel resource */ |
1127 | for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) { | 1155 | 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) | |||
1130 | 1158 | ||
1131 | /* Channel error interrupts */ | 1159 | /* Channel error interrupts */ |
1132 | cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); | 1160 | cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch)); |
1133 | if (RCANFD_CERFL_ERR(cerfl)) | 1161 | sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch)); |
1134 | rcar_canfd_error(ndev); | 1162 | txerr = RCANFD_CSTS_TECCNT(sts); |
1163 | rxerr = RCANFD_CSTS_RECCNT(sts); | ||
1164 | if (unlikely(RCANFD_CERFL_ERR(cerfl))) | ||
1165 | rcar_canfd_error(ndev, cerfl, txerr, rxerr); | ||
1166 | |||
1167 | /* Handle state change to lower states */ | ||
1168 | if (unlikely((priv->can.state != CAN_STATE_ERROR_ACTIVE) && | ||
1169 | (priv->can.state != CAN_STATE_BUS_OFF))) | ||
1170 | rcar_canfd_state_change(ndev, txerr, rxerr); | ||
1135 | 1171 | ||
1136 | /* Handle Tx interrupts */ | 1172 | /* Handle Tx interrupts */ |
1137 | sts = rcar_canfd_read(priv->base, | 1173 | sts = rcar_canfd_read(priv->base, |
1138 | RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); | 1174 | RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX)); |
1139 | if (sts & RCANFD_CFSTS_CFTXIF) | 1175 | if (likely(sts & RCANFD_CFSTS_CFTXIF)) |
1140 | rcar_canfd_tx_done(ndev); | 1176 | rcar_canfd_tx_done(ndev); |
1141 | } | 1177 | } |
1142 | return IRQ_HANDLED; | 1178 | return IRQ_HANDLED; |