]> cloudbase.mooo.com Git - avrcpm.git/blame - avr/mmc.asm
* Some bug fixes
[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
9c15f366
L
120
121
122 .cseg
123
124;------------------------------------------------
125; Multiply 32 bit value in yh,yl,xh,xl by 512
126
127mul_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;------------------------------------------------
138spi_rcvr:
139 out SPDR,_255
140spi_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;------------------------------------------------
154spi_xmit:
155.if MMC_DEBUG > 2
156 printstring ">"
157 rcall printhex
158 printstring " "
159.endif
160 out SPDR,temp
161 ;fall thru
162spi_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
171mmcWaitReady:
172 push temp2
173 ldi temp2,2 ;Wait for ready in timeout of 500ms.
174 rcall spi_rcvr
175mmc_wrl:
176 sts delay_timer2,_255
177mmc_wrl1:
178 rcall spi_rcvr
179 cp temp,_255
180 brne mmc_wrl2
181 ldi temp,1
182 rjmp mmc_wrbreak
183
184mmc_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
191mmc_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
201mmcDeselect:
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
211mmcSelect:
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
224mmcCmd:
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
253mmc_cmddo:
254.if MMC_DEBUG
255 sbrc temp2,7
256 rjmp dmmccmd_nonl
257 printnewline
258
259dmmccmd_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
282mmc_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
303mmc_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
318mmc_cmdres:
825ecc9d
L
319 push temp2
320 ldi temp2,10
9c15f366
L
321mmc_cmdrl:
322 rcall spi_rcvr
323 sbrs temp,7
324 rjmp mmc_cmdexit
825ecc9d 325 dec temp2
9c15f366
L
326 brne mmc_cmdrl
327
328; Return with response value
329
330mmc_cmdexit:
825ecc9d 331 pop temp2
9c15f366
L
332.if MMC_DEBUG
333 printstring " CMDRes: "
334 rcall printhex
335 printstring " "
336 rcall uart_wait_empty
337.endif
338 tst temp ;set flags
339 ret
340
08716d4f
L
341;------------------------------------------------
342; Send command and receive ocr response
343; temp2: Command, zl: expected cmd response
344; yh..xl: Argument
345; return: yh..xl: ocr, z flag == 1, if return from mmcCmd was ok.
346
347mmcCmd_ocr:
348
349 rcall mmcCmd
350
351 cp temp,zl ;
352 brne mmc_cocr_e
353
354; Get trailing return value of R7 response
355
356 ldi temp2,4
357 ldiw z,0x1a ;memory address of xl
358mmc_cocr1:
359 rcall spi_rcvr
360 st z+,temp
361 dec temp2
362 brne mmc_cocr1
363 ;z-flag =1
364mmc_cocr_e:
365 ret
366
9c15f366
L
367;------------------------------------------------
368; Check if 1 sec timeout
369; return Z-Flag set, if timeout
370
371mmc_timeout_1s:
372 lds temp,delay_timer1
373 tst temp
374 brne mmc_ttex
825ecc9d 375 dec zh
9c15f366
L
376 breq mmc_ttex
377 ldi temp,100
378 sts delay_timer1,temp
379mmc_ttex:
380 ret
381
382
383;------------------------------------------------
384; "Public" functions
385;------------------------------------------------
386
387;------------------------------------------------
388; Initialize MMC/SD card
389
390mmcInit:
391.if MMC_DEBUG
392 printnewline
393 printstring "mmcInit "
394.endif
395 lds temp,mmcStat ;Set 'NO INIT' status
396 sbr temp,MMCST_NOINIT
397 sts mmcStat,temp
398
399 spi_clkslow
400 ldi temp2,10
401mmci_lp:
402 rcall spi_rcvr
403 dec temp2 ;80 dummy clocks
404 brne mmci_lp
405
406 ldi temp3,0 ;Card type
407 ldi temp2,CMD0
408 ldiw y,0
409 movw x,y
410 rcall mmcCmd ;Enter Idle state
411 cpi temp,1
412 breq mmci_1
413 rjmp mmci_lend
414mmci_1:
825ecc9d 415 ldi zh,10 ;Initialization timeout of 1000 ms.
9c15f366
L
416 ldi temp,100
417 sts delay_timer1,temp
418 ldi temp2,CMD8
419 ldiw y,0
420 ldi xh,0x01
421 ldi xl,0xAA
08716d4f
L
422 ldi zl,1
423 rcall mmcCmd_ocr
424 brne mmci_sdv1 ;SDv2?
9c15f366 425
08716d4f
L
426 cpi yl,0x01 ;ocr[2]
427 brne mmci_sdv1
428 cpi yh,0xAA ;ocr[3]
9c15f366
L
429 brne mmci_sdv1
430
431; The card can work at vdd range of 2.7-3.6V.
432; Wait for leaving idle state (ACMD41 with HCS bit).
433
434 ldi temp2,ACMD41
435 ldi yh,0x40
436 ldi yl,0
437 ldi xh,0
438 ldi xl,0
439mmci_v2l2:
440 rcall mmcCmd
441 breq mmci_ccc
442 rcall mmc_timeout_1s
443 brne mmci_v2l2
444 rjmp mmci_sdv2end
445
446; Check CCS bit in the OCR
447mmci_ccc:
448 ldi temp2,CMD58
449 ldi yh,0
08716d4f
L
450 ldi zl,0
451 rcall mmcCmd_ocr
452
9c15f366
L
453 brne mmci_sdv2end
454
9c15f366 455 sbr temp3,CT_SD2
08716d4f 456 sbrc xl,6
9c15f366
L
457 sbr temp3,CT_BLOCK
458
459mmci_sdv2end:
460 rjmp mmci_lend
461
462; SDv1 or MMCv3
463
464mmci_sdv1:
465 ldi temp2,ACMD41
466 ldiw y,0
467 movw x,y
468 rcall mmcCmd
469 cpi temp,2
470 brsh mmci_mmcv3
471 sbr temp3,CT_SD1 ;SDv1
472 ldi temp2,ACMD41
473 rjmp mmci_v1_l
474mmci_mmcv3:
475 sbr temp3,CT_MMC ;MMCv3
476 ldi temp2,CMD1
477
478; Wait for leaving idle state
479mmci_v1_l:
480 rcall mmcCmd
481 breq mmci_v1_2
482 rcall mmc_timeout_1s
483 brne mmci_v1_l
484 rjmp mmci_lend ;Timeout
485
486; Set R/W block length to 512
487mmci_v1_2:
488 ldi temp2,CMD16
489 ldiw x,512
490 rcall mmcCmd
491 breq mmci_lend
492 ldi temp3,0
493
494mmci_lend:
495 sts mmcCardType,temp3
496 rcall mmcDeselect
497
498; Initialization succeded?
499
500 lds temp,mmcStat
501 tst temp3
502 breq mmci_lex
503 cbr temp,MMCST_NOINIT ;Yes, clear 'NO INIT' status
504 sts mmcStat,temp
505mmci_lex:
506
507.if MMC_DEBUG
508 printnewline
509 printstring " CT: "
510 push temp
511 lds temp,mmcCardType
512 rcall printhex
513 pop temp
514 printstring " InitRes: "
515 rcall printhex
516 printstring " "
517.endif
518
519 spi_disable
520 ret
521
522
523;--------------------------------------------------------------
524; Read sector
525; z: Pointer to the data buffer to store read data
526; yh..xl: Start sector number (LBA)
527
528mmcReadSect:
529.if MMC_DEBUG > 1
530 printnewline
531 printstring "mmcRdSect "
532.endif
533 ldiw z,hostbuf ;for now
534
535 lds _tmp0,mmcStat
536 ldi temp,RES_NOTRDY
537 sbrc _tmp0,MMCST_NOINIT
538 ret
539
540 spi_clkfast
541 lds temp,mmcCardType
542 sbrs temp,log2(CT_BLOCK)
543 rcall mul_yx_512 ;Convert to byte address (*512)
544
545 ldi temp2,CMD17
546 rcall mmcCmd
547 ldi temp2,RES_ERROR
548 brne mmc_rdex
549
550; Receive a data packet from MMC
551
552 ldiw y,512 ;Number of bytes to tranfer
553 ldi temp,200 ;Wait for data packet in timeout of 200ms.
554 sts delay_timer1,temp
555mmc_rcv_wl:
556 rcall spi_rcvr
557 cp temp,_255
558 brne mmc_rcv_start
559 lds _tmp0,delay_timer1
560 cp _tmp0,_0
561 brne mmc_rcv_wl
562.if MMC_DEBUG > 1
563 printstring "TIMEOUT "
564 rjmp mmc_rcv_dbg1
565.endif
566
567mmc_rcv_start:
568.if MMC_DEBUG > 1
569 cpi temp,0xFE ;If not valid data token,
570 breq mmc_rcv_dbg1
571 printstring "Token: "
572 rcall printhex
573 printstring " "
574mmc_rcv_dbg1:
575.endif
576 cpi temp,0xFE ;If not valid data token,
577 brne mmc_rdex
578
579 rcall spi_rcvr ;Shift in first byte.
4160d270
L
580.if MMC_DEBUG > 3
581 printnewline
582 rcall printhex
583 printstring " "
584.endif
9c15f366
L
585 out SPDR,_255 ;Start shift in next byte.
586mmc_rcv_rl:
587 sbiw yl,1
588 breq mmc_rcv_rle
589 st z+,temp
590 spi_waitm
591 in temp,SPDR
4160d270
L
592.if MMC_DEBUG > 3
593 rcall printhex
594 printstring " "
595.endif
9c15f366
L
596 out SPDR,_255
597 rjmp mmc_rcv_rl
598
599mmc_rcv_rle:
600 st z+,temp ;Store last byte in buffer
4160d270
L
601.if MMC_DEBUG > 3
602 printnewline
603.endif
08716d4f 604 rcall spi_wait ;while SPI module shifts in crc part1.
9c15f366
L
605 rcall spi_rcvr ;Read second crc.
606
607 ldi temp2,RES_OK ;Return success
608mmc_rdex:
609 rcall mmcDeselect
610 spi_disable
611 mov temp,temp2
612.if MMC_DEBUG > 1
613 printstring "RdSectRes: "
614 rcall printhex
615 printstring " "
616.endif
617 ret
618
92202636 619
64219415 620;--------------------------------------------------------------
92202636 621; Read word
08716d4f
L
622; Read Word to ZL,ZH at given ZL/ZH Offset
623; Needed for reading a single FAT16 entry without killing the
624; entries in hostbuffer...
64219415 625;
08716d4f 626; in zh,zl: Pointer to word within the sector to read
64219415 627; in yh..xl: Start sector number (LBA)
08716d4f 628; out zh,zl: Word thats been read
64219415
FZ
629
630mmcReadWord:
631.if MMC_DEBUG > 1
632 printnewline
08716d4f 633 printstring "mmcRdWord "
64219415 634.endif
08716d4f
L
635 lds _tmp0,mmcStat
636 ldi temp,RES_NOTRDY
64219415
FZ
637 sbrc _tmp0,MMCST_NOINIT
638 ret
639
640 spi_clkfast
08716d4f 641 lds temp,mmcCardType
64219415
FZ
642 sbrs temp,log2(CT_BLOCK)
643 rcall mul_yx_512 ;Convert to byte address (*512)
92202636 644
08716d4f 645 ldi temp2,CMD17
64219415 646 rcall mmcCmd
08716d4f 647 ldi temp2,RES_ERROR
64219415
FZ
648 brne mmc_rdexw
649
650; Receive a data packet from MMC
651
652 ldiw y,512 ;Number of bytes to tranfer
08716d4f
L
653 ldi temp,200 ;Wait for data packet in timeout of 200ms.
654 sts delay_timer1,temp
64219415
FZ
655mmc_rcvw_wl:
656 rcall spi_rcvr
08716d4f 657 cp temp,_255
64219415 658 brne mmc_rcvw_start
08716d4f
L
659 lds temp2,delay_timer1
660 cpi temp2,0
64219415
FZ
661 brne mmc_rcvw_wl
662mmc_rcvw_start:
08716d4f
L
663 cpi temp,0xFE ;If not valid data token,
664 ldi temp2,RES_ERROR
64219415
FZ
665 brne mmc_rdexw
666
667 rcall spi_rcvr ;Shift in first byte.
08716d4f 668 out SPDR,_255 ;Start shift in next byte.
64219415
FZ
669mmc_rcvw_rl:
670 sbiw yl,1
671 breq mmc_rcvw_rle
08716d4f
L
672 cp zl,_0
673 cpc zh,_0
92202636
L
674 breq mmc_rcvw_sto
675
676 sbiw zl,1
64219415 677 spi_waitm
08716d4f
L
678 in temp,SPDR
679 out SPDR,_255
64219415
FZ
680 rjmp mmc_rcvw_rl
681
92202636
L
682mmc_rcvw_sto:
683 mov zl,temp
678fc0b0 684 spi_waitm
08716d4f
L
685 in temp,SPDR
686 out SPDR,_255
92202636
L
687 mov zh,temp
688
678fc0b0
FZ
689mmc_rcvw_rl2:
690 sbiw yl,1
691 breq mmc_rcvw_rle
692 spi_waitm
08716d4f
L
693 in temp,SPDR
694 out SPDR,_255
92202636 695 rjmp mmc_rcvw_rl2
64219415 696mmc_rcvw_rle:
64219415
FZ
697 rcall spi_wait ; while SPI module shifts in crc part1.
698 rcall spi_rcvr ;Read second crc.
699
08716d4f 700 ldi temp2,RES_OK ;Return success
64219415
FZ
701mmc_rdexw:
702 rcall mmcDeselect
703 spi_disable
704 mov temp,temp2
705.if MMC_DEBUG > 1
08716d4f 706 printstring "RdWordRes: "
64219415 707 rcall printhex
08716d4f 708 printstring " "
64219415 709.endif
92202636 710 ret
9c15f366
L
711
712;--------------------------------------------------------------
713; Write sector
714; z: Pointer to the data to be written
715; yh..xl: Sector number (LBA)
716
717mmcWriteSect:
718.if MMC_DEBUG > 1
719 printnewline
720 printstring "mmcWrSect "
721.endif
722 ldiw z,hostbuf ;for now
723
724 lds _tmp0,mmcStat
725 ldi temp,RES_NOTRDY
726 sbrc _tmp0,MMCST_NOINIT
727 ret
728
729 spi_clkfast
730 lds temp,mmcCardType
731 sbrs temp,log2(CT_BLOCK)
732 rcall mul_yx_512 ;Convert to byte address (*512)
733
734 ldi temp2,CMD24
735 rcall mmcCmd
736 brne mmc_wrexer
737
738; Send a data packet to MMC
739
740.if MMC_DEBUG > 2
741; printnewline
742 printstring "mmcXMIT "
743.endif
744 rcall mmcWaitReady
745 breq mmc_wrexer
746
747 ldi temp,0xFE ;Data token
748 out SPDR,temp
749 ldiw y,512
750mmc_x_loop:
751 ld temp,z+
752 spi_waitm
753 out SPDR,temp
754 sbiw yl,1
755 brne mmc_x_loop
756
757 rcall spi_wait
758 ldi temp,0xFF ;dummy crc
759 rcall spi_xmit
760 rcall spi_xmit
761 rcall spi_rcvr
762.if MMC_DEBUG > 2
763 printstring "XMITRes: "
764 rcall printhex
765 printstring " "
766.endif
767 andi temp,0x1F ;If not accepted, return with error
768 cpi temp,0x05
769 ldi temp2,RES_OK ;Return success
770 breq mmc_wrex
771
772mmc_wrexer:
773 ldi temp,RES_ERROR
774mmc_wrex:
775 rcall mmcDeselect
776 spi_disable
777 mov temp,temp2
778.if MMC_DEBUG > 1
779 printstring "WrSectRes: "
780 rcall printhex
781 printstring " "
782.endif
783 ret
784
785;--------------------------------------------------------------
786; vim:set ts=8 noet nowrap
787