📄 sdi.c
字号:
/* * ApOS (Another Project software for s3c2410) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Copyright caiyuqing * */#include "../include/s3c2410/s3c2410.h"#include "../include/s3c2410/cpu.h"#include "../include/kernel/irq.h"#include "../include/s3c2410/sdi.h"#include "../include/s3c2410/dma.h"//#define SDI_DEBUG 1#define SD_CMD(n) sd_cmd[(n)]//命令索引#define CRC_CHECK 1//需要进行CRC校验#define CRC_UNCHECK 0//不需要进行CRC校验#define NO_RESP 0//无Response#define SHORT_RESP 1//短Response#define LONG_RESP 2//长Response#define DEFAULT_RCA 0x0000//默认的RCA#define SDI_READ 0//sdi读操作#define SDI_WRITE 1//sdi写操作#define BLOCK(n) (512*(n))#define CURRENT_STATE(n) (((n)>>9)&0x0f)extern char sys_dma2_done;extern irq_ptr irq_rotunie[32];extern struct dma_ctrl_object dma_ctrl_object;struct sdi_control_obj sdi_control_obj;unsigned char sd_cmd[64]; //命令数组unsigned short sdi_RCA; //sd卡的RCAunsigned int sdi_status;struct CID_struct sdi_CID; //sd卡的CIDstruct CSD_register_struct sdi_CSD; //sd卡的CSD/* * 写缓冲,该缓冲区保存着准备往sd卡的写入的数据 */#define PAGES 2unsigned char sd_Tx_buf[512*PAGES];/* * 读缓冲,该缓冲区保存着刚从sd卡读出的数据 */unsigned char sd_Rx_buf[512*PAGES];void dma2_irq(struct cpu_registers *regs);void sdi_irq(struct cpu_registers *regs);int sdi_send_status(unsigned short rca,unsigned int *status);void dump_sd_Rx_buf(int page){ int i; for(i=0;i<512;i++) printk("%0x ",sd_Rx_buf[page*512+i]);}/* * 初始化 sdi_control_obj */void sdi_ctrl_obj_init(struct sdi_control_obj *sco){ sco->sdicon =&rSDICON; sco->sdipre =&rSDIPRE; sco->sdicarg =&rSDICARG; sco->sdiccon =&rSDICCON; sco->sdicsta =&rSDICSTA; sco->sdirsp0 =&rSDIRSP0; sco->sdirsp1 =&rSDIRSP1; sco->sdirsp2 =&rSDIRSP2; sco->sdirsp3 =&rSDIRSP3; sco->sdidtimer =&rSDIDTIMER; sco->sdibsize =&rSDIBSIZE; sco->sdidcon =&rSDIDCON; sco->sdidcnt =&rSDIDCNT; sco->sdidsta =&rSDIDSTA; sco->sdifsta =&rSDIFSTA; sco->sdidat =&rSDIDAT; sco->sdiimsk =&rSDIIMSK; *sco->sdiimsk =(1<<SDI_INT_CMDSENT); irq_rotunie[INT_SDI]=sdi_irq;//初始化sdi的中断向量 irq_mask(INT_SDI,IRQ_MASK);//关闭sdi的中断,目前没有使用到中断}//初始化命令数组int init_sd_cmd(){ int i; for(i=0;i<64;i++) { sd_cmd[i]=i|(1<<6); }}/* * 本函数检测命令发送是否成功 * */#define RESP_FIN (finish&(1<<SDICSTA_RSP_FIN_OFFSET))#define TIME_OUT (finish&(1<<SDICSTA_CMD_TOUT_OFFSET))int sdi_cmd_check(int cmd,unsigned int be_resp,char check_crc){ int finish =0; /* * status保存了命令检测后的状态. * 如果命令超时,则status的第30bit被置位; * 如果CRC校验出错,则status的第31bit被置位; * 如果命令发送成功且得到正确的Response,则status的低8位保存了该 * Response的索引(RspIndex) */ int status =0; /* * No Response */ if(!be_resp) { finish=*sdi_control_obj.sdicsta; /* * if rSDICSTA[11]==1,command end */ while(!(finish&(1<<SDICSTA_CMD_SENT_OFFSET))) { finish=*sdi_control_obj.sdicsta; } #ifdef SDI_DEBUG printk("sdi cmd%d with no response send :].\n",(cmd&0x3F)); #endif /* * Clear command end state */ *sdi_control_obj.sdicsta=finish; return status; } else// With Response { finish=*sdi_control_obj.sdicsta; /* * if rSDICSTA[9 ]==1,Response end * if rSDICSTA[10]==1,Time out */ while(!(RESP_FIN|TIME_OUT)) { finish=*sdi_control_obj.sdicsta; } #ifdef SDI_DEBUG printk("sdi cmd%d with response send.\n",(cmd&0x3F)); #endif /* * 若该Response有CRC校验,则进行检测 */ if(check_crc&&(finish&(1<<SDICSTA_RSP_CRC_OFFSET))) { status|=1<<31; } /* * 检测是否超时 */ if(finish&(1<<SDICSTA_CMD_TOUT_OFFSET)) { status|=1<<30; } /* * Clear the bit whicth has been set * 寄存器sdicsta清0 */ *sdi_control_obj.sdicsta=(finish&0x1E00); status|=(finish&0xff); return status; }}/* * 本函数实现sdi模块的命令发送 * 成功返回1,失败返回0 */int sdi_send_cmd(unsigned int cmd,int arg, char rsp,char check_crc){ int status =0; /* * with_data 标明该命令是否有带参数.有为1,无为0 * */ char with_data =0; /* * abort_cmd 标明该命令是否是abort command(CMD(12),CMD(52)). * 是为1,否为0 */ char abort_cmd =0; //该命令是否有带参数 if(arg!=-1) { with_data=1; *sdi_control_obj.sdicarg=arg; } //该命令是否为abort command if(cmd==SD_CMD(12)||cmd==SD_CMD(52)) { abort_cmd=1; } //该命令是否有有Response,若有,则该Response是什么类型的 switch(rsp) { //no Response case NO_RESP: *sdi_control_obj.sdiccon=(abort_cmd<<SDICCON_ABORD_CMD_OFFSET)| (with_data<<SDICCON_WITH_DATA_OFFSET)| (1<<SDICCON_CMD_START_OFFSET)| cmd; break; //short Response case SHORT_RESP: *sdi_control_obj.sdiccon=(abort_cmd<<SDICCON_ABORD_CMD_OFFSET)| (with_data<<SDICCON_WITH_DATA_OFFSET)| (0<<SDICCON_LONG_RSP_OFFSET)| (1<<SDICCON_WAIT_RSP_OFFSET)| (1<<SDICCON_CMD_START_OFFSET)| cmd; break; //long Response case LONG_RESP: *sdi_control_obj.sdiccon=(abort_cmd<<SDICCON_ABORD_CMD_OFFSET)| (with_data<<SDICCON_WITH_DATA_OFFSET)| (1<<SDICCON_LONG_RSP_OFFSET)| (1<<SDICCON_WAIT_RSP_OFFSET)| (1<<SDICCON_CMD_START_OFFSET)| cmd; break; } //检测命令发送和Response的接收是否成功 status=sdi_cmd_check(cmd,rsp,check_crc); if(status&0xffffff00)//命令检测失败 { return 0; } return 1; //命令检测成功}/* * 检测数据传送是否正确 */#define DAT_FIN (finish&(1<<SDIDSTA_DAT_FIN_OFFSET)) //rSDIDSTA[4]==1,data finish#define DAT_TOUT (finish&(1<<SDIDSTA_DAT_TOUT_OFFSET)) //rSDIDSTA[5]==1,time outint sdi_data_check(){ int finish; finish=*sdi_control_obj.sdidsta; while( !(DAT_FIN|DAT_TOUT)) { finish=*sdi_control_obj.sdidsta; } *sdi_control_obj.sdidsta=finish&0x7FC; // Clear error state if((finish&0xfc)!=(1<<SDIDSTA_DAT_FIN_OFFSET)) { return 0;//数据传送出错 } return 1;//数据传送成功}/* * 设置sdi的DMA通道 */void sdi_dma_setup(char operate,unsigned int *mem_addr,int blocks){ struct dma_ctrl_object *dma_obj =&dma_ctrl_object; /* * 使用2号DMA通道进行数据传送数据 */ struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+DMA_CHANEL_2; /* * 每个block占用512 byte * 关于tc的详细信息请查看dma.c中的说明(在dma.c中搜索tc即可) */ unsigned int tc=blocks*512; irq_rotunie[INT_DMA2] =&dma2_irq; irq_mask(INT_DMA2,IRQ_UNMASK); sys_dma2_done=0;//dma标志清0,若sys_dma2_done为1表示dma操作已经完成 switch(operate) { /* * 写SD卡. * 数据传送源是RAM. * DMA的寄存器disrcc: * LOC位被设置为AHB;(同步于AHB总线) * INC位被设置为INCREMENT;(访问后递增) * DMA的寄存器disrc:mem_addr(数据传送源地址) * * * 数据传送目标是SD卡. * DMA的寄存器didstc: * LOC位被设置为APB;(同步于APB总线) * INC位被设置为FIXED;(访问后不递增) * DMA的寄存器didst:sdi_control_obj.sdidat(数据传送目标) */ case SDI_WRITE: *dma_chanel->disrcc=(AHB<<LOC_OFFSET)|(INCREMENT<<INC_OFFSET); *dma_chanel->didstc=(APB<<LOC_OFFSET)|(FIXED<<INC_OFFSET); *dma_chanel->disrc=mem_addr; *dma_chanel->didst=sdi_control_obj.sdidat; break; /* * 读SD卡.与写操作的源地址和目标地址相反,各个寄存器的设置类似. * ... */ case SDI_READ: *dma_chanel->disrcc=(APB<<LOC_OFFSET)|(FIXED<<INC_OFFSET); *dma_chanel->didstc=(AHB<<LOC_OFFSET)|(INCREMENT<<INC_OFFSET); *dma_chanel->disrc=sdi_control_obj.sdidat; *dma_chanel->didst=mem_addr; break; } /* * sdi的DMA通道的属性设置 * 具体信息可查看dma.c 398行~434行 */ *dma_chanel->dcon=(1<<DMD_HS_OFFSET)|(0<<SYNC_OFFSET)| (1<<INT_OFFSET)|(0<<TSZ_OFFSET)|(1<<SERVMODE_OFFSET)|(2<<HWSRCSEL_OFFSET)| (1<<SWHW_SEL_OFFSET)|(1<<RELOAD_OFFSET)|(BYTE<<DSZ_OFFSET)|(tc); while(((*dma_chanel->dstat)>>20)&0x3==1);//DMA is busy /* * 打开sdi的DMA通道 */ *dma_chanel->dmasktrig=(0<<STOP_OFFSET)|(1<<ON_OFF_OFFSET)|0;}/* * dma chanel2 中断服务例程 */void dma2_irq(struct cpu_registers *regs){ clean_src_pnd(INT_DMA2); clean_int_pnd(INT_DMA2); irq_mask(INT_DMA2,IRQ_MASK); dma_stop(DMA_CHANEL_2); sys_dma2_done=1;//sys_dma2_done设为1,表示DMA操作完成}/* * sdi中断服务例程 */void sdi_irq(struct cpu_registers *regs){ irq_mask(INT_SDI,IRQ_MASK); printk("sdi interrupt\n"); clean_src_pnd(INT_SDI); clean_int_pnd(INT_SDI); irq_mask(INT_SDI,IRQ_UNMASK);} /* * 往SD卡写一个块的数据,每个块为512 byte * 写成功返回 1,否则返回0 * * Tx_buf为写缓冲首地址,sd_addr为SD卡待写入块的首地址. * 事实上本函数将Tx_buf中的512个字节数据写入SD卡(写入 * 地址为sd_addr~sd_addr+512),每次写入的长度都是 * 512 byte(一个块) * * 我们可以这样调用:sdi_dma_write_block(sd_Tx_buf,BLOCK(10)); * 上面的调用是把 sd_Tx_buf中的512byte 写在SD卡的第10个块(扇区) */int sdi_dma_write_block(unsigned short rca,unsigned char *Tx_buf, unsigned int sd_addr){ unsigned int card_state; int cmd_status; int check_conter; /* * 循环检测SD卡,直到其处于tran状态 */ for(check_conter=0;check_conter<100;check_conter++) { cmd_status=sdi_send_status(rca,&card_state); if((CURRENT_STATE(card_state)==TRAN_STATE) && cmd_status) { break; } } if(check_conter==100) { #ifdef SDI_DEBUG printk("function \"sdi_dma_write_block\" card state fail 0x%0x,0x%0x.\n", (card_state>>9)&0xf,cmd_status); #endif return 0; } //必须清除DCache,否则数据会出错 invalidate_DCache(); *sdi_control_obj.sdicon|=(1<<1);//FIFO Reset sdi_dma_setup(SDI_WRITE,Tx_buf,1);//设置sdi的DMA通道 *sdi_control_obj.sdidcon=(1<<SDIDCON_TARSP_OFFSET)| (1<<SDIDCON_BLK_MODE_OFFSET)| (1<<SDIDCON_WIDE_BUS_OFFSET)| (1<<SDIDCON_DMA_ENABLE_OFFSET)| (3<<SDIDCON_DAT_MODE_OFFSET)| (1<<SDIDCON_BLK_NUM_OFFSET); /* * 向SD卡发送写单个块的命令(CMD24 WRITE_BLOCK) */ cmd_status=sdi_send_cmd(SD_CMD(24),sd_addr,SHORT_RESP,CRC_CHECK); if(cmd_status!=1) { return 0; } while(!sys_dma2_done);//等待DMA的完成 *sdi_control_obj.sdidcon&=~(1<<15);//关闭sdi的DMA if(!sdi_data_check())//数据检测 { return 0;//写入出错 } else { return 1;//写入成功 } }/* * 读取SD卡一个块的数据,每个块为512 byte * 读成功返回 1,否则返回0 * * Rx_buf为读缓冲首地址,sd_addr为SD卡待读出块的首地址. * 事实上本函数是将SD卡上地址为sd_addr~sd_addr+512的数据写入Rx_buf, * 每次写入Rx_buf的数据长度都是512 byte(一个块) * * 我们可以这样调用:sdi_dma_read_block(sd_Rx_buf,BLOCK(10)); * 上面的调用是把SD卡的第10个块(扇区,512byte)的数据读到 sd_Rx_buf中 */int sdi_dma_read_block(unsigned short rca,unsigned char *Rx_buf, unsigned int sd_addr){ unsigned int card_state; int cmd_status; int check_conter; /* * 循环检测SD卡,直到其处于tran状态 */ for(check_conter=0;check_conter<100;check_conter++) { cmd_status=sdi_send_status(rca,&card_state); if((CURRENT_STATE(card_state)==TRAN_STATE) && cmd_status) { break; } } if(check_conter==100) { #ifdef SDI_DEBUG printk("function \"sdi_dma_read_block\" card state fail %0x,%0x.\n", (card_state>>9)&0xf,cmd_status); #endif return 0; } //必须清除DCache,否则数据会出错 invalidate_DCache(); *sdi_control_obj.sdicon|=(1<<1);//FIFO Reset
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -