谈一个GPIO驱动

谈一个GPIO驱动

一切的操作其实都是gpio的变化,这也就是说gpio驱动是基本的入门,我想很多人应该对gpio都不陌生,从单片机stc89开始,一直都伴随着gpio的操作。mov P1 ,XX 之类的,不过如今咱们已经升级到arm上面的开发了,其实arm只是名字换了,操作也还是那样。所以今天我就献丑来谈一下关于arm11 s3c6410上面的gpio操作的驱动。。

首先必须了解的就是io口的一些要素,在stc89上,其实并不是特别明显,要输出就写入,要读取就直接读就好了(先写1再读取)。但一些稍显高级的片子就不同了,一般将io会增加了输入输出模式的控制,和数据引脚的寄存器。这就一下子多了2个寄存器出来了,一般称作CON DAT这2个寄存器,CON用来配置引脚工作在输出模式还是输入模式,而DAT寄存器对应的就是引脚的值了,输出就往里面写值,输入则是读取。。而arm则还有PUL寄存器,也叫上拉寄存器,上拉可以保证引脚任何时候都有确定的值,不受干扰。这些就是gpio的一些基本信息。

有了上述的基本信息之后,对于gpio驱动也就不是那么难以入手了。简单的说明下假设输出为0x0001  输入为0x0000 那么将引脚配置为输出1 那就是CON = 0x0001 DAT = 1

就是这么简单,关键是怎么去写这个寄存器的问题了。

首先我这里以s3c6410 GPM作为例子来写一个GPM驱动程序。首先先建立platform文件,将资源加载到bus上。

struct resource s3c_gpio_m_resource[] = {
	[0] = {
		.start 	= S3C_6410_GPM_BASE,
		.end	= S3C_6410_GPM_BASE + S3C_6410_GPM_SZ -1,
		.flags	= IORESOURCE_MEM,
	},
};


static struct platform_device *my_device;


static int __init initialization_function(void)
{
	int ret;  


	my_device = platform_device_alloc("gpio_m", -1);  
	
	platform_device_add_resources(my_device,s3c_gpio_m_resource,ARRAY_SIZE(s3c_gpio_m_resource));
	ret = platform_device_add(my_device);
    if(ret)  
       	printk("platform_device_add failed!/n");  
         
    return ret;  
}


static void __exit cleanup_function(void)
{
	platform_device_unregister(my_device);
}
好了。下面就是进入主题,,写一个gpio驱动的主要部分。。。

1:从bus中回去资源(GPM的地址以及寄存器的大小)

2:各种操作这些寄存器的操作就行了。

这里我并没有使用内核给我们提供的readl writel 这2个操作函数。我直接定义了如下的结构体

typedef struct {
	volatile unsigned int gpmcon;
	volatile unsigned int gpmdat;
	volatile unsigned int gpmpud;		
}GPIO_M_INFO,*PGPIO_M_INFO;
主要将寄存器首地址赋给该结构体的指针。那么直接读取也就可以操作寄存器了。

接着就是获取资源了。。

static int get_resource(struct device_dev* dev,struct platform_device *pfdev)
{
	struct resource *mem_res;
	struct resource	*gpm_mem;
	mem_res = platform_get_resource(pfdev,IORESOURCE_MEM,0);
	if(NULL == mem_res){
		return -ENOENT;
	}
	
	gpm_mem = request_mem_region(mem_res->start,resource_size(mem_res),pfdev->name);
	if(NULL == gpm_mem){
		return -EBUSY;
	}
	dev->gpm = ioremap(mem_res->start,resource_size(mem_res));
	if(NULL == dev->gpm){
		release_mem_region(mem_res->start, resource_size(mem_res));
		return -EINVAL;
	}
#ifdef _DEBUG_
	printk(KERN_INFO "GPMCON : 0x%08x\n",dev->gpm->gpmcon);
	printk(KERN_INFO "GPMDAT : 0x%08x\n",dev->gpm->gpmdat);
	printk(KERN_INFO "GPMPUD : 0x%08x\n",dev->gpm->gpmpud);
#endif
	return 0;
}

怎么样?这些都有了,下面具体怎么操作应该不需要我多讲了。我这里贴出GPM寄存器图

谈一个GPIO驱动_第1张图片


对照着上面的配置情况,写入具体的数值,那么对应的管脚也会有相应的电平反映的。。。

