nkeynes@31: /** nkeynes@94: * $Id: asic.c,v 1.11 2006-02-05 04:05:27 nkeynes Exp $ nkeynes@31: * nkeynes@31: * Support for the miscellaneous ASIC functions (Primarily event multiplexing, nkeynes@31: * and DMA). nkeynes@31: * nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@31: */ nkeynes@35: nkeynes@35: #define MODULE asic_module nkeynes@35: nkeynes@1: #include nkeynes@1: #include "dream.h" nkeynes@1: #include "mem.h" nkeynes@1: #include "sh4/intc.h" nkeynes@56: #include "sh4/dmac.h" nkeynes@2: #include "dreamcast.h" nkeynes@25: #include "maple/maple.h" nkeynes@25: #include "gdrom/ide.h" nkeynes@15: #include "asic.h" nkeynes@1: #define MMIO_IMPL nkeynes@1: #include "asic.h" nkeynes@1: /* nkeynes@1: * Open questions: nkeynes@1: * 1) Does changing the mask after event occurance result in the nkeynes@1: * interrupt being delivered immediately? nkeynes@1: * TODO: Logic diagram of ASIC event/interrupt logic. nkeynes@1: * nkeynes@1: * ... don't even get me started on the "EXTDMA" page, about which, apparently, nkeynes@1: * practically nothing is publicly known... nkeynes@1: */ nkeynes@1: nkeynes@15: struct dreamcast_module asic_module = { "ASIC", asic_init, NULL, NULL, NULL, nkeynes@23: NULL, NULL, NULL }; nkeynes@15: nkeynes@20: void asic_check_cleared_events( void ); nkeynes@20: nkeynes@1: void asic_init( void ) nkeynes@1: { nkeynes@1: register_io_region( &mmio_region_ASIC ); nkeynes@1: register_io_region( &mmio_region_EXTDMA ); nkeynes@1: mmio_region_ASIC.trace_flag = 0; /* Because this is called so often */ nkeynes@1: asic_event( EVENT_GDROM_CMD ); nkeynes@1: } nkeynes@1: nkeynes@1: void mmio_region_ASIC_write( uint32_t reg, uint32_t val ) nkeynes@1: { nkeynes@1: switch( reg ) { nkeynes@56: case PIRQ0: nkeynes@56: case PIRQ1: nkeynes@56: case PIRQ2: nkeynes@56: /* Clear any interrupts */ nkeynes@56: MMIO_WRITE( ASIC, reg, MMIO_READ(ASIC, reg)&~val ); nkeynes@56: asic_check_cleared_events(); nkeynes@56: break; nkeynes@56: case MAPLE_STATE: nkeynes@56: MMIO_WRITE( ASIC, reg, val ); nkeynes@56: if( val & 1 ) { nkeynes@56: uint32_t maple_addr = MMIO_READ( ASIC, MAPLE_DMA) &0x1FFFFFE0; nkeynes@56: WARN( "Maple request initiated at %08X, halting", maple_addr ); nkeynes@56: maple_handle_buffer( maple_addr ); nkeynes@56: MMIO_WRITE( ASIC, reg, 0 ); nkeynes@56: } nkeynes@56: break; nkeynes@56: case PVRDMACTL: /* Initiate PVR DMA transfer */ nkeynes@94: MMIO_WRITE( ASIC, reg, val ); nkeynes@94: WARN( "Write to ASIC (%03X <= %08X) [%s: %s]", nkeynes@94: reg, val, MMIO_REGID(ASIC,reg), MMIO_REGDESC(ASIC,reg) ); nkeynes@56: if( val & 1 ) { nkeynes@56: uint32_t dest_addr = MMIO_READ( ASIC, PVRDMADEST) &0x1FFFFFE0; nkeynes@56: uint32_t count = MMIO_READ( ASIC, PVRDMACNT ); nkeynes@56: char *data = alloca( count ); nkeynes@56: uint32_t rcount = DMAC_get_buffer( 2, data, count ); nkeynes@56: if( rcount != count ) nkeynes@56: WARN( "PVR received %08X bytes from DMA, expected %08X", rcount, count ); nkeynes@56: if( (dest_addr &0xF0000000) == 0x10000000 ) { /* TA */ nkeynes@56: pvr2ta_write( data, rcount ); nkeynes@56: } nkeynes@56: asic_event( EVENT_PVR_DMA ); nkeynes@56: } nkeynes@56: break; nkeynes@56: default: nkeynes@56: MMIO_WRITE( ASIC, reg, val ); nkeynes@56: WARN( "Write to ASIC (%03X <= %08X) [%s: %s]", nkeynes@56: reg, val, MMIO_REGID(ASIC,reg), MMIO_REGDESC(ASIC,reg) ); nkeynes@1: } nkeynes@1: } nkeynes@1: nkeynes@1: int32_t mmio_region_ASIC_read( uint32_t reg ) nkeynes@1: { nkeynes@1: int32_t val; nkeynes@1: switch( reg ) { nkeynes@2: /* nkeynes@2: case 0x89C: nkeynes@2: sh4_stop(); nkeynes@2: return 0x000000B; nkeynes@2: */ nkeynes@94: case PIRQ0: nkeynes@94: case PIRQ1: nkeynes@94: case PIRQ2: nkeynes@94: case IRQA0: nkeynes@94: case IRQA1: nkeynes@94: case IRQA2: nkeynes@94: case IRQB0: nkeynes@94: case IRQB1: nkeynes@94: case IRQB2: nkeynes@94: case IRQC0: nkeynes@94: case IRQC1: nkeynes@94: case IRQC2: nkeynes@94: val = MMIO_READ(ASIC, reg); nkeynes@94: // WARN( "Read from ASIC (%03X => %08X) [%s: %s]", nkeynes@94: // reg, val, MMIO_REGID(ASIC,reg), MMIO_REGDESC(ASIC,reg) ); nkeynes@94: return val; nkeynes@94: case G2STATUS: nkeynes@94: return 0; /* find out later if there's any cases we actually need to care about */ nkeynes@94: default: nkeynes@94: val = MMIO_READ(ASIC, reg); nkeynes@94: WARN( "Read from ASIC (%03X => %08X) [%s: %s]", nkeynes@94: reg, val, MMIO_REGID(ASIC,reg), MMIO_REGDESC(ASIC,reg) ); nkeynes@94: return val; nkeynes@1: } nkeynes@94: nkeynes@1: } nkeynes@1: nkeynes@1: void asic_event( int event ) nkeynes@1: { nkeynes@1: int offset = ((event&0x60)>>3); nkeynes@1: int result = (MMIO_READ(ASIC, PIRQ0 + offset)) |= (1<<(event&0x1F)); nkeynes@1: nkeynes@1: if( result & MMIO_READ(ASIC, IRQA0 + offset) ) nkeynes@1: intc_raise_interrupt( INT_IRQ13 ); nkeynes@1: if( result & MMIO_READ(ASIC, IRQB0 + offset) ) nkeynes@1: intc_raise_interrupt( INT_IRQ11 ); nkeynes@1: if( result & MMIO_READ(ASIC, IRQC0 + offset) ) nkeynes@1: intc_raise_interrupt( INT_IRQ9 ); nkeynes@1: } nkeynes@1: nkeynes@20: void asic_check_cleared_events( ) nkeynes@20: { nkeynes@20: int i, setA = 0, setB = 0, setC = 0; nkeynes@20: uint32_t bits; nkeynes@20: for( i=0; i<3; i++ ) { nkeynes@20: bits = MMIO_READ( ASIC, PIRQ0 + i ); nkeynes@20: setA |= (bits & MMIO_READ(ASIC, IRQA0 + i )); nkeynes@20: setB |= (bits & MMIO_READ(ASIC, IRQB0 + i )); nkeynes@20: setC |= (bits & MMIO_READ(ASIC, IRQC0 + i )); nkeynes@20: } nkeynes@20: if( setA == 0 ) nkeynes@20: intc_clear_interrupt( INT_IRQ13 ); nkeynes@20: if( setB == 0 ) nkeynes@20: intc_clear_interrupt( INT_IRQ11 ); nkeynes@20: if( setC == 0 ) nkeynes@20: intc_clear_interrupt( INT_IRQ9 ); nkeynes@20: } nkeynes@1: nkeynes@1: nkeynes@1: MMIO_REGION_WRITE_FN( EXTDMA, reg, val ) nkeynes@1: { nkeynes@2: switch( reg ) { nkeynes@2: case IDEALTSTATUS: /* Device control */ nkeynes@2: ide_write_control( val ); nkeynes@2: break; nkeynes@2: case IDEDATA: nkeynes@2: ide_write_data_pio( val ); nkeynes@2: break; nkeynes@2: case IDEFEAT: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.feature = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDECOUNT: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.count = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDELBA0: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.lba0 = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDELBA1: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.lba1 = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDELBA2: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.lba2 = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDEDEV: nkeynes@2: if( ide_can_write_regs() ) nkeynes@2: idereg.device = (uint8_t)val; nkeynes@2: break; nkeynes@2: case IDECMD: nkeynes@2: if( ide_can_write_regs() ) { nkeynes@2: ide_clear_interrupt(); nkeynes@2: ide_write_command( (uint8_t)val ); nkeynes@2: } nkeynes@2: break; nkeynes@2: default: nkeynes@56: WARN( "EXTDMA write %08X <= %08X", reg, val ); nkeynes@56: nkeynes@2: MMIO_WRITE( EXTDMA, reg, val ); nkeynes@2: } nkeynes@1: } nkeynes@1: nkeynes@1: MMIO_REGION_READ_FN( EXTDMA, reg ) nkeynes@1: { nkeynes@56: uint32_t val; nkeynes@1: switch( reg ) { nkeynes@2: case IDEALTSTATUS: return idereg.status; nkeynes@2: case IDEDATA: return ide_read_data_pio( ); nkeynes@2: case IDEFEAT: return idereg.error; nkeynes@2: case IDECOUNT:return idereg.count; nkeynes@2: case IDELBA0: return idereg.disc; nkeynes@2: case IDELBA1: return idereg.lba1; nkeynes@2: case IDELBA2: return idereg.lba2; nkeynes@2: case IDEDEV: return idereg.device; nkeynes@2: case IDECMD: nkeynes@2: ide_clear_interrupt(); nkeynes@2: return idereg.status; nkeynes@1: default: nkeynes@56: val = MMIO_READ( EXTDMA, reg ); nkeynes@94: //DEBUG( "EXTDMA read %08X => %08X", reg, val ); nkeynes@56: return val; nkeynes@1: } nkeynes@1: } nkeynes@1: