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