diff options
Diffstat (limited to 'drivers/net/can/cc770/cc770.c')
-rw-r--r-- | drivers/net/can/cc770/cc770.c | 100 |
1 files changed, 61 insertions, 39 deletions
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index 1e37313054f3..6da69af103e6 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c | |||
@@ -390,37 +390,23 @@ static int cc770_get_berr_counter(const struct net_device *dev, | |||
390 | return 0; | 390 | return 0; |
391 | } | 391 | } |
392 | 392 | ||
393 | static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) | 393 | static void cc770_tx(struct net_device *dev, int mo) |
394 | { | 394 | { |
395 | struct cc770_priv *priv = netdev_priv(dev); | 395 | struct cc770_priv *priv = netdev_priv(dev); |
396 | struct net_device_stats *stats = &dev->stats; | 396 | struct can_frame *cf = (struct can_frame *)priv->tx_skb->data; |
397 | struct can_frame *cf = (struct can_frame *)skb->data; | ||
398 | unsigned int mo = obj2msgobj(CC770_OBJ_TX); | ||
399 | u8 dlc, rtr; | 397 | u8 dlc, rtr; |
400 | u32 id; | 398 | u32 id; |
401 | int i; | 399 | int i; |
402 | 400 | ||
403 | if (can_dropped_invalid_skb(dev, skb)) | ||
404 | return NETDEV_TX_OK; | ||
405 | |||
406 | if ((cc770_read_reg(priv, | ||
407 | msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) { | ||
408 | netdev_err(dev, "TX register is still occupied!\n"); | ||
409 | return NETDEV_TX_BUSY; | ||
410 | } | ||
411 | |||
412 | netif_stop_queue(dev); | ||
413 | |||
414 | dlc = cf->can_dlc; | 401 | dlc = cf->can_dlc; |
415 | id = cf->can_id; | 402 | id = cf->can_id; |
416 | if (cf->can_id & CAN_RTR_FLAG) | 403 | rtr = cf->can_id & CAN_RTR_FLAG ? 0 : MSGCFG_DIR; |
417 | rtr = 0; | 404 | |
418 | else | 405 | cc770_write_reg(priv, msgobj[mo].ctrl0, |
419 | rtr = MSGCFG_DIR; | 406 | MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); |
420 | cc770_write_reg(priv, msgobj[mo].ctrl1, | 407 | cc770_write_reg(priv, msgobj[mo].ctrl1, |
421 | RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES); | 408 | RMTPND_RES | TXRQST_RES | CPUUPD_SET | NEWDAT_RES); |
422 | cc770_write_reg(priv, msgobj[mo].ctrl0, | 409 | |
423 | MSGVAL_SET | TXIE_SET | RXIE_RES | INTPND_RES); | ||
424 | if (id & CAN_EFF_FLAG) { | 410 | if (id & CAN_EFF_FLAG) { |
425 | id &= CAN_EFF_MASK; | 411 | id &= CAN_EFF_MASK; |
426 | cc770_write_reg(priv, msgobj[mo].config, | 412 | 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) | |||
439 | for (i = 0; i < dlc; i++) | 425 | for (i = 0; i < dlc; i++) |
440 | cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); | 426 | cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); |
441 | 427 | ||
442 | /* Store echo skb before starting the transfer */ | ||
443 | can_put_echo_skb(skb, dev, 0); | ||
444 | |||
445 | cc770_write_reg(priv, msgobj[mo].ctrl1, | 428 | cc770_write_reg(priv, msgobj[mo].ctrl1, |
446 | RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); | 429 | RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); |
430 | cc770_write_reg(priv, msgobj[mo].ctrl0, | ||
431 | MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC); | ||
432 | } | ||
447 | 433 | ||
448 | stats->tx_bytes += dlc; | 434 | static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) |
435 | { | ||
436 | struct cc770_priv *priv = netdev_priv(dev); | ||
437 | unsigned int mo = obj2msgobj(CC770_OBJ_TX); | ||
449 | 438 | ||
439 | if (can_dropped_invalid_skb(dev, skb)) | ||
440 | return NETDEV_TX_OK; | ||
450 | 441 | ||
451 | /* | 442 | netif_stop_queue(dev); |
452 | * HM: We had some cases of repeated IRQs so make sure the | 443 | |
453 | * INT is acknowledged I know it's already further up, but | 444 | if ((cc770_read_reg(priv, |
454 | * doing again fixed the issue | 445 | msgobj[mo].ctrl1) & TXRQST_UNC) == TXRQST_SET) { |
455 | */ | 446 | netdev_err(dev, "TX register is still occupied!\n"); |
456 | cc770_write_reg(priv, msgobj[mo].ctrl0, | 447 | return NETDEV_TX_BUSY; |
457 | MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); | 448 | } |
449 | |||
450 | priv->tx_skb = skb; | ||
451 | cc770_tx(dev, mo); | ||
458 | 452 | ||
459 | return NETDEV_TX_OK; | 453 | return NETDEV_TX_OK; |
460 | } | 454 | } |
@@ -680,19 +674,46 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) | |||
680 | struct cc770_priv *priv = netdev_priv(dev); | 674 | struct cc770_priv *priv = netdev_priv(dev); |
681 | struct net_device_stats *stats = &dev->stats; | 675 | struct net_device_stats *stats = &dev->stats; |
682 | unsigned int mo = obj2msgobj(o); | 676 | unsigned int mo = obj2msgobj(o); |
677 | struct can_frame *cf; | ||
678 | u8 ctrl1; | ||
679 | |||
680 | ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); | ||
683 | 681 | ||
684 | /* Nothing more to send, switch off interrupts */ | ||
685 | cc770_write_reg(priv, msgobj[mo].ctrl0, | 682 | cc770_write_reg(priv, msgobj[mo].ctrl0, |
686 | MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); | 683 | MSGVAL_RES | TXIE_RES | RXIE_RES | INTPND_RES); |
687 | /* | 684 | cc770_write_reg(priv, msgobj[mo].ctrl1, |
688 | * We had some cases of repeated IRQ so make sure the | 685 | RMTPND_RES | TXRQST_RES | MSGLST_RES | NEWDAT_RES); |
689 | * INT is acknowledged | 686 | |
687 | if (unlikely(!priv->tx_skb)) { | ||
688 | netdev_err(dev, "missing tx skb in tx interrupt\n"); | ||
689 | return; | ||
690 | } | ||
691 | |||
692 | if (unlikely(ctrl1 & MSGLST_SET)) { | ||
693 | stats->rx_over_errors++; | ||
694 | stats->rx_errors++; | ||
695 | } | ||
696 | |||
697 | /* When the CC770 is sending an RTR message and it receives a regular | ||
698 | * message that matches the id of the RTR message, it will overwrite the | ||
699 | * outgoing message in the TX register. When this happens we must | ||
700 | * process the received message and try to transmit the outgoing skb | ||
701 | * again. | ||
690 | */ | 702 | */ |
691 | cc770_write_reg(priv, msgobj[mo].ctrl0, | 703 | if (unlikely(ctrl1 & NEWDAT_SET)) { |
692 | MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); | 704 | cc770_rx(dev, mo, ctrl1); |
705 | cc770_tx(dev, mo); | ||
706 | return; | ||
707 | } | ||
693 | 708 | ||
709 | cf = (struct can_frame *)priv->tx_skb->data; | ||
710 | stats->tx_bytes += cf->can_dlc; | ||
694 | stats->tx_packets++; | 711 | stats->tx_packets++; |
712 | |||
713 | can_put_echo_skb(priv->tx_skb, dev, 0); | ||
695 | can_get_echo_skb(dev, 0); | 714 | can_get_echo_skb(dev, 0); |
715 | priv->tx_skb = NULL; | ||
716 | |||
696 | netif_wake_queue(dev); | 717 | netif_wake_queue(dev); |
697 | } | 718 | } |
698 | 719 | ||
@@ -804,6 +825,7 @@ struct net_device *alloc_cc770dev(int sizeof_priv) | |||
804 | priv->can.do_set_bittiming = cc770_set_bittiming; | 825 | priv->can.do_set_bittiming = cc770_set_bittiming; |
805 | priv->can.do_set_mode = cc770_set_mode; | 826 | priv->can.do_set_mode = cc770_set_mode; |
806 | priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; | 827 | priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; |
828 | priv->tx_skb = NULL; | ||
807 | 829 | ||
808 | memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags)); | 830 | memcpy(priv->obj_flags, cc770_obj_flags, sizeof(cc770_obj_flags)); |
809 | 831 | ||