]> cloudbase.mooo.com Git - avrcpm.git/blame - avr/mmc.asm
* Tidied up configuration
[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:
319 ldi temp,10
320 mov _tmp1,temp
321mmc_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
330mmc_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
08716d4f
L
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
346mmcCmd_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
357mmc_cocr1:
358 rcall spi_rcvr
359 st z+,temp
360 dec temp2
361 brne mmc_cocr1
362 ;z-flag =1
363mmc_cocr_e:
364 ret
365
9c15f366
L
366;------------------------------------------------
367; Check if 1 sec timeout
368; return Z-Flag set, if timeout
369
370mmc_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
378mmc_ttex:
379 ret
380
381
382;------------------------------------------------
383; "Public" functions
384;------------------------------------------------
385
386;------------------------------------------------
387; Initialize MMC/SD card
388
389mmcInit:
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
400mmci_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
413mmci_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
08716d4f
L
421 ldi zl,1
422 rcall mmcCmd_ocr
423 brne mmci_sdv1 ;SDv2?
9c15f366 424
08716d4f
L
425 cpi yl,0x01 ;ocr[2]
426 brne mmci_sdv1
427 cpi yh,0xAA ;ocr[3]
9c15f366
L
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
438mmci_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
446mmci_ccc:
447 ldi temp2,CMD58
448 ldi yh,0
08716d4f
L
449 ldi zl,0
450 rcall mmcCmd_ocr
451
9c15f366
L
452 brne mmci_sdv2end
453
9c15f366 454 sbr temp3,CT_SD2
08716d4f 455 sbrc xl,6
9c15f366
L
456 sbr temp3,CT_BLOCK
457
458mmci_sdv2end:
459 rjmp mmci_lend
460
461; SDv1 or MMCv3
462
463mmci_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
473mmci_mmcv3:
474 sbr temp3,CT_MMC ;MMCv3
475 ldi temp2,CMD1
476
477; Wait for leaving idle state
478mmci_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
486mmci_v1_2:
487 ldi temp2,CMD16
488 ldiw x,512
489 rcall mmcCmd
490 breq mmci_lend
491 ldi temp3,0
492
493mmci_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
504mmci_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
527mmcReadSect:
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
554mmc_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
566mmc_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 " "
573mmc_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.
4160d270
L
579.if MMC_DEBUG > 3
580 printnewline
581 rcall printhex
582 printstring " "
583.endif
9c15f366
L
584 out SPDR,_255 ;Start shift in next byte.
585mmc_rcv_rl:
586 sbiw yl,1
587 breq mmc_rcv_rle
588 st z+,temp
589 spi_waitm
590 in temp,SPDR
4160d270
L
591.if MMC_DEBUG > 3
592 rcall printhex
593 printstring " "
594.endif
9c15f366
L
595 out SPDR,_255
596 rjmp mmc_rcv_rl
597
598mmc_rcv_rle:
599 st z+,temp ;Store last byte in buffer
4160d270
L
600.if MMC_DEBUG > 3
601 printnewline
602.endif
08716d4f 603 rcall spi_wait ;while SPI module shifts in crc part1.
9c15f366
L
604 rcall spi_rcvr ;Read second crc.
605
606 ldi temp2,RES_OK ;Return success
607mmc_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
92202636 618
64219415 619;--------------------------------------------------------------
92202636 620; Read word
08716d4f
L
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...
64219415 624;
08716d4f 625; in zh,zl: Pointer to word within the sector to read
64219415 626; in yh..xl: Start sector number (LBA)
08716d4f 627; out zh,zl: Word thats been read
64219415
FZ
628
629mmcReadWord:
630.if MMC_DEBUG > 1
631 printnewline
08716d4f 632 printstring "mmcRdWord "
64219415 633.endif
08716d4f
L
634 lds _tmp0,mmcStat
635 ldi temp,RES_NOTRDY
64219415
FZ
636 sbrc _tmp0,MMCST_NOINIT
637 ret
638
639 spi_clkfast
08716d4f 640 lds temp,mmcCardType
64219415
FZ
641 sbrs temp,log2(CT_BLOCK)
642 rcall mul_yx_512 ;Convert to byte address (*512)
92202636 643
08716d4f 644 ldi temp2,CMD17
64219415 645 rcall mmcCmd
08716d4f 646 ldi temp2,RES_ERROR
64219415
FZ
647 brne mmc_rdexw
648
649; Receive a data packet from MMC
650
651 ldiw y,512 ;Number of bytes to tranfer
08716d4f
L
652 ldi temp,200 ;Wait for data packet in timeout of 200ms.
653 sts delay_timer1,temp
64219415
FZ
654mmc_rcvw_wl:
655 rcall spi_rcvr
08716d4f 656 cp temp,_255
64219415 657 brne mmc_rcvw_start
08716d4f
L
658 lds temp2,delay_timer1
659 cpi temp2,0
64219415
FZ
660 brne mmc_rcvw_wl
661mmc_rcvw_start:
08716d4f
L
662 cpi temp,0xFE ;If not valid data token,
663 ldi temp2,RES_ERROR
64219415
FZ
664 brne mmc_rdexw
665
666 rcall spi_rcvr ;Shift in first byte.
08716d4f 667 out SPDR,_255 ;Start shift in next byte.
64219415
FZ
668mmc_rcvw_rl:
669 sbiw yl,1
670 breq mmc_rcvw_rle
08716d4f
L
671 cp zl,_0
672 cpc zh,_0
92202636
L
673 breq mmc_rcvw_sto
674
675 sbiw zl,1
64219415 676 spi_waitm
08716d4f
L
677 in temp,SPDR
678 out SPDR,_255
64219415
FZ
679 rjmp mmc_rcvw_rl
680
92202636
L
681mmc_rcvw_sto:
682 mov zl,temp
678fc0b0 683 spi_waitm
08716d4f
L
684 in temp,SPDR
685 out SPDR,_255
92202636
L
686 mov zh,temp
687
678fc0b0
FZ
688mmc_rcvw_rl2:
689 sbiw yl,1
690 breq mmc_rcvw_rle
691 spi_waitm
08716d4f
L
692 in temp,SPDR
693 out SPDR,_255
92202636 694 rjmp mmc_rcvw_rl2
64219415 695mmc_rcvw_rle:
64219415
FZ
696 rcall spi_wait ; while SPI module shifts in crc part1.
697 rcall spi_rcvr ;Read second crc.
698
08716d4f 699 ldi temp2,RES_OK ;Return success
64219415
FZ
700mmc_rdexw:
701 rcall mmcDeselect
702 spi_disable
703 mov temp,temp2
704.if MMC_DEBUG > 1
08716d4f 705 printstring "RdWordRes: "
64219415 706 rcall printhex
08716d4f 707 printstring " "
64219415 708.endif
92202636 709 ret
9c15f366
L
710
711;--------------------------------------------------------------
712; Write sector
713; z: Pointer to the data to be written
714; yh..xl: Sector number (LBA)
715
716mmcWriteSect:
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
749mmc_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
771mmc_wrexer:
772 ldi temp,RES_ERROR
773mmc_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