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

[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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -766,6 +766,7 @@ struct bcm43xx_private {
	 * This is currently always BCM43xx_BUSTYPE_PCI
	 */
	u8 bustype;
	u64 dma_mask;

	u16 board_vendor;
	u16 board_type;
+124 −47
Original line number Diff line number Diff line
@@ -145,16 +145,14 @@ dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,
			  int tx)
{
	dma_addr_t dmaaddr;
	int direction = PCI_DMA_FROMDEVICE;

	if (tx) {
		dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
					 buf, len,
					 DMA_TO_DEVICE);
	} else {
		dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
	if (tx)
		direction = PCI_DMA_TODEVICE;

	dmaaddr = pci_map_single(ring->bcm->pci_dev,
					 buf, len,
					 DMA_FROM_DEVICE);
	}
					 direction);

	return dmaaddr;
}
@@ -166,13 +164,13 @@ void unmap_descbuffer(struct bcm43xx_dmaring *ring,
		      int tx)
{
	if (tx) {
		dma_unmap_single(&ring->bcm->pci_dev->dev,
		pci_unmap_single(ring->bcm->pci_dev,
				 addr, len,
				 DMA_TO_DEVICE);
				 PCI_DMA_TODEVICE);
	} else {
		dma_unmap_single(&ring->bcm->pci_dev->dev,
		pci_unmap_single(ring->bcm->pci_dev,
				 addr, len,
				 DMA_FROM_DEVICE);
				 PCI_DMA_FROMDEVICE);
	}
}

@@ -183,8 +181,8 @@ void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,
{
	assert(!ring->tx);

	dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev,
				addr, len, DMA_FROM_DEVICE);
	pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
				    addr, len, PCI_DMA_FROMDEVICE);
}

static inline
@@ -194,8 +192,8 @@ void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,
{
	assert(!ring->tx);

	dma_sync_single_for_device(&ring->bcm->pci_dev->dev,
				   addr, len, DMA_FROM_DEVICE);
	pci_dma_sync_single_for_cpu(ring->bcm->pci_dev,
				    addr, len, PCI_DMA_TODEVICE);
}

/* Unmap and free a descriptor buffer. */
@@ -214,17 +212,53 @@ void free_descriptor_buffer(struct bcm43xx_dmaring *ring,

static int alloc_ringmemory(struct bcm43xx_dmaring *ring)
{
	struct device *dev = &(ring->bcm->pci_dev->dev);

	ring->descbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
					    &(ring->dmabase), GFP_KERNEL);
	ring->descbase = pci_alloc_consistent(ring->bcm->pci_dev, BCM43xx_DMA_RINGMEMSIZE,
					    &(ring->dmabase));
	if (!ring->descbase) {
		printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");
		return -ENOMEM;
		/* Allocation may have failed due to pci_alloc_consistent
		   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);

	return 0;
out_err:
	printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");
	return -ENOMEM;
}

static void free_ringmemory(struct bcm43xx_dmaring *ring)
@@ -407,6 +441,29 @@ static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring,
	if (unlikely(!skb))
		return -ENOMEM;
	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->dmaaddr = dmaaddr;
	skb->dev = ring->bcm->net_dev;
@@ -636,8 +693,10 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm,
	err = dmacontroller_setup(ring);
	if (err)
		goto err_free_ringmemory;
	return ring;

out:
	printk(KERN_ERR PFX "Error in bcm43xx_setup_dmaring\n");
	return ring;

err_free_ringmemory:
@@ -705,30 +764,16 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm)
	struct bcm43xx_dmaring *ring;
	int err = -ENOMEM;
	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;
		nobits = 64;
	} else if (mask == DMA_32BIT_MASK)
		nobits = 32;
	else
		nobits = 30;
	err = pci_set_dma_mask(bcm->pci_dev, mask);
	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 */
	}
	err = pci_set_dma_mask(bcm->pci_dev, bcm->dma_mask);
	if (err)
		goto no_dma;
	err = pci_set_consistent_dma_mask(bcm->pci_dev, bcm->dma_mask);
	if (err)
		goto no_dma;

	/* setup TX DMA channels. */
	ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64);
@@ -774,7 +819,9 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm)
		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;
out:
	return err;
@@ -800,7 +847,17 @@ err_destroy_tx1:
err_destroy_tx0:
	bcm43xx_destroy_dmaring(dma->tx_ring0);
	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. */
@@ -905,6 +962,7 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
	struct bcm43xx_dmadesc_generic *desc;
	struct bcm43xx_dmadesc_meta *meta;
	dma_addr_t dmaaddr;
	struct sk_buff *bounce_skb;

	assert(skb_shinfo(skb)->nr_frags == 0);

@@ -924,9 +982,28 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring,
			       skb->len - sizeof(struct bcm43xx_txhdr),
			       (cur_frag == 0),
			       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;
	dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
	meta->dmaaddr = dmaaddr;

	fill_descriptor(ring, desc, dmaaddr,