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