|
@@ -390,37 +390,23 @@ static int cc770_get_berr_counter(const struct net_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
+static void cc770_tx(struct net_device *dev, int mo)
|
|
|
{
|
|
|
struct cc770_priv *priv = netdev_priv(dev);
|
|
|
- struct net_device_stats *stats = &dev->stats;
|
|
|
- struct can_frame *cf = (struct can_frame *)skb->data;
|
|
|
- unsigned int mo = obj2msgobj(CC770_OBJ_TX);
|
|
|
+ struct can_frame *cf = (struct can_frame *)priv->tx_skb->data;
|
|
|
u8 dlc, rtr;
|
|
|
u32 id;
|
|
|
int i;
|
|
|
|
|
|
- if (can_dropped_invalid_skb(dev, skb))
|
|
|
- return NETDEV_TX_OK;
|
|
|
-
|
|
|
- if ((cc770_read_reg(priv,
|
|
|
- msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
|
|
|
- netdev_err(dev, "TX register is still occupied!\n");
|
|
|
- return NETDEV_TX_BUSY;
|
|
|
- }
|
|
|
-
|
|
|
- netif_stop_queue(dev);
|
|
|
-
|
|
|
dlc = cf->can_dlc;
|
|
|
id = cf->can_id;
|
|
|
- if (cf->can_id & CAN_RTR_FLAG)
|
|
|
- rtr = 0;
|
|
|
- else
|
|
|
- rtr = MSGCFG_DIR;
|
|
|
+ rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR;
|
|
|
+
|
|
|
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
+ MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
|
|
|
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
|
|
RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES);
|
|
|
- cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
- MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES);
|
|
|
+
|
|
|
if (id & CAN_EFF_FLAG) {
|
|
|
id &= CAN_EFF_MASK;
|
|
|
cc770_write_reg(priv, msgobj[mo].config,
|
|
@@ -439,22 +425,30 @@ static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
for (i = 0; i < dlc; i++)
|
|
|
cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]);
|
|
|
|
|
|
- /* Store echo skb before starting the transfer */
|
|
|
- can_put_echo_skb(skb, dev, 0);
|
|
|
-
|
|
|
cc770_write_reg(priv, msgobj[mo].ctrl1,
|
|
|
- RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
|
|
|
+ RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC);
|
|
|
+ cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
+ MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC);
|
|
|
+}
|
|
|
|
|
|
- stats->tx_bytes += dlc;
|
|
|
+static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
+{
|
|
|
+ struct cc770_priv *priv = netdev_priv(dev);
|
|
|
+ unsigned int mo = obj2msgobj(CC770_OBJ_TX);
|
|
|
|
|
|
+ if (can_dropped_invalid_skb(dev, skb))
|
|
|
+ return NETDEV_TX_OK;
|
|
|
|
|
|
- /*
|
|
|
- * HM: We had some cases of repeated IRQs so make sure the
|
|
|
- * INT is acknowledged I know it's already further up, but
|
|
|
- * doing again fixed the issue
|
|
|
- */
|
|
|
- cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
|
|
|
+ netif_stop_queue(dev);
|
|
|
+
|
|
|
+ if ((cc770_read_reg(priv,
|
|
|
+ msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) {
|
|
|
+ netdev_err(dev, "TX register is still occupied!\n");
|
|
|
+ return NETDEV_TX_BUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ priv->tx_skb = skb;
|
|
|
+ cc770_tx(dev, mo);
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
@@ -680,19 +674,47 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
|
|
|
struct cc770_priv *priv = netdev_priv(dev);
|
|
|
struct net_device_stats *stats = &dev->stats;
|
|
|
unsigned int mo = obj2msgobj(o);
|
|
|
+ struct can_frame *cf;
|
|
|
+ u8 ctrl1;
|
|
|
+
|
|
|
+ ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1);
|
|
|
|
|
|
- /* Nothing more to send, switch off interrupts */
|
|
|
cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES);
|
|
|
- /*
|
|
|
- * We had some cases of repeated IRQ so make sure the
|
|
|
- * INT is acknowledged
|
|
|
+ cc770_write_reg(priv, msgobj[mo].ctrl1,
|
|
|
+ RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES);
|
|
|
+
|
|
|
+ if (unlikely(!priv->tx_skb)) {
|
|
|
+ netdev_err(dev, "missing tx skb in tx interrupt\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (unlikely(ctrl1 & MSGLST_SET)) {
|
|
|
+ stats->rx_over_errors++;
|
|
|
+ stats->rx_errors++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* When the CC770 is sending an RTR message and it receives a regular
|
|
|
+ * message that matches the id of the RTR message, it will overwrite the
|
|
|
+ * outgoing message in the TX register. When this happens we must
|
|
|
+ * process the received message and try to transmit the outgoing skb
|
|
|
+ * again.
|
|
|
*/
|
|
|
- cc770_write_reg(priv, msgobj[mo].ctrl0,
|
|
|
- MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES);
|
|
|
+ if (unlikely(ctrl1 & NEWDAT_SET)) {
|
|
|
+ cc770_rx(dev, mo, ctrl1);
|
|
|
+ cc770_tx(dev, mo);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- stats->tx_packets++;
|
|
|
+ can_put_echo_skb(priv->tx_skb, dev, 0);
|
|
|
can_get_echo_skb(dev, 0);
|
|
|
+
|
|
|
+ cf = (struct can_frame *)priv->tx_skb->data;
|
|
|
+ stats->tx_bytes += cf->can_dlc;
|
|
|
+ stats->tx_packets++;
|
|
|
+
|
|
|
+ priv->tx_skb = NULL;
|
|
|
+
|
|
|
netif_wake_queue(dev);
|
|
|
}
|
|
|
|
|
@@ -804,6 +826,7 @@ struct net_device *alloc_cc770dev(int sizeof_priv)
|
|
|
priv->can.do_set_bittiming = cc770_set_bittiming;
|
|
|
priv->can.do_set_mode = cc770_set_mode;
|
|
|
priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
|
|
|
+ priv->tx_skb = NULL;
|
|
|
|
|
|
memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags));
|
|
|
|