solos: First attempt at DMA support
authorDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 28 Jan 2009 05:46:56 +0000 (16:46 +1100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Wed, 28 Jan 2009 05:46:56 +0000 (16:46 +1100)
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/atm/solos-pci.c

index b7d4af3..63c9ad0 100644 (file)
@@ -55,6 +55,8 @@
 #define FLASH_BUSY     0x60
 #define FPGA_MODE      0x5C
 #define FLASH_MODE     0x58
+#define TX_DMA_ADDR(port)      (0x40 + (4 * (port)))
+#define RX_DMA_ADDR(port)      (0x30 + (4 * (port)))
 
 #define DATA_RAM_SIZE  32768
 #define BUF_SIZE       4096
@@ -78,6 +80,14 @@ struct pkt_hdr {
        __le16 type;
 };
 
+struct solos_skb_cb {
+       struct atm_vcc *vcc;
+       uint32_t dma_addr;
+};
+
+
+#define SKB_CB(skb)            ((struct solos_skb_cb *)skb->cb)
+
 #define PKT_DATA       0
 #define PKT_COMMAND    1
 #define PKT_POPEN      3
@@ -98,8 +108,11 @@ struct solos_card {
        struct list_head param_queue;
        struct sk_buff_head tx_queue[4];
        struct sk_buff_head cli_queue[4];
+       struct sk_buff *tx_skb[4];
+       struct sk_buff *rx_skb[4];
        wait_queue_head_t param_wq;
        wait_queue_head_t fw_wq;
+       int using_dma;
 };
 
 
@@ -588,44 +601,64 @@ void solos_bh(unsigned long card_arg)
 
        for (port = 0; port < card->nr_ports; port++) {
                if (card_flags & (0x10 << port)) {
-                       struct pkt_hdr header;
+                       struct pkt_hdr _hdr, *header;
                        struct sk_buff *skb;
                        struct atm_vcc *vcc;
                        int size;
 
-                       rx_done |= 0x10 << port;
+                       if (card->using_dma) {
+                               skb = card->rx_skb[port];
+                               pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, skb->len,
+                                                PCI_DMA_FROMDEVICE);
+
+                               card->rx_skb[port] = alloc_skb(2048, GFP_ATOMIC);
+                               if (card->rx_skb[port]) {
+                                       SKB_CB(card->rx_skb[port])->dma_addr =
+                                               pci_map_single(card->dev, skb->data, skb->len,
+                                                              PCI_DMA_FROMDEVICE);
+                                       iowrite32(SKB_CB(card->rx_skb[port])->dma_addr,
+                                                 card->config_regs + RX_DMA_ADDR(port));
+                               }
+                               header = (void *)skb->data;
+                               size = le16_to_cpu(header->size);
+                               skb_put(skb, size + sizeof(*header));
+                               skb_pull(skb, sizeof(*header));
+                       } else {
+                               header = &_hdr;
 
-                       memcpy_fromio(&header, RX_BUF(card, port), sizeof(header));
+                               rx_done |= 0x10 << port;
 
-                       size = le16_to_cpu(header.size);
+                               memcpy_fromio(header, RX_BUF(card, port), sizeof(*header));
 
-                       skb = alloc_skb(size + 1, GFP_ATOMIC);
-                       if (!skb) {
-                               if (net_ratelimit())
-                                       dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
-                               continue;
-                       }
+                               size = le16_to_cpu(header->size);
 
-                       memcpy_fromio(skb_put(skb, size),
-                                     RX_BUF(card, port) + sizeof(header),
-                                     size);
+                               skb = alloc_skb(size + 1, GFP_ATOMIC);
+                               if (!skb) {
+                                       if (net_ratelimit())
+                                               dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
+                                       continue;
+                               }
 
+                               memcpy_fromio(skb_put(skb, size),
+                                             RX_BUF(card, port) + sizeof(*header),
+                                             size);
+                       }
                        if (atmdebug) {
                                dev_info(&card->dev->dev, "Received: device %d\n", port);
                                dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n",
-                                        size, le16_to_cpu(header.vpi),
-                                        le16_to_cpu(header.vci));
+                                        size, le16_to_cpu(header->vpi),
+                                        le16_to_cpu(header->vci));
                                print_buffer(skb);
                        }
 
