/////////////////////////////////////////////////////////////////////////////////// // File : dma_driver.c // Date : 23/11/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #if !defined(X_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(Y_SIZE) # error: You must define X_SIZE in the hard_config.h file #endif #if !defined(X_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(Y_WIDTH) # error: You must define X_WIDTH in the hard_config.h file #endif #if !defined(NB_DMA_CHANNELS) # error: You must define NB_DMA_CHANNELS in the hard_config.h file #endif #if !defined(SEG_DMA_BASE) # error: You must define SEG_DMA_BASE in the hard_config.h file #endif #if !defined(PERI_CLUSTER_INCREMENT) # error: You must define PERI_CLUSTER_INCREMENT in the hard_config.h file #endif extern volatile unsigned int _ptabs_vaddr[]; /////////////////////////////////////////////////////////////////////////////// // This low level function returns the value contained in register "index" // in the DMA component contained in cluster "cluster_xy" /////////////////////////////////////////////////////////////////////////////// static unsigned int _dma_get_register( unsigned int cluster_xy, // cluster index unsigned int channel_id, // channel index unsigned int index ) // register index { unsigned int vaddr = SEG_DMA_BASE + (cluster_xy * PERI_CLUSTER_INCREMENT) + (channel_id * DMA_SPAN) + (index << 2); return ioread32( (void*)vaddr ); } /////////////////////////////////////////////////////////////////////////////// // This low level function sets a new value in register "index" // in the DMA component contained in cluster "cluster_xy" /////////////////////////////////////////////////////////////////////////////// static void _dma_set_register( unsigned int cluster_xy, // cluster index unsigned int channel_id, // channel index unsigned int index, // register index unsigned int value ) // value to be written { unsigned int vaddr = SEG_DMA_BASE + (cluster_xy * PERI_CLUSTER_INCREMENT) + (channel_id * DMA_SPAN) + (index << 2); iowrite32( (void*)vaddr, value ); } //////////////////////////////////////////////// unsigned int _dma_init( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // disable interrupt for selected channel _dma_set_register(cluster_xy, channel_id, DMA_IRQ_DISABLE, 1); return 0; #else return 1; #endif } ////////////////////////////////////////////////// unsigned int _dma_reset( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // reset selected channel _dma_set_register(cluster_xy, channel_id, DMA_RESET, 0); return 0; #else return 1; #endif } ////////////////////////////////////////////////////// unsigned int _dma_get_status( unsigned int cluster_xy, unsigned int channel_id ) { #if NB_DMA_CHANNELS > 0 // parameters checking unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) return 1; if (y >= Y_SIZE) return 1; if (channel_id >= NB_DMA_CHANNELS) return 1; // get selected channel status return _dma_get_register(cluster_xy, channel_id, DMA_LEN); #else return DMA_IDLE; #endif } //////////////////////////////////////////////////////// void _dma_start_transfer( unsigned int cluster_xy, // DMA cluster unsigned int channel_id, // DMA channel unsigned long long dst_paddr, // physical address unsigned long long src_paddr, // physical address unsigned int size ) // bytes { #if NB_DMA_CHANNELS > 0 // selected channel configuration and lauching _dma_set_register(cluster_xy, channel_id, DMA_SRC, (unsigned int)(src_paddr)); _dma_set_register(cluster_xy, channel_id, DMA_SRC_EXT, (unsigned int)(src_paddr>>32)); _dma_set_register(cluster_xy, channel_id, DMA_DST, (unsigned int)(dst_paddr)); _dma_set_register(cluster_xy, channel_id, DMA_DST_EXT, (unsigned int)(dst_paddr>>32)); _dma_set_register(cluster_xy, channel_id, DMA_LEN, (unsigned int)size); #endif } /////////////////////////////////////////////////////// void _dma_physical_copy( unsigned int cluster_xy, // DMA cluster unsigned int channel_id, // DMA channel unsigned long long dst_paddr, // destination physical address unsigned long long src_paddr, // source physical address unsigned int size ) // bytes { #if NB_DMA_CHANNELS > 0 // check DMA channel parameters unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) ) { _puts("\n[DMA ERROR] in _dma_physical_copy() : illegal DMA channel "); _exit(); } // check buffers alignment constraints if ( (dst_paddr & 0x3) || (src_paddr & 0x3) || (size & 0x3) ) { _puts("\n[DMA ERROR] in _dma_physical_copy() : buffer unaligned\n"); _exit(); } #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] enter _dma_physical_copy() for channel["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( channel_id ); _puts("] at cycle "); _putd( _get_proctime() ); _puts("\n - src_paddr = "); _putl( src_paddr ); _puts("\n - dst_paddr = "); _putl( dst_paddr ); _puts("\n - bytes = "); _putd( size ); _puts("\n"); #endif // dma channel configuration & lauching _dma_start_transfer( cluster_xy, channel_id, dst_paddr, src_paddr, size ); // scan dma channel status unsigned int status = _dma_get_status( cluster_xy, channel_id ); while( (status != DMA_SUCCESS) && (status != DMA_READ_ERROR) && (status != DMA_WRITE_ERROR) ) { status = _dma_get_status( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] _dma_physical_copy() : ... waiting on DMA_STATUS register\n"); #endif } // analyse status if( status != DMA_SUCCESS ) { _puts("\n[DMA ERROR] in _dma_physical_copy() : bad DMA_STATUS"); _exit(); } // reset dma channel _dma_reset( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] exit _dma_physical_copy() at cycle "); _putd( _get_proctime() ); _puts("\n"); #endif #else // NB_DMA_CHANNELS == 0 _puts("\n[DMA ERROR] in _dma_physical_copy() : NB_DMA_CHANNELS == 0\n"); _exit(); #endif } //////////////////////////////////////// void _dma_copy( unsigned int cluster_xy, // DMA cluster unsigned int channel_id, // DMA channel unsigned int vspace_id, // vspace index for v2p translation unsigned int dst_vaddr, // dst_vaddr buffer vbase unsigned int src_vaddr, // src_vaddr buffer vbase unsigned int size ) // bytes { #if NB_DMA_CHANNELS > 0 // check DMA channel parameters unsigned int x = cluster_xy >> Y_WIDTH; unsigned int y = cluster_xy & ((1<= X_SIZE) || (y >= Y_SIZE) || (channel_id >= NB_DMA_CHANNELS) ) { _puts("\n[DMA ERROR] in _dma_copy() : illegal DMA channel "); _exit(); } // check buffers alignment constraints if ( (dst_vaddr & 0x3) || (src_vaddr & 0x3) || (size & 0x3) ) { _puts("\n[DMA ERROR] in _dma_copy() : buffer unaligned\n"); _exit(); } unsigned int ppn; unsigned int flags; #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] enter _dma_copy() for channel["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( channel_id ); _puts("] at cycle "); _putd( _get_proctime() ); _puts("\n - src_vaddr = "); _putx( src_vaddr ); _puts("\n - dst_vaddr = "); _putx( dst_vaddr ); _puts("\n - bytes = "); _putd( size ); _puts("\n"); #endif // checking alignment constraints if ( (((unsigned int)dst_vaddr) & 0x3) || (((unsigned int)src_vaddr) & 0x3) || (size & 0x3) ) { _puts("\n[DMA ERROR] in _dma_copy() : buffer unaligned\n"); _exit(); } // get vspace page table pointer unsigned int pt = _ptabs_vaddr[vspace_id]; // get src_paddr buffer physical addresse _v2p_translate( (page_table_t*)pt, // page table pointer src_vaddr>>12, // vpn &ppn, // ppn &flags ); // flags unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | (unsigned long long)(src_vaddr & 0x00000FFF); // get dst_paddr buffer physical addresse _v2p_translate( (page_table_t*)pt, // page table pointer dst_vaddr>>12, // vpn &ppn, // ppn &flags ); // flags unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | (unsigned long long)(dst_vaddr & 0x00000FFF); #if GIET_DEBUG_DMA_DRIVER _puts("\n - src_paddr = "); _putl( src_paddr ); _puts("\n - dst_paddr = "); _putl( dst_paddr ); _puts("\n"); #endif // dma channel configuration & lauching _dma_start_transfer( cluster_xy, channel_id, dst_paddr, src_paddr, size ); // scan dma channel status unsigned int status = _dma_get_status( cluster_xy, channel_id ); while( (status != DMA_SUCCESS) && (status != DMA_READ_ERROR) && (status != DMA_WRITE_ERROR) ) { status = _dma_get_status( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register\n"); #endif } // analyse status if( status != DMA_SUCCESS ) { _puts("\n[DMA ERROR] in _dma_copy() : bad DMA_STATUS\n"); _exit(); } // reset dma channel _dma_reset( cluster_xy, channel_id ); #if GIET_DEBUG_DMA_DRIVER _puts("\n[DMA DEBUG] exit _dma_copy() at cycle "); _putd( _get_proctime() ); _puts("\n"); #endif #else // NB_DMA_CHANNELS == 0 _puts("\n[DMA ERROR] in _dma_copy() : NB_DMA_CHANNELS == 0\n"); _exit(); #endif } // end _dma_copy ///////////////////////////////////// void _dma_isr( unsigned int irq_type, unsigned int irq_id, unsigned int channel ) { _puts("\n[DMA ERROR] _dma_isr() not implemented\n"); _exit(); } // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4