tm_rspmem.s 5.76 KB
/*
 * tm_rspmem.s: can be loaded into RSP DMEM (0x04000000 .. 0x0400fffc)
 *				or RSP IMEM (0x04001000 .. 0x04001ffc)
 */
#include "sys/regdef.h"
#include "sys/asm.h"
#include "tinymon.h"
#include "rcp.h"

        .text
        .set noreorder	

LEAF (tm_rspmem)                       	# Address = 0x04000000 or 0x04001000

/*
 * This version of tinyMon is imem/dmem resident, and so may read/write ramrom
 * locations with impunity (as long as it checks the PI status reg after each
 * access).
 *
 * Uses default exception vectors in PIF (whatever they may be).
 * 
 * Host to R4300 command protocol:
 *
 * Host writes "DG_RAMROM_REQUEST" (0x1) to the cart_int register;
 * Host then polls the gio_sync register for "DG_RAMROM_GRANT" (0x2).
 *
 *	R4300 polls the cart_int register for "DG_RAMROM_REQUEST"; when
 *	it sees it, it polls PI_STATUS_REG until the IO & DMA busy bits
 *	are clear, then writes "DG_RAMROM_GRANT" to the gio sync register.
 *	R4300 then polls the cart_int register for "DG_RAMROM_CMD_READY" (0x3).
 *
 * Host receives "DG_RAMROM_GRANT", writes the read/write command into the 
 * first two words of ramrom, sets the cart_int register to 
 * "DG_RAMROM_CMD_READY", then polls the gio_sync register for 
 * "DG_RAMROM_CMD_DONE" (0x4).
 *
 *	R4300 polls the cart_int register for "DG_RAMROM_CMD_READY"; when
 *	it sees it, it processes the read/write request, (for reads only) 
 *      waits for DMA/IO to complete (so the host can read ramrom), then sets 
 *	the gio sync register to "DG_RAMROM_CMD_DONE", and goes back to 
 *	polling the cart_int register, looking for "DG_RAMROM_REQUEST".
 *
 * Host receives "DG_RAMROM_CMD_DONE", and (for read commands) read the 
 * second word of ramrom (which has the readback value, written by the R4300).
 *
 * When the Host wants to issue another command, it writes "DG_RAMROM_REQUEST"
 * to the cart_int register, and this starts the next round of cmd processing.
 *
 */

 /*
  * On startup, set the gio sync register to indicate that the host can go
  * ahead & access ramrom.  Only do this once at startup; thereafter, we'll
  * keep in synch with the host.  The default state of the ramrom is 
  * "DG_RAMROM_CMD_DONE", which means the host can go ahead & write to the
  * cartridge interrupt to arbitrate for access to ramrom.
  */
	lui	t0, GIO_BASE_REG_UPPER_K1
	li	t1, DG_RAMROM_CMD_DONE
	sw	t1, GIO_SYNC_REG_OFFSET(t0)
	jal	waitForIdle
	nop

waitForHostRequest:
	
	lw	t1, CART_INTR_REG_OFFSET(t0)
	nop
	andi	t1, t1, DG_CARTREG_MASK
	li	t2, DG_RAMROM_REQUEST
	bne	t1, t2, waitForHostRequest
	nop

/*
 * Got a host request for access to ramrom.  Check to make sure no io/dma
 * is in progress, tell the host ramrom is theirs for the taking, then 
 * poll the cartridge interrupt register and wait for DG_RAMROM_CMD_READY.
 */


/*
 * Dma and/or IO is complete; tell host to go ahead & write the command to
 * ramrom, and wait for the host to tell us they're done.
 */
	lui	t0, GIO_BASE_REG_UPPER_K1
	li	t1, DG_RAMROM_GRANT
	sw	t1, GIO_SYNC_REG_OFFSET(t0)
	jal	waitForIdle
	nop

waitForCmdReady:

	lw	t3, CART_INTR_REG_OFFSET(t0)
	nop
	andi	t3, t3, DG_CARTREG_MASK
	li	t4, DG_RAMROM_CMD_READY
	bne	t3, t4, waitForCmdReady
	nop
	
