mirror of
https://github.com/array-in-a-matrix/SAROO.git
synced 2025-04-02 10:31:43 -04:00
497 lines
9.4 KiB
C
497 lines
9.4 KiB
C
|
|
/*
|
|
* stm32_sdio.c
|
|
* low level driver for STM32F10x SDIO
|
|
* only SD card suport. no MMC support.
|
|
*
|
|
* writen by tpu, 2014-10-28
|
|
*/
|
|
|
|
#include "string.h"
|
|
|
|
#include "main.h"
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
#define SDCLK_LOW 198
|
|
#define SDCLK_HIGH 3 // 24MHz
|
|
//#define SDCLK_HIGH 4 // 18MHz
|
|
//#define SDCLK_HIGH 6 // 12MHz
|
|
|
|
#define RESP_NONE(cmd) ((cmd&0x00c0)==0x0000)
|
|
#define RESP_SHORT(cmd) ((cmd&0x00c0)==0x0040)
|
|
#define RESP_LONG(cmd) ((cmd&0x00c0)==0x00c0)
|
|
#define RESP_NOCRC(cmd) ((cmd&0x0400)!=0x0000)
|
|
|
|
#define HAS_DATA_R(cmd) ((cmd&0x0100)==0x0100)
|
|
#define HAS_DATA_W(cmd) ((cmd&0x0200)==0x0200)
|
|
#define HAS_DATA(cmd) ((cmd&0x0300)!=0x0000)
|
|
#define NO_DATA(cmd) ((cmd&0x0300)==0x0000)
|
|
|
|
#define CMD_RESP_NONE 0x0000
|
|
#define CMD_RESP_SHORT 0x0040
|
|
#define CMD_RESP_LONG 0x00c0
|
|
#define CMD_RESP_NOCRC 0x0400
|
|
#define CMD_HAS_DATA_R 0x0100
|
|
#define CMD_HAS_DATA_W 0x0200
|
|
|
|
#define CMD_00 ( 0 | CMD_RESP_NONE)
|
|
#define CMD_02 ( 2 | CMD_RESP_LONG | CMD_RESP_NOCRC)
|
|
#define CMD_03 ( 3 | CMD_RESP_SHORT)
|
|
#define CMD_06 ( 6 | CMD_RESP_SHORT)
|
|
#define CMD_07 ( 7 | CMD_RESP_SHORT)
|
|
#define CMD_08 ( 8 | CMD_RESP_SHORT)
|
|
#define CMD_09 ( 9 | CMD_RESP_LONG | CMD_RESP_NOCRC)
|
|
#define CMD_12 (12 | CMD_RESP_SHORT)
|
|
#define CMD_13 (13 | CMD_RESP_SHORT)
|
|
#define CMD_16 (16 | CMD_RESP_SHORT)
|
|
#define CMD_17 (17 | CMD_RESP_SHORT | CMD_HAS_DATA_R)
|
|
#define CMD_18 (18 | CMD_RESP_SHORT | CMD_HAS_DATA_R)
|
|
#define CMD_23 (23 | CMD_RESP_SHORT)
|
|
#define CMD_24 (24 | CMD_RESP_SHORT | CMD_HAS_DATA_W)
|
|
#define CMD_25 (25 | CMD_RESP_SHORT | CMD_HAS_DATA_W)
|
|
#define CMD_41 (41 | CMD_RESP_SHORT | CMD_RESP_NOCRC)
|
|
#define CMD_55 (55 | CMD_RESP_SHORT)
|
|
|
|
#define CMD_ERROR (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)
|
|
#define DATA_ERROR (SDIO_STA_STBITERR | SDIO_STA_RXOVERR | SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL)
|
|
|
|
/******************************************************************************/
|
|
|
|
static u32 cmd_resp[4];
|
|
static u8 sd_cid[16];
|
|
static u8 sd_csd[16];
|
|
static int is_sdhc;
|
|
static int sd_rca;
|
|
|
|
static int bsize;
|
|
static int csize;
|
|
static int cblock;
|
|
|
|
static OS_SEM cmd_sem;
|
|
|
|
/******************************************************************************/
|
|
|
|
static void sd_clk_set(int div)
|
|
{
|
|
u32 val;
|
|
|
|
val = SDIO->CLKCR;
|
|
val &= 0xfffffd00;
|
|
val |= div;
|
|
if(div!=SDCLK_LOW){
|
|
//val |= 0x0200;
|
|
}
|
|
SDIO->CLKCR = val;
|
|
}
|
|
|
|
static void sd_clk_enable(int en)
|
|
{
|
|
if(en)
|
|
SDIO->CLKCR |= 0x0100;
|
|
else
|
|
SDIO->CLKCR &= ~0x0100;
|
|
}
|
|
|
|
static void sd_bus_width(int width)
|
|
{
|
|
u32 val;
|
|
|
|
val = SDIO->CLKCR;
|
|
val &= 0xffffe7ff;
|
|
if(width==4)
|
|
val |= 0x00000800;
|
|
SDIO->CLKCR = val;
|
|
}
|
|
|
|
static void sd_power_on(int on)
|
|
{
|
|
if(on)
|
|
SDIO->POWER = 0x00000003;
|
|
else
|
|
SDIO->POWER = 0x00000000;
|
|
}
|
|
|
|
static int sd_card_insert(void)
|
|
{
|
|
return (GPIOA->IDR&0x8000)? 0: 1;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void SDIO_IRQHandler(void)
|
|
{
|
|
SDIO->MASK = 0;
|
|
isr_sem_send(&cmd_sem);
|
|
}
|
|
|
|
static int sd_cmd(u32 cmd, u32 arg)
|
|
{
|
|
u32 mask, stat;
|
|
int retv;
|
|
|
|
if(HAS_DATA(cmd)){
|
|
mask = SDIO_STA_DATAEND | DATA_ERROR | CMD_ERROR;
|
|
}else if(RESP_NONE(cmd)){
|
|
mask = SDIO_STA_CMDSENT | CMD_ERROR;
|
|
}else{
|
|
mask = SDIO_STA_CMDREND | CMD_ERROR;
|
|
}
|
|
SDIO->MASK = mask;
|
|
SDIO->ARG = arg;
|
|
SDIO->CMD = 0x0400|(cmd&0x00ff);
|
|
|
|
retv = os_sem_wait(&cmd_sem, 0x8000);
|
|
if(retv==OS_R_TMO){
|
|
printk("sd_cmd: irq timeout!\n");
|
|
stat = 0xffffffff;
|
|
goto _exit;
|
|
}
|
|
|
|
stat = SDIO->STA;
|
|
if(RESP_NOCRC(cmd)){
|
|
stat &= 0xfffffffe;
|
|
}
|
|
if(stat&(CMD_ERROR|DATA_ERROR)){
|
|
// CMD or DATA error
|
|
printk("sd_cmd: CMD_%02d error %08x!\n", (cmd&0x3f), stat);
|
|
goto _exit;
|
|
}
|
|
stat = 0;
|
|
|
|
if(RESP_SHORT(cmd)){
|
|
cmd_resp[0] = SDIO->RESP1;
|
|
}else if(RESP_LONG(cmd)){
|
|
cmd_resp[0] = SDIO->RESP4;
|
|
cmd_resp[1] = SDIO->RESP3;
|
|
cmd_resp[2] = SDIO->RESP2;
|
|
cmd_resp[3] = SDIO->RESP1;
|
|
}
|
|
|
|
if(HAS_DATA(cmd)){
|
|
while(DMA2_Channel4->CNDTR);
|
|
}
|
|
|
|
_exit:
|
|
DMA2_Channel4->CCR = 0;
|
|
SDIO->DCTRL = 0;
|
|
SDIO->MASK = 0;
|
|
SDIO->CMD = 0;
|
|
SDIO->ICR = 0x000007ff;
|
|
return stat;
|
|
}
|
|
|
|
static int sd_acmd(u32 cmd, u32 arg)
|
|
{
|
|
int retv;
|
|
|
|
retv = sd_cmd(CMD_55, sd_rca<<16);
|
|
if(retv)
|
|
return retv;
|
|
|
|
retv = sd_cmd(cmd, arg);
|
|
return retv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int sd_read_blocks(u32 block, int count, u8 *buf)
|
|
{
|
|
int retv, dsize;
|
|
|
|
dsize = count*512;
|
|
|
|
SDIO->DTIMER = 0x0fffffff;
|
|
SDIO->DLEN = dsize;
|
|
SDIO->DCTRL = 0x0000009b;
|
|
|
|
DMA2_Channel4->CCR = 0;
|
|
DMA2_Channel4->CNDTR = dsize/4;
|
|
DMA2_Channel4->CPAR = (u32)&(SDIO->FIFO);
|
|
DMA2_Channel4->CMAR = (u32)buf;
|
|
DMA2_Channel4->CCR = 0x2a81;
|
|
|
|
if(!is_sdhc)
|
|
block *= 512;
|
|
|
|
if(count==1){
|
|
retv = sd_cmd(CMD_17, block);
|
|
}else{
|
|
retv = sd_cmd(CMD_18, block);
|
|
if(retv==0){
|
|
retv = sd_cmd(CMD_12, 0);
|
|
}
|
|
}
|
|
|
|
return retv;
|
|
}
|
|
|
|
int sd_write_blocks(u32 block, int count, u8 *buf)
|
|
{
|
|
int retv, dsize;
|
|
|
|
dsize = count*512;
|
|
|
|
SDIO->DTIMER = 0x0fffffff;
|
|
SDIO->DLEN = dsize;
|
|
SDIO->DCTRL = 0x00000099;
|
|
|
|
DMA2_Channel4->CCR = 0;
|
|
DMA2_Channel4->CNDTR = dsize/4;
|
|
DMA2_Channel4->CPAR = (u32)&(SDIO->FIFO);
|
|
DMA2_Channel4->CMAR = (u32)buf;
|
|
DMA2_Channel4->CCR = 0x2a91;
|
|
|
|
if(!is_sdhc)
|
|
block *= 512;
|
|
|
|
if(count==1){
|
|
retv = sd_cmd(CMD_24, block);
|
|
}else{
|
|
retv = sd_acmd(CMD_23, count);
|
|
if(retv){
|
|
return retv;
|
|
}
|
|
retv = sd_cmd(CMD_25, block);
|
|
if(retv==0){
|
|
retv = sd_cmd(CMD_12, 0);
|
|
}
|
|
}
|
|
|
|
return retv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
#if 0
|
|
|
|
#define SDBUF_SIZE 8
|
|
|
|
static u8 sdbuf[SDBUF_SIZE*512];
|
|
|
|
int sd_read_sector(u32 start, int size, u8 *buf)
|
|
{
|
|
int rsize, retv;
|
|
u32 type = ((u32)buf)>>24;
|
|
|
|
if(sd_rca==0)
|
|
return -1;
|
|
|
|
if(type==0x20){
|
|
return sd_read_blocks(start, size, buf);
|
|
}
|
|
|
|
while(size){
|
|
rsize = (size>SDBUF_SIZE)? SDBUF_SIZE : size;
|
|
retv = sd_read_blocks(start, rsize, sdbuf);
|
|
if(retv)
|
|
return retv;
|
|
memcpy(buf, sdbuf, rsize*512);
|
|
buf += rsize*512;
|
|
size -= rsize;
|
|
start += rsize;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int sd_write_sector(u32 start, int size, u8 *buf)
|
|
{
|
|
int rsize, retv;
|
|
u32 type = ((u32)buf)>>24;
|
|
|
|
if(sd_rca==0)
|
|
return -1;
|
|
|
|
if(type==0x20){
|
|
return sd_write_blocks(start, size, buf);
|
|
}
|
|
|
|
while(size){
|
|
rsize = (size>SDBUF_SIZE)? SDBUF_SIZE : size;
|
|
memcpy(sdbuf, buf, rsize*512);
|
|
retv = sd_write_blocks(start, rsize, sdbuf);
|
|
if(retv)
|
|
return retv;
|
|
buf += rsize*512;
|
|
size -= rsize;
|
|
start += rsize;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int sd_read_sector(u32 start, int size, u8 *buf)
|
|
{
|
|
return sd_read_blocks(start, size, buf);
|
|
}
|
|
|
|
|
|
int sd_write_sector(u32 start, int size, u8 *buf)
|
|
{
|
|
return sd_write_blocks(start, size, buf);
|
|
}
|
|
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
|
|
int sdcard_identify(void)
|
|
{
|
|
int retv, hcs;
|
|
char tmp[8];
|
|
|
|
os_dly_wait(10);
|
|
sd_cmd(CMD_00, 0);
|
|
os_dly_wait(10);
|
|
sd_cmd(CMD_00, 0);
|
|
os_dly_wait(10);
|
|
|
|
retv = sd_cmd(CMD_08, 0x000001aa);
|
|
printk("CMD_08: retv=%08x resp0=%08x\n", retv, cmd_resp[0]);
|
|
if(retv)
|
|
hcs = 0;
|
|
else
|
|
hcs = 1<<30;
|
|
|
|
while(1){
|
|
retv = sd_acmd(CMD_41, 0x80100000|hcs);
|
|
if(retv)
|
|
break;
|
|
if(cmd_resp[0]&0x80000000)
|
|
break;
|
|
}
|
|
if(retv){
|
|
printk("ACMD41 error! %08x\n", retv);
|
|
return -1;
|
|
}
|
|
|
|
is_sdhc = 0;
|
|
if(cmd_resp[0]&0x40000000)
|
|
is_sdhc = 1;
|
|
printk(" V2.0: %s\n", (hcs?"Yes":"No"));
|
|
printk(" SDHC: %s\n", (is_sdhc?"Yes":"No"));
|
|
|
|
// Send CID
|
|
retv = sd_cmd(CMD_02, 0x00000000);
|
|
if(retv)
|
|
return retv;
|
|
memcpy(sd_cid, (u8*)cmd_resp, 16);
|
|
|
|
tmp[0] = sd_cid[12];
|
|
tmp[1] = sd_cid[11];
|
|
tmp[2] = sd_cid[10];
|
|
tmp[3] = sd_cid[9];
|
|
tmp[4] = sd_cid[8];
|
|
tmp[5] = 0;
|
|
printk(" Name: %s\n", tmp);
|
|
|
|
// Send RCA
|
|
retv = sd_cmd(CMD_03, 0x00000000);
|
|
if(retv)
|
|
return retv;
|
|
sd_rca = cmd_resp[0]>>16;
|
|
|
|
// Send CSD
|
|
retv = sd_cmd(CMD_09, (sd_rca<<16));
|
|
if(retv){
|
|
sd_rca =0;
|
|
return retv;
|
|
}
|
|
memcpy(sd_csd, (u8*)cmd_resp, 16);
|
|
|
|
// Get Size form CSD
|
|
bsize = 1<<(sd_csd[10]&0x0f);
|
|
printk(" Blen: %d\n", bsize);
|
|
|
|
if(is_sdhc){
|
|
csize = (sd_csd[8]<<16) | (sd_csd[7]<<8) | sd_csd[6];
|
|
csize &= 0x003fffff;
|
|
csize = (csize+1)*512;
|
|
if(bsize==512){
|
|
cblock = csize*2;
|
|
}else if(bsize==1024){
|
|
cblock = csize;
|
|
}else{
|
|
cblock = csize/2;
|
|
}
|
|
}else{
|
|
int m;
|
|
m = (sd_csd[6]<<8) | sd_csd[5];
|
|
m = (m>>7)&7;
|
|
m = 1<<(m+2);
|
|
csize = (sd_csd[9]<<16) | (sd_csd[8]<<8) | sd_csd[7];
|
|
csize = (csize>>6)&0x00000fff;
|
|
csize = (csize+1)*m;
|
|
cblock = csize;
|
|
if(bsize==512) csize /= 2;
|
|
if(bsize==2048) csize *= 2;
|
|
}
|
|
printk(" Blks: %d\n", cblock);
|
|
printk(" Size: %d K\n", csize);
|
|
|
|
// Select CARD
|
|
retv = sd_cmd(CMD_07, sd_rca<<16);
|
|
if(retv){
|
|
sd_rca = 0;
|
|
return retv;
|
|
}
|
|
//printk("CMD_07: resp=%08x\n", cmd_resp[0]);
|
|
sd_clk_set(SDCLK_HIGH);
|
|
|
|
// Set Block Len
|
|
sd_cmd(CMD_16, 512);
|
|
//printk("CMD_16: resp=%08x\n", cmd_resp[0]);
|
|
|
|
#if 1
|
|
// Switch to 4bit
|
|
retv = sd_acmd(CMD_06, 2);
|
|
if(retv)
|
|
return retv;
|
|
//printk("ACMD06: resp=%08x\n", cmd_resp[0]);
|
|
sd_bus_width(4);
|
|
#endif
|
|
|
|
retv = sd_acmd(CMD_13, 0);
|
|
if(retv)
|
|
return retv;
|
|
os_dly_wait(1);
|
|
//printk("ACMD13: resp=%08x\n", cmd_resp[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int sdio_init(void)
|
|
{
|
|
SDIO->MASK = 0x00000000;
|
|
SDIO->ICR = 0x00c007ff;
|
|
SDIO->DCTRL= 0;
|
|
SDIO->CMD = 0;
|
|
SDIO->CLKCR = 0x0000;
|
|
SDIO->CLKCR = 0x2000;
|
|
|
|
sd_rca = 0;
|
|
os_sem_init(&cmd_sem, 0);
|
|
NVIC_SetPriority(SDIO_IRQn, 6);
|
|
NVIC_EnableIRQ(SDIO_IRQn);
|
|
|
|
sd_clk_set(SDCLK_LOW);
|
|
sd_bus_width(1);
|
|
sd_power_on(1);
|
|
sd_clk_enable(1);
|
|
|
|
if(sd_card_insert()){
|
|
printk("SDCARD insert!\n");
|
|
sdcard_identify();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|