sd2snes/mk1-src/sdcard.c
2010-09-06 23:51:48 +02:00

774 lines
19 KiB
C

/* sd2iec - SD/MMC to Commodore serial bus interface/controller
Copyright (C) 2007-2009 Ingo Korb <ingo@akana.de>
Inspiration and low-level SD/MMC access based on code from MMC2IEC
by Lars Pontoppidan et al., see sdcard.c|h and config.h.
FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
sdcard.c: SD/MMC access routines
Extended, optimized and cleaned version of code from MMC2IEC,
original copyright header follows:
//
// Title : SD/MMC Card driver
// Author : Lars Pontoppidan, Aske Olsson, Pascal Dufour,
// Date : Jan. 2006
// Version : 0.42
// Target MCU : Atmel AVR Series
//
// CREDITS:
// This module is developed as part of a project at the technical univerisity of
// Denmark, DTU.
//
// DESCRIPTION:
// This SD card driver implements the fundamental communication with a SD card.
// The driver is confirmed working on 8 MHz and 14.7456 MHz AtMega32 and has
// been tested successfully with a large number of different SD and MMC cards.
//
// DISCLAIMER:
// The author is in no way responsible for any problems or damage caused by
// using this code. Use at your own risk.
//
// LICENSE:
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
The exported functions in this file are weak-aliased to their corresponding
versions defined in diskio.h so when this file is the only diskio provider
compiled in they will be automatically used by the linker.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/crc16.h>
#include <util/delay.h>
#include "config.h"
#include "avrcompat.h"
#include "crc7.h"
#include "diskio.h"
#include "spi.h"
#include "uart.h"
#include "sdcard.h"
#include "fpga_spi.h"
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef CONFIG_TWINSD
# define MAX_CARDS 2
#else
# define MAX_CARDS 1
#endif
// SD/MMC commands
#define GO_IDLE_STATE 0
#define SEND_OP_COND 1
#define SWITCH_FUNC 6
#define SEND_IF_COND 8
#define SEND_CSD 9
#define SEND_CID 10
#define STOP_TRANSMISSION 12
#define SEND_STATUS 13
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define READ_MULTIPLE_BLOCK 18
#define WRITE_BLOCK 24
#define WRITE_MULTIPLE_BLOCK 25
#define PROGRAM_CSD 27
#define SET_WRITE_PROT 28
#define CLR_WRITE_PROT 29
#define SEND_WRITE_PROT 30
#define ERASE_WR_BLK_STAR_ADDR 32
#define ERASE_WR_BLK_END_ADDR 33
#define ERASE 38
#define LOCK_UNLOCK 42
#define APP_CMD 55
#define GEN_CMD 56
#define READ_OCR 58
#define CRC_ON_OFF 59
// SD ACMDs
#define SD_STATUS 13
#define SD_SEND_NUM_WR_BLOCKS 22
#define SD_SET_WR_BLK_ERASE_COUNT 23
#define SD_SEND_OP_COND 41
#define SD_SET_CLR_CARD_DETECT 42
#define SD_SEND_SCR 51
// R1 status bits
#define STATUS_IN_IDLE 1
#define STATUS_ERASE_RESET 2
#define STATUS_ILLEGAL_COMMAND 4
#define STATUS_CRC_ERROR 8
#define STATUS_ERASE_SEQ_ERROR 16
#define STATUS_ADDRESS_ERROR 32
#define STATUS_PARAMETER_ERROR 64
/* Card types - cardtype == 0 is MMC */
#define CARD_SD (1<<0)
#define CARD_SDHC (1<<1)
static uint8_t cardtype[MAX_CARDS];
/**
* getbits - read value from bit buffer
* @buffer: pointer to the data buffer
* @start : index of the first bit in the value
* @bits : number of bits in the value
*
* This function returns a value from the memory region passed as
* buffer, starting with bit "start" and "bits" bit long. The buffer
* is assumed to be MSB first, passing 0 for start will read starting
* from the highest-value bit of the first byte of the buffer.
*/
static uint32_t getbits(void *buffer, uint16_t start, int8_t bits) {
uint8_t *buf = buffer;
uint32_t result = 0;
if ((start % 8) != 0) {
/* Unaligned start */
result += buf[start / 8] & (0xff >> (start % 8));
bits -= 8 - (start % 8);
start += 8 - (start % 8);
}
while (bits >= 8) {
result = (result << 8) + buf[start / 8];
start += 8;
bits -= 8;
}
if (bits > 0) {
result = result << bits;
result = result + (buf[start / 8] >> (8-bits));
} else if (bits < 0) {
/* Fraction of a single byte */
result = result >> -bits;
}
return result;
}
static uint8_t sdResponse(uint8_t expected)
{
unsigned short count = 0x0FFF;
while ((spiTransferByte(0xFF) != expected) && count )
count--;
// If count didn't run out, return success
return (count != 0);
}
static uint8_t sdWaitWriteFinish(void)
{
uint32_t count = 0x1FFFF; // wait for quite some time
while ((spiTransferByte(0xFF) == 0) && count )
count--;
// If count didn't run out, return success
return (count != 0);
}
static void deselectCard(uint8_t card) {
// Send 8 clock cycles
SPI_SS_HIGH(card);
spiTransferByte(0xff);
}
/**
* sendCommand - send a command to the SD card
* @card : card number to be accessed
* @command : command to be sent
* @parameter: parameter to be sent
* @deselect : Flags if the card should be deselected afterwards
*
* This function calculates the correct CRC7 for the command and
* parameter and transmits all of it to the SD card. If requested
* the card will be deselected afterwards.
*/
static int sendCommand(const uint8_t card,
const uint8_t command,
const uint32_t parameter,
const uint8_t deselect) {
union {
uint32_t l;
uint8_t c[4];
} long2char;
uint8_t i,crc=0,errorcount;
uint16_t counter;
long2char.l = parameter;
crc = crc7update(0 , 0x40+command);
crc = crc7update(crc, long2char.c[3]);
crc = crc7update(crc, long2char.c[2]);
crc = crc7update(crc, long2char.c[1]);
crc = crc7update(crc, long2char.c[0]);
crc = (crc << 1) | 1;
errorcount = 0;
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
// Select card
SPI_SS_LOW(card);
#ifdef CONFIG_TWINSD
if (card == 0 && command == GO_IDLE_STATE)
/* Force both cards to SPI mode simultaneously */
SPI_SS_LOW(1);
#endif
// Transfer command
spiTransferByte(0x40+command);
spiTransferLong(parameter);
spiTransferByte(crc);
// Wait for a valid response
counter = 0;
do {
i = spiTransferByte(0xff);
counter++;
} while (i & 0x80 && counter < 0x1000);
#ifdef CONFIG_TWINSD
if (card == 0 && command == GO_IDLE_STATE)
SPI_SS_HIGH(1);
#endif
// Check for CRC error
// can't reliably retry unless deselect is allowed
if (deselect && (i & STATUS_CRC_ERROR)) {
uart_putc('x');
deselectCard(card);
errorcount++;
continue;
}
if (deselect) deselectCard(card);
break;
}
return i;
}
// Extended init sequence for SDHC support
static uint8_t extendedInit(const uint8_t card) {
uint8_t i;
uint32_t answer;
// Send CMD8: SEND_IF_COND
// 0b000110101010 == 2.7-3.6V supply, check pattern 0xAA
i = sendCommand(card, SEND_IF_COND, 0b000110101010, 0);
if (i > 1) {
// Card returned an error, ok (MMC oder SD1.x) but not SDHC
deselectCard(card);
return TRUE;
}
// No error, continue SDHC initialization
answer = spiTransferLong(0);
deselectCard(card);
if (((answer >> 8) & 0x0f) != 0b0001) {
// Card didn't accept our voltage specification
return FALSE;
}
// Verify echo-back of check pattern
if ((answer & 0xff) != 0b10101010) {
// Check pattern mismatch, working but not SD2.0 compliant
// The specs say we should not use the card, but let's try anyway.
return TRUE;
}
return TRUE;
}
// SD common initialisation
static void sdInit(const uint8_t card) {
uint8_t i;
uint16_t counter;
SD_SPI_OFFLOAD = 0;
counter = 0xffff;
do {
// Prepare for ACMD, send CMD55: APP_CMD
i = sendCommand(card, APP_CMD, 0, 1);
if (i > 1)
// Command not accepted, could be MMC
return;
// Send ACMD41: SD_SEND_OP_COND
// 1L<<30 == Host has High Capacity Support
i = sendCommand(card, SD_SEND_OP_COND, 1L<<30, 1);
// Repeat while card card accepts command but isn't ready
} while (i == 1 && --counter > 0);
// Ignore failures, there is at least one Sandisk MMC card
// that accepts CMD55, but not ACMD41.
if (i == 0)
/* We know that a card is SD if ACMD41 was accepted. */
cardtype[card] |= CARD_SD;
}
/* Detect changes of SD card 0 */
#ifdef SD_CHANGE_VECT
ISR(SD_CHANGE_VECT) {
if (SDCARD_DETECT)
disk_state = DISK_CHANGED;
else
disk_state = DISK_REMOVED;
}
#endif
#ifdef CONFIG_TWINSD
/* Detect changes of SD card 1 */
ISR(SD2_CHANGE_VECT) {
if (SD2_DETECT)
disk_state = DISK_CHANGED;
else
disk_state = DISK_REMOVED;
}
#endif
//
// Public functions
//
void sd_init(void) {
SDCARD_DETECT_SETUP();
SDCARD_WP_SETUP();
SD_CHANGE_SETUP();
#ifdef CONFIG_TWINSD
/* Initialize the control lines for card 2 */
SD2_SETUP();
SD2_CHANGE_SETUP();
#endif
}
void disk_init(void) __attribute__ ((weak, alias("sd_init")));
DSTATUS sd_status(BYTE drv) {
#ifdef CONFIG_TWINSD
if (drv != 0) {
if (SD2_DETECT) {
if (SD2_PIN & SD2_WP) {
return STA_PROTECT;
} else {
return RES_OK;
}
} else {
return STA_NOINIT|STA_NODISK;
}
} else
#endif
if (SDCARD_DETECT) {
// uart_putc('0');
if (SDCARD_WP) {
// uart_putc('1');
return STA_PROTECT;
} else {
// uart_putc('2');
return RES_OK;
}
} else {
// uart_putc('3');
return STA_NOINIT|STA_NODISK;
}
}
DSTATUS disk_status(BYTE drv) __attribute__ ((weak, alias("sd_status")));
/**
* sd_initialize - initialize SD card
* @drv : drive
*
* This function tries to initialize the selected SD card.
*/
DSTATUS sd_initialize(BYTE drv) {
uint8_t i;
uint16_t counter;
uint32_t answer;
if (drv >= MAX_CARDS)
return STA_NOINIT|STA_NODISK;
/* Don't bother initializing a card that isn't there */
// uart_putc('#');
if (sd_status(drv) & STA_NODISK)
return sd_status(drv);
/* JLB: Should be in sd_init, but some uIEC versions have
* IEC lines tied to SPI, so I moved it here to resolve the
* conflict.
*/
spiInit();
disk_state = DISK_ERROR;
cardtype[drv] = 0;
SPI_SS_HIGH(drv);
// Send 80 clks
for (i=0; i<10; i++) {
spiTransferByte(0xFF);
}
// Reset card
i = sendCommand(drv, GO_IDLE_STATE, 0, 1);
if (i != 1) {
return STA_NOINIT | STA_NODISK;
}
if (!extendedInit(drv))
return STA_NOINIT | STA_NODISK;
sdInit(drv);
counter = 0xffff;
// According to the spec READ_OCR should work at this point
// without retries. One of my Sandisk-cards thinks otherwise.
do {
// Send CMD58: READ_OCR
i = sendCommand(drv, READ_OCR, 0, 0);
if (i > 1)
deselectCard(drv);
} while (i > 1 && counter-- > 0);
if (counter > 0) {
answer = spiTransferLong(0);
// See if the card likes our supply voltage
if (!(answer & SD_SUPPLY_VOLTAGE)) {
// The code isn't set up to completely ignore the card,
// but at least report it as nonworking
deselectCard(drv);
return STA_NOINIT | STA_NODISK;
}
// See what card we've got
if (answer & 0x40000000) {
cardtype[drv] |= CARD_SDHC;
}
}
// Keep sending CMD1 (SEND_OP_COND) command until zero response
counter = 0xffff;
do {
i = sendCommand(drv, SEND_OP_COND, 1L<<30, 1);
counter--;
} while (i != 0 && counter > 0);
if (counter==0) {
return STA_NOINIT | STA_NODISK;
}
#ifdef CONFIG_SD_DATACRC
// Enable CRC checking
// The SD spec says that the host "should" send CRC_ON_OFF before ACMD_SEND_OP_COND.
// The MMC manual I have says that CRC_ON_OFF isn't allowed before SEND_OP_COND.
// Let's just hope that all SD cards work with this order. =(
i = sendCommand(drv, CRC_ON_OFF, 1, 1);
if (i > 1) {
return STA_NOINIT | STA_NODISK;
}
#endif
// Send MMC CMD16(SET_BLOCKLEN) to 512 bytes
i = sendCommand(drv, SET_BLOCKLEN, 512, 1);
if (i != 0) {
return STA_NOINIT | STA_NODISK;
}
// Thats it!
disk_state = DISK_OK;
return sd_status(drv);
}
DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sd_initialize")));
/**
* sd_read - reads sectors from the SD card to buffer
* @drv : drive
* @buffer: pointer to the buffer
* @sector: first sector to be read
* @count : number of sectors to be read
*
* This function reads count sectors from the SD card starting
* at sector to buffer. Returns RES_ERROR if an error occured or
* RES_OK if successful. Up to SD_AUTO_RETRIES will be made if
* the calculated data CRC does not match the one sent by the
* card. If there were errors during the command transmission
* disk_state will be set to DISK_ERROR and no retries are made.
*/
DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) {
uint8_t sec,res,tmp,errorcount;
uint16_t crc,recvcrc;
if (drv >= MAX_CARDS)
return RES_PARERR;
for (sec=0;sec<count;sec++) {
errorcount = 0;
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
if (cardtype[drv] & CARD_SDHC)
res = sendCommand(drv, READ_SINGLE_BLOCK, sector+sec, 0);
else
res = sendCommand(drv, READ_SINGLE_BLOCK, (sector+sec) << 9, 0);
if (res != 0) {
uart_putc('?');
dprintf("SD error: %02x\n", res);
SPI_SS_HIGH(drv);
disk_state = DISK_ERROR;
return RES_ERROR;
}
// Wait for data token
if (!sdResponse(0xFE)) {
uart_putc('-');
SPI_SS_HIGH(drv);
disk_state = DISK_ERROR;
return RES_ERROR;
}
uint16_t i;
#ifdef CONFIG_SD_DATACRC
BYTE *oldbuffer = buffer;
#endif
// Get data
crc = 0;
if(SD_SPI_OFFLOAD) {
// uart_putc('O');
PORTB |= _BV(PB2);
DDRB |= _BV(PB2);
_delay_us(1);
PORTB &= ~_BV(PB2);
DDRB &= ~_BV(PB7); // tristate SCK
PORTB |= _BV(PB2);
DDRB &= ~_BV(PB2);
_delay_us(1);
while(!(PINB & _BV(PB2)));
DDRB |= _BV(PB7);
DDRB |= _BV(PB2);
// _delay_us(1);
deselectCard(drv);
SD_SPI_OFFLOAD = 0;
return RES_OK;
SPDR = 0xff;
} else {
// Initiate data exchange over SPI
SPDR = 0xff;
for (i=0; i<512; i++) {
// Wait until data has been received
loop_until_bit_is_set(SPSR, SPIF);
tmp = SPDR;
// Transmit the next byte while we store the current one
SPDR = 0xff;
*(buffer++) = tmp;
#ifdef CONFIG_SD_DATACRC
crc = _crc_xmodem_update(crc, tmp);
#endif
}
}
// Wait until the first CRC byte is received
loop_until_bit_is_set(SPSR, SPIF);
// Check CRC
recvcrc = (SPDR << 8) + spiTransferByte(0xff);
#ifdef CONFIG_SD_DATACRC
if (recvcrc != crc) {
uart_putc('X');
deselectCard(drv);
errorcount++;
buffer = oldbuffer;
continue;
}
#endif
break;
}
deselectCard(drv);
if (errorcount >= CONFIG_SD_AUTO_RETRIES) return RES_ERROR;
}
return RES_OK;
}
DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_read")));
/**
* sd_write - writes sectors from buffer to the SD card
* @drv : drive
* @buffer: pointer to the buffer
* @sector: first sector to be written
* @count : number of sectors to be written
*
* This function writes count sectors from buffer to the SD card
* starting at sector. Returns RES_ERROR if an error occured,
* RES_WPRT if the card is currently write-protected or RES_OK
* if successful. Up to SD_AUTO_RETRIES will be made if the card
* signals a CRC error. If there were errors during the command
* transmission disk_state will be set to DISK_ERROR and no retries
* are made.
*/
DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) {
uint8_t res,sec,errorcount,status;
uint16_t crc;
if (drv >= MAX_CARDS)
return RES_PARERR;
#ifdef CONFIG_TWINSD
if (drv != 0) {
if (SD2_PIN & SD2_WP)
return RES_WRPRT;
} else
#endif
if (SDCARD_WP) return RES_WRPRT;
for (sec=0;sec<count;sec++) {
errorcount = 0;
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
if (cardtype[drv] & CARD_SDHC)
res = sendCommand(drv, WRITE_BLOCK, sector+sec, 0);
else
res = sendCommand(drv, WRITE_BLOCK, (sector+sec)<<9, 0);
if (res != 0) {
uart_putc('C');
SPI_SS_HIGH(drv);
disk_state = DISK_ERROR;
return RES_ERROR;
}
// Send data token
spiTransferByte(0xFE);
uint16_t i;
const BYTE *oldbuffer = buffer;
// Send data
crc = 0;
for (i=0; i<512; i++) {
#ifdef CONFIG_SD_DATACRC
crc = _crc_xmodem_update(crc, *buffer);
#endif
spiTransferByte(*(buffer++));
}
// Send CRC
spiTransferByte(crc >> 8);
spiTransferByte(crc & 0xff);
// Get and check status feedback
status = spiTransferByte(0xFF);
// Retry if neccessary
if ((status & 0x0F) != 0x05) {
uart_putc('X');
deselectCard(drv);
errorcount++;
buffer = oldbuffer;
continue;
}
// Wait for write finish
if (!sdWaitWriteFinish()) {
uart_putc('W');
SPI_SS_HIGH(drv);
disk_state = DISK_ERROR;
return RES_ERROR;
}
break;
}
deselectCard(drv);
if (errorcount >= CONFIG_SD_AUTO_RETRIES) {
if (!(status & STATUS_CRC_ERROR))
disk_state = DISK_ERROR;
return RES_ERROR;
}
}
return RES_OK;
}
DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_write")));
DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) {
uint8_t i;
uint8_t buf[18];
uint32_t capacity;
if (drv >= MAX_CARDS)
return RES_NOTRDY;
if (sd_status(drv) & STA_NODISK)
return RES_NOTRDY;
if (page != 0)
return RES_ERROR;
/* Try to calculate the total number of sectors on the card */
/* FIXME: Write a generic data read function and merge with sd_read */
if (sendCommand(drv, SEND_CSD, 0, 0) != 0) {
deselectCard(drv);
return RES_ERROR;
}
/* Wait for data token */
if (!sdResponse(0xfe)) {
deselectCard(drv);
return RES_ERROR;
}
for (i=0;i<18;i++) {
buf[i] = spiTransferByte(0xff);
}
deselectCard(drv);
if (cardtype[drv] & CARD_SDHC) {
/* Special CSD for SDHC cards */
capacity = (1 + getbits(buf,127-69,22)) * 1024;
} else {
/* Assume that MMC-CSD 1.0/1.1/1.2 and SD-CSD 1.1 are the same... */
uint8_t exponent = 2 + getbits(buf, 127-49, 3);
capacity = 1 + getbits(buf, 127-73, 12);
exponent += getbits(buf, 127-83,4) - 9;
while (exponent--) capacity *= 2;
}
diskinfo0_t *di = buffer;
di->validbytes = sizeof(diskinfo0_t);
di->disktype = DISK_TYPE_SD;
di->sectorsize = 2;
di->sectorcount = capacity;
return RES_OK;
}
DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sd_getinfo")));