diff options
-rw-r--r-- | drivers/net/can/c_can/c_can.c | 52 |
1 files changed, 50 insertions, 2 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 38f9adaf15ac..cef9967eff93 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c | |||
@@ -817,6 +817,38 @@ static void c_can_do_tx(struct net_device *dev) | |||
817 | } | 817 | } |
818 | 818 | ||
819 | /* | 819 | /* |
820 | * If we have a gap in the pending bits, that means we either | ||
821 | * raced with the hardware or failed to readout all upper | ||
822 | * objects in the last run due to quota limit. | ||
823 | */ | ||
824 | static u32 c_can_adjust_pending(u32 pend) | ||
825 | { | ||
826 | u32 weight, lasts; | ||
827 | |||
828 | if (pend == RECEIVE_OBJECT_BITS) | ||
829 | return pend; | ||
830 | |||
831 | /* | ||
832 | * If the last set bit is larger than the number of pending | ||
833 | * bits we have a gap. | ||
834 | */ | ||
835 | weight = hweight32(pend); | ||
836 | lasts = fls(pend); | ||
837 | |||
838 | /* If the bits are linear, nothing to do */ | ||
839 | if (lasts == weight) | ||
840 | return pend; | ||
841 | |||
842 | /* | ||
843 | * Find the first set bit after the gap. We walk backwards | ||
844 | * from the last set bit. | ||
845 | */ | ||
846 | for (lasts--; pend & (1 << (lasts - 1)); lasts--); | ||
847 | |||
848 | return pend & ~((1 << lasts) - 1); | ||
849 | } | ||
850 | |||
851 | /* | ||
820 | * theory of operation: | 852 | * theory of operation: |
821 | * | 853 | * |
822 | * c_can core saves a received CAN message into the first free message | 854 | * c_can core saves a received CAN message into the first free message |
@@ -843,7 +875,7 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota) | |||
843 | u32 num_rx_pkts = 0; | 875 | u32 num_rx_pkts = 0; |
844 | unsigned int msg_obj, msg_ctrl_save; | 876 | unsigned int msg_obj, msg_ctrl_save; |
845 | struct c_can_priv *priv = netdev_priv(dev); | 877 | struct c_can_priv *priv = netdev_priv(dev); |
846 | u16 val; | 878 | u32 val, pend = 0; |
847 | 879 | ||
848 | /* | 880 | /* |
849 | * It is faster to read only one 16bit register. This is only possible | 881 | * It is faster to read only one 16bit register. This is only possible |
@@ -852,7 +884,23 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota) | |||
852 | BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16, | 884 | BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16, |
853 | "Implementation does not support more message objects than 16"); | 885 | "Implementation does not support more message objects than 16"); |
854 | 886 | ||
855 | while (quota > 0 && (val = priv->read_reg(priv, C_CAN_INTPND1_REG))) { | 887 | while (quota > 0) { |
888 | |||
889 | if (!pend) { | ||
890 | pend = priv->read_reg(priv, C_CAN_INTPND1_REG); | ||
891 | if (!pend) | ||
892 | return num_rx_pkts; | ||
893 | /* | ||
894 | * If the pending field has a gap, handle the | ||
895 | * bits above the gap first. | ||
896 | */ | ||
897 | val = c_can_adjust_pending(pend); | ||
898 | } else { | ||
899 | val = pend; | ||
900 | } | ||
901 | /* Remove the bits from pend */ | ||
902 | pend &= ~val; | ||
903 | |||
856 | while ((msg_obj = ffs(val)) && quota > 0) { | 904 | while ((msg_obj = ffs(val)) && quota > 0) { |
857 | val &= ~BIT(msg_obj - 1); | 905 | val &= ~BIT(msg_obj - 1); |
858 | 906 | ||