好了。。该篇就写到这。。有不明白的,还望留言,我会第一时间回复各位。。谢谢


#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/slab.h>						//kmalloc kfree
#include <linux/errno.h>
#include <asm/uaccess.h>					//copy_to_user copy_from_user_user
#include <linux/device.h>
#include <linux/spinlock.h>				
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/io.h>


#include <asm/io.h>							//readl()
	
#include <mach/irqs.h>						//中断号
#include <linux/irq.h>						//中断函数
#include <linux/interrupt.h>				//中断类型
#include <linux/timer.h>
#include <linux/poll.h>


#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>


#include "register_cdev.h"
#include "gpio_m_info.h"


#define _DEBUG_
#define CHR_NAME	"gpio_m"
#define CLASS_NAME	"chass_gpio_m"
#define DEVICE_NAME	"device_gpio_m"


//结构体声明,字符设备结构体
struct device_dev{
	struct 				cdev cdev;
	struct class 		*my_class;
	GPIO_M_INFO			*gpm;
	spinlock_t			spin;					//自旋锁,实现内存的互斥操作
};


struct 	device_dev 	*g_devp;					//设备结构指针
//默认主设备号,0为自动分配,否则外部设置 
int g_major = 0;




static int gpio_m_open(	struct inode *inode,
						struct file *filp)
{
	/*将设备结构体指针赋给文件私有数据指针*/
	struct device_dev *devp;
	devp = container_of(inode->i_cdev,struct device_dev,cdev);
	filp->private_data = devp;
	/*判断文件打开的标志*/
	return 0;
}


static int gpio_m_release(	struct inode *inode,
							struct file *filp)
{
	struct device_dev *devp;
	devp = filp->private_data;
	
	return 0;
}


static void set_cfg(unsigned int dat,unsigned int pin,struct device_dev *devp)
{
	unsigned int temp;
	spin_lock(&devp->spin);
	temp = devp->gpm->gpmcon;
	temp &= (~((0x0F) << (pin << 2)));
	temp |= ((dat) << (pin << 2));
	devp->gpm->gpmcon = temp;
	spin_unlock(&devp->spin);
}


static void set_dat(unsigned int dat,unsigned int pin,struct device_dev *devp)
{
	spin_lock(&devp->spin);
	if(dat){
		devp->gpm->gpmdat |= (0x01 << pin);
	}else{
		devp->gpm->gpmdat &= ~(0x01 << pin);
	}
	spin_unlock(&devp->spin);
}


static void set_pull(unsigned int dat,unsigned int pin,struct device_dev *devp)
{
	unsigned int temp;
	spin_lock(&devp->spin);
	temp = devp->gpm->gpmpud;
	temp &= (~((0x03) << (pin << 1))); 
	temp |= ((dat) << (pin << 1));
	devp->gpm->gpmpud = temp;
	spin_unlock(&devp->spin);
}


static long gpio_m_unlocked_ioctl(	struct file *filp,
									unsigned int cmd,
									unsigned long arg)
{
	int i = 0;
	int flag = 0;
	struct device_dev *devp;
	devp = filp->private_data;
	
	switch(cmd){
	case GPM_SET_OUT_BIT:
		flag++;
	case GPM_SET_IN_BIT:
		set_cfg(flag,arg,devp);
		break;
	case GPM_SET_MUT_OUT_BIT:
		flag++;
	case GPM_SET_MUT_IN_BIT:
		for(i = 0; arg;i++){
			if(arg & 0x01)
				set_cfg(flag,i,devp);
			arg >>= 1;
		}
		break;
	case GPM_SET_DAT:
		flag++;
	case GPM_CLR_DAT:
		set_dat(flag,arg,devp);
		break;
	case GPM_SET_MUT_DAT:
		flag++;
	case GPM_CLR_MUT_DAT:
		for(i = 0; arg;i++){
			if(arg & 0x01)
				set_dat(flag,i,devp);
			arg >>= 1;
		}
		break;
	case GPM_SET_PULL_UP:
		flag++;
	case GPM_SET_PULL_DOWN:
		flag++;
	case GPM_SET_PULL_DISABLE:
		set_pull(flag,arg,devp);
		break;
	case GPM_SET_MUT_PULL_UP:
		flag++;
	case GPM_SET_MUT_PULL_DOWN:
		flag++;
	case GPM_SET_MUT_PULL_DISABLE:
		for(i = 0; arg;i++){
			if(arg & 0x01)
				set_dat(flag,i,devp);
			arg >>= 1;
		}
		break;
	default:
		break;
	}
	
	return 0;
}						


//设置字符设备对应的fops
static struct file_operations device_fops = {
  	.owner			= THIS_MODULE,
  	.open			= gpio_m_open,
  	.release		= gpio_m_release,
	.unlocked_ioctl = gpio_m_unlocked_ioctl,
}; 


static int get_resource(struct device_dev* dev,struct platform_device *pfdev)
{
	struct resource *mem_res;
	struct resource	*gpm_mem;
	mem_res = platform_get_resource(pfdev,IORESOURCE_MEM,0);
	if(NULL == mem_res){
		return -ENOENT;
	}
	/* 将指定的内存段标明已被使用,防止其它程序再次加载 */
	gpm_mem = request_mem_region(mem_res->start,resource_size(mem_res),pfdev->name);
	if(NULL == gpm_mem){
		return -EBUSY;
	}
	/* 	ioremap是架构的问题产生的,应为早起的intel的芯片其io寄存器并不是统一编址,
		并不能直接操作内存那样访问,所以需要将其映射到内存地址 */
	dev->gpm = ioremap(mem_res->start,resource_size(mem_res));
	if(NULL == dev->gpm){
		release_mem_region(mem_res->start, resource_size(mem_res));
		return -EINVAL;
	}
#ifdef _DEBUG_
	printk(KERN_INFO "GPMCON : 0x%08x\n",dev->gpm->gpmcon);
	printk(KERN_INFO "GPMDAT : 0x%08x\n",dev->gpm->gpmdat);
	printk(KERN_INFO "GPMPUD : 0x%08x\n",dev->gpm->gpmpud);
#endif
	return 0;
}


static void free_memory(struct device_dev* dev,struct platform_device *pdev)
{
	struct resource *mem_res;
	
	iounmap((void *) dev->gpm);
	dev->gpm = NULL;
	
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (mem_res != NULL)
		release_mem_region(mem_res->start, resource_size(mem_res));
	
	if(dev){
		kfree(dev);
		g_devp = 0;
	}
}


static int __devinit key_probe(struct platform_device *dev)
{
	/*初始化代码*/
	int 	result;
	
	//分配设备结构体空间
	g_devp = kmalloc(sizeof(struct device_dev),GFP_KERNEL);
	if (!g_devp) {		
		result = -ENOMEM;
		return -ENOMEM;	
	}
	memset(g_devp,0,sizeof(struct device_dev));
	
	/* 获取platform资源(按键信息) */
	if((result = get_resource(g_devp,dev)),result){
		return result;
	}
	spin_lock_init(&g_devp->spin);
	/* 申请设备号,关联file_operation并生成设备文件 */
	result = register_setup(	&g_major,
								&(g_devp->cdev),
								&device_fops,
								CHR_NAME,
								CLASS_NAME,
								DEVICE_NAME,
								&(g_devp->my_class) );
	if(result){
		free_memory(g_devp,dev);
	}
	printk(KERN_INFO "Sucessful init_function magor = %d\n",g_major);
	return result;
}	


static int __devexit key_remove(struct platform_device *dev)
{
	/*释放代码*/
	unregister_setup(g_major,&(g_devp->cdev),g_devp->my_class);
	free_memory(g_devp,dev);										//释放已申请的内存
	printk(KERN_INFO "Sucessful cleanup_function\n");
	return 0; 
}


static struct platform_driver my_driver = {
	.probe	= key_probe,
	.remove = __devexit_p(key_remove),
	.driver = {
		.name = "gpio_m",
		.owner = THIS_MODULE,
	}
};	


static int __init initialization_function(void)
{
	return platform_driver_register(&my_driver);
}


static void __exit cleanup_function(void)
{
	platform_driver_unregister(&my_driver);
}


//注册模块加载卸载函数
module_init(initialization_function);					//指定模块加载函数
module_exit(cleanup_function);							//指定模块卸载函数


//模块参数
module_param(g_major,int,S_IRUGO);               		//导出参数,为了人工设定主设备号


//模块信息及许可证
MODULE_AUTHOR("LvApp");									//作者
MODULE_LICENSE("Dual BSD/GPL");							//许可证
MODULE_DESCRIPTION("A simple GPM module");				//描述
MODULE_ALIAS("GPM");									//别名


你可能感兴趣的:(谈一个GPIO驱动)