⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_port.c

📁 这是一个Linux驱动程序
💻 C
字号:
/************************************************************
Copyright (C), 2005-2010, Embedded System of China Tech. Co., Ltd.
    www.emsyschina.com
FileName: at91_port.c
Author: Version : Date: Casiawu(wujh@emsyschina.com)
Description: // PORT驱动程序模块
1. -------
History: // 历史修改记录
<author> <time> <version > <desc>
casiawu 2008/2/12 1.0 build this module for linux (2.6.21)
***********************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>

#include <asm/uaccess.h>
#include <asm/arch/hardware.h>				//必须
#include <asm/arch/io.h>				    //包含at91_sys_write()
#include <asm/arch/at91rm9200.h>			//包含各个基地址(必须)
#include <asm/arch/at91rm9200_mc.h>		    //包含EBI_SMC2_CSR[]
#include <asm/arch/at91_pio.h>				//包含GPIO寄存器
#include <asm/arch/at91_pmc.h>				//包含GPIO寄存器
#include <asm/arch/gpio.h>

#undef DPRINTK                               /* undef it, just in case */
#ifdef PORT_DEBUG
/* This one if debugging is on, and kernel space */
#define DPRINTK(fmt, args...) printk("port: "fmt, ## args)
#else
#define DPRINTK(fmt, args...) /* not debugging: nothing */
#endif

