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中增加相应内容,如下:


 
 
   
   
   
   
  1. amba_pl {
  2. #address-cells= <0x1>;
  3. #size-cells= <0x1>;
  4. compatible= "simple-bus";
  5. ranges;
  6. srio_axi_config@40000000{
  7. compatible= "xlnx,xps-rio-1.00.a";
  8. reg= <0x40000000 0x40000000>;
  9. };
  10. };


Amba_pl对应PL部分的amba,devicetree中原有的amba对应PS部分,两个位于同一层。

 

3、  驱动设计

RapidIO IP核驱动实现对物理地址0x40000000到0x7fffffff的读、写操作,可以参考xilinxPL部分CAN IP核的驱动代码。实现过程需要注意地址的虚实转换,0x40000000开始的这一段地址是物理地址,需要将这段地址进行映射,确保CPU访问的地址经过MMU转换后确实对应这一地址。

 

 


 
 
   
   
   
   
  1. /*
  2. * rio-xiic.c
  3. * Copyright (c) 2002-2007 Xilinx Inc.
  4. * Copyright (c) 2009-2010 Intel Corporation
  5. *
  6. */
  7. /* Supports:
  8. * Xilinx RapidIO
  9. */
  10. #include
  11. #include
  12. #include
  13. #include
  14. #include
  15. #include
  16. #include
  17. #include
  18. #include
  19. #include
  20. #include
  21. #include
  22. #include
  23. #define DRIVER_NAME "xiic-rio"
  24. #define SRIO_ZYNQ_BASEADDR 0x40000000
  25. #define SRIO_ZYNQ_NODE_BASEADDR 0x10100
  26. #define SRIO_ZYNQ_MAX_HOPCOUNT 13
  27. struct xiic_rio {
  28. struct mutex lock;
  29. u8 *data;
  30. };
  31. /* We need global varriable for maped address */
  32. static void __iomem* _rio_base = NULL;
  33. static inline void rio_setreg32(unsigned int addrBase,unsigned int addrOffset,unsigned int value)
  34. {
  35. iowrite32(value, addrBase + addrOffset);
  36. }
  37. static inline int rio_getreg32(unsigned int addrBase,unsigned int addrOffset)
  38. {
  39. unsigned int reg_addr;
  40. reg_addr=addrBase+addrOffset;
  41. return ioread32(reg_addr);
  42. }
  43. static ssize_t hlMaintWrite(unsigned int dstId,unsigned short hopcount, unsigned int offset, unsigned int writedata)
  44. {
  45. unsigned int reg_addr;
  46. if( hopcount > SRIO_ZYNQ_MAX_HOPCOUNT )
  47. {
  48. printk( "!!!error, hopcount = %d, > %d\n",hopcount,SRIO_ZYNQ_MAX_HOPCOUNT);
  49. return -1;
  50. }
  51. rio_setreg32(( unsigned int)_rio_base,SRIO_ZYNQ_NODE_BASEADDR,dstId);
  52. reg_addr = (((hopcount+ 1)<< 24)|offset);
  53. rio_setreg32(( unsigned int)_rio_base,reg_addr,writedata);
  54. return 0;
  55. }
  56. static ssize_t hlMaintRead(unsigned int dstId,unsigned short hopcount, unsigned int offset, void *mrdataAdr)
  57. {
  58. unsigned int reg_addr;
  59. if( hopcount > SRIO_ZYNQ_MAX_HOPCOUNT )
  60. {
  61. printk( "!!!error, hopcount = %d, > %d\n",hopcount,SRIO_ZYNQ_MAX_HOPCOUNT);
  62. return -1;
  63. }
  64. rio_setreg32(( unsigned int)_rio_base,SRIO_ZYNQ_NODE_BASEADDR,dstId);
  65. reg_addr = (((hopcount+ 1)<< 24)|offset);
  66. mrdataAdr = rio_getreg32(( unsigned int)_rio_base,reg_addr);
  67. printk( "M_SRIO_MAINT_REG_READ: hopcount = %d, offset = 0x%x, value = 0x%x\n",hopcount,offset,mrdataAdr);
  68. return 0;
  69. }
  70. static SIMPLE_DEV_PM_OPS(xiic_rio_pm_ops, hlMaintRead,hlMaintWrite);
  71. static int xiic_rio_probe(struct platform_device *pdev)
  72. {
  73. struct xiic_rio *rio;
  74. struct resource *res;
  75. unsigned int mtRdata= 0;
  76. rio = kzalloc( sizeof(struct xiic_rio), GFP_KERNEL);
  77. if (!rio)
  78. return -ENOMEM;
  79. /* Get Mapped address */
  80. _rio_base = ioremap_nocache(SRIO_ZYNQ_BASEADDR, 0xe000000);
  81. if (!_rio_base)
  82. return -ENOMEM;
  83. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  84. platform_set_drvdata(pdev, rio);
  85. hlMaintRead( 0xFF, 0, 0, mtRdata);
  86. return 0;
  87. }
  88. static int xiic_rio_remove(struct platform_device *pdev)
  89. {
  90. struct xiic_rio *rio = platform_get_drvdata(pdev);
  91. kfree(rio);
  92. return 0;
  93. }
  94. static const struct of_device_id xiic_of_match[] = {
  95. { .compatible = "xlnx,xps-rio-1.00.a", },
  96. {},
  97. };
  98. MODULE_DEVICE_TABLE(of, xiic_of_match);
  99. static struct platform_driver xiic_rio_driver = {
  100. .probe = xiic_rio_probe,
  101. .remove = xiic_rio_remove,
  102. .driver = {
  103. .name = DRIVER_NAME,
  104. .of_match_table = of_match_ptr(xiic_of_match),
  105. .pm = &xiic_rio_pm_ops,
  106. },
  107. };
  108. module_platform_driver(xiic_rio_driver);
  109. MODULE_AUTHOR( "[email protected]");
  110. MODULE_DESCRIPTION( "Xilinx Rio IP Core driver");
  111. 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)