filename | src/sh4/dmac.c |
changeset | 54:d8b73031289c |
next | 422:61a0598e07ff |
author | nkeynes |
date | Thu Oct 04 08:47:27 2007 +0000 (16 years ago) |
permissions | -rw-r--r-- |
last change | Suppress redundant T flag loads Tweak run_slice for performance |
file | annotate | diff | log | raw |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/sh4/dmac.c Thu Oct 04 08:47:27 2007 +00001.3 @@ -0,0 +1,351 @@1.4 +/**1.5 + * $Id: dmac.c,v 1.1 2006-01-01 08:08:40 nkeynes Exp $1.6 + *1.7 + * SH4 onboard DMA controller (DMAC) peripheral.1.8 + *1.9 + * Copyright (c) 2005 Nathan Keynes.1.10 + *1.11 + * This program is free software; you can redistribute it and/or modify1.12 + * it under the terms of the GNU General Public License as published by1.13 + * the Free Software Foundation; either version 2 of the License, or1.14 + * (at your option) any later version.1.15 + *1.16 + * This program is distributed in the hope that it will be useful,1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1.19 + * GNU General Public License for more details.1.20 + */1.21 +#define MODULE sh4_module1.22 +1.23 +#include "dream.h"1.24 +#include "mem.h"1.25 +#include "sh4/sh4core.h"1.26 +#include "sh4/sh4mmio.h"1.27 +#include "sh4/intc.h"1.28 +#include "sh4/dmac.h"1.29 +1.30 +static int DMAC_xfer_size[8] = {8, 1, 2, 4, 32, 1, 1, 1};1.31 +1.32 +/* Control flags */1.33 +#define CHCR_IE 0x04 /* Interrupt Enable */1.34 +#define CHCR_TE 0x02 /* Transfer End */1.35 +#define CHCR_DE 0x01 /* DMAC Enable */1.36 +1.37 +#define IS_DMAC_ENABLED() ((MMIO_READ(DMAC,DMAOR)&0x07) == 0x01)1.38 +1.39 +#define IS_AUTO_REQUEST(val) ((val & 0x0C00) == 0x0400)1.40 +#define CHANNEL_RESOURCE(val) ((val >> 8) & 0x0F)1.41 +1.42 +#define DMA_SOURCE(chan) MMIO_READ(DMAC, SAR0 + (chan<<4))1.43 +#define DMA_DEST(chan) MMIO_READ(DMAC, DAR0 + (chan<<4))1.44 +#define DMA_COUNT(chan) (MMIO_READ(DMAC, DMATCR0 + (chan<<4)) & 0x00FFFFFF)1.45 +#define DMA_CONTROL(chan) MMIO_READ(DMAC, CHCR0 + (chan<<4))1.46 +#define IS_CHANNEL_ENABLED(ctrl) ((ctrl & 0x03) == 0x01)1.47 +#define IS_CHANNEL_IRQ_ENABLED(ctrl) (ctrl & CHCR_IE)1.48 +#define IS_CHANNEL_IRQ_ACTIVE(ctrl) ((ctrl & (CHCR_IE|CHCR_TE)) == (CHCR_IE|CHCR_TE))1.49 +1.50 +#define DMARES_MEMORY_TO_MEMORY 0x001.51 +#define DMARES_MEMORY_TO_DEVICE 0x021.52 +#define DMARES_DEVICE_TO_MEMORY 0x031.53 +#define DMARES_MEMORY_TO_MEMORY_AUTO 0x041.54 +#define DMARES_MEMORY_TO_PERIPH_AUTO 0x051.55 +#define DMARES_PERIPH_TO_MEMORY_AUTO 0x061.56 +#define DMARES_SCI_TRANSMIT_EMPTY 0x081.57 +#define DMARES_SCI_RECEIVE_FULL 0x091.58 +#define DMARES_SCIF_TRANSMIT_EMPTY 0x0A1.59 +#define DMARES_SCIF_RECEIVE_FULL 0x0B1.60 +#define DMARES_MEMORY_TO_MEMORY_TMU 0x0C1.61 +#define DMARES_MEMORY_TO_PERIPH_TMU 0x0D1.62 +#define DMARES_PERIPH_TO_MEMORY_TMU 0x0E1.63 +1.64 +void DMAC_set_control( uint32_t channel, uint32_t val )1.65 +{1.66 + uint32_t oldval = DMA_CONTROL(channel);1.67 + int resource;1.68 + MMIO_WRITE( DMAC, CHCR0 + (channel<<4), val );1.69 +1.70 + /* If TE or IE are cleared, clear the interrupt request */1.71 + if( IS_CHANNEL_IRQ_ACTIVE(oldval) &&1.72 + !IS_CHANNEL_IRQ_ACTIVE(val) )1.73 + intc_clear_interrupt( INT_DMA_DMTE0+channel );1.74 +1.75 + resource = CHANNEL_RESOURCE(val);1.76 + if( IS_CHANNEL_ENABLED(val) ) {1.77 + if( resource >= DMARES_MEMORY_TO_MEMORY_AUTO &&1.78 + resource < DMARES_SCI_TRANSMIT_EMPTY ) {1.79 + /* Autorun */1.80 + }1.81 + }1.82 +1.83 + /* Everything else we don't need to care about until we actually try to1.84 + * run the channel1.85 + */1.86 +}1.87 +1.88 +int32_t mmio_region_DMAC_read( uint32_t reg )1.89 +{1.90 + return MMIO_READ( DMAC, reg );1.91 +}1.92 +1.93 +void mmio_region_DMAC_write( uint32_t reg, uint32_t val )1.94 +{1.95 + switch( reg ) {1.96 + case DMAOR:1.97 + MMIO_WRITE( DMAC, reg, val );1.98 + break;1.99 + case CHCR0: DMAC_set_control( 0, val ); break;1.100 + case CHCR1: DMAC_set_control( 1, val ); break;1.101 + case CHCR2: DMAC_set_control( 2, val ); break;1.102 + case CHCR3: DMAC_set_control( 3, val ); break;1.103 + default:1.104 + MMIO_WRITE( DMAC, reg, val );1.105 + }1.106 +}1.107 +1.108 +/**1.109 + * Execute up to run_count transfers on the specified channel. Assumes the1.110 + * trigger for the channel has been received.1.111 + *1.112 + * @param channel Channel number (0-3) to run.1.113 + * @param run_count number of transfers to execute, or 0 to run to the1.114 + * end of the transfer count.1.115 + * @return actual number of transfers run1.116 + */1.117 +int DMAC_run_channel( uint32_t channel, uint32_t run_count )1.118 +{1.119 + char burst[32]; /* Transfer burst */1.120 + uint32_t control = DMA_CONTROL(channel);1.121 +1.122 + if( IS_CHANNEL_ENABLED(control) ) {1.123 + uint32_t source = DMA_SOURCE(channel);1.124 + uint32_t dest = DMA_DEST(channel);1.125 + uint32_t count = DMA_COUNT( channel );1.126 + if( count == 0 )1.127 + count = 0x01000000;1.128 + if( run_count == 0 || run_count > count )1.129 + run_count = count;1.130 + uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ];1.131 + int source_step, dest_step;1.132 + int resource = (control >> 8) & 0x0F;1.133 + switch( (control >> 14) & 0x03 ) {1.134 + case 0: dest_step = 0; break;1.135 + case 1: dest_step = xfersize; break;1.136 + case 2: dest_step = -xfersize; break;1.137 + case 3: dest_step = 0; break; /* Illegal */1.138 + }1.139 + switch( (control >> 12) & 0x03 ) {1.140 + case 0: source_step = 0; break;1.141 + case 1: source_step = xfersize; break;1.142 + case 2: source_step = -xfersize; break;1.143 + case 3: source_step = 0; break; /* Illegal */1.144 + }1.145 +1.146 + while( run_count > 0 ) {1.147 + /* Origin */1.148 + if( (resource & 0x02) == 0 ) {1.149 + /* Source is a normal memory address */1.150 +1.151 + } else {1.152 + /* Device */1.153 + }1.154 +1.155 + /* Destination */1.156 + if( (resource & 0x01) == 0 ) {1.157 + /* Destination is a normal memory address */1.158 + } else {1.159 + }1.160 + run_count--;1.161 + count--;1.162 + }1.163 + }1.164 +}1.165 +1.166 +/**1.167 + * Fetch a block of data by DMA from memory to an external device (ie the1.168 + * ASIC). The DMA channel must be configured for Mem=>dev or it will return1.169 + * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral1.170 + * transfers.1.171 + *1.172 + * @return the number of bytes actually transferred.1.173 + */1.174 +uint32_t DMAC_get_buffer( int channel, char *buf, uint32_t numBytes )1.175 +{1.176 + uint32_t control = DMA_CONTROL(channel);1.177 + uint32_t source, count, run_count, size, i;1.178 + char tmp[32];1.179 +1.180 + if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )1.181 + return 0;1.182 +1.183 + if( ((control >> 8) & 0x0F) != DMARES_MEMORY_TO_DEVICE ) {1.184 + /* Error? */1.185 +1.186 + return 0;1.187 + }1.188 +1.189 + source = DMA_SOURCE(channel);1.190 + count = DMA_COUNT(channel);1.191 + if( count == 0 ) count = 0x01000000;1.192 +1.193 + size = DMAC_xfer_size[ (control >> 4)&0x07 ];1.194 + run_count = numBytes / size;1.195 + if( run_count > count || run_count == 0 )1.196 + run_count = count;1.197 +1.198 + /* Do copy - FIXME: doesn't work when crossing regions */1.199 + char *region = mem_get_region( source );1.200 + switch( (control >> 12) & 0x03 ) {1.201 + case 0:1.202 + memcpy( tmp, region, size );1.203 + for( i=0; i<run_count; i++ ) {1.204 + memcpy( buf, tmp, size );1.205 + buf += size;1.206 + }1.207 + break;1.208 + case 1:1.209 + i = run_count * size;1.210 + memcpy( buf, region, i );1.211 + source += i;1.212 + break;1.213 + case 2:1.214 + for( i=0; i<run_count; i++ ) {1.215 + memcpy( buf, region, size );1.216 + buf += size;1.217 + region -= size;1.218 + }1.219 + source -= (run_count * size);1.220 + break;1.221 + default:1.222 + return 0; /* Illegal */1.223 + }1.224 +1.225 + /* Update the channel registers */1.226 + count -= run_count;1.227 + MMIO_WRITE( DMAC, SAR0 + (channel<<4), source );1.228 + MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );1.229 + if( count == 0 ) {1.230 + control |= CHCR_TE;1.231 + if( IS_CHANNEL_IRQ_ENABLED(control) )1.232 + intc_raise_interrupt( INT_DMA_DMTE0 + channel );1.233 + MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );1.234 + }1.235 +1.236 + return run_count * size;1.237 +}1.238 +1.239 +uint32_t DMAC_put_buffer( int channel, char *buf, uint32_t numBytes )1.240 +{1.241 + uint32_t control = DMA_CONTROL(channel);1.242 + uint32_t dest, count, run_count, size, i;1.243 + char tmp[32];1.244 +1.245 + if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )1.246 + return 0;1.247 +1.248 + if( ((control >> 8) & 0x0F) != DMARES_DEVICE_TO_MEMORY ) {1.249 + /* Error? */1.250 + return 0;1.251 + }1.252 +1.253 + dest = DMA_DEST(channel);1.254 + count = DMA_COUNT(channel);1.255 + if( count == 0 ) count = 0x01000000;1.256 +1.257 + size = DMAC_xfer_size[ (control >> 4)&0x07 ];1.258 + run_count = numBytes / size;1.259 + if( run_count > count || run_count == 0 )1.260 + run_count = count;1.261 +1.262 + /* Do copy - FIXME: doesn't work when crossing regions */1.263 + char *region = mem_get_region( dest );1.264 + switch( (control >> 12) & 0x03 ) {1.265 + case 0:1.266 + for( i=0; i<run_count; i++ ) {1.267 + /* Doesn't make a whole lot of sense, but hey... */1.268 + memcpy( region, buf, size );1.269 + buf += size;1.270 + }1.271 + break;1.272 + case 1:1.273 + i = run_count * size;1.274 + memcpy( region, buf, i );1.275 + dest += i;1.276 + break;1.277 + case 2:1.278 + for( i=0; i<run_count; i++ ) {1.279 + memcpy( region, buf, size );1.280 + buf += size;1.281 + region -= size;1.282 + }1.283 + dest -= (run_count * size);1.284 + break;1.285 + default:1.286 + return 0; /* Illegal */1.287 + }1.288 +1.289 + /* Update the channel registers */1.290 + count -= run_count;1.291 + MMIO_WRITE( DMAC, DAR0 + (channel<<4), dest );1.292 + MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );1.293 + if( count == 0 ) {1.294 + control |= CHCR_TE;1.295 + if( IS_CHANNEL_IRQ_ENABLED(control) )1.296 + intc_raise_interrupt( INT_DMA_DMTE0 + channel );1.297 + MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );1.298 + }1.299 + return run_count * size;1.300 +}1.301 +1.302 +void DMAC_reset( void )1.303 +{1.304 +1.305 +}1.306 +1.307 +void DMAC_save_state( FILE *F )1.308 +{1.309 +1.310 +}1.311 +1.312 +int DMAC_load_state( FILE *f )1.313 +{1.314 + return 0;1.315 +}1.316 +1.317 +void DMAC_trigger( int resource )1.318 +{1.319 + int i;1.320 + if( !IS_DMAC_ENABLED() )1.321 + return;1.322 + for( i=0; i<4; i++ ) {1.323 + uint32_t control = DMA_CONTROL(i);1.324 + if( IS_CHANNEL_ENABLED(control) ) {1.325 + uint32_t channel_res = CHANNEL_RESOURCE(control);1.326 + switch( resource ) {1.327 + case DMAC_EXTERNAL:1.328 + if( channel_res == DMARES_MEMORY_TO_MEMORY )1.329 + DMAC_run_channel(i,1);1.330 + break;1.331 + case DMAC_SCI_TDE:1.332 + if( channel_res == DMARES_SCI_TRANSMIT_EMPTY )1.333 + DMAC_run_channel(i,1);1.334 + break;1.335 + case DMAC_SCI_RDF:1.336 + if( channel_res == DMARES_SCI_RECEIVE_FULL )1.337 + DMAC_run_channel(i,1);1.338 + break;1.339 + case DMAC_SCIF_TDE:1.340 + if( channel_res == DMARES_SCIF_TRANSMIT_EMPTY )1.341 + DMAC_run_channel(i,1);1.342 + break;1.343 + case DMAC_SCIF_RDF:1.344 + if( channel_res == DMARES_SCIF_RECEIVE_FULL )1.345 + DMAC_run_channel(i,1);1.346 + break;1.347 + case DMAC_TMU_ICI:1.348 + if( channel_res >= DMARES_MEMORY_TO_MEMORY_TMU )1.349 + DMAC_run_channel(i,1);1.350 + break;1.351 + }1.352 + }1.353 + }1.354 +}
.