/* * EHCI HCD (Host Controller Driver) SRAM Glue. * */ #ifndef CONFIG_HAVE_GENERIC_DMA_COHERENT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ehci-sram.h" struct dma_coherent_mem { void *virt_base; dma_addr_t device_base; phys_addr_t pfn_base; int size; int flags; unsigned long *bitmap; }; static void *ehci_sram_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs); static void ehci_sram_free(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs); static struct dma_map_ops sram_ops; static struct dma_map_ops *dma_ops_org; /* NOTE: the following routine should be in arch/x86/include/asm/dma-mapping.h * along with get_dma_ops. */ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) { #ifdef CONFIG_X86_DEV_DMA_OPS dev->archdata.dma_ops = ops; #endif } /* Attach SRAM to device */ int ehci_sram_declare(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags) { void __iomem *mem_base = NULL; int pages = size >> PAGE_SHIFT; int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); dma_ops_org = get_dma_ops(dev); if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) goto out; if (!size) goto out; if (dev->dma_mem) goto out; mem_base = ioremap(bus_addr, size); if (!mem_base) goto out; dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); if (!dev->dma_mem) goto out; dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); if (!dev->dma_mem->bitmap) goto free1_out; dev->dma_mem->virt_base = mem_base; dev->dma_mem->device_base = device_addr; dev->dma_mem->pfn_base = PFN_DOWN(bus_addr); dev->dma_mem->size = pages; dev->dma_mem->flags = flags; memcpy(&sram_ops, dma_ops_org, sizeof(struct dma_map_ops)); sram_ops.alloc = ehci_sram_alloc; sram_ops.free = ehci_sram_free; set_dma_ops(dev, &sram_ops); return 1; free1_out: kfree(dev->dma_mem); out: if (mem_base) iounmap(mem_base); return 0; } /* Detach SRAM from device */ void ehci_sram_release(struct device *dev) { struct dma_coherent_mem *mem = dev->dma_mem; if (!mem) return; set_dma_ops(dev, dma_ops_org); dev->dma_mem = NULL; iounmap(mem->virt_base); kfree(mem->bitmap); kfree(mem); } /** * ehci_sram_alloc() - try to allocate memory from the SRAM * * @dev: device from which we allocate memory * @size: size of requested memory area * @dma_handle: This will be filled with the correct dma handle * * This function allocate a block from SRAM. * * Returns the virtual address to allocated area, or NULL if failed. */ void *ehci_sram_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs) { struct dma_coherent_mem *mem; int order = get_order(size); int pageno; void *pret; if (!dev) return 0; mem = dev->dma_mem; if (!mem) return 0; pret = NULL; if (unlikely(size > (mem->size << PAGE_SHIFT))) goto err; pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); if (unlikely(pageno < 0)) goto err; /* * Memory was found in the per-device area. */ *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); pret = mem->virt_base + (pageno << PAGE_SHIFT); memset(pret, 0, size); return pret; err: /* * In the case where the allocation can not be satisfied from the * per-device area, try to fall back to generic memory if the * constraints allow it. */ if (dma_ops_org->alloc) return dma_ops_org->alloc(dev, size, dma_handle, gfp, attrs); return NULL; } /** * ehci_sram_free() - try to free the memory allocated from SRAM * @dev: device from which the memory was allocated * @size: size of memory area * @vaddr: virtual address of allocated pages * * This checks whether the memory was allocated from the SRAM * and if so, releases that memory. * * Returns 1 if we correctly released the memory, or 0 if not */ void ehci_sram_free(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs) { struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; int order = get_order(size); if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; bitmap_release_region(mem->bitmap, page, order); } else if (mem && dma_ops_org->free) { dma_ops_org->free(dev, size, vaddr, dma_handle, attrs); } } #endif