/* * * ######################################################################## * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope 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; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * ######################################################################## * * */ #include #include #include #if defined(CONFIG_ARCH_IPQ806X_DT) || defined(CONFIG_MACH_BCM963138) extern void v7_flush_kern_dcache_area(unsigned long page_address, ssize_t size); extern void v7_dma_flush_range(unsigned long start_virt_addr, unsigned long end_virt_addr); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void dma_cache_inv(unsigned long start_virt_addr, size_t len) { v7_dma_flush_range(start_virt_addr, start_virt_addr + len - 1); } EXPORT_SYMBOL(dma_cache_inv); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void dma_cache_wback_inv(unsigned long page_address, ssize_t size) { v7_flush_kern_dcache_area(page_address, size); } EXPORT_SYMBOL(dma_cache_wback_inv); #endif #include static DEFINE_SPINLOCK(dma_lock); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static struct _dma_alloc_list { atomic_t alloced; unsigned int size; void *ptr; struct _dma_alloc_list *next; } *dma_anchor; #define ALIGN_SIZE(size) ((size + (2 * L1_CACHE_BYTES) - 1) & ~((1 << L1_CACHE_SHIFT) - 1)) /*--- #define DEBUG_AVM_DMAPOOL ---*/ #if defined(DEBUG_AVM_DMAPOOL) #define DBG_TRC(args...) printk(args) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void avm_dump_dma_pool(void) { struct _dma_alloc_list *dma_list = dma_anchor; spin_lock(&dma_lock); while(dma_list) { unsigned int use = atomic_read(&dma_list->alloced); DBG_TRC("%s: %p ptr=%p %s size=%u next=%p\n", __func__, dma_list, dma_list->ptr, use == 0 ? "free " : "alloced" , dma_list->size, dma_list->next); dma_list = dma_list->next; } spin_unlock(&dma_lock); } #else /*--- #if defined(DEBUG_AVM_DMAPOOL) ---*/ #define DBG_TRC(args...) #define avm_dump_dma_pool() #endif /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void avm_dma_pool_init(unsigned char *base, unsigned int size) { printk(KERN_ERR"%s: base=%p size=%u\n", __func__, base, size); dma_anchor = (struct _dma_alloc_list *)base; dma_anchor->size = size; dma_anchor->ptr = NULL; dma_anchor->next = NULL; atomic_set(&dma_anchor->alloced, 0); /*--- avm_dump_dma_pool(); ---*/ } /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void *avm_dma_pool_alloc(unsigned int size, dma_addr_t *handle) { struct _dma_alloc_list *dma_list = dma_anchor; unsigned int needed_size; spin_lock(&dma_lock); needed_size = ALIGN_SIZE(size + sizeof(struct _dma_alloc_list)); DBG_TRC("%s: size=%u needed_size=%u---------\n", __func__, size, needed_size); while(dma_list) { if((atomic_read(&dma_list->alloced) == 0) && (dma_list->size >= needed_size)) { unsigned int rest_size = dma_list->size - needed_size; if(rest_size > sizeof(struct _dma_alloc_list)) { struct _dma_alloc_list *next; /*--- vorneweg der (reduzierte) freie Bereich ---*/ dma_list->size = rest_size; next = dma_list->next; /*--- aktuellen next merken ---*/ dma_list->next = (struct _dma_alloc_list *)(((unsigned char *)dma_list) + dma_list->size); /*--- DBG_TRC("\t%s: %p (free) size=%u next=%p\n", __func__, dma_list, dma_list->size, dma_list->next); ---*/ dma_list = dma_list->next; dma_list->next = next; dma_list->size = needed_size; dma_list->ptr = (void *)(((unsigned long)(dma_list + 1) + L1_CACHE_BYTES) & ~((1 << L1_CACHE_SHIFT) - 1)); /*--- DBG_TRC("\t%s: %p(alloced) size=%u ptr=%p next=%p\n", __func__, dma_list, dma_list->size, dma_list->ptr, dma_list->next); ---*/ atomic_set(&dma_list->alloced, 0x1); } *handle = virt_to_phys(dma_list->ptr); spin_unlock(&dma_lock); avm_dump_dma_pool(); return dma_list->ptr; } dma_list = dma_list->next; } spin_unlock(&dma_lock); return NULL; } EXPORT_SYMBOL(avm_dma_pool_alloc); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ void avm_dma_pool_free(void *vaddr){ struct _dma_alloc_list *dma_list = dma_anchor; struct _dma_alloc_list *dma_list_next, *dma_list_prev = NULL; spin_lock(&dma_lock); DBG_TRC("%s: free=%p ---------\n", __func__, vaddr); while(dma_list) { if((atomic_read(&dma_list->alloced)) && (dma_list->ptr == vaddr)) { /*--- DBG_TRC("\t%s: dma_list=%p matched\n", __func__, dma_list); ---*/ if(dma_list_prev && atomic_read(&dma_list_prev->alloced) == 0) { /*--- verbinde den Vorgaenger mit aktuellen Eintrag ---*/ dma_list_prev->size += dma_list->size; dma_list_prev->next = dma_list->next; atomic_set(&dma_list->alloced, 0); dma_list = dma_list_prev; /*--- DBG_TRC("\t%s: dma_list_prev=%p also free - add\n", __func__, dma_list); ---*/ } dma_list_next = dma_list->next; if(dma_list_next && atomic_read(&dma_list_next->alloced) == 0) { /*--- verbinde den Nachfolger mit aktuellen Eintrag ---*/ dma_list->size += dma_list_next->size; dma_list->next = dma_list_next->next; atomic_set(&dma_list->alloced, 0); /*--- DBG_TRC("\t%s: dma_list_next=%p also free - add\n", __func__, dma_list_next); ---*/ } atomic_set(&dma_list->alloced, 0); break; } dma_list_prev = dma_list; dma_list = dma_list->next; } spin_unlock(&dma_lock); avm_dump_dma_pool(); } EXPORT_SYMBOL(avm_dma_pool_free);