/* -*- c -*- */
/*
* Copyright 2008 Ettus Research LLC
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see .
*/
#include "sd.h"
#include "memory_map.h"
#include "stdint.h"
#include "stdio.h"
static inline void
sd_packarg(unsigned char *argument,unsigned int value)
{
argument[3] = (unsigned char)(value >> 24);
argument[2] = (unsigned char)(value >> 16);
argument[1] = (unsigned char)(value >> 8);
argument[0] = (unsigned char)(value);
}
int
sd_init(void)
{
unsigned char response[5];
unsigned char argument[4];
int i,j;
for(i=0;i<4;i++)
argument[i] = 0;
// Set clock to less than 400 kHz to start out
sdspi_regs->clkdiv = 128;
// Delay at least 74 cycles
sd_assert_cs();
for(i = 0; i < 100; i++)
sd_send_byte(SD_IDLE);
sd_deassert_cs();
// Initialization Sequence -- CMD0 CMD55 ACMD41 CMD58
// Put card in idle state
if(sd_send_command(SD_CMD0,SD_CMD0_R,response,argument)==0)
return 0; // Something went wrong in command
j = 0;
do {
j++;
if(sd_send_command(SD_CMD55,SD_CMD55_R,response,argument)==1)
sd_send_command(SD_ACMD41,SD_ACMD41_R,response,argument);
else
j = SD_IDLE_WAIT_MAX;
}
while(((response[0] & SD_MSK_IDLE) == SD_MSK_IDLE) && (j < SD_IDLE_WAIT_MAX));
if(j>= SD_IDLE_WAIT_MAX) // IDLE timeout exceeded, card asleep
return 0;
// CMD58 reads the SD card capabilities
if(sd_send_command(SD_CMD58,SD_CMD58_R,response,argument)==0)
return 0; // CMD58 FAIL
if((response[2] & SD_MSK_OCR_33) != SD_MSK_OCR_33)
return 0; // Card doesn't do 3.3V
//printf("OCR = %x %x %x %x\n",response[0],response[1],response[2],response[3]);
// Set blocklen here
sd_packarg(argument,SD_BLOCKLEN);
if(sd_send_command(SD_CMD16,SD_CMD16_R,response,argument)==0)
return 0; // Set Blocklen failed
// Reset back to high speed
sdspi_regs->clkdiv = 4;
//puts("finished init\n");
return 1;
}
int sd_send_command(unsigned char cmd,unsigned char response_type,
unsigned char *response,unsigned char *argument)
{
int i;
char response_length;
unsigned char tmp;
sd_assert_cs();
sd_send_byte((cmd & 0x3F) | 0x40);
for(i=3;i>=0;i--)
sd_send_byte(argument[i]);
sd_send_byte(SD_CRC); // Always the same
response_length = 0;
switch(response_type)
{
case SD_R1:
case SD_R1B:
response_length = 1;
break;
case SD_R2:
response_length = 2;
break;
case SD_R3:
response_length = 5;
break;
default:
break;
}
// Wait for a response, which will have a 0 start bit
i = 0;
do
{
tmp = sd_rcv_byte();
i++;
}
while(((tmp & 0x80) != 0) && i < SD_CMD_TIMEOUT);
if(i>= SD_CMD_TIMEOUT)
{
sd_deassert_cs();
//puts("cmd send timeout\n");
return 0;
}
for(i=response_length-1; i>=0; i--)
{
response[i] = tmp;
tmp = sd_rcv_byte();
}
i = 0;
if(response_type == SD_R1B)
{
do
{
i++;
tmp = sd_rcv_byte();
}
while(tmp != SD_IDLE);
sd_send_byte(SD_IDLE);
}
//puts("send cmd success\n");
sd_deassert_cs();
return 1;
}
int
sd_read_block (unsigned int blockaddr, unsigned char *buf)
{
unsigned char response[5];
unsigned char argument[4];
unsigned int i = 0;
unsigned char tmp;
blockaddr <<= SD_BLOCKLEN_NBITS;
sd_packarg(argument,blockaddr);
if(sd_send_command(SD_CMD17,SD_CMD17_R,response,argument)==0)
return 0; //Failed READ;
if(response[0] != 0)
return 0; //Misaligned READ
sd_assert_cs();
i = 0;
do
{
tmp = sd_rcv_byte();
i++;
}
while((tmp == 0xFF) && (i < SD_RD_TIMEOUT));
if((i>= SD_RD_TIMEOUT) ||((tmp & SD_MSK_TOK_DATAERROR) == 0))
{
sd_send_byte(SD_IDLE); // Send a dummy before quitting
return 0; // Data ERROR
}
for(i=0;i