nkeynes@185: /** nkeynes@561: * $Id$ nkeynes@185: * nkeynes@185: * DMA support code nkeynes@185: * nkeynes@185: * Copyright (c) 2006 Nathan Keynes. nkeynes@185: * nkeynes@185: * This program is free software; you can redistribute it and/or modify nkeynes@185: * it under the terms of the GNU General Public License as published by nkeynes@185: * the Free Software Foundation; either version 2 of the License, or nkeynes@185: * (at your option) any later version. nkeynes@185: * nkeynes@185: * This program is distributed in the hope that it will be useful, nkeynes@185: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@185: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@185: * GNU General Public License for more details. nkeynes@185: */ nkeynes@185: nkeynes@185: #include "dma.h" nkeynes@185: #include "asic.h" nkeynes@185: nkeynes@185: #define DMA_BASE 0xFFA00000 nkeynes@185: nkeynes@185: #define DMA_SAR(c) (DMA_BASE+0x00+(c<<4)) nkeynes@185: #define DMA_DAR(c) (DMA_BASE+0x04+(c<<4)) nkeynes@185: #define DMA_TCR(c) (DMA_BASE+0x08+(c<<4)) nkeynes@185: #define DMA_CHCR(c) (DMA_BASE+0x0C+(c<<4)) nkeynes@185: #define DMA_OR (DMA_BASE+0x40) nkeynes@185: nkeynes@185: #define ASIC_BASE 0xA05F6000 nkeynes@185: #define PVR_DMA_DEST (ASIC_BASE+0x800) nkeynes@185: #define PVR_DMA_COUNT (ASIC_BASE+0x804) nkeynes@185: #define PVR_DMA_CTL (ASIC_BASE+0x808) nkeynes@185: #define PVR_DMA_REGION (ASIC_BASE+0x884) nkeynes@185: nkeynes@753: #define SORT_DMA_TABLE (ASIC_BASE+0x810) nkeynes@753: #define SORT_DMA_DATA (ASIC_BASE+0x814) nkeynes@753: #define SORT_DMA_TABLEBITS (ASIC_BASE+0x818) nkeynes@753: #define SORT_DMA_DATASIZE (ASIC_BASE+0x81C) nkeynes@753: #define SORT_DMA_CTL (ASIC_BASE+0x820) nkeynes@753: #define SORT_DMA_COUNT (ASIC_BASE+0x860) nkeynes@753: nkeynes@185: void dmac_dump_channel( FILE *f, unsigned int channel ) nkeynes@185: { nkeynes@185: fprintf( f, "DMAC SAR: %08X Count: %08X Ctl: %08X OR: %08X\n", nkeynes@185: long_read(DMA_SAR(channel)), long_read(DMA_TCR(channel)), nkeynes@185: long_read(DMA_CHCR(channel)), long_read(DMA_OR) ); nkeynes@185: } nkeynes@185: nkeynes@185: nkeynes@185: /** nkeynes@185: * Setup the DMAC for a transfer. Assumes 32-byte block transfer. nkeynes@185: * Caller is responsible for making sure no-one else is using the nkeynes@185: * channel already. nkeynes@185: * nkeynes@185: * @param channel DMA channel to use, 0 to 3 nkeynes@185: * @param source source address (if a memory source) nkeynes@185: * @param dest destination address (if a memory destination) nkeynes@185: * @param length number of bytes to transfer (must be a multiple of nkeynes@185: * 32. nkeynes@185: * @param direction 0 = host to device, 1 = device to host nkeynes@185: */ nkeynes@185: void dmac_prepare_channel( int channel, uint32_t source, uint32_t dest, nkeynes@185: uint32_t length, int direction ) nkeynes@185: { nkeynes@185: uint32_t control; nkeynes@185: nkeynes@185: if( direction == 0 ) { nkeynes@185: /* DMA Disabled, IRQ disabled, 32 byte transfer, burst mode, nkeynes@185: * Memory => Device, Source addr increment, dest addr fixed nkeynes@185: */ nkeynes@185: control = 0x000012C0; nkeynes@185: } else { nkeynes@185: /* DMA Disabled, IRQ disabled, 32 byte transfer, burst mode, nkeynes@185: * Device => Memory, Source addr fixed, dest addr increment nkeynes@185: */ nkeynes@185: control = 0x000043C0; nkeynes@185: } nkeynes@185: long_write( DMA_CHCR(channel), control ); nkeynes@185: long_write( DMA_SAR(channel), source ); nkeynes@185: long_write( DMA_DAR(channel), dest ); nkeynes@185: long_write( DMA_TCR(channel), (length >> 5) ); nkeynes@185: control |= 0x0001; nkeynes@185: long_write( DMA_CHCR(channel), control ); /* Enable DMA channel */ nkeynes@185: long_write( DMA_OR, 0x8201 ); /* Ensure the DMAC config is set */ nkeynes@185: } nkeynes@185: nkeynes@185: nkeynes@185: int pvr_dma_write( unsigned int target, char *buf, int len, int region ) nkeynes@185: { nkeynes@185: uint32_t addr =(uint32_t)buf; nkeynes@185: int result; nkeynes@185: if( (addr & 0xFFFFFFE0) != addr ) { nkeynes@185: fprintf( stderr, "Address error: Attempting DMA from %08X\n", addr ); nkeynes@185: return -1; nkeynes@185: } nkeynes@185: long_write( PVR_DMA_CTL, 0 ); /* Stop PVR dma if it's already running */ nkeynes@185: asic_clear(); nkeynes@185: nkeynes@185: dmac_prepare_channel( 2, (uint32_t)buf, 0, len, 0 ); /* Allocate channel 2 */ nkeynes@185: long_write( PVR_DMA_DEST, target ); nkeynes@185: long_write( PVR_DMA_COUNT, len ); nkeynes@185: long_write( PVR_DMA_REGION, region ); nkeynes@185: nkeynes@185: CHECK_IEQUALS( target, long_read(PVR_DMA_DEST) ); nkeynes@185: CHECK_IEQUALS( len, long_read(PVR_DMA_COUNT) ); nkeynes@185: CHECK_IEQUALS( 0, long_read(PVR_DMA_REGION) ); nkeynes@185: CHECK_IEQUALS( (uint32_t)buf, long_read(DMA_SAR(2)) ); nkeynes@185: CHECK_IEQUALS( len/32, long_read(DMA_TCR(2)) ); nkeynes@185: CHECK_IEQUALS( 0x12C1, long_read(DMA_CHCR(2)) ); nkeynes@185: nkeynes@185: long_write( PVR_DMA_CTL, 1 ); nkeynes@185: result = asic_wait(EVENT_PVR_DMA); nkeynes@185: nkeynes@185: if( result != 0 ) { nkeynes@185: fprintf( stderr, "PVR DMA failed (timeout)\n" ); nkeynes@185: asic_dump(stderr); nkeynes@185: fprintf( stderr, "Dest: %08X Count: %08X Ctl: %08X\n", long_read(PVR_DMA_DEST), nkeynes@185: long_read(PVR_DMA_COUNT), long_read(PVR_DMA_CTL) ); nkeynes@185: dmac_dump_channel(stderr, 2); nkeynes@185: long_write( PVR_DMA_CTL, 0 ); nkeynes@185: } nkeynes@185: nkeynes@185: CHECK_IEQUALS( 0, long_read(PVR_DMA_CTL) ); nkeynes@185: CHECK_IEQUALS( ((uint32_t)buf)+len, long_read(DMA_SAR(2)) ); nkeynes@185: CHECK_IEQUALS( 0, long_read(DMA_TCR(2)) ); nkeynes@185: CHECK_IEQUALS( 0x12C3, long_read(DMA_CHCR(2)) ); nkeynes@185: CHECK_IEQUALS( target, long_read(PVR_DMA_DEST) ); nkeynes@185: CHECK_IEQUALS( 0, long_read(PVR_DMA_COUNT) ); nkeynes@185: CHECK_IEQUALS( 0, long_read(PVR_DMA_REGION) ); nkeynes@185: nkeynes@185: return result; nkeynes@185: } nkeynes@753: nkeynes@753: int sort_dma_write( char *sorttable, int tablelen, char *data, int datalen, int bitwidth, int datasize ) nkeynes@753: { nkeynes@753: int result; nkeynes@753: uint32_t tableaddr = (uint32_t)sorttable; nkeynes@753: uint32_t dataaddr = (uint32_t)data; nkeynes@753: nkeynes@753: long_write( SORT_DMA_CTL, 0 ); nkeynes@753: asic_clear(); nkeynes@753: nkeynes@753: long_write( SORT_DMA_TABLE, tableaddr ); nkeynes@753: long_write( SORT_DMA_DATA, dataaddr ); nkeynes@753: long_write( SORT_DMA_TABLEBITS, bitwidth ); nkeynes@753: long_write( SORT_DMA_DATASIZE, datasize ); nkeynes@753: long_write( SORT_DMA_CTL, 1 ); nkeynes@753: result = asic_wait2(EVENT_SORT_DMA, EVENT_SORT_DMA_ERR); nkeynes@753: if( result == -1 ) { nkeynes@753: fprintf( stderr, "SORT DMA failed (timeout)\n" ); nkeynes@753: asic_dump(stderr); nkeynes@753: fprintf( stderr, "Table: %08X Count: %08X Ctl: %08X\n", long_read(SORT_DMA_TABLE), long_read(SORT_DMA_COUNT), nkeynes@753: long_read(SORT_DMA_CTL) ); nkeynes@753: long_write( SORT_DMA_CTL, 0 ); nkeynes@753: } nkeynes@753: CHECK_IEQUALS( 0, long_read(SORT_DMA_CTL) ); nkeynes@753: return result; nkeynes@753: }