ZYNQ下Linux驱动代码的编写

好长时间没有更新博客了,因为最近比较烦躁所以没有心情去写了,今天这篇呢就写一下在ZYNQ上跑linux系统后的驱动代码编写。

用到ZYNQ芯片后那么必然会涉及到PS和PL之间的通信(本文主要说的是ZYNQ跑的linux系统。裸机不在本文范围内(一直觉得xilinx的SDK做的比较烂,不想用)),PL和PS之间的通信总线是基于AXI总线(关于这个总线自己去查,他有好几种方式,我不管了),一般情况下PL较大的数据传递到PS时用DMA是比较方便的(也需要写驱动),但是也有另外一种方式通过PL内的BRAM来实现。

BRAM通信有啥好处呢(自己想)?哎呀,不想码字了,好累,直接上代码吧,看下面代码:

/*  axi-bram.c - The simplest kernel module.

* Copyright (C) 2013 - 2016 Xilinx, Inc
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.

*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License along
*   with this program. If not, see <http://www.gnu.org/licenses/>.

*/
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>

#include "axi-bram.h"
/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("axi-bram - loadable module template generated by petalinux-create -t modules");

#define DRIVER_NAME "axibram"

static int irq ;

unsigned char slchrdev = 0;
static unsigned char bram_flg;

static dev_t devno;
static struct cdev bram_cdev;
static struct class *axi_bram_class;
static struct fasync_struct *irq_asunc;
static struct axi_bram_dev *axi_bram_dev;

static irqreturn_t axi_bram_irq(int irq, void *lp)
{
//	printk("axi-bram interrupt\n");
	bram_flg = 1;
	kill_fasync(&irq_asunc,SIGIO,POLL_IN);
	return IRQ_HANDLED;
}

static int axi_bram_fasync(int fd, struct file *filp, int on)
{
	return fasync_helper(fd,filp,on,&irq_asunc);
}

/* File operations */
static int axi_bram_open(struct inode *inode, struct file *filp)
{
	unsigned int mn;
	printk("bram node open\n");
	mn = iminor(inode);
	filp->private_data = (void *) mn;
	return SUCCESS;
}

int axi_bram_release(struct inode *inode, struct file *filp)
{
	return SUCCESS;
}

static ssize_t axi_bram_write(struct file * filep, const char __user * buf,
                         size_t count, loff_t * f_pos)
{
	printk("in to write fun\n");
//	copy_from_user(buf, kbuf, count);
	return count;
}

static ssize_t axi_bram_read(struct file * filep, char __user * buf,
                         size_t count, loff_t * f_pos)
{
//	printk("in to read fun\n");
	copy_to_user(buf, &bram_flg, count);
	
	bram_flg = 0;
	return count;
}


static long axi_bram_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
{
	switch (cmd)
	{
		case AXI_BRAM_START:
			writel(axi_bram_dev->bram_init_data, axi_bram_dev->bram_virtaddr+4*1);				// set init data 
			writel(axi_bram_dev->bram_data_len,  axi_bram_dev->bram_virtaddr+4*2);				// set data len
			writel(axi_bram_dev->bram_start_addr,axi_bram_dev->bram_virtaddr+4*3);				// set start data
			writel(PL_BRAM_START,axi_bram_dev->bram_virtaddr);
			break;
		case AXI_BRAM_DATA_LEN:
			axi_bram_dev->bram_data_len = arg;
			break;
		case AXI_BRAM_INIT_DATA:
			axi_bram_dev->bram_init_data = arg;
			break;
		case AXI_BRAM_START_ADDR:
			axi_bram_dev->bram_start_addr = arg;
			break;
		case AXI_BRAM_STOP:
			writel(PL_BRAM_STOP,axi_bram_dev->bram_virtaddr);
			break;
		case AXI_BRAAM_DEINIT:
			break;
		default:
			return -EOPNOTSUPP;
	}

	return SUCCESS;
}

static struct file_operations axi_bram_fops =
{
	.owner  = THIS_MODULE,
	.read   = axi_bram_read,
	.open   = axi_bram_open,
	.write  = axi_bram_write,
	.fasync = axi_bram_fasync,
	.unlocked_ioctl = axi_bram_ioctl,
	.release = axi_bram_release,
};

