summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorRob Purchase <shotofadds@rockbox.org>2009-07-19 17:31:56 +0000
committerRob Purchase <shotofadds@rockbox.org>2009-07-19 17:31:56 +0000
commit3a88746780aceed6253967b39db753c2c8c8304b (patch)
tree5dfef64276e3e5205a228f8cdbb109b5f437ab7b /firmware/target
parent8f5264e6186572532433eecaad0a618c81e48a3f (diff)
downloadrockbox-3a88746780aceed6253967b39db753c2c8c8304b.tar.gz
rockbox-3a88746780aceed6253967b39db753c2c8c8304b.zip
D2: A working read/write SD(HC) driver, based on the PP equivalent. See CowonD2Info wiki for usage notes/restrictions.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21967 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/tcc780x/sd-tcc780x.c835
1 files changed, 835 insertions, 0 deletions
diff --git a/firmware/target/arm/tcc780x/sd-tcc780x.c b/firmware/target/arm/tcc780x/sd-tcc780x.c
new file mode 100644
index 0000000000..8b0ac8a8a6
--- /dev/null
+++ b/firmware/target/arm/tcc780x/sd-tcc780x.c
@@ -0,0 +1,835 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Daniel Ankers
11 * Copyright (C) 2009 Rob Purchase
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "sd.h"
23#include "system.h"
24#include <string.h>
25#include "hotswap.h"
26#include "storage.h"
27#include "led.h"
28#include "thread.h"
29#include "disk.h"
30#include "fat.h"
31#include "ata_idle_notify.h"
32#include "usb.h"
33
34#if defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
35#define CARD_NUM_INTERNAL 0
36#define CARD_NUM_SLOT 1
37#elif !defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
38#define CARD_NUM_SLOT 0
39#endif
40
41#define EC_OK 0
42#define EC_FAILED 1
43#define EC_NOCARD 2
44#define EC_WAIT_STATE_FAILED 3
45#define EC_POWER_UP 4
46#define EC_FIFO_WR_EMPTY 5
47#define EC_FIFO_WR_DONE 6
48#define EC_TRAN_READ_ENTRY 7
49#define EC_TRAN_READ_EXIT 8
50#define EC_TRAN_WRITE_ENTRY 9
51#define EC_TRAN_WRITE_EXIT 10
52#define EC_COMMAND 11
53
54/* for compatibility */
55static long last_disk_activity = -1;
56
57/** static, private data **/
58static bool initialized = false;
59
60static long next_yield = 0;
61#define MIN_YIELD_PERIOD 1000
62
63static tCardInfo card_info[NUM_DRIVES];
64static tCardInfo *currcard = NULL; /* current active card */
65
66struct sd_card_status
67{
68 int retry;
69 int retry_max;
70};
71
72static struct sd_card_status sd_status[NUM_DRIVES] =
73{
74#ifdef HAVE_INTERNAL_SD
75 { 0, 1 },
76#endif
77#ifdef HAVE_HOTSWAP
78 { 0, 10 }
79#endif
80};
81
82/* Shoot for around 75% usage */
83static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)];
84static const char sd_thread_name[] = "sd";
85static struct mutex sd_mtx SHAREDBSS_ATTR;
86static struct event_queue sd_queue;
87
88static int sd_first_drive = 0;
89
90
91static bool sd_poll_status(unsigned int trigger, long timeout)
92{
93 long t = USEC_TIMER;
94
95 while ((SDISTATUS & trigger) != trigger)
96 {
97 long time = USEC_TIMER;
98
99 if (TIME_AFTER(time, next_yield))
100 {
101 long ty = USEC_TIMER;
102 yield();
103 timeout += USEC_TIMER - ty;
104 next_yield = ty + MIN_YIELD_PERIOD;
105 }
106
107 if (TIME_AFTER(time, t + timeout))
108 return false;
109 }
110
111 return true;
112}
113
114static int sd_command(unsigned int cmd, unsigned int arg,
115 unsigned long* response, unsigned int resp_type)
116{
117 int sdi_cmd = cmd;
118
119 sdi_cmd |= (127<<12) | (1<<11); /* max wait time | enable */
120
121 if (resp_type)
122 {
123 /* response type & response required flag */
124 sdi_cmd |= (resp_type<<7) | (1<<6);
125 }
126
127 if (cmd == SD_READ_SINGLE_BLOCK ||
128 cmd == SD_READ_MULTIPLE_BLOCK ||
129 cmd == SD_WRITE_BLOCK ||
130 cmd == SD_WRITE_MULTIPLE_BLOCK)
131 {
132 sdi_cmd |= (1<<10); /* request data transfer */
133 }
134
135 if (!sd_poll_status(SDISTATUS_CMD_PATH_RDY, 100000))
136 return -EC_COMMAND;
137
138 SDIARGU = arg;
139 SDICMD = sdi_cmd;
140
141 udelay(10);
142
143 if (response == NULL)
144 return 0;
145
146 if (!sd_poll_status(SDISTATUS_RESP_RCVD, 100000))
147 return -EC_COMMAND;
148
149 if (resp_type == SDICMD_RES_TYPE2)
150 {
151 response[0] = SDIRSPARGU0;
152 response[1] = SDIRSPARGU1;
153 response[2] = SDIRSPARGU2;
154 response[3] = SDIRSPARGU3;
155 }
156 else
157 {
158 response[0] = SDIRSPARGU0;
159 }
160
161 return 0;
162}
163
164static int sd_wait_for_state(unsigned int state, int id)
165{
166 unsigned long response = 0;
167 unsigned int timeout = 0x80000;
168
169 long start_time = USEC_TIMER;
170
171 while (1)
172 {
173 int ret = sd_command
174 (SD_SEND_STATUS, currcard->rca, &response, SDICMD_RES_TYPE1);
175
176 long us;
177
178 if (ret < 0)
179 return ret*100 - id;
180
181 if (((response >> 9) & 0xf) == state)
182 {
183 return 0;
184 }
185
186 if (TIME_AFTER(USEC_TIMER, start_time + timeout))
187 return -EC_WAIT_STATE_FAILED*100 - id;
188
189 us = USEC_TIMER;
190 if (TIME_AFTER(us, next_yield))
191 {
192 yield();
193 timeout += USEC_TIMER - us;
194 next_yield = us + MIN_YIELD_PERIOD;
195 }
196 }
197}
198
199static void sd_card_mux(int card_no)
200{
201 /* We only support the default card */
202 (void)card_no;
203}
204
205#ifdef HAVE_HOTSWAP
206
207bool card_detect_target(void)
208{
209 return (GPIOB & (1<<26)) == 0; /* low active */
210}
211
212void card_enable_monitoring_target(bool on)
213{
214 if (on)
215 {
216 IEN |= EXT0_IRQ_MASK;
217 }
218 else
219 {
220 IEN &= ~EXT0_IRQ_MASK;
221 }
222}
223
224static int sd1_oneshot_callback(struct timeout *tmo)
225{
226 (void)tmo;
227
228 /* This is called only if the state was stable for 300ms - check state
229 * and post appropriate event. */
230 if (card_detect_target())
231 queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
232 else
233 queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
234
235 return 0;
236}
237
238void EXT0(void)
239{
240 static struct timeout sd1_oneshot;
241
242 timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0);
243}
244
245bool sd_removable(IF_MD_NONVOID(int card_no))
246{
247#ifndef HAVE_MULTIDRIVE
248 const int card_no = 0;
249#endif
250 return (card_no == CARD_NUM_SLOT);
251}
252
253bool sd_present(IF_MD_NONVOID(int card_no))
254{
255#ifndef HAVE_MULTIDRIVE
256 const int card_no = 0;
257#endif
258 return (card_info[card_no].initialized &&
259 card_info[card_no].numblocks > 0);
260}
261
262#else
263
264bool card_detect_target(void)
265{
266 return false;
267}
268
269bool sd_removable(IF_MD_NONVOID(int card_no))
270{
271#ifndef HAVE_MULTIDRIVE
272 const int card_no = 0;
273#endif
274 (void)card_no;
275
276 return false;
277}
278
279#endif /* HAVE_HOTSWAP */
280
281
282static void sd_init_device(int card_no)
283{
284 int ret;
285 unsigned long response;
286
287 /* Initialise card data as blank */
288 memset(currcard, 0, sizeof(*currcard));
289
290 /* Switch card mux to card to initialize */
291 sd_card_mux(card_no);
292
293#ifdef HAVE_HOTSWAP
294 /* Check card is inserted */
295 if (card_no == CARD_NUM_SLOT)
296 {
297 if (GPIOB & (1<<26))
298 {
299 ret = -EC_NOCARD;
300 goto card_init_error;
301 }
302
303 /* Card will not power up unless this is done */
304 GPIOC_CLEAR = (1<<24);
305 }
306#endif
307
308 ret = sd_command(SD_GO_IDLE_STATE, 0, NULL, SDICMD_RES_TYPE1);
309
310 if (ret < 0)
311 goto card_init_error;
312
313 /* Use slow clock during identification (24MHz / 60 = 400kHz) */
314 SDICLK = (1<<12) | 59;
315
316 sd_command(SD_SEND_IF_COND, 0x1aa, &response, SDICMD_RES_TYPE3);
317
318 if (!sd_poll_status(SDISTATUS_CMD_PATH_RDY, 100000))
319 goto card_init_error;
320
321 currcard->ocr = 0;
322
323 long start_tick = current_tick;
324
325 while ((currcard->ocr & (1<<31)) == 0
326 && TIME_BEFORE(current_tick, start_tick + HZ))
327 {
328 udelay(100);
329 sd_command(SD_APP_CMD, 0, NULL, SDICMD_RES_TYPE1);
330
331 int arg = 0x100000 | ((response == 0x1aa) ? (1<<30):0);
332 sd_command(SD_APP_OP_COND, arg, &currcard->ocr, SDICMD_RES_TYPE3);
333 }
334
335 if ((currcard->ocr & (1<<31)) == 0)
336 {
337 ret = -EC_POWER_UP;
338 goto card_init_error;
339 }
340
341 ret = sd_command
342 (SD_ALL_SEND_CID, 0, currcard->cid, SDICMD_RES_TYPE2);
343
344 if (ret < 0)
345 goto card_init_error;
346
347 ret = sd_command
348 (SD_SEND_RELATIVE_ADDR, 0, &currcard->rca, SDICMD_RES_TYPE1);
349
350 if (ret < 0)
351 goto card_init_error;
352
353 ret = sd_command
354 (SD_SEND_CSD, currcard->rca, currcard->csd, SDICMD_RES_TYPE2);
355
356 if (ret < 0)
357 goto card_init_error;
358
359 sd_parse_csd(currcard);
360
361 ret = sd_command
362 (SD_SELECT_CARD, currcard->rca, NULL, SDICMD_RES_TYPE1);
363
364 if (ret < 0)
365 goto card_init_error;
366
367 ret = sd_command
368 (SD_APP_CMD, currcard->rca, NULL, SDICMD_RES_TYPE1);
369
370 if (ret < 0)
371 goto card_init_error;
372
373 ret = sd_command /* 4 bit */
374 (SD_SET_BUS_WIDTH, currcard->rca | 2, NULL, SDICMD_RES_TYPE1);
375
376 if (ret < 0)
377 goto card_init_error;
378
379 ret = sd_command
380 (SD_SET_BLOCKLEN, currcard->blocksize, NULL, SDICMD_RES_TYPE1);
381
382 if (ret < 0)
383 goto card_init_error;
384
385 currcard->initialized = 1;
386 return;
387
388 /* Card failed to initialize so disable it */
389card_init_error:
390 currcard->initialized = ret;
391 return;
392}
393
394/* lock must already be acquired */
395static void sd_select_device(int card_no)
396{
397 currcard = &card_info[card_no];
398
399 if (currcard->initialized > 0)
400 {
401 /* This card is already initialized - switch to it */
402 sd_card_mux(card_no);
403 return;
404 }
405
406 if (currcard->initialized == 0)
407 {
408 /* Card needs (re)init */
409 sd_init_device(card_no);
410 }
411}
412
413int sd_read_sectors(IF_MD2(int card_no,) unsigned long start, int incount,
414 void* inbuf)
415{
416#ifndef HAVE_MULTIDRIVE
417 const int card_no = 0;
418#endif
419
420 int ret = 0;
421 bool aligned;
422 unsigned char* buf_end;
423
424 mutex_lock(&sd_mtx);
425 sd_enable(true);
426 led(true);
427
428sd_read_retry:
429 if ((card_no == CARD_NUM_SLOT) && !card_detect_target())
430 {
431 /* no external sd-card inserted */
432 ret = -EC_NOCARD;
433 goto sd_read_error;
434 }
435
436 sd_select_device(card_no);
437
438 if (currcard->initialized < 0)
439 {
440 ret = currcard->initialized;
441 goto sd_read_error;
442 }
443
444 last_disk_activity = current_tick;
445
446 ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_ENTRY);
447
448 if (ret < 0)
449 goto sd_read_error;
450
451 /* Use full SD clock for data transfer (PCK_SDMMC) */
452 SDICLK = (1<<13) | (1<<12); /* bypass divider | enable */
453
454 /* Block count | FIFO count | Block size (2^9) | 4-bit bus */
455 SDIDCTRL = (incount << 13) | (4<<8) | (9<<4) | (1<<2);
456 SDIDCTRL |= (1<<12); /* nReset */
457
458 SDIDCTRL2 = (1<<2); /* multi block, read */
459
460 if (currcard->ocr & (1<<30))
461 ret = sd_command(SD_READ_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
462 else
463 ret = sd_command(SD_READ_MULTIPLE_BLOCK, start * 512, NULL, SDICMD_RES_TYPE1);
464
465 if (ret < 0)
466 goto sd_read_error;
467
468 aligned = (((int)inbuf & 3) == 0);
469
470 buf_end = (unsigned char *)inbuf + incount * currcard->blocksize;
471
472 while (inbuf < (void*)buf_end)
473 {
474 if (!sd_poll_status(SDISTATUS_FIFO_FETCH_REQ, 100000))
475 goto sd_read_error;
476
477 if (aligned)
478 {
479 unsigned int* ptr = (unsigned int*)inbuf;
480 *ptr++ = SDIRDATA;
481 *ptr++ = SDIRDATA;
482 *ptr++ = SDIRDATA;
483 *ptr = SDIRDATA;
484
485 }
486 else
487 {
488 int tmp_buf[4];
489
490 tmp_buf[0] = SDIRDATA;
491 tmp_buf[1] = SDIRDATA;
492 tmp_buf[2] = SDIRDATA;
493 tmp_buf[3] = SDIRDATA;
494
495 memcpy(inbuf, tmp_buf, 16);
496 }
497 inbuf += 16;
498 }
499
500 ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, SDICMD_RES_TYPE1);
501 if (ret < 0)
502 goto sd_read_error;
503
504 ret = sd_wait_for_state(SD_TRAN, EC_TRAN_READ_EXIT);
505 if (ret < 0)
506 goto sd_read_error;
507
508 while (1)
509 {
510 led(false);
511 sd_enable(false);
512 mutex_unlock(&sd_mtx);
513
514 return ret;
515
516sd_read_error:
517 if (sd_status[card_no].retry < sd_status[card_no].retry_max
518 && ret != -EC_NOCARD)
519 {
520 sd_status[card_no].retry++;
521 currcard->initialized = 0;
522 goto sd_read_retry;
523 }
524 }
525}
526
527int sd_write_sectors(IF_MD2(int card_no,) unsigned long start, int count,
528 const void* outbuf)
529{
530/* Write support is not finished yet */
531/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks
532 to improve performance */
533#ifndef HAVE_MULTIDRIVE
534 const int card_no = 0;
535#endif
536 int ret;
537 const unsigned char *buf_end;
538 bool aligned;
539
540 mutex_lock(&sd_mtx);
541 sd_enable(true);
542 led(true);
543
544sd_write_retry:
545 if ((card_no == CARD_NUM_SLOT) && !card_detect_target())
546 {
547 /* no external sd-card inserted */
548 ret = -EC_NOCARD;
549 goto sd_write_error;
550 }
551
552 sd_select_device(card_no);
553
554 if (currcard->initialized < 0)
555 {
556 ret = currcard->initialized;
557 goto sd_write_error;
558 }
559
560 ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_ENTRY);
561
562 if (ret < 0)
563 goto sd_write_error;
564
565 /* Use full SD clock for data transfer (PCK_SDMMC) */
566 SDICLK = (1<<13) | (1<<12); /* bypass divider | enable */
567
568 /* Block count | FIFO count | Block size (2^9) | 4-bit bus */
569 SDIDCTRL = (count<<13) | (4<<8) | (9<<4) | (1<<2);
570 SDIDCTRL |= (1<<12); /* nReset */
571
572 SDIDCTRL2 = (1<<2) | (1<<1); /* multi block, write */
573
574 if (currcard->ocr & (1<<30))
575 ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
576 else
577 ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start * 512, NULL, SDICMD_RES_TYPE1);
578
579 if (ret < 0)
580 goto sd_write_error;
581
582 aligned = (((int)outbuf & 3) == 0);
583
584 buf_end = (unsigned char *)outbuf + count * currcard->blocksize;
585
586 while (outbuf < (void*)buf_end)
587 {
588 if (aligned)
589 {
590 unsigned int* ptr = (unsigned int*)outbuf;
591 SDIWDATA = *ptr++;
592 SDIWDATA = *ptr++;
593 SDIWDATA = *ptr++;
594 SDIWDATA = *ptr;
595 }
596 else
597 {
598 int tmp_buf[4];
599
600 memcpy(tmp_buf, outbuf, 16);
601
602 SDIWDATA = tmp_buf[0];
603 SDIWDATA = tmp_buf[1];
604 SDIWDATA = tmp_buf[2];
605 SDIWDATA = tmp_buf[3];
606 }
607 outbuf += 16;
608
609 /* Wait for the FIFO to empty */
610 if (!sd_poll_status(SDISTATUS_FIFO_LOAD_REQ, 0x80000))
611 {
612 ret = -EC_FIFO_WR_EMPTY;
613 goto sd_write_error;
614 }
615 }
616
617 last_disk_activity = current_tick;
618
619 if (!sd_poll_status(SDISTATUS_MULTIBLOCK_END, 0x80000))
620 {
621 ret = -EC_FIFO_WR_DONE;
622 goto sd_write_error;
623 }
624
625 ret = sd_command(SD_STOP_TRANSMISSION, 0, NULL, SDICMD_RES_TYPE1);
626 if (ret < 0)
627 goto sd_write_error;
628
629 ret = sd_wait_for_state(SD_TRAN, EC_TRAN_WRITE_EXIT);
630 if (ret < 0)
631 goto sd_write_error;
632
633 while (1)
634 {
635 led(false);
636 sd_enable(false);
637 mutex_unlock(&sd_mtx);
638
639 return ret;
640
641sd_write_error:
642 if (sd_status[card_no].retry < sd_status[card_no].retry_max
643 && ret != -EC_NOCARD)
644 {
645 sd_status[card_no].retry++;
646 currcard->initialized = 0;
647 goto sd_write_retry;
648 }
649 }
650}
651
652static void sd_thread(void) __attribute__((noreturn));
653static void sd_thread(void)
654{
655 struct queue_event ev;
656 bool idle_notified = false;
657
658 while (1)
659 {
660 queue_wait_w_tmo(&sd_queue, &ev, HZ);
661
662 switch ( ev.id )
663 {
664#ifdef HAVE_HOTSWAP
665 case SYS_HOTSWAP_INSERTED:
666 case SYS_HOTSWAP_EXTRACTED:
667 fat_lock(); /* lock-out FAT activity first -
668 prevent deadlocking via disk_mount that
669 would cause a reverse-order attempt with
670 another thread */
671 mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
672 into driver that bypass the fat cache */
673
674 /* We now have exclusive control of fat cache and ata */
675
676 /* Release "by force", ensure file descriptors aren't leaked and
677 any busy ones are invalid if mounting */
678 disk_unmount(sd_first_drive + CARD_NUM_SLOT);
679
680 /* Force card init for new card, re-init for re-inserted one or
681 * clear if the last attempt to init failed with an error. */
682 card_info[CARD_NUM_SLOT].initialized = 0;
683 sd_status[CARD_NUM_SLOT].retry = 0;
684
685 if (ev.id == SYS_HOTSWAP_INSERTED)
686 disk_mount(sd_first_drive + CARD_NUM_SLOT);
687
688 queue_broadcast(SYS_FS_CHANGED, 0);
689
690 /* Access is now safe */
691 mutex_unlock(&sd_mtx);
692 fat_unlock();
693 break;
694#endif
695 case SYS_TIMEOUT:
696 if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
697 {
698 idle_notified = false;
699 }
700 else
701 {
702 /* never let a timer wrap confuse us */
703 next_yield = USEC_TIMER;
704
705 if (!idle_notified)
706 {
707 call_storage_idle_notifys(false);
708 idle_notified = true;
709 }
710 }
711 break;
712
713 case SYS_USB_CONNECTED:
714 usb_acknowledge(SYS_USB_CONNECTED_ACK);
715 /* Wait until the USB cable is extracted again */
716 usb_wait_for_disconnect(&sd_queue);
717 break;
718
719 case SYS_USB_DISCONNECTED:
720 usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
721 break;
722 }
723 }
724}
725
726void sd_enable(bool on)
727{
728 if(on)
729 {
730 /* Enable controller & clock */
731 BCLKCTR |= DEV_SDMMC;
732 PCLK_SDMMC = PCK_EN | (CKSEL_PLL0<<24) | 7; /* 192/8 = 24MHz */
733 }
734 else
735 {
736 /* Disable controller & clock */
737 BCLKCTR &= ~DEV_SDMMC;
738 PCLK_SDMMC &= ~PCK_EN;
739 }
740}
741
742int sd_init(void)
743{
744 int ret = 0;
745
746 if (!initialized)
747 mutex_init(&sd_mtx);
748
749 mutex_lock(&sd_mtx);
750
751 led(false);
752
753 if (!initialized)
754 {
755 initialized = true;
756
757 SWRESET |= DEV_SDMMC;
758 SWRESET &= ~DEV_SDMMC;
759
760 /* Configure dual-purpose pins for SD usage */
761 PORTCFG0 &= ~(3<<16);
762 PORTCFG0 |= (1<<16); /* SD_D0 & SD_D1 */
763
764 PORTCFG2 &= ~((3<<2) | (3<<0));
765 PORTCFG2 |= ((1<<2) | (1<<0)); /* SD_D2/D3/CK/CMD */
766
767 /* Configure card detection GPIO as input */
768 GPIOB_DIR &= ~(1<<26);
769
770 /* Configure card power(?) GPIO as output */
771 GPIOC_DIR |= (1<<24);
772
773 queue_init(&sd_queue, true);
774 create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0,
775 sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
776 IF_COP(, CPU));
777
778 sleep(HZ/10);
779
780#ifdef HAVE_HOTSWAP
781 /* Configure interrupts for the card slot */
782 TMODE &= ~EXT0_IRQ_MASK; /* edge-triggered */
783 TMODEA |= EXT0_IRQ_MASK; /* trigger on both edges */
784#endif
785 }
786
787 mutex_unlock(&sd_mtx);
788
789 return ret;
790}
791
792long sd_last_disk_activity(void)
793{
794 return last_disk_activity;
795}
796
797tCardInfo *card_get_info_target(int card_no)
798{
799 return &card_info[card_no];
800}
801
802#ifdef CONFIG_STORAGE_MULTI
803
804int sd_num_drives(int first_drive)
805{
806 /* Store which logical drive number(s) we have been assigned */
807 sd_first_drive = first_drive;
808
809#if defined(HAVE_INTERNAL_SD) && defined(HAVE_HOTSWAP)
810 return 2;
811#else
812 return 1;
813#endif
814}
815
816void sd_sleepnow(void)
817{
818}
819
820bool sd_disk_is_active(void)
821{
822 return false;
823}
824
825int sd_soft_reset(void)
826{
827 return 0;
828}
829
830int sd_spinup_time(void)
831{
832 return 0;
833}
834
835#endif /* CONFIG_STORAGE_MULTI */