diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7fc692a..34298c9 100644 Index: linux-omap-dev/drivers/misc/Kconfig =================================================================== --- linux-omap-dev.orig/drivers/misc/Kconfig 2005-11-02 09:11:35.000000000 -0800 +++ linux-omap-dev/drivers/misc/Kconfig 2005-11-02 09:12:17.000000000 -0800 @@ -28,5 +28,10 @@ If unsure, say N. +config OMAP_DMATEST + tristate "OMAP DMA test module" + depends on ARCH_OMAP + + endmenu Index: linux-omap-dev/drivers/misc/Makefile =================================================================== --- linux-omap-dev.orig/drivers/misc/Makefile 2005-11-02 09:11:35.000000000 -0800 +++ linux-omap-dev/drivers/misc/Makefile 2005-11-02 09:12:17.000000000 -0800 @@ -3,5 +3,7 @@ # obj- := misc.o # Dummy rule to force built-in.o to be made +obj-$(CONFIG_OMAP_DMATEST) += omap-dma-test.o + obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ Index: linux-omap-dev/drivers/misc/omap-dma-test.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-omap-dev/drivers/misc/omap-dma-test.c 2005-11-02 09:25:40.000000000 -0800 @@ -0,0 +1,346 @@ +/* + * OMAP DMA cache flush test + * + * This test modules schedules a SDRAM to SDRAM (EMIFF to EMIFF) DMA transfer + * at high rate and verifies that the data got transferred OK. + * + * The purpose of the test is to expose cache flushing problems. Can also be + * used as a sample module on setting up OMAP DMA transfers. + * + * Changelog: + * - 20040221 Initial version + * - 20040128 Changed to use dma-mapping.h API because of flush problems + * with non-cache-aligned addresses + * - 20040202 Added missing include for device.h + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define VERSION "20051102" +#define VERIFY_DATA +//#define DEBUG + +#define TEST_SCHEDULE_FREQUENCY (1) +#define NR_TEST_THREADS 8 + +static void * test_threads[NR_TEST_THREADS]; + +struct thread_data { + int nr; + pid_t pid; + char * name; +}; + +struct dma_transfer { + unsigned long virt_from; + unsigned long virt_to; + unsigned long phys_from; + unsigned long phys_to; + unsigned long len; + int ch; /* Assigned DMA channel number */ + unsigned short data_type; /* Data length */ + unsigned short elem_count; /* Elements to transfer */ + unsigned short frame_count; /* Framses to transfer */ + unsigned int status; /* Callback status */ +}; + +#ifdef VERIFY_DATA +/* + * Verifiens the dma tranfer worked and cache flushing for regions works + */ +static void omap_dma_verify_data(struct dma_transfer * dma) +{ + unsigned int i; + + for (i = 0; i < dma->len; i++) { + if (readb(dma->virt_from + i) != readb(dma->virt_to + i)) { + printk(KERN_ERR "DMA copy or cache flush problem!\n"); + + printk(KERN_ERR "DMA%i %08x @ %08lx != %08x @ %08lx\n", + dma->ch, readl(dma->virt_from), dma->phys_from, + readl(dma->virt_to), dma->phys_to); + BUG(); + } + } +} +#endif + +/* + * The callback gets called by the DMA interrupt handler after + * the transfer is complete. + */ +static void omap_dma_memcpy_callback(int lch, u16 ch_status, void *data) +{ + struct dma_transfer * dma = (struct dma_transfer *)data; + +#ifdef DEBUG + printk(KERN_INFO " CB%i len: %lx v: %lx p: %lx -> v: %lx p %lx\n", + dma->ch, dma->len, + dma->virt_from, dma->phys_from, + dma->virt_to, dma->phys_to); +#endif + + dma->status++; +} + +/* + * Handles the DMA transfer between two EMIFF memory areas with DMA. + * Maybe this would be faster than memcpy() for some larger chunks of data? + */ +static int omap_dma_mem_transfer(struct dma_transfer * dma) +{ + dma->data_type = OMAP_DMA_DATA_TYPE_S8; + dma->status = 0; + + int src_port = 0; + int dst_port = 0; + int sync_dev = 0; + + /* + * FIXME: Both elem_count and frame_count are limited to 65536, so + * we should really do the tranfer in larger chunks. + * But for testing cache flushing this will do. + */ + if (dma->len > 65536) { + printk(KERN_ERR "DMA test is limited to 65536 bytes\n"); + return -ENOMEM; + } + + dma->elem_count = dma->len; + dma->frame_count = 1; + + /* Get a DMA channel */ + if (omap_request_dma(OMAP_DMA_NO_DEVICE, "mem to mem", + omap_dma_memcpy_callback, (void *)dma, &dma->ch)) { + printk(KERN_ERR "Could not get DMA with omap_request_dma()\n"); + return -ENOMEM; + } + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_EMIFF; + dst_port = OMAP_DMA_PORT_EMIFF; + } + + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_NO_DEVICE; + + /* Transfer len = (data_type * elem_count * frame_count) */ + omap_set_dma_transfer_params(dma->ch, dma->data_type, dma->elem_count, + dma->frame_count, OMAP_DMA_SYNC_ELEMENT, + sync_dev, 0); + + /* Source EMFIFF, increase source address after each transfer */ + omap_set_dma_src_params(dma->ch, src_port, + OMAP_DMA_AMODE_POST_INC, + dma->phys_from, 0, 0); + + /* Destination EMIFF, increase dest address after each transfer */ + omap_set_dma_dest_params(dma->ch, dst_port, + OMAP_DMA_AMODE_POST_INC, + dma->phys_to, 0, 0); + + /* + * If we did not use dma-mapping, would need to flush "from" data + * area from cache here with something like: + * __cpuc_flush_user_range(dma->virt_from, dma->virt_to, 0); + */ + +#ifdef DEBUG + printk(KERN_INFO " CH%i len: %lx v: %lx p: %lx -> v: %lx p %lx\n", + dma->ch, dma->len, + dma->virt_from, dma->phys_from, + dma->virt_to, dma->phys_to); +#endif + + omap_start_dma(dma->ch); + + /* + * In this case we must wait for the callback to complete + */ + while(!dma->status) { + //udelay(50); + + /* + * Artificially delay the freeing to use more channels for + * testing purposes only. NOTE: Can't do this in a tasklet. + */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + current->state = TASK_RUNNING; + } + omap_free_dma(dma->ch); + + /* + * If we did not use dma-mapping, we would need to invalidate + * (but not clean) the "to" data area from cache at this point. Note + * that dma_inv_range(dma->virt_from, dma->virt_to) would not + * always work, as it also cleans any addresses that are not cache + * line aligned. + */ + +#ifdef VERIFY_DATA + omap_dma_verify_data(dma); +#endif + + return 0; +} + +/* + * Test function to test DMA and cache flushing. + * Does a simple EMIFF to EMIFF DMA transfer and verifies the data + * got copied over correctly. + */ +static void omap_dmatest_work(unsigned long data) +{ + struct dma_transfer * dma; + int ret; + struct thread_data * thread = (struct thread_data *)data; + + /* Wait for 10 secs before starting */ + daemonize("%s", thread->name); + allow_signal(SIGTERM); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ * 10); + current->state = TASK_RUNNING; + + for(;;) { + /* FIXME: Does not free mem in SIGTERM currently */ + if (signal_pending(current)) + flush_signals(current); + +#ifdef DEBUG + printk(KERN_INFO "DMA test thread: %02i\n", thread->nr); +#endif + + /* Set up transfer data */ + dma = kmalloc(sizeof(struct dma_transfer), GFP_KERNEL); + if (!dma) { + printk(KERN_ERR "Could not kmalloc DMA transfer\n"); + return; + } + memset(dma, 0, sizeof(struct dma_transfer)); + + /* Transfer only 4 bytes for now */ + dma->len = 4; + + dma->virt_from = (unsigned long)dma_alloc_coherent(NULL, + dma->len, + (dma_addr_t *)&dma->phys_from, + GFP_KERNEL); + if (!dma->virt_from) + goto free2; + + dma->virt_to = (unsigned long)dma_alloc_coherent(NULL, + dma->len, + (dma_addr_t *)&dma->phys_to, + GFP_KERNEL); + if (!dma->virt_to) + goto free3; + + /* Set some test values */ + writel(0x12345678, dma->virt_from); + + /* Do the transfer */ + ret = omap_dma_mem_transfer(dma); + if (ret) { + /* Anything to do? */ + } + + free3: + dma_free_coherent(NULL, dma->len, (void *)dma->virt_to, + dma->phys_to); + + free2: + dma_free_coherent(NULL, dma->len, (void *)dma->virt_from, + dma->phys_from); + + kfree(dma); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(TEST_SCHEDULE_FREQUENCY); + current->state = TASK_RUNNING; + } +} + +static char *name = "dmatest"; + +static int __init omap_dmatest_init(void) +{ + int i; + + /* Schedule multiple concurrent dma tests */ + memset(test_threads, 0, NR_TEST_THREADS * sizeof(void *)); + for (i = 0; i < NR_TEST_THREADS; i++) { + struct thread_data * thread; + thread = kmalloc(sizeof(struct thread_data), GFP_KERNEL); + if (!thread) + goto free_threads; + + memset(thread, 0, sizeof(struct thread_data)); + thread->nr = i; + thread->name = name; + + /* Schedule the test thread */ + thread->pid = kernel_thread((void *)omap_dmatest_work, + thread, 0); + + test_threads[i] = thread; + } + + printk("WARNING: OMAP DMA cache test version %s scheduled. " + "Hogs CPU, do not use for production!\n", VERSION); + + return 0; + + free_threads: + for (i = 0; i < NR_TEST_THREADS; i++) { + if (test_threads[i]) + kfree(test_threads[i]); + } + + return -ENOMEM; +} + +static void __exit omap_dmatest_exit(void) +{ + int i; + + for (i = 0; i < NR_TEST_THREADS; i++) { + struct thread_data * thread = test_threads[i]; + + if (thread) { + kill_proc(thread->pid, SIGTERM, 1); + kfree(thread); + test_threads[i] = 0; + } + } + + return; +} + +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("OMAP DMA test module"); +MODULE_LICENSE("GPL"); +module_init(omap_dmatest_init); +module_exit(omap_dmatest_exit);