nkeynes@54: /** nkeynes@561: * $Id$ nkeynes@54: * nkeynes@54: * SH4 onboard DMA controller (DMAC) peripheral. nkeynes@54: * nkeynes@54: * Copyright (c) 2005 Nathan Keynes. nkeynes@54: * nkeynes@54: * This program is free software; you can redistribute it and/or modify nkeynes@54: * it under the terms of the GNU General Public License as published by nkeynes@54: * the Free Software Foundation; either version 2 of the License, or nkeynes@54: * (at your option) any later version. nkeynes@54: * nkeynes@54: * This program is distributed in the hope that it will be useful, nkeynes@54: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@54: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@54: * GNU General Public License for more details. nkeynes@54: */ nkeynes@54: #define MODULE sh4_module nkeynes@54: nkeynes@54: #include "dream.h" nkeynes@54: #include "mem.h" nkeynes@54: #include "sh4/sh4core.h" nkeynes@54: #include "sh4/sh4mmio.h" nkeynes@54: #include "sh4/intc.h" nkeynes@54: #include "sh4/dmac.h" nkeynes@54: nkeynes@54: static int DMAC_xfer_size[8] = {8, 1, 2, 4, 32, 1, 1, 1}; nkeynes@54: nkeynes@54: /* Control flags */ nkeynes@54: #define CHCR_IE 0x04 /* Interrupt Enable */ nkeynes@54: #define CHCR_TE 0x02 /* Transfer End */ nkeynes@54: #define CHCR_DE 0x01 /* DMAC Enable */ nkeynes@54: nkeynes@54: #define IS_DMAC_ENABLED() ((MMIO_READ(DMAC,DMAOR)&0x07) == 0x01) nkeynes@54: nkeynes@54: #define IS_AUTO_REQUEST(val) ((val & 0x0C00) == 0x0400) nkeynes@54: #define CHANNEL_RESOURCE(val) ((val >> 8) & 0x0F) nkeynes@54: nkeynes@54: #define DMA_SOURCE(chan) MMIO_READ(DMAC, SAR0 + (chan<<4)) nkeynes@54: #define DMA_DEST(chan) MMIO_READ(DMAC, DAR0 + (chan<<4)) nkeynes@54: #define DMA_COUNT(chan) (MMIO_READ(DMAC, DMATCR0 + (chan<<4)) & 0x00FFFFFF) nkeynes@54: #define DMA_CONTROL(chan) MMIO_READ(DMAC, CHCR0 + (chan<<4)) nkeynes@54: #define IS_CHANNEL_ENABLED(ctrl) ((ctrl & 0x03) == 0x01) nkeynes@54: #define IS_CHANNEL_IRQ_ENABLED(ctrl) (ctrl & CHCR_IE) nkeynes@54: #define IS_CHANNEL_IRQ_ACTIVE(ctrl) ((ctrl & (CHCR_IE|CHCR_TE)) == (CHCR_IE|CHCR_TE)) nkeynes@54: nkeynes@54: #define DMARES_MEMORY_TO_MEMORY 0x00 nkeynes@54: #define DMARES_MEMORY_TO_DEVICE 0x02 nkeynes@54: #define DMARES_DEVICE_TO_MEMORY 0x03 nkeynes@54: #define DMARES_MEMORY_TO_MEMORY_AUTO 0x04 nkeynes@54: #define DMARES_MEMORY_TO_PERIPH_AUTO 0x05 nkeynes@54: #define DMARES_PERIPH_TO_MEMORY_AUTO 0x06 nkeynes@54: #define DMARES_SCI_TRANSMIT_EMPTY 0x08 nkeynes@54: #define DMARES_SCI_RECEIVE_FULL 0x09 nkeynes@54: #define DMARES_SCIF_TRANSMIT_EMPTY 0x0A nkeynes@54: #define DMARES_SCIF_RECEIVE_FULL 0x0B nkeynes@54: #define DMARES_MEMORY_TO_MEMORY_TMU 0x0C nkeynes@54: #define DMARES_MEMORY_TO_PERIPH_TMU 0x0D nkeynes@54: #define DMARES_PERIPH_TO_MEMORY_TMU 0x0E nkeynes@54: nkeynes@54: void DMAC_set_control( uint32_t channel, uint32_t val ) nkeynes@54: { nkeynes@54: uint32_t oldval = DMA_CONTROL(channel); nkeynes@54: int resource; nkeynes@54: MMIO_WRITE( DMAC, CHCR0 + (channel<<4), val ); nkeynes@736: nkeynes@54: /* If TE or IE are cleared, clear the interrupt request */ nkeynes@54: if( IS_CHANNEL_IRQ_ACTIVE(oldval) && nkeynes@736: !IS_CHANNEL_IRQ_ACTIVE(val) ) nkeynes@736: intc_clear_interrupt( INT_DMA_DMTE0+channel ); nkeynes@736: nkeynes@54: resource = CHANNEL_RESOURCE(val); nkeynes@54: if( IS_CHANNEL_ENABLED(val) ) { nkeynes@736: if( resource >= DMARES_MEMORY_TO_MEMORY_AUTO && nkeynes@736: resource < DMARES_SCI_TRANSMIT_EMPTY ) { nkeynes@736: /* Autorun */ nkeynes@736: } nkeynes@54: } nkeynes@54: nkeynes@54: /* Everything else we don't need to care about until we actually try to nkeynes@54: * run the channel nkeynes@54: */ nkeynes@54: } nkeynes@54: nkeynes@929: MMIO_REGION_READ_FN( DMAC, reg ) nkeynes@54: { nkeynes@929: return MMIO_READ( DMAC, reg&0xFFF ); nkeynes@54: } nkeynes@975: MMIO_REGION_READ_DEFSUBFNS(DMAC) nkeynes@54: nkeynes@929: MMIO_REGION_WRITE_FN( DMAC, reg, val ) nkeynes@54: { nkeynes@929: reg &= 0xFFF; nkeynes@54: switch( reg ) { nkeynes@54: case DMAOR: nkeynes@736: MMIO_WRITE( DMAC, reg, val ); nkeynes@736: break; nkeynes@54: case CHCR0: DMAC_set_control( 0, val ); break; nkeynes@54: case CHCR1: DMAC_set_control( 1, val ); break; nkeynes@54: case CHCR2: DMAC_set_control( 2, val ); break; nkeynes@54: case CHCR3: DMAC_set_control( 3, val ); break; nkeynes@54: default: nkeynes@736: MMIO_WRITE( DMAC, reg, val ); nkeynes@54: } nkeynes@54: } nkeynes@54: nkeynes@54: /** nkeynes@54: * Execute up to run_count transfers on the specified channel. Assumes the nkeynes@54: * trigger for the channel has been received. nkeynes@54: * nkeynes@54: * @param channel Channel number (0-3) to run. nkeynes@54: * @param run_count number of transfers to execute, or 0 to run to the nkeynes@54: * end of the transfer count. nkeynes@54: */ nkeynes@422: void DMAC_run_channel( uint32_t channel, uint32_t run_count ) nkeynes@54: { nkeynes@422: nkeynes@422: #if 0 /* Should really finish this */ nkeynes@54: char burst[32]; /* Transfer burst */ nkeynes@54: uint32_t control = DMA_CONTROL(channel); nkeynes@54: nkeynes@54: if( IS_CHANNEL_ENABLED(control) ) { nkeynes@736: uint32_t source = DMA_SOURCE(channel); nkeynes@736: uint32_t dest = DMA_DEST(channel); nkeynes@736: uint32_t count = DMA_COUNT( channel ); nkeynes@736: if( count == 0 ) nkeynes@736: count = 0x01000000; nkeynes@736: if( run_count == 0 || run_count > count ) nkeynes@736: run_count = count; nkeynes@736: uint32_t xfersize = DMAC_xfer_size[ (control >> 4)&0x07 ]; nkeynes@736: int source_step, dest_step; nkeynes@736: int resource = (control >> 8) & 0x0F; nkeynes@736: switch( (control >> 14) & 0x03 ) { nkeynes@736: case 0: dest_step = 0; break; nkeynes@736: case 1: dest_step = xfersize; break; nkeynes@736: case 2: dest_step = -xfersize; break; nkeynes@736: case 3: dest_step = 0; break; /* Illegal */ nkeynes@736: } nkeynes@736: switch( (control >> 12) & 0x03 ) { nkeynes@736: case 0: source_step = 0; break; nkeynes@736: case 1: source_step = xfersize; break; nkeynes@736: case 2: source_step = -xfersize; break; nkeynes@736: case 3: source_step = 0; break; /* Illegal */ nkeynes@736: } nkeynes@736: nkeynes@736: while( run_count > 0 ) { nkeynes@736: /* Origin */ nkeynes@736: if( (resource & 0x02) == 0 ) { nkeynes@736: /* Source is a normal memory address */ nkeynes@736: nkeynes@736: } else { nkeynes@736: /* Device */ nkeynes@736: } nkeynes@736: nkeynes@736: /* Destination */ nkeynes@736: if( (resource & 0x01) == 0 ) { nkeynes@736: /* Destination is a normal memory address */ nkeynes@736: } else { nkeynes@736: } nkeynes@736: run_count--; nkeynes@736: count--; nkeynes@736: } nkeynes@54: } nkeynes@422: #endif nkeynes@54: } nkeynes@54: nkeynes@54: /** nkeynes@54: * Fetch a block of data by DMA from memory to an external device (ie the nkeynes@54: * ASIC). The DMA channel must be configured for Mem=>dev or it will return nkeynes@54: * no bytes and whinge mightily. Note this is NOT used for SH4 peripheral nkeynes@54: * transfers. nkeynes@54: * nkeynes@54: * @return the number of bytes actually transferred. nkeynes@54: */ nkeynes@502: uint32_t DMAC_get_buffer( int channel, sh4ptr_t buf, uint32_t numBytes ) nkeynes@54: { nkeynes@54: uint32_t control = DMA_CONTROL(channel); nkeynes@54: uint32_t source, count, run_count, size, i; nkeynes@54: char tmp[32]; nkeynes@54: nkeynes@54: if( !IS_CHANNEL_ENABLED(control) || !IS_DMAC_ENABLED() ) nkeynes@736: return 0; nkeynes@736: nkeynes@54: if( ((control >> 8) & 0x0F) != DMARES_MEMORY_TO_DEVICE ) { nkeynes@736: /* Error? */ nkeynes@736: nkeynes@736: return 0; nkeynes@54: } nkeynes@736: nkeynes@54: source = DMA_SOURCE(channel); nkeynes@54: count = DMA_COUNT(channel); nkeynes@54: if( count == 0 ) count = 0x01000000; nkeynes@54: nkeynes@54: size = DMAC_xfer_size[ (control >> 4)&0x07 ]; nkeynes@54: run_count = numBytes / size; nkeynes@54: if( run_count > count || run_count == 0 ) nkeynes@736: run_count = count; nkeynes@54: nkeynes@54: /* Do copy - FIXME: doesn't work when crossing regions */ nkeynes@502: sh4ptr_t region = mem_get_region( source ); nkeynes@54: switch( (control >> 12) & 0x03 ) { nkeynes@54: case 0: nkeynes@736: memcpy( tmp, region, size ); nkeynes@736: for( i=0; i> 8) & 0x0F) != DMARES_DEVICE_TO_MEMORY ) { nkeynes@736: /* Error? */ nkeynes@736: return 0; nkeynes@54: } nkeynes@736: nkeynes@54: dest = DMA_DEST(channel); nkeynes@54: count = DMA_COUNT(channel); nkeynes@54: if( count == 0 ) count = 0x01000000; nkeynes@54: nkeynes@54: size = DMAC_xfer_size[ (control >> 4)&0x07 ]; nkeynes@54: run_count = numBytes / size; nkeynes@54: if( run_count > count || run_count == 0 ) nkeynes@736: run_count = count; nkeynes@54: nkeynes@54: /* Do copy - FIXME: doesn't work when crossing regions */ nkeynes@502: sh4ptr_t region = mem_get_region( dest ); nkeynes@54: switch( (control >> 12) & 0x03 ) { nkeynes@54: case 0: nkeynes@736: for( i=0; i= DMARES_MEMORY_TO_MEMORY_TMU ) nkeynes@736: DMAC_run_channel(i,1); nkeynes@736: break; nkeynes@736: } nkeynes@736: } nkeynes@54: } nkeynes@54: }