1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 Daniel Ankers
11 * Copyright © 2008 Rafaël Carré
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 /* Driver for the ARM PL180 SD/MMC controller inside AS3525 SoC */
25 #include "config.h" /* for HAVE_MULTIVOLUME */
35 #include "pl180.h" /* SD controller */
36 #include "pl081.h" /* DMA controller */
37 #include "dma-target.h" /* DMA request lines */
38 #include "clock-target.h"
41 #include "ata_idle_notify.h"
49 #define MCI_NO_FLAGS (0<<0)
50 #define MCI_RESP (1<<0)
51 #define MCI_LONG_RESP (1<<1)
52 #define MCI_ARG (1<<2)
54 /* ARM PL180 registers */
55 #define MCI_POWER(i) (*(volatile unsigned char *) (pl180_base[i]+0x00))
56 #define MCI_CLOCK(i) (*(volatile unsigned long *) (pl180_base[i]+0x04))
57 #define MCI_ARGUMENT(i) (*(volatile unsigned long *) (pl180_base[i]+0x08))
58 #define MCI_COMMAND(i) (*(volatile unsigned long *) (pl180_base[i]+0x0C))
59 #define MCI_RESPCMD(i) (*(volatile unsigned long *) (pl180_base[i]+0x10))
60 #define MCI_RESP0(i) (*(volatile unsigned long *) (pl180_base[i]+0x14))
61 #define MCI_RESP1(i) (*(volatile unsigned long *) (pl180_base[i]+0x18))
62 #define MCI_RESP2(i) (*(volatile unsigned long *) (pl180_base[i]+0x1C))
63 #define MCI_RESP3(i) (*(volatile unsigned long *) (pl180_base[i]+0x20))
64 #define MCI_DATA_TIMER(i) (*(volatile unsigned long *) (pl180_base[i]+0x24))
65 #define MCI_DATA_LENGTH(i) (*(volatile unsigned short*) (pl180_base[i]+0x28))
66 #define MCI_DATA_CTRL(i) (*(volatile unsigned char *) (pl180_base[i]+0x2C))
67 #define MCI_DATA_CNT(i) (*(volatile unsigned short*) (pl180_base[i]+0x30))
68 #define MCI_STATUS(i) (*(volatile unsigned long *) (pl180_base[i]+0x34))
69 #define MCI_CLEAR(i) (*(volatile unsigned long *) (pl180_base[i]+0x38))
70 #define MCI_MASK0(i) (*(volatile unsigned long *) (pl180_base[i]+0x3C))
71 #define MCI_MASK1(i) (*(volatile unsigned long *) (pl180_base[i]+0x40))
72 #define MCI_SELECT(i) (*(volatile unsigned long *) (pl180_base[i]+0x44))
73 #define MCI_FIFO_CNT(i) (*(volatile unsigned long *) (pl180_base[i]+0x48))
76 (MCI_DATA_CRC_FAIL | MCI_DATA_TIMEOUT | MCI_RX_OVERRUN | MCI_TX_UNDERRUN)
78 #define MCI_FIFO(i) ((unsigned long *) (pl180_base[i]+0x80))
80 #define INTERNAL_AS3525 0 /* embedded SD card */
81 #define SD_SLOT_AS3525 1 /* SD slot if present */
83 static const int pl180_base
[NUM_VOLUMES
] = {
85 #ifdef HAVE_MULTIVOLUME
90 static int sd_init_card(const int drive
);
91 static void init_pl180_controller(const int drive
);
92 /* TODO : BLOCK_SIZE != SECTOR_SIZE ? */
93 #define BLOCK_SIZE 512
94 #define SECTOR_SIZE 512
96 static tSDCardInfo card_info
[NUM_VOLUMES
];
98 /* for compatibility */
99 static long last_disk_activity
= -1;
101 #define MIN_YIELD_PERIOD 5 /* ticks */
102 static long next_yield
= 0;
104 static long sd_stack
[(DEFAULT_STACK_SIZE
*2 + 0x200)/sizeof(long)];
105 static const char sd_thread_name
[] = "ata/sd";
106 static struct mutex sd_mtx SHAREDBSS_ATTR
;
107 static struct event_queue sd_queue
;
109 static bool sd_enabled
= false;
112 static struct wakeup transfer_completion_signal
;
113 static volatile bool retry
;
115 static inline void mci_delay(void) { int i
= 0xffff; while(i
--) ; }
117 static void mci_set_clock_divider(const int drive
, int divider
)
119 int clock
= MCI_CLOCK(drive
);
123 /* use divide logic */
124 clock
&= ~MCI_CLOCK_BYPASS
;
126 /* convert divider to MCI_CLOCK logic */
127 divider
= (divider
/2) - 1;
133 /* bypass dividing logic */
134 clock
|= MCI_CLOCK_BYPASS
;
138 MCI_CLOCK(drive
) = clock
| divider
;
144 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
145 static bool sd1_oneshot_callback(struct timeout
*tmo
)
149 /* This is called only if the state was stable for 300ms - check state
150 * and post appropriate event. */
151 if (card_detect_target())
153 queue_broadcast(SYS_HOTSWAP_INSERTED
, 0);
156 queue_broadcast(SYS_HOTSWAP_EXTRACTED
, 0);
163 static struct timeout sd1_oneshot
;
166 timeout_register(&sd1_oneshot
, sd1_oneshot_callback
, (3*HZ
/10), 0);
173 const int status
= MCI_STATUS(INTERNAL_AS3525
);
175 if(status
& MCI_ERROR
)
178 wakeup_signal(&transfer_completion_signal
);
179 MCI_CLEAR(INTERNAL_AS3525
) = status
;
182 #ifdef HAVE_MULTIVOLUME
185 const int status
= MCI_STATUS(SD_SLOT_AS3525
);
187 if(status
& MCI_ERROR
)
190 wakeup_signal(&transfer_completion_signal
);
191 MCI_CLEAR(SD_SLOT_AS3525
) = status
;
195 static bool send_cmd(const int drive
, const int cmd
, const int arg
,
196 const int flags
, int *response
)
200 while(MCI_STATUS(drive
) & MCI_CMD_ACTIVE
);
202 if(MCI_COMMAND(drive
) & MCI_COMMAND_ENABLE
) /* clears existing command */
204 MCI_COMMAND(drive
) = 0;
208 val
= cmd
| MCI_COMMAND_ENABLE
;
211 val
|= MCI_COMMAND_RESPONSE
;
212 if(flags
& MCI_LONG_RESP
)
213 val
|= MCI_COMMAND_LONG_RESPONSE
;
216 MCI_CLEAR(drive
) = 0x7ff;
218 MCI_ARGUMENT(drive
) = (flags
& MCI_ARG
) ? arg
: 0;
219 MCI_COMMAND(drive
) = val
;
221 while(MCI_STATUS(drive
) & MCI_CMD_ACTIVE
); /* wait for cmd completion */
223 MCI_COMMAND(drive
) = 0;
224 MCI_ARGUMENT(drive
) = ~0;
226 status
= MCI_STATUS(drive
);
227 MCI_CLEAR(drive
) = 0x7ff;
231 if(status
& MCI_CMD_TIMEOUT
)
233 else if(status
& (MCI_CMD_CRC_FAIL
/* FIXME? */ | MCI_CMD_RESP_END
))
234 { /* resp received */
235 if(flags
& MCI_LONG_RESP
)
237 /* store the response in little endian order for the words */
238 response
[0] = MCI_RESP3(drive
);
239 response
[1] = MCI_RESP2(drive
);
240 response
[2] = MCI_RESP1(drive
);
241 response
[3] = MCI_RESP0(drive
);
244 response
[0] = MCI_RESP0(drive
);
248 else if(status
& MCI_CMD_SENT
)
254 static int sd_init_card(const int drive
)
257 unsigned long c_mult
;
259 int max_tries
= 100; /* max acmd41 attemps */
262 if(!send_cmd(drive
, SD_GO_IDLE_STATE
, 0, MCI_NO_FLAGS
, NULL
))
268 if(send_cmd(drive
, SD_SEND_IF_COND
, 0x1AA, MCI_RESP
|MCI_ARG
, &response
))
269 if((response
& 0xFFF) == 0x1AA)
273 /* some MicroSD cards seems to need more delays, so play safe */
279 if( !send_cmd(drive
, SD_APP_CMD
, 0, MCI_RESP
|MCI_ARG
, &response
) ||
280 !(response
& (1<<5)) )
286 if(!send_cmd(drive
, SD_APP_OP_COND
, (sdhc
? 0x40FF8000 : (1<<23)),
287 MCI_RESP
|MCI_ARG
, &card_info
[drive
].ocr
))
290 } while(!(card_info
[drive
].ocr
& (1<<31)) && max_tries
--);
296 if(!send_cmd(drive
, SD_ALL_SEND_CID
, 0, MCI_RESP
|MCI_LONG_RESP
|MCI_ARG
,
297 card_info
[drive
].cid
))
301 if(!send_cmd(drive
, SD_SEND_RELATIVE_ADDR
, 0, MCI_RESP
|MCI_ARG
,
302 &card_info
[drive
].rca
))
306 if(!send_cmd(drive
, SD_SEND_CSD
, card_info
[drive
].rca
,
307 MCI_RESP
|MCI_LONG_RESP
|MCI_ARG
, card_info
[drive
].csd
))
310 /* These calculations come from the Sandisk SD card product manual */
311 if( (card_info
[drive
].csd
[3]>>30) == 0)
313 /* CSD version 1.0 */
314 c_size
= ((card_info
[drive
].csd
[2] & 0x3ff) << 2) + (card_info
[drive
].csd
[1]>>30) + 1;
315 c_mult
= 4 << ((card_info
[drive
].csd
[1] >> 15) & 7);
316 card_info
[drive
].max_read_bl_len
= 1 << ((card_info
[drive
].csd
[2] >> 16) & 15);
317 card_info
[drive
].block_size
= BLOCK_SIZE
; /* Always use 512 byte blocks */
318 card_info
[drive
].numblocks
= c_size
* c_mult
* (card_info
[drive
].max_read_bl_len
/512);
319 card_info
[drive
].capacity
= card_info
[drive
].numblocks
* card_info
[drive
].block_size
;
321 #ifdef HAVE_MULTIVOLUME
322 else if( (card_info
[drive
].csd
[3]>>30) == 1)
324 /* CSD version 2.0 */
325 c_size
= ((card_info
[drive
].csd
[2] & 0x3f) << 16) + (card_info
[drive
].csd
[1]>>16) + 1;
326 card_info
[drive
].max_read_bl_len
= 1 << ((card_info
[drive
].csd
[2] >> 16) & 0xf);
327 card_info
[drive
].block_size
= BLOCK_SIZE
; /* Always use 512 byte blocks */
328 card_info
[drive
].numblocks
= c_size
<< 10;
329 card_info
[drive
].capacity
= card_info
[drive
].numblocks
* card_info
[drive
].block_size
;
333 if(!send_cmd(drive
, SD_SELECT_CARD
, card_info
[drive
].rca
, MCI_ARG
, NULL
))
336 if(!send_cmd(drive
, SD_APP_CMD
, card_info
[drive
].rca
, MCI_ARG
, NULL
))
339 if(!send_cmd(drive
, SD_SET_BUS_WIDTH
, card_info
[drive
].rca
| 2, MCI_ARG
, NULL
))
342 if(!send_cmd(drive
, SD_SET_BLOCKLEN
, card_info
[drive
].block_size
, MCI_ARG
,
346 card_info
[drive
].initialized
= 1;
348 mci_set_clock_divider(drive
, 1); /* full speed */
353 static void sd_thread(void) __attribute__((noreturn
));
354 static void sd_thread(void)
356 struct queue_event ev
;
357 bool idle_notified
= false;
361 queue_wait_w_tmo(&sd_queue
, &ev
, HZ
);
366 case SYS_HOTSWAP_INSERTED
:
367 case SYS_HOTSWAP_EXTRACTED
:
368 fat_lock(); /* lock-out FAT activity first -
369 prevent deadlocking via disk_mount that
370 would cause a reverse-order attempt with
372 mutex_lock(&sd_mtx
); /* lock-out card activity - direct calls
373 into driver that bypass the fat cache */
375 /* We now have exclusive control of fat cache and ata */
377 disk_unmount(SD_SLOT_AS3525
); /* release "by force", ensure file
378 descriptors aren't leaked and any busy
379 ones are invalid if mounting */
381 /* Force card init for new card, re-init for re-inserted one or
382 * clear if the last attempt to init failed with an error. */
383 card_info
[SD_SLOT_AS3525
].initialized
= 0;
385 if (ev
.id
== SYS_HOTSWAP_INSERTED
)
388 init_pl180_controller(SD_SLOT_AS3525
);
389 sd_init_card(SD_SLOT_AS3525
);
390 disk_mount(SD_SLOT_AS3525
);
393 queue_broadcast(SYS_FS_CHANGED
, 0);
395 /* Access is now safe */
396 mutex_unlock(&sd_mtx
);
402 if (TIME_BEFORE(current_tick
, last_disk_activity
+(3*HZ
)))
404 idle_notified
= false;
408 /* never let a timer wrap confuse us */
409 next_yield
= current_tick
;
413 call_storage_idle_notifys(false);
414 idle_notified
= true;
419 case SYS_USB_CONNECTED
:
420 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
421 /* Wait until the USB cable is extracted again */
422 usb_wait_for_disconnect(&sd_queue
);
425 case SYS_USB_DISCONNECTED
:
426 usb_acknowledge(SYS_USB_DISCONNECTED_ACK
);
433 static void init_pl180_controller(const int drive
)
435 MCI_COMMAND(drive
) = MCI_DATA_CTRL(drive
) = 0;
436 MCI_CLEAR(drive
) = 0x7ff;
438 MCI_MASK0(drive
) = MCI_MASK1(drive
) = MCI_ERROR
| MCI_DATA_END
;
440 #ifdef HAVE_MULTIVOLUME
442 (drive
== INTERNAL_AS3525
) ? INTERRUPT_NAND
: INTERRUPT_MCI0
;
444 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
445 /* setup isr for microsd monitoring */
446 VIC_INT_ENABLE
|= (INTERRUPT_GPIOA
);
447 /* clear previous irq */
449 /* enable edge detecting */
451 /* detect both raising and falling edges */
457 VIC_INT_ENABLE
|= INTERRUPT_NAND
;
460 MCI_POWER(drive
) = MCI_POWER_UP
|(10 /*voltage*/ << 2); /* use OF voltage */
463 MCI_POWER(drive
) |= MCI_POWER_ON
;
466 MCI_SELECT(drive
) = 0;
468 MCI_CLOCK(drive
) = MCI_CLOCK_ENABLE
;
469 MCI_CLOCK(drive
) &= ~MCI_CLOCK_POWERSAVE
;
471 /* set MCLK divider */
472 mci_set_clock_divider(drive
,
473 CLK_DIV(AS3525_PCLK_FREQ
, AS3525_SD_IDENT_FREQ
));
479 CGU_IDE
= (1<<7) /* AHB interface enable */ |
480 (1<<6) /* interface enable */ |
481 ((CLK_DIV(AS3525_PLLA_FREQ
, AS3525_IDE_FREQ
) - 1) << 2) |
482 1; /* clock source = PLLA */
485 CGU_PERI
|= CGU_NAF_CLOCK_ENABLE
;
486 #ifdef HAVE_MULTIVOLUME
487 CGU_PERI
|= CGU_MCI_CLOCK_ENABLE
;
488 CCU_IO
&= ~(1<<3); /* bits 3:2 = 01, xpd is SD interface */
492 wakeup_init(&transfer_completion_signal
);
494 init_pl180_controller(INTERNAL_AS3525
);
495 ret
= sd_init_card(INTERNAL_AS3525
);
498 #ifdef HAVE_MULTIVOLUME
499 init_pl180_controller(SD_SLOT_AS3525
);
505 queue_init(&sd_queue
, true);
506 create_thread(sd_thread
, sd_stack
, sizeof(sd_stack
), 0,
507 sd_thread_name
IF_PRIO(, PRIORITY_USER_INTERFACE
) IF_COP(, CPU
));
516 #ifdef STORAGE_GET_INFO
517 void sd_get_info(IF_MV2(int drive
,) struct storage_info
*info
)
519 #ifndef HAVE_MULTIVOLUME
522 info
->sector_size
=card_info
[drive
].block_size
;
523 info
->num_sectors
=card_info
[drive
].numblocks
;
524 info
->vendor
="Rockbox";
525 info
->product
= (drive
== 0) ? "Internal Storage" : "SD Card Slot";
526 info
->revision
="0.00";
531 bool sd_removable(IF_MV_NONVOID(int drive
))
533 #ifndef HAVE_MULTIVOLUME
539 bool sd_present(IF_MV_NONVOID(int drive
))
541 #ifndef HAVE_MULTIVOLUME
544 return (card_info
[drive
].initialized
&& card_info
[drive
].numblocks
> 0);
548 static int sd_wait_for_state(const int drive
, unsigned int state
)
550 unsigned int response
= 0;
551 unsigned int timeout
= 100; /* ticks */
552 long t
= current_tick
;
558 if(!send_cmd(drive
, SD_SEND_STATUS
, card_info
[drive
].rca
,
559 MCI_RESP
|MCI_ARG
, &response
))
562 if (((response
>> 9) & 0xf) == state
)
565 if(TIME_AFTER(current_tick
, t
+ timeout
))
568 if (TIME_AFTER((tick
= current_tick
), next_yield
))
571 timeout
+= current_tick
- tick
;
572 next_yield
= tick
+ MIN_YIELD_PERIOD
;
577 static int sd_transfer_sectors(IF_MV2(int drive
,) unsigned long start
,
578 int count
, void* buf
, bool write
)
580 #ifndef HAVE_MULTIVOLUME
585 /* skip SanDisk OF */
586 if (drive
== INTERNAL_AS3525
)
587 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
598 if (card_info
[drive
].initialized
<= 0)
600 ret
= sd_init_card(drive
);
601 if (!(card_info
[drive
].initialized
))
603 panicf("card not initialised (%d)", ret
);
604 goto sd_transfer_error
;
608 last_disk_activity
= current_tick
;
609 ret
= sd_wait_for_state(drive
, SD_TRAN
);
612 panicf("wait for state failed on drive %d", drive
);
613 goto sd_transfer_error
;
620 /* Interrupt handler might set this to true during transfer */
622 /* 128 * 512 = 2^16, and doesn't fit in the 16 bits of DATA_LENGTH
623 * register, so we have to transfer maximum 127 sectors at a time. */
624 unsigned int transfer
= (count
>= 128) ? 127 : count
; /* sectors */
626 write
? SD_WRITE_MULTIPLE_BLOCK
: SD_READ_MULTIPLE_BLOCK
;
628 if(card_info
[drive
].ocr
& (1<<30) ) /* SDHC */
629 ret
= send_cmd(drive
, cmd
, start
, MCI_ARG
, NULL
);
631 ret
= send_cmd(drive
, cmd
, start
* BLOCK_SIZE
,
635 panicf("transfer multiple blocks failed (%d)", ret
);
638 dma_enable_channel(0, buf
, MCI_FIFO(drive
),
639 (drive
== INTERNAL_AS3525
) ? DMA_PERI_SD
: DMA_PERI_SD_SLOT
,
640 DMAC_FLOWCTRL_PERI_MEM_TO_PERI
, true, false, 0, DMA_S8
, NULL
);
642 dma_enable_channel(0, MCI_FIFO(drive
), buf
,
643 (drive
== INTERNAL_AS3525
) ? DMA_PERI_SD
: DMA_PERI_SD_SLOT
,
644 DMAC_FLOWCTRL_PERI_PERI_TO_MEM
, false, true, 0, DMA_S8
, NULL
);
646 MCI_DATA_TIMER(drive
) = 0x1000000; /* FIXME: arbitrary */
647 MCI_DATA_LENGTH(drive
) = transfer
* card_info
[drive
].block_size
;
648 MCI_DATA_CTRL(drive
) = (1<<0) /* enable */ |
649 (!write
<<1) /* transfer direction */ |
651 (9<<4) /* 2^9 = 512 */ ;
654 wakeup_wait(&transfer_completion_signal
, TIMEOUT_BLOCK
);
657 buf
+= transfer
* SECTOR_SIZE
;
662 last_disk_activity
= current_tick
;
664 if(!send_cmd(drive
, SD_STOP_TRANSMISSION
, 0, MCI_NO_FLAGS
, NULL
))
667 panicf("STOP TRANSMISSION failed");
668 goto sd_transfer_error
;
671 ret
= sd_wait_for_state(drive
, SD_TRAN
);
674 panicf(" wait for state TRAN failed (%d)", ret
);
675 goto sd_transfer_error
;
684 mutex_unlock(&sd_mtx
);
688 panicf("transfer error : %d",ret
);
689 card_info
[drive
].initialized
= 0;
693 int sd_read_sectors(IF_MV2(int drive
,) unsigned long start
, int count
,
696 return sd_transfer_sectors(IF_MV2(drive
,) start
, count
, buf
, false);
699 int sd_write_sectors(IF_MV2(int drive
,) unsigned long start
, int count
,
703 #ifdef BOOTLOADER /* we don't need write support in bootloader */
704 #ifdef HAVE_MULTIVOLUME
712 return sd_transfer_sectors(IF_MV2(drive
,) start
, count
, (void*)buf
, true);
725 void sd_spindown(int seconds
)
730 long sd_last_disk_activity(void)
732 return last_disk_activity
;
735 void sd_enable(bool on
)
737 if (sd_enabled
== on
)
738 return; /* nothing to do */
741 CGU_PERI
|= CGU_NAF_CLOCK_ENABLE
;
742 #ifdef HAVE_MULTIVOLUME
743 CGU_PERI
|= CGU_MCI_CLOCK_ENABLE
;
744 /* Needed for buttonlight and MicroSD to work at the same time */
745 /* Turn ROD control on, as the OF does */
746 SD_MCI_POWER
|= (1<<7);
749 CGU_IDE
|= (1<<7) /* AHB interface enable */ |
750 (1<<6) /* interface enable */;
755 CGU_PERI
&= ~CGU_NAF_CLOCK_ENABLE
;
756 #ifdef HAVE_MULTIVOLUME
757 CGU_PERI
&= ~CGU_MCI_CLOCK_ENABLE
;
758 /* Needed for buttonlight and MicroSD to work at the same time */
759 /* Turn ROD control off, as the OF does */
760 SD_MCI_POWER
&= ~(1<<7);
763 CGU_IDE
&= ~((1<<7)|(1<<6));
768 /* move the sd-card info to mmc struct */
769 tCardInfo
*card_get_info_target(int card_no
)
772 static tCardInfo card
;
773 static const char mantissa
[] = { /* *10 */
774 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
775 static const int exponent
[] = { /* use varies */
776 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 };
778 card
.initialized
= card_info
[card_no
].initialized
;
779 card
.ocr
= card_info
[card_no
].ocr
;
780 for(i
=0; i
<4; i
++) card
.csd
[i
] = card_info
[card_no
].csd
[i
];
781 for(i
=0; i
<4; i
++) card
.cid
[i
] = card_info
[card_no
].cid
[i
];
782 card
.numblocks
= card_info
[card_no
].numblocks
;
783 card
.blocksize
= card_info
[card_no
].block_size
;
784 temp
= card_extract_bits(card
.csd
, 29, 3);
785 card
.speed
= mantissa
[card_extract_bits(card
.csd
, 25, 4)]
786 * exponent
[temp
> 2 ? 7 : temp
+ 4];
787 card
.nsac
= 100 * card_extract_bits(card
.csd
, 16, 8);
788 temp
= card_extract_bits(card
.csd
, 13, 3);
789 card
.tsac
= mantissa
[card_extract_bits(card
.csd
, 9, 4)]
790 * exponent
[temp
] / 10;
791 card
.cid
[0] = htobe32(card
.cid
[0]); /* ascii chars here */
792 card
.cid
[1] = htobe32(card
.cid
[1]); /* ascii chars here */
793 temp
= *((char*)card
.cid
+13); /* adjust year<=>month, 1997 <=> 2000 */
794 *((char*)card
.cid
+13) = (unsigned char)((temp
>> 4) | (temp
<< 4)) + 3;
799 bool card_detect_target(void)
802 /* TODO: add e200/c200 */
803 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
804 return !(GPIOA_PIN(2));
811 void card_enable_monitoring_target(bool on
)
815 /* add e200v2/c200v2 here */
816 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
823 #if defined(SANSA_E200V2) || defined(SANSA_FUZE)
831 #endif /* BOOTLOADER */