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