//定义设置读写的PORT地址
static unsigned int PortAddr=0x0;
module_param(PortAddr, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(PortAddr, "The PortAddr for Reading and Writing");

//定义设置读写的数据宽度
static unsigned short WrRdWidth=16;
module_param(WrRdWidth, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(WrRdWidth, "The DataWidth for Reading and Writing");

//定义设置读写的PORT设备名
static unsigned int PortLen =0x10;
module_param(PortLen, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(PortLen, "The PortLen for Reading and Writing");

#define PORTDEV	"port0"

typedef struct _para_s
{
	unsigned int   data;
	unsigned short offset;
	unsigned short type;
}para_s,*para_t;

/*---------------------------------------------------------
//全局变量、常量定义:
---------------------------------------------------------*/
static int	port_major=0;					
static struct class*	port_class;

//设备指针
static struct platform_device*	port_devices = NULL;
static struct cdev	port_chrdev;
static unsigned long Port_VirAddr=0;

/*---------------------------------------------------------
//open函数;
//---------------------------------------------------------*/
static int port_open(struct inode * s_node,struct file * s_file)
{
	try_module_get(THIS_MODULE);    
	return 0;
}

/*---------------------------------------------------------
//close函数;
//---------------------------------------------------------*/
static int port_close(struct inode * s_node,struct file * s_file)
{
	module_put(THIS_MODULE);
	return 0;
}


/*---------------------------------------------------------
//read函数;
//
//---------------------------------------------------------*/
static int port_read(struct file*s_file, char *r_buf,int len,loff_t* loff)
{	
	unsigned char IO_data0=0;
	unsigned short IO_data1=0;
	unsigned int IO_data2=0;
	para_s parameter;

	if(copy_from_user(&parameter,r_buf,sizeof(para_s)))	
		return -EFAULT;

	switch(parameter.type)
	{
		//byte
		case 1:
			IO_data0 = __raw_readb(Port_VirAddr + parameter.offset);
			rmb(); 
			parameter.data = IO_data0;
			break;

		//half-word
		case 2:
			IO_data1 = __raw_readw(Port_VirAddr + parameter.offset);
			rmb(); 
			parameter.data = IO_data1;
			break;

		//word
		case 4:
			IO_data2 = __raw_readl(Port_VirAddr + parameter.offset);
			rmb();
			parameter.data = IO_data2; 	
			break;

		default:
			return -1;
	}

	if((copy_to_user(r_buf,&parameter,sizeof(para_s))))	
		return -EFAULT;
			
	return sizeof(para_s);
}

/*---------------------------------------------------------
//write函数;
//---------------------------------------------------------*/
static int port_write(struct file*s_file, const char *w_buf,int len,loff_t* loff)
{
	unsigned char IO_data0=0;
	unsigned short IO_data1=0;
	unsigned int IO_data2=0;
	para_s parameter;

	if(copy_from_user(&parameter,w_buf,sizeof(para_s)))	
		return -EFAULT;

	switch(parameter.type)
	{
		//byte
		case 1:
			//printk("offset=%d\n\r",parameter.offset);			
			IO_data0 = parameter.data;
			//printk("IO_data0=%x\n\r",IO_data0);		
			__raw_writeb(IO_data0, Port_VirAddr + parameter.offset);
			wmb();
			udelay(10);			
			break;

		//half-word
		case 2:
			IO_data1 = parameter.data;
			__raw_writew(IO_data1, Port_VirAddr + parameter.offset);
			wmb();			
			break;

		//word
		case 4:
			IO_data2 = parameter.data;
			__raw_writel(IO_data2,Port_VirAddr+parameter.offset);
			wmb();			
			break;

		default:
			break;
	}
			
	return sizeof(para_s);  		
}


/*---------------------------------------------------------
//文件操作结构体;
---------------------------------------------------------*/
struct file_operations port_fops =
{
	.read		=	(void(*)) port_read,
	.write		=	(void(*)) port_write,	
	.open		=	(void(*)) port_open,
	.release	=	(void(*)) port_close,    	
 };

// 板载GPIO寄存器设置
static int port_config(void)
{
	unsigned char bank=0,csr=0;
	unsigned int  width=0;

	bank = (unsigned char)((PortAddr & 0xF0000000) >> 28) ;
	printk("bank=%d\n\r",bank);
		
	//设置所有端口
	switch(bank)
	{
		//bank 7
		case 8:
    		//PIO Enable A Register
			at91_set_A_periph(AT91_PIN_PC13, 0); 
			csr = 7;
			switch(WrRdWidth)
			{
				case 8:
					width = AT91_SMC_DBW_8;
				break;
			
				case 16:
				default:
					width = AT91_SMC_DBW_16;
				break;
			}
			break;

		//bank 6
		case 7:
    		//PIO Enable A Register
			at91_set_A_periph(AT91_PIN_PC12, 0); 
			csr = 6;
			switch(WrRdWidth)
			{
				case 8:
					width = AT91_SMC_DBW_8;
				break;
				
				case 16:
				default:
					width = AT91_SMC_DBW_16;
				break;
			}			
			break;

		//bank 5
		case 6:
    		//PIO Enable A Register
			at91_set_A_periph(AT91_PIN_PC11, 0); 
			csr = 5;
			switch(WrRdWidth)
			{
				case 8:
					width = AT91_SMC_DBW_8;
				break;
			
				case 16:
				default:
					width = AT91_SMC_DBW_16;
				break;
			}			
			break;

		//bank 4
		case 5:
    		//PIO Enable A Register
			at91_set_A_periph(AT91_PIN_PC10, 0); 
			csr = 4;
			switch(WrRdWidth)
			{
				case 8:
					width = AT91_SMC_DBW_8;
				break;
			
				case 16:
				default:
					width = AT91_SMC_DBW_16;
				break;
			}			
			break;			

		default:
			return -1;
	}

	//定义EBI控制寄存器的读写参数
	at91_sys_write(AT91_SMC_CSR(csr),((AT91_SMC_NWS & 0x3) | AT91_SMC_WSEN									
        | (AT91_SMC_TDF & 0x100) | AT91_SMC_BAT 									        | width | (AT91_SMC_RWHOLD & (0x1<<28))));	

	//申请内存区域,以检测该地址空间是否被使用;
	if (!request_mem_region(PortAddr,PortLen,PORTDEV))
			printk("Error request mem address! \r\n");         
    			//进行内存区域的映射,把物理地址映射为逻辑地址;  
	Port_VirAddr = (unsigned long)ioremap_nocache(PortAddr,PortLen);	
	
	return 0;
}


//系统探测函数
static int __devinit port_probe(struct platform_device *pdev)
{
	int ret;
	dev_t dev_id;
	struct class_device* cls_port_dev;

	//PORT 寄存器配置
	if(port_config() < 0 )
		return -1;

	printk("device %s-%d detected!\n", pdev->name, pdev->id);

	dev_id = MKDEV(port_major, 0);
	pdev->dev.devt = dev_id;

	cdev_init(&port_chrdev, &port_fops);
	port_chrdev.owner = THIS_MODULE;

	ret = cdev_add(&port_chrdev, dev_id, 1);
	if (ret)
	{
		printk("fail to register driver for %s %d!\n",PORTDEV, pdev->id);
		return ret;
	}

	platform_set_drvdata(pdev, &port_chrdev);


	cls_port_dev = class_device_create(port_class,NULL,dev_id,&pdev->dev,PORTDEV);
	if (IS_ERR(cls_port_dev))
		return PTR_ERR(cls_port_dev);

	DPRINTK("driver for %s.%d (%d,%d) registered\n",PORTDEV, 0, MAJOR(dev_id), MINOR(dev_id));

	return ret;
}


static int __devexit port_remove(struct platform_device *pdev)
{
	struct cdev* chrdev;

    iounmap((void *)Port_VirAddr);
	    //释放申请的内存区域;    release_mem_region(PortAddr,PortLen);
	
	class_device_destroy(port_class, pdev->dev.devt);

	chrdev = platform_get_drvdata(pdev);

	if (chrdev)
		cdev_del(&port_chrdev);

	platform_set_drvdata(pdev, NULL);
    
	pdev->dev.devt = 0;

	printk("%s removed!\n",PORTDEV);

	return 0;
}


#ifdef CONFIG_PM
static int port_suspend(struct platform_device *pdev, pm_message_t state)
{
	return 0;
}

static int port_resume(struct platform_device *pdev)
{
	return 0;
}
#else
#define port_suspend NULL
#define port_resume  NULL
#endif

static struct platform_driver port_driver = 
{   
    .probe      = port_probe,   
    .remove     = __devexit_p(port_remove), 
#ifdef CONFIG_PM    
    .suspend    = port_suspend, 
    .resume     = port_resume,
#endif
    .driver     = 
    {    
    	.name	= PORTDEV,
        .owner  = THIS_MODULE,  
    },
};

//*********************************************************
//初始化模块函数;
//*********************************************************
static int __init port_init_module(void)
{
	int ret;

	dev_t dev = MKDEV(port_major, 0);

	printk("PortAddr is the port phyaddr: 0x%x\n", PortAddr);
	printk("WrRdWidth is the operation width: %d\n", WrRdWidth);
	printk("PortLen is port devname: 0x%x\n", PortLen);

	port_devices = platform_device_alloc(PORTDEV, 0);
	if (!port_devices)
		return -ENOMEM;

	ret = platform_device_add(port_devices);
	if (ret < 0) {
		platform_device_put(port_devices);
		return ret;
	}
		
	/* Figure out our device number. */
	if (port_major)
		ret = register_chrdev_region(dev, 1, PORTDEV);
	else 
	{
		ret = alloc_chrdev_region(&dev, 0, 1, PORTDEV);
		if (ret) 
		{
			printk(KERN_WARNING "GPIO_MODULE_NAME: unable to get major %d\n", port_major);
			return ret;
		}
		port_major = MAJOR(dev);
	}

	//create class
	port_class = class_create(THIS_MODULE, PORTDEV);
	if (IS_ERR(port_class))
		return PTR_ERR(port_class);

	ret = platform_driver_register(&port_driver);
    
	return ret;
}

//*********************************************************
//清除模块函数;
//*********************************************************
static void __exit port_cleanup_module(void)
{
	platform_driver_unregister(&port_driver);

	class_destroy(port_class);

	unregister_chrdev_region(MKDEV(port_major, 0), 1);
    
	platform_device_unregister(port_devices);
}


module_init(port_init_module);
module_exit(port_cleanup_module);

MODULE_AUTHOR("casiawu");
MODULE_DESCRIPTION("AT91 PORT Driver (PORT)");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -