Zynq-Linux移植学习笔记之14-RapidIO驱动开发
2017-08-04 14:51
766 查看
Zynq-Linux移植学习笔记之14-RapidIO驱动开发
在对zynq进行linux驱动开发时,除了需要针对zynq内ARM自带的控制器适配驱动外,还需要对zynq PL部分的IP核进行驱动开发。对于ARM来说,zynq PL部分的IP核就是一段地址空间,这段地址空间包含了该IP的一系列寄存器,ARM操作该IP核的寄存器也就是操作这段地址空间,而PL部分IP的驱动也就是对IP寄存器的操作。
1、 硬件设计
在vivado内进行设计时,RapidIO IP核通过AXI总线与ARM相连,地址空间区域如图:从0x40000000-0x7FFFFFFF均为RapidIO IP的地址空间,注意这里的地址是物理地址,在zynq的裸程序中,可以通过xil_out32()或xil_in32()等函数直接操纵该地址的值,也即对RapidIO IP核寄存器的读写操作。
补充一点,考虑到RapidIO IP使用的一致性以及预防配置出错,硬件设计时已经将RapidIO IP寄存器进行了正确配置,这一部分是在硬件FPGA编程时实现的,软件部分并不需要从头开始配置RapidIO IP核。因此,对RapidIO IP驱动的开发也只需要实现对寄存器的读、写这两个函数即可。
2、 devicetree设计
由于RapidIO IP核位于PL部分,需要在devicetree中增加相应内容,如下:amba_pl { #address-cells= <0x1>; #size-cells= <0x1>; compatible= "simple-bus"; ranges; srio_axi_config@40000000{ compatible= "xlnx,xps-rio-1.00.a"; reg= <0x40000000 0x40000000>; }; };
Amba_pl对应PL部分的amba,devicetree中原有的amba对应PS部分,两个位于同一层。
3、 驱动设计
RapidIO IP核驱动实现对物理地址0x40000000到0x7fffffff的读、写操作,可以参考xilinxPL部分CAN IP核的驱动代码。实现过程需要注意地址的虚实转换,0x40000000开始的这一段地址是物理地址,需要将这段地址进行映射,确保CPU访问的地址经过MMU转换后确实对应这一地址。/* * rio-xiic.c * Copyright (c) 2002-2007 Xilinx Inc. * Copyright (c) 2009-2010 Intel Corporation * */ /* Supports: * Xilinx RapidIO */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/i2c-xiic.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/of.h> #define DRIVER_NAME "xiic-rio" #define SRIO_ZYNQ_BASEADDR 0x40000000 #define SRIO_ZYNQ_NODE_BASEADDR 0x10100 #define SRIO_ZYNQ_MAX_HOPCOUNT 13 struct xiic_rio { struct mutex lock; u8 *data; }; /* We need global varriable for maped address */ static void __iomem* _rio_base = NULL; static inline void rio_setreg32(unsigned int addrBase,unsigned int addrOffset,unsigned int value) { iowrite32(value, addrBase + addrOffset); } static inline int rio_getreg32(unsigned int addrBase,unsigned int addrOffset) { unsigned int reg_addr; reg_addr=addrBase+addrOffset; return ioread32(reg_addr); } static ssize_t hlMaintWrite(unsigned int dstId,unsigned short hopcount, unsigned int offset, unsigned int writedata) { unsigned int reg_addr; if( hopcount > SRIO_ZYNQ_MAX_HOPCOUNT ) { printk("!!!error, hopcount = %d, > %d\n",hopcount,SRIO_ZYNQ_MAX_HOPCOUNT); return -1; } rio_setreg32((unsigned int)_rio_base,SRIO_ZYNQ_NODE_BASEADDR,dstId); reg_addr = (((hopcount+1)<<24)|offset); rio_setreg32((unsigned int)_rio_base,reg_addr,writedata); return 0; } static ssize_t hlMaintRead(unsigned int dstId,unsigned short hopcount, unsigned int offset, void *mrdataAdr) { unsigned int reg_addr; if( hopcount > SRIO_ZYNQ_MAX_HOPCOUNT ) { printk("!!!error, hopcount = %d, > %d\n",hopcount,SRIO_ZYNQ_MAX_HOPCOUNT); return -1; } rio_setreg32((unsigned int)_rio_base,SRIO_ZYNQ_NODE_BASEADDR,dstId); reg_addr = (((hopcount+1)<<24)|offset); mrdataAdr = rio_getreg32((unsigned int)_rio_base,reg_addr); printk("M_SRIO_MAINT_REG_READ: hopcount = %d, offset = 0x%x, value = 0x%x\n",hopcount,offset,mrdataAdr); return 0; } static SIMPLE_DEV_PM_OPS(xiic_rio_pm_ops, hlMaintRead,hlMaintWrite); static int xiic_rio_probe(struct platform_device *pdev) { struct xiic_rio *rio; struct resource *res; unsigned int mtRdata=0; rio = kzalloc(sizeof(struct xiic_rio), GFP_KERNEL); if (!rio) return -ENOMEM; /* Get Mapped address */ _rio_base = ioremap_nocache(SRIO_ZYNQ_BASEADDR, 0xe000000); if (!_rio_base) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); platform_set_drvdata(pdev, rio); hlMaintRead(0xFF,0, 0, mtRdata); return 0; } static int xiic_rio_remove(struct platform_device *pdev) { struct xiic_rio *rio = platform_get_drvdata(pdev); kfree(rio); return 0; } static const struct of_device_id xiic_of_match[] = { { .compatible = "xlnx,xps-rio-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, xiic_of_match); static struct platform_driver xiic_rio_driver = { .probe = xiic_rio_probe, .remove = xiic_rio_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(xiic_of_match), .pm = &xiic_rio_pm_ops, }, }; module_platform_driver(xiic_rio_driver); MODULE_AUTHOR("info@mocean-labs.com"); MODULE_DESCRIPTION("Xilinx Rio IP Core driver"); MODULE_LICENSE("GPL v2");
上面代码中ioremap实现的就是物理地址的映射,该函数的第二个参数为映射的大小,由于模块上DDR只有1G,所以实际最大的映射空间只有224M,能访问IP核的实际地址空间为0x40000000-0x4e000000。驱动中实现读写两个函数,对zynq FPGA地址的访问可以直接调用ioread32()、iowrite32(),这两个函数和xil_out32()、xil_in32()相对应。
完成驱动后修改Kconfig文件和Makefile文件,加入驱动选项,这里是合在了I2C总线驱动里面:
4、 测试
对该驱动的测试主要是通过调用读函数访问地址空间,判断返回值是否符合预期。由于RapidIO IP核能够访问到CPS 1848,可以通过判断返回值是否是1848的device ID加以验证。在probe中调用函数hlMaintRead(0xFF,0, 0, mtRdata),返回值如下:
Value和1848 datasheet中的一致,验证通过。
5、 总结
在对zynq PL部分IP核的驱动开发过程中需要注意地址转换问题,下面两种异常都是属于地址问题这种异常是映射空间不够大,对应于ioremap第二个参数
相关文章推荐
- Zynq-Linux移植学习笔记之14-RapidIO驱动开发
- Zynq-Linux移植学习笔记之18-Zynq下NOR_FLASH挂载文件系统
- Zynq-Linux移植学习笔记之23-QSPI速度配置
- Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
- Zynq-Linux移植学习笔记之四-fsbl
- Zynq-Linux移植学习笔记之19-启动加载与固化
- Zynq-Linux移植学习笔记之17-Zynq下linuxPL部分Flash
- Zynq-Linux移植学习笔记之八-linux网络驱动
- Zynq-Linux移植学习笔记之二-知识点
- Zynq-Linux移植学习笔记之11-qspi驱动配置
- Zynq-Linux移植学习笔记之12-gpio驱动配置
- Zynq-Linux移植学习笔记之十-u-boot网络配置
- Zynq-Linux移植学习笔记之九-petalinux
- Zynq-Linux移植学习笔记之五-rootfs配置
- Zynq-Linux移植学习笔记之七-网络驱动
- Zynq-Linux移植学习笔记之22-Linux应用程序调用shell脚本
- Zynq-Linux移植学习笔记之16-Zynq下linux XADC驱动
- Zynq-Linux移植学习笔记之21-Linux启动时自动以root账号登录
- Zynq-Linux移植学习笔记之七-网络驱动
- Zynq-Linux移植学习笔记之一-入门