/* Broadcom BCM43xx wireless driver DMA ringbuffer and descriptor allocation/management Copyright (c) 2005, 2006 Michael Buesch Some code in this file is derived from the b44.c driver Copyright (C) 2002 David S. Miller Copyright (C) Pekka Pietikainen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "bcm43xx.h" #include "bcm43xx_dma.h" #include "bcm43xx_main.h" #include "bcm43xx_debugfs.h" #include "bcm43xx_power.h" #include "bcm43xx_xmit.h" #include #include #include #include static inline int free_slots(struct bcm43xx_dmaring *ring) { return (ring->nr_slots - ring->used_slots); } static inline int next_slot(struct bcm43xx_dmaring *ring, int slot) { assert(slot >= -1 && slot <= ring->nr_slots - 1); if (slot == ring->nr_slots - 1) return 0; return slot + 1; } static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot) { assert(slot >= 0 && slot <= ring->nr_slots - 1); if (slot == 0) return ring->nr_slots - 1; return slot - 1; } /* Request a slot for usage. */ static inline int request_slot(struct bcm43xx_dmaring *ring) { int slot; assert(ring->tx); assert(!ring->suspended); assert(free_slots(ring) != 0); slot = next_slot(ring, ring->current_slot); ring->current_slot = slot; ring->used_slots++; /* Check the number of available slots and suspend TX, * if we are running low on free slots. */ if (unlikely(free_slots(ring) < ring->suspend_mark)) { netif_stop_queue(ring->bcm->net_dev); ring->suspended = 1; } #ifdef CONFIG_BCM43XX_DEBUG if (ring->used_slots > ring->max_used_slots) ring->max_used_slots = ring->used_slots; #endif /* CONFIG_BCM43XX_DEBUG*/ return slot; } /* Return a slot to the free slots. */ static inline void return_slot(struct bcm43xx_dmaring *ring, int slot) { assert(ring->tx); ring->used_slots--; /* Check if TX is suspended and check if we have * enough free slots to resume it again. */ if (unlikely(ring->suspended)) { if (free_slots(ring) >= ring->resume_mark) { ring->suspended = 0; netif_wake_queue(ring->bcm->net_dev); } } } u16 bcm43xx_dmacontroller_base(int dma64bit, int controller_idx) { static const u16 map64[] = { BCM43xx_MMIO_DMA64_BASE0, BCM43xx_MMIO_DMA64_BASE1, BCM43xx_MMIO_DMA64_BASE2, BCM43xx_MMIO_DMA64_BASE3, BCM43xx_MMIO_DMA64_BASE4, BCM43xx_MMIO_DMA64_BASE5, }; static const u16 map32[] = { BCM43xx_MMIO_DMA32_BASE0, BCM43xx_MMIO_DMA32_BASE1, BCM43xx_MMIO_DMA32_BASE2, BCM43xx_MMIO_DMA32_BASE3, BCM43xx_MMIO_DMA32_BASE4, BCM43xx_MMIO_DMA32_BASE5, }; if (dma64bit) { assert(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map64)); return map64[controller_idx]; } assert(controller_idx >= 0 && controller_idx < ARRAY_SIZE(map32)); return map32[controller_idx]; } static inline dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, unsigned char *buf, size_t len, int tx) { dma_addr_t dmaaddr; 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, buf, len, DMA_FROM_DEVICE); } return dmaaddr; } static inline void unmap_descbuffer(struct bcm43xx_dmaring *ring, dma_addr_t addr, size_t len, int tx) { if (tx) { dma_unmap_single(&ring->bcm->pci_dev->dev, addr, len, DMA_TO_DEVICE); } else { dma_unmap_single(&ring->bcm->pci_dev->dev, addr, len, DMA_FROM_DEVICE); } } static inline void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, dma_addr_t addr, size_t len) { assert(!ring->tx); dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, addr, len, DMA_FROM_DEVICE); } static inline void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, dma_addr_t addr, size_t len) { assert(!ring->tx); dma_sync_single_for_device(&ring->bcm->pci_dev->dev, addr, len, DMA_FROM_DEVICE); } /* Unmap and free a descriptor buffer. */ static inline void free_descriptor_buffer(struct bcm43xx_dmaring *ring, struct bcm43xx_dmadesc_meta *meta, int irq_context) { assert(meta->skb); if (irq_context) dev_kfree_skb_irq(meta->skb); else dev_kfree_skb(meta->skb); meta->skb = NULL; } 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); if (!ring->descbase) { printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); return -ENOMEM; } memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); return 0; } static void free_ringmemory(struct bcm43xx_dmaring *ring) { struct device *dev = &(ring->bcm->pci_dev->dev); dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, ring->descbase, ring->dmabase); } /* Reset the RX DMA channel */ int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, u16 mmio_base, int dma64) { int i; u32 value; u16 offset; offset = dma64 ? BCM43xx_DMA64_RXCTL : BCM43xx_DMA32_RXCTL; bcm43xx_write32(bcm, mmio_base + offset, 0); for (i = 0; i < 1000; i++) { offset = dma64 ? BCM43xx_DMA64_RXSTATUS : BCM43xx_DMA32_RXSTATUS; value = bcm43xx_read32(bcm, mmio_base + offset); if (dma64) { value &= BCM43xx_DMA64_RXSTAT; if (value == BCM43xx_DMA64_RXSTAT_DISABLED) { i = -1; break; } } else { value &= BCM43xx_DMA32_RXSTATE; if (value == BCM43xx_DMA32_RXSTAT_DISABLED) { i = -1; break; } } udelay(10); } if (i != -1) { printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n"); return -ENODEV; } return 0; } /* Reset the RX DMA channel */ int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, u16 mmio_base, int dma64) { int i; u32 value; u16 offset; for (i = 0; i < 1000; i++) { offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS; value = bcm43xx_read32(bcm, mmio_base + offset); if (dma64) { value &= BCM43xx_DMA64_TXSTAT; if (value == BCM43xx_DMA64_TXSTAT_DISABLED || value == BCM43xx_DMA64_TXSTAT_IDLEWAIT || value == BCM43xx_DMA64_TXSTAT_STOPPED) break; } else { value &= BCM43xx_DMA32_TXSTATE; if (value == BCM43xx_DMA32_TXSTAT_DISABLED || value == BCM43xx_DMA32_TXSTAT_IDLEWAIT || value == BCM43xx_DMA32_TXSTAT_STOPPED) break; } udelay(10); } offset = dma64 ? BCM43xx_DMA64_TXCTL : BCM43xx_DMA32_TXCTL; bcm43xx_write32(bcm, mmio_base + offset, 0); for (i = 0; i < 1000; i++) { offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS; value = bcm43xx_read32(bcm, mmio_base + offset); if (dma64) { value &= BCM43xx_DMA64_TXSTAT; if (value == BCM43xx_DMA64_TXSTAT_DISABLED) { i = -1; break; } } else { value &= BCM43xx_DMA32_TXSTATE; if (value == BCM43xx_DMA32_TXSTAT_DISABLED) { i = -1; break; } } udelay(10); } if (i != -1) { printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n"); return -ENODEV; } /* ensure the reset is completed. */ udelay(300); return 0; } static void fill_descriptor(struct bcm43xx_dmaring *ring, struct bcm43xx_dmadesc_generic *desc, dma_addr_t dmaaddr, u16 bufsize, int start, int end, int irq) { int slot; slot = bcm43xx_dma_desc2idx(ring, desc); assert(slot >= 0 && slot < ring->nr_slots); if (ring->dma64) { u32 ctl0 = 0, ctl1 = 0; u32 addrlo, addrhi; u32 addrext; addrlo = (u32)(dmaaddr & 0xFFFFFFFF); addrhi = (((u64)dmaaddr >> 32) & ~BCM43xx_DMA64_ROUTING); addrext = (((u64)dmaaddr >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT); addrhi |= ring->routing; if (slot == ring->nr_slots - 1) ctl0 |= BCM43xx_DMA64_DCTL0_DTABLEEND; if (start) ctl0 |= BCM43xx_DMA64_DCTL0_FRAMESTART; if (end) ctl0 |= BCM43xx_DMA64_DCTL0_FRAMEEND; if (irq) ctl0 |= BCM43xx_DMA64_DCTL0_IRQ; ctl1 |= (bufsize - ring->frameoffset) & BCM43xx_DMA64_DCTL1_BYTECNT; ctl1 |= (addrext << BCM43xx_DMA64_DCTL1_ADDREXT_SHIFT) & BCM43xx_DMA64_DCTL1_ADDREXT_MASK; desc->dma64.control0 = cpu_to_le32(ctl0); desc->dma64.control1 = cpu_to_le32(ctl1); desc->dma64.address_low = cpu_to_le32(addrlo); desc->dma64.address_high = cpu_to_le32(addrhi); } else { u32 ctl; u32 addr; u32 addrext; addr = (u32)(dmaaddr & ~BCM43xx_DMA32_ROUTING); addrext = (u32)(dmaaddr & BCM43xx_DMA32_ROUTING) >> BCM43xx_DMA32_ROUTING_SHIFT; addr |= ring->routing; ctl = (bufsize - ring->frameoffset) & BCM43xx_DMA32_DCTL_BYTECNT; if (slot == ring->nr_slots - 1) ctl |= BCM43xx_DMA32_DCTL_DTABLEEND; if (start) ctl |= BCM43xx_DMA32_DCTL_FRAMESTART; if (end) ctl |= BCM43xx_DMA32_DCTL_FRAMEEND; if (irq) ctl |= BCM43xx_DMA32_DCTL_IRQ; ctl |= (addrext << BCM43xx_DMA32_DCTL_ADDREXT_SHIFT) & BCM43xx_DMA32_DCTL_ADDREXT_MASK; desc->dma32.control = cpu_to_le32(ctl); desc->dma32.address = cpu_to_le32(addr); } } static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, struct bcm43xx_dmadesc_generic *desc, struct bcm43xx_dmadesc_meta *meta, gfp_t gfp_flags) { struct bcm43xx_rxhdr *rxhdr; struct bcm43xx_hwxmitstatus *xmitstat; dma_addr_t dmaaddr; struct sk_buff *skb; assert(!ring->tx); skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); if (unlikely(!skb)) return -ENOMEM; dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); meta->skb = skb; meta->dmaaddr = dmaaddr; skb->dev = ring->bcm->net_dev; fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); rxhdr = (struct bcm43xx_rxhdr *)(skb->data); rxhdr->frame_length = 0; rxhdr->flags1 = 0; xmitstat = (struct bcm43xx_hwxmitstatus *)(skb->data); xmitstat->cookie = 0; return 0; } /* Allocate the initial descbuffers. * This is used for an RX ring only. */ static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring) { int i, err = -ENOMEM; struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; for (i = 0; i < ring->nr_slots; i++) { desc = bcm43xx_dma_idx2desc(ring, i, &meta); err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); if (err) goto err_unwind; } mb(); ring->used_slots = ring->nr_slots; err = 0; out: return err; err_unwind: for (i--; i >= 0; i--) { desc = bcm43xx_dma_idx2desc(ring, i, &meta); unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); dev_kfree_skb(meta->skb); } goto out; } /* Do initial setup of the DMA controller. * Reset the controller, write the ring busaddress * and switch the "enable" bit on. */ static int dmacontroller_setup(struct bcm43xx_dmaring *ring) { int err = 0; u32 value; u32 addrext; if (ring->tx) { if (ring->dma64) { u64 ringbase = (u64)(ring->dmabase); addrext = ((ringbase >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT); value = BCM43xx_DMA64_TXENABLE; value |= (addrext << BCM43xx_DMA64_TXADDREXT_SHIFT) & BCM43xx_DMA64_TXADDREXT_MASK; bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, value); bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO, (ringbase & 0xFFFFFFFF)); bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI, ((ringbase >> 32) & ~BCM43xx_DMA64_ROUTING) | ring->routing); } else { u32 ringbase = (u32)(ring->dmabase); addrext = (ringbase >> BCM43xx_DMA32_ROUTING_SHIFT); value = BCM43xx_DMA32_TXENABLE; value |= (addrext << BCM43xx_DMA32_TXADDREXT_SHIFT) & BCM43xx_DMA32_TXADDREXT_MASK; bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, value); bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING, (ringbase & ~BCM43xx_DMA32_ROUTING) | ring->routing); } } else { err = alloc_initial_descbuffers(ring); if (err) goto out; if (ring->dma64) { u64 ringbase = (u64)(ring->dmabase); addrext = ((ringbase >> 32) >> BCM43xx_DMA64_ROUTING_SHIFT); value = (ring->frameoffset << BCM43xx_DMA64_RXFROFF_SHIFT); value |= BCM43xx_DMA64_RXENABLE; value |= (addrext << BCM43xx_DMA64_RXADDREXT_SHIFT) & BCM43xx_DMA64_RXADDREXT_MASK; bcm43xx_dma_write(ring, BCM43xx_DMA64_RXCTL, value); bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO, (ringbase & 0xFFFFFFFF)); bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI, ((ringbase >> 32) & ~BCM43xx_DMA64_ROUTING) | ring->routing); bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX, 200); } else { u32 ringbase = (u32)(ring->dmabase); addrext = (ringbase >> BCM43xx_DMA32_ROUTING_SHIFT); value = (ring->frameoffset << BCM43xx_DMA32_RXFROFF_SHIFT); value |= BCM43xx_DMA32_RXENABLE; value |= (addrext << BCM43xx_DMA32_RXADDREXT_SHIFT) & BCM43xx_DMA32_RXADDREXT_MASK; bcm43xx_dma_write(ring, BCM43xx_DMA32_RXCTL, value); bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING, (ringbase & ~BCM43xx_DMA32_ROUTING) | ring->routing); bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX, 200); } } out: return err; } /* Shutdown the DMA controller. */ static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring) { if (ring->tx) { bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base, ring->dma64); if (ring->dma64) { bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO, 0); bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI, 0); } else bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING, 0); } else { bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base, ring->dma64); if (ring->dma64) { bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO, 0); bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI, 0); } else bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING, 0); } } static void free_all_descbuffers(struct bcm43xx_dmaring *ring) { struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; int i; if (!ring->used_slots) return; for (i = 0; i < ring->nr_slots; i++) { desc = bcm43xx_dma_idx2desc(ring, i, &meta); if (!meta->skb) { assert(ring->tx); continue; } if (ring->tx) { unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); } else { unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); } free_descriptor_buffer(ring, meta, 0); } } /* Main initialization function. */ static struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, int controller_index, int for_tx, int dma64) { struct bcm43xx_dmaring *ring; int err; int nr_slots; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out; nr_slots = BCM43xx_RXRING_SLOTS; if (for_tx) nr_slots = BCM43xx_TXRING_SLOTS; ring->meta = kcalloc(nr_slots, sizeof(struct bcm43xx_dmadesc_meta), GFP_KERNEL); if (!ring->meta) goto err_kfree_ring; ring->routing = BCM43xx_DMA32_CLIENTTRANS; if (dma64) ring->routing = BCM43xx_DMA64_CLIENTTRANS; #ifdef CONFIG_BCM947XX if (bcm->pci_dev->bus->number == 0) ring->routing = dma64 ? BCM43xx_DMA64_NOTRANS : BCM43xx_DMA32_NOTRANS; #endif ring->bcm = bcm; ring->nr_slots = nr_slots; ring->suspend_mark = ring->nr_slots * BCM43xx_TXSUSPEND_PERCENT / 100; ring->resume_mark = ring->nr_slots * BCM43xx_TXRESUME_PERCENT / 100; assert(ring->suspend_mark < ring->resume_mark); ring->mmio_base = bcm43xx_dmacontroller_base(dma64, controller_index); ring->index = controller_index; ring->dma64 = !!dma64; if (for_tx) { ring->tx = 1; ring->current_slot = -1; } else { if (ring->index == 0) { ring->rx_buffersize = BCM43xx_DMA0_RX_BUFFERSIZE; ring->frameoffset = BCM43xx_DMA0_RX_FRAMEOFFSET; } else if (ring->index == 3) { ring->rx_buffersize = BCM43xx_DMA3_RX_BUFFERSIZE; ring->frameoffset = BCM43xx_DMA3_RX_FRAMEOFFSET; } else assert(0); } err = alloc_ringmemory(ring); if (err) goto err_kfree_meta; err = dmacontroller_setup(ring); if (err) goto err_free_ringmemory; out: return ring; err_free_ringmemory: free_ringmemory(ring); err_kfree_meta: kfree(ring->meta); err_kfree_ring: kfree(ring); ring = NULL; goto out; } /* Main cleanup function. */ static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring) { if (!ring) return; dprintk(KERN_INFO PFX "DMA-%s 0x%04X (%s) max used slots: %d/%d\n", (ring->dma64) ? "64" : "32", ring->mmio_base, (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots); /* Device IRQs are disabled prior entering this function, * so no need to take care of concurrency with rx handler stuff. */ dmacontroller_cleanup(ring); free_all_descbuffers(ring); free_ringmemory(ring); kfree(ring->meta); kfree(ring); } void bcm43xx_dma_free(struct bcm43xx_private *bcm) { struct bcm43xx_dma *dma; if (bcm43xx_using_pio(bcm)) return; dma = bcm43xx_current_dma(bcm); bcm43xx_destroy_dmaring(dma->rx_ring3); dma->rx_ring3 = NULL; bcm43xx_destroy_dmaring(dma->rx_ring0); dma->rx_ring0 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring5); dma->tx_ring5 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring4); dma->tx_ring4 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring3); dma->tx_ring3 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring2); dma->tx_ring2 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring1); dma->tx_ring1 = NULL; bcm43xx_destroy_dmaring(dma->tx_ring0); dma->tx_ring0 = NULL; } int bcm43xx_dma_init(struct bcm43xx_private *bcm) { struct bcm43xx_dma *dma = bcm43xx_current_dma(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) { 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 */ } /* setup TX DMA channels. */ ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64); if (!ring) goto out; dma->tx_ring0 = ring; ring = bcm43xx_setup_dmaring(bcm, 1, 1, dma64); if (!ring) goto err_destroy_tx0; dma->tx_ring1 = ring; ring = bcm43xx_setup_dmaring(bcm, 2, 1, dma64); if (!ring) goto err_destroy_tx1; dma->tx_ring2 = ring; ring = bcm43xx_setup_dmaring(bcm, 3, 1, dma64); if (!ring) goto err_destroy_tx2; dma->tx_ring3 = ring; ring = bcm43xx_setup_dmaring(bcm, 4, 1, dma64); if (!ring) goto err_destroy_tx3; dma->tx_ring4 = ring; ring = bcm43xx_setup_dmaring(bcm, 5, 1, dma64); if (!ring) goto err_destroy_tx4; dma->tx_ring5 = ring; /* setup RX DMA channels. */ ring = bcm43xx_setup_dmaring(bcm, 0, 0, dma64); if (!ring) goto err_destroy_tx5; dma->rx_ring0 = ring; if (bcm->current_core->rev < 5) { ring = bcm43xx_setup_dmaring(bcm, 3, 0, dma64); if (!ring) goto err_destroy_rx0; dma->rx_ring3 = ring; } dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", nobits); err = 0; out: return err; err_destroy_rx0: bcm43xx_destroy_dmaring(dma->rx_ring0); dma->rx_ring0 = NULL; err_destroy_tx5: bcm43xx_destroy_dmaring(dma->tx_ring5); dma->tx_ring5 = NULL; err_destroy_tx4: bcm43xx_destroy_dmaring(dma->tx_ring4); dma->tx_ring4 = NULL; err_destroy_tx3: bcm43xx_destroy_dmaring(dma->tx_ring3); dma->tx_ring3 = NULL; err_destroy_tx2: bcm43xx_destroy_dmaring(dma->tx_ring2); dma->tx_ring2 = NULL; err_destroy_tx1: bcm43xx_destroy_dmaring(dma->tx_ring1); dma->tx_ring1 = NULL; err_destroy_tx0: bcm43xx_destroy_dmaring(dma->tx_ring0); dma->tx_ring0 = NULL; goto out; } /* Generate a cookie for the TX header. */ static u16 generate_cookie(struct bcm43xx_dmaring *ring, int slot) { u16 cookie = 0x1000; /* Use the upper 4 bits of the cookie as * DMA controller ID and store the slot number * in the lower 12 bits. * Note that the cookie must never be 0, as this * is a special value used in RX path. */ switch (ring->index) { case 0: cookie = 0xA000; break; case 1: cookie = 0xB000; break; case 2: cookie = 0xC000; break; case 3: cookie = 0xD000; break; case 4: cookie = 0xE000; break; case 5: cookie = 0xF000; break; } assert(((u16)slot & 0xF000) == 0x0000); cookie |= (u16)slot; return cookie; } /* Inspect a cookie and find out to which controller/slot it belongs. */ static struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm, u16 cookie, int *slot) { struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); struct bcm43xx_dmaring *ring = NULL; switch (cookie & 0xF000) { case 0xA000: ring = dma->tx_ring0; break; case 0xB000: ring = dma->tx_ring1; break; case 0xC000: ring = dma->tx_ring2; break; case 0xD000: ring = dma->tx_ring3; break; case 0xE000: ring = dma->tx_ring4; break; case 0xF000: ring = dma->tx_ring5; break; default: assert(0); } *slot = (cookie & 0x0FFF); assert(*slot >= 0 && *slot < ring->nr_slots); return ring; } static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring, int slot) { u16 offset; int descsize; /* Everything is ready to start. Buffers are DMA mapped and * associated with slots. * "slot" is the last slot of the new frame we want to transmit. * Close your seat belts now, please. */ wmb(); slot = next_slot(ring, slot); offset = (ring->dma64) ? BCM43xx_DMA64_TXINDEX : BCM43xx_DMA32_TXINDEX; descsize = (ring->dma64) ? sizeof(struct bcm43xx_dmadesc64) : sizeof(struct bcm43xx_dmadesc32); bcm43xx_dma_write(ring, offset, (u32)(slot * descsize)); } static void dma_tx_fragment(struct bcm43xx_dmaring *ring, struct sk_buff *skb, u8 cur_frag) { int slot; struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; dma_addr_t dmaaddr; assert(skb_shinfo(skb)->nr_frags == 0); slot = request_slot(ring); desc = bcm43xx_dma_idx2desc(ring, slot, &meta); /* Add a device specific TX header. */ assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr)); /* Reserve enough headroom for the device tx header. */ __skb_push(skb, sizeof(struct bcm43xx_txhdr)); /* Now calculate and add the tx header. * The tx header includes the PLCP header. */ bcm43xx_generate_txhdr(ring->bcm, (struct bcm43xx_txhdr *)skb->data, skb->data + sizeof(struct bcm43xx_txhdr), skb->len - sizeof(struct bcm43xx_txhdr), (cur_frag == 0), generate_cookie(ring, slot)); meta->skb = skb; dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); meta->dmaaddr = dmaaddr; fill_descriptor(ring, desc, dmaaddr, skb->len, 1, 1, 1); /* Now transfer the whole frame. */ dmacontroller_poke_tx(ring, slot); } int bcm43xx_dma_tx(struct bcm43xx_private *bcm, struct ieee80211_txb *txb) { /* We just received a packet from the kernel network subsystem. * Add headers and DMA map the memory. Poke * the device to send the stuff. * Note that this is called from atomic context. */ struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1; u8 i; struct sk_buff *skb; assert(ring->tx); if (unlikely(free_slots(ring) < txb->nr_frags)) { /* The queue should be stopped, * if we are low on free slots. * If this ever triggers, we have to lower the suspend_mark. */ dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n"); return -ENOMEM; } for (i = 0; i < txb->nr_frags; i++) { skb = txb->fragments[i]; /* Take skb from ieee80211_txb_free */ txb->fragments[i] = NULL; dma_tx_fragment(ring, skb, i); } ieee80211_txb_free(txb); return 0; } void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, struct bcm43xx_xmitstatus *status) { struct bcm43xx_dmaring *ring; struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; int is_last_fragment; int slot; u32 tmp; ring = parse_cookie(bcm, status->cookie, &slot); assert(ring); assert(ring->tx); while (1) { assert(slot >= 0 && slot < ring->nr_slots); desc = bcm43xx_dma_idx2desc(ring, slot, &meta); if (ring->dma64) { tmp = le32_to_cpu(desc->dma64.control0); is_last_fragment = !!(tmp & BCM43xx_DMA64_DCTL0_FRAMEEND); } else { tmp = le32_to_cpu(desc->dma32.control); is_last_fragment = !!(tmp & BCM43xx_DMA32_DCTL_FRAMEEND); } unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); free_descriptor_buffer(ring, meta, 1); /* Everything belonging to the slot is unmapped * and freed, so we can return it. */ return_slot(ring, slot); if (is_last_fragment) break; slot = next_slot(ring, slot); } bcm->stats.last_tx = jiffies; } static void dma_rx(struct bcm43xx_dmaring *ring, int *slot) { struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; struct bcm43xx_rxhdr *rxhdr; struct sk_buff *skb; u16 len; int err; dma_addr_t dmaaddr; desc = bcm43xx_dma_idx2desc(ring, *slot, &meta); sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); skb = meta->skb; if (ring->index == 3) { /* We received an xmit status. */ struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data; struct bcm43xx_xmitstatus stat; int i = 0; stat.cookie = le16_to_cpu(hw->cookie); while (stat.cookie == 0) { if (unlikely(++i >= 10000)) { assert(0); break; } udelay(2); barrier(); stat.cookie = le16_to_cpu(hw->cookie); } stat.flags = hw->flags; stat.cnt1 = hw->cnt1; stat.cnt2 = hw->cnt2; stat.seq = le16_to_cpu(hw->seq); stat.unknown = le16_to_cpu(hw->unknown); bcm43xx_debugfs_log_txstat(ring->bcm, &stat); bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat); /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); return; } rxhdr = (struct bcm43xx_rxhdr *)skb->data; len = le16_to_cpu(rxhdr->frame_length); if (len == 0) { int i = 0; do { udelay(2); barrier(); len = le16_to_cpu(rxhdr->frame_length); } while (len == 0 && i++ < 5); if (unlikely(len == 0)) { /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); goto drop; } } if (unlikely(len > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers * big enough. So simply ignore this packet. */ int cnt = 0; s32 tmp = len; while (1) { desc = bcm43xx_dma_idx2desc(ring, *slot, &meta); /* recycle the descriptor buffer. */ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); *slot = next_slot(ring, *slot); cnt++; tmp -= ring->rx_buffersize; if (tmp <= 0) break; } printkl(KERN_ERR PFX "DMA RX buffer too small " "(len: %u, buffer: %u, nr-dropped: %d)\n", len, ring->rx_buffersize, cnt); goto drop; } len -= IEEE80211_FCS_LEN; dmaaddr = meta->dmaaddr; err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); if (unlikely(err)) { dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n"); sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); goto drop; } unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); skb_put(skb, len + ring->frameoffset); skb_pull(skb, ring->frameoffset); err = bcm43xx_rx(ring->bcm, skb, rxhdr); if (err) { dev_kfree_skb_irq(skb); goto drop; } drop: return; } void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) { u32 status; u16 descptr; int slot, current_slot; #ifdef CONFIG_BCM43XX_DEBUG int used_slots = 0; #endif assert(!ring->tx); if (ring->dma64) { status = bcm43xx_dma_read(ring, BCM43xx_DMA64_RXSTATUS); descptr = (status & BCM43xx_DMA64_RXSTATDPTR); current_slot = descptr / sizeof(struct bcm43xx_dmadesc64); } else { status = bcm43xx_dma_read(ring, BCM43xx_DMA32_RXSTATUS); descptr = (status & BCM43xx_DMA32_RXDPTR); current_slot = descptr / sizeof(struct bcm43xx_dmadesc32); } assert(current_slot >= 0 && current_slot < ring->nr_slots); slot = ring->current_slot; for ( ; slot != current_slot; slot = next_slot(ring, slot)) { dma_rx(ring, &slot); #ifdef CONFIG_BCM43XX_DEBUG if (++used_slots > ring->max_used_slots) ring->max_used_slots = used_slots; #endif } if (ring->dma64) { bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX, (u32)(slot * sizeof(struct bcm43xx_dmadesc64))); } else { bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX, (u32)(slot * sizeof(struct bcm43xx_dmadesc32))); } ring->current_slot = slot; } void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) { assert(ring->tx); bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1); if (ring->dma64) { bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL) | BCM43xx_DMA64_TXSUSPEND); } else { bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL) | BCM43xx_DMA32_TXSUSPEND); } } void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) { assert(ring->tx); if (ring->dma64) { bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL) & ~BCM43xx_DMA64_TXSUSPEND); } else { bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL) & ~BCM43xx_DMA32_TXSUSPEND); } bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1); }