OpenRisc-30-SD card controller模块分析与验证
来源:互联网 发布:tensorflow 推荐系统 编辑:程序博客网 时间:2024/05/17 03:18
引言
ORPSoC的硬件平台是包含SD card controller控制器的,但是对应的linux里面却没有对应的linux的驱动程序,这使ORPSoC的SD card的使用收到了很大的限制。没有驱动,硬件是不能工作的,SD卡控制器的驱动,linux提供了非常好的framework,在写驱动时只要开发者=关心最底层的部分,就是和硬件直接打交道的部分,即linuxMMC framework的HOST部分。
本小节并不介绍linux的MMC的framework,而把注意力放在核心部分,即直接对硬件的操作,即写一个简单的linux driver来验证一下硬件的正确性。
1,linux的MMC/SD的framework
虽然本小节并不介绍符合linux MMC框架的SD卡控制器的驱动,但是作为必须了解的部分,会对编写真正的驱动有帮助,在此简单介绍一下。
如下图,整个框架分为三层,上面两层与硬件无关,有linux提供,下面一层(host)由用户编写,所以我们在编写SD卡控制器的驱动时,只需要填充实现HOST层的接口函数即可。
2,硬件部分
1>什么是SD Host 控制器,如下图:
2>ipcore的下载与例化:
ORPSoC的sd card控制器的ipcore可以在官网下载:
http://opencores.org/project,sdcard_mass_storage_controller
当然只下载下来是不能使用的,还要在挂在wishbone总线上才行,还用DMA的连线和中断连线,即例化工作。ORPSoC的例化工作已经做了,所以对于使用ORPSoC的,这部分工作就省掉了。
关于ORPSoC的中断使用情况,前面已经介绍过了,请参考:
http://blog.csdn.net/rill_zhen/article/details/8894856
关于ipcore的例化,前面也已经介绍过了,请参考:
http://blog.csdn.net/rill_zhen/article/details/8722664
和
http://blog.csdn.net/rill_zhen/article/details/8784510
以及
http://blog.csdn.net/rill_zhen/article/details/8849149
3>SD card controller的wishbone slave地址
要想控制SD卡控制器,必须要知道其对应的总线地址,如下图,为0x9e,即其设备起始地址为0x9e00_0000
4>SD卡控制器的中断号
通过之前对ORPSoC的中断系统的分析可知,SDC使用的中断号为14,15,16三个:
http://blog.csdn.net/rill_zhen/article/details/8894856
本小节并不处理SD卡控制器的中断部分。
3,软件部分
通过上面的分析之后,仔细看一下从官网下载ipcore时附带的datasheet,我们就可以开始编写一个简单的linux驱动了,本驱动只测试硬件的正确性。
关于如何编写ipcore的linux驱动,和具体的操作步骤,也已经介绍过了,请参考:
http://blog.csdn.net/rill_zhen/article/details/8700937
下面是code list:
1>sdcmsc.c:
/**Rill 130617*rillzhen@gmail.com*/#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/blkdev.h>#include <linux/ioport.h>#include <asm/io.h>#include "sdcmsc.h"MODULE_LICENSE("Dual BSD/GPL");#define MMC_BLOCK_MAJOR 179void __iomem *sdcmsc_base = NULL;static int sdcmsc_card_cmd(unsigned cmd, unsigned arg, unsigned *response){unsigned reg;unsigned temp;//printk(KERN_ALERT "OK before SDCMSC_COMMAND\n");//reg = ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);//printk(KERN_ALERT "sdcmsc_int_status before:0x%x\n",reg);// Send command to cardcmd = le32_to_cpu(cmd);iowrite32(cmd, sdcmsc_base + SDCMSC_COMMAND);arg = le32_to_cpu(arg);iowrite32(arg, sdcmsc_base + SDCMSC_ARGUMENT);//printk(KERN_ALERT "OK after SDCMSC_ARGUMENT\n");// Wait for responseunsigned mask = SDCMSC_NORMAL_INT_STATUS_EI | SDCMSC_NORMAL_INT_STATUS_CC;//printk(KERN_ALERT "CMD:%d, ARGUMENT:%d\n", cmd, arg); do {reg = ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);reg=cpu_to_le32(reg);//printk(KERN_ALERT "reg:0x%x\n",reg);} while(!(reg&mask));iowrite32(0, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);printk(KERN_ALERT "OK after SDCMSC_NORMAL_INT_STATUS\n");//Optionally read response registerif(response) {temp = ioread32(sdcmsc_base + SDCMSC_RESPONSE);temp = cpu_to_le32(temp);*response = temp;}// Check for errorsif(reg & SDCMSC_NORMAL_INT_STATUS_EI) {printk(KERN_ALERT "Come on baby!\n");reg = ioread32(sdcmsc_base + SDCMSC_ERROR_INT_STATUS);reg=cpu_to_le32(reg);printk(KERN_ALERT "ERROR_INT_STATUS:0x%x\n",reg);if(reg & (1 << 3)) printk(KERN_ALERT "Command index error\n");if(reg & (1 << 1)) printk(KERN_ALERT "Command CRC error\n");if(reg & (1 << 0)) printk(KERN_ALERT "Command timeout\n");return 0;}else{return 1;}}int sdcmsc_card_init(void){unsigned cmd;unsigned reg;unsigned arg;int is_v20;int is_sdhc;unsigned rca;unsigned card_capacity;printk(KERN_ALERT "Before first iowrite32\n");// Set highest possible timeoutreg = le32_to_cpu(0xFFFE);iowrite32(reg,sdcmsc_base + SDCMSC_TIMEOUT);printk(KERN_ALERT "After first iowrite32\n");//Reset the peripheralreg = le32_to_cpu(1);iowrite32(reg, sdcmsc_base + SDCMSC_SOFTWARE_RESET);reg = le32_to_cpu(2);iowrite32(reg, sdcmsc_base + SDCMSC_CLOCK_DIVIDER);reg = le32_to_cpu(0);iowrite32(reg, sdcmsc_base + SDCMSC_SOFTWARE_RESET);//iowrite32(0xFFFF, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);//reg =ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);//reg=cpu_to_le32(reg);//printk(KERN_ALERT "Test Int_status:0x%x\n", reg);//iowrite32(0, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);printk(KERN_ALERT "Ok at 1\n");//Send CMD0 to switch the card to idle statecmd = SDCMSC_COMMAND_CMDI(0);if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;printk(KERN_ALERT "OK at 2\n");// Send CMD8 offering 2.7V to 3.6V range// If the card doesn't response it means either:// 1. Card supports v2.0 but can't communicate using current voltage levels// 2. Card doesn't support v2.0cmd = SDCMSC_COMMAND_CMDI(8) |SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;is_v20 = sdcmsc_card_cmd(cmd, 0x1AA, NULL);if(is_v20) printk(KERN_ALERT "This is sd version 2.0\n");elseprintk(KERN_ALERT "This isn't sd version 2.0\n");do {reg = ioread32(sdcmsc_base + SDCMSC_CARD_STATUS);reg = cpu_to_le32(reg);} while(reg & SDCMSC_CARD_STATUS_CICMD);unsigned tCounter=0;// Repeat ACMD41 until card set the busy bit to 1// Since ACMD is an extended command, it must be preceded// by CMD55/*do {printk(KERN_ALERT "%d times\n",++tCounter);cmd = SDCMSC_COMMAND_CMDI(55) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;cmd = SDCMSC_COMMAND_CMDI(41)| SDCMSC_COMMAND_RTS_48;arg = is_v20 ? 0x40FF8000 :0x00FF8000;if(!sdcmsc_card_cmd(cmd, arg, ®)) return 0;} while(!(reg & 0x80000000));printk(KERN_ALERT "Before is_sdhc!\n");is_sdhc = !!(reg & 0x40000000); */is_sdhc=1; // Issue CMD2 to switch from ready state to ident. Unfortunately, it is// not possible to read whole CID because the command can be issued only// once, and the peripheral can store only 32bit of the command at once.cmd = SDCMSC_COMMAND_CMDI(2) |SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;printk(KERN_ALERT "after command 2\n");//Issue CMD3 to get RCA and switch from ident state to stbycmd = SDCMSC_COMMAND_CMDI(3) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0, ®)) return 0;rca = reg & 0xFFFF0000;printk(KERN_ALERT "after command 3\n");//Calculate card capacity. Use information stored in CSD register.if(is_sdhc) {cmd = SDCMSC_COMMAND_CMDI(9) |SDCMSC_COMMAND_CMDW(1) |SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, rca, ®)) return 0;card_capacity = reg & 0x3F;card_capacity <<=16;cmd = SDCMSC_COMMAND_CMDI(9) |SDCMSC_COMMAND_CMDW(2) |SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, rca, ®)) return 0;reg >>=16;card_capacity |= reg;card_capacity +=1;card_capacity *=1000;}else {cmd = SDCMSC_COMMAND_CMDI(9) |SDCMSC_COMMAND_CMDW(1) |SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, rca, ®)) return 0;unsigned read_bl_len = (reg >>16) & 0x0F;unsigned c_size = reg & 0x3FF;c_size <<= 2;cmd = SDCMSC_COMMAND_CMDI(9) |SDCMSC_COMMAND_CMDW(2) |SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, rca, ®)) return 0;c_size |= (reg >>30) & 0x03;unsigned c_size_mult = (reg >> 15) & 0x07;card_capacity = c_size + 1;card_capacity *= 1 << (c_size_mult + 2);card_capacity *= 1 << (read_bl_len);card_capacity >>= 9;}// Put card in transfer statecmd = SDCMSC_COMMAND_CMDI(7) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, rca, ®)) return 0;if(reg != 0x700) return 0;// Set block size to 512cmd = SDCMSC_COMMAND_CMDI(16) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd ,512, NULL)) return 0;// Set 4-bits bus modecmd = SDCMSC_COMMAND_CMDI(55) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, rca, NULL)) return 0;cmd = SDCMSC_COMMAND_CMDI(6) |SDCMSC_COMMAND_CICE |SDCMSC_COMMAND_CIRC |SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0x02, NULL)) return 0;return 1;}static int ocores_sdcmsc_init(void){int res;res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//if(res) return res;printk(KERN_ALERT "test successfully before request_mem_region!\n");if(!request_mem_region(SDCMSC_BASE,SDCMSC_ADR_LEN,"ocores-sdcmsc")){printk(KERN_ALERT "ocores-sdcmsc request_mem_region fails!");return -1;}printk(KERN_ALERT "test successfully before request_mem_region!\n");sdcmsc_base = ioremap(SDCMSC_BASE,SDCMSC_ADR_LEN);if(!sdcmsc_base){printk(KERN_ALERT "ocores-sdcmsc ioremap failed!");return -1;}printk(KERN_ALERT "test successfully after ioremap!\n");sdcmsc_card_init();}static void ocores_sdcmsc_exit(void){unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");}module_init(ocores_sdcmsc_init);module_exit(ocores_sdcmsc_exit);
2>sdcmsc.h
/**Rill 130617*rillzhen@gmail.com*/// SDCMSC address space#define SDCMSC_BASE 0x9e000000#define SDCMSC_ADR_LEN 0xa0// Register space#define SDCMSC_ARGUMENT 0x00#define SDCMSC_COMMAND 0x04#define SDCMSC_CARD_STATUS 0x08#define SDCMSC_RESPONSE 0x0C#define SDCMSC_CONTROLLER_SETTING 0x1C#define SDCMSC_BLOCK_SIZE 0x20#define SDCMSC_POWER_CONTROL 0x24#define SDCMSC_SOFTWARE_RESET 0x28#define SDCMSC_TIMEOUT 0x2C#define SDCMSC_NORMAL_INT_STATUS 0x30#define SDCMSC_ERROR_INT_STATUS 0x34#define SDCMSC_NORMAL_INT_ENABLE 0x38#define SDCMSC_ERROR_INT_ENABLE 0x3C#define SDCMSC_CAPABILITY 0x48#define SDCMSC_CLOCK_DIVIDER 0x4C#define SDCMSC_BD_BUFFER_STATUS 0x50#define SDCMSC_DAT_INT_STATUS 0x54#define SDCMSC_DAT_INT_ENABLE 0x58#define SDCMSC_BD_RX 0x60#define SDCMSC_BD_TX 0x80// SDCMSC_COMMAND bits#define SDCMSC_COMMAND_CMDI(x) (x << 8)#define SDCMSC_COMMAND_CMDW(x) (x << 6)#define SDCMSC_COMMAND_CICE 0x10#define SDCMSC_COMMAND_CIRC 0x08#define SDCMSC_COMMAND_RTS_48 0x02#define SDCMSC_COMMAND_RTS_136 0x01//SDCMSC_CARD_STATUS bits#define SDCMSC_CARD_STATUS_CICMD 0x01// SDCMSC_NORMAL_INT_STATUS bits#define SDCMSC_NORMAL_INT_STATUS_EI 0x8000#define SDCMSC_NORMAL_INT_STATUS_CC 0x0001// SDCMSC_DAT_INT_STATUS#define SDCMSC_DAT_INT_STATUS_TRS 0x01
3>makefile
##Rill 130617#rillzhen@gmail.com#ifneq ($(KERNELRELEASE), )obj-m := sdcmsc.oelseKERNELDIR ?= /home/openrisc/soc-design/linuxPWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules ARCH=openrisc CROSS_COMPILE=or32-linux-clean:rm -rf .*.cmd *.o *.mod.c *.ko .tm_versions *.order *.symversendif
4,验证结果
找一个小的sd卡,插到ORPSoC板子的SD卡插槽里面,
将驱动insmod到板子上,可以看到插入的SD卡的版本信息(v2.0),验证的后面几步没有成功,但是已经说明SD卡控制器是可以工作的,如下图:
5,需要注意的问题
在验证的过程中,一定注意以下两个问题:
1>大小端的问题:
ORPSoC的OpenRisc是大端的,但是ipcore内部是小端操作的,所以在控制ipcore时一定要做字节序(byteorder)的转换,这个问题在之前的blog中也提到过,调用函数如下:
cpu_to_le32() 和le32_to_cpu()
2>SD卡控制器命令的timeout:
尽量设置的大一点,如果太小,发送CMD就会超时(可以通过读取状态寄存器获得失败原因)。至于为什么出现这种情况,现在还不清楚。
6,eCos平台下的sd card controller驱动测试
上面都是基于linux的测试,目前,唯一支持orpsoc的sd card controller的驱动的平台就是eCos,所以,在eCos下测试一下是很有必要的。
参考http://blog.csdn.net/rill_zhen/article/details/9271721
在if_sdcmsc.c驱动里面增加打印信息,可见,eCos下的初始化阶段可以通过!从下图可以看出,我当时测试的sd卡的容量(容量=capacity*512=386258*512=1977614336)是2G。
至于测试程序部分(fatfs1.c),mount动作失败,还需继续查找原因解决。
7,小结
本小节只是对sd卡控制器的硬件的一个简单验证,并不是一个完整的SD卡控制器的linux驱动,但是能确定硬件的正确性,意义也是很大的。至于如何编写符合linux MMC/SD框架的驱动,那是另外的话题了,并且有很多资料可以参考,这里就不再赘述。
8,附录
ecos下的sd card controller的driver:
1>代码获取
SVN地址:可以用svn客户端下载。也可以下载整个ecos-3.0工程,以获得更多信息。
http://opencores.org/ocsvn/openrisc/openrisc/trunk/rtos/ecos-3.0/packages/devs/disk/opencores/sdcmsc/current/src/if_sdcmsc.c
如果不想安装svn,可以通过wensvn访问,地址如下:
http://opencores.org/websvn,listing?repname=openrisc&path=%2Fopenrisc%2Ftrunk%2Frtos%2Fecos-3.0%2Fpackages%2Fdevs%2Fdisk%2Fopencores%2Fsdcmsc%2Fcurrent%2Fsrc%2F#path_openrisc_trunk_rtos_ecos-3.0_packages_devs_disk_opencores_sdcmsc_current_src_
2>codelist
如果连一个新窗口也懒得打开,那么版本为798时的代码如下:
if_sdcmsc.c:
//==========================================================================//// if_sdcmsc.c//// Provide a disk device driver for SDCard Mass Storage Controller////==========================================================================// ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2004, 2006 Free Software Foundation, Inc. //// eCos 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 2 or (at your option) any later // version. //// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //// As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. //// This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //==========================================================================//#####DESCRIPTIONBEGIN####//// Author: Piotr Skrzypek// Date: 2012-05-01////####DESCRIPTIONEND####//==========================================================================#include <pkgconf/system.h>#include <cyg/infra/cyg_type.h>#include <cyg/infra/cyg_ass.h>#include <cyg/infra/diag.h>#include <cyg/hal/hal_arch.h>#include <cyg/hal/hal_if.h>#include <cyg/hal/hal_intr.h>#include <string.h>#include <errno.h>#include <cyg/io/io.h>#include <cyg/io/devtab.h>#include <cyg/io/disk.h>// Settings exported from CDL#include <pkgconf/devs_disk_opencores_sdcmsc.h>// SDCMSC address space#define SDCMSC_BASE 0x9e000000// Register space#define SDCMSC_ARGUMENT 0x00#define SDCMSC_COMMAND 0x04#define SDCMSC_CARD_STATUS 0x08#define SDCMSC_RESPONSE 0x0C#define SDCMSC_CONTROLLER_SETTING 0x1C#define SDCMSC_BLOCK_SIZE 0x20#define SDCMSC_POWER_CONTROL 0x24#define SDCMSC_SOFTWARE_RESET 0x28#define SDCMSC_TIMEOUT 0x2C#define SDCMSC_NORMAL_INT_STATUS 0x30#define SDCMSC_ERROR_INT_STATUS 0x34#define SDCMSC_NORMAL_INT_ENABLE 0x38#define SDCMSC_ERROR_INT_ENABLE 0x3C#define SDCMSC_CAPABILITY 0x48#define SDCMSC_CLOCK_DIVIDER 0x4C#define SDCMSC_BD_BUFFER_STATUS 0x50#define SDCMSC_DAT_INT_STATUS 0x54#define SDCMSC_DAT_INT_ENABLE 0x58#define SDCMSC_BD_RX 0x60#define SDCMSC_BD_TX 0x80// SDCMSC_COMMAND bits#define SDCMSC_COMMAND_CMDI(x) (x << 8)#define SDCMSC_COMMAND_CMDW(x) (x << 6)#define SDCMSC_COMMAND_CICE 0x10#define SDCMSC_COMMAND_CIRC 0x08#define SDCMSC_COMMAND_RTS_48 0x02#define SDCMSC_COMMAND_RTS_136 0x01//SDCMSC_CARD_STATUS bits#define SDCMSC_CARD_STATUS_CICMD 0x01// SDCMSC_NORMAL_INT_STATUS bits#define SDCMSC_NORMAL_INT_STATUS_EI 0x8000#define SDCMSC_NORMAL_INT_STATUS_CC 0x0001// SDCMSC_DAT_INT_STATUS#define SDCMSC_DAT_INT_STATUS_TRS 0x01typedef struct cyg_sdcmsc_disk_info_t {int is_v20;int is_sdhc;cyg_uint32 rca;int connected;} cyg_sdcmsc_disk_info_t;static int sdcmsc_card_cmd(cyg_uint32 cmd, cyg_uint32 arg, cyg_uint32 *response) {// Send command to cardHAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_COMMAND, cmd);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_ARGUMENT, arg);// Wait for responsecyg_uint32 reg;cyg_uint32 mask = SDCMSC_NORMAL_INT_STATUS_EI | SDCMSC_NORMAL_INT_STATUS_CC;do {HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_NORMAL_INT_STATUS, reg);} while(!(reg & mask));HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_NORMAL_INT_STATUS, 0);// Optionally read response registerif(response) {HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_RESPONSE, *response);}// Check for errorsif(reg & SDCMSC_NORMAL_INT_STATUS_EI) {HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_ERROR_INT_STATUS, reg);if(reg & (1 << 3)) diag_printf("Command index error\n");if(reg & (1 << 1)) diag_printf("Command CRC error\n");if(reg & (1 << 0)) diag_printf("Command timeout\n");HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_ERROR_INT_STATUS, 0);return 0;}else {return 1;}}// Card initialization and identification implemented according to// Physical Layer Simplified Specification Version 3.01static int sdcmsc_card_init(cyg_sdcmsc_disk_info_t *data, char *serial, char *firmware_rev, char *model_num, cyg_uint32 *capacity) {cyg_uint32 reg;cyg_uint32 cmd;cyg_uint32 arg;// Send CMD0 to switch the card to idle statecmd = SDCMSC_COMMAND_CMDI(0);if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;// Send CMD8 offering 2.7V to 3.6V range// If the card doesn't responde it means either:// 1. Card supports v2.0 but can't communicate using// current voltage levels// 2. Card does not support v2.0cmd = SDCMSC_COMMAND_CMDI(8) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;data->is_v20 = sdcmsc_card_cmd(cmd, 0x1AA, NULL);do {HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_CARD_STATUS, reg);} while(reg & SDCMSC_CARD_STATUS_CICMD);// Repeat ACMD41 until card set the busy bit to 1// Since ACMD is an extended command, it must be preceded// by CMD55do {cmd = SDCMSC_COMMAND_CMDI(55) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;cmd = SDCMSC_COMMAND_CMDI(41) | SDCMSC_COMMAND_RTS_48;arg = data->is_v20 ? 0x40FF8000 : 0x00FF8000;if(!sdcmsc_card_cmd(cmd, arg, ®)) return 0;} while(!(reg & 0x80000000));data->is_sdhc = !!(reg & 0x40000000);// Issue CMD2 to switch from ready state to ident. Unfortunately, it is// not possible to read whole CID because the command can be issued only// once, and the peripheral can store only 32bit of the command at once.cmd = SDCMSC_COMMAND_CMDI(2) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, 0, NULL)) return 0;// Issue CMD3 to get RCA and switch from ident state to stby.cmd = SDCMSC_COMMAND_CMDI(3) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0, ®)) return 0;data->rca = reg & 0xFFFF0000;// Calculate card capacity. Use information stored in CSD register.cyg_uint32 card_capacity;if(data->is_sdhc) {cmd = SDCMSC_COMMAND_CMDI(9) | SDCMSC_COMMAND_CMDW(1) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;card_capacity = reg & 0x3F;card_capacity <<= 16;cmd = SDCMSC_COMMAND_CMDI(9) | SDCMSC_COMMAND_CMDW(2) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;reg >>= 16;card_capacity |= reg;card_capacity += 1;card_capacity *= 1000;}else {cmd = SDCMSC_COMMAND_CMDI(9) | SDCMSC_COMMAND_CMDW(1) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;cyg_uint32 read_bl_len = (reg >> 16) & 0x0F;cyg_uint32 c_size = reg & 0x3FF;c_size <<= 2;cmd = SDCMSC_COMMAND_CMDI(9) | SDCMSC_COMMAND_CMDW(2) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;c_size |= (reg >> 30) & 0x03;cyg_uint32 c_size_mult = (reg >> 15) & 0x07;card_capacity = c_size + 1;card_capacity *= 1 << (c_size_mult + 2);card_capacity *= 1 << (read_bl_len);card_capacity >>= 9;}// Fill disk identification struct using information in CID register// use OEM/APPlication ID field to fill model_num,// Product revision field to fill firmware_rev,// and Product serial number to field to fill serialcmd = SDCMSC_COMMAND_CMDI(10) | SDCMSC_COMMAND_CMDW(0) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;model_num[0] = (reg >> 16) & 0xFF;model_num[1] = (reg >> 8) & 0xFF;model_num[2] = 0;cmd = SDCMSC_COMMAND_CMDI(10) | SDCMSC_COMMAND_CMDW(2) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;firmware_rev[0] = (reg >> 24) & 0xFF;firmware_rev[1] = 0;serial[0] = (reg >> 16) & 0xFF;serial[1] = (reg >> 8) & 0xFF;serial[2] = reg & 0xFF;cmd = SDCMSC_COMMAND_CMDI(10) | SDCMSC_COMMAND_CMDW(3) | SDCMSC_COMMAND_RTS_136;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;serial[3] = (reg >> 24) & 0xFF;// Put card in transfer state cmd = SDCMSC_COMMAND_CMDI(7) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, data->rca, ®)) return 0;if(reg != 0x700) return 0;// Set block size to 512cmd = SDCMSC_COMMAND_CMDI(16) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 512, NULL)) return 0;// Set 4-bits bus modecmd = SDCMSC_COMMAND_CMDI(55) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, data->rca, NULL)) return 0;cmd = SDCMSC_COMMAND_CMDI(6) | SDCMSC_COMMAND_CICE | SDCMSC_COMMAND_CIRC | SDCMSC_COMMAND_RTS_48;if(!sdcmsc_card_cmd(cmd, 0x02, NULL)) return 0;return 1;}static int sdcmsc_card_queue(cyg_sdcmsc_disk_info_t *data, int direction_transmit,int block_addr,cyg_uint32 buffer_addr) { // SDSC cards use byte addressing, while SDHC use block addressing. // It is therefore required to multiply the address by 512 if // we are dealing with SDSC card, to remain compatible with the API.if(!data->is_sdhc) {block_addr <<= 9;}if(direction_transmit) {HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_BD_TX, buffer_addr);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_BD_TX, block_addr);}else {HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_BD_RX, buffer_addr);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_BD_RX, block_addr);}// Now wait for the responsecyg_uint32 reg;do {HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_DAT_INT_STATUS, reg);} while(!reg);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_DAT_INT_STATUS, 0);// Check for errorsif(reg == SDCMSC_DAT_INT_STATUS_TRS) {return 1;}else {if(reg & (1 << 5)) diag_printf("Transmission error\n");if(reg & (1 << 4)) diag_printf("Command error\n");if(reg & (1 << 2)) diag_printf("FIFO error\n");if(reg & (1 << 1)) diag_printf("Retry error\n");return 0;}}// This is an API function. Is is called once, in the beginningstatic cyg_bool sdcmsc_disk_init(struct cyg_devtab_entry* tab) {// Set highest possible timeoutHAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_TIMEOUT, 0xFFFE);// Reset the peripheralHAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_SOFTWARE_RESET, 1);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_CLOCK_DIVIDER, 2);HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_SOFTWARE_RESET, 0);// Call upper leveldisk_channel* ch = (disk_channel*) tab->priv;return (*ch->callbacks->disk_init)(tab);}// This function is called when user mounts the diskstatic Cyg_ErrNo sdcmsc_disk_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry *sub_tab, const char* name) {disk_channel *ch = (disk_channel*) (*tab)->priv;cyg_sdcmsc_disk_info_t *data = (cyg_sdcmsc_disk_info_t*) ch->dev_priv;// If the card was not initialized yet, it's time to do it// and call disk_connected callbackif(!data->connected) {cyg_disk_identify_t id;// Pass dummy CHS geometry and hope the upper level // will use LBA mode. To guess CHS we would need to// analyze partition table and confront LBA and CHS// addresses. And it would work only if proper LBA// field is stored in MBR. Is is definitely something// that should be done by upper level.id.cylinders_num = 1;id.heads_num = 1;id.sectors_num = 1;id.phys_block_size = 1;id.max_transfer = 512;// Initialize the carddata->connected = sdcmsc_card_init(data, id.serial, id.firmware_rev, id.model_num, &id.lba_sectors_num);if(data->connected) {// Let upper level know there is a new disk(*ch->callbacks->disk_connected)(*tab, &id);}}// Call upper levelreturn (*ch->callbacks->disk_lookup)(tab, sub_tab, name);}// API function to read block from the diskstatic Cyg_ErrNo sdcmsc_disk_read(disk_channel* ch, void* buf, cyg_uint32 blocks, cyg_uint32 first_block) {cyg_sdcmsc_disk_info_t *data = (cyg_sdcmsc_disk_info_t*) ch->dev_priv;int i;int result;cyg_uint32 reg;for(i = 0; i < blocks; i++) {// Check for free receive buffersHAL_READ_UINT32(SDCMSC_BASE + SDCMSC_BD_BUFFER_STATUS, reg);reg >>= 8;reg &= 0xFF;if(reg == 0) {return -EIO;}result = sdcmsc_card_queue(data, 0, first_block, (cyg_uint32) buf);if(!result) {return -EIO;}}return ENOERR;}// API function to write block to diskstatic Cyg_ErrNo sdcmsc_disk_write(disk_channel* ch, const void* buf, cyg_uint32 blocks, cyg_uint32 first_block) {cyg_sdcmsc_disk_info_t *data = (cyg_sdcmsc_disk_info_t*) ch->dev_priv;int i;int result;cyg_uint32 reg;for(i = 0; i < blocks; i++) {// Check for free transmit buffersHAL_READ_UINT32(SDCMSC_BASE + SDCMSC_BD_BUFFER_STATUS, reg);reg &= 0xFF;if(reg == 0) {return -EIO;}result = sdcmsc_card_queue(data, 1, first_block, (cyg_uint32) buf);if(!result) {return -EIO;}}return ENOERR;}// API function to fetch driver configuration and disk info.static Cyg_ErrNo sdcmsc_disk_get_config(disk_channel* ch, cyg_uint32 key, const void* buf, cyg_uint32* len) {CYG_UNUSED_PARAM(disk_channel*, ch);CYG_UNUSED_PARAM(cyg_uint32, key);CYG_UNUSED_PARAM(const void*, buf);CYG_UNUSED_PARAM(cyg_uint32*, len);return -EINVAL;}// API function to update driver status information.static Cyg_ErrNo sdcmsc_disk_set_config(disk_channel* ch, cyg_uint32 key, const void* buf, cyg_uint32* len) {cyg_sdcmsc_disk_info_t *data = (cyg_sdcmsc_disk_info_t*) ch->dev_priv;if(key == CYG_IO_SET_CONFIG_DISK_UMOUNT) { if(ch->info->mounts == 0) {data->connected = false;return (ch->callbacks->disk_disconnected)(ch); }else {return ENOERR;}}else {return -EINVAL;}}// Register the driver in the systemstatic cyg_sdcmsc_disk_info_t cyg_sdcmsc_disk0_hwinfo = {.connected = 0};DISK_FUNS(cyg_sdcmsc_disk_funs, sdcmsc_disk_read, sdcmsc_disk_write, sdcmsc_disk_get_config, sdcmsc_disk_set_config);DISK_CONTROLLER(cyg_sdcmsc_disk_controller_0, cyg_sdcmsc_disk0_hwinfo);DISK_CHANNEL(cyg_sdcmsc_disk0_channel, cyg_sdcmsc_disk_funs, cyg_sdcmsc_disk0_hwinfo, cyg_sdcmsc_disk_controller_0, true, //mbr supported 4 //partitions);BLOCK_DEVTAB_ENTRY(cyg_sdcmsc_disk0_devtab_entry, CYGDAT_DEVS_DISK_OPENCORES_SDCMSC_DISK0_NAME, 0, &cyg_io_disk_devio, &sdcmsc_disk_init, &sdcmsc_disk_lookup, &cyg_sdcmsc_disk0_channel);// EOF if_sdcmsc.c
enjoy!
- OpenRisc-30-SD card controller模块分析与验证
- OpenRisc-35-基于orpsoc,eCos的sd card controller的测试实验
- OpenRisc-38-基于orpsoc,linux的sd card controller的测试实验
- OpenRisc-51-基于orpsoc,linux的sd card controller的驱动程序
- OpenRisc-59-jtag_tap模块分析
- LINUX SD card driver分析
- SD Card 驱动流程分析
- SD Card 驱动流程分析
- SD Card 驱动流程分析
- OpenRisc-40-or1200的MMU模块分析
- OpenRisc-41-or1200的cache模块分析
- OpenRisc-42-or1200的ALU模块分析
- OpenRisc-43-or1200的IF模块分析
- OpenRisc-45-or1200的ID模块分析
- OpenRisc-46-or1200的MA模块分析
- OpenRisc-47-or1200的WB模块分析
- OpenRisc-48-or1200的SPRS模块分析
- OpenRisc-49-or1200的except模块分析
- oracle 将表从一个表空间移动到另一个表空间
- 升级Flash Builder 4.6中的Flash Player和AIR版本
- redis 资料收集
- 环境变量的配置
- 同事的Excel中做的数据图突然不能选择
- OpenRisc-30-SD card controller模块分析与验证
- oracle 相关
- 跟踪状态(TASK_TRACED)
- springmvc
- cocos2d-x 游戏暂停界面,监听home键,返回键,Menu键 解决方案
- sap 预制凭证与暂存凭证的区别
- HTTP协议详解
- net time 发生系统错误5的解决
- _cdecl和_stdcall