]> cloudbase.mooo.com Git - avrcpm.git/blame - avr/mmc.asm
* Set macro I2C=0 as default.
[avrcpm.git] / avr / mmc.asm
CommitLineData
9c15f366
L
1; MMC/SD-Card routines
2;
3; Copyright (C) 2010 Leo C.
4;
5; This file is part of avrcpm.
6;
7; avrcpm is free software: you can redistribute it and/or modify it
8; under the terms of the GNU General Public License as published by
9; the Free Software Foundation, either version 3 of the License, or
10; (at your option) any later version.
11;
12; avrcpm is distributed in the hope that it will be useful,
13; but WITHOUT ANY WARRANTY; without even the implied warranty of
14; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15; GNU General Public License for more details.
16;
17; You should have received a copy of the GNU General Public License
18; along with avrcpm. If not, see <http://www.gnu.org/licenses/>.
19;
20; $Id$
21;
22
23/* Definitions for MMC/SDC command */
24#define CMD0 (0) /* GO_IDLE_STATE */
25#define CMD1 (1) /* SEND_OP_COND (MMC) */
26#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
27#define CMD8 (8) /* SEND_IF_COND */
28#define CMD9 (9) /* SEND_CSD */
29#define CMD10 (10) /* SEND_CID */
30#define CMD12 (12) /* STOP_TRANSMISSION */
31#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
32#define CMD16 (16) /* SET_BLOCKLEN */
33#define CMD17 (17) /* READ_SINGLE_BLOCK */
34#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
35#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
36#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
37#define CMD24 (24) /* WRITE_BLOCK */
38#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
39#define CMD55 (55) /* APP_CMD */
40#define CMD58 (58) /* READ_OCR */
41
42/* Disk Status Bits (DSTATUS) */
43#define MMCST_NOINIT 0x01 /* Drive not initialized */
44#define MMCST_NODISK 0x02 /* No medium in the drive */
45#define MMCST_PROTECT 0x04 /* Write protected */
46
47/* Card type flags (CardType) */
48#define CT_MMC 0x01 /* MMC ver 3 */
49#define CT_SD1 0x02 /* SD ver 1 */
50#define CT_SD2 0x04 /* SD ver 2 */
51#define CT_SDC (CT_SD1|CT_SD2) /* SD */
52#define CT_BLOCK 0x08 /* Block addressing */
53
54#define RES_OK 0 /* 0: Successful */
55#define RES_ERROR 1 /* 1: R/W Error */
56#define RES_WRPRT 2 /* 2: Write Protected */
57#define RES_NOTRDY 3 /* 3: Not Ready */
58#define RES_PARERR 4 /* 4: Invalid Parameter */
59
4160d270
L
60
61#define SPI_MODE_0 (0<<CPOL)|(0<<CPHA)
62#define SPI_MODE_1 (0<<CPOL)|(1<<CPHA)
63#define SPI_MODE_2 (1<<CPOL)|(0<<CPHA)
64#define SPI_MODE_3 (1<<CPOL)|(1<<CPHA)
65#define SPI_MODE SPI_MODE_0
66
9c15f366
L
67;------------------------------------------------
68;
69.macro spi_clkslow
70.if MMC_DEBUG > 1
71 printstring "SPI_CLK_SLOW "
72.endif
4160d270 73 ldi temp,SPI_MODE|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0) ;clk/128
9c15f366
L
74 out SPCR,temp
75 out SPSR,_0
76.endm
77
78;------------------------------------------------
79;
80.macro spi_clkfast
81.if MMC_DEBUG > 1
82 printstring "SPI_CLK_FAST "
83.endif
4160d270 84 ldi temp,SPI_MODE|(1<<SPE)|(1<<MSTR) ;clk/4
9c15f366
L
85 out SPCR,temp
86#if MMC_SPI2X
87 ldi temp,(1<<SPI2X)
88 out SPSR,temp
89#else
90 out SPSR,_0
91#endif
92.endm
93
94;------------------------------------------------
95;
96.macro spi_disable
97.if MMC_DEBUG > 1
98 printstring "SPI_DISABLE "
99.endif
100 out SPCR,_0
101.endm
102
103
104;------------------------------------------------
105;
106.macro spi_waitm
107 .set spiwl_ = PC
108 sbism8 SPSR,SPIF
109 rjmp spiwl_
110.endm
111
112;------------------------------------------------
113
114 .dseg
115
116mmcStat:
117 .byte 1
118mmcCardType:
119 .byte 1
120mmc_ocr:
121 .byte 4
122
123
124
125 .cseg
126
127;------------------------------------------------
128; Multiply 32 bit value in yh,yl,xh,xl by 512
129
130mul_yx_512:
131 mov yh,yl
132 mov yl,xh
133 mov xh,xl
134 ldi xl,0
135 lsl xh
136 rol yl
137 rol yh
138 ret
139
140;------------------------------------------------
141spi_rcvr:
142 out SPDR,_255
143spi_rcvr_l:
144 sbism8 SPSR,SPIF
145 rjmp spi_rcvr_l
146 in temp,SPDR
147.if MMC_DEBUG > 2
148 push temp
149 printstring "<"
150 rcall printhex
151 printstring " "
152 pop temp
153.endif
154 ret
155
156;------------------------------------------------
157spi_xmit:
158.if MMC_DEBUG > 2
159 printstring ">"
160 rcall printhex
161 printstring " "
162.endif
163 out SPDR,temp
164 ;fall thru
165spi_wait:
166 sbism8 SPSR,SPIF
167 rjmp spi_wait
168 ret
169
170;------------------------------------------------
171; Wait for card ready
172; return 1:OK, 0:Timeout
173
174mmcWaitReady:
175 push temp2
176 ldi temp2,2 ;Wait for ready in timeout of 500ms.
177 rcall spi_rcvr
178mmc_wrl:
179 sts delay_timer2,_255
180mmc_wrl1:
181 rcall spi_rcvr
182 cp temp,_255
183 brne mmc_wrl2
184 ldi temp,1
185 rjmp mmc_wrbreak
186
187mmc_wrl2:
188 lds temp,delay_timer2
189 cpi temp,0
190 brne mmc_wrl1
191 dec temp2
192 brne mmc_wrl ;tmp is 0 here
193
194mmc_wrbreak:
195 pop temp2
196 tst temp ;set flags
197 ret
198
199
200;------------------------------------------------
201; Deselect the card and release SPI bus
202; return 0
203
204mmcDeselect:
205 sbi P_MMC_CS,mmc_cs ; CS high
206 rcall spi_rcvr
207 clr temp
208 ret
209
210;------------------------------------------------
211; Select the card and wait for ready
212; return 255:Successful, 0:Timeout
213
214mmcSelect:
215 cbi P_MMC_CS,mmc_cs ; CS low
216 rcall mmcWaitReady
217 breq mmcDeselect ;return via Deselect
218 sbr temp,255
219 ret
220
221
222;------------------------------------------------
223; Send a command packet to MMC
224; temp2: Command
225; yh..xl: Argument
226
227mmcCmd:
228 sbrs temp2,7
229 rjmp mmc_cmddo
230
231; ACMD<n> is the command sequence of CMD55-CMD<n>
232
233 push yh
234 push yl
235 push xh
236 push xl
237 push temp2
238 ldiw y,0
239 movw x,y
240 ldi temp2,CMD55
241 rcall mmcCmd
242 pop temp2
243 pop xl
244 pop xh
245 pop yl
246 pop yh
247
248 cpi temp,2
249 brlo mmc_cmddo ; fall thru, if (retval <= 1)
250
251 tst temp
252 ret ; else return error
253
254; Select the card and wait for ready
255
256mmc_cmddo:
257.if MMC_DEBUG
258 sbrc temp2,7
259 rjmp dmmccmd_nonl
260 printnewline
261
262dmmccmd_nonl:
263 printstring "mmcCMD: "
264 mov temp,temp2
265 cbr temp,0x80
266 rcall printhex
267 printstring " "
268 push temp2
269 movw temp,y
270 rcall printhexw
271 movw temp,x
272 rcall printhexw
273 printstring " "
274 pop temp2
275.endif
276 rcall mmcDeselect
277 rcall mmcSelect
278 brne mmc_cmd_p
279
280 ldi temp,0xFF
281 rjmp mmc_cmdexit
282
283; Send command packet
284
285mmc_cmd_p:
286 mov temp,temp2
287 cbr temp,0x80
288 sbr temp,0x40
289 rcall spi_xmit
290 out SPDR,yh
291 rcall spi_wait
292 out SPDR,yl
293 rcall spi_wait
294 out SPDR,xh
295 rcall spi_wait
296 out SPDR,xl
297 rcall spi_wait
298
299 ldi temp,0x95 ;CRC for CMD0(0)
300 cpi temp2,CMD0
301 breq mmc_cmdxcrc
302 ldi temp,0x87 ;CRC for CMD8(0x1AA)
303 cpi temp2,CMD8
304 breq mmc_cmdxcrc
305 ldi temp,0x01 ;Dummy CRC + Stop
306mmc_cmdxcrc:
307.if MMC_DEBUG
308 printstring ".. "
309 rcall printhex
310.endif
311 rcall spi_xmit
312
313; Receive command response
314
315 cpi temp2,CMD12 ; Skip a stuff byte when stop reading
316 brne mmc_cmdres
317 rcall spi_rcvr
318
319; Wait for a valid response in timeout of 10 attempts
320
321mmc_cmdres:
322 ldi temp,10
323 mov _tmp1,temp
324mmc_cmdrl:
325 rcall spi_rcvr
326 sbrs temp,7
327 rjmp mmc_cmdexit
328 dec _tmp1
329 brne mmc_cmdrl
330
331; Return with response value
332
333mmc_cmdexit:
334.if MMC_DEBUG
335 printstring " CMDRes: "
336 rcall printhex
337 printstring " "
338 rcall uart_wait_empty
339.endif
340 tst temp ;set flags
341 ret
342
343;------------------------------------------------
344; Check if 1 sec timeout
345; return Z-Flag set, if timeout
346
347mmc_timeout_1s:
348 lds temp,delay_timer1
349 tst temp
350 brne mmc_ttex
351 dec temp4
352 breq mmc_ttex
353 ldi temp,100
354 sts delay_timer1,temp
355mmc_ttex:
356 ret
357
358
359;------------------------------------------------
360; "Public" functions
361;------------------------------------------------
362
363;------------------------------------------------
364; Initialize MMC/SD card
365
366mmcInit:
367.if MMC_DEBUG
368 printnewline
369 printstring "mmcInit "
370.endif
371 lds temp,mmcStat ;Set 'NO INIT' status
372 sbr temp,MMCST_NOINIT
373 sts mmcStat,temp
374
375 spi_clkslow
376 ldi temp2,10
377mmci_lp:
378 rcall spi_rcvr
379 dec temp2 ;80 dummy clocks
380 brne mmci_lp
381
382 ldi temp3,0 ;Card type
383 ldi temp2,CMD0
384 ldiw y,0
385 movw x,y
386 rcall mmcCmd ;Enter Idle state
387 cpi temp,1
388 breq mmci_1
389 rjmp mmci_lend
390mmci_1:
391 ldi temp4,10 ;Initialization timeout of 1000 ms.
392 ldi temp,100
393 sts delay_timer1,temp
394 ldi temp2,CMD8
395 ldiw y,0
396 ldi xh,0x01
397 ldi xl,0xAA
398 rcall mmcCmd
399 cpi temp,1 ;SDv2?
400 brne mmci_sdv1
401
402; Get trailing return value of R7 response
403
404 ldi temp2,4
405 ldiw z,mmc_ocr
406mmci_v2l1:
407 rcall spi_rcvr
408 st z+,temp
409 dec temp2
410 brne mmci_v2l1
411 sbiw z,4
412 ldd temp,z+2
413 cpi temp,0x01
414 ldd temp,z+3
415 cpc temp,xl ;Reuse 0xAA value in xl
416 brne mmci_sdv1
417
418; The card can work at vdd range of 2.7-3.6V.
419; Wait for leaving idle state (ACMD41 with HCS bit).
420
421 ldi temp2,ACMD41
422 ldi yh,0x40
423 ldi yl,0
424 ldi xh,0
425 ldi xl,0
426mmci_v2l2:
427 rcall mmcCmd
428 breq mmci_ccc
429 rcall mmc_timeout_1s
430 brne mmci_v2l2
431 rjmp mmci_sdv2end
432
433; Check CCS bit in the OCR
434mmci_ccc:
435 ldi temp2,CMD58
436 ldi yh,0
437 rcall mmcCmd
438 brne mmci_sdv2end
439
440 ldi temp2,4
441mmci_v2l3:
442 rcall spi_rcvr
443 st z+,temp
444 dec temp2
445 brne mmci_v2l3
446 sbiw z,4
447
448 sbr temp3,CT_SD2
449 ldd temp,z+0
450 sbrc temp,6
451 sbr temp3,CT_BLOCK
452
453mmci_sdv2end:
454 rjmp mmci_lend
455
456; SDv1 or MMCv3
457
458mmci_sdv1:
459 ldi temp2,ACMD41
460 ldiw y,0
461 movw x,y
462 rcall mmcCmd
463 cpi temp,2
464 brsh mmci_mmcv3
465 sbr temp3,CT_SD1 ;SDv1
466 ldi temp2,ACMD41
467 rjmp mmci_v1_l
468mmci_mmcv3:
469 sbr temp3,CT_MMC ;MMCv3
470 ldi temp2,CMD1
471
472; Wait for leaving idle state
473mmci_v1_l:
474 rcall mmcCmd
475 breq mmci_v1_2
476 rcall mmc_timeout_1s
477 brne mmci_v1_l
478 rjmp mmci_lend ;Timeout
479
480; Set R/W block length to 512
481mmci_v1_2:
482 ldi temp2,CMD16
483 ldiw x,512
484 rcall mmcCmd
485 breq mmci_lend
486 ldi temp3,0
487
488mmci_lend:
489 sts mmcCardType,temp3
490 rcall mmcDeselect
491
492; Initialization succeded?
493
494 lds temp,mmcStat
495 tst temp3
496 breq mmci_lex
497 cbr temp,MMCST_NOINIT ;Yes, clear 'NO INIT' status
498 sts mmcStat,temp
499mmci_lex:
500
501.if MMC_DEBUG
502 printnewline
503 printstring " CT: "
504 push temp
505 lds temp,mmcCardType
506 rcall printhex
507 pop temp
508 printstring " InitRes: "
509 rcall printhex
510 printstring " "
511.endif
512
513 spi_disable
514 ret
515
516
517;--------------------------------------------------------------
518; Read sector
519; z: Pointer to the data buffer to store read data
520; yh..xl: Start sector number (LBA)
521
522mmcReadSect:
523.if MMC_DEBUG > 1
524 printnewline
525 printstring "mmcRdSect "
526.endif
527 ldiw z,hostbuf ;for now
528
529 lds _tmp0,mmcStat
530 ldi temp,RES_NOTRDY
531 sbrc _tmp0,MMCST_NOINIT
532 ret
533
534 spi_clkfast
535 lds temp,mmcCardType
536 sbrs temp,log2(CT_BLOCK)
537 rcall mul_yx_512 ;Convert to byte address (*512)
538
539 ldi temp2,CMD17
540 rcall mmcCmd
541 ldi temp2,RES_ERROR
542 brne mmc_rdex
543
544; Receive a data packet from MMC
545
546 ldiw y,512 ;Number of bytes to tranfer
547 ldi temp,200 ;Wait for data packet in timeout of 200ms.
548 sts delay_timer1,temp
549mmc_rcv_wl:
550 rcall spi_rcvr
551 cp temp,_255
552 brne mmc_rcv_start
553 lds _tmp0,delay_timer1
554 cp _tmp0,_0
555 brne mmc_rcv_wl
556.if MMC_DEBUG > 1
557 printstring "TIMEOUT "
558 rjmp mmc_rcv_dbg1
559.endif
560
561mmc_rcv_start:
562.if MMC_DEBUG > 1
563 cpi temp,0xFE ;If not valid data token,
564 breq mmc_rcv_dbg1
565 printstring "Token: "
566 rcall printhex
567 printstring " "
568mmc_rcv_dbg1:
569.endif
570 cpi temp,0xFE ;If not valid data token,
571 brne mmc_rdex
572
573 rcall spi_rcvr ;Shift in first byte.
4160d270
L
574.if MMC_DEBUG > 3
575 printnewline
576 rcall printhex
577 printstring " "
578.endif
9c15f366
L
579 out SPDR,_255 ;Start shift in next byte.
580mmc_rcv_rl:
581 sbiw yl,1
582 breq mmc_rcv_rle
583 st z+,temp
584 spi_waitm
585 in temp,SPDR
4160d270
L
586.if MMC_DEBUG > 3
587 rcall printhex
588 printstring " "
589.endif
9c15f366
L
590 out SPDR,_255
591 rjmp mmc_rcv_rl
592
593mmc_rcv_rle:
594 st z+,temp ;Store last byte in buffer
4160d270
L
595.if MMC_DEBUG > 3
596 printnewline
597.endif
9c15f366
L
598 rcall spi_wait ; while SPI module shifts in crc part1.
599 rcall spi_rcvr ;Read second crc.
600
601 ldi temp2,RES_OK ;Return success
602mmc_rdex:
603 rcall mmcDeselect
604 spi_disable
605 mov temp,temp2
606.if MMC_DEBUG > 1
607 printstring "RdSectRes: "
608 rcall printhex
609 printstring " "
610.endif
611 ret
612
92202636 613
64219415 614;--------------------------------------------------------------
92202636
L
615; Read word
616; TODO: Read Word to ZL,ZH at given ZL/ZH Offset
617; Need for reading of single FAT16 Entrys without killing the
618; Entrys in hostbuffer...
64219415
FZ
619;
620; in zh,zl: Pointer to Word within the Sector to read
621; in yh..xl: Start sector number (LBA)
92202636 622; out zh,zl : word thats been read
64219415
FZ
623
624mmcReadWord:
625.if MMC_DEBUG > 1
626 printnewline
627 printstring "mmcRdWord "
628.endif
629 lds _tmp0,mmcStat
630 ldi temp,RES_NOTRDY
631 sbrc _tmp0,MMCST_NOINIT
632 ret
633
634 spi_clkfast
635 lds temp,mmcCardType
636 sbrs temp,log2(CT_BLOCK)
637 rcall mul_yx_512 ;Convert to byte address (*512)
92202636 638
64219415
FZ
639 ldi temp2,CMD17
640 rcall mmcCmd
641 ldi temp2,RES_ERROR
642 brne mmc_rdexw
643
644; Receive a data packet from MMC
645
646 ldiw y,512 ;Number of bytes to tranfer
647 ldi temp,200 ;Wait for data packet in timeout of 200ms.
648 sts delay_timer1,temp
649mmc_rcvw_wl:
650 rcall spi_rcvr
651 cp temp,_255
652 brne mmc_rcvw_start
653 lds temp2,delay_timer1
654 cpi temp2,0
655 brne mmc_rcvw_wl
656mmc_rcvw_start:
657 cpi temp,0xFE ;If not valid data token,
658 ldi temp2,RES_ERROR
659 brne mmc_rdexw
660
661 rcall spi_rcvr ;Shift in first byte.
662 out SPDR,_255 ;Start shift in next byte.
663mmc_rcvw_rl:
664 sbiw yl,1
665 breq mmc_rcvw_rle
92202636
L
666 cp zl,_0
667 cpc zh,_0
668 breq mmc_rcvw_sto
669
670 sbiw zl,1
64219415
FZ
671 spi_waitm
672 in temp,SPDR
673 out SPDR,_255
674 rjmp mmc_rcvw_rl
675
92202636
L
676mmc_rcvw_sto:
677 mov zl,temp
678fc0b0
FZ
678 spi_waitm
679 in temp,SPDR
680 out SPDR,_255
92202636
L
681 mov zh,temp
682
678fc0b0
FZ
683mmc_rcvw_rl2:
684 sbiw yl,1
685 breq mmc_rcvw_rle
686 spi_waitm
687 in temp,SPDR
688 out SPDR,_255
92202636 689 rjmp mmc_rcvw_rl2
64219415 690mmc_rcvw_rle:
64219415
FZ
691 rcall spi_wait ; while SPI module shifts in crc part1.
692 rcall spi_rcvr ;Read second crc.
693
694 ldi temp2,RES_OK ;Return success
695mmc_rdexw:
696 rcall mmcDeselect
697 spi_disable
698 mov temp,temp2
699.if MMC_DEBUG > 1
700 printstring "RdWordRes: "
701 rcall printhex
702 printstring " "
703.endif
92202636 704 ret
9c15f366
L
705
706;--------------------------------------------------------------
707; Write sector
708; z: Pointer to the data to be written
709; yh..xl: Sector number (LBA)
710
711mmcWriteSect:
712.if MMC_DEBUG > 1
713 printnewline
714 printstring "mmcWrSect "
715.endif
716 ldiw z,hostbuf ;for now
717
718 lds _tmp0,mmcStat
719 ldi temp,RES_NOTRDY
720 sbrc _tmp0,MMCST_NOINIT
721 ret
722
723 spi_clkfast
724 lds temp,mmcCardType
725 sbrs temp,log2(CT_BLOCK)
726 rcall mul_yx_512 ;Convert to byte address (*512)
727
728 ldi temp2,CMD24
729 rcall mmcCmd
730 brne mmc_wrexer
731
732; Send a data packet to MMC
733
734.if MMC_DEBUG > 2
735; printnewline
736 printstring "mmcXMIT "
737.endif
738 rcall mmcWaitReady
739 breq mmc_wrexer
740
741 ldi temp,0xFE ;Data token
742 out SPDR,temp
743 ldiw y,512
744mmc_x_loop:
745 ld temp,z+
746 spi_waitm
747 out SPDR,temp
748 sbiw yl,1
749 brne mmc_x_loop
750
751 rcall spi_wait
752 ldi temp,0xFF ;dummy crc
753 rcall spi_xmit
754 rcall spi_xmit
755 rcall spi_rcvr
756.if MMC_DEBUG > 2
757 printstring "XMITRes: "
758 rcall printhex
759 printstring " "
760.endif
761 andi temp,0x1F ;If not accepted, return with error
762 cpi temp,0x05
763 ldi temp2,RES_OK ;Return success
764 breq mmc_wrex
765
766mmc_wrexer:
767 ldi temp,RES_ERROR
768mmc_wrex:
769 rcall mmcDeselect
770 spi_disable
771 mov temp,temp2
772.if MMC_DEBUG > 1
773 printstring "WrSectRes: "
774 rcall printhex
775 printstring " "
776.endif
777 ret
778
779;--------------------------------------------------------------
780; vim:set ts=8 noet nowrap
781