zoukankan      html  css  js  c++  java
  • 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
    ");
    //	reg = ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);
    //	printk(KERN_ALERT "sdcmsc_int_status before:0x%x
    ",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
    ");
    	// Wait for response
    	unsigned mask = SDCMSC_NORMAL_INT_STATUS_EI | SDCMSC_NORMAL_INT_STATUS_CC;
    //	printk(KERN_ALERT "CMD:%d, ARGUMENT:%d
    ", cmd, arg);
            
    	do {
    	reg = ioread32(sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);
    	reg=cpu_to_le32(reg);
    //	printk(KERN_ALERT "reg:0x%x
    ",reg);
    	} while(!(reg&mask));
    	iowrite32(0, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);
    	printk(KERN_ALERT "OK after SDCMSC_NORMAL_INT_STATUS
    ");
    	//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!
    ");
    		reg = ioread32(sdcmsc_base + SDCMSC_ERROR_INT_STATUS);
    		reg=cpu_to_le32(reg);
    		printk(KERN_ALERT "ERROR_INT_STATUS:0x%x
    ",reg);
    		if(reg & (1 << 3)) printk(KERN_ALERT "Command index error
    ");
    		if(reg & (1 << 1)) printk(KERN_ALERT "Command CRC error
    ");
    		if(reg & (1 << 0)) printk(KERN_ALERT "Command timeout
    ");
    		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
    ");
    	// Set highest possible timeout
    	reg = le32_to_cpu(0xFFFE);
    	iowrite32(reg,sdcmsc_base + SDCMSC_TIMEOUT);	
    	printk(KERN_ALERT "After first iowrite32
    ");
    	//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
    ", reg);
    //	iowrite32(0, sdcmsc_base + SDCMSC_NORMAL_INT_STATUS);
    
    	printk(KERN_ALERT "Ok at 1
    ");
    	//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
    ");
    	// 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
    ");
    	else
    		printk(KERN_ALERT "This isn't sd version 2.0
    ");
    		
    	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
    ",++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!
    ");
    	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
    ");
    	//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
    ");
    	//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!
    ");
    	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!
    ");
    	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!
    ");
    	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访问,地址如下:

    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 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
    ");
    		if(reg & (1 << 1)) diag_printf("Command CRC error
    ");
    		if(reg & (1 << 0)) diag_printf("Command timeout
    ");
    		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
    ");
    		if(reg & (1 << 4)) diag_printf("Command error
    ");
    		if(reg & (1 << 2)) diag_printf("FIFO error
    ");
    		if(reg & (1 << 1)) diag_printf("Retry error
    ");
    		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!


  • 相关阅读:
    连通分量板子
    2017年7月17日
    强连通缩点— HDU1827
    马拉车代码
    表达式求值
    Gym-100883F、Gym-101095B状态压缩小结
    矩阵快速幂小结-Hdu2604
    3月27日
    简单移动端自适应轮播图
    上了热搜榜前端工程师面试内幕
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3141253.html
Copyright © 2011-2022 走看看