Search
lxdream.org :: lxdream/src/sh4/dmac.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/dmac.c
changeset 54:d8b73031289c
next422:61a0598e07ff
author nkeynes
date Sun Jan 01 08:08:40 2006 +0000 (14 years ago)
permissions -rw-r--r--
last change Add (partial) DMAC implementation
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/sh4/dmac.c Sun Jan 01 08:08:40 2006 +0000
1.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 modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.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 of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.20 + */
1.21 +#define MODULE sh4_module
1.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 0x00
1.51 +#define DMARES_MEMORY_TO_DEVICE 0x02
1.52 +#define DMARES_DEVICE_TO_MEMORY 0x03
1.53 +#define DMARES_MEMORY_TO_MEMORY_AUTO 0x04
1.54 +#define DMARES_MEMORY_TO_PERIPH_AUTO 0x05
1.55 +#define DMARES_PERIPH_TO_MEMORY_AUTO 0x06
1.56 +#define DMARES_SCI_TRANSMIT_EMPTY 0x08
1.57 +#define DMARES_SCI_RECEIVE_FULL 0x09
1.58 +#define DMARES_SCIF_TRANSMIT_EMPTY 0x0A
1.59 +#define DMARES_SCIF_RECEIVE_FULL 0x0B
1.60 +#define DMARES_MEMORY_TO_MEMORY_TMU 0x0C
1.61 +#define DMARES_MEMORY_TO_PERIPH_TMU 0x0D
1.62 +#define DMARES_PERIPH_TO_MEMORY_TMU 0x0E
1.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 to
1.84 + * run the channel
1.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 the
1.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 the
1.114 + * end of the transfer count.
1.115 + * @return actual number of transfers run
1.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 the
1.168 + * ASIC). The DMA channel must be configured for Mem=>dev or it will return
1.169 + * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral
1.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 +}
.