Teacup_Firmware/pff_diskio.c

256 lines
7.2 KiB
C

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2014 */
/* */
/* ... completed by ... */
/* */
/* PFF - Low level disk control module for AVR (C)ChaN, 2014 */
/* File avr_mmcp.c, part of avr/ in pffsample, a package with sample */
/* implementations on http://elm-chan.org/fsw/ff/00index_p.html */
/*-----------------------------------------------------------------------*/
/* Changes for Teacup see pff.c. */
#include "pff_diskio.h"
#ifdef SD
#include <stdlib.h>
#include "spi.h"
#include "delay.h"
/* Definitions for MMC/SDC command. */
#define CMD0 (0x40 + 0) /* GO_IDLE_STATE */
#define CMD1 (0x40 + 1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0xC0 + 41) /* SEND_OP_COND (SDC) */
#define CMD8 (0x40 + 8) /* SEND_IF_COND */
#define CMD16 (0x40 + 16) /* SET_BLOCKLEN */
#define CMD17 (0x40 + 17) /* READ_SINGLE_BLOCK */
#define CMD24 (0x40 + 24) /* WRITE_BLOCK */
#define CMD55 (0x40 + 55) /* APP_CMD */
#define CMD58 (0x40 + 58) /* READ_OCR */
/* Card type flags (card_type). */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_BLOCK 0x08 /* Block addressing */
static BYTE card_type; /* Card type flags. */
/** Send a command packet to MMC/SD card.
\param cmd Command.
\param arg Argument (32 bit).
\return response byte (bit 7 == 1: send failed).
*/
static BYTE send_cmd(BYTE cmd, DWORD arg) {
BYTE n, res;
if (cmd & 0x80) { /* ACMD<n> is the command sequence of CMD55-CMD<n>. */
cmd &= 0x7F;
res = send_cmd(CMD55, 0);
if (res > 1)
return res;
}
/* Select the card */
spi_deselect_sd();
spi_rw(0xFF);
spi_select_sd();
spi_rw(0xFF);
/* Send command packet */
spi_rw(cmd); /* Start + Command index */
spi_rw((BYTE)(arg >> 24)); /* Argument[31..24] */
spi_rw((BYTE)(arg >> 16)); /* Argument[23..16] */
spi_rw((BYTE)(arg >> 8)); /* Argument[15..8] */
spi_rw((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) + Stop */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) Stop */
spi_rw(n);
/* Receive command response */
n = 10; /* Wait for a response, try 10 times. */
do {
res = spi_rw(0xFF);
n--;
} while ((res & 0x80) && n);
return res;
}
/** Initialize disk drive.
\return 0 on success, else STA_NOINIT.
Initialize disk drive and also find out which kind of card we're talking to.
Description about what's going on here see
http://elm-chan.org/docs/mmc/mmc_e.html, center section.
*/
DSTATUS disk_initialize(void) {
BYTE n, cmd, ty, ocr[4];
UINT timeout;
spi_deselect_sd();
spi_speed_100_400();
for (n = 10; n; n--) /* 80 dummy clocks with CS = high. */
spi_rw(0xFF);
/* Find card type: MMCv3, SDv1 or SDv2. */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Software reset, enter idle state. */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? (rejected on others) */
for (n = 0; n < 4; n++) /* Get trailing return value of R7. */
ocr[n] = spi_rw(0xFF);
if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
/* The card can work at Vdd range of 2.7 - 3.6V. */
/* Wait for leaving idle state (ACMD41 with HCS bit). */
for (timeout = 10000; timeout && send_cmd(ACMD41, 1UL << 30); timeout--)
delay_us(100);
/* Find out wether it's a block device (CCS bit in OCR). */
if (timeout && send_cmd(CMD58, 0) == 0) {
for (n = 0; n < 4; n++)
ocr[n] = spi_rw(0xFF);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
}
else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
}
else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
/* Wait for leaving idle state. */
for (timeout = 10000; timeout && send_cmd(cmd, 0); timeout--)
delay_us(100);
/* Set R/W block length to 512. */
if ( ! timeout || send_cmd(CMD16, 512) != 0)
ty = 0;
}
}
card_type = ty;
spi_deselect_sd();
spi_rw(0xFF);
return ty ? 0 : STA_NOINIT;
}
/** Read partial sector.
\param buffer Received data should go in here.
\param sector Sector number (LBA).
\param offset Offset into the sector.
\param count Byte count (bit 15: destination).
\return RES_OK on success, else RES_ERROR.
This is the main reading function. Forming this into directory listings,
file reads and such stuff is done by Petit FatFs it's self.
Description about what's going on here see
http://elm-chan.org/docs/mmc/mmc_e.html, bottom section.
*/
#if _USE_READ
DRESULT disk_readp(BYTE* buffer, DWORD sector, UINT offset, UINT count) {
DRESULT result;
BYTE token = 0xFF;
uint16_t timeout;
UINT trailing = 514 - offset - count; /* 514 = block size + 2 bytes CRC */
/* Convert to byte address on non-block cards. */
if ( ! (card_type & CT_BLOCK))
sector *= 512;
/* Read one sector, copy only as many bytes as required. */
result = RES_ERROR;
spi_speed_max();
if (send_cmd(CMD17, sector) == 0) { /* Read single block. */
/* Wait for data packet. */
for (timeout = 40000; timeout && (token == 0xFF); timeout--)
token = spi_rw(0xFF);
if (token == 0xFE) { /* Valid data arrived. */
/* Skip leading bytes. */
while (offset--)
spi_rw(0xFF);
/* Receive the requested part of the sector. */
do {
/**
Note that this isn't optimised for performance. The increment and
the dereferencing could be done while waiting for the SPIF bit
to become set in spi_rw(). See http://www.matuschek.net/atmega-spi/.
*/
*buffer++ = spi_rw(0xFF);
} while (--count);
/* Skip trailing bytes and CRC. */
while (trailing--)
spi_rw(0xFF);
result = RES_OK;
}
}
spi_deselect_sd(); /* Every send_cmd() selects. */
spi_rw(0xFF);
return result;
}
#endif /* _USE_READ */
/** Write partial pector.
\param buff Pointer to the data to be written.
NULL: Initiate/Finalize write operation.
\param sc Sector number (LBA) or number of bytes to send.
\return RES_OK on success, else RES_ERROR.
This is the main writing function. Forming this into file writes and such
stuff is done by Petit FatFs it's self.
Description about what's going on here see
http://elm-chan.org/docs/mmc/mmc_e.html, bottom section.
*/
#if _USE_WRITE
DRESULT disk_writep(const BYTE* buff, DWORD sc) {
#error disk_writep() not yet implemented
DRESULT res;
if ( ! buff) {
if (sc) {
// Initiate write process
}
else {
// Finalize write process
}
}
else {
// Send data to the disk
}
return res;
}
#endif /* _USE_WRITE */
#endif /* SD */