Search
lxdream.org :: lxdream/src/sh4/dmac.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/dmac.c
changeset 54:d8b73031289c
next422:61a0598e07ff
author nkeynes
date Sat Sep 29 05:33:02 2007 +0000 (16 years ago)
permissions -rw-r--r--
last change Modify termination again to allow early exit (eg on end-of-page), as well
as allowing follow through on conditional branches if desired.
view annotate diff log raw
     1 /**
     2  * $Id: dmac.c,v 1.1 2006-01-01 08:08:40 nkeynes Exp $
     3  * 
     4  * SH4 onboard DMA controller (DMAC) peripheral.
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    18 #define MODULE sh4_module
    20 #include "dream.h"
    21 #include "mem.h"
    22 #include "sh4/sh4core.h"
    23 #include "sh4/sh4mmio.h"
    24 #include "sh4/intc.h"
    25 #include "sh4/dmac.h"
    27 static int DMAC_xfer_size[8] = {8, 1, 2, 4, 32, 1, 1, 1};
    29 /* Control flags */
    30 #define CHCR_IE 0x04  /* Interrupt Enable */
    31 #define CHCR_TE 0x02  /* Transfer End */
    32 #define CHCR_DE 0x01  /* DMAC Enable */
    34 #define IS_DMAC_ENABLED() ((MMIO_READ(DMAC,DMAOR)&0x07) == 0x01)
    36 #define IS_AUTO_REQUEST(val) ((val & 0x0C00) == 0x0400)
    37 #define CHANNEL_RESOURCE(val) ((val >> 8) & 0x0F)
    39 #define DMA_SOURCE(chan) MMIO_READ(DMAC, SAR0 + (chan<<4))
    40 #define DMA_DEST(chan) MMIO_READ(DMAC, DAR0 + (chan<<4))
    41 #define DMA_COUNT(chan) (MMIO_READ(DMAC, DMATCR0 + (chan<<4)) & 0x00FFFFFF)
    42 #define DMA_CONTROL(chan) MMIO_READ(DMAC, CHCR0 + (chan<<4))
    43 #define IS_CHANNEL_ENABLED(ctrl) ((ctrl & 0x03) == 0x01)
    44 #define IS_CHANNEL_IRQ_ENABLED(ctrl) (ctrl & CHCR_IE)
    45 #define IS_CHANNEL_IRQ_ACTIVE(ctrl) ((ctrl & (CHCR_IE|CHCR_TE)) == (CHCR_IE|CHCR_TE))
    47 #define DMARES_MEMORY_TO_MEMORY 0x00
    48 #define DMARES_MEMORY_TO_DEVICE 0x02
    49 #define DMARES_DEVICE_TO_MEMORY 0x03
    50 #define DMARES_MEMORY_TO_MEMORY_AUTO 0x04
    51 #define DMARES_MEMORY_TO_PERIPH_AUTO 0x05
    52 #define DMARES_PERIPH_TO_MEMORY_AUTO 0x06
    53 #define DMARES_SCI_TRANSMIT_EMPTY 0x08
    54 #define DMARES_SCI_RECEIVE_FULL 0x09
    55 #define DMARES_SCIF_TRANSMIT_EMPTY 0x0A
    56 #define DMARES_SCIF_RECEIVE_FULL 0x0B
    57 #define DMARES_MEMORY_TO_MEMORY_TMU 0x0C
    58 #define DMARES_MEMORY_TO_PERIPH_TMU 0x0D
    59 #define DMARES_PERIPH_TO_MEMORY_TMU 0x0E
    61 void DMAC_set_control( uint32_t channel, uint32_t val ) 
    62 {
    63     uint32_t oldval = DMA_CONTROL(channel);
    64     int resource;
    65     MMIO_WRITE( DMAC, CHCR0 + (channel<<4), val );
    67     /* If TE or IE are cleared, clear the interrupt request */
    68     if( IS_CHANNEL_IRQ_ACTIVE(oldval) &&
    69 	!IS_CHANNEL_IRQ_ACTIVE(val) )
    70 	intc_clear_interrupt( INT_DMA_DMTE0+channel );
    72     resource = CHANNEL_RESOURCE(val);
    73     if( IS_CHANNEL_ENABLED(val) ) {
    74 	if( resource >= DMARES_MEMORY_TO_MEMORY_AUTO && 
    75 	    resource < DMARES_SCI_TRANSMIT_EMPTY ) {
    76 	    /* Autorun */
    77 	}
    78     }
    80     /* Everything else we don't need to care about until we actually try to
    81      * run the channel
    82      */
    83 }
    85 int32_t mmio_region_DMAC_read( uint32_t reg )
    86 {
    87     return MMIO_READ( DMAC, reg );
    88 }
    90 void mmio_region_DMAC_write( uint32_t reg, uint32_t val ) 
    91 {
    92     switch( reg ) {
    93     case DMAOR:
    94 	MMIO_WRITE( DMAC, reg, val );
    95 	break;
    96     case CHCR0: DMAC_set_control( 0, val ); break;
    97     case CHCR1: DMAC_set_control( 1, val ); break;
    98     case CHCR2: DMAC_set_control( 2, val ); break;
    99     case CHCR3: DMAC_set_control( 3, val ); break;
   100     default:
   101 	MMIO_WRITE( DMAC, reg, val );
   102     }
   103 }
   105 /**
   106  * Execute up to run_count transfers on the specified channel. Assumes the
   107  * trigger for the channel has been received.
   108  *
   109  * @param channel Channel number (0-3) to run.
   110  * @param run_count number of transfers to execute, or 0 to run to the 
   111  * end of the transfer count.
   112  * @return actual number of transfers run
   113  */
   114 int DMAC_run_channel( uint32_t channel, uint32_t run_count )
   115 {
   116     char burst[32]; /* Transfer burst */
   117     uint32_t control = DMA_CONTROL(channel);
   119     if( IS_CHANNEL_ENABLED(control) ) {
   120 	uint32_t source = DMA_SOURCE(channel);
   121 	uint32_t dest = DMA_DEST(channel);
   122 	uint32_t count = DMA_COUNT( channel );
   123 	if( count == 0 )
   124 	    count = 0x01000000;
   125 	if( run_count == 0 || run_count > count )
   126 	    run_count = count;
   127 	uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ];
   128 	int source_step, dest_step;
   129 	int resource = (control >> 8) & 0x0F;
   130 	switch( (control >> 14) & 0x03 ) {
   131 	case 0: dest_step = 0; break;
   132 	case 1: dest_step = xfersize; break;
   133 	case 2: dest_step = -xfersize; break;
   134 	case 3: dest_step = 0; break; /* Illegal */
   135 	}
   136 	switch( (control >> 12) & 0x03 ) {
   137 	case 0: source_step = 0; break;
   138 	case 1: source_step = xfersize; break;
   139 	case 2: source_step = -xfersize; break;
   140 	case 3: source_step = 0; break; /* Illegal */
   141 	}
   143 	while( run_count > 0 ) {
   144 	    /* Origin */
   145 	    if( (resource & 0x02) == 0 ) {
   146 		/* Source is a normal memory address */
   148 	    } else {
   149 		/* Device */
   150 	    }
   152 	    /* Destination */
   153 	    if( (resource & 0x01) == 0 ) {
   154 		/* Destination is a normal memory address */
   155 	    } else {
   156 	    }
   157 	    run_count--; 
   158 	    count--;
   159 	}
   160     }
   161 }
   163 /**
   164  * Fetch a block of data by DMA from memory to an external device (ie the
   165  * ASIC). The DMA channel must be configured for Mem=>dev or it will return
   166  * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral
   167  * transfers.
   168  *
   169  * @return the number of bytes actually transferred.
   170  */
   171 uint32_t DMAC_get_buffer( int channel, char *buf, uint32_t numBytes )
   172 {
   173     uint32_t control = DMA_CONTROL(channel);
   174     uint32_t source, count, run_count, size, i;
   175     char tmp[32];
   177     if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
   178 	return 0;
   180     if( ((control >> 8) & 0x0F) !=  DMARES_MEMORY_TO_DEVICE ) {
   181 	/* Error? */
   183 	return 0;
   184     } 
   186     source = DMA_SOURCE(channel);
   187     count = DMA_COUNT(channel);
   188     if( count == 0 ) count = 0x01000000;
   190     size = DMAC_xfer_size[ (control >> 4)&0x07 ];
   191     run_count = numBytes / size;
   192     if( run_count > count || run_count == 0 )
   193 	run_count = count;
   195     /* Do copy - FIXME: doesn't work when crossing regions */
   196     char *region = mem_get_region( source );
   197     switch( (control >> 12) & 0x03 ) {
   198     case 0: 
   199 	memcpy( tmp, region, size );
   200 	for( i=0; i<run_count; i++ ) {
   201 	    memcpy( buf, tmp, size );
   202 	    buf += size;
   203 	}
   204 	break;
   205     case 1: 
   206 	i = run_count * size;
   207 	memcpy( buf, region, i );
   208 	source += i;
   209 	break;
   210     case 2: 
   211 	for( i=0; i<run_count; i++ ) {
   212 	    memcpy( buf, region, size );
   213 	    buf += size;
   214 	    region -= size;
   215 	}
   216 	source -= (run_count * size);
   217 	break;
   218     default:
   219 	return 0; /* Illegal */
   220     }
   222     /* Update the channel registers */
   223     count -= run_count;
   224     MMIO_WRITE( DMAC, SAR0 + (channel<<4), source );
   225     MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
   226     if( count == 0 ) {
   227 	control |= CHCR_TE; 
   228 	if( IS_CHANNEL_IRQ_ENABLED(control) )
   229 	    intc_raise_interrupt( INT_DMA_DMTE0 + channel );
   230 	MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
   231     }
   233     return run_count * size;
   234 }
   236 uint32_t DMAC_put_buffer( int channel, char *buf, uint32_t numBytes )
   237 {
   238     uint32_t control = DMA_CONTROL(channel);
   239     uint32_t dest, count, run_count, size, i;
   240     char tmp[32];
   242     if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
   243 	return 0;
   245     if( ((control >> 8) & 0x0F) !=  DMARES_DEVICE_TO_MEMORY ) {
   246 	/* Error? */
   247 	return 0;
   248     } 
   250     dest = DMA_DEST(channel);
   251     count = DMA_COUNT(channel);
   252     if( count == 0 ) count = 0x01000000;
   254     size = DMAC_xfer_size[ (control >> 4)&0x07 ];
   255     run_count = numBytes / size;
   256     if( run_count > count || run_count == 0 )
   257 	run_count = count;
   259     /* Do copy - FIXME: doesn't work when crossing regions */
   260     char *region = mem_get_region( dest );
   261     switch( (control >> 12) & 0x03 ) {
   262     case 0: 
   263 	for( i=0; i<run_count; i++ ) { 
   264 	    /* Doesn't make a whole lot of sense, but hey... */
   265 	    memcpy( region, buf, size );
   266 	    buf += size;
   267 	}
   268 	break;
   269     case 1: 
   270 	i = run_count * size;
   271 	memcpy( region, buf, i );
   272 	dest += i;
   273 	break;
   274     case 2: 
   275 	for( i=0; i<run_count; i++ ) {
   276 	    memcpy( region, buf, size );
   277 	    buf += size;
   278 	    region -= size;
   279 	}
   280 	dest -= (run_count * size);
   281 	break;
   282     default:
   283 	return 0; /* Illegal */
   284     }
   286     /* Update the channel registers */
   287     count -= run_count;
   288     MMIO_WRITE( DMAC, DAR0 + (channel<<4), dest );
   289     MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
   290     if( count == 0 ) {
   291 	control |= CHCR_TE; 
   292 	if( IS_CHANNEL_IRQ_ENABLED(control) )
   293 	    intc_raise_interrupt( INT_DMA_DMTE0 + channel );
   294 	MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
   295     }
   296     return run_count * size;
   297 }
   299 void DMAC_reset( void )
   300 {
   302 }
   304 void DMAC_save_state( FILE *F ) 
   305 {
   307 }
   309 int DMAC_load_state( FILE *f )
   310 {
   311     return 0;
   312 }
   314 void DMAC_trigger( int resource )
   315 {
   316     int i;
   317     if( !IS_DMAC_ENABLED() )
   318 	return;
   319     for( i=0; i<4; i++ ) {
   320 	uint32_t control = DMA_CONTROL(i);
   321 	if( IS_CHANNEL_ENABLED(control) ) {
   322 	    uint32_t channel_res = CHANNEL_RESOURCE(control);
   323 	    switch( resource ) {
   324 	    case DMAC_EXTERNAL:
   325 		if( channel_res == DMARES_MEMORY_TO_MEMORY )
   326 		    DMAC_run_channel(i,1);
   327 		break;
   328 	    case DMAC_SCI_TDE:
   329 		if( channel_res == DMARES_SCI_TRANSMIT_EMPTY )
   330 		    DMAC_run_channel(i,1);
   331 		break;
   332 	    case DMAC_SCI_RDF:
   333 		if( channel_res == DMARES_SCI_RECEIVE_FULL )
   334 		    DMAC_run_channel(i,1);
   335 		break;
   336 	    case DMAC_SCIF_TDE:
   337 		if( channel_res == DMARES_SCIF_TRANSMIT_EMPTY )
   338 		    DMAC_run_channel(i,1);
   339 		break;
   340 	    case DMAC_SCIF_RDF:
   341 		if( channel_res == DMARES_SCIF_RECEIVE_FULL )
   342 		    DMAC_run_channel(i,1);
   343 		break;
   344 	    case DMAC_TMU_ICI:
   345 		if( channel_res >= DMARES_MEMORY_TO_MEMORY_TMU ) 
   346 		    DMAC_run_channel(i,1);
   347 		break;
   348 	    }
   349 	}
   350     }
   351 }
.