static int axi_bram_probe(struct platform_device *pdev)
{
	int rc;
	int status = 0;
	struct device_node *node=NULL;							
	/*Allocate device node */
	node = pdev->dev.of_node;
	printk("Device Tree Probing\n");
	
	/* Get IRQ for the device */
	irq = platform_get_irq(pdev, 0);
	if (!irq) {
		printk("no IRQ found\n");
		return 0;
	}
	
	rc = request_irq(irq, &axi_bram_irq, 0, DRIVER_NAME, NULL);
	if (rc) {
		printk("Could not allocate interrupt.\n");
		goto fail3;
	}
	
	/* Allocate a private structure to manage this device */
	axi_bram_dev = kmalloc(sizeof(struct axi_bram_dev), GFP_KERNEL);
	if (axi_bram_dev == NULL)
	{
		printk("unable to allocate device structure\n");
		return -ENOMEM;
	}else
		memset(axi_bram_dev, 0, sizeof(struct axi_bram_dev));
		
	axi_bram_dev->bram_virtaddr = of_iomap(node, 0);
	if (!axi_bram_dev->bram_virtaddr)
	{
		status = -ENOMEM;
		printk("unable to IOMAP adc registers\n");
		goto fail1;
	}
	
	axi_bram_dev->pdev = pdev;					
	/* Initialize our device mutex */
	mutex_init(&axi_bram_dev->mutex);		   
	
	status =alloc_chrdev_region(&devno,0, AXI_BRAM_COUNT,MODULE_NAME);
	if (status < 0)
	{
		printk("unable to alloc chrdev main devnod slaver devnod\n");
		goto fail2;
	}
	
	cdev_init(&bram_cdev, &axi_bram_fops);
	bram_cdev.owner = THIS_MODULE;
	bram_cdev.ops   = &axi_bram_fops;
	
	status = cdev_add(&bram_cdev,devno,AXI_BRAM_COUNT);

	axi_bram_class = class_create(THIS_MODULE, MODULE_NAME);
	printk("class create seccussful\n");
	
	device_create(axi_bram_class, NULL,
	              MKDEV(MAJOR(devno), slchrdev),
	              NULL, DRIVER_NAME);

	printk("xlnx PL user ip added successfully\n");
	return SUCCESS;
	
fail3:
	free_irq(irq, NULL);
fail2:
	free_irq(irq, NULL);
	kfree(axi_bram_dev);
	iounmap(axi_bram_dev->bram_virtaddr);
fail1:
	free_irq(irq, NULL);
	kfree(axi_bram_dev);
	
	return status;	
}

static int axi_bram_remove(struct platform_device *pdev)
{		

	free_irq(irq, NULL);
	device_destroy(axi_bram_class,MKDEV(MAJOR(devno),slchrdev));
	/* Unmap the adc I/O memory */
	if (axi_bram_dev->bram_virtaddr)
		iounmap(axi_bram_dev->bram_virtaddr);

	if (axi_bram_dev)
	{
		/* free device memory */
		kfree(axi_bram_dev);
	}
	printk("bram Unload Success \n");
	/* Unload the structural class */
	class_destroy(axi_bram_class);
	/* delete chaar device  */
	cdev_del(&bram_cdev);
	/* unregister char device region from kernel */
	unregister_chrdev_region(devno, AXI_BRAM_COUNT);
	printk("char device remove Success \n");
	return SUCCESS;
}

static struct of_device_id axi_bram_of_match[] = {
	{ .compatible = "xlnx,axi-bram", },
	{ /* end of list */ },
};

static struct platform_driver axi_bram_driver = {
	.driver = {
		.name  = MODULE_NAME,
		.owner = THIS_MODULE,
		.of_match_table	= axi_bram_of_match,
	},
	.probe		= axi_bram_probe,
	.remove		= axi_bram_remove,
};

static int __init axi_bram_init(void)
{
	printk("<1>Hello module world.\n");
	return platform_driver_register(&axi_bram_driver);
}

static void __exit axi_bram_exit(void)
{
	platform_driver_unregister(&axi_bram_driver);
	printk(KERN_ALERT "Goodbye module world.\n");
}

module_init(axi_bram_init);
module_exit(axi_bram_exit);

上面的代码是直接可以用的,我直接从工程中粘贴过来的,主要的功能是PS写一下数据到BRAM中后,通知PL读取里面的数据,PL写一些数据通知PS去读取数据,这样的话,在PS端的linux下就可以用两个线程乒乓的处理数据啦(我懒没有写demo,各位自行验证)。

对了,上面的那个还包含了一个驱动中断(不用的可以忽略)。

给了驱动不给头文件是不道德的(主要是不想码字),虽然比较简单但是还是贴上来吧,看下面代码:

#ifndef AXI_BRAM_H
#define AXI_BRAM_H

#define SUCCESS                 		0
#define FAILURE                         -1

#define MODULE_NAME             		"axi-bram"

#define PL_BRAM_STOP					0
#define PL_BRAM_START					1

#define AXI_BRAM_COUNT					1	
/* IOCTL defines */
#define AXI_BRAM_IOCTL_BASE				'X'							
#define AXI_BRAM_STOP					_IO(AXI_BRAM_IOCTL_BASE, 0)
#define AXI_BRAM_START					_IO(AXI_BRAM_IOCTL_BASE, 1)
#define AXI_BRAM_DATA_LEN				_IO(AXI_BRAM_IOCTL_BASE, 2)
#define AXI_BRAM_INIT_DATA				_IO(AXI_BRAM_IOCTL_BASE, 3)
#define AXI_BRAM_START_ADDR				_IO(AXI_BRAM_IOCTL_BASE, 4)
#define AXI_BRAAM_DEINIT				_IO(AXI_BRAM_IOCTL_BASE, 5)

struct axi_bram_dev
{
	struct mutex mutex;
	struct platform_device *pdev;
	/* PL_bram Hardware device constants */
	void *bram_virtaddr;
	/* pl bram cmd para*/
	int bram_start;
	int bram_data_len;
	int bram_init_data;
	int bram_start_addr;
};

#endif

当然了,BRAM的地址映射啥的,需要自己在VIVADO中设置,我不想截图了,自己研究一下吧。

差不多了,就这样吧,

拜了个拜!

 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值