Search
lxdream.org :: lxdream/src/sh4/dmac.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/dmac.c
changeset 929:fd8cb0c82f5f
prev736:a02d1475ccfd
next975:007bf7eb944f
author nkeynes
date Sat Dec 20 03:01:40 2008 +0000 (15 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change First pass experiment using cached decoding.
file annotate diff log raw
nkeynes@54
     1
/**
nkeynes@561
     2
 * $Id$
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@736
    66
nkeynes@54
    67
    /* If TE or IE are cleared, clear the interrupt request */
nkeynes@54
    68
    if( IS_CHANNEL_IRQ_ACTIVE(oldval) &&
nkeynes@736
    69
            !IS_CHANNEL_IRQ_ACTIVE(val) )
nkeynes@736
    70
        intc_clear_interrupt( INT_DMA_DMTE0+channel );
nkeynes@736
    71
nkeynes@54
    72
    resource = CHANNEL_RESOURCE(val);
nkeynes@54
    73
    if( IS_CHANNEL_ENABLED(val) ) {
nkeynes@736
    74
        if( resource >= DMARES_MEMORY_TO_MEMORY_AUTO && 
nkeynes@736
    75
                resource < DMARES_SCI_TRANSMIT_EMPTY ) {
nkeynes@736
    76
            /* Autorun */
nkeynes@736
    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@929
    85
MMIO_REGION_READ_FN( DMAC, reg )
nkeynes@54
    86
{
nkeynes@929
    87
    return MMIO_READ( DMAC, reg&0xFFF );
nkeynes@54
    88
}
nkeynes@54
    89
nkeynes@929
    90
MMIO_REGION_WRITE_FN( DMAC, reg, val )
nkeynes@54
    91
{
nkeynes@929
    92
    reg &= 0xFFF;
nkeynes@54
    93
    switch( reg ) {
nkeynes@54
    94
    case DMAOR:
nkeynes@736
    95
        MMIO_WRITE( DMAC, reg, val );
nkeynes@736
    96
        break;
nkeynes@54
    97
    case CHCR0: DMAC_set_control( 0, val ); break;
nkeynes@54
    98
    case CHCR1: DMAC_set_control( 1, val ); break;
nkeynes@54
    99
    case CHCR2: DMAC_set_control( 2, val ); break;
nkeynes@54
   100
    case CHCR3: DMAC_set_control( 3, val ); break;
nkeynes@54
   101
    default:
nkeynes@736
   102
        MMIO_WRITE( DMAC, reg, val );
nkeynes@54
   103
    }
nkeynes@54
   104
}
nkeynes@54
   105
nkeynes@54
   106
/**
nkeynes@54
   107
 * Execute up to run_count transfers on the specified channel. Assumes the
nkeynes@54
   108
 * trigger for the channel has been received.
nkeynes@54
   109
 *
nkeynes@54
   110
 * @param channel Channel number (0-3) to run.
nkeynes@54
   111
 * @param run_count number of transfers to execute, or 0 to run to the 
nkeynes@54
   112
 * end of the transfer count.
nkeynes@54
   113
 */
nkeynes@422
   114
void DMAC_run_channel( uint32_t channel, uint32_t run_count )
nkeynes@54
   115
{
nkeynes@422
   116
nkeynes@422
   117
#if 0 /* Should really finish this */
nkeynes@54
   118
    char burst[32]; /* Transfer burst */
nkeynes@54
   119
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   120
nkeynes@54
   121
    if( IS_CHANNEL_ENABLED(control) ) {
nkeynes@736
   122
        uint32_t source = DMA_SOURCE(channel);
nkeynes@736
   123
        uint32_t dest = DMA_DEST(channel);
nkeynes@736
   124
        uint32_t count = DMA_COUNT( channel );
nkeynes@736
   125
        if( count == 0 )
nkeynes@736
   126
            count = 0x01000000;
nkeynes@736
   127
        if( run_count == 0 || run_count > count )
nkeynes@736
   128
            run_count = count;
nkeynes@736
   129
        uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@736
   130
        int source_step, dest_step;
nkeynes@736
   131
        int resource = (control >> 8) & 0x0F;
nkeynes@736
   132
        switch( (control >> 14) & 0x03 ) {
nkeynes@736
   133
        case 0: dest_step = 0; break;
nkeynes@736
   134
        case 1: dest_step = xfersize; break;
nkeynes@736
   135
        case 2: dest_step = -xfersize; break;
nkeynes@736
   136
        case 3: dest_step = 0; break; /* Illegal */
nkeynes@736
   137
        }
nkeynes@736
   138
        switch( (control >> 12) & 0x03 ) {
nkeynes@736
   139
        case 0: source_step = 0; break;
nkeynes@736
   140
        case 1: source_step = xfersize; break;
nkeynes@736
   141
        case 2: source_step = -xfersize; break;
nkeynes@736
   142
        case 3: source_step = 0; break; /* Illegal */
nkeynes@736
   143
        }
nkeynes@736
   144
nkeynes@736
   145
        while( run_count > 0 ) {
nkeynes@736
   146
            /* Origin */
nkeynes@736
   147
            if( (resource & 0x02) == 0 ) {
nkeynes@736
   148
                /* Source is a normal memory address */
nkeynes@736
   149
nkeynes@736
   150
            } else {
nkeynes@736
   151
                /* Device */
nkeynes@736
   152
            }
nkeynes@736
   153
nkeynes@736
   154
            /* Destination */
nkeynes@736
   155
            if( (resource & 0x01) == 0 ) {
nkeynes@736
   156
                /* Destination is a normal memory address */
nkeynes@736
   157
            } else {
nkeynes@736
   158
            }
nkeynes@736
   159
            run_count--; 
nkeynes@736
   160
            count--;
nkeynes@736
   161
        }
nkeynes@54
   162
    }
nkeynes@422
   163
#endif
nkeynes@54
   164
}
nkeynes@54
   165
nkeynes@54
   166
/**
nkeynes@54
   167
 * Fetch a block of data by DMA from memory to an external device (ie the
nkeynes@54
   168
 * ASIC). The DMA channel must be configured for Mem=>dev or it will return
nkeynes@54
   169
 * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral
nkeynes@54
   170
 * transfers.
nkeynes@54
   171
 *
nkeynes@54
   172
 * @return the number of bytes actually transferred.
nkeynes@54
   173
 */
nkeynes@502
   174
uint32_t DMAC_get_buffer( int channel, sh4ptr_t buf, uint32_t numBytes )
nkeynes@54
   175
{
nkeynes@54
   176
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   177
    uint32_t source, count, run_count, size, i;
nkeynes@54
   178
    char tmp[32];
nkeynes@54
   179
nkeynes@54
   180
    if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
nkeynes@736
   181
        return 0;
nkeynes@736
   182
nkeynes@54
   183
    if( ((control >> 8) & 0x0F) !=  DMARES_MEMORY_TO_DEVICE ) {
nkeynes@736
   184
        /* Error? */
nkeynes@736
   185
nkeynes@736
   186
        return 0;
nkeynes@54
   187
    } 
nkeynes@736
   188
nkeynes@54
   189
    source = DMA_SOURCE(channel);
nkeynes@54
   190
    count = DMA_COUNT(channel);
nkeynes@54
   191
    if( count == 0 ) count = 0x01000000;
nkeynes@54
   192
nkeynes@54
   193
    size = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@54
   194
    run_count = numBytes / size;
nkeynes@54
   195
    if( run_count > count || run_count == 0 )
nkeynes@736
   196
        run_count = count;
nkeynes@54
   197
nkeynes@54
   198
    /* Do copy - FIXME: doesn't work when crossing regions */
nkeynes@502
   199
    sh4ptr_t region = mem_get_region( source );
nkeynes@54
   200
    switch( (control >> 12) & 0x03 ) {
nkeynes@54
   201
    case 0: 
nkeynes@736
   202
        memcpy( tmp, region, size );
nkeynes@736
   203
        for( i=0; i<run_count; i++ ) {
nkeynes@736
   204
            memcpy( buf, tmp, size );
nkeynes@736
   205
            buf += size;
nkeynes@736
   206
        }
nkeynes@736
   207
        break;
nkeynes@54
   208
    case 1: 
nkeynes@736
   209
        i = run_count * size;
nkeynes@736
   210
        memcpy( buf, region, i );
nkeynes@736
   211
        source += i;
nkeynes@736
   212
        break;
nkeynes@54
   213
    case 2: 
nkeynes@736
   214
        for( i=0; i<run_count; i++ ) {
nkeynes@736
   215
            memcpy( buf, region, size );
nkeynes@736
   216
            buf += size;
nkeynes@736
   217
            region -= size;
nkeynes@736
   218
        }
nkeynes@736
   219
        source -= (run_count * size);
nkeynes@736
   220
        break;
nkeynes@54
   221
    default:
nkeynes@736
   222
        return 0; /* Illegal */
nkeynes@54
   223
    }
nkeynes@54
   224
nkeynes@54
   225
    /* Update the channel registers */
nkeynes@54
   226
    count -= run_count;
nkeynes@54
   227
    MMIO_WRITE( DMAC, SAR0 + (channel<<4), source );
nkeynes@54
   228
    MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
nkeynes@54
   229
    if( count == 0 ) {
nkeynes@736
   230
        control |= CHCR_TE; 
nkeynes@736
   231
        if( IS_CHANNEL_IRQ_ENABLED(control) )
nkeynes@736
   232
            intc_raise_interrupt( INT_DMA_DMTE0 + channel );
nkeynes@736
   233
        MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
nkeynes@54
   234
    }
nkeynes@54
   235
nkeynes@54
   236
    return run_count * size;
nkeynes@54
   237
}
nkeynes@54
   238
nkeynes@502
   239
uint32_t DMAC_put_buffer( int channel, sh4ptr_t buf, uint32_t numBytes )
nkeynes@54
   240
{
nkeynes@54
   241
    uint32_t control = DMA_CONTROL(channel);
nkeynes@54
   242
    uint32_t dest, count, run_count, size, i;
nkeynes@54
   243
nkeynes@54
   244
    if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() )
nkeynes@736
   245
        return 0;
nkeynes@736
   246
nkeynes@54
   247
    if( ((control >> 8) & 0x0F) !=  DMARES_DEVICE_TO_MEMORY ) {
nkeynes@736
   248
        /* Error? */
nkeynes@736
   249
        return 0;
nkeynes@54
   250
    } 
nkeynes@736
   251
nkeynes@54
   252
    dest = DMA_DEST(channel);
nkeynes@54
   253
    count = DMA_COUNT(channel);
nkeynes@54
   254
    if( count == 0 ) count = 0x01000000;
nkeynes@54
   255
nkeynes@54
   256
    size = DMAC_xfer_size[ (control >> 4)&0x07 ];
nkeynes@54
   257
    run_count = numBytes / size;
nkeynes@54
   258
    if( run_count > count || run_count == 0 )
nkeynes@736
   259
        run_count = count;
nkeynes@54
   260
nkeynes@54
   261
    /* Do copy - FIXME: doesn't work when crossing regions */
nkeynes@502
   262
    sh4ptr_t region = mem_get_region( dest );
nkeynes@54
   263
    switch( (control >> 12) & 0x03 ) {
nkeynes@54
   264
    case 0: 
nkeynes@736
   265
        for( i=0; i<run_count; i++ ) { 
nkeynes@736
   266
            /* Doesn't make a whole lot of sense, but hey... */
nkeynes@736
   267
            memcpy( region, buf, size );
nkeynes@736
   268
            buf += size;
nkeynes@736
   269
        }
nkeynes@736
   270
        break;
nkeynes@54
   271
    case 1: 
nkeynes@736
   272
        i = run_count * size;
nkeynes@736
   273
        memcpy( region, buf, i );
nkeynes@736
   274
        dest += i;
nkeynes@736
   275
        break;
nkeynes@54
   276
    case 2: 
nkeynes@736
   277
        for( i=0; i<run_count; i++ ) {
nkeynes@736
   278
            memcpy( region, buf, size );
nkeynes@736
   279
            buf += size;
nkeynes@736
   280
            region -= size;
nkeynes@736
   281
        }
nkeynes@736
   282
        dest -= (run_count * size);
nkeynes@736
   283
        break;
nkeynes@54
   284
    default:
nkeynes@736
   285
        return 0; /* Illegal */
nkeynes@54
   286
    }
nkeynes@54
   287
nkeynes@54
   288
    /* Update the channel registers */
nkeynes@54
   289
    count -= run_count;
nkeynes@54
   290
    MMIO_WRITE( DMAC, DAR0 + (channel<<4), dest );
nkeynes@54
   291
    MMIO_WRITE( DMAC, DMATCR0 + (channel<<4), count );
nkeynes@54
   292
    if( count == 0 ) {
nkeynes@736
   293
        control |= CHCR_TE; 
nkeynes@736
   294
        if( IS_CHANNEL_IRQ_ENABLED(control) )
nkeynes@736
   295
            intc_raise_interrupt( INT_DMA_DMTE0 + channel );
nkeynes@736
   296
        MMIO_WRITE( DMAC, CHCR0 + (channel<<4), control );
nkeynes@54
   297
    }
nkeynes@54
   298
    return run_count * size;
nkeynes@54
   299
}
nkeynes@54
   300
nkeynes@54
   301
void DMAC_reset( void )
nkeynes@54
   302
{
nkeynes@54
   303
nkeynes@54
   304
}
nkeynes@54
   305
nkeynes@54
   306
void DMAC_save_state( FILE *F ) 
nkeynes@54
   307
{
nkeynes@54
   308
nkeynes@54
   309
}
nkeynes@54
   310
nkeynes@54
   311
int DMAC_load_state( FILE *f )
nkeynes@54
   312
{
nkeynes@54
   313
    return 0;
nkeynes@54
   314
}
nkeynes@54
   315
nkeynes@54
   316
void DMAC_trigger( int resource )
nkeynes@54
   317
{
nkeynes@54
   318
    int i;
nkeynes@54
   319
    if( !IS_DMAC_ENABLED() )
nkeynes@736
   320
        return;
nkeynes@54
   321
    for( i=0; i<4; i++ ) {
nkeynes@736
   322
        uint32_t control = DMA_CONTROL(i);
nkeynes@736
   323
        if( IS_CHANNEL_ENABLED(control) ) {
nkeynes@736
   324
            uint32_t channel_res = CHANNEL_RESOURCE(control);
nkeynes@736
   325
            switch( resource ) {
nkeynes@736
   326
            case DMAC_EXTERNAL:
nkeynes@736
   327
                if( channel_res == DMARES_MEMORY_TO_MEMORY )
nkeynes@736
   328
                    DMAC_run_channel(i,1);
nkeynes@736
   329
                break;
nkeynes@736
   330
            case DMAC_SCI_TDE:
nkeynes@736
   331
                if( channel_res == DMARES_SCI_TRANSMIT_EMPTY )
nkeynes@736
   332
                    DMAC_run_channel(i,1);
nkeynes@736
   333
                break;
nkeynes@736
   334
            case DMAC_SCI_RDF:
nkeynes@736
   335
                if( channel_res == DMARES_SCI_RECEIVE_FULL )
nkeynes@736
   336
                    DMAC_run_channel(i,1);
nkeynes@736
   337
                break;
nkeynes@736
   338
            case DMAC_SCIF_TDE:
nkeynes@736
   339
                if( channel_res == DMARES_SCIF_TRANSMIT_EMPTY )
nkeynes@736
   340
                    DMAC_run_channel(i,1);
nkeynes@736
   341
                break;
nkeynes@736
   342
            case DMAC_SCIF_RDF:
nkeynes@736
   343
                if( channel_res == DMARES_SCIF_RECEIVE_FULL )
nkeynes@736
   344
                    DMAC_run_channel(i,1);
nkeynes@736
   345
                break;
nkeynes@736
   346
            case DMAC_TMU_ICI:
nkeynes@736
   347
                if( channel_res >= DMARES_MEMORY_TO_MEMORY_TMU ) 
nkeynes@736
   348
                    DMAC_run_channel(i,1);
nkeynes@736
   349
                break;
nkeynes@736
   350
            }
nkeynes@736
   351
        }
nkeynes@54
   352
    }
nkeynes@54
   353
}
.