Hello! First sorry for the long mail! :-)
I started another attemp to get the Linux kernel working correctly on the AmigaOne with it's famous (and buggy) ArticiaS northbridge. There were a lot of people that tried this before, but I think all of them didn't address the real problem. The problem is that the ArticiaS northbrige doesn't support cache coherency correctly. The previous developers of the AmigaOne port just limited the transfer size of some DMA transfers, for example in the IDE driver, or added a specific cache flush to the driver code to avoid data corruption. This didn't work correctly. In the 2.6.x kernel series data corruption appeared also with other drivers that use DMA (e.g. ethernet), therefore I thought to fix the problem in the DMA layer (include/asm-ppc/dma-mapping.h) for all drivers. I tried to change the code to handle the AmigaOne platform as a non cache coherent architecture and to do all necessary cache flushes in software. AFAIK also the developers of AmigaOS4 have worked around the bugs of the ArticiaS in this way. Well, first it seemed that I had succeeded and Linux was working correctly with DMA (tested it by calculating the checksum of a 700MB ISO file and with DMA activated for the IDE drives), but after porting my patches from kernel 2.6.12.6 to 2.6.14.2 it became apparent that the patches don't work correctly yet (but better as the other attempts ;-). Also the kernel reported a lot of "lost interrupts" messages and errors about expired "DMA timers". Well, as a complete kernel newbie (never hacked in the kernel before) and because I don't fully understand the code for DMA on non cache coherent platform, I have to seek advice now. I included all relevant patches for the AmigaOne below. My patches are for "include/asm-ppc/dma-mapping.h" and "arch/ppc/kernel/amigaone_dma-mapping.c". The other patches were either made by some OS4 developers (Ross Vumbaca) or Ken Moffat (ported the patches to 2.6.x). If somebody wants to take a look at all the patches for the 2.6.x kernel, just write me. The code in amigaone_dma-mapping.c was directly copied from dma-mapping.c (in arch/ppc/kernel/). I think the main problem lies in dma-mapping.h, were I just added some cache flush functions to the DMA allocation functions for coherent architectures (dma_alloc_coherent, dma_free_coherent). So can anyone explain me, what the differences between the DMA allocation functions for coherent and non coherent architectures are? How can the non coherent DMA memory allocation functions be adapted for the AmigaOne? I have another question regarding the cputable.c file. Ken Moffat commented out CPU_FTR_NEED_COHERENT for the AmigaOne platform. I tried to find information about why this define is needed, but couldn't find anything. Does anybody know, how this affects the functionality of the kernel on PPC 745x CPUs? I'm glad for any useful input! Thanks in advance! regards, Gerhard Patches: diff -Naurp linux-2.6.14.2/arch/ppc/kernel/amigaone_dma-mapping.c linux-2.6.14.2-a1-1/arch/ppc/kernel/amigaone_dma-mapping.c --- linux-2.6.14.2/arch/ppc/kernel/amigaone_dma-mapping.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.14.2-a1-1/arch/ppc/kernel/amigaone_dma-mapping.c 2005-11-23 21:45:41.000000000 +0100 @@ -0,0 +1,138 @@ /* includes commented out... +/* + * make an area consistent. + */ +void __dma_sync(void *vaddr, size_t size, int direction) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + switch (direction) { + case DMA_NONE: + BUG(); + case DMA_FROM_DEVICE: /* invalidate only */ + invalidate_dcache_range(start, end); + break; + case DMA_TO_DEVICE: /* writeback only */ + clean_dcache_range(start, end); + break; + case DMA_BIDIRECTIONAL: /* writeback and invalidate */ + flush_dcache_range(start, end); + break; + } +} +EXPORT_SYMBOL(__dma_sync); + +#ifdef CONFIG_HIGHMEM +/* + * __dma_sync_page() implementation for systems using highmem. + * In this case, each page of a buffer must be kmapped/kunmapped + * in order to have a virtual address for __dma_sync(). This must + * not sleep so kmap_atmomic()/kunmap_atomic() are used. + * + * Note: yes, it is possible and correct to have a buffer extend + * beyond the first page. + */ +static inline void __dma_sync_page_highmem(struct page *page, + unsigned long offset, size_t size, int direction) +{ + size_t seg_size = min((size_t)PAGE_SIZE, size) - offset; + size_t cur_size = seg_size; + unsigned long flags, start, seg_offset = offset; + int nr_segs = PAGE_ALIGN(size + (PAGE_SIZE - offset))/PAGE_SIZE; + int seg_nr = 0; + + local_irq_save(flags); + + do { + start = (unsigned long)kmap_atomic(page + seg_nr, + KM_PPC_SYNC_PAGE) + seg_offset; + + /* Sync this buffer segment */ + __dma_sync((void *)start, seg_size, direction); + kunmap_atomic((void *)start, KM_PPC_SYNC_PAGE); + seg_nr++; + + /* Calculate next buffer segment size */ + seg_size = min((size_t)PAGE_SIZE, size - cur_size); + + /* Add the segment size to our running total */ + cur_size += seg_size; + seg_offset = 0; + } while (seg_nr < nr_segs); + + local_irq_restore(flags); +} +#endif /* CONFIG_HIGHMEM */ + +/* + * __dma_sync_page makes memory consistent. identical to __dma_sync, but + * takes a struct page instead of a virtual address + */ +void __dma_sync_page(struct page *page, unsigned long offset, + size_t size, int direction) +{ +#ifdef CONFIG_HIGHMEM + __dma_sync_page_highmem(page, offset, size, direction); +#else + unsigned long start = (unsigned long)page_address(page) + offset; + __dma_sync((void *)start, size, direction); +#endif +} +EXPORT_SYMBOL(__dma_sync_page); diff -Naurp linux-2.6.14.2/arch/ppc/kernel/cputable.c linux-2.6.14.2-a1-1/arch/ppc/kernel/cputable.c --- linux-2.6.14.2/arch/ppc/kernel/cputable.c 2005-11-11 06:33:12.000000000 +0100 +++ linux-2.6.14.2-a1-1/arch/ppc/kernel/cputable.c 2005-11-23 21:45:41.000000000 +0100 @@ -410,8 +410,10 @@ struct cpu_spec cpu_specs[] = { CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | - CPU_FTR_NEED_COHERENT, +#ifndef CONFIG_AMIGAONE /* chipset cannot do coherent dma */ + CPU_FTR_NEED_COHERENT | +#endif /* !CONFIG_AMIGAONE */ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP, .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, .icache_bsize = 32, .dcache_bsize = 32, @@ -475,8 +477,10 @@ struct cpu_spec cpu_specs[] = { CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | - CPU_FTR_NEED_COHERENT, +#ifndef CONFIG_AMIGAONE /* chipset cannot do coherent dma */ + CPU_FTR_NEED_COHERENT | +#endif /* !CONFIG_AMIGAONE */ + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS, .cpu_user_features = COMMON_PPC | PPC_FEATURE_ALTIVEC_COMP, .icache_bsize = 32, .dcache_bsize = 32, iff -Naurp linux-2.6.14.2/include/asm-ppc/dma-mapping.h linux-2.6.14.2-a1-1/include/asm-ppc/dma-mapping.h --- linux-2.6.14.2/include/asm-ppc/dma-mapping.h 2005-11-11 06:33:12.000000000 +0100 +++ linux-2.6.14.2-a1-1/include/asm-ppc/dma-mapping.h 2005-11-23 21:45:42.000000000 +0100 @@ -36,15 +36,35 @@ extern void __dma_sync_page(struct page * Cache coherent cores. */ +#define __dma_alloc_coherent(gfp, size, handle) NULL +#define __dma_free_coherent(size, addr) do { } while (0) + +#ifndef CONFIG_AMIGAONE + #define dma_cache_inv(_start,_size) do { } while (0) #define dma_cache_wback(_start,_size) do { } while (0) #define dma_cache_wback_inv(_start,_size) do { } while (0) -#define __dma_alloc_coherent(gfp, size, handle) NULL -#define __dma_free_coherent(size, addr) do { } while (0) #define __dma_sync(addr, size, rw) do { } while (0) #define __dma_sync_page(pg, off, sz, rw) do { } while (0) +#else /* ! CONFIG_AMIGAONE */ + +#include <asm/tlbflush.h> +/* Include TLB cache flush functions! */ + +extern void __dma_sync(void *vaddr, size_t size, int direction); +extern void __dma_sync_page(struct page *page, unsigned long offset, + size_t size, int direction); +#define dma_cache_inv(_start,_size) \ + invalidate_dcache_range(_start, (_start + _size)) +#define dma_cache_wback(_start,_size) \ + clean_dcache_range(_start, (_start + _size)) +#define dma_cache_wback_inv(_start,_size) \ + flush_dcache_range(_start, (_start + _size)) + +#endif /* ! CONFIG_AMIGAONE */ + #endif /* ! CONFIG_NOT_COHERENT_CACHE */ #define dma_supported(dev, mask) (1) @@ -77,6 +97,15 @@ static inline void *dma_alloc_coherent(s if (ret != NULL) { memset(ret, 0, size); + +#ifdef CONFIG_AMIGAONE + /* + * Invalidate any data that might be lurking in the + * kernel direct-mapped region for device DMA. + */ + flush_dcache_range((unsigned long)ret, (unsigned long)ret + size); +#endif /* CONFIG_AMIGAONE */ + *dma_handle = virt_to_bus(ret); } @@ -92,6 +121,13 @@ dma_free_coherent(struct device *dev, si __dma_free_coherent(size, vaddr); #else free_pages((unsigned long)vaddr, get_order(size)); + +#ifdef CONFIG_AMIGAONE + + flush_tlb_kernel_range((unsigned long)vaddr, (unsigned long)vaddr + size); + +#endif /* CONFIG_AMIGAONE */ + #endif } diff -Naurp linux-2.6.14.2/include/asm-ppc/dma.h linux-2.6.14.2-a1-1/include/asm-ppc/dma.h --- linux-2.6.14.2/include/asm-ppc/dma.h 2005-11-11 06:33:12.000000000 +0100 +++ linux-2.6.14.2-a1-1/include/asm-ppc/dma.h 2005-11-23 21:45:42.000000000 +0100 @@ -33,8 +33,12 @@ #endif /* The maximum address that we can perform a DMA transfer to on this platform */ -/* Doesn't really apply... */ +/* Doesn't really apply... - except for the AmigaOne. */ +#ifdef CONFIG_AMIGAONE +#define MAX_DMA_ADDRESS 0x00FFFFFF +#else #define MAX_DMA_ADDRESS 0xFFFFFFFF +#endif /* in arch/ppc/kernel/setup.c -- Cort */ extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ; -- 10 GB Mailbox, 100 FreeSMS/Monat http://www.gmx.net/de/go/topmail +++ GMX - die erste Adresse für Mail, Message, More +++ -- To UNSUBSCRIBE, email to [EMAIL PROTECTED] with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]