diff options
author | Larry Finger <Larry.Finger@lwfinger.net> | 2007-01-21 23:27:35 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2007-02-07 15:23:53 -0500 |
commit | 471030390d4561f430c9aea36e72d96bd2ee48f1 (patch) | |
tree | 8d9f3aa4618fc5e91da2fe25aaa0eac548d40714 /drivers/net/wireless | |
parent | 192b775cc811b0e9e0d174ffdd5a814794392482 (diff) |
[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: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_dma.c | 171 |
2 files changed, 125 insertions, 47 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index 8286678513b9..4168b1a807ef 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h | |||
@@ -766,6 +766,7 @@ struct bcm43xx_private { | |||
766 | * This is currently always BCM43xx_BUSTYPE_PCI | 766 | * This is currently always BCM43xx_BUSTYPE_PCI |
767 | */ | 767 | */ |
768 | u8 bustype; | 768 | u8 bustype; |
769 | u64 dma_mask; | ||
769 | 770 | ||
770 | u16 board_vendor; | 771 | u16 board_vendor; |
771 | u16 board_type; | 772 | u16 board_type; |
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c index 978ed099e285..6e0dc76400e5 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c | |||
@@ -145,16 +145,14 @@ dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, | |||
145 | int tx) | 145 | int tx) |
146 | { | 146 | { |
147 | dma_addr_t dmaaddr; | 147 | dma_addr_t dmaaddr; |
148 | int direction = PCI_DMA_FROMDEVICE; | ||
148 | 149 | ||
149 | if (tx) { | 150 | if (tx) |
150 | dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, | 151 | direction = PCI_DMA_TODEVICE; |
151 | buf, len, | 152 | |
152 | DMA_TO_DEVICE); | 153 | dmaaddr = pci_map_single(ring->bcm->pci_dev, |
153 | } else { | ||
154 | dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, | ||
155 | buf, len, | 154 | buf, len, |
156 | DMA_FROM_DEVICE); | 155 | direction); |
157 | } | ||
158 | 156 | ||
159 | return dmaaddr; | 157 | return dmaaddr; |
160 | } | 158 | } |
@@ -166,13 +164,13 @@ void unmap_descbuffer(struct bcm43xx_dmaring *ring, | |||
166 | int tx) | 164 | int tx) |
167 | { | 165 | { |
168 | if (tx) { | 166 | if (tx) { |
169 | dma_unmap_single(&ring->bcm->pci_dev->dev, | 167 | pci_unmap_single(ring->bcm->pci_dev, |
170 | addr, len, | 168 | addr, len, |
171 | DMA_TO_DEVICE); | 169 | PCI_DMA_TODEVICE); |
172 | } else { | 170 | } else { |
173 | dma_unmap_single(&ring->bcm->pci_dev->dev, | 171 | pci_unmap_single(ring->bcm->pci_dev, |
174 | addr, len, | 172 | addr, len, |
175 | DMA_FROM_DEVICE); | 173 | PCI_DMA_FROMDEVICE); |
176 | } | 174 | } |
177 | } | 175 | } |
178 | 176 | ||
@@ -183,8 +181,8 @@ void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, | |||
183 | { | 181 | { |
184 | assert(!ring->tx); | 182 | assert(!ring->tx); |
185 | 183 | ||
186 | dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, | 184 | pci_dma_sync_single_for_cpu(ring->bcm->pci_dev, |
187 | addr, len, DMA_FROM_DEVICE); | 185 | addr, len, PCI_DMA_FROMDEVICE); |
188 | } | 186 | } |
189 | 187 | ||
190 | static inline | 188 | static inline |
@@ -194,8 +192,8 @@ void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, | |||
194 | { | 192 | { |
195 | assert(!ring->tx); | 193 | assert(!ring->tx); |
196 | 194 | ||
197 | dma_sync_single_for_device(&ring->bcm->pci_dev->dev, | 195 | pci_dma_sync_single_for_cpu(ring->bcm->pci_dev, |
198 | addr, len, DMA_FROM_DEVICE); | 196 | addr, len, PCI_DMA_TODEVICE); |
199 | } | 197 | } |
200 | 198 | ||
201 | /* Unmap and free a descriptor buffer. */ | 199 | /* Unmap and free a descriptor buffer. */ |
@@ -214,17 +212,53 @@ void free_descriptor_buffer(struct bcm43xx_dmaring *ring, | |||
214 | 212 | ||
215 | static int alloc_ringmemory(struct bcm43xx_dmaring *ring) | 213 | static int alloc_ringmemory(struct bcm43xx_dmaring *ring) |
216 | { | 214 | { |
217 | struct device *dev = &(ring->bcm->pci_dev->dev); | 215 | ring->descbase = pci_alloc_consistent(ring->bcm->pci_dev, BCM43xx_DMA_RINGMEMSIZE, |
218 | 216 | &(ring->dmabase)); | |
219 | ring->descbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, | ||
220 | &(ring->dmabase), GFP_KERNEL); | ||
221 | if (!ring->descbase) { | 217 | if (!ring->descbase) { |
222 | printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); | 218 | /* Allocation may have failed due to pci_alloc_consistent |
223 | return -ENOMEM; | 219 | insisting on use of GFP_DMA, which is more restrictive |
220 | than necessary... */ | ||
221 | struct dma_desc *rx_ring; | ||
222 | dma_addr_t rx_ring_dma; | ||
223 | |||
224 | rx_ring = kzalloc(BCM43xx_DMA_RINGMEMSIZE, GFP_KERNEL); | ||
225 | if (!rx_ring) | ||
226 | goto out_err; | ||
227 | |||
228 | rx_ring_dma = pci_map_single(ring->bcm->pci_dev, rx_ring, | ||
229 | BCM43xx_DMA_RINGMEMSIZE, | ||
230 | PCI_DMA_BIDIRECTIONAL); | ||
231 | |||
232 | if (pci_dma_mapping_error(rx_ring_dma) || | ||
233 | rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) { | ||
234 | /* Sigh... */ | ||
235 | if (!pci_dma_mapping_error(rx_ring_dma)) | ||
236 | pci_unmap_single(ring->bcm->pci_dev, | ||
237 | rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE, | ||
238 | PCI_DMA_BIDIRECTIONAL); | ||
239 | rx_ring_dma = pci_map_single(ring->bcm->pci_dev, | ||
240 | rx_ring, BCM43xx_DMA_RINGMEMSIZE, | ||
241 | PCI_DMA_BIDIRECTIONAL); | ||
242 | if (pci_dma_mapping_error(rx_ring_dma) || | ||
243 | rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) { | ||
244 | assert(0); | ||
245 | if (!pci_dma_mapping_error(rx_ring_dma)) | ||
246 | pci_unmap_single(ring->bcm->pci_dev, | ||
247 | rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE, | ||
248 | PCI_DMA_BIDIRECTIONAL); | ||
249 | goto out_err; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | ring->descbase = rx_ring; | ||
254 | ring->dmabase = rx_ring_dma; | ||
224 | } | 255 | } |
225 | memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); | 256 | memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); |
226 | 257 | ||
227 | return 0; | 258 | return 0; |
259 | out_err: | ||
260 | printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); | ||
261 | return -ENOMEM; | ||
228 | } | 262 | } |
229 | 263 | ||
230 | static void free_ringmemory(struct bcm43xx_dmaring *ring) | 264 | static void free_ringmemory(struct bcm43xx_dmaring *ring) |
@@ -407,6 +441,29 @@ static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, | |||
407 | if (unlikely(!skb)) | 441 | if (unlikely(!skb)) |
408 | return -ENOMEM; | 442 | return -ENOMEM; |
409 | dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); | 443 | dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); |
444 | /* This hardware bug work-around adapted from the b44 driver. | ||
445 | The chip may be unable to do PCI DMA to/from anything above 1GB */ | ||
446 | if (pci_dma_mapping_error(dmaaddr) || | ||
447 | dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) { | ||
448 | /* This one has 30-bit addressing... */ | ||
449 | if (!pci_dma_mapping_error(dmaaddr)) | ||
450 | pci_unmap_single(ring->bcm->pci_dev, | ||
451 | dmaaddr, ring->rx_buffersize, | ||
452 | PCI_DMA_FROMDEVICE); | ||
453 | dev_kfree_skb_any(skb); | ||
454 | skb = __dev_alloc_skb(ring->rx_buffersize,GFP_DMA); | ||
455 | if (skb == NULL) | ||
456 | return -ENOMEM; | ||
457 | dmaaddr = pci_map_single(ring->bcm->pci_dev, | ||
458 | skb->data, ring->rx_buffersize, | ||
459 | PCI_DMA_FROMDEVICE); | ||
460 | if (pci_dma_mapping_error(dmaaddr) || | ||
461 | dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) { | ||
462 | assert(0); | ||
463 | dev_kfree_skb_any(skb); | ||
464 | return -ENOMEM; | ||
465 | } | ||
466 | } | ||
410 | meta->skb = skb; | 467 | meta->skb = skb; |
411 | meta->dmaaddr = dmaaddr; | 468 | meta->dmaaddr = dmaaddr; |
412 | skb->dev = ring->bcm->net_dev; | 469 | skb->dev = ring->bcm->net_dev; |
@@ -636,8 +693,10 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, | |||
636 | err = dmacontroller_setup(ring); | 693 | err = dmacontroller_setup(ring); |
637 | if (err) | 694 | if (err) |
638 | goto err_free_ringmemory; | 695 | goto err_free_ringmemory; |
696 | return ring; | ||
639 | 697 | ||
640 | out: | 698 | out: |
699 | printk(KERN_ERR PFX "Error in bcm43xx_setup_dmaring\n"); | ||
641 | return ring; | 700 | return ring; |
642 | 701 | ||
643 | err_free_ringmemory: | 702 | err_free_ringmemory: |
@@ -705,30 +764,16 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) | |||
705 | struct bcm43xx_dmaring *ring; | 764 | struct bcm43xx_dmaring *ring; |
706 | int err = -ENOMEM; | 765 | int err = -ENOMEM; |
707 | int dma64 = 0; | 766 | int dma64 = 0; |
708 | u64 mask = bcm43xx_get_supported_dma_mask(bcm); | ||
709 | int nobits; | ||
710 | 767 | ||
711 | if (mask == DMA_64BIT_MASK) { | 768 | bcm->dma_mask = bcm43xx_get_supported_dma_mask(bcm); |
769 | if (bcm->dma_mask == DMA_64BIT_MASK) | ||
712 | dma64 = 1; | 770 | dma64 = 1; |
713 | nobits = 64; | 771 | err = pci_set_dma_mask(bcm->pci_dev, bcm->dma_mask); |
714 | } else if (mask == DMA_32BIT_MASK) | 772 | if (err) |
715 | nobits = 32; | 773 | goto no_dma; |
716 | else | 774 | err = pci_set_consistent_dma_mask(bcm->pci_dev, bcm->dma_mask); |
717 | nobits = 30; | 775 | if (err) |
718 | err = pci_set_dma_mask(bcm->pci_dev, mask); | 776 | goto no_dma; |
719 | err |= pci_set_consistent_dma_mask(bcm->pci_dev, mask); | ||
720 | if (err) { | ||
721 | #ifdef CONFIG_BCM43XX_PIO | ||
722 | printk(KERN_WARNING PFX "DMA not supported on this device." | ||
723 | " Falling back to PIO.\n"); | ||
724 | bcm->__using_pio = 1; | ||
725 | return -ENOSYS; | ||
726 | #else | ||
727 | printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " | ||
728 | "Please recompile the driver with PIO support.\n"); | ||
729 | return -ENODEV; | ||
730 | #endif /* CONFIG_BCM43XX_PIO */ | ||
731 | } | ||
732 | 777 | ||
733 | /* setup TX DMA channels. */ | 778 | /* setup TX DMA channels. */ |
734 | ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64); | 779 | ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64); |
@@ -774,7 +819,9 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) | |||
774 | dma->rx_ring3 = ring; | 819 | dma->rx_ring3 = ring; |
775 | } | 820 | } |
776 | 821 | ||
777 | dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", nobits); | 822 | dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", |
823 | (bcm->dma_mask == DMA_64BIT_MASK) ? 64 : | ||
824 | (bcm->dma_mask == DMA_32BIT_MASK) ? 32 : 30); | ||
778 | err = 0; | 825 | err = 0; |
779 | out: | 826 | out: |
780 | return err; | 827 | return err; |
@@ -800,7 +847,17 @@ err_destroy_tx1: | |||
800 | err_destroy_tx0: | 847 | err_destroy_tx0: |
801 | bcm43xx_destroy_dmaring(dma->tx_ring0); | 848 | bcm43xx_destroy_dmaring(dma->tx_ring0); |
802 | dma->tx_ring0 = NULL; | 849 | dma->tx_ring0 = NULL; |
803 | goto out; | 850 | no_dma: |
851 | #ifdef CONFIG_BCM43XX_PIO | ||
852 | printk(KERN_WARNING PFX "DMA not supported on this device." | ||
853 | " Falling back to PIO.\n"); | ||
854 | bcm->__using_pio = 1; | ||
855 | return -ENOSYS; | ||
856 | #else | ||
857 | printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " | ||
858 | "Please recompile the driver with PIO support.\n"); | ||
859 | return -ENODEV; | ||
860 | #endif /* CONFIG_BCM43XX_PIO */ | ||
804 | } | 861 | } |
805 | 862 | ||
806 | /* Generate a cookie for the TX header. */ | 863 | /* Generate a cookie for the TX header. */ |
@@ -905,6 +962,7 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, | |||
905 | struct bcm43xx_dmadesc_generic *desc; | 962 | struct bcm43xx_dmadesc_generic *desc; |
906 | struct bcm43xx_dmadesc_meta *meta; | 963 | struct bcm43xx_dmadesc_meta *meta; |
907 | dma_addr_t dmaaddr; | 964 | dma_addr_t dmaaddr; |
965 | struct sk_buff *bounce_skb; | ||
908 | 966 | ||
909 | assert(skb_shinfo(skb)->nr_frags == 0); | 967 | assert(skb_shinfo(skb)->nr_frags == 0); |
910 | 968 | ||
@@ -924,9 +982,28 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, | |||
924 | skb->len - sizeof(struct bcm43xx_txhdr), | 982 | skb->len - sizeof(struct bcm43xx_txhdr), |
925 | (cur_frag == 0), | 983 | (cur_frag == 0), |
926 | generate_cookie(ring, slot)); | 984 | generate_cookie(ring, slot)); |
985 | dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); | ||
986 | if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) { | ||
987 | /* chip cannot handle DMA to/from > 1GB, use bounce buffer (copied from b44 driver) */ | ||
988 | if (!dma_mapping_error(dmaaddr)) | ||
989 | unmap_descbuffer(ring, dmaaddr, skb->len, 1); | ||
990 | bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC|GFP_DMA); | ||
991 | if (!bounce_skb) | ||
992 | return; | ||
993 | dmaaddr = map_descbuffer(ring, bounce_skb->data, bounce_skb->len, 1); | ||
994 | if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) { | ||
995 | if (!dma_mapping_error(dmaaddr)) | ||
996 | unmap_descbuffer(ring, dmaaddr, skb->len, 1); | ||
997 | dev_kfree_skb_any(bounce_skb); | ||
998 | assert(0); | ||
999 | return; | ||
1000 | } | ||
1001 | memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); | ||
1002 | dev_kfree_skb_any(skb); | ||
1003 | skb = bounce_skb; | ||
1004 | } | ||
927 | 1005 | ||
928 | meta->skb = skb; | 1006 | meta->skb = skb; |
929 | dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); | ||
930 | meta->dmaaddr = dmaaddr; | 1007 | meta->dmaaddr = dmaaddr; |
931 | 1008 | ||
932 | fill_descriptor(ring, desc, dmaaddr, | 1009 | fill_descriptor(ring, desc, dmaaddr, |