summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2009-01-28 16:46:56 +1100
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-01-28 16:46:56 +1100
commit909372317e67bdbbfced5dab3ade3437e3f2b254 (patch)
treeafb45c7a2537df688b1a56bec9b8d11591a4cdae
parentc0fe30265a1fe3a69e0ce0d08b49de1dda9c1190 (diff)
downloadop-kernel-dev-909372317e67bdbbfced5dab3ade3437e3f2b254.zip
op-kernel-dev-909372317e67bdbbfced5dab3ade3437e3f2b254.tar.gz
solos: First attempt at DMA support
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/atm/solos-pci.c118
1 files changed, 90 insertions, 28 deletions
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index b7d4af3..63c9ad0 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -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);
OpenPOWER on IntegriCloud