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 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
nkeynes@54
     1
/**
nkeynes@54
     2
 * $Id: dmac.c,v 1.1 2006-01-01 08:08:40 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
 * @return actual number of transfers run
nkeynes@54
   113
 */
nkeynes@54
   114
int DMAC_run_channel( uint32_t channel, uint32_t run_count )
nkeynes@54
   115
{
nkeynes@54
   116
    char burst[32]; /* Transfer burst */
nkeynes@54
   117
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   118
nkeynes@54
   119
    if( IS_CHANNEL_ENABLED(control) ) {
nkeynes@54
   120
	uint32_t source = DMA_SOURCE(channel);
nkeynes@54
   121
	uint32_t dest = DMA_DEST(channel);
nkeynes@54
   122
	uint32_t count = DMA_COUNT( channel );
nkeynes@54
   123
	if( count == 0 )
nkeynes@54
   124
	    count = 0x01000000;
nkeynes@54
   125
	if( run_count == 0 || run_count > count )
nkeynes@54
   126
	    run_count = count;
nkeynes@54
   127
	uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@54
   128
	int source_step, dest_step;
nkeynes@54
   129
	int resource = (control >> 8) & 0x0F;
nkeynes@54
   130
	switch( (control >> 14) & 0x03 ) {
nkeynes@54
   131
	case 0: dest_step = 0; break;
nkeynes@54
   132
	case 1: dest_step = xfersize; break;
nkeynes@54
   133
	case 2: dest_step = -xfersize; break;
nkeynes@54
   134
	case 3: dest_step = 0; break; /* Illegal */
nkeynes@54
   135
	}
nkeynes@54
   136
	switch( (control >> 12) & 0x03 ) {
nkeynes@54
   137
	case 0: source_step = 0; break;
nkeynes@54
   138
	case 1: source_step = xfersize; break;
nkeynes@54
   139
	case 2: source_step = -xfersize; break;
nkeynes@54
   140
	case 3: source_step = 0; break; /* Illegal */
nkeynes@54
   141
	}
nkeynes@54
   142
	
nkeynes@54
   143
	while( run_count > 0 ) {
nkeynes@54
   144
	    /* Origin */
nkeynes@54
   145
	    if( (resource & 0x02) == 0 ) {
nkeynes@54
   146
		/* Source is a normal memory address */
nkeynes@54
   147
		
nkeynes@54
   148
	    } else {
nkeynes@54
   149
		/* Device */
nkeynes@54
   150
	    }
nkeynes@54
   151
	    
nkeynes@54
   152
	    /* Destination */
nkeynes@54
   153
	    if( (resource & 0x01) == 0 ) {
nkeynes@54
   154
		/* Destination is a normal memory address */
nkeynes@54
   155
	    } else {
nkeynes@54
   156
	    }
nkeynes@54
   157
	    run_count--; 
nkeynes@54
   158
	    count--;
nkeynes@54
   159
	}
nkeynes@54
   160
    }
nkeynes@54
   161
}
nkeynes@54
   162
nkeynes@54
   163
/**
nkeynes@54
   164
 * Fetch a block of data by DMA from memory to an external device (ie the
nkeynes@54
   165
 * ASIC). The DMA channel must be configured for Mem=>dev or it will return
nkeynes@54
   166
 * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral
nkeynes@54
   167
 * transfers.
nkeynes@54
   168
 *
nkeynes@54
   169
 * @return the number of bytes actually transferred.
nkeynes@54
   170
 */
nkeynes@54
   171
