/* * Copyright 2012 Freescale Semiconductor, Inc. * * Simple test/example module for Faraday eDMA. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that 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 * *************************************************************************** * Changes: * v0.001 29 February 2008 Andrey Butok * Initial Release * * NOTE: This module tests eDMA driver performing * a simple memory to memory transfer with a 32 bit * source and destination transfer size that generates * an interrupt when the transfer is complete. */ #include #include #include #include #include #include #include #define MCF_EDMA_TEST_DRIVER_VERSION "Revision: 0.001" #define MCF_EDMA_TEST_DRIVER_AUTHOR \ "Freescale Semiconductor Inc, Andrey Butok" #define MCF_EDMA_TEST_DRIVER_DESC \ "Simple testing module for Faraday eDMA " #define MCF_EDMA_TEST_DRIVER_INFO \ MCF_EDMA_TEST_DRIVER_VERSION " " MCF_EDMA_TEST_DRIVER_DESC #define MCF_EDMA_TEST_DRIVER_LICENSE "GPL" #define MCF_EDMA_TEST_DRIVER_NAME "mcf_edma_test" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /****************For testing options*************/ #if 0 #define ALWAYS_ENABLED_MODE 1 /* test edma always enabled mode */ #define ROUTETO_DMA1 1 /* test Mux 1,2 sources (64 ~ 117) for dma1 */ #define ROUTETO_DMA0 1 /* test Mux 0,3 sources (0 ~ 63) for dma0 */ #endif #ifndef ALWAYS_ENABLED_MODE & ROUTETO_DMA1 #define ROUTETO_DMA0 #endif /*************************************************/ /* Global variable used to signal main process when interrupt is recognized */ static int mcf_edma_test_interrupt; int *mcf_edma_test_interrupt_p = &mcf_edma_test_interrupt; /********************************************************************/ static int mcf_edma_test_handler(int channel, void *dev_id) { /* Clear interrupt flag */ mcf_edma_confirm_interrupt_handled(channel); /* Set interrupt status flag to TRUE */ mcf_edma_test_interrupt = TRUE; return IRQ_HANDLED; } /********************************************************************/ int mcf_edma_test_block_compare(u8 *block1, u8 *block2, u32 size) { u32 i; asm("nop;\n"); asm("nop;\n"); for (i = 0; i < (size); i++) { if ((*(u8 *) (block1 + i)) != (*(u8 *) (block2 + i))) { printk(KERN_INFO "Invalid edma test value: "); printk(KERN_INFO "0x%x != 0x%x\n", *(u8 *)(block1 + i), *(u8 *)(block2 + i)); return FALSE; } } return TRUE; } /********************************************************************/ void mcf_edma_test_run(void) { u16 byte_count; u32 i, j; u8 *start_address; u8 *dest_address; u32 test_data; int channel; u32 allocated_channels_dma0 = 0; u32 allocated_channels_dma1 = 0; printk(KERN_INFO "\n===============================================\n"); printk(KERN_INFO "\nStarting eDMA transfer tests!\n"); /* Initialize test variables */ byte_count = 0x20; test_data = 0xA5A5A5A5; /* DMA buffer must be from GFP_DMA zone, so it will not be cached */ start_address = kmalloc(byte_count, GFP_DMA); if (start_address == NULL) { printk(KERN_INFO MCF_EDMA_TEST_DRIVER_NAME ": failed to allocate DMA[%d] buffer\n", byte_count); goto err_out; } dest_address = kmalloc(byte_count, GFP_DMA); if (dest_address == NULL) { printk(KERN_INFO MCF_EDMA_TEST_DRIVER_NAME ": failed to allocate DMA[%d] buffer\n", byte_count); goto err_free_mem; } /* Test all automatically allocated DMA channels. The test data is * complemented at the end of the loop, so that the testData value * isn't the same twice in a row */ #ifdef ALWAYS_ENABLED_MODE for (i = 0; i < 20; i++) { #elif ROUTETO_DMA1 for (i = 64; i < 96; i++) { #else for (i = 0; i < 32; i++) { #endif /* request eDMA channel */ channel = mcf_edma_request_channel( #ifdef ALWAYS_ENABLED_MODE MCF_EDMA_CHANNEL_ANY, #else i, #endif mcf_edma_test_handler, NULL, 0x6, NULL, NULL, MCF_EDMA_TEST_DRIVER_NAME); if (channel < 0) goto test_end; #ifdef ALWAYS_ENABLED_MODE if (i > 9 && i < 20) printk(KERN_INFO "*****channel: %d ***** routed to : %d\n", i + 118, channel); else printk(KERN_INFO "*****channel: %d ***** routed to : %d\n", i + 54, channel); #else printk(KERN_INFO "*****channel: %d ***** routed to : %d\n", i, channel); #endif if (channel >= 0 && channel < 16) allocated_channels_dma0 |= (1 << channel); else if (channel >= 48 && channel < 64) allocated_channels_dma0 |= (1 << (channel - 48 + 16)); else if (channel >= 16 && channel < 48) allocated_channels_dma1 |= (1 << channel - 16); /* Initialize data for DMA to move */ for (j = 0; j < byte_count; j = j + 4) *((u32 *) (start_address + j)) = test_data; flush_cache_all(); /* Clear interrupt status indicator */ mcf_edma_test_interrupt = FALSE; /* Configure DMA Channel TCD */ #ifdef ALWAYS_ENABLED_MODE mcf_edma_set_tcd_params(channel, (u32) start_address, (u32) dest_address, (0 | MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT), 0x04, byte_count, 0x0, 1, 1, 0x04, 0x0, 0x1, 0x1, 0x0); #else mcf_edma_set_tcd_params(channel, (u32) start_address, (u32) dest_address, (0 | MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT), 0x04, byte_count, 0x0, 1, 1, 0x04, 0x0, 0x1, 0x0, 0x0); #endif /* Start DMA. */ mcf_edma_start_transfer(channel); printk(KERN_INFO "DMA channel %d testing started.\n", channel); /* Wait for DMA to complete */ while (!*mcf_edma_test_interrupt_p) ; /* Test data */ if (mcf_edma_test_block_compare (start_address, dest_address, byte_count)) printk(KERN_INFO "Data transfered correctly.\n"); else printk(KERN_INFO "ERROR: Data error!\n"); printk(KERN_INFO "DMA channel %d testing complete.\n", channel); printk(KERN_INFO "-------------------------------\n"); /* Complement test data so next channel test does not * use same values */ test_data = ~test_data; } test_end: printk(KERN_INFO "All tests have completed\n\n"); printk(KERN_INFO "dma0: 0x%08x dma1: 0x%08x\n\n", allocated_channels_dma0, allocated_channels_dma1); printk(KERN_INFO "Automatically allocated eDMA channels:"); for (i = 0; i < MCF_EDMA_CHANNELS; i++) { if (i < 16 && allocated_channels_dma0 & (1 << i)) { printk(KERN_INFO "%d,\n", i); mcf_edma_free_channel(i, NULL); } if (i >= 48 && allocated_channels_dma0 & (1 << (i - 48 + 16))) { printk(KERN_INFO "%d,\n", i); mcf_edma_free_channel(i, NULL); } if (i >= 16 && i <= 47 && allocated_channels_dma1 & (1 << (i - 16))) { printk(KERN_INFO "%d,\n", i); mcf_edma_free_channel(i, NULL); } } printk(KERN_INFO "===============================================\n\n"); kfree(dest_address); err_free_mem: kfree(start_address); err_out: return; } /********************************************************************/ static int __init mcf_edma_test_init(void) { mcf_edma_test_run(); /* We intentionaly return -EAGAIN to prevent keeping * the module. It does all its work from init() * and doesn't offer any runtime functionality */ return -EAGAIN; } static void __exit mcf_edma_test_exit(void) { } module_init(mcf_edma_test_init); module_exit(mcf_edma_test_exit); MODULE_DESCRIPTION(MCF_EDMA_TEST_DRIVER_INFO); MODULE_AUTHOR(MCF_EDMA_TEST_DRIVER_AUTHOR); MODULE_LICENSE(MCF_EDMA_TEST_DRIVER_LICENSE);