sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
mmce_sio2.c
1
2#include <dmacman.h>
3#include <sio2regs.h>
4#include <thbase.h>
5#include <thevent.h>
6#include <intrman.h>
7
8#include "ioplib.h"
9#include "irx_imports.h"
10#include "iop_regs.h"
11
12#include "mmce_sio2.h"
13#include "sio2man_hook.h"
14
15#include "module_debug.h"
16
17#define EF_SIO2_INTR_COMPLETE 0x00000200
18#define EF_SIO2_TRANSFER_TIMEOUT 0x00000400
19
20static int mmce_port;
21
22//Port ctrl settings used for all transfers
23static u32 mmce_sio2_port_ctrl1;
24static u32 mmce_sio2_port_ctrl2;
25
26static u32 sio2_save_ctrl;
27static int event_flag = -1;
28
29//SIO2MAN's intr handler
30int (*sio2man_intr_handler_ptr)(void *arg);
31void *sio2man_intr_arg_ptr;
32
33//Replacement intr handler
34int (*mmce_sio2_intr_handler_ptr)(void *arg) = NULL;
35void *mmce_sio2_intr_arg_ptr = NULL;
36
37iop_sys_clock_t timeout_200ms;
38iop_sys_clock_t timeout_1s;
39iop_sys_clock_t timeout_2s;
40
41u8 mmce_sio2_use_alarm = 1;
42
43int mmce_sio2_intr_handler(void *arg)
44{
45 int ef = *(int *)arg;
46
47 inl_sio2_stat_set(0x3);
48
49 iSetEventFlag(ef, EF_SIO2_INTR_COMPLETE);
50
51 return 1;
52}
53
54unsigned int mmce_sio2_timeout_handler(void *arg)
55{
56 int ef = *(int *)arg;
57 iSetEventFlag(ef, EF_SIO2_TRANSFER_TIMEOUT);
58 return 0;
59}
60
61int mmce_sio2_init()
62{
63 int rv;
64
65 //Install necessary hooks
66 rv = sio2man_hook_init();
67 if (rv == -1) {
68 DPRINTF("Failed to init sio2man hook\n");
69 return -1;
70 }
71
72 //Create event flag for intr
73 iop_event_t event;
74
75 event.attr = 2;
76 event.option = 0;
77 event.bits = 0;
78
79 event_flag = CreateEventFlag(&event);
80 if (event_flag < 0) {
81 DPRINTF("Failed to create event flag\n");
82 sio2man_hook_deinit();
83 return -2;
84 }
85
86 //Assign intr pointers
87 mmce_sio2_intr_handler_ptr = &mmce_sio2_intr_handler;
88 mmce_sio2_intr_arg_ptr = &event_flag;
89
90 //Ensure IOP DMA channels are enabled
91 sceSetDMAPriority(IOP_DMAC_SIO2in, 3);
92 sceSetDMAPriority(IOP_DMAC_SIO2out, 3);
93 sceEnableDMAChannel(IOP_DMAC_SIO2in);
94 sceEnableDMAChannel(IOP_DMAC_SIO2out);
95
96 //Ensure SIO2 intrs are enabled
97 EnableIntr(IOP_IRQ_SIO2);
98
99 //200ms
100 timeout_200ms.hi = 0x0;
101 timeout_200ms.lo = 0x708000;
102
103 //1s
104 timeout_1s.hi = 0x0;
105 timeout_1s.lo = 0x2328000;
106
107 //2s
108 timeout_2s.hi = 0x0;
109 timeout_2s.lo = 0x4650000;
110
111 mmce_sio2_port_ctrl1 =
112 PCTRL0_ATT_LOW_PER(0x5) |
113 PCTRL0_ATT_MIN_HIGH_PER(0x0) |
114 PCTRL0_BAUD0_DIV(0x2) |
115 PCTRL0_BAUD1_DIV(0xff);
116
117 mmce_sio2_port_ctrl2 =
118 PCTRL1_ACK_TIMEOUT_AFTER(0xffff) |
119 PCTRL1_WAIT_CYCLES_AFTER_ACK_LOW(0x5) |
120 PCTRL1_UNK24(0x0) |
121 PCTRL1_IF_MODE_SPI_DIFF(0x0);
122
123 return 0;
124}
125
126void mmce_sio2_deinit()
127{
128 sio2man_hook_deinit();
129 DeleteEventFlag(event_flag);
130}
131
132void mmce_sio2_lock()
133{
134 int state;
135
136 //Lock sio2man driver so we can use it exclusively
137 sio2man_hook_sio2_lock();
138
139 sio2_save_ctrl = inl_sio2_ctrl_get();
140
141 //Swap SIO2MAN's intr handler with ours
142 CpuSuspendIntr(&state);
143 mmce_sio2_intrman_internals_ptr->interrupt_handler_table[17].handler = mmce_sio2_intr_handler_ptr;
144 mmce_sio2_intrman_internals_ptr->interrupt_handler_table[17].userdata = mmce_sio2_intr_arg_ptr;
145 CpuResumeIntr(state);
146
147 //Copy port ctrl settings to SIO2 registers
148 inl_sio2_portN_ctrl1_set(mmce_port, mmce_sio2_port_ctrl1);
149 inl_sio2_portN_ctrl2_set(mmce_port, mmce_sio2_port_ctrl2);
150}
151
152void mmce_sio2_unlock()
153{
154 int state;
155
156 //Swap our intr handler with SIO2MAN's intr handler
157 CpuSuspendIntr(&state);
158 mmce_sio2_intrman_internals_ptr->interrupt_handler_table[17].handler = sio2man_intr_handler_ptr;
159 mmce_sio2_intrman_internals_ptr->interrupt_handler_table[17].userdata = sio2man_intr_arg_ptr;
160 CpuResumeIntr(state);
161
162 //Restore ctrl state, and reset STATE + FIFOS
163 inl_sio2_ctrl_set(sio2_save_ctrl | 0xc);
164
165 //Unlock sio2man driver
166 sio2man_hook_sio2_unlock();
167}
168
169void mmce_sio2_set_port(int port)
170{
171 mmce_port = port;
172}
173
174int mmce_sio2_get_port()
175{
176 return mmce_port;
177}
178
179void mmce_sio2_update_ack_wait_cycles(int cycles)
180{
181 DPRINTF("mmceman: setting cycles to: 0x%x\n", cycles);
182
183 mmce_sio2_port_ctrl2 =
184 PCTRL1_ACK_TIMEOUT_AFTER(0xffff) |
185 PCTRL1_WAIT_CYCLES_AFTER_ACK_LOW(cycles)|
186 PCTRL1_UNK24(0x0) |
187 PCTRL1_IF_MODE_SPI_DIFF(0x0);
188}
189
190void mmce_sio2_set_use_alarm(int value)
191{
192 mmce_sio2_use_alarm = value;
193}
194
195int mmce_sio2_tx_rx_pio(u8 tx_size, u8 rx_size, u8 *tx_buf, u8 *rx_buf, iop_sys_clock_t *timeout)
196{
197 u32 resbits;
198
199 //Reset SIO2 + FIFO pointers, enable interrupts
200 inl_sio2_ctrl_set(0x3ac);
201
202 //Add transfer to queue
203 inl_sio2_regN_set(0,
204 TR_CTRL_PORT_NR(mmce_port) |
205 TR_CTRL_PAUSE(0) |
206 TR_CTRL_TX_MODE_PIO_DMA(0) |
207 TR_CTRL_RX_MODE_PIO_DMA(0) |
208 TR_CTRL_NORMAL_TR(1) |
209 TR_CTRL_SPECIAL_TR(0) |
210 TR_CTRL_BAUD_DIV(0) |
211 TR_CTRL_WAIT_ACK_FOREVER(0) |
212 TR_CTRL_TX_DATA_SZ(tx_size) |
213 TR_CTRL_RX_DATA_SZ(rx_size));
214
215
216 //Copy data to TX FIFO
217 for (int i = 0; i < tx_size; i++) {
218 inl_sio2_data_out(tx_buf[i]);
219 }
220
221 //Start transfer
222 inl_sio2_ctrl_set(0x3a1);
223
224 //Set timeout alarm
225 SetAlarm(timeout, mmce_sio2_timeout_handler, &event_flag);
226
227 //Wait for completion or timeout
228 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
229 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
230 DPRINTF("Transfer timed out, resetting SIO2\n");
231 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
232 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
233 return -1;
234 }
235
236 //Cancel timeout alarm
237 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
238
239 //Clear flags
240 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
241
242 //Copy data out of RX FIFO
243 for (int i = 0; i < rx_size; i++) {
244 rx_buf[i] = inl_sio2_data_in();
245 }
246
247 return 0;
248}
249
250//Simplified read for sectors / MMCEDRV
251int mmce_sio2_rx_dma(u8 *buffer, u32 size)
252{
253 u32 resbits;
254
255 //dma element count (round down)
256 u32 elements = size / 256;
257
258 u32 elements_do = 0;
259 u32 elements_done = 0;
260
261 //used for all elements 256 bytes in size
262 u32 dma_element = TR_CTRL_PORT_NR(mmce_port) |
263 TR_CTRL_PAUSE(0) |
264 TR_CTRL_TX_MODE_PIO_DMA(0) |
265 TR_CTRL_RX_MODE_PIO_DMA(1) |
266 TR_CTRL_NORMAL_TR(1) |
267 TR_CTRL_SPECIAL_TR(0) |
268 TR_CTRL_BAUD_DIV(0) |
269 TR_CTRL_WAIT_ACK_FOREVER(0) |
270 TR_CTRL_TX_DATA_SZ(0) |
271 TR_CTRL_RX_DATA_SZ(256);
272
273 while (elements != 0) {
274 //Reset SIO2 + FIFO pointers, enable interrupts
275 inl_sio2_ctrl_set(0x3ac);
276
277 elements_do = elements > 16 ? 16 : elements;
278
279 //Copy DMA transfer elements to SIO2 registers
280 for (int i = 0; i < elements_do; i++) {
281 inl_sio2_regN_set(i, dma_element);
282 }
283
284 //Start DMA transfer
285 sceSetSliceDMA(IOP_DMAC_SIO2out, &buffer[elements_done * 256], 0x100 >> 2, elements_do, DMAC_TO_MEM);
286 sceStartDMA(IOP_DMAC_SIO2out);
287
288 //Start the transfer
289 inl_sio2_ctrl_set(0x3a1);
290
291 //Set timeout alarm
292 if (mmce_sio2_use_alarm)
293 SetAlarm(&timeout_2s, mmce_sio2_timeout_handler, &event_flag);
294
295 //Wait for completion or timeout
296 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
297 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
298 DPRINTF("Transfer timed out, resetting SIO2\n");
299 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
300 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
301 return -1;
302 }
303
304 //Cancel timeout alarm
305 if (mmce_sio2_use_alarm)
306 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
307
308 //Clear flags
309 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
310
311 elements -= elements_do;
312 elements_done += elements_do;
313 }
314
315 return 0;
316}
317
318//Mixed DMA / PIO RX transfer
319int mmce_sio2_rx_mixed(u8 *buffer, u32 size)
320{
321 u32 resbits;
322
323 //dma element count (round down)
324 u32 elements = size / 256;
325
326 //remainder < 256
327 u32 pio_size = size - (elements * 256);
328
329 u32 bytes_done = 0;
330 u32 elements_do = 0;
331
332 u32 offset = 0;
333 u32 offset_pio = 0;
334
335 //used for all elements 256 bytes in size
336 u32 dma_element = TR_CTRL_PORT_NR(mmce_port) |
337 TR_CTRL_PAUSE(0) |
338 TR_CTRL_TX_MODE_PIO_DMA(0) |
339 TR_CTRL_RX_MODE_PIO_DMA(1) |
340 TR_CTRL_NORMAL_TR(1) |
341 TR_CTRL_SPECIAL_TR(0) |
342 TR_CTRL_BAUD_DIV(0) |
343 TR_CTRL_WAIT_ACK_FOREVER(0) |
344 TR_CTRL_TX_DATA_SZ(0) |
345 TR_CTRL_RX_DATA_SZ(256);
346
347 //used for all elements less than 256 bytes in size
348 u32 pio_element = TR_CTRL_PORT_NR(mmce_port) |
349 TR_CTRL_PAUSE(0) |
350 TR_CTRL_TX_MODE_PIO_DMA(0) |
351 TR_CTRL_RX_MODE_PIO_DMA(0) |
352 TR_CTRL_NORMAL_TR(1) |
353 TR_CTRL_SPECIAL_TR(0) |
354 TR_CTRL_BAUD_DIV(0) |
355 TR_CTRL_WAIT_ACK_FOREVER(0) |
356 TR_CTRL_TX_DATA_SZ(0) |
357 TR_CTRL_RX_DATA_SZ(pio_size);
358
359 while (bytes_done < size) {
360 //Reset SIO2 + FIFO pointers, enable interrupts
361 inl_sio2_ctrl_set(0x3ac);
362
363 //Used for DMA
364 offset = bytes_done;
365
366 elements_do = elements > 16 ? 16 : elements;
367
368 //Copy DMA transfer elements to SIO2 registers
369 for (int i = 0; i < elements_do; i++) {
370 inl_sio2_regN_set(i, dma_element);
371 }
372
373 bytes_done += elements_do * 256;
374
375 //Copy PIO element / term transfer queue if needed
376 if (elements_do < 16) {
377 if (pio_size != 0x0) {
378 inl_sio2_regN_set(elements_do, pio_element);
379
380 offset_pio = bytes_done;
381 bytes_done += pio_size;
382 }
383 }
384
385 //Start DMA transfer if needed
386 if (elements_do != 0) {
387 sceSetSliceDMA(IOP_DMAC_SIO2out, &buffer[offset], 0x100 >> 2, elements_do, DMAC_TO_MEM);
388 sceStartDMA(IOP_DMAC_SIO2out);
389 }
390
391 //Start the transfer
392 inl_sio2_ctrl_set(0x3a1);
393
394 //Set timeout alarm
395 if (mmce_sio2_use_alarm)
396 SetAlarm(&timeout_2s, mmce_sio2_timeout_handler, &event_flag);
397
398 //Wait for completion or timeout
399 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
400 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
401 DPRINTF("Detected transfer timeout, attempting to reset SIO2\n");
402 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
403 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
404 return -1;
405 }
406
407 //Cancel timeout alarm
408 if (mmce_sio2_use_alarm)
409 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
410
411 //Clear flags
412 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
413
414 elements -= elements_do;
415 }
416
417 //Copy data from RX FIFO
418 if (pio_size != 0x0) {
419 for (int i = 0; i < pio_size; i++) {
420 buffer[offset_pio + i] = inl_sio2_data_in();
421 }
422 }
423
424 return 0;
425}
426
427int mmce_sio2_tx_mixed(u8 *buffer, u32 size)
428{
429 int res;
430 u32 resbits;
431
432 //dma element count (round down)
433 u32 elements = size / 256;
434
435 //remainder < 256
436 u32 pio_size = size - (elements * 256);
437
438 u32 bytes_done = 0;
439 u32 elements_do = 0;
440
441 u8 waiting = 1;
442 u8 rx_buf[3];
443
444 //used for all elements 256 bytes in size
445 u32 dma_element = TR_CTRL_PORT_NR(mmce_port) |
446 TR_CTRL_PAUSE(0) |
447 TR_CTRL_TX_MODE_PIO_DMA(1) |
448 TR_CTRL_RX_MODE_PIO_DMA(0) |
449 TR_CTRL_NORMAL_TR(1) |
450 TR_CTRL_SPECIAL_TR(0) |
451 TR_CTRL_BAUD_DIV(0) |
452 TR_CTRL_WAIT_ACK_FOREVER(0) |
453 TR_CTRL_TX_DATA_SZ(256) |
454 TR_CTRL_RX_DATA_SZ(0);
455
456 //used for all elements less than 256 bytes in size
457 u32 pio_element = TR_CTRL_PORT_NR(mmce_port) |
458 TR_CTRL_PAUSE(0) |
459 TR_CTRL_TX_MODE_PIO_DMA(0) |
460 TR_CTRL_RX_MODE_PIO_DMA(0) |
461 TR_CTRL_NORMAL_TR(1) |
462 TR_CTRL_SPECIAL_TR(0) |
463 TR_CTRL_BAUD_DIV(0) |
464 TR_CTRL_WAIT_ACK_FOREVER(0) |
465 TR_CTRL_TX_DATA_SZ(pio_size) |
466 TR_CTRL_RX_DATA_SZ(0);
467
468 while(1) {
469 //Waiting stage
470 if (waiting == 1) {
471 res = mmce_sio2_tx_rx_pio(0, 2, NULL, rx_buf, &timeout_2s);
472 if (res == -1) {
473 DPRINTF("%s ERROR: Timed out waiting for ready\n", __func__);
474 return -1;
475 }
476
477 if (bytes_done >= size)
478 break;
479
480 //Move to transfer stage
481 waiting = 0;
482
483 //Transfer stage
484 } else if (waiting == 0) {
485 elements_do = elements > 16 ? 16 : elements;
486
487 //Full 4KB DMA transfer
488 if (elements_do == 16) {
489 //Reset SIO2 + FIFO pointers, enable interrupts
490 inl_sio2_ctrl_set(0x3ac);
491
492 for (int i = 0; i < elements_do; i++) {
493 inl_sio2_regN_set(i, dma_element);
494 }
495
496 sceSetSliceDMA(IOP_DMAC_SIO2in, &buffer[bytes_done], 0x100 >> 2, elements_do, DMAC_FROM_MEM);
497 sceStartDMA(IOP_DMAC_SIO2in);
498
499 bytes_done += elements_do * 256;
500 elements -= elements_do;
501
502 //Start the transfer
503 inl_sio2_ctrl_set(0x3a1);
504
505 //Set timeout alarm
506 SetAlarm(&timeout_2s, mmce_sio2_timeout_handler, &event_flag);
507
508 //Wait for completion or timeout
509 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
510 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
511 DPRINTF("Detected transfer timeout, attempting to reset SIO2\n");
512 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
513 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
514 return -1;
515 }
516
517 //Cancel timeout alarm
518 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
519
520 //Clear flags
521 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
522
523 waiting = 1;
524
525 //Sub 4KB DMA transfer
526 } else {
527 //Remaining DMA elements
528 if (elements_do != 0) {
529
530 //Reset SIO2 + FIFO pointers, enable interrupts
531 inl_sio2_ctrl_set(0x3ac);
532
533 for (int i = 0; i < elements_do; i++) {
534 inl_sio2_regN_set(i, dma_element);
535 }
536
537 sceSetSliceDMA(IOP_DMAC_SIO2in, &buffer[bytes_done], 0x100 >> 2, elements_do, DMAC_FROM_MEM);
538 sceStartDMA(IOP_DMAC_SIO2in);
539
540 bytes_done += elements_do * 256;
541 elements -= elements_do;
542
543 //Start the transfer
544 inl_sio2_ctrl_set(0x3a1);
545
546 //Set timeout alarm
547 SetAlarm(&timeout_2s, mmce_sio2_timeout_handler, &event_flag);
548
549 //Wait for completion or timeout
550 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
551 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
552 DPRINTF("Detected transfer timeout, attempting to reset SIO2\n");
553 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
554 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
555 return -1;
556 }
557
558 //Cancel timeout alarm
559 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
560
561 //Clear flags
562 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
563 }
564 //PIO element
565 if (pio_size != 0) {
566
567 //Reset SIO2 + FIFO pointers, enable interrupts
568 inl_sio2_ctrl_set(0x3ac);
569
570 inl_sio2_regN_set(0, pio_element);
571
572 //Place data TX FIFO
573 for (int i = 0; i < pio_size; i++) {
574 inl_sio2_data_out(buffer[bytes_done + i]);
575 }
576
577 bytes_done += pio_size;
578 pio_size = 0;
579
580 //Start the transfer
581 inl_sio2_ctrl_set(0x3a1);
582
583 //Set timeout alarm
584 SetAlarm(&timeout_2s, mmce_sio2_timeout_handler, &event_flag);
585
586 //Wait for completion or timeout
587 WaitEventFlag(event_flag, EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE, 1, &resbits);
588 if (resbits & EF_SIO2_TRANSFER_TIMEOUT) {
589 DPRINTF("Detected transfer timeout, attempting to reset SIO2\n");
590 inl_sio2_ctrl_set(0x3ac); //Reset SIO2
591 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
592 return -1;
593 }
594
595 //Cancel timeout alarm
596 CancelAlarm(mmce_sio2_timeout_handler, &event_flag);
597
598 //Clear flags
599 ClearEventFlag(event_flag, ~(EF_SIO2_TRANSFER_TIMEOUT | EF_SIO2_INTR_COMPLETE));
600 }
601 waiting = 1;
602 }
603
604 }
605 }
606 return 0;
607}