OpenRisc-30-SD card controller模块分析与验证
引言
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 179 void __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 card
cmd = 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 response
unsigned 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 register
if(response) {
temp = ioread32(sdcmsc_base + SDCMSC_RESPONSE);
temp = cpu_to_le32(temp);
*response = temp;
} // Check for errors
if(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 timeout
reg = le32_to_cpu(0xFFFE);
iowrite32(reg,sdcmsc_base + SDCMSC_TIMEOUT);
printk(KERN_ALERT "After first iowrite32\n");
//Reset the peripheral
reg = 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 state
cmd = 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.0
cmd = 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");
else
printk(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 stby
cmd = 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 state
cmd = 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 512
cmd = 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 mode
cmd = 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.o
else
KERNELDIR ?= /home/openrisc/soc-design/linux
PWD := $(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 *.symvers
endif
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,小结
本小节只是对sd卡控制器的硬件的一个简单验证,并不是一个完整的SD卡控制器的linux驱动,但是能确定硬件的正确性,意义也是很大的。至于如何编写符合linux MMC/SD框架的驱动,那是另外的话题了,并且有很多资料可以参考,这里就不再赘述。
7,附录
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访问,地址如下:
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 0x01 typedef 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 card
HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_COMMAND, cmd);
HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_ARGUMENT, arg); // Wait for response
cyg_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 register
if(response) {
HAL_READ_UINT32(SDCMSC_BASE + SDCMSC_RESPONSE, *response);
} // Check for errors
if(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.01
static 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 state
cmd = 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.0
cmd = 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 CMD55
do {
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 serial
cmd = 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 512
cmd = 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 mode
cmd = 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 response
cyg_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 errors
if(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 beginning
static cyg_bool sdcmsc_disk_init(struct cyg_devtab_entry* tab) { // Set highest possible timeout
HAL_WRITE_UINT32(SDCMSC_BASE + SDCMSC_TIMEOUT, 0xFFFE); // Reset the peripheral
HAL_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 level
disk_channel* ch = (disk_channel*) tab->priv;
return (*ch->callbacks->disk_init)(tab);
} // This function is called when user mounts the disk
static 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 callback
if(!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 card
data->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 level
return (*ch->callbacks->disk_lookup)(tab, sub_tab, name);
} // API function to read block from the disk
static 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 buffers
HAL_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 disk
static 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 buffers
HAL_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 system static 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
可以参考这个代码来完成后续的符合linux下MMC/SD framework的驱动的编写工作。
enjoy!
OpenRisc-30-SD card controller模块分析与验证的更多相关文章
- OpenRisc-35-基于orpsoc,eCos的sd card controller的测试实验
引言 之前,曾经在orpsoc的平台上,测试验证过其sd card controller的linux的驱动,但是并不是很完美,经过努力,终于在eCos下完成了其全部功能的验证,包括驱动层验证,文件系统 ...
- 5. [mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)
零.说明(重要,需要先搞清楚概念有助于后面的理解) 1.mmc core card相关模块为对应card实现相应的操作,包括初始化操作.以及对应的总线操作集合.负责和对应card协议层相关的东西. 这 ...
- Linux SD/MMC/SDIO驱动分析
一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆 ...
- SD card技术了解并WINCE下SDHC驱动开发(updated)
Suumary: 简单介绍了一下SD卡的历史和发展,同时结合MX31 ADS上的WINCE 下SDHC驱动更深入的了解该硬件的一些行为特点. 了解SD card SD是Secure Digital C ...
- Linux SD/MMC/SDIO驱动分析_转
转自:Linux SD/MMC/SDIO驱动分析 https://www.cnblogs.com/cslunatic/p/3678045.html#3053341 一.SD/MMC/SDIO概念 ...
- 【转】linux设备驱动之MMC SD卡——核心层简单分析
原文网址:http://blog.chinaunix.net/uid-28685940-id-3889878.html /*************************************** ...
- 【原创】kafka controller源代码分析(二)
四.TopicDeletionManager.scala 管理topic删除的状态机,具体逻辑如下: TopicCommand发送topic删除命令,在zk的/admin/delete_topics目 ...
- I.MX6 uSDHC SD card register
/**************************************************************************** * I.MX6 uSDHC SD card ...
- OpenRisc-43-or1200的IF模块分析
引言 “喂饱饥饿的CPU”,是计算机体系结构设计者时刻要考虑的问题.要解决这个问题,方法大体可分为两部分,第一就是利用principle of locality而引进的cache技术,缩短取指时间,第 ...
随机推荐
- JVM--常见的虚拟机回收算法
Serial GC -XX:+UseSerialGC 这样就设置为了串行GC回收方式,这种方式在Client模式下是默认的. 一般是使用在单机程序,小内存,CPU个数少的机器上. 没有复杂的算法,只用 ...
- 动态规划之一最长上升子序列LIS
//最长上升子序列 #include<iostream> #include<cstring> using namespace std; const int maxn = 101 ...
- 高级UIKit-04(NSUserDefaults、NSKeyedArchiver、对象归档方法)
[day05_1_UserDefault]:判断应用程序是否是第一次运行 NSUserDefaults:用来保存应用程序的配置信息如:程序运行次数,用户登陆信息等. // 使用系统提供的NSUserD ...
- 列表标题栏添加CheckBox(自定义HanderView的时候实现)
前段时间项目上的要求,要实现一个列表(见下图1).类似网页上的列表,可以通过选中标题栏的复选框,实现全选或者全不选的功能.但是看了很久,都没看到Qt哪个方法可以实现在标题栏添加控件. 图1 要实现这样 ...
- Linux 命令整理
一.文件目录命令 1.建立目录:mkdir 目录名 2.删除空目录:rmdir 目录名 3.无条件删除子目录: rm -rf 目录名 4.改变当前目录:cd 目录名 (进入用户home目录:cd ~; ...
- Swift学习笔记十三:继承
一个类能够继承(inherit)还有一个类的方法(methods),属性(property)和其他特性 一.基本的语法 class Human{ var name :String init(){ na ...
- UI_UIImageView 基本操作
UI_UIImageView 经常用法 // 使用ImageView 通过 name 找到图片 UIImage *image = [UIImage imageNamed:@"bg_2&quo ...
- Linux下基于Erlang的高并发TCP连接压力实验
1.实验环境: 联想小型机: 操作系统:RedHat Enterprise LinuxServer release6.4(Santiago) 内核版本号:Linux server1 2.6.32-35 ...
- BZOJ 1257: [CQOI2007]余数之和sum( 数论 )
n >= k 部分对答案的贡献为 k * (n - k) n < k 部分贡献为 ∑ (k - ⌊k / i⌋ * i) = ∑ , ⌊k / i⌋ 相等的数是连续的一段, 此时这段连 ...
- input标签的hidden属性的应用及作用
定义:传输关于客户端/服务器交互的状态信息. Transmits state information about client/server interaction. 解释: 此元素在页面中不显示,在 ...