-                       switch (le16_to_cpu(header.type)) {
+                       switch (le16_to_cpu(header->type)) {
                        case PKT_DATA:
-                               vcc = find_vcc(card->atmdev[port], le16_to_cpu(header.vpi),
-                                              le16_to_cpu(header.vci));
+                               vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi),
+                                              le16_to_cpu(header->vci));
                                if (!vcc) {
                                        if (net_ratelimit())
                                                dev_warn(&card->dev->dev, "Received packet for unknown VCI.VPI %d.%d on port %d\n",
-                                                        le16_to_cpu(header.vci), le16_to_cpu(header.vpi),
+                                                        le16_to_cpu(header->vci), le16_to_cpu(header->vpi),
                                                         port);
                                        continue;
                                }
@@ -839,7 +872,7 @@ static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
 {
        int old_len;
 
-       *(void **)skb->cb = vcc;
+       SKB_CB(skb)->vcc = vcc;
 
        spin_lock(&card->tx_queue_lock);
        old_len = skb_queue_len(&card->tx_queue[port]);
@@ -881,17 +914,37 @@ static int fpga_tx(struct solos_card *card)
                                         port);
                                print_buffer(skb);
                        }
-                       memcpy_toio(TX_BUF(card, port), skb->data, skb->len);
+                       if (card->using_dma) {
+                               if (card->tx_skb[port]) {
+                                       struct sk_buff *oldskb = card->tx_skb[port];
 
-                       vcc = *(void **)skb->cb;
+                                       pci_unmap_single(card->dev, SKB_CB(oldskb)->dma_addr,
+                                                        oldskb->len, PCI_DMA_TODEVICE);
 
-                       if (vcc) {
-                               atomic_inc(&vcc->stats->tx);
-                               solos_pop(vcc, skb);
-                       } else
-                               dev_kfree_skb_irq(skb);
+                                       vcc = SKB_CB(oldskb)->vcc;
 
-                       tx_started |= 1 << port; //Set TX full flag
+                                       if (vcc) {
+                                               atomic_inc(&vcc->stats->tx);
+                                               solos_pop(vcc, oldskb);
+                                       } else
+                                               dev_kfree_skb_irq(oldskb);
+                               }
+
+                               SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
+                                                              skb->len, PCI_DMA_TODEVICE);
+                               iowrite32(SKB_CB(skb)->dma_addr, card->config_regs + TX_DMA_ADDR(port));
+                       } else {
+                               memcpy_toio(TX_BUF(card, port), skb->data, skb->len);
+                               tx_started |= 1 << port; //Set TX full flag
+
+                               vcc = SKB_CB(skb)->vcc;
+
+                               if (vcc) {
+                                       atomic_inc(&vcc->stats->tx);
+                                       solos_pop(vcc, skb);
+                               } else
+                                       dev_kfree_skb_irq(skb);
+                       }
                }
        }
        if (tx_started)
@@ -999,6 +1052,12 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
                goto out;
        }
 
+       err = pci_set_dma_mask(dev, DMA_32BIT_MASK);
+       if (err) {
+               dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n");
+               goto out;
+       }
+
        err = pci_request_regions(dev, "solos");
        if (err) {
                dev_warn(&dev->dev, "Failed to request regions\n");
@@ -1035,6 +1094,9 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
        dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n",
                 major_ver, minor_ver, fpga_ver);
 
+       if (fpga_ver > 27)
+               card->using_dma = 1;
+
        card->nr_ports = 2; /* FIXME: Detect daughterboard */
 
        pci_set_drvdata(dev, card);