/*
 * Find out what kind of operation this is (read or write) and 
 * handle the operation.
 */

	lui	t0, RAMROM_BASE_UPPER_K1
	lw	t1, 0(t0)
	nop
	li	t3, DG_TINYMON_READ_OP
	beq	t1, t3, doReadCmd
	nop
	li	t3, DG_TINYMON_WRITE_OP
	beq	t1, t3, doWriteCmd
	nop
	li	t3, DG_TINYMON_DMA_READ_OP
	beq	t1, t3, doDmaReadCmd
	nop
	li	t3, DG_TINYMON_DMA_WRITE_OP
	beq	t1, t3, doDmaWriteCmd
	nop
/*
 * If it's not a read or write or dma operation, lie & say "command complete".
 */
	b	cmdDone
	nop

doWriteCmd:
	lui	t2, RAMROM_BASE_UPPER_K1
	lw	t0, 4(t2)	# 2nd word of ramrom has write address
	lw	t1, 8(t2)	# 3rd word of ramrom has data to write
	nop
	sw	t1, 0(t0)	# do the requested write on behalf of the user
	jal	waitForIdle
	nop
	b	cmdDone
	nop

doDmaWriteCmd:
	lui	t2, RAMROM_BASE_UPPER_K1
	lw	t0, 4(t2)	# 2nd word of ramrom has ramrom source address
	lw	t1, 8(t2)	# 3rd word of ramrom has rdram dest address
	lw	t3, 12(t2)	# 4th word of ramrom has length count in bytes
	sw	t0, PHYS_TO_K1(PI_CART_ADDR_REG)
	sw	t1, PHYS_TO_K1(PI_DRAM_ADDR_REG)
	sw	t3, PHYS_TO_K1(PI_WR_LEN_REG)	# This starts ramrom->rdram DMA
	jal	waitForIdle
	nop
	b	cmdDone
	nop

doDmaReadCmd:
	lui	t2, RAMROM_BASE_UPPER_K1
	lw	t0, 4(t2)	# 2nd word of ramrom has ramrom dest address
	lw	t1, 8(t2)	# 3rd word of ramrom has rdram src address
	lw	t3, 12(t2)	# 4th word of ramrom has length count in bytes
	sw	t0, PHYS_TO_K1(PI_CART_ADDR_REG)
	sw	t1, PHYS_TO_K1(PI_DRAM_ADDR_REG)
	sw	t3, PHYS_TO_K1(PI_RD_LEN_REG)	# This starts rdram->ramrom DMA
	jal	waitForIdle
	nop
	b	cmdDone
	nop

doReadCmd:

	
	lui	t2, RAMROM_BASE_UPPER_K1
	lw	t0, 4(t2)	# 2nd word of ramrom has read address
	nop
	lw	t1, 0(t0)	# do the requested read into t1
	nop
/*
 * Write the readback value (in t1) into the third word of ramrom.
 */
	sw	t1, 8(t2)
	jal	waitForIdle
	nop
/*
 * Fall through to cmdDone, which sets the gio sync register to 
 * "DG_RAMROM_CMD_DONE", then branches back to where we
 * poll the cart_int register, looking for "DG_RAMROM_REQUEST".
 */

cmdDone:
	lui	t0, GIO_BASE_REG_UPPER_K1
	li	t1, DG_RAMROM_CMD_DONE
	sw	t1, GIO_SYNC_REG_OFFSET(t0)
	jal	waitForIdle
	nop
	b	waitForHostRequest
	nop

/*
 * General purpose subroutine which spins, waiting for the PI controller to
 * become idle.  Should be called after each RAMROM cartridge interface request
 * (for either memory or registers).
 */
waitForIdle:
	.set noreorder
#ifdef __linux__
	.rept 0x1f
#else
	.repeat 0x1f
#endif
	nop		# Wait a bit in case we just started a DMA operation.
	.endr
	lui	t2, PI_BASE_REG_UPPER_K1
waitLoop:
	lw	t3, PI_STATUS_REG_OFFSET(t2)
	nop
	andi	t4, t3, (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)
	bne	t4, zero, waitLoop
	nop
	jr      ra
	nop

END (tm_rspmem)