PS2SDK
PS2 Homebrew Libraries
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 
18 IRX_ID("mx4sio", 1, 2);
19 
20 /* globals */
21 dma_command_t cmd;
22 int sio2_event_flag;
23 
24 static int sd_detect_thread_id = -1;
25 static sio2_transfer_data_t global_td;
26 static uint8_t sio2_current_baud = SIO2_BAUD_DIV_SLOW;
27 static uint32_t sio2_save_crtl;
28 
29 /* ISR triggered by the completion of DMA transfer: SIO2 RX FIFO -> MEM */
30 int 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 */
84 int 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 
139 void 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 
147 void 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 
167 void 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 
196 void 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 
219 void 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 
227 uint8_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 
265 uint8_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 */
305 uint8_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 
346 uint8_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 
388 uint8_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 
431 void 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 
480 void 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 
535 void 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 
578 void 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 
609 static 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 
646 static 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")
667 inline 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 */
690 const 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 */
725 static int module_start(int argc, char *argv[], void *startaddr, ModuleInfo_t *mi)
726 {
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 
801 error4:
802  DeleteThread(sd_detect_thread_id);
803 error3:
804  sio2man_hook_deinit();
805 error2:
806  DeleteEventFlag(sio2_event_flag);
807 error1:
808  return MODULE_NO_RESIDENT_END;
809 }
810 
811 static 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 
833 int _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 }
RegisterIntrHandler
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition: intrman.c:115
dmacman.h
bdm.h
_ModuleInfo::newflags
u16 newflags
Definition: loadcore.h:36
xsio2man.h
thbase.h
DisableIntr
int DisableIntr(int irq, int *res)
Definition: intrman.c:385
thread
Definition: thcommon.h:143
CpuSuspendIntr
int CpuSuspendIntr(int *state)
Definition: intrman.c:195
event
Definition: thcommon.h:71
loadcore.h
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
EnableIntr
int EnableIntr(int irq)
Definition: intrman.c:336
thsemap.h
_iop_thread
Definition: thbase.h:39
CpuResumeIntr
int CpuResumeIntr(int state)
Definition: intrman.c:217
sio2_transfer_data_t
Definition: sio2man.h:33
dma_command_t
Definition: mx4sio.h:39
intrman.h
_ModuleInfo
Definition: loadcore.h:31
iop_event_t
Definition: thevent.h:37
thevent.h