/* * * ######################################################################## * * 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) \ || defined(CONFIG_BCM963178) 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); /**-------------------------------------------------------------------------**\ * per dma_pool_alloc() geholter Speicher befindet sich seit 3.9-Kernel im * VMALLOC-Bereich * Dieser Speicher ist aufgrund noch nicht verstandener MMU-Konfiguration aus * FIQ nicht zugreifbar (wenn aus USER_MODE?) * Somit funktioniert der pcmlink-Treiber aktuell im FIQ-Mode nicht. * * Bis 3.8-Kernel wird dagegen ein Memory-Area jenseits von VMALLOC verwendet * [start=(CONSISTENT_END(0xffe00000UL) - DEFAULT_CONSISTENT_DMA_SIZE(SZ_2M), * end=CONSISTENT_END] * Hier wird dann per ioremap die MMU entsprechend konfiguriert. * (siehe cat /proc/vmallocinfo) * * Als Workarround verwenden wir hier erstmal das eigentlich nur fuer den * AVM_BOOT_STRING freigehaltene 1MByte-Segment * Entsprechende DMA-Alloc-Funktionalitaet nur im pcmlink verwendet \**-------------------------------------------------------------------------**/ extern void *avm_boot_string_virt_addr; extern uint32_t avm_boot_string_size; int __init init_avm_dmapool(void) { if (avm_boot_string_virt_addr) avm_dma_pool_init( (void *)(avm_boot_string_virt_addr + PAGE_SIZE), avm_boot_string_size - PAGE_SIZE); return 0; } core_initcall(init_avm_dmapool);