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 Wed Jan 03 09:00:17 2007 +0000 (17 years ago)
permissions -rw-r--r--
last change Adjust timers when they're read rather than waiting until the next time
slice. Also temporarily cut the CPU time by 4.
Initialize the FRQCR register to 0x0E0A for convenience
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 }
.