sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
mx4sio.c
1#include <bdm.h>
2#include <dmacman.h>
3#include <intrman.h>
4#include <loadcore.h>
5#include <thbase.h>
6#include <thevent.h>
7#include <thsemap.h>
8#include <xsio2man.h>
9
10#include "mx4sio.h"
11#include "crc16.h"
12#include "sio2man_hook.h"
13#include "sio2regs.h"
14#include "spi_sdcard_driver.h"
15
16#include "module_debug.h"
17
18IRX_ID("mx4sio", 1, 2);
19
20/* globals */
22int sio2_event_flag;
23
24static int sd_detect_thread_id = -1;
25static sio2_transfer_data_t global_td;
26static uint8_t sio2_current_baud = SIO2_BAUD_DIV_SLOW;
27static uint32_t sio2_save_crtl;
28
29/* ISR triggered by the completion of DMA transfer: SIO2 RX FIFO -> MEM */
30int mx_sio2_dma_isr_rx(void *arg)
31{
32 int ef = *(int *)arg;
33 int eflags = EF_SIO2_INTR_REVERSE;
34
35 /* clear SIO2 stat reg */
36 inl_sio2_stat_set(inl_sio2_stat_get());
37
38 /* wait for PIO portion of transfer to complete */
39 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
40 ;
41
42#ifdef CONFIG_USE_CRC16
43 /* get CRC16 bytes from RX FIFO */
44 cmd.crc[cmd.sectors_transferred] = reverse_byte_LUT8[inl_sio2_data_in()] << 8;
45 cmd.crc[cmd.sectors_transferred] |= reverse_byte_LUT8[inl_sio2_data_in()];
46#else
47 inl_sio2_data_in();
48 inl_sio2_data_in();
49#endif
50
51 /* NOTE: Some cards respond with 0xFE immediately after the CRC16, others do not.
52 * Try to get 0xFE regardless of whether the transfers complete as it's
53 * needed for correct alignment of CMD12 (STOP_TRANSMISSION) later */
54 if (inl_sio2_data_in() != 0xFE) {
55 cmd.response = mx_sio2_wait_equal(0xFE, READ_TOKEN_TIMEOUT);
56 } else {
57 cmd.response = SPISD_RESULT_OK;
58 }
59
60 cmd.sectors_transferred++;
61
62 if (cmd.sectors_transferred < cmd.sector_count && cmd.abort == 0) {
63
64 if (cmd.response == SPISD_RESULT_OK) {
65 /* start next DMA transfer */
66 mx_sio2_start_rx_dma(&cmd.buffer[cmd.sectors_transferred * SECTOR_SIZE]);
67 /* notify thread data is ready to be reversed */
68 iSetEventFlag(ef, eflags);
69 /* return from ISR */
70 return 1;
71 }
72
73 cmd.abort = CMD_ABORT_NO_READ_TOKEN;
74 }
75
76 /* done or error, notify thread */
77 eflags |= EF_SIO2_INTR_COMPLETE;
78 iSetEventFlag(ef, eflags);
79
80 return 1;
81}
82
83/* ISR triggered by the completetion of DMA transfer: MEM -> SIO2 TX FIFO */
84int mx_sio2_dma_isr_tx(void *arg)
85{
86 int ef = *(int *)arg;
87 int eflags = 0;
88
89 /* clear SIO2 stat reg */
90 inl_sio2_stat_set(inl_sio2_stat_get());
91
92 /* SIO2 requires all data to be sent to be present in the TX FIFO
93 * prior to starting the transfer. As such, SIO2 will not
94 * have completed the transfer prior to reaching this ISR */
95
96 /* wait for transfer to complete */
97 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
98 ;
99
100 /* CRC16 not yet implemented for writes */
101 mx_sio2_write_dummy();
102 mx_sio2_write_dummy();
103
104 cmd.sectors_transferred++;
105
106 /* get data response token */
107 /* 0101 = data accepted */
108 /* 1011 = data rejected, CRC error */
109 /* 1101 = data rejected, write error */
110 cmd.response = mx_sio2_wait_equal_masked(0x5, 0x1F, 10);
111 if (cmd.response != 0x5) {
112 M_DEBUG("ERROR: write data rejected, token 0x%x\n", cmd.response);
113 cmd.abort = 1;
114 }
115
116 /* wait for card to finish programming */
117 cmd.response = mx_sio2_wait_equal(0xFF, 0x80000);
118 if (cmd.response != SPISD_RESULT_OK) {
119 M_DEBUG("ERROR: failed to finish programming\n");
120 cmd.abort = 1;
121 }
122
123 if (cmd.sectors_transferred < cmd.sector_count && cmd.abort == 0) {
124 /* send write token */
125 mx_sio2_write_byte(0xFC);
126 /* start next DMA transfer */
127 mx_sio2_start_tx_dma(&cmd.buffer[cmd.sectors_transferred * SECTOR_SIZE]);
128 /* return from ISR */
129 return 1;
130 }
131
132 /* done or error, notify thread */
133 eflags = EF_SIO2_INTR_COMPLETE;
134 iSetEventFlag(ef, eflags);
135
136 return 1;
137}
138
139void mx_sio2_init_ports(sio2_transfer_data_t *td)
140{
141 for (uint8_t i = 0; i < 4; i++) {
142 inl_sio2_portN_ctrl1_set(i, td->port_ctrl1[i]);
143 inl_sio2_portN_ctrl2_set(i, td->port_ctrl2[i]);
144 }
145}
146
147void mx_sio2_init_td(sio2_transfer_data_t *td)
148{
149 for (uint8_t i = 0; i < 4; i++) {
150 td->port_ctrl1[i] = 0;
151 td->port_ctrl2[i] = 0;
152 }
153
154 td->port_ctrl1[PORT_NR] =
155 PCTRL0_ATT_LOW_PER(0x5) |
156 PCTRL0_ATT_MIN_HIGH_PER(0x5) |
157 PCTRL0_BAUD0_DIV(0x78) | /* BAUD0 is unused */
158 PCTRL0_BAUD1_DIV(sio2_current_baud); /* BAUD1 is used for every transfer */
159
160 td->port_ctrl2[PORT_NR] =
161 PCTRL1_ACK_TIMEOUT_PER(0x12C) |
162 PCTRL1_INTER_BYTE_PER(0x0) |
163 PCTRL1_UNK24(0x0) |
164 PCTRL1_IF_MODE_SPI_DIFF(0x0);
165}
166
167void mx_sio2_lock(uint8_t intr_type)
168{
169 int state;
170
171 /* lock sio2man driver so we can use it exclusively */
172 sio2man_hook_sio2_lock();
173
174 /* save ctrl state */
175 sio2_save_crtl = inl_sio2_ctrl_get();
176
177 /* we're in control, setup the ports for our use */
178 mx_sio2_init_ports(&global_td);
179
180 /* enable DMA interrupts */
181 CpuSuspendIntr(&state);
182
183 if (intr_type == INTR_RX) {
184 RegisterIntrHandler(IOP_IRQ_DMA_SIO2_OUT, 1, mx_sio2_dma_isr_rx, &sio2_event_flag);
185 EnableIntr(IOP_IRQ_DMA_SIO2_OUT);
186 }
187
188 if (intr_type == INTR_TX) {
189 RegisterIntrHandler(IOP_IRQ_DMA_SIO2_IN, 1, mx_sio2_dma_isr_tx, &sio2_event_flag);
190 EnableIntr(IOP_IRQ_DMA_SIO2_IN);
191 }
192
193 CpuResumeIntr(state);
194}
195
196void mx_sio2_unlock(uint8_t intr_type)
197{
198 int state;
199 int res;
200
201 /* disable DMA interrupts */
202 CpuSuspendIntr(&state);
203
204 if (intr_type == INTR_RX)
205 DisableIntr(IOP_IRQ_DMA_SIO2_OUT, &res);
206
207 if (intr_type == INTR_TX)
208 DisableIntr(IOP_IRQ_DMA_SIO2_IN, &res);
209
210 CpuResumeIntr(state);
211
212 /* restore ctrl state, and reset STATE + FIFOS */
213 inl_sio2_ctrl_set(sio2_save_crtl | 0xc);
214
215 /* unlock sio2man driver */
216 sio2man_hook_sio2_unlock();
217}
218
219void mx_sio2_set_baud(uint8_t baud)
220{
221 sio2_current_baud = baud;
222
223 mx_sio2_init_td(&global_td);
224 mx_sio2_init_ports(&global_td);
225}
226
227uint8_t mx_sio2_write_byte(uint8_t byte)
228{
229 /* reset SIO2 + FIFO pointers, disable interrupts */
230 inl_sio2_ctrl_set(0x0bc);
231
232 inl_sio2_regN_set(0,
233 TR_CTRL_PORT_NR(PORT_NR) |
234 TR_CTRL_PAUSE(0) |
235 TR_CTRL_TX_MODE_PIO_DMA(0) |
236 TR_CTRL_RX_MODE_PIO_DMA(0) |
237 TR_CTRL_NORMAL_TR(1) |
238 TR_CTRL_SPECIAL_TR(0) |
239 TR_CTRL_BAUD_DIV(1) |
240 TR_CTRL_WAIT_ACK_FOREVER(0) |
241 TR_CTRL_TX_DATA_SZ(1) |
242 TR_CTRL_RX_DATA_SZ(1));
243 inl_sio2_regN_set(1, 0);
244
245 /* put byte in TX FIFO */
246 inl_sio2_data_out(reverse_byte_LUT8[byte]);
247
248 /* start queue exec */
249 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
250
251 /* wait for completion */
252 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
253 ;
254
255#ifdef DEBUG_VERBOSE
256 uint8_t rx = reverse_byte_LUT8[inl_sio2_data_in()];
257 M_DEBUG("W:0x%x, R:0x%x\n", byte, rx);
258 return rx;
259#endif
260
261 /* get byte from RX FIFO */
262 return reverse_byte_LUT8[inl_sio2_data_in()];
263}
264
265uint8_t mx_sio2_write_dummy(void)
266{
267 /* reset SIO2 + FIFO pointers, disable interrupts */
268 inl_sio2_ctrl_set(0x0bc);
269
270 /* add transfer to queue */
271 inl_sio2_regN_set(0,
272 TR_CTRL_PORT_NR(PORT_NR) |
273 TR_CTRL_PAUSE(0) |
274 TR_CTRL_TX_MODE_PIO_DMA(0) |
275 TR_CTRL_RX_MODE_PIO_DMA(0) |
276 TR_CTRL_NORMAL_TR(1) |
277 TR_CTRL_SPECIAL_TR(0) |
278 TR_CTRL_BAUD_DIV(1) |
279 TR_CTRL_WAIT_ACK_FOREVER(0) |
280 TR_CTRL_TX_DATA_SZ(1) |
281 TR_CTRL_RX_DATA_SZ(1));
282 inl_sio2_regN_set(1, 0);
283
284 /* put byte in TX FIFO */
285 inl_sio2_data_out(0xFF);
286
287 /* start queue exec */
288 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
289
290 /* wait for completion */
291 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
292 ;
293
294#ifdef DEBUG_VERBOSE
295 uint8_t rx = reverse_byte_LUT8[inl_sio2_data_in()];
296 M_DEBUG("R:0x%x\n", rx);
297 return rx;
298#endif
299
300 /* get byte from RX FIFO */
301 return reverse_byte_LUT8[inl_sio2_data_in()];
302}
303
304/* 1.440uS delay between bytes with clk div 0x2 */
305uint8_t mx_sio2_wait_equal(uint8_t value, uint32_t count)
306{
307 uint8_t exp_byte = reverse_byte_LUT8[value];
308 uint8_t in_byte = 0;
309
310 while (count > 0 && in_byte != exp_byte) {
311 /* reset SIO2 + FIFO pointers, disable interrupts */
312 inl_sio2_ctrl_set(0x0bc);
313
314 inl_sio2_regN_set(0,
315 TR_CTRL_PORT_NR(PORT_NR) |
316 TR_CTRL_PAUSE(0) |
317 TR_CTRL_TX_MODE_PIO_DMA(0) |
318 TR_CTRL_RX_MODE_PIO_DMA(0) |
319 TR_CTRL_NORMAL_TR(1) |
320 TR_CTRL_SPECIAL_TR(0) |
321 TR_CTRL_BAUD_DIV(1) |
322 TR_CTRL_WAIT_ACK_FOREVER(0) |
323 TR_CTRL_TX_DATA_SZ(0) |
324 TR_CTRL_RX_DATA_SZ(1));
325 inl_sio2_regN_set(1, 0);
326
327 /* start queue exec */
328 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
329
330 /* wait for completion */
331 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
332 ;
333
334 in_byte = inl_sio2_data_in();
335
336#ifdef DEBUG_VERBOSE
337 M_DEBUG("WE: 0x%x, EX: 0x%x\n", reverse_byte_LUT8[in_byte], value);
338#endif
339
340 count--;
341 }
342
343 return (exp_byte != in_byte) ? SPISD_RESULT_ERROR : SPISD_RESULT_OK;
344}
345
346uint8_t mx_sio2_wait_not_equal(uint8_t value, uint32_t count)
347{
348 uint8_t exp_byte = reverse_byte_LUT8[value];
349 uint8_t in_byte = exp_byte;
350
351 while (count > 0 && in_byte == exp_byte) {
352 /* reset SIO2 + FIFO pointers, disable interrupts */
353 inl_sio2_ctrl_set(0x0bc);
354
355 /* add transfer to queue */
356 inl_sio2_regN_set(0,
357 TR_CTRL_PORT_NR(PORT_NR) |
358 TR_CTRL_PAUSE(0) |
359 TR_CTRL_TX_MODE_PIO_DMA(0) |
360 TR_CTRL_RX_MODE_PIO_DMA(0) |
361 TR_CTRL_NORMAL_TR(1) |
362 TR_CTRL_SPECIAL_TR(0) |
363 TR_CTRL_BAUD_DIV(1) |
364 TR_CTRL_WAIT_ACK_FOREVER(0) |
365 TR_CTRL_TX_DATA_SZ(0) |
366 TR_CTRL_RX_DATA_SZ(1));
367 inl_sio2_regN_set(1, 0);
368
369 /* start queue exec */
370 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
371
372 /* wait for completion */
373 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
374 ;
375
376 /* get byte from RX FIFO */
377 in_byte = inl_sio2_data_in();
378
379#ifdef DEBUG_VERBOSE
380 M_DEBUG("WNE: 0x%x, EX: !0x%x\n", reverse_byte_LUT8[in_byte], value);
381#endif
382 count--;
383 }
384
385 return reverse_byte_LUT8[in_byte];
386}
387
388uint8_t mx_sio2_wait_equal_masked(uint8_t value, uint8_t mask, uint32_t count)
389{
390 uint8_t exp_byte = reverse_byte_LUT8[value];
391 uint8_t rev_mask = reverse_byte_LUT8[mask];
392 uint8_t in_byte = 0;
393
394 while (count > 0 && in_byte != exp_byte) {
395 /* reset SIO2 + FIFO pointers, disable interrupts */
396 inl_sio2_ctrl_set(0x0bc);
397
398 inl_sio2_regN_set(0,
399 TR_CTRL_PORT_NR(PORT_NR) |
400 TR_CTRL_PAUSE(0) |
401 TR_CTRL_TX_MODE_PIO_DMA(0) |
402 TR_CTRL_RX_MODE_PIO_DMA(0) |
403 TR_CTRL_NORMAL_TR(1) |
404 TR_CTRL_SPECIAL_TR(0) |
405 TR_CTRL_BAUD_DIV(1) |
406 TR_CTRL_WAIT_ACK_FOREVER(0) |
407 TR_CTRL_TX_DATA_SZ(0) |
408 TR_CTRL_RX_DATA_SZ(1));
409 inl_sio2_regN_set(1, 0);
410
411 /* start queue exec */
412 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
413
414 /* wait for completion */
415 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
416 ;
417
418 in_byte = inl_sio2_data_in();
419
420#ifdef DEBUG_VERBOSE
421 M_DEBUG("WEM: 0x%x M:\n", reverse_byte_LUT8[in_byte], reverse_byte_LUT8[in_byte & rev_mask]);
422#endif
423 in_byte = in_byte & rev_mask;
424
425 count--;
426 }
427
428 return reverse_byte_LUT8[in_byte];
429}
430
431void mx_sio2_rx_pio(uint8_t *buffer, uint32_t size)
432{
433 uint32_t transfer_size;
434#ifdef DEBUG_VERBOSE
435 uint8_t *buffer_start = buffer;
436#endif
437 while (size > 0) {
438 /* SIO2 can only transfer 256 bytes at a time */
439 transfer_size = size > SIO2_MAX_TRANSFER_SIZE ? SIO2_MAX_TRANSFER_SIZE : size;
440
441 /* reset SIO2 + FIFO pointers, disable interrupts */
442 inl_sio2_ctrl_set(0x0bc);
443
444 /* add transfer to queue */
445 inl_sio2_regN_set(0,
446 TR_CTRL_PORT_NR(PORT_NR) |
447 TR_CTRL_PAUSE(0) |
448 TR_CTRL_TX_MODE_PIO_DMA(0) |
449 TR_CTRL_RX_MODE_PIO_DMA(0) |
450 TR_CTRL_NORMAL_TR(1) |
451 TR_CTRL_SPECIAL_TR(0) |
452 TR_CTRL_BAUD_DIV(1) |
453 TR_CTRL_WAIT_ACK_FOREVER(0) |
454 TR_CTRL_TX_DATA_SZ(0) |
455 TR_CTRL_RX_DATA_SZ(transfer_size));
456 inl_sio2_regN_set(1, 0);
457
458 /* start queue exec */
459 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
460
461 /* wait for completion */
462 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
463 ;
464
465 /* PIO: IOP <- SIO2 */
466 for (int i = 0; i < transfer_size; i++) {
467 *buffer++ = reverse_byte_LUT8[inl_sio2_data_in()];
468 }
469
470#ifdef DEBUG_VERBOSE
471 for (int i = 0; i < transfer_size; i++) {
472 M_DEBUG("W:0xFF, R:0x%x\n", buffer_start[i]);
473 }
474#endif
475
476 size -= transfer_size;
477 }
478}
479
480void mx_sio2_tx_pio(uint8_t *buffer, uint32_t size)
481{
482 uint32_t transfer_size;
483
484#ifdef DEBUG_VERBOSE
485 uint8_t *buffer_start = buffer;
486#endif
487
488 while (size > 0) {
489 /* SIO2 can only transfer 256 bytes at a time */
490 transfer_size = size > SIO2_MAX_TRANSFER_SIZE ? SIO2_MAX_TRANSFER_SIZE : size;
491
492 /* reset SIO2 + FIFO pointers, disable interrupts */
493 inl_sio2_ctrl_set(0x0bc);
494
495 /* add transfer to queue */
496 inl_sio2_regN_set(0,
497 TR_CTRL_PORT_NR(PORT_NR) |
498 TR_CTRL_PAUSE(0) |
499 TR_CTRL_TX_MODE_PIO_DMA(0) |
500 TR_CTRL_RX_MODE_PIO_DMA(0) |
501 TR_CTRL_NORMAL_TR(1) |
502 TR_CTRL_SPECIAL_TR(0) |
503 TR_CTRL_BAUD_DIV(1) |
504 TR_CTRL_WAIT_ACK_FOREVER(0) |
505 TR_CTRL_TX_DATA_SZ(transfer_size) |
506#ifdef DEBUG_VERBOSE
507 TR_CTRL_RX_DATA_SZ(transfer_size));
508#else
509 TR_CTRL_RX_DATA_SZ(0));
510#endif
511
512 inl_sio2_regN_set(1, 0);
513
514 /* PIO: IOP -> SIO2 */
515 for (int i = 0; i < transfer_size; i++) {
516 inl_sio2_data_out(reverse_byte_LUT8[*buffer++]);
517 }
518
519 /* start queue exec */
520 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
521
522 /* wait for completion */
523 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
524 ;
525
526#ifdef DEBUG_VERBOSE
527 for (int i = 0; i < transfer_size; i++) {
528 M_DEBUG("W:0x%x, R:0x%x\n", buffer_start[i], reverse_byte_LUT8[inl_sio2_data_in()]);
529 }
530#endif
531 size -= transfer_size;
532 }
533}
534
535void mx_sio2_start_rx_dma(uint8_t *buffer)
536{
537 /* DMA: 256 bytes */
538 const uint32_t tr_ctrl_dma =
539 TR_CTRL_PAUSE(0) |
540 TR_CTRL_TX_MODE_PIO_DMA(0) |
541 TR_CTRL_RX_MODE_PIO_DMA(1) |
542 TR_CTRL_NORMAL_TR(1) |
543 TR_CTRL_SPECIAL_TR(0) |
544 TR_CTRL_TX_DATA_SZ(0) |
545 TR_CTRL_RX_DATA_SZ(0x100) |
546 TR_CTRL_BAUD_DIV(1) |
547 TR_CTRL_WAIT_ACK_FOREVER(0);
548
549 /* PIO: CRC16 (2 bytes) + 0xFE token (1 byte) */
550 const uint32_t tr_ctrl_pio =
551 TR_CTRL_PAUSE(0) |
552 TR_CTRL_TX_MODE_PIO_DMA(0) |
553 TR_CTRL_RX_MODE_PIO_DMA(0) |
554 TR_CTRL_NORMAL_TR(1) |
555 TR_CTRL_SPECIAL_TR(0) |
556 TR_CTRL_TX_DATA_SZ(0) |
557 TR_CTRL_RX_DATA_SZ(3) |
558 TR_CTRL_BAUD_DIV(1) |
559 TR_CTRL_WAIT_ACK_FOREVER(0);
560
561 /* reset SIO2 + FIFO pointers, disable interrupts */
562 inl_sio2_ctrl_set(0x0bc);
563
564 /* add transfers to queue */
565 inl_sio2_regN_set(0, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
566 inl_sio2_regN_set(1, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
567 inl_sio2_regN_set(2, tr_ctrl_pio | TR_CTRL_PORT_NR(PORT_NR));
568 inl_sio2_regN_set(3, 0);
569
570 /* enable dmac transfer */
571 sceSetSliceDMA(IOP_DMAC_SIO2out, buffer, 0x100 >> 2, 2, DMAC_TO_MEM);
572 sceStartDMA(IOP_DMAC_SIO2out);
573
574 /* start queue exec */
575 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
576}
577
578void mx_sio2_start_tx_dma(uint8_t *buffer)
579{
580 /* DMA: 256 bytes */
581 const uint32_t tr_ctrl_dma =
582 TR_CTRL_PAUSE(0) |
583 TR_CTRL_TX_MODE_PIO_DMA(1) |
584 TR_CTRL_RX_MODE_PIO_DMA(0) |
585 TR_CTRL_NORMAL_TR(1) |
586 TR_CTRL_SPECIAL_TR(0) |
587 TR_CTRL_TX_DATA_SZ(0x100) |
588 TR_CTRL_RX_DATA_SZ(0) |
589 TR_CTRL_BAUD_DIV(1) |
590 TR_CTRL_WAIT_ACK_FOREVER(0);
591
592 /* reset SIO2 + FIFO pointers, disable interrupts */
593 inl_sio2_ctrl_set(0x0bc);
594
595 /* add transfers to queue */
596 inl_sio2_regN_set(0, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
597 inl_sio2_regN_set(1, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
598 inl_sio2_regN_set(2, 0);
599 inl_sio2_regN_set(3, 0);
600
601 /* enable dmac transfer */
602 sceSetSliceDMA(IOP_DMAC_SIO2in, buffer, 0x100 >> 2, 2, DMAC_FROM_MEM);
603 sceStartDMA(IOP_DMAC_SIO2in);
604
605 /* start queue exec */
606 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
607}
608
609static void sd_detect()
610{
611 uint16_t results;
612
613 mx_sio2_lock(INTR_NONE);
614
615 if (sdcard.initialized == 0) {
616 M_PRINTF("Trying to init card\n");
617 /* bring card up from identification mode to data-transfer mode */
618 if (spisd_init_card() == SPISD_RESULT_OK) {
619
620 /* get card capacity and attach to BDM */
621 if (spisd_get_card_info() == SPISD_RESULT_OK) {
622 bdm_connect_bd(&bd);
623 sdcard.initialized = 1;
624 }
625 }
626 } else {
627 /* try to detect card removal by requesting card status (CMD13) */
628 results = spisd_read_status_register();
629 /* comparing without a mask might be a bit overkill */
630 if (results != 0x0) {
631 /* try to recover */
632 results = spisd_recover();
633 /* maybe add a var to keep track of capacity */
634 if ((sdcard.initialized == 1) && (results != SPISD_RESULT_OK)) {
635 /* recovery failed, disconnect from BDM */
636 M_DEBUG("Recovery failed, disconnecting from bdm.\n");
637 bdm_disconnect_bd(&bd);
638 sdcard.initialized = 0;
639 }
640 }
641 }
642
643 mx_sio2_unlock(INTR_NONE);
644}
645
646static void sd_detect_thread(void *arg)
647{
648 (void)arg;
649
650 M_PRINTF("card detection thread running\n");
651
652 while (1) {
653 DelayThread(1000 * 1000);
654
655 /* try to detect card removal if it hasn't been used recently */
656 if (sdcard.used == 0)
657 sd_detect();
658 sdcard.used = 0;
659 }
660}
661
662
663/* Maximus32's C r3000 optimized byte reversal */
664/* 58-59uS avg on DECKARD */
665#pragma GCC push_options
666#pragma GCC optimize("-O3")
667inline void reverse_buffer(uint32_t *buffer, uint32_t count)
668{
669 const uint32_t mask0F = 0x0F0F0F0F;
670 const uint32_t maskF0 = 0xF0F0F0F0;
671 const uint32_t mask33 = 0x33333333;
672 const uint32_t maskCC = 0xCCCCCCCC;
673 const uint32_t mask55 = 0x55555555;
674 const uint32_t maskAA = 0xAAAAAAAA;
675 uint32_t n;
676
677#pragma GCC unroll 2
678 for (int i = 0; i < count; i++) {
679 n = buffer[i];
680 n = ((n & maskF0) >> 4) | ((n & mask0F) << 4);
681 n = ((n & maskCC) >> 2) | ((n & mask33) << 2);
682 n = ((n & maskAA) >> 1) | ((n & mask55) << 1);
683
684 buffer[i] = n;
685 }
686}
687#pragma GCC pop_options
688
689/* LUT for single byte reversal */
690const uint8_t reverse_byte_LUT8[256] = {
691 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
692 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
693 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
694 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
695 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
696 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
697 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
698 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
699 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
700 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
701 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
702 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
703 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
704 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
705 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
706 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
707 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
708 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
709 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
710 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
711 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
712 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
713 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
714 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
715 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
716 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
717 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
718 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
719 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
720 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
721 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
722 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff};
723
724/* module */
725static int module_start(int argc, char *argv[], void *startaddr, ModuleInfo_t *mi)
726{
728 iop_thread_t thread;
729 int rv;
730
731#ifndef MINI_DRIVER
732 int i;
733
734 M_PRINTF("Starting module\n");
735 for (i = 0; i < argc; i++)
736 M_PRINTF(" - argv[%d] = %s\n", i, argv[i]);
737#else
738 (void)argc;
739 (void)argv;
740#endif
741 (void)startaddr;
742
743 /* create default transfer descriptor */
744 mx_sio2_init_td(&global_td);
745
746 /* create event flag */
747 event.attr = 2;
748 event.option = 0;
749 event.bits = 0;
750 sio2_event_flag = CreateEventFlag(&event);
751 if (sio2_event_flag < 0) {
752 M_PRINTF("ERROR: CreateEventFlag returned %d\n", sio2_event_flag);
753 goto error1;
754 }
755
756 rv = sio2man_hook_init();
757 if (rv < 0) {
758 M_PRINTF("ERROR: sio2man_hook_init returned %d\n", rv);
759 goto error2;
760 }
761
762 /* Just in case sio2man was not loaded, we initialize the dmac channels too */
763 sceSetDMAPriority(IOP_DMAC_SIO2in, 3);
764 sceSetDMAPriority(IOP_DMAC_SIO2out, 3);
765 sceEnableDMAChannel(IOP_DMAC_SIO2in);
766 sceEnableDMAChannel(IOP_DMAC_SIO2out);
767
768 /* After a reboot the SD will always respond with:
769 * - 0xff 0xff 0xc1 0x3f
770 * - followed by an infinite amount of 0xff
771 */
772 mx_sio2_lock(INTR_NONE);
773 mx_sio2_rx_pio((void *)&rv, 4);
774 mx_sio2_unlock(INTR_NONE);
775
776 /* create SD card detection thread */
777 thread.attr = TH_C;
778 thread.thread = sd_detect_thread;
779 thread.option = 0;
780 thread.priority = USER_LOWEST_PRIORITY;
781 thread.stacksize = 0x1000; // 4KiB
782 rv = sd_detect_thread_id = CreateThread(&thread);
783 if (rv < 0) {
784 M_PRINTF("ERROR: CreateThread returned %d\n", rv);
785 goto error3;
786 }
787
788 /* Start thread */
789 rv = StartThread(sd_detect_thread_id, NULL);
790 if (rv < 0) {
791 M_PRINTF("ERROR: StartThread returned %d\n", rv);
792 goto error4;
793 }
794
795 // If modload has certain flags set indicating new version,
796 // set the unloadable flag
797 if (mi && ((mi->newflags & 2) != 0))
798 mi->newflags |= 0x10;
799 return MODULE_RESIDENT_END;
800
801error4:
802 DeleteThread(sd_detect_thread_id);
803error3:
804 sio2man_hook_deinit();
805error2:
806 DeleteEventFlag(sio2_event_flag);
807error1:
808 return MODULE_NO_RESIDENT_END;
809}
810
811static int module_stop(int argc, char *argv[], void *startaddr, ModuleInfo_t *mi)
812{
813#ifndef MINI_DRIVER
814 int i;
815
816 M_PRINTF("Stopping module\n");
817 for (i = 0; i < argc; i++)
818 M_PRINTF(" - argv[%d] = %s\n", i, argv[i]);
819#else
820 (void)argc;
821 (void)argv;
822#endif
823 (void)startaddr;
824 (void)mi;
825
826 DeleteThread(sd_detect_thread_id);
827 sio2man_hook_deinit();
828 DeleteEventFlag(sio2_event_flag);
829
830 return MODULE_NO_RESIDENT_END;
831}
832
833int _start(int argc, char *argv[], void *startaddr, ModuleInfo_t *mi)
834{
835 M_PRINTF("MX4SIO v1.2\n");
836
837 if (argc >= 0)
838 return module_start(argc, argv, startaddr, mi);
839 else
840 return module_stop(-argc, argv, startaddr, mi);
841}
int CpuResumeIntr(int state)
Definition intrman.c:217
int DisableIntr(int irq, int *res)
Definition intrman.c:385
int CpuSuspendIntr(int *state)
Definition intrman.c:195
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition intrman.c:115
int EnableIntr(int irq)
Definition intrman.c:336
struct _ModuleInfo ModuleInfo_t
u16 newflags
Definition loadcore.h:37
u32 count
start sector of fragmented bd/file