]> cloudbase.mooo.com Git - avrcpm.git/blob - avr/mmc.asm
* MMC/SD Bootloader support
[avrcpm.git] / avr / mmc.asm
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
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
67 ;------------------------------------------------
68 ;
69 .macro spi_clkslow
70 .if MMC_DEBUG > 1
71 printstring "SPI_CLK_SLOW "
72 .endif
73 ldi temp,SPI_MODE|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0) ;clk/128
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
84 ldi temp,SPI_MODE|(1<<SPE)|(1<<MSTR) ;clk/4
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
116 mmcStat:
117 .byte 1
118 mmcCardType:
119 .byte 1
120
121
122 .cseg
123
124 ;------------------------------------------------
125 ; Multiply 32 bit value in yh,yl,xh,xl by 512
126
127 mul_yx_512:
128 mov yh,yl
129 mov yl,xh
130 mov xh,xl
131 ldi xl,0
132 lsl xh
133 rol yl
134 rol yh
135 ret
136
137 ;------------------------------------------------
138 spi_rcvr:
139 out SPDR,_255
140 spi_rcvr_l:
141 sbism8 SPSR,SPIF
142 rjmp spi_rcvr_l
143 in temp,SPDR
144 .if MMC_DEBUG > 2
145 push temp
146 printstring "<"
147 rcall printhex
148 printstring " "
149 pop temp
150 .endif
151 ret
152
153 ;------------------------------------------------
154 spi_xmit:
155 .if MMC_DEBUG > 2
156 printstring ">"
157 rcall printhex
158 printstring " "
159 .endif
160 out SPDR,temp
161 ;fall thru
162 spi_wait:
163 sbism8 SPSR,SPIF
164 rjmp spi_wait
165 ret
166
167 ;------------------------------------------------
168 ; Wait for card ready
169 ; return 1:OK, 0:Timeout
170
171 mmcWaitReady:
172 push temp2
173 ldi temp2,2 ;Wait for ready in timeout of 500ms.
174 rcall spi_rcvr
175 mmc_wrl:
176 sts delay_timer2,_255
177 mmc_wrl1:
178 rcall spi_rcvr
179 cp temp,_255
180 brne mmc_wrl2
181 ldi temp,1
182 rjmp mmc_wrbreak
183
184 mmc_wrl2:
185 lds temp,delay_timer2
186 cpi temp,0
187 brne mmc_wrl1
188 dec temp2
189 brne mmc_wrl ;tmp is 0 here
190
191 mmc_wrbreak:
192 pop temp2
193 tst temp ;set flags
194 ret
195
196
197 ;------------------------------------------------
198 ; Deselect the card and release SPI bus
199 ; return 0
200
201 mmcDeselect:
202 sbi P_MMC_CS,mmc_cs ; CS high
203 rcall spi_rcvr
204 clr temp
205 ret
206
207 ;------------------------------------------------
208 ; Select the card and wait for ready
209 ; return 255:Successful, 0:Timeout
210
211 mmcSelect:
212 cbi P_MMC_CS,mmc_cs ; CS low
213 rcall mmcWaitReady
214 breq mmcDeselect ;return via Deselect
215 sbr temp,255
216 ret
217
218
219 ;------------------------------------------------
220 ; Send a command packet to MMC
221 ; temp2: Command
222 ; yh..xl: Argument
223
224 mmcCmd:
225 sbrs temp2,7
226 rjmp mmc_cmddo
227
228 ; ACMD<n> is the command sequence of CMD55-CMD<n>
229
230 push yh
231 push yl
232 push xh
233 push xl
234 push temp2
235 ldiw y,0
236 movw x,y
237 ldi temp2,CMD55
238 rcall mmcCmd
239 pop temp2
240 pop xl
241 pop xh
242 pop yl
243 pop yh
244
245 cpi temp,2
246 brlo mmc_cmddo ; fall thru, if (retval <= 1)
247
248 tst temp
249 ret ; else return error
250
251 ; Select the card and wait for ready
252
253 mmc_cmddo:
254 .if MMC_DEBUG
255 sbrc temp2,7
256 rjmp dmmccmd_nonl
257 printnewline
258
259 dmmccmd_nonl:
260 printstring "mmcCMD: "
261 mov temp,temp2
262 cbr temp,0x80
263 rcall printhex
264 printstring " "
265 push temp2
266 movw temp,y
267 rcall printhexw
268 movw temp,x
269 rcall printhexw
270 printstring " "
271 pop temp2
272 .endif
273 rcall mmcDeselect
274 rcall mmcSelect
275 brne mmc_cmd_p
276
277 ldi temp,0xFF
278 rjmp mmc_cmdexit
279
280 ; Send command packet
281
282 mmc_cmd_p:
283 mov temp,temp2
284 cbr temp,0x80
285 sbr temp,0x40
286 rcall spi_xmit
287 out SPDR,yh
288 rcall spi_wait
289 out SPDR,yl
290 rcall spi_wait
291 out SPDR,xh
292 rcall spi_wait
293 out SPDR,xl
294 rcall spi_wait
295
296 ldi temp,0x95 ;CRC for CMD0(0)
297 cpi temp2,CMD0
298 breq mmc_cmdxcrc
299 ldi temp,0x87 ;CRC for CMD8(0x1AA)
300 cpi temp2,CMD8
301 breq mmc_cmdxcrc
302 ldi temp,0x01 ;Dummy CRC + Stop
303 mmc_cmdxcrc:
304 .if MMC_DEBUG
305 printstring ".. "
306 rcall printhex
307 .endif
308 rcall spi_xmit
309
310 ; Receive command response
311
312 cpi temp2,CMD12 ; Skip a stuff byte when stop reading
313 brne mmc_cmdres
314 rcall spi_rcvr
315
316 ; Wait for a valid response in timeout of 10 attempts
317
318 mmc_cmdres:
319 ldi temp,10
320 mov _tmp1,temp
321 mmc_cmdrl:
322 rcall spi_rcvr
323 sbrs temp,7
324 rjmp mmc_cmdexit
325 dec _tmp1
326 brne mmc_cmdrl
327
328 ; Return with response value
329
330 mmc_cmdexit:
331 .if MMC_DEBUG
332 printstring " CMDRes: "
333 rcall printhex
334 printstring " "
335 rcall uart_wait_empty
336 .endif
337 tst temp ;set flags
338 ret
339
340 ;------------------------------------------------
341 ; Send command and receive ocr response
342 ; temp2: Command, zl: expected cmd response
343 ; yh..xl: Argument
344 ; return: yh..xl: ocr, z flag == 1, if return from mmcCmd was ok.
345
346 mmcCmd_ocr:
347
348 rcall mmcCmd
349
350 cp temp,zl ;
351 brne mmc_cocr_e
352
353 ; Get trailing return value of R7 response
354
355 ldi temp2,4
356 ldiw z,0x1a ;memory address of xl
357 mmc_cocr1:
358 rcall spi_rcvr
359 st z+,temp
360 dec temp2
361 brne mmc_cocr1
362 ;z-flag =1
363 mmc_cocr_e:
364 ret
365
366 ;------------------------------------------------
367 ; Check if 1 sec timeout
368 ; return Z-Flag set, if timeout
369
370 mmc_timeout_1s:
371 lds temp,delay_timer1
372 tst temp
373 brne mmc_ttex
374 dec temp4
375 breq mmc_ttex
376 ldi temp,100
377 sts delay_timer1,temp
378 mmc_ttex:
379 ret
380
381
382 ;------------------------------------------------
383 ; "Public" functions
384 ;------------------------------------------------
385
386 ;------------------------------------------------
387 ; Initialize MMC/SD card
388
389 mmcInit:
390 .if MMC_DEBUG
391 printnewline
392 printstring "mmcInit "
393 .endif
394 lds temp,mmcStat ;Set 'NO INIT' status
395 sbr temp,MMCST_NOINIT
396 sts mmcStat,temp
397
398 spi_clkslow
399 ldi temp2,10
400 mmci_lp:
401 rcall spi_rcvr
402 dec temp2 ;80 dummy clocks
403 brne mmci_lp
404
405 ldi temp3,0 ;Card type
406 ldi temp2,CMD0
407 ldiw y,0
408 movw x,y
409 rcall mmcCmd ;Enter Idle state
410 cpi temp,1
411 breq mmci_1
412 rjmp mmci_lend
413 mmci_1:
414 ldi temp4,10 ;Initialization timeout of 1000 ms.
415 ldi temp,100
416 sts delay_timer1,temp
417 ldi temp2,CMD8
418 ldiw y,0
419 ldi xh,0x01
420 ldi xl,0xAA
421 ldi zl,1
422 rcall mmcCmd_ocr
423 brne mmci_sdv1 ;SDv2?
424
425 cpi yl,0x01 ;ocr[2]
426 brne mmci_sdv1
427 cpi yh,0xAA ;ocr[3]
428 brne mmci_sdv1
429
430 ; The card can work at vdd range of 2.7-3.6V.
431 ; Wait for leaving idle state (ACMD41 with HCS bit).
432
433 ldi temp2,ACMD41
434 ldi yh,0x40
435 ldi yl,0
436 ldi xh,0
437 ldi xl,0
438 mmci_v2l2:
439 rcall mmcCmd
440 breq mmci_ccc
441 rcall mmc_timeout_1s
442 brne mmci_v2l2
443 rjmp mmci_sdv2end
444
445 ; Check CCS bit in the OCR
446 mmci_ccc:
447 ldi temp2,CMD58
448 ldi yh,0
449 ldi zl,0
450 rcall mmcCmd_ocr
451
452 brne mmci_sdv2end
453
454 sbr temp3,CT_SD2
455 sbrc xl,6
456 sbr temp3,CT_BLOCK
457
458 mmci_sdv2end:
459 rjmp mmci_lend
460
461 ; SDv1 or MMCv3
462
463 mmci_sdv1:
464 ldi temp2,ACMD41
465 ldiw y,0
466 movw x,y
467 rcall mmcCmd
468 cpi temp,2
469 brsh mmci_mmcv3
470 sbr temp3,CT_SD1 ;SDv1
471 ldi temp2,ACMD41
472 rjmp mmci_v1_l
473 mmci_mmcv3:
474 sbr temp3,CT_MMC ;MMCv3
475 ldi temp2,CMD1
476
477 ; Wait for leaving idle state
478 mmci_v1_l:
479 rcall mmcCmd
480 breq mmci_v1_2
481 rcall mmc_timeout_1s
482 brne mmci_v1_l
483 rjmp mmci_lend ;Timeout
484
485 ; Set R/W block length to 512
486 mmci_v1_2:
487 ldi temp2,CMD16
488 ldiw x,512
489 rcall mmcCmd
490 breq mmci_lend
491 ldi temp3,0
492
493 mmci_lend:
494 sts mmcCardType,temp3
495 rcall mmcDeselect
496
497 ; Initialization succeded?
498
499 lds temp,mmcStat
500 tst temp3
501 breq mmci_lex
502 cbr temp,MMCST_NOINIT ;Yes, clear 'NO INIT' status
503 sts mmcStat,temp
504 mmci_lex:
505
506 .if MMC_DEBUG
507 printnewline
508 printstring " CT: "
509 push temp
510 lds temp,mmcCardType
511 rcall printhex
512 pop temp
513 printstring " InitRes: "
514 rcall printhex
515 printstring " "
516 .endif
517
518 spi_disable
519 ret
520
521
522 ;--------------------------------------------------------------
523 ; Read sector
524 ; z: Pointer to the data buffer to store read data
525 ; yh..xl: Start sector number (LBA)
526
527 mmcReadSect:
528 .if MMC_DEBUG > 1
529 printnewline
530 printstring "mmcRdSect "
531 .endif
532 ldiw z,hostbuf ;for now
533
534 lds _tmp0,mmcStat
535 ldi temp,RES_NOTRDY
536 sbrc _tmp0,MMCST_NOINIT
537 ret
538
539 spi_clkfast
540 lds temp,mmcCardType
541 sbrs temp,log2(CT_BLOCK)
542 rcall mul_yx_512 ;Convert to byte address (*512)
543
544 ldi temp2,CMD17
545 rcall mmcCmd
546 ldi temp2,RES_ERROR
547 brne mmc_rdex
548
549 ; Receive a data packet from MMC
550
551 ldiw y,512 ;Number of bytes to tranfer
552 ldi temp,200 ;Wait for data packet in timeout of 200ms.
553 sts delay_timer1,temp
554 mmc_rcv_wl:
555 rcall spi_rcvr
556 cp temp,_255
557 brne mmc_rcv_start
558 lds _tmp0,delay_timer1
559 cp _tmp0,_0
560 brne mmc_rcv_wl
561 .if MMC_DEBUG > 1
562 printstring "TIMEOUT "
563 rjmp mmc_rcv_dbg1
564 .endif
565
566 mmc_rcv_start:
567 .if MMC_DEBUG > 1
568 cpi temp,0xFE ;If not valid data token,
569 breq mmc_rcv_dbg1
570 printstring "Token: "
571 rcall printhex
572 printstring " "
573 mmc_rcv_dbg1:
574 .endif
575 cpi temp,0xFE ;If not valid data token,
576 brne mmc_rdex
577
578 rcall spi_rcvr ;Shift in first byte.
579 .if MMC_DEBUG > 3
580 printnewline
581 rcall printhex
582 printstring " "
583 .endif
584 out SPDR,_255 ;Start shift in next byte.
585 mmc_rcv_rl:
586 sbiw yl,1
587 breq mmc_rcv_rle
588 st z+,temp
589 spi_waitm
590 in temp,SPDR
591 .if MMC_DEBUG > 3
592 rcall printhex
593 printstring " "
594 .endif
595 out SPDR,_255
596 rjmp mmc_rcv_rl
597
598 mmc_rcv_rle:
599 st z+,temp ;Store last byte in buffer
600 .if MMC_DEBUG > 3
601 printnewline
602 .endif
603 rcall spi_wait ;while SPI module shifts in crc part1.
604 rcall spi_rcvr ;Read second crc.
605
606 ldi temp2,RES_OK ;Return success
607 mmc_rdex:
608 rcall mmcDeselect
609 spi_disable
610 mov temp,temp2
611 .if MMC_DEBUG > 1
612 printstring "RdSectRes: "
613 rcall printhex
614 printstring " "
615 .endif
616 ret
617
618
619 ;--------------------------------------------------------------
620 ; Read word
621 ; Read Word to ZL,ZH at given ZL/ZH Offset
622 ; Needed for reading a single FAT16 entry without killing the
623 ; entries in hostbuffer...
624 ;
625 ; in zh,zl: Pointer to word within the sector to read
626 ; in yh..xl: Start sector number (LBA)
627 ; out zh,zl: Word thats been read
628
629 mmcReadWord:
630 .if MMC_DEBUG > 1
631 printnewline
632 printstring "mmcRdWord "
633 .endif
634 lds _tmp0,mmcStat
635 ldi temp,RES_NOTRDY
636 sbrc _tmp0,MMCST_NOINIT
637 ret
638
639 spi_clkfast
640 lds temp,mmcCardType
641 sbrs temp,log2(CT_BLOCK)
642 rcall mul_yx_512 ;Convert to byte address (*512)
643
644 ldi temp2,CMD17
645 rcall mmcCmd
646 ldi temp2,RES_ERROR
647 brne mmc_rdexw
648
649 ; Receive a data packet from MMC
650
651 ldiw y,512 ;Number of bytes to tranfer
652 ldi temp,200 ;Wait for data packet in timeout of 200ms.
653 sts delay_timer1,temp
654 mmc_rcvw_wl:
655 rcall spi_rcvr
656 cp temp,_255
657 brne mmc_rcvw_start
658 lds temp2,delay_timer1
659 cpi temp2,0
660 brne mmc_rcvw_wl
661 mmc_rcvw_start:
662 cpi temp,0xFE ;If not valid data token,
663 ldi temp2,RES_ERROR
664 brne mmc_rdexw
665
666 rcall spi_rcvr ;Shift in first byte.
667 out SPDR,_255 ;Start shift in next byte.
668 mmc_rcvw_rl:
669 sbiw yl,1
670 breq mmc_rcvw_rle
671 cp zl,_0
672 cpc zh,_0
673 breq mmc_rcvw_sto
674
675 sbiw zl,1
676 spi_waitm
677 in temp,SPDR
678 out SPDR,_255
679 rjmp mmc_rcvw_rl
680
681 mmc_rcvw_sto:
682 mov zl,temp
683 spi_waitm
684 in temp,SPDR
685 out SPDR,_255
686 mov zh,temp
687
688 mmc_rcvw_rl2:
689 sbiw yl,1
690 breq mmc_rcvw_rle
691 spi_waitm
692 in temp,SPDR
693 out SPDR,_255
694 rjmp mmc_rcvw_rl2
695 mmc_rcvw_rle:
696 rcall spi_wait ; while SPI module shifts in crc part1.
697 rcall spi_rcvr ;Read second crc.
698
699 ldi temp2,RES_OK ;Return success
700 mmc_rdexw:
701 rcall mmcDeselect
702 spi_disable
703 mov temp,temp2
704 .if MMC_DEBUG > 1
705 printstring "RdWordRes: "
706 rcall printhex
707 printstring " "
708 .endif
709 ret
710
711 ;--------------------------------------------------------------
712 ; Write sector
713 ; z: Pointer to the data to be written
714 ; yh..xl: Sector number (LBA)
715
716 mmcWriteSect:
717 .if MMC_DEBUG > 1
718 printnewline
719 printstring "mmcWrSect "
720 .endif
721 ldiw z,hostbuf ;for now
722
723 lds _tmp0,mmcStat
724 ldi temp,RES_NOTRDY
725 sbrc _tmp0,MMCST_NOINIT
726 ret
727
728 spi_clkfast
729 lds temp,mmcCardType
730 sbrs temp,log2(CT_BLOCK)
731 rcall mul_yx_512 ;Convert to byte address (*512)
732
733 ldi temp2,CMD24
734 rcall mmcCmd
735 brne mmc_wrexer
736
737 ; Send a data packet to MMC
738
739 .if MMC_DEBUG > 2
740 ; printnewline
741 printstring "mmcXMIT "
742 .endif
743 rcall mmcWaitReady
744 breq mmc_wrexer
745
746 ldi temp,0xFE ;Data token
747 out SPDR,temp
748 ldiw y,512
749 mmc_x_loop:
750 ld temp,z+
751 spi_waitm
752 out SPDR,temp
753 sbiw yl,1
754 brne mmc_x_loop
755
756 rcall spi_wait
757 ldi temp,0xFF ;dummy crc
758 rcall spi_xmit
759 rcall spi_xmit
760 rcall spi_rcvr
761 .if MMC_DEBUG > 2
762 printstring "XMITRes: "
763 rcall printhex
764 printstring " "
765 .endif
766 andi temp,0x1F ;If not accepted, return with error
767 cpi temp,0x05
768 ldi temp2,RES_OK ;Return success
769 breq mmc_wrex
770
771 mmc_wrexer:
772 ldi temp,RES_ERROR
773 mmc_wrex:
774 rcall mmcDeselect
775 spi_disable
776 mov temp,temp2
777 .if MMC_DEBUG > 1
778 printstring "WrSectRes: "
779 rcall printhex
780 printstring " "
781 .endif
782 ret
783
784 ;--------------------------------------------------------------
785 ; vim:set ts=8 noet nowrap
786