From a6163888f3c56123b1db313743c6147ba498732c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 8 Aug 2014 14:42:07 +0300 Subject: Add third_party libs --- third_party/fatfs/port/sample-mmc.c | 559 ++++++++++++++++++++++++++++++++++++ 1 file changed, 559 insertions(+) create mode 100644 third_party/fatfs/port/sample-mmc.c (limited to 'third_party/fatfs/port/sample-mmc.c') diff --git a/third_party/fatfs/port/sample-mmc.c b/third_party/fatfs/port/sample-mmc.c new file mode 100644 index 0000000..db98971 --- /dev/null +++ b/third_party/fatfs/port/sample-mmc.c @@ -0,0 +1,559 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + + +#include +#include "diskio.h" + + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + + +/* Port Controls (Platform dependent) */ +#define SELECT() PORTB &= ~1 /* MMC CS = L */ +#define DESELECT() PORTB |= 1 /* MMC CS = H */ + +#define SOCKPORT PINB /* Socket contact port */ +#define SOCKWP 0x20 /* Write protect switch (PB5) */ +#define SOCKINS 0x10 /* Card detect switch (PB4) */ + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + + + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +#define xmit_spi(dat) SPDR=(dat); loop_until_bit_is_set(SPSR,SPIF) + + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + SPDR = 0xFF; + loop_until_bit_is_set(SPSR, SPIF); + return SPDR; +} + +/* Alternative macro to receive data fast */ +#define rcvr_spi_m(dst) SPDR=0xFF; loop_until_bit_is_set(SPSR,SPIF); *(dst)=SPDR + + + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + PORTE &= ~0x80; /* Socket power ON */ + for (Timer1 = 3; Timer1; ); /* Wait for 30ms */ + PORTB = 0b10110101; /* Enable drivers */ + DDRB = 0b11000111; + SPCR = 0b01010000; /* Initialize SPI port (Mode 0) */ + SPSR = 0b00000001; +} + +static +void power_off (void) +{ + SELECT(); /* Wait for card ready */ + wait_ready(); + DESELECT(); + rcvr_spi(); + + SPCR = 0; /* Disable SPI function */ + DDRB = 0b11000000; /* Disable drivers */ + PORTB = 0b10110000; + PORTE |= 0x80; /* Socket power OFF */ + Stat |= STA_NOINIT; /* Set STA_NOINIT */ +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return (PORTE & 0x80) ? 0 : 1; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 10; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + + + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + + case MMC_GET_TYPE : /* Get card type flags (1 byte) */ + *ptr = CardType; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ + static BYTE pv; + BYTE n, s; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + + n = pv; + pv = SOCKPORT & (SOCKWP | SOCKINS); /* Sample socket switch */ + + if (n == pv) { /* Have contacts stabled? */ + s = Stat; + + if (pv & SOCKWP) /* WP is H (write protected) */ + s |= STA_PROTECT; + else /* WP is L (write enabled) */ + s &= ~STA_PROTECT; + + if (pv & SOCKINS) /* INS = H (Socket empty) */ + s |= (STA_NODISK | STA_NOINIT); + else /* INS = L (Card inserted) */ + s &= ~STA_NODISK; + + Stat = s; + } +} + -- cgit v1.3.1