OpenRisc-38-基于orpsoc,linux的sd card controller的测试实验

引言

之前,我们在eCos下对ORPSoC的SD卡控制器进行了测试,这次我们将进行一次基于linux的测试和验证。

关于基于eCos下的SD card controller测试请参考:

http://blog.csdn.net/rill_zhen/article/details/9365477

http://blog.csdn.net/rill_zhen/article/details/9111213


本小节只验证对SD卡的基本的初始化,表示SD卡控制器可以在linux下正常工作。

1,测试目的

前面,我们已经在eCos平台下对ORPSoC的SD卡控制器进行了测试,实现了对SD卡的文件的创建,读写操作。但是在linux下还没有成功,linux在ORPSoC上也是非常重要的OS,所以非常需要完成基于linux的SD card controller的驱动。


2,测试步骤

本小节的操作过程,在之前的blog中已经反复描述,这里就不在赘述,如有疑问请参考:

http://blog.csdn.net/rill_zhen/article/details/8700937

http://blog.csdn.net/rill_zhen/article/details/8535317


3,驱动编码

参考之前基于eCos下的驱动,我们可以很容易得到linux下的驱动,code list 如下:

和之前相同,也是三个文件:ip_mkg.c,ip_mkg.h,Makefile

1>ip_mkg.c


/*
*
* rill mkg driver
*
*/
#include <linux/vmalloc.h>
#include <linux/slab.h>

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* get_user and put_user */
//#include <linux/clk.h>
//#include <linux/ioport.h>
#include <asm/io.h> /*ioremap*/
#include <linux/platform_device.h> /*cleanup_module*/

#include <asm-generic/io.h>

#include "ip_mkg.h"


#include <linux/interrupt.h>
#include <linux/delay.h>

volatile int g_irq_test_counter = 0;


void	__iomem 	*g_mkg_mem_base = NULL;


typedef struct sdc_data_s 
{
 int is_v20;
 int is_sdhc;
 unsigned int rca;
 int connected;
}sdc_data_s;




static int device_open(struct inode *inode, struct file *file)
{
	g_mkg_mem_base = ioremap(MKG_MEM_BASE,MKG_MEM_LEN);
	if(NULL == g_mkg_mem_base)
	{
		printk(KERN_ERR "mkg open ioremap error!\n");
		return -1;
	}
	else
	{
		printk("mkg ioremap addr:%d!\n",(int)g_mkg_mem_base);
	}

	return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
	return 0;
}


static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
	/*int ret_val = 0;

	char * data = NULL;
	
	data = (char*)kmalloc(4, GFP_KERNEL);
	if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) 

	ioread32(g_mkg_mem_base+length);
	printk("============read:%d\n",);*/

	printk("mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter);
	
	return 1;
}

static ssize_t device_write(struct file *filp, const char *buffer, size_t count, loff_t *offset)
{
	//iowrite32(2,g_mkg_mem_base);
	return 1;
}

long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
#if 0

   int ret_val = 0;
   unsigned int ret = 0;
   struct reg_data *new_regs;
   printk("ioctl======\n");

   switch(ioctl_num)
   {
      case IOCTL_REG_SET:
	  {
		 new_regs = (struct reg_data*)kmalloc(sizeof(struct reg_data), GFP_KERNEL);
		 if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) 
		 	{
			    kfree(new_regs);
			    printk(KERN_ERR " error copy line_datafrom user.\n");
				return -1;
		 	}

			//iowrite16(new_regs->value,g_mkg_mem_base+new_regs->addr);
		 kfree(new_regs);
     }
	 break;

	case IOCTL_REG_GET:
	{
	 new_regs = (struct reg_data*)kmalloc(sizeof(struct reg_data), GFP_KERNEL);
	 if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) 
	 	{
		    kfree(new_regs);
		    printk(KERN_ERR " error copy line_datafrom user.\n");
			return -1;
	 	}

		//ret = ioread16(g_mkg_mem_base+new_regs->addr);
	 	kfree(new_regs);
		return ret;
	}
	break;
      
   }
#endif

  return -1;
}

struct file_operations our_file_ops = {
  .unlocked_ioctl = device_ioctl,
  .read = device_read,
  .write = device_write,
  .open = device_open,
  .release = device_release,
  .owner = THIS_MODULE,
};





void reg_set(int addr,unsigned int value)
{
	iowrite32(le32_to_cpu(value),g_mkg_mem_base+addr);
}

unsigned int reg_get(int addr,unsigned int * value)
{
	unsigned int ret = 0;

	ret = cpu_to_le32(ioread32(g_mkg_mem_base+addr));
	*value = ret;

	return ret;
}

int disk_init(void)
{
	// Set highest possible timeout
	reg_set(SDCMSC_TIMEOUT,0xfffe);
	
	// Reset the peripheral
	reg_set(SDCMSC_SOFTWARE_RESET,1);
	reg_set(SDCMSC_CLOCK_DIVIDER,2);
	reg_set(SDCMSC_SOFTWARE_RESET,0);
	printk("disk init ok!\n");
	return 1;
}

static int sdcmsc_card_cmd(unsigned int cmd,unsigned int arg,unsigned int *response,int flag) 
{	
	unsigned int reg; 
	unsigned int mask;
	
	// Send command to card	
	reg_set(SDCMSC_COMMAND, cmd);	
	reg_set(SDCMSC_ARGUMENT, arg);	
	// Wait for response	
	
	mask = SDCMSC_NORMAL_INT_STATUS_EI |  SDCMSC_NORMAL_INT_STATUS_CC;
	do		
	{		
		reg_get(SDCMSC_NORMAL_INT_STATUS, ®);
	} while(!(reg & mask));	

	reg_set(SDCMSC_NORMAL_INT_STATUS, 0);	
	// Optionally read response register	
	if(flag) 
	{
		reg_get(SDCMSC_RESPONSE, response);
	}
	// Check for errors	
	if(reg & SDCMSC_NORMAL_INT_STATUS_EI) 
	{		
		reg_get(SDCMSC_ERROR_INT_STATUS, ®);
		if(reg & (1 << 3)) printk("Command index error\n");
		if(reg & (1 << 1)) printk("Command CRC error\n");		
		if(reg & (1 << 0)) printk("Command timeout\n");		
		reg_set(SDCMSC_ERROR_INT_STATUS, 0);		

		return 0;
	}
	else 
	{
		return 1;
	}
}


int sdc_card_init(sdc_data_s * data,char* serial,char* firmware_rev,char* model_num,unsigned int* capacity)
{
	unsigned int reg = 0;
	unsigned int cmd = 0;
	unsigned int arg = 0;
	unsigned int card_capacity;
	unsigned int read_bl_len;
	unsigned int c_size;
	unsigned int c_size_mult;

	// Send CMD0 to switch the card to idle state	
	cmd = SDCMSC_COMMAND_CMDI(0);
	if(!sdcmsc_card_cmd(cmd, 0, NULL,0)) 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,0);
	printk("is_v20:0x%x\n",data->is_v20);//rill add debug	

	do {
			reg_get(SDCMSC_CARD_STATUS, ®);
	   } 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,0)) return 0;
		cmd = SDCMSC_COMMAND_CMDI(41) |
			SDCMSC_COMMAND_RTS_48;
		arg = data->is_v20 ?
			0x40FF8000 :
			0x00FF8000;
		if(!sdcmsc_card_cmd(cmd, arg, ®,1)) return 0;
	} while(!(reg & 0x80000000));

	data->is_sdhc = !!(reg & 0x40000000);
	printk("is_sdhc:0x%x\n",data->is_sdhc);	
	// 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,0)) 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, ®,1)) return 0;
	data->rca = reg & 0xFFFF0000;
	printk("rca:0x%x\n",data->rca);
	// Calculate card capacity. Use information stored in CSD register.	
	
	if(data->is_sdhc)
	{	
		cmd = SDCMSC_COMMAND_CMDI(9) |
			SDCMSC_COMMAND_CMDW(1) |
			SDCMSC_COMMAND_RTS_136;
		if(!sdcmsc_card_cmd(cmd, data->rca, ®,1)) 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, ®,1)) 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, ®,1)) return 0;
		read_bl_len = (reg >> 16) & 0x0F;
		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, ®,1)) return 0;
		c_size |= (reg >> 30) & 0x03;	
		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;	
	}	
	printk("card capacity,sector_num:0x%x,size_bytes:0x%x\n",card_capacity,card_capacity*512);	
	// 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, ®,1)) 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, ®,1)) 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, ®,1)) 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, ®,1)) 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,0)) 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,0)) return 0;	
	cmd = SDCMSC_COMMAND_CMDI(6) |
		SDCMSC_COMMAND_CICE |
		SDCMSC_COMMAND_CIRC |
		SDCMSC_COMMAND_RTS_48;
	if(!sdcmsc_card_cmd(cmd, 0x02, NULL,0)) return 0;
	printk("sdc init success!\n");	

	return 1;
}

int disk_lookup(void)
{
	sdc_data_s data;
	char serial;
	char firmware_rev;
	char model_num;
	unsigned int capacity;

	data.connected = 0;

	data.connected = sdc_card_init(&data,&serial,&firmware_rev,&model_num,&capacity);
	if(data.connected)
	{
		printk("sdc_card_init done!\n");
	}
	else
	{
		printk("sdc_card_init failed!\n");
	}

	return 1;
}


void test(void)
{
		
	printk("sdc test start4==\n");
	disk_init();
	disk_lookup();


	printk("sdc test end==\n\n"); 

}






int init_module()
{
	int ret_val;
	int ret;
	void __iomem *ret_from_request;


	//=== Allocate character device 
	ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &our_file_ops);
	if (ret_val < 0)
	{
		printk(KERN_ALERT " device %s failed(%d)\n", DEVICE_NAME, ret_val);
		return ret_val;
	}

	ret = check_mem_region(MKG_MEM_BASE, MKG_MEM_LEN);
	if (ret < 0) 
	{
		printk(KERN_ERR "mkg check_mem_region bussy error!\n");
		return -1;
	}

	ret_from_request = request_mem_region(MKG_MEM_BASE, MKG_MEM_LEN, "ip_mkg");

	//===ioremap mkg registers

	g_mkg_mem_base = ioremap(MKG_MEM_BASE,MKG_MEM_LEN);
	if(NULL == g_mkg_mem_base)
	{
		printk(KERN_ERR "mkg ioremap error!\n");
		return -1;
	}
	else
	{
		;//printk("mkg ioremap addr:%d!\n",(unsigned int)g_mkg_mem_base);
	}

	printk("mkg module init done!\n");


	test();

	return 0;
}

void cleanup_module()
{
	release_mem_region(MKG_MEM_BASE, MKG_MEM_LEN);

	unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rill zhen:rill_zhen@126.com");



2>ip_mkg.h


#ifndef __IP_MKG_H__
#define __IP_MKG_H__

#define MAJOR_NUM	102
#define DEVICE_NAME	"ip_mkg"
#define MKG_MEM_BASE 0x9e000000
#define MKG_MEM_LEN	256

#define MKG_IRQ_INDEX	0x3


#define IOCTL_REG_SET 0
#define IOCTL_REG_GET 1


// 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



#endif



3>Makefile


# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)

    # Assume the source tree is where the running kernel was built
    # You should set KERNELDIR in the environment if it's elsewhere
    KERNELDIR ?= /home/openrisc/soc-design/linux
    # The current directory is passed to sub-makes as argument
    PWD := $(shell pwd)

modules:
	make -C $(KERNELDIR) M=$(PWD) modules ARCH=openrisc CROSS_COMPILE=or32-linux-

modules_install:
	make -C $(KERNELDIR) M=$(PWD) modules_install ARCH=openrisc CROSS_COMPILE=or32-linux-

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers

.PHONY: modules modules_install clean

else
    # called from kernel build system: just declare what our modules are
    obj-m := ip_mkg.o
endif



4,验证结果

参考之前的操作步骤将驱动进行编译,运行linux,将ip_mkg.ko弄到板子上,并insmod。执行结果如下:

从中可以看出:

1>我的SD卡控制器是支持2.0的

2>我的SD卡是SDHC,不是SDSC。

3>我的SD卡的容量是0xea600000,换成十进制就是3932160000,也就是3.9G多一点。这和SD卡表面标示的"4G"是符合的,也是和SDHC的标准符合的。





5,SD卡的IO

1>编码

上面的测试证明SD卡控制器是初始化成功了,但是SD卡能不能读写还是疑问,所以,为了进一步验证,我们需要编写简单的读写测试函数,代码如下:

需要说明的是,我们只读取第一个block的数据(0~511。共512 bytes),所以dma地址为0。

另外需要注意的是sd卡控制器的DMA需要的是物理地址,所以需要把kmalloc获得的虚拟地址转换一下。

还有,这个代码有点小问题,由于virt_adr是4个字节的,所以for循环512是不对的,只需要循环512/4次就可以了。

void sdc_read(void)
{
	int loop = 0;
		unsigned int reg; 
		unsigned int  * virt_adr;
		unsigned int phy_adr;
		
		virt_adr = (unsigned int  *)kmalloc(512, GFP_DMA);
		phy_adr=virt_to_phys(virt_adr);
	
		reg_set(SDCMSC_BD_RX,phy_adr);
		reg_set(SDCMSC_BD_RX,0);
	
		do {
				reg_get(SDCMSC_DAT_INT_STATUS, ®);
		   } while(!reg);
	
		if(reg == SDCMSC_DAT_INT_STATUS_TRS)
		{
			for(loop=0;loop<512;loop++)
			{
				printk("virt_adr:0x%x=0x%x\n",loop,*(virt_adr+loop));
			}
		}
		else
		{
			printk("sdc read error:0x%x!\n",reg);
		}

}

void test(void)
{
		
	printk("sdc test start4==\n");
	disk_init();
	disk_lookup();

	sdc_read();

	printk("sdc test end==\n\n"); 

}


2>测试结果

修改完代码之后,重新编译加载就可得到结果,为了验证读出的数据是否正确,我们用winhex软件也读一下,两者做一个比较,一看便知。

左边是驱动中读出的数据,右边是winhex读出的数据,两者相同,说明SD卡的基本I/O是没问题的。下一步的就是文件系统的事情了。



6,小结

经过努力,现在ORPSoC中的SD card controller在eCos下和linux下都可以work了。

有了SD卡的帮助,我们可以实现很多功能,要不然没有SD卡,ORPSoC的外部flash最大只能是8MB,对应稍大点的应用(显示一段视频,图片),显然不够用。

enjoy!


7,参考文献

http://blog.csdn.net/rill_zhen/article/details/9365477


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值