uint32_t DMAC_get_buffer( int channel, char *buf, uint32_t numBytes )
nkeynes@54
   172
{
nkeynes@54
   173
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   174
    uint32_t source, count, run_count, size, i;
nkeynes@54
   175
    char tmp[32];
nkeynes@54
   176
nkeynes@54
   177
    if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
nkeynes@54
   178
	return 0;
nkeynes@54
   179
    
nkeynes@54
   180
    if( ((control >> 8) & 0x0F) !=  DMARES_MEMORY_TO_DEVICE ) {
nkeynes@54
   181
	/* Error? */
nkeynes@54
   182
	
nkeynes@54
   183
	return 0;
nkeynes@54
   184
    } 
nkeynes@54
   185
    
nkeynes@54
   186
    source = DMA_SOURCE(channel);
nkeynes@54
   187
    count = DMA_COUNT(channel);
nkeynes@54
   188
    if( count == 0 ) count = 0x01000000;
nkeynes@54
   189
nkeynes@54
   190
    size = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@54
   191
    run_count = numBytes / size;
nkeynes@54
   192
    if( run_count > count || run_count == 0 )
nkeynes@54
   193
	run_count = count;
nkeynes@54
   194
nkeynes@54
   195
    /* Do copy - FIXME: doesn't work when crossing regions */
nkeynes@54
   196
    char *region = mem_get_region( source );
nkeynes@54
   197
    switch( (control >> 12) & 0x03 ) {
nkeynes@54
   198
    case 0: 
nkeynes@54
   199
	memcpy( tmp, region, size );
nkeynes@54
   200
	for( i=0; i<run_count; i++ ) {
nkeynes@54
   201
	    memcpy( buf, tmp, size );
nkeynes@54
   202
	    buf += size;
nkeynes@54
   203
	}
nkeynes@54
   204
	break;
nkeynes@54
   205
    case 1: 
nkeynes@54
   206
	i = run_count * size;
nkeynes@54
   207
	memcpy( buf, region, i );
nkeynes@54
   208
	source += i;
nkeynes@54
   209
	break;
nkeynes@54
   210
    case 2: 
nkeynes@54
   211
	for( i=0; i<run_count; i++ ) {
nkeynes@54
   212
	    memcpy( buf, region, size );
nkeynes@54
   213
	    buf += size;
nkeynes@54
   214
	    region -= size;
nkeynes@54
   215
	}
nkeynes@54
   216
	source -= (run_count * size);
nkeynes@54
   217
	break;
nkeynes@54
   218
    default:
nkeynes@54
   219
	return 0; /* Illegal */
nkeynes@54
   220
    }
nkeynes@54
   221
nkeynes@54
   222
    /* Update the channel registers */
nkeynes@54
   223
    count -= run_count;
nkeynes@54
   224
    MMIO_WRITE( DMAC, SAR0 + (channel<<4), source );
nkeynes@54
   225
    MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
nkeynes@54
   226
    if( count == 0 ) {
nkeynes@54
   227
	control |= CHCR_TE; 
nkeynes@54
   228
	if( IS_CHANNEL_IRQ_ENABLED(control) )
nkeynes@54
   229
	    intc_raise_interrupt( INT_DMA_DMTE0 + channel );
nkeynes@54
   230
	MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
nkeynes@54
   231
    }
nkeynes@54
   232
nkeynes@54
   233
    return run_count * size;
nkeynes@54
   234
}
nkeynes@54
   235
nkeynes@54
   236
uint32_t DMAC_put_buffer( int channel, char *buf, uint32_t numBytes )
nkeynes@54
   237
{
nkeynes@54
   238
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   239
    uint32_t dest, count, run_count, size, i;
nkeynes@54
   240
    char tmp[32];
nkeynes@54
   241
nkeynes@54
   242
    if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
nkeynes@54
   243
	return 0;
nkeynes@54
   244
    
nkeynes@54
   245
    if( ((control >> 8) & 0x0F) !=  DMARES_DEVICE_TO_MEMORY ) {
nkeynes@54
   246
	/* Error? */
nkeynes@54
   247
	return 0;
nkeynes@54
   248
    } 
nkeynes@54
   249
    
nkeynes@54
   250
    dest = DMA_DEST(channel);
nkeynes@54
   251
    count = DMA_COUNT(channel);
nkeynes@54
   252
    if( count == 0 ) count = 0x01000000;
nkeynes@54
   253
nkeynes@54
   254
    size = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@54
   255
    run_count = numBytes / size;
nkeynes@54
   256
    if( run_count > count || run_count == 0 )
nkeynes@54
   257
	run_count = count;
nkeynes@54
   258
nkeynes@54
   259
    /* Do copy - FIXME: doesn't work when crossing regions */
nkeynes@54
   260
    char *region = mem_get_region( dest );
nkeynes@54
   261
    switch( (control >> 12) & 0x03 ) {
nkeynes@54
   262
    case 0: 
nkeynes@54
   263
	for( i=0; i<run_count; i++ ) { 
nkeynes@54
   264
	    /* Doesn't make a whole lot of sense, but hey... */
nkeynes@54
   265
	    memcpy( region, buf, size );
nkeynes@54
   266
	    buf += size;
nkeynes@54
   267
	}
nkeynes@54
   268
	break;
nkeynes@54
   269
    case 1: 
nkeynes@54
   270
	i = run_count * size;
nkeynes@54
   271
	memcpy( region, buf, i );
nkeynes@54
   272
	dest += i;
nkeynes@54
   273
	break;
nkeynes@54
   274
    case 2: 
nkeynes@54
   275
	for( i=0; i<run_count; i++ ) {
nkeynes@54
   276
	    memcpy( region, buf, size );
nkeynes@54
   277
	    buf += size;
nkeynes@54
   278
	    region -= size;
nkeynes@54
   279
	}
nkeynes@54
   280
	dest -= (run_count * size);
nkeynes@54
   281
	break;
nkeynes@54
   282
    default:
nkeynes@54
   283
	return 0; /* Illegal */
nkeynes@54
   284
    }
nkeynes@54
   285
nkeynes@54
   286
    /* Update the channel registers */
nkeynes@54
   287
    count -= run_count;
nkeynes@54
   288
    MMIO_WRITE( DMAC, DAR0 + (channel<<4), dest );
nkeynes@54
   289
    MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
nkeynes@54
   290
    if( count == 0 ) {
nkeynes@54
   291
	control |= CHCR_TE; 
nkeynes@54
   292
	if( IS_CHANNEL_IRQ_ENABLED(control) )
nkeynes@54
   293
	    intc_raise_interrupt( INT_DMA_DMTE0 + channel );
nkeynes@54
   294
	MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
nkeynes@54
   295
    }
nkeynes@54
   296
    return run_count * size;
nkeynes@54
   297
}
nkeynes@54
   298
