diff options
Diffstat (limited to 'drivers/atm')
-rw-r--r-- | drivers/atm/solos-pci.c | 20 |
1 files changed, 13 insertions, 7 deletions
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index acba08df5eb0..bf59c407fec9 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c | |||
@@ -100,6 +100,7 @@ struct solos_card { | |||
100 | void __iomem *config_regs; | 100 | void __iomem *config_regs; |
101 | void __iomem *buffers; | 101 | void __iomem *buffers; |
102 | int nr_ports; | 102 | int nr_ports; |
103 | int tx_mask; | ||
103 | struct pci_dev *dev; | 104 | struct pci_dev *dev; |
104 | struct atm_dev *atmdev[4]; | 105 | struct atm_dev *atmdev[4]; |
105 | struct tasklet_struct tlet; | 106 | struct tasklet_struct tlet; |
@@ -590,15 +591,13 @@ void solos_bh(unsigned long card_arg) | |||
590 | struct solos_card *card = (void *)card_arg; | 591 | struct solos_card *card = (void *)card_arg; |
591 | int port; | 592 | int port; |
592 | uint32_t card_flags; | 593 | uint32_t card_flags; |
593 | uint32_t tx_mask; | ||
594 | uint32_t rx_done = 0; | 594 | uint32_t rx_done = 0; |
595 | 595 | ||
596 | card_flags = ioread32(card->config_regs + FLAGS_ADDR); | 596 | card_flags = ioread32(card->config_regs + FLAGS_ADDR); |
597 | 597 | ||
598 | /* The TX bits are set if the channel is busy; clear if not. We want to | 598 | /* The TX bits are set if the channel is busy; clear if not. We want to |
599 | invoke fpga_tx() unless _all_ the bits for active channels are set */ | 599 | invoke fpga_tx() unless _all_ the bits for active channels are set */ |
600 | tx_mask = (1 << card->nr_ports) - 1; | 600 | if ((card_flags & card->tx_mask) != card->tx_mask) |
601 | if ((card_flags & tx_mask) != tx_mask) | ||
602 | fpga_tx(card); | 601 | fpga_tx(card); |
603 | 602 | ||
604 | for (port = 0; port < card->nr_ports; port++) { | 603 | for (port = 0; port < card->nr_ports; port++) { |
@@ -887,15 +886,20 @@ static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, | |||
887 | struct atm_vcc *vcc) | 886 | struct atm_vcc *vcc) |
888 | { | 887 | { |
889 | int old_len; | 888 | int old_len; |
889 | unsigned long flags; | ||
890 | 890 | ||
891 | SKB_CB(skb)->vcc = vcc; | 891 | SKB_CB(skb)->vcc = vcc; |
892 | 892 | ||
893 | spin_lock(&card->tx_queue_lock); | 893 | spin_lock_irqsave(&card->tx_queue_lock, flags); |
894 | old_len = skb_queue_len(&card->tx_queue[port]); | 894 | old_len = skb_queue_len(&card->tx_queue[port]); |
895 | skb_queue_tail(&card->tx_queue[port], skb); | 895 | skb_queue_tail(&card->tx_queue[port], skb); |
896 | spin_unlock(&card->tx_queue_lock); | 896 | if (!old_len) { |
897 | card->tx_mask |= (1 << port); | ||
898 | } | ||
899 | spin_unlock_irqrestore(&card->tx_queue_lock, flags); | ||
897 | 900 | ||
898 | /* If TX might need to be started, do so */ | 901 | /* Theoretically we could just schedule the tasklet here, but |
902 | that introduces latency we don't want -- it's noticeable */ | ||
899 | if (!old_len) | 903 | if (!old_len) |
900 | fpga_tx(card); | 904 | fpga_tx(card); |
901 | } | 905 | } |
@@ -911,7 +915,7 @@ static int fpga_tx(struct solos_card *card) | |||
911 | 915 | ||
912 | spin_lock_irqsave(&card->tx_lock, flags); | 916 | spin_lock_irqsave(&card->tx_lock, flags); |
913 | 917 | ||
914 | tx_pending = ioread32(card->config_regs + FLAGS_ADDR); | 918 | tx_pending = ioread32(card->config_regs + FLAGS_ADDR) & card->tx_mask; |
915 | 919 | ||
916 | dev_vdbg(&card->dev->dev, "TX Flags are %X\n", tx_pending); | 920 | dev_vdbg(&card->dev->dev, "TX Flags are %X\n", tx_pending); |
917 | 921 | ||
@@ -925,6 +929,8 @@ static int fpga_tx(struct solos_card *card) | |||
925 | 929 | ||
926 | spin_lock(&card->tx_queue_lock); | 930 | spin_lock(&card->tx_queue_lock); |
927 | skb = skb_dequeue(&card->tx_queue[port]); | 931 | skb = skb_dequeue(&card->tx_queue[port]); |
932 | if (!skb) | ||
933 | card->tx_mask &= ~(1 << port); | ||
928 | spin_unlock(&card->tx_queue_lock); | 934 | spin_unlock(&card->tx_queue_lock); |
929 | 935 | ||
930 | if (skb && !card->using_dma) { | 936 | if (skb && !card->using_dma) { |