tm_rdram.s
6.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
* tm_rdram.s
*/
#include "sys/regdef.h"
#include "sys/asm.h"
#include "tinymon.h"
#include "rcp.h"
.text
.set noreorder
LEAF (tm_rdram) # Address = 0x80000000.
/*
* This version of tinyMon is rdram resident, and so may read/write ramrom
* locations with impunity (as long as it checks the PI status reg after each
* access).
*
* 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.
*
*/
# Start of exception vectors
# Address = 0x80000000
lui s0, RAMROM_BASE_UPPER_K1
addiu s0, s0, 0x780 # utlbmiss boot exception vector
j s0 # jump to 0xb0000780 (0x780 into ramrom)
nop
.align 7 # Align to next exception vector
# Address = 0x80000080.
lui s0, RAMROM_BASE_UPPER_K1
addiu s0, s0, 0x780 # utlbmiss boot exception vector
j s0 # jump to 0xb0000780 (0x780 into ramrom)
nop
.align 8 # Align to next exception vector
# Address = 0x80000180.
lui s0, RAMROM_BASE_UPPER_K1
addiu s0, s0, 0x780 # utlbmiss boot exception vector
j s0 # jump to 0xb0000780 (0x780 into ramrom)
nop
.align 9 # Align to start of tinymon
# Address = 0x80000200.
/*
* 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:
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_rdram)