nkeynes@54
   299
void DMAC_reset( void )
nkeynes@54
   300
{
nkeynes@54
   301
nkeynes@54
   302
}
nkeynes@54
   303
nkeynes@54
   304
void DMAC_save_state( FILE *F ) 
nkeynes@54
   305
{
nkeynes@54
   306
nkeynes@54
   307
}
nkeynes@54
   308
nkeynes@54
   309
int DMAC_load_state( FILE *f )
nkeynes@54
   310
{
nkeynes@54
   311
    return 0;
nkeynes@54
   312
}
nkeynes@54
   313
nkeynes@54
   314
void DMAC_trigger( int resource )
nkeynes@54
   315
{
nkeynes@54
   316
    int i;
nkeynes@54
   317
    if( !IS_DMAC_ENABLED() )
nkeynes@54
   318
	return;
nkeynes@54
   319
    for( i=0; i<4; i++ ) {
nkeynes@54
   320
	uint32_t control = DMA_CONTROL(i);
nkeynes@54
   321
	if( IS_CHANNEL_ENABLED(control) ) {
nkeynes@54
   322
	    uint32_t channel_res = CHANNEL_RESOURCE(control);
nkeynes@54
   323
	    switch( resource ) {
nkeynes@54
   324
	    case DMAC_EXTERNAL:
nkeynes@54
   325
		if( channel_res == DMARES_MEMORY_TO_MEMORY )
nkeynes@54
   326
		    DMAC_run_channel(i,1);
nkeynes@54
   327
		break;
nkeynes@54
   328
	    case DMAC_SCI_TDE:
nkeynes@54
   329
		if( channel_res == DMARES_SCI_TRANSMIT_EMPTY )
nkeynes@54
   330
		    DMAC_run_channel(i,1);
nkeynes@54
   331
		break;
nkeynes@54
   332
	    case DMAC_SCI_RDF:
nkeynes@54
   333
		if( channel_res == DMARES_SCI_RECEIVE_FULL )
nkeynes@54
   334
		    DMAC_run_channel(i,1);
nkeynes@54
   335
		break;
nkeynes@54
   336
	    case DMAC_SCIF_TDE:
nkeynes@54
   337
		if( channel_res == DMARES_SCIF_TRANSMIT_EMPTY )
nkeynes@54
   338
		    DMAC_run_channel(i,1);
nkeynes@54
   339
		break;
nkeynes@54
   340
	    case DMAC_SCIF_RDF:
nkeynes@54
   341
		if( channel_res == DMARES_SCIF_RECEIVE_FULL )
nkeynes@54
   342
		    DMAC_run_channel(i,1);
nkeynes@54
   343
		break;
nkeynes@54
   344
	    case DMAC_TMU_ICI:
nkeynes@54
   345
		if( channel_res >= DMARES_MEMORY_TO_MEMORY_TMU ) 
nkeynes@54
   346
		    DMAC_run_channel(i,1);
nkeynes@54
   347
		break;
nkeynes@54
   348
	    }
nkeynes@54
   349
	}
nkeynes@54
   350
    }
nkeynes@54
   351
}
.