1. 程式人生 > >Zynq-Linux移植學習筆記之14-RapidIO驅動開發

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;
                   
[email protected]
{ 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("[email protected]");
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第二個引數