blob: d4ec5a1a9d814f06321b56a0bde4dca3888b6dc1 [file] [log] [blame]
* EHCI HCD (Host Controller Driver) SRAM Glue.
#include <linux/slab.h>
#include <linux/dmapool.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wait.h>
#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)
dev->archdata.dma_ops = ops;
/* 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; = ehci_sram_free;
set_dma_ops(dev, &sram_ops);
return 1;
if (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)
set_dma_ops(dev, dma_ops_org);
dev->dma_mem = NULL;
* 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;
* 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);