5 Hardware routines for reading and writing to the Wii's internal
9 Michael Wiedenbauer (shagkur)
10 Dave Murphy (WinterMute)
11 Sven Peter <svpe@gmx.net>
13 Redistribution and use in source and binary forms, with or without modification,
14 are permitted provided that the following conditions are met:
16 1. Redistributions of source code must retain the above copyright notice,
17 this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright notice,
19 this list of conditions and the following disclaimer in the documentation and/or
20 other materials provided with the distribution.
21 3. The name of the author may not be used to endorse or promote products derived
22 from this software without specific prior written permission.
24 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
26 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 #include <ogc/disc_io.h>
44 #include <sdcard/wiisd_io.h>
47 #include <processor.h>
49 #define SDIO_HEAPSIZE 0x400
51 #define PAGE_SIZE512 512
53 #define SDIOHCR_RESPONSE 0x10
54 #define SDIOHCR_HOSTCONTROL 0x28
55 #define SDIOHCR_POWERCONTROL 0x29
56 #define SDIOHCR_CLOCKCONTROL 0x2c
57 #define SDIOHCR_TIMEOUTCONTROL 0x2e
58 #define SDIOHCR_SOFTWARERESET 0x2f
60 #define SDIOHCR_HOSTCONTROL_4BIT 0x02
62 #define SDIO_DEFAULT_TIMEOUT 0xe
64 #define IOCTL_SDIO_WRITEHCREG 0x01
65 #define IOCTL_SDIO_READHCREG 0x02
66 #define IOCTL_SDIO_READCREG 0x03
67 #define IOCTL_SDIO_RESETCARD 0x04
68 #define IOCTL_SDIO_WRITECREG 0x05
69 #define IOCTL_SDIO_SETCLK 0x06
70 #define IOCTL_SDIO_SENDCMD 0x07
71 #define IOCTL_SDIO_SETBUSWIDTH 0x08
72 #define IOCTL_SDIO_READMCREG 0x09
73 #define IOCTL_SDIO_WRITEMCREG 0x0A
74 #define IOCTL_SDIO_GETSTATUS 0x0B
75 #define IOCTL_SDIO_GETOCR 0x0C
76 #define IOCTL_SDIO_READDATA 0x0D
77 #define IOCTL_SDIO_WRITEDATA 0x0E
79 #define SDIOCMD_TYPE_BC 1
80 #define SDIOCMD_TYPE_BCR 2
81 #define SDIOCMD_TYPE_AC 3
82 #define SDIOCMD_TYPE_ADTC 4
84 #define SDIO_RESPONSE_NONE 0
85 #define SDIO_RESPONSE_R1 1
86 #define SDIO_RESPONSE_R1B 2
87 #define SDIO_RESPOSNE_R2 3
88 #define SDIO_RESPONSE_R3 4
89 #define SDIO_RESPONSE_R4 5
90 #define SDIO_RESPONSE_R5 6
91 #define SDIO_RESPONSE_R6 7
93 #define SDIO_CMD_GOIDLE 0x00
94 #define SDIO_CMD_ALL_SENDCID 0x02
95 #define SDIO_CMD_SENDRCA 0x03
96 #define SDIO_CMD_SELECT 0x07
97 #define SDIO_CMD_DESELECT 0x07
98 #define SDIO_CMD_SENDIFCOND 0x08
99 #define SDIO_CMD_SENDCSD 0x09
100 #define SDIO_CMD_SENDCID 0x0A
101 #define SDIO_CMD_SENDSTATUS 0x0D
102 #define SDIO_CMD_SETBLOCKLEN 0x10
103 #define SDIO_CMD_READBLOCK 0x11
104 #define SDIO_CMD_READMULTIBLOCK 0x12
105 #define SDIO_CMD_WRITEBLOCK 0x18
106 #define SDIO_CMD_WRITEMULTIBLOCK 0x19
107 #define SDIO_CMD_APPCMD 0x37
109 #define SDIO_ACMD_SETBUSWIDTH 0x06
110 #define SDIO_ACMD_SENDSCR 0x33
111 #define SDIO_ACMD_SENDOPCOND 0x29
113 #define SDIO_STATUS_CARD_INSERTED 0x1
114 #define SDIO_STATUS_CARD_INITIALIZED 0x10000
115 #define SDIO_STATUS_CARD_SDHC 0x100000
117 #define READ_BL_LEN ((u8)(__sd0_csd[5]&0x0f))
118 #define WRITE_BL_LEN ((u8)(((__sd0_csd[12]&0x03)<<2)|((__sd0_csd[13]>>6)&0x03)))
141 static s32 __sd0_fd
= -1;
142 static u16 __sd0_rca
= 0;
143 static s32 __sd0_initialized
= 0;
144 static s32 __sd0_sdhc
= 0;
145 static u8 __sd0_csd
[16];
146 static u8 __sd0_cid
[16];
148 static s32 __sdio_initialized
= 0;
150 static char _sd0_fs
[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/slot0";
152 static s32
__sdio_sendcommand(u32 cmd
,u32 cmd_type
,u32 rsp_type
,u32 arg
,u32 blk_cnt
,u32 blk_size
,void *buffer
,void *reply
,u32 rlen
)
155 STACK_ALIGN(ioctlv
,iovec
,3,32);
156 STACK_ALIGN(struct _sdiorequest
,request
,1,32);
157 STACK_ALIGN(struct _sdioresponse
,response
,1,32);
160 request
->cmd_type
= cmd_type
;
161 request
->rsp_type
= rsp_type
;
163 request
->blk_cnt
= blk_cnt
;
164 request
->blk_size
= blk_size
;
165 request
->dma_addr
= buffer
;
166 request
->isdma
= ((buffer
!=NULL
)?1:0);
169 if(request
->isdma
|| __sd0_sdhc
== 1) {
170 iovec
[0].data
= request
;
171 iovec
[0].len
= sizeof(struct _sdiorequest
);
172 iovec
[1].data
= buffer
;
173 iovec
[1].len
= (blk_size
*blk_cnt
);
174 iovec
[2].data
= response
;
175 iovec
[2].len
= sizeof(struct _sdioresponse
);
176 ret
= IOS_Ioctlv(__sd0_fd
,IOCTL_SDIO_SENDCMD
,2,1,iovec
);
178 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_SENDCMD
,request
,sizeof(struct _sdiorequest
),response
,sizeof(struct _sdioresponse
));
180 if(reply
&& !(rlen
>16)) memcpy(reply
,response
,rlen
);
182 // printf(" cmd= %08x\n", cmd);
187 static s32
__sdio_setclock(u32 set
)
190 STACK_ALIGN(u32
,clock
,1,32);
193 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_SETCLK
,clock
,sizeof(u32
),NULL
,0);
197 static s32
__sdio_getstatus()
200 STACK_ALIGN(u32
,status
,1,32);
202 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_GETSTATUS
,NULL
,0,status
,sizeof(u32
));
203 if(ret
<0) return ret
;
208 static s32
__sdio_resetcard()
211 STACK_ALIGN(u32
,status
,1,32);
214 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_RESETCARD
,NULL
,0,status
,sizeof(u32
));
215 if(ret
<0) return ret
;
217 __sd0_rca
= (u16
)(*status
>>16);
218 return (*status
&0xffff);
221 static s32
__sdio_gethcr(u8 reg
, u8 size
, u32
*val
)
224 STACK_ALIGN(u32
,hcr_value
,1,32);
225 STACK_ALIGN(u32
,hcr_query
,6,32);
227 if(val
==NULL
) return IPC_EINVAL
;
237 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_READHCREG
,(void*)hcr_query
,24,hcr_value
,sizeof(u32
));
244 static s32
__sdio_sethcr(u8 reg
, u8 size
, u32 data
)
247 STACK_ALIGN(u32
,hcr_query
,6,32);
255 ret
= IOS_Ioctl(__sd0_fd
,IOCTL_SDIO_WRITEHCREG
,(void*)hcr_query
,24,NULL
,0);
261 static s32
__sdio_waithcr(u8 reg
, u8 size
, u8 unset
, u32 mask
)
269 ret
= __sdio_gethcr(reg
, size
, &val
);
270 if(ret
< 0) return ret
;
271 if((unset
&& !(val
& mask
)) || (!unset
&& (val
& mask
))) return 0;
278 static s32
__sdio_setbuswidth(u32 bus_width
)
283 ret
= __sdio_gethcr(SDIOHCR_HOSTCONTROL
, 1, &hc_reg
);
284 if(ret
<0) return ret
;
287 hc_reg
&= ~SDIOHCR_HOSTCONTROL_4BIT
;
288 if(bus_width
==4) hc_reg
|= SDIOHCR_HOSTCONTROL_4BIT
;
290 return __sdio_sethcr(SDIOHCR_HOSTCONTROL
, 1, hc_reg
);
293 static s32
__sd0_getstatus()
298 ret
= __sdio_sendcommand(SDIO_CMD_SENDSTATUS
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,(__sd0_rca
<<16),0,0,NULL
,&status
,sizeof(u32
));
299 if(ret
<0) return ret
;
304 static s32
__sd0_getrca()
309 ret
= __sdio_sendcommand(SDIO_CMD_SENDRCA
,0,SDIO_RESPONSE_R5
,0,0,0,NULL
,&rca
,sizeof(rca
));
310 if(ret
<0) return ret
;
312 __sd0_rca
= (u16
)(rca
>>16);
316 static s32
__sd0_select()
320 ret
= __sdio_sendcommand(SDIO_CMD_SELECT
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1B
,(__sd0_rca
<<16),0,0,NULL
,NULL
,0);
325 static s32
__sd0_deselect()
329 ret
= __sdio_sendcommand(SDIO_CMD_DESELECT
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1B
,0,0,0,NULL
,NULL
,0);
334 static s32
__sd0_setblocklength(u32 blk_len
)
338 ret
= __sdio_sendcommand(SDIO_CMD_SETBLOCKLEN
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,blk_len
,0,0,NULL
,NULL
,0);
343 static s32
__sd0_setbuswidth(u32 bus_width
)
349 if(bus_width
==4) val
= 0x0002;
351 ret
= __sdio_sendcommand(SDIO_CMD_APPCMD
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,(__sd0_rca
<<16),0,0,NULL
,NULL
,0);
352 if(ret
<0) return ret
;
354 ret
= __sdio_sendcommand(SDIO_ACMD_SETBUSWIDTH
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,val
,0,0,NULL
,NULL
,0);
359 static s32
__sd0_getcsd()
363 ret
= __sdio_sendcommand(SDIO_CMD_SENDCSD
,SDIOCMD_TYPE_AC
,SDIO_RESPOSNE_R2
,(__sd0_rca
<<16),0,0,NULL
,__sd0_csd
,16);
368 static s32
__sd0_getcid()
372 ret
= __sdio_sendcommand(SDIO_CMD_ALL_SENDCID
,0,SDIO_RESPOSNE_R2
,(__sd0_rca
<<16),0,0,NULL
,__sd0_cid
,16);
378 static bool __sd0_initio()
383 struct _sdioresponse resp
;
386 status
= __sdio_getstatus();
388 if(!(status
& SDIO_STATUS_CARD_INSERTED
))
391 if(!(status
& SDIO_STATUS_CARD_INITIALIZED
))
393 // IOS doesn't like this card, so we need to convice it to accept it.
395 // reopen the handle which makes IOS clean stuff up
397 __sd0_fd
= IOS_Open(_sd0_fs
,1);
399 // reset the host controller
400 if(__sdio_sethcr(SDIOHCR_SOFTWARERESET
, 1, 7) < 0) goto fail
;
401 if(__sdio_waithcr(SDIOHCR_SOFTWARERESET
, 1, 1, 7) < 0) goto fail
;
403 // initialize interrupts (sd_reset_card does this on success)
404 __sdio_sethcr(0x34, 4, 0x13f00c3);
405 __sdio_sethcr(0x38, 4, 0x13f00c3);
409 ret
= __sdio_sethcr(SDIOHCR_POWERCONTROL
, 1, 0xe);
410 if(ret
< 0) goto fail
;
411 ret
= __sdio_sethcr(SDIOHCR_POWERCONTROL
, 1, 0xf);
412 if(ret
< 0) goto fail
;
414 // enable internal clock, wait until it gets stable and enable sd clock
415 ret
= __sdio_sethcr(SDIOHCR_CLOCKCONTROL
, 2, 0);
416 if(ret
< 0) goto fail
;
417 ret
= __sdio_sethcr(SDIOHCR_CLOCKCONTROL
, 2, 0x101);
418 if(ret
< 0) goto fail
;
419 ret
= __sdio_waithcr(SDIOHCR_CLOCKCONTROL
, 2, 0, 2);
420 if(ret
< 0) goto fail
;
421 ret
= __sdio_sethcr(SDIOHCR_CLOCKCONTROL
, 2, 0x107);
422 if(ret
< 0) goto fail
;
425 ret
= __sdio_sethcr(SDIOHCR_TIMEOUTCONTROL
, 1, SDIO_DEFAULT_TIMEOUT
);
426 if(ret
< 0) goto fail
;
428 // standard SDHC initialization process
429 ret
= __sdio_sendcommand(SDIO_CMD_GOIDLE
, 0, 0, 0, 0, 0, NULL
, NULL
, 0);
430 if(ret
< 0) goto fail
;
431 ret
= __sdio_sendcommand(SDIO_CMD_SENDIFCOND
, 0, SDIO_RESPONSE_R6
, 0x1aa, 0, 0, NULL
, &resp
, sizeof(resp
));
432 if(ret
< 0) goto fail
;
433 if((resp
.rsp_fields
[0] & 0xff) != 0xaa) goto fail
;
438 ret
= __sdio_sendcommand(SDIO_CMD_APPCMD
, SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,0,0,0,NULL
,NULL
,0);
439 if(ret
< 0) goto fail
;
440 ret
= __sdio_sendcommand(SDIO_ACMD_SENDOPCOND
, 0, SDIO_RESPONSE_R3
, 0x40300000, 0, 0, NULL
, &resp
, sizeof(resp
));
441 if(ret
< 0) goto fail
;
442 if(resp
.rsp_fields
[0] & (1 << 31)) break;
446 if(tries
< 0) goto fail
;
448 // FIXME: SDv2 cards which are not high-capacity won't work :/
449 if(resp
.rsp_fields
[0] & (1 << 30))
454 ret
= __sd0_getcid();
455 if(ret
< 0) goto fail
;
456 ret
= __sd0_getrca();
457 if(ret
< 0) goto fail
;
459 else if(status
&SDIO_STATUS_CARD_SDHC
)
464 ret
= __sdio_setbuswidth(4);
465 if(ret
<0) return false;
467 ret
= __sdio_setclock(1);
468 if(ret
<0) return false;
470 ret
= __sd0_select();
471 if(ret
<0) return false;
473 ret
= __sd0_setblocklength(PAGE_SIZE512
);
475 ret
= __sd0_deselect();
479 ret
= __sd0_setbuswidth(4);
481 ret
= __sd0_deselect();
486 __sd0_initialized
= 1;
490 __sdio_sethcr(SDIOHCR_SOFTWARERESET
, 1, 7);
491 __sdio_waithcr(SDIOHCR_SOFTWARERESET
, 1, 1, 7);
493 __sd0_fd
= IOS_Open(_sd0_fs
,1);
497 bool sdio_Deinitialize()
503 __sdio_initialized
= 0;
509 if(__sdio_initialized
==1) return true;
512 hId
= iosCreateHeap(SDIO_HEAPSIZE
);
513 if(hId
<0) return false;
516 __sd0_fd
= IOS_Open(_sd0_fs
,1);
523 if(__sd0_initio()==false) {
527 __sdio_initialized
= 1;
535 if(__sd0_initialized
==0) return false;
539 __sd0_initialized
= 0;
543 bool sdio_ReadSectors(sec_t sector
, sec_t numSectors
,void* buffer
)
549 if(buffer
==NULL
) return false;
551 ret
= __sd0_select();
552 if(ret
<0) return false;
554 if((u32
)buffer
& 0x1F) {
556 rbuf
= iosAlloc(hId
,PAGE_SIZE512
);
563 while(numSectors
>0) {
564 if(__sd0_sdhc
== 0) blk_off
= (sector
*PAGE_SIZE512
);
565 else blk_off
= sector
;
566 ret
= __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,blk_off
,1,PAGE_SIZE512
,rbuf
,NULL
,0);
568 memcpy(ptr
,rbuf
,PAGE_SIZE512
);
577 if(__sd0_sdhc
== 0) sector
*= PAGE_SIZE512
;
578 ret
= __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,sector
,numSectors
,PAGE_SIZE512
,buffer
,NULL
,0);
586 bool sdio_WriteSectors(sec_t sector
, sec_t numSectors
,const void* buffer
)
592 if(buffer
==NULL
) return false;
594 ret
= __sd0_select();
595 if(ret
<0) return false;
597 if((u32
)buffer
& 0x1F) {
598 wbuf
= iosAlloc(hId
,PAGE_SIZE512
);
605 while(numSectors
>0) {
606 if(__sd0_sdhc
== 0) blk_off
= (sector
*PAGE_SIZE512
);
607 else blk_off
= sector
;
608 memcpy(wbuf
,ptr
,PAGE_SIZE512
);
609 ret
= __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,blk_off
,1,PAGE_SIZE512
,wbuf
,NULL
,0);
619 if(__sd0_sdhc
== 0) sector
*= PAGE_SIZE512
;
620 ret
= __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK
,SDIOCMD_TYPE_AC
,SDIO_RESPONSE_R1
,sector
,numSectors
,PAGE_SIZE512
,(char *)buffer
,NULL
,0);
628 bool sdio_ClearStatus()
633 bool sdio_IsInserted()
635 return ((__sdio_getstatus() & SDIO_STATUS_CARD_INSERTED
) ==
636 SDIO_STATUS_CARD_INSERTED
);
639 bool sdio_IsInitialized()
641 return ((__sdio_getstatus() & SDIO_STATUS_CARD_INITIALIZED
) ==
642 SDIO_STATUS_CARD_INITIALIZED
);
645 const DISC_INTERFACE __io_wiisd
= {
647 FEATURE_MEDIUM_CANREAD
| FEATURE_MEDIUM_CANWRITE
| FEATURE_WII_SD
,
648 (FN_MEDIUM_STARTUP
)&sdio_Startup
,
649 (FN_MEDIUM_ISINSERTED
)&sdio_IsInserted
,
650 (FN_MEDIUM_READSECTORS
)&sdio_ReadSectors
,
651 (FN_MEDIUM_WRITESECTORS
)&sdio_WriteSectors
,
652 (FN_MEDIUM_CLEARSTATUS
)&sdio_ClearStatus
,
653 (FN_MEDIUM_SHUTDOWN
)&sdio_Shutdown