Commit 47103039 authored by Larry Finger's avatar Larry Finger Committed by John W. Linville

[PATCH] bcm43xx: Fix problem with >1 GB RAM

Some versions of the bcm43xx chips only support 30-bit DMA, which means
that the descriptors and buffers must be in the first 1 GB of RAM. On
the i386 and x86_64 architectures with more than 1 GB RAM, an incorrect
assignment may occur. This patch ensures that the various DMA addresses
are within the capability of the chip. Testing has been limited to x86_64
as no one has an i386 system with more than 1 GB RAM.
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 192b775c
...@@ -766,6 +766,7 @@ struct bcm43xx_private { ...@@ -766,6 +766,7 @@ struct bcm43xx_private {
* This is currently always BCM43xx_BUSTYPE_PCI * This is currently always BCM43xx_BUSTYPE_PCI
*/ */
u8 bustype; u8 bustype;
u64 dma_mask;
u16 board_vendor; u16 board_vendor;
u16 board_type; u16 board_type;
......
...@@ -145,16 +145,14 @@ dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, ...@@ -145,16 +145,14 @@ dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,
int tx) int tx)
{ {
dma_addr_t dmaaddr; dma_addr_t dmaaddr;
int direction = PCI_DMA_FROMDEVICE;
if (tx) { if (tx)
dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, direction = PCI_DMA_TODEVICE;
buf, len,
DMA_TO_DEVICE); dmaaddr = pci_map_single(ring->bcm->pci_dev,
} else {
dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
buf, len, buf, len,
DMA_FROM_DEVICE); direction);
}
return dmaaddr; return dmaaddr;
} }
...@@ -166,13 +164,13 @@ void unmap_descbuffer(struct bcm43xx_dmaring *ring, ...@@ -166,13 +164,13 @@ void unmap_descbuffer(struct bcm43xx_dmaring *ring,
int tx) int tx)
{ {
if (tx) { if (tx) {
dma_unmap_single(&ring->bcm->pci_dev->dev, pci_unmap_single(ring->bcm->pci_dev,
addr, len, addr, len,
DMA_TO_DEVICE); PCI_DMA_TODEVICE);
} else { } else {
dma_unmap_single(&ring->bcm->pci_dev->dev, pci_unmap_single(ring->bcm->pci_dev,
addr, len, addr, len,
DMA_FROM_DEVICE); PCI_DMA_FROMDEVICE);
} }
} }
...@@ -183,8 +181,8 @@ void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, ...@@ -183,8 +181,8 @@ void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,
{ {
assert(!ring->tx); assert(!ring->tx);
dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
addr, len, DMA_FROM_DEVICE); addr, len, PCI_DMA_FROMDEVICE);
} }
static inline static inline
...@@ -194,8 +192,8 @@ void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, ...@@ -194,8 +192,8 @@ void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,
{ {
assert(!ring->tx); assert(!ring->tx);
dma_sync_single_for_device(&ring->bcm->pci_dev->dev, pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
addr, len, DMA_FROM_DEVICE); addr, len, PCI_DMA_TODEVICE);
} }
/* Unmap and free a descriptor buffer. */ /* Unmap and free a descriptor buffer. */
...@@ -214,17 +212,53 @@ void free_descriptor_buffer(struct bcm43xx_dmaring *ring, ...@@ -214,17 +212,53 @@ void free_descriptor_buffer(struct bcm43xx_dmaring *ring,
static int alloc_ringmemory(struct bcm43xx_dmaring *ring) static int alloc_ringmemory(struct bcm43xx_dmaring *ring)
{ {
struct device *dev = &(ring->bcm->pci_dev->dev); ring->descbase = pci_alloc_consistent(ring->bcm->pci_dev, BCM43xx_DMA_RINGMEMSIZE,
&(ring->dmabase));
ring->descbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
&(ring->dmabase), GFP_KERNEL);
if (!ring->descbase) { if (!ring->descbase) {
printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); /* Allocation may have failed due to pci_alloc_consistent
return -ENOMEM; insisting on use of GFP_DMA, which is more restrictive
than necessary... */
struct dma_desc *rx_ring;
dma_addr_t rx_ring_dma;
rx_ring = kzalloc(BCM43xx_DMA_RINGMEMSIZE, GFP_KERNEL);
if (!rx_ring)
goto out_err;
rx_ring_dma = pci_map_single(ring->bcm->pci_dev, rx_ring,
BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(rx_ring_dma) ||
rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) {
/* Sigh... */
if (!pci_dma_mapping_error(rx_ring_dma))
pci_unmap_single(ring->bcm->pci_dev,
rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
rx_ring_dma = pci_map_single(ring->bcm->pci_dev,
rx_ring, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(rx_ring_dma) ||
rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) {
assert(0);
if (!pci_dma_mapping_error(rx_ring_dma))
pci_unmap_single(ring->bcm->pci_dev,
rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE,
PCI_DMA_BIDIRECTIONAL);
goto out_err;
}
}
ring->descbase = rx_ring;
ring->dmabase = rx_ring_dma;
} }
memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE);
return 0; return 0;
out_err:
printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");
return -ENOMEM;
} }
static void free_ringmemory(struct bcm43xx_dmaring *ring) static void free_ringmemory(struct bcm43xx_dmaring *ring)
...@@ -407,6 +441,29 @@ static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, ...@@ -407,6 +441,29 @@ static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring,
if (unlikely(!skb)) if (unlikely(!skb))
return -ENOMEM; return -ENOMEM;
dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
/* This hardware bug work-around adapted from the b44 driver.
The chip may be unable to do PCI DMA to/from anything above 1GB */
if (pci_dma_mapping_error(dmaaddr) ||
dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) {
/* This one has 30-bit addressing... */
if (!pci_dma_mapping_error(dmaaddr))
pci_unmap_single(ring->bcm->pci_dev,
dmaaddr, ring->rx_buffersize,
PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
skb = __dev_alloc_skb(ring->rx_buffersize,GFP_DMA);
if (skb == NULL)
return -ENOMEM;
dmaaddr = pci_map_single(ring->bcm->pci_dev,
skb->data, ring->rx_buffersize,
PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(dmaaddr) ||
dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) {
assert(0);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
}
meta->skb = skb; meta->skb = skb;
meta->dmaaddr = dmaaddr; meta->dmaaddr = dmaaddr;
skb->dev = ring->bcm->net_dev; skb->dev = ring->bcm->net_dev;
...@@ -636,8 +693,10 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, ...@@ -636,8 +693,10 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm,
err = dmacontroller_setup(ring); err = dmacontroller_setup(ring);
if (err) if (err)
goto err_free_ringmemory; goto err_free_ringmemory;
return ring;
out: out:
printk(KERN_ERR PFX "Error in bcm43xx_setup_dmaring\n");
return ring; return ring;
err_free_ringmemory: err_free_ringmemory:
...@@ -705,30 +764,16 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) ...@@ -705,30 +764,16 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm)
struct bcm43xx_dmaring *ring; struct bcm43xx_dmaring *ring;
int err = -ENOMEM; int err = -ENOMEM;
int dma64 = 0; int dma64 = 0;
u64 mask = bcm43xx_get_supported_dma_mask(bcm);
int nobits;
if (mask == DMA_64BIT_MASK) { bcm->dma_mask = bcm43xx_get_supported_dma_mask(bcm);
if (bcm->dma_mask == DMA_64BIT_MASK)
dma64 = 1; dma64 = 1;
nobits = 64; err = pci_set_dma_mask(bcm->pci_dev, bcm->dma_mask);
} else if (mask == DMA_32BIT_MASK) if (err)
nobits = 32; goto no_dma;
else err = pci_set_consistent_dma_mask(bcm->pci_dev, bcm->dma_mask);
nobits = 30; if (err)
err = pci_set_dma_mask(bcm->pci_dev, mask); goto no_dma;
err |= pci_set_consistent_dma_mask(bcm->pci_dev, mask);
if (err) {
#ifdef CONFIG_BCM43XX_PIO
printk(KERN_WARNING PFX "DMA not supported on this device."
" Falling back to PIO.\n");
bcm->__using_pio = 1;
return -ENOSYS;
#else
printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "
"Please recompile the driver with PIO support.\n");
return -ENODEV;
#endif /* CONFIG_BCM43XX_PIO */
}
/* setup TX DMA channels. */ /* setup TX DMA channels. */
ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64); ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64);
...@@ -774,7 +819,9 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) ...@@ -774,7 +819,9 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm)
dma->rx_ring3 = ring; dma->rx_ring3 = ring;
} }
dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", nobits); dprintk(KERN_INFO PFX "%d-bit DMA initialized\n",
(bcm->dma_mask == DMA_64BIT_MASK) ? 64 :
(bcm->dma_mask == DMA_32BIT_MASK) ? 32 : 30);
err = 0; err = 0;
out: out:
return err; return err;
...@@ -800,7 +847,17 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) ...@@ -800,7 +847,17 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm)
err_destroy_tx0: err_destroy_tx0:
bcm43xx_destroy_dmaring(dma->tx_ring0); bcm43xx_destroy_dmaring(dma->tx_ring0);
dma->tx_ring0 = NULL; dma->tx_ring0 = NULL;
goto out; no_dma:
#ifdef CONFIG_BCM43XX_PIO
printk(KERN_WARNING PFX "DMA not supported on this device."
" Falling back to PIO.\n");
bcm->__using_pio = 1;
return -ENOSYS;
#else
printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "
"Please recompile the driver with PIO support.\n");
return -ENODEV;
#endif /* CONFIG_BCM43XX_PIO */
} }
/* Generate a cookie for the TX header. */ /* Generate a cookie for the TX header. */
...@@ -905,6 +962,7 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, ...@@ -905,6 +962,7 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_generic *desc;
struct bcm43xx_dmadesc_meta *meta; struct bcm43xx_dmadesc_meta *meta;
dma_addr_t dmaaddr; dma_addr_t dmaaddr;
struct sk_buff *bounce_skb;
assert(skb_shinfo(skb)->nr_frags == 0); assert(skb_shinfo(skb)->nr_frags == 0);
...@@ -924,9 +982,28 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, ...@@ -924,9 +982,28 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
skb->len - sizeof(struct bcm43xx_txhdr), skb->len - sizeof(struct bcm43xx_txhdr),
(cur_frag == 0), (cur_frag == 0),
generate_cookie(ring, slot)); generate_cookie(ring, slot));
dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) {
/* chip cannot handle DMA to/from > 1GB, use bounce buffer (copied from b44 driver) */
if (!dma_mapping_error(dmaaddr))
unmap_descbuffer(ring, dmaaddr, skb->len, 1);
bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC|GFP_DMA);
if (!bounce_skb)
return;
dmaaddr = map_descbuffer(ring, bounce_skb->data, bounce_skb->len, 1);
if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) {
if (!dma_mapping_error(dmaaddr))
unmap_descbuffer(ring, dmaaddr, skb->len, 1);
dev_kfree_skb_any(bounce_skb);
assert(0);
return;
}
memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
skb = bounce_skb;
}
meta->skb = skb; meta->skb = skb;
dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
meta->dmaaddr = dmaaddr; meta->dmaaddr = dmaaddr;
fill_descriptor(ring, desc, dmaaddr, fill_descriptor(ring, desc, dmaaddr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment