阅读:2211回复:5
linux外设(SJA1000)驱动问题
在做一个ARM9-AT91RM9200挂CAN-SJA1000芯片的linux驱动时,出现以下问题:
把实地址通过ioremap映射到内核虚拟地址以后,对CAN的测试寄存器进行读写 的时候,出现不稳定的状况,写数进去,然后再读出来,这样写读26万多次,总会出现 一些读出来的数与写进去的不一致的现象,有时候多到几百次不一致,有时候 少,大概就几次。大家帮忙分析一下可能是什么原因导致的?谢谢了。 linux内核版本:linux-2.4.19 CPU:AT91RM9200 启动过程如下:9200-boot -> u-boot-1.1.4 -> linux-2.4.19 CAN芯片的片选初始化在 9200-boot 中进行,初始化过程如下: void AT91F_Initcs4cs5() { //* Setup MEMC to support CS4=smc AT91C_BASE_EBI->EBI_CSA |= AT91C_EBI_CS4A_SMC; //* Setup Flash AT91C_BASE_SMC2->SMC2_CSR[4] = (AT91C_SMC2_NWS & 0x7f) | AT91C_SMC2_WSEN | (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; AT91C_BASE_SMC2->SMC2_CSR[5] = (AT91C_SMC2_NWS & 0x7f) | AT91C_SMC2_WSEN | (AT91C_SMC2_TDF & 0x300) | AT91C_SMC2_BAT | AT91C_SMC2_DBW_8; } 在u-boot中定义 CONFIG_SKIP_LOWLEVEL_INIT 这样boot中的cpu初始化配置就不会被改写了。 有一个现象必须提一下,我在 9200-boot中用如下代码测试则写读都正确,测试多少次都不会 出现不一致的现象。 ============9200-boot的can测试代码 开始================= #define CAN0 (volatile char*)0x50040000 volatile char *BASEADD; void setcanport(unsigned int reg, unsigned char data1) { BASEADD = CAN0; *(BASEADD+8)=reg; *(BASEADD)=data1; } unsigned char getcanport(unsigned char reg) { unsigned char temp; BASEADD = CAN0; *(BASEADD+8)=reg; temp=*BASEADD; return (temp); } //寄存器读写测试 int RWtest(unsigned char chanal) { unsigned char temp = 0; int i,j,readErr = 0, readOk = 0; for(i=0; i < 10240; i++) { for(j=0;j<256;j++) { setcanport(9,j); temp = getcanport(9); if(temp != j) readOk += 1; else readErr += 1; } } return readErr; } ============9200-boot的can测试代码 结束================= ============linux下的can驱动 开始================= #define CAN_SYS_HWADDR_BASE 0x50040000 static void *can_sys_v_addr; //map can phy_addr to linux_addr static void setcanport(unsigned char reg, unsigned char data) { writeb(reg,can_sys_v_addr+0x8);//模拟ALE锁存地址,发地址信号; CAN的ale信号线接A3脚 writeb(data,can_sys_v_addr); //发送数据到地址 } static unsigned char getcanport(unsigned char reg) { unsigned char temp = 0; writeb(reg,can_sys_v_addr+0x8); //模拟ALE锁存地址,发地址信号; temp=readb(can_sys_v_addr); //从指定地址读取数据 return (temp); } /****************************************************************** * 函 数 名: rw_test * 功 能: can芯片寄存器读写测试 * 入口参数: unsigned char chanal //can端口选择 *******************************************************************/ static int rw_test() { unsigned char temp = 0xBB; int i,j,readErr = 0, readOk=0; for(j=0; j < 1024; j++ ) { for(i=0; i < 256; i++) { setcanport(9,i); temp = getcanport(9); if( temp!= i) { readErr += 1; } else { readOk +=1; } } } printk("========[Test end.] readErr = %d readOk = %d======\n", readErr, readOk); return num; } /*--linux设备文件结构定义---*/ static struct file_operations can_fops = { // owner: THIS_MODULE, read : read_can, write: write_can, ioctl: ioctl_can, open : open_can, release: release_can, }; #ifdef CONFIG_DEVFS_FS static devfs_handle_t devfs_can_dir, devfs_can_ram; #endif static int __init test_can_init(void) { int result; int i = 0; struct resource *res; AT91F_PIOB_CfgPMC(); result=register_chrdev(0,DEVICE_NAME,&can_fops); //注册一个设备,得到驱动的主设备号,动态 if(result<0) { printk("cannot get can major number\n"); //没有成功 return result; } canMajor = result; printk("CAN are successful registed major:%d\n", canMajor); #ifdef CONFIG_DEVFS_FS devfs_can_dir = devfs_mk_dir(NULL, "can_b", NULL); devfs_can_ram = devfs_register(devfs_can_dir, DEV_FILE_NAME , DEVFS_FL_DEFAULT, canMajor, CAN_MINOR , S_IFCHR | S_IRUSR | S_IWUSR, &can_fops, NULL); #endif res = request_mem_region(CAN_SYS_HWADDR_BASE, 0x10, "can_b"); if(res == NULL) { printk("<1>request_mem_region:can_b failed!\n"); return -1; } can_sys_v_addr = ioremap(CAN_SYS_HWADDR_BASE,0x10);//映射物理地址到IO内存,可以让软件直接访问IO内存 if(can_sys_v_addr == NULL) { printk("<1>ioremap:can_b failed!\n"); return -1; } } #ifdef DEBUG_PRINT printk(DEVICE_NAME "initialized\n"); #endif return 0; } static void __exit test_can_exit(void) { unsigned int i = 0; #ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_can_ram); devfs_unregister(devfs_can_dir); #endif iounmap(can_sys_v_addr); release_mem_region(CAN_SYS_HWADDR_BASE, 0x10); unregister_chrdev(canMajor,DEVICE_NAME); } /*--linux 模块驱动注册函数---*/ module_init(test_can_init); module_exit(test_can_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("dddd"); MODULE_DESCRIPTION("CAN driver for at91rm9200"); ============linux下的can驱动 结束================= |
|
沙发#
发布于:2007-03-09 11:49
检查时序配合是否合适,建议static void setcanport()函数 每次写数据后延时很短时间,再测试一下 | 写完后读一下若不正确重复写直到正确或超时,不使用延时.若单纯操作的是寄存器,就奇怪了.
|
|
板凳#
发布于:2007-03-09 14:31
引用第1楼zhaoyanghong于2007-03-09 11:49发表的“”: 谢谢回复。你说的我也有试过,也是不行的。can芯片的寄存器有些只读,有的只写,所有重复写入读出的方法行不通。 问题是在boot里面,直接操作实地址都是成功的,那应该说明时序配合应该合适的吧? |
|
地板#
发布于:2007-03-09 17:12
引用第2楼beta_guo于2007-03-09 14:31发表的“”: 如此那是, 你关中断了吗 ? NMI中断有无发生? |
|
地下室#
发布于:2007-03-12 22:28
中断关闭了
|
|
5楼#
发布于:2007-03-13 09:18
引用第4楼beta_guo于2007-03-12 22:28发表的“”: 每次写后printk "jiffes" 或其它方式跟踪一下,或是外设硬件内存有问题---更换一个试试[但你将实模式是好的,这种情况也不会发生], 是很奇怪! |
|