PS2SDK
PS2 Homebrew Libraries
spi_sdcard_driver.c
1 #include <bdm.h>
2 #include <stdint.h>
3 #include <thevent.h>
4 #include <thbase.h>
5 
6 #include "spi_sdcard_driver.h"
7 #include "spi_sdcard_crc7.h"
8 #include "crc16.h"
9 #include "mx4sio.h"
10 #include "sio2regs.h"
11 
12 #include "module_debug.h"
13 
14 #define CMD0 (0 | 0x40) /* Reset */
15 #define CMD1 (1 | 0x40) /* Send Operator Condition - SEND_OP_COND */
16 #define CMD8 (8 | 0x40) /* Send Interface Condition - SEND_IF_COND */
17 #define CMD9 (9 | 0x40) /* Read CSD */
18 #define CMD10 (10 | 0x40) /* Read CID */
19 #define CMD12 (12 | 0x40) /* Stop data transmit */
20 #define CMD13 (13 | 0x40) /* SEND_STATUS */
21 #define CMD16 (16 | 0x40) /* Set block size, should return 0x00 */
22 #define CMD17 (17 | 0x40) /* Read single block */
23 #define CMD18 (18 | 0x40) /* Read multi block */
24 #define ACMD23 (23 | 0x40) /* Prepare erase N-blocks before multi block write */
25 #define CMD24 (24 | 0x40) /* Write single block */
26 #define CMD25 (25 | 0x40) /* Write multi block */
27 #define ACMD41 (41 | 0x40) /* should return 0x00 */
28 #define CMD55 (55 | 0x40) /* should return 0x01 */
29 #define CMD58 (58 | 0x40) /* Read OCR */
30 #define CMD59 (59 | 0x40) /* CRC disable/enable, should return 0x00 */
31 
32 #define SPISD_R1_IDLE_FLAG (0x01)
33 #define SPISD_R1_ERASE_RESET_FLAG (0x02)
34 #define SPISD_R1_ILLEGAL_CMD_FLAG (0x04)
35 #define SPISD_R1_CMD_CRC_FLAG (0x08)
36 #define SPISD_R1_ERASE_SEQ_ERROR_FLAG (0x10)
37 #define SPISD_R1_ADDR_ERROR_FLAG (0x20)
38 #define SPISD_R1_PARAM_ERROR_FLAG (0x40)
39 #define SPISD_R1_ZERO_FLAG (0x80)
40 
41 #define CMD_WAIT_RESP_TIMEOUT (100U)
42 #define WAIT_IDLE_TIMEOUT (50U)
43 #define MAX_RETRIES 4
44 
45 #define CMD_CRC_OFFSET 5
46 #define CRC7_SHIFT_MASK(crc7) ((crc7) << 1U | 1U)
47 
48 /* globals */
49 spisd_t sdcard;
50 
51 /* BDM interface */
52 struct block_device bd = {
53  NULL, /* priv */
54  "sdc", /* name */
55  1, /* devNr, mx4sio is always in the second port */
56  0, /* parNr */
57  0x00, /* parId */
58  SECTOR_SIZE, /* sectorSize */
59  0, /* sectorOffset */
60  0, /* sectorCount */
61  spisd_read,
62  spisd_write,
63  spisd_flush,
64  spisd_stop,
65  "mx4sio" /* path */ };
66 
67 /* NOTE: SIO2 does *NOT* allow for direct control of /CS line.
68  * It's controlled by the SIO2 hardware and automatically asserted at the start
69  * of a transfer and deasserted at the end. This has lead to the need for some
70  * conditions surrounding dummy writes to avoid timing disruption. */
71 static uint8_t spisd_send_cmd(uint8_t cmd, uint32_t arg)
72 {
73  uint8_t response = 0xFF;
74 
75  /* avoid disrupting CMD12 alignment */
76  if (cmd != CMD12 && cmd != CMD0)
77  mx_sio2_write_dummy();
78 
79  uint8_t packet[] = {cmd, arg >> 24, arg >> 16, arg >> 8, arg, 0};
80  packet[CMD_CRC_OFFSET] = CRC7_SHIFT_MASK(crc7(packet, CMD_CRC_OFFSET));
81 
82  /* begin SIO2 PIO TX transfer */
83  mx_sio2_tx_pio(packet, sizeof(packet));
84 
85  /* discard extra data on CMD12 */
86  if (cmd == CMD12)
87  mx_sio2_write_dummy();
88 
89  /* wait for card to respond */
90  response = mx_sio2_wait_not_equal(0xFF, CMD_WAIT_RESP_TIMEOUT);
91 
92  /* avoid disrupting alignment on commands with mutli byte responses */
93  if (cmd != CMD9 && cmd != CMD10 && cmd != CMD13 && cmd != CMD18 && cmd != CMD25) {
94  mx_sio2_write_dummy();
95  }
96 
97  return response;
98 }
99 
100 static uint8_t spisd_send_cmd_recv_data(uint8_t cmd, uint32_t arg, uint8_t *data, size_t size)
101 {
102  uint8_t response = 0xFF;
103 
104  mx_sio2_write_dummy();
105 
106  uint8_t packet[] = {cmd, arg >> 24, arg >> 16, arg >> 8, arg, 0};
107  packet[CMD_CRC_OFFSET] = CRC7_SHIFT_MASK(crc7(packet, CMD_CRC_OFFSET));
108 
109  /* begin SIO2 PIO TX transfer */
110  mx_sio2_tx_pio(packet, sizeof(packet));
111 
112  /* wait for card to respond */
113  response = mx_sio2_wait_not_equal(0xFF, CMD_WAIT_RESP_TIMEOUT);
114  if (response != 0xFF) {
115  /* start SIO2 PIO RX transfer */
116  mx_sio2_rx_pio(data, size);
117  }
118 
119  mx_sio2_write_dummy();
120 
121  return response;
122 }
123 
124 static uint8_t spisd_read_register(uint8_t *buff, uint32_t len)
125 {
126  uint8_t results = SPISD_RESULT_ERROR;
127 
128  results = mx_sio2_wait_equal(0xFE, 2000);
129  if (results == SPISD_RESULT_OK) {
130  /* got read token, start SIO2 PIO RX transfer */
131  mx_sio2_rx_pio(buff, len);
132  }
133 
134  return results;
135 }
136 
137 /* TODO: use this to verify writes */
138 uint16_t spisd_read_status_register()
139 {
140  uint16_t response;
141 
142  response = spisd_send_cmd(CMD13, 0) << 8;
143  response |= mx_sio2_write_dummy();
144 
145  mx_sio2_write_dummy();
146 
147  return response;
148 }
149 
150 int spisd_init_card()
151 {
152  uint16_t timeout = WAIT_IDLE_TIMEOUT;
153  uint8_t response = 0;
154  uint8_t buffer[6];
155 
156  /* set baud to (400kHZ) for init */
157  mx_sio2_set_baud(SIO2_BAUD_DIV_SLOW);
158 
159  /* send at least 74 dummy clocks */
160  /*
161  uint8_t dummy_clks[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
162  0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
163 
164  mx_sio2_tx_pio(dummy_clks, 10);*/
165 
166  for (int i = 0; i < 16; i++) {
167  mx_sio2_write_dummy();
168  }
169 
170  do {
171  response = spisd_send_cmd(CMD0, 0);
172  timeout--;
173  } while ((response != SPISD_R1_IDLE_FLAG) && timeout > 0);
174 
175  if (!timeout) {
176  M_DEBUG("ERROR: CMD0 returned 0x%x, exp: 0x1\n", response);
177  return SPISD_RESULT_TIMEOUT;
178  }
179 
180  /* send CMD8 with check pattern, store R3 response in buffer */
181  response = spisd_send_cmd_recv_data(CMD8, 0x1AA, buffer, sizeof(buffer));
182 
183  /* if CMD8 response is idle, card is CSD v2 */
184  if (response == SPISD_R1_IDLE_FLAG) {
185 
186  /* valid check pattern */
187  if (buffer[2] == 0x01 && buffer[3] == 0xAA) {
188  /* CMD55 / ACMD41 pairs */
189  for (int i = 0; i < 0xFFF; i++) {
190  response = spisd_send_cmd(CMD55, 0);
191  if (response != 0x01) {
192  M_DEBUG("ERROR: CMD55 returned 0x%x, exp: 0x1\n", response);
193  return SPISD_RESULT_TIMEOUT;
194  }
195 
196  response = spisd_send_cmd(ACMD41, 0x40000000);
197  if (response == 0x00) {
198  break;
199  }
200  }
201 
202  if (response != 0x00) {
203  M_DEBUG("ERROR: ACMD41 returned 0x%x, exp: 0x1\n", response);
204  return SPISD_RESULT_TIMEOUT;
205  }
206 
207  /* read OCR by CMD58 */
208  response = spisd_send_cmd_recv_data(CMD58, 0, buffer, sizeof(buffer));
209  if (response != 0x00) {
210  M_DEBUG("ERROR: CMD58 returned 0x%x, exp 0x0\n", response);
211  return SPISD_RESULT_TIMEOUT;
212  }
213 
214  /* OCR -> CCS(bit30) 1: SDV2HC 0: SDV2 */
215  sdcard.card_type = (buffer[0] & 0x40) ? CARD_TYPE_SDV2HC : CARD_TYPE_SDV2;
216 
217  /* set baud to 25MHz */
218  mx_sio2_set_baud(SIO2_BAUD_DIV_FAST);
219 
220  } else {
221  M_DEBUG("ERROR: CMD8 check pattern failed, got 0x%x, 0x%x\n", buffer[2], buffer[3]);
222  return SPISD_RESULT_ERROR;
223  }
224 
225  /* if CMD8 response is illegal, card is CSD v1 / MMC */
226  } else if (response & SPISD_R1_ILLEGAL_CMD_FLAG) {
227  M_DEBUG("CMD8 illegal, trying CSD v1.0 init\n");
228 
229  /* end of CMD8, dummy write */
230  mx_sio2_write_dummy();
231 
232  /* CMD55 / ACMD41 pairs */
233  for (int i = 0; i < 0xFFF; i++) {
234  response = spisd_send_cmd(CMD55, 0);
235  if (response != 0x01) {
236  M_DEBUG("ERROR: CMD55 returned 0x%x, exp 0x1\n", response);
237  return SPISD_RESULT_TIMEOUT;
238  }
239 
240  response = spisd_send_cmd(ACMD41, 0);
241  if (response == 0x00) {
242  break;
243  }
244  }
245 
246  /* no response to CMD55 / ACMD41 means MMC card */
247  if (response != 0x00) {
248  for (int i = 0; i < 0xFFF; i++) {
249  response = spisd_send_cmd(CMD1, 0);
250  if (response == 0x00) {
251  break;
252  }
253  }
254 
255  if (response != 0x00) {
256  M_DEBUG("ERROR: CMD1 returned 0x%x, exp 0x0\n", response);
257  return SPISD_RESULT_TIMEOUT;
258  }
259 
260  sdcard.card_type = CARD_TYPE_MMC;
261  M_DEBUG("Card Type : MMC\r\n");
262  } else {
263  sdcard.card_type = CARD_TYPE_SDV1;
264  M_DEBUG("Card Type : CSD v1\r\n");
265  }
266 
267  /* set baud to 25MHz */
268  mx_sio2_set_baud(SIO2_BAUD_DIV_FAST);
269 
270  /* CRC disable */
271  response = spisd_send_cmd(CMD59, 0);
272 
273  if (response != 0x00) {
274  M_DEBUG("Send CMD59 should return 0x00, response=0x%02x\r\n", response);
275  return SPISD_RESULT_TIMEOUT;
276  }
277 
278  /* set the block size */
279  response = spisd_send_cmd(CMD16, 512);
280  if (response != 0x00) {
281  M_DEBUG("ERROR: CMD16 returned 0x%x, exp 0x0\n", response);
282  return SPISD_RESULT_TIMEOUT;
283  }
284 
285  /* CMD8 response invalid */
286  } else {
287  M_DEBUG("ERROR: CMD8 returned 0x%x, exp 0x1 or 0x4\n", response);
288  return SPISD_RESULT_ERROR;
289  }
290 
291  return SPISD_RESULT_OK;
292 }
293 
294 /* get card info and attach to bdm driver */
295 int spisd_get_card_info()
296 {
297  /* 16 bytes + 2 byte CRC16 */
298  uint8_t reg_data[18];
299 
300  /* send CMD9, read CSD */
301  uint8_t result = spisd_send_cmd(CMD9, 0);
302  if (result != 0x0) {
303  return result;
304  }
305 
306  result = spisd_read_register(sdcard.csd, sizeof(reg_data));
307 
308  /* dummy write between reg reads */
309  mx_sio2_write_dummy();
310 
311  if (result != 0x0) {
312  return SPISD_RESULT_ERROR;
313  }
314 
315  /* send CMD10, read CID */
316  result = spisd_send_cmd(CMD10, 0);
317 
318  if (result != 0x0) {
319  return SPISD_RESULT_ERROR;
320  }
321 
322  result = spisd_read_register(reg_data, sizeof(reg_data));
323 
324  mx_sio2_write_dummy();
325 
326  if (result != 0x0) {
327  return SPISD_RESULT_ERROR;
328  }
329 
330  sdcard.cid.ManufacturerID = reg_data[0];
331  /* Byte 1 */
332  sdcard.cid.OEM_AppliID = reg_data[1] << 8;
333  /* Byte 2 */
334  sdcard.cid.OEM_AppliID |= reg_data[2];
335  /* Byte 3 */
336  sdcard.cid.ProdName1 = reg_data[3] << 24;
337  /* Byte 4 */
338  sdcard.cid.ProdName1 |= reg_data[4] << 16;
339  /* Byte 5 */
340  sdcard.cid.ProdName1 |= reg_data[5] << 8;
341  /* Byte 6 */
342  sdcard.cid.ProdName1 |= reg_data[6];
343  /* Byte 7 */
344  sdcard.cid.ProdName2 = reg_data[7];
345  /* Byte 8 */
346  sdcard.cid.ProdRev = reg_data[8];
347  /* Byte 9 */
348  sdcard.cid.ProdSN = reg_data[9] << 24;
349  /* Byte 10 */
350  sdcard.cid.ProdSN |= reg_data[10] << 16;
351  /* Byte 11 */
352  sdcard.cid.ProdSN |= reg_data[11] << 8;
353  /* Byte 12 */
354  sdcard.cid.ProdSN |= reg_data[12];
355  /* Byte 13 */
356  sdcard.cid.Reserved1 |= (reg_data[13] & 0xF0) >> 4;
357  /* Byte 14 */
358  sdcard.cid.ManufactDate = (reg_data[13] & 0x0F) << 8;
359  /* Byte 15 */
360  sdcard.cid.ManufactDate |= reg_data[14];
361  /* Byte 16 */
362  sdcard.cid.CID_CRC = (reg_data[15] & 0xFE) >> 1;
363 
364  struct t_csdVer1 *csdv1 = (struct t_csdVer1 *)sdcard.csd;
365  struct t_csdVer2 *csdv2 = (struct t_csdVer2 *)sdcard.csd;
366 
367  /* CSD v1 - SDSC */
368  if (csdv1->csd_structure == 0) {
369  unsigned int c_size_mult = (csdv1->c_size_multHi << 1) | csdv1->c_size_multLo;
370  unsigned int c_size = (csdv1->c_sizeHi << 10) | (csdv1->c_sizeMd << 2) | csdv1->c_sizeLo;
371  unsigned int blockNr = (c_size + 1) << (c_size_mult + 2);
372  unsigned int blockLen = 1 << csdv1->read_bl_len;
373  unsigned int capacity = blockNr * blockLen;
374 
375  bd.sectorCount = capacity / 512;
376 
377  /* CSD v2 - SDHC, SDXC */
378  } else if (csdv1->csd_structure == 1) {
379  unsigned int c_size = (csdv2->c_sizeHi << 16) | (csdv2->c_sizeMd << 8) | csdv2->c_sizeLo;
380  bd.sectorCount = (c_size + 1) * 1024;
381  }
382 
383  M_PRINTF("%lu %u-byte logical blocks: (%luMB / %luMiB)\n", (u32)bd.sectorCount, bd.sectorSize, (u32)bd.sectorCount / ((1000 * 1000) / bd.sectorSize), (u32)bd.sectorCount / ((1024 * 1024) / bd.sectorSize));
384 
385  return SPISD_RESULT_OK;
386 }
387 
388 int spisd_recover()
389 {
390  int rv;
391  /* flush 256 bytes */
392  for (int i = 0; i < 64; i++)
393  mx_sio2_rx_pio((void *)&rv, 4);
394 
395  if (spisd_init_card() != SPISD_RESULT_OK) {
396  M_DEBUG("recovery failed to reinit card!\n");
397  return SPISD_RESULT_ERROR;
398  }
399 
400  if (spisd_get_card_info() != SPISD_RESULT_OK) {
401  M_DEBUG("recovery failed to get card info!\n");
402  return SPISD_RESULT_ERROR;
403  }
404 
405  return SPISD_RESULT_OK;
406 }
407 
408 /* read functions */
409 static int spisd_read_multi_begin(uint32_t sector)
410 {
411  uint8_t results = SPISD_RESULT_ERROR;
412  /* get idle */
413  results = mx_sio2_wait_equal(0xFF, 4000);
414 
415  if (results == SPISD_RESULT_OK) {
416  /* non SDHC/SDXC are addressed in 1-byte units */
417  if (sdcard.card_type != CARD_TYPE_SDV2HC) {
418  sector = sector << 9;
419  }
420 
421  /* send CMD18 to being multi block read */
422  results = spisd_send_cmd(CMD18, sector);
423  if (results == SPISD_RESULT_OK) {
424  /* wait for first read token (0xFE) */
425  results = mx_sio2_wait_equal(0xFE, 100000);
426  }
427  }
428 
429  return results;
430 }
431 
432 static int spisd_read_multi_do(void *buffer, uint16_t count)
433 {
434  /* setup DMA cmd struct */
435  cmd.buffer = buffer;
436  cmd.sector_count = count;
437  cmd.sectors_transferred = 0;
438  cmd.sectors_reversed = 0;
439  cmd.response = SPISD_RESULT_OK;
440  cmd.abort = 0;
441 
442  /* start first DMA transfer */
443  mx_sio2_start_rx_dma(buffer);
444 
445  /* process events from DMA completion interrupt */
446  while (1) {
447  uint32_t resbits;
448 
449  WaitEventFlag(sio2_event_flag, EF_SIO2_INTR_REVERSE | EF_SIO2_INTR_COMPLETE, 1, &resbits);
450 
451  if (resbits & EF_SIO2_INTR_REVERSE) {
452  ClearEventFlag(sio2_event_flag, ~EF_SIO2_INTR_REVERSE);
453 
454  while (cmd.sectors_reversed < cmd.sectors_transferred && cmd.abort == 0) {
455  void *buf = (uint32_t *)&cmd.buffer[cmd.sectors_reversed * SECTOR_SIZE];
456  reverse_buffer(buf, SECTOR_SIZE / 4);
457 
458 #ifdef CONFIG_USE_CRC16
459  uint16_t crc_a = crc16(buf, SECTOR_SIZE);
460  uint16_t crc_b = cmd.crc[cmd.sectors_reversed];
461  if (crc_a != crc_b) {
462  // CRC mismatch:
463  // - Signal ISR to stop reading
464  // - Wait for complete event from ISR
465  M_DEBUG("CRC mismatch on sector %i, got: 0x%x, exp 0x%x\n", cmd.sectors_reversed, crc_b, crc_a);
466  cmd.abort = CMD_ERROR_CRC16_INVALID;
467  }
468 #endif
469  if (cmd.abort == 0)
470  cmd.sectors_reversed++;
471  }
472  }
473 
474  if (resbits & EF_SIO2_INTR_COMPLETE) {
475  ClearEventFlag(sio2_event_flag, ~EF_SIO2_INTR_COMPLETE);
476  break;
477  }
478  }
479 
480  return cmd.sectors_reversed;
481 }
482 
483 static void spisd_read_multi_end()
484 {
485  /* 0xFE token will be received in the ISR prior to this function being called
486  * ensuring the start of CMD12 is aligned with the end of 0xFE
487  * See 7.5.2.2 Stop Transmission Timing of the SD Physical Layer Specification
488  * for more details */
489  spisd_send_cmd(CMD12, 0);
490 }
491 
492 /* write functions */
493 static int spisd_write_multi_begin(uint32_t sector, uint16_t count)
494 {
495  uint8_t results = SPISD_RESULT_ERROR;
496 
497  /* get idle */
498  results = mx_sio2_wait_equal(0xFF, 4000);
499 
500  if (results == SPISD_RESULT_OK) {
501  /* non SDHC/SDXC are addressed in 1-byte units */
502  if (sdcard.card_type != CARD_TYPE_SDV2HC) {
503  sector = sector << 9;
504  }
505 
506  /* issue ACMD23 to pre-erase sectors on non MMC cards */
507  if (sdcard.card_type != CARD_TYPE_MMC) {
508  results = spisd_send_cmd(CMD55, 0);
509  results = spisd_send_cmd(ACMD23, count);
510  }
511 
512  /* send CMD25 to begin multi block write */
513  results = spisd_send_cmd(CMD25, sector);
514  }
515 
516  mx_sio2_write_dummy();
517  mx_sio2_write_dummy();
518  mx_sio2_write_dummy();
519 
520  return results;
521 }
522 
523 static int spisd_write_multi_do(void* buffer, uint16_t count)
524 {
525  uint32_t resbits;
526 
527  /* setup DMA cmd struct */
528  cmd.buffer = buffer;
529  cmd.sector_count = count;
530  cmd.sectors_transferred = 0;
531  cmd.sectors_reversed = 0;
532  cmd.response = SPISD_RESULT_OK;
533  cmd.abort = 0;
534 
535  /* send initial write token */
536  mx_sio2_write_byte(0xFC);
537 
538  /* start transfer */
539  mx_sio2_start_tx_dma(buffer);
540 
541  /* wait for transfer to complete */
542  WaitEventFlag(sio2_event_flag, EF_SIO2_INTR_COMPLETE, 1, &resbits);
543 
544  if (resbits & EF_SIO2_INTR_COMPLETE) {
545  ClearEventFlag(sio2_event_flag, ~EF_SIO2_INTR_COMPLETE);
546  }
547 
548  return cmd.sectors_transferred;
549 }
550 
551 static int spisd_write_multi_end()
552 {
553  uint8_t results = SPISD_RESULT_ERROR;
554 
555  /* issue stop transmission token */
556  results = mx_sio2_write_byte(0xFD);
557  if (results != 0x0) {
558  mx_sio2_write_dummy();
559  mx_sio2_write_dummy();
560 
561  /* give card time to finish programming */
562  results = mx_sio2_wait_equal(0xFF, 0x800000);
563  if (results == SPISD_RESULT_OK) {
564  mx_sio2_write_dummy();
565  mx_sio2_write_dummy();
566  }
567  } else {
568  M_DEBUG("ERROR: failed to end write, 0xFD response 0x%x\n", results);
569  }
570 
571  mx_sio2_write_dummy();
572 
573  return results;
574 }
575 
576 /*
577  * BDM interface:
578  * - BDM -> "spisd" library
579  */
580 int spisd_read(struct block_device *bd, uint64_t sector, void *buffer, uint16_t count)
581 {
582  uint16_t sectors_left = count;
583  uint16_t results = 0;
584  uint16_t retries = 0;
585 
586  (void)bd;
587 
588  if (count == 0)
589  return 0;
590 
591  M_DEBUG("%s: sector %i, count: %i \n", __FUNCTION__, (u32)sector, count);
592 
593  mx_sio2_lock(INTR_RX);
594 
595  while (sectors_left > 0 && retries < MAX_RETRIES) {
596 
597  /* issue CMD18 to begin transfer */
598  results = spisd_read_multi_begin((uint32_t)sector);
599  if (results != SPISD_RESULT_OK) {
600  M_DEBUG("ERROR: failed to start multi-block read\n");
601  break;
602  }
603 
604  /* start reading blocks */
605  results = spisd_read_multi_do(buffer, sectors_left);
606  sectors_left = sectors_left - results;
607 
608  /* fail condition */
609  if (sectors_left > 0) {
610  buffer = (uint8_t *)buffer + (results * 512);
611  M_DEBUG("ERROR: failed to read all sectors, read:%i, abort:%i\n", sectors_left, cmd.abort);
612 
613  if (cmd.abort == CMD_ABORT_NO_READ_TOKEN) {
614  /* this can only be resolved by resetting the card */
615  if (spisd_recover() != SPISD_RESULT_OK) {
616  /* if recovery fails, do not try to continue */
617  break;
618  }
619  }
620  }
621  /* send CMD12, end transfer */
622  spisd_read_multi_end();
623 
624  retries++;
625  }
626 
627  sdcard.used = 1;
628 
629  mx_sio2_unlock(INTR_RX);
630 
631  return count - sectors_left;
632 }
633 
634 int spisd_write(struct block_device *bd, uint64_t sector, const void *buffer, uint16_t count)
635 {
636  (void)bd;
637 
638  uint16_t sectors_left = count;
639  uint16_t results = 0;
640  uint16_t retries = 0;
641 
642  if (count == 0)
643  return 0;
644 
645  M_DEBUG("%s: sector %i, count: %i \n", __FUNCTION__, (u32)sector, count);
646 
647  /* recast */
648  void *write_buffer = (uint32_t*)buffer;
649 
650  /* pre-reverse the entire buffer */
651  reverse_buffer(write_buffer, ((count * SECTOR_SIZE) / 4));
652 
653  mx_sio2_lock(INTR_TX);
654 
655  while (sectors_left > 0 && retries < MAX_RETRIES) {
656 
657  /* issue CMD25 to begin transfer */
658  results = spisd_write_multi_begin(sector, count);
659  if (results != SPISD_RESULT_OK) {
660  M_DEBUG("ERROR: failed to start multi-block write\n");
661  break;
662  }
663 
664  /* start writing blocks */
665  results = spisd_write_multi_do(write_buffer, sectors_left);
666  sectors_left = sectors_left - results;
667 
668  /* fail condition */
669  if (sectors_left > 0) {
670  write_buffer = (uint8_t *)write_buffer + (results * 512); /* update buffer for next attempt */
671  M_DEBUG("ERROR: failed to write all sectors, wrote: %i\n", results);
672  }
673 
674  /* send 0xFD, end transfer */
675  results = spisd_write_multi_end();
676  if (results != SPISD_RESULT_OK) {
677  M_DEBUG("ERROR: failed to end multi-block write\n");
678  break;
679  }
680 
681  retries++;
682  }
683 
684  sdcard.used = 1;
685 
686  mx_sio2_unlock(INTR_TX);
687 
688  return count - sectors_left;
689 }
690 
691 void spisd_flush(struct block_device *bd)
692 {
693  (void)bd;
694  return;
695 }
696 
697 int spisd_stop(struct block_device *bd)
698 {
699  (void)bd;
700  return 0;
701 }
bdm.h
t_csdVer1
Definition: spi_sdcard_driver.h:32
thbase.h
t_csdVer2
Definition: spi_sdcard_driver.h:84
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
spisd_t
Definition: spi_sdcard_driver.h:150
block_device
Definition: bdm.h:21
thevent.h