谈一个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寄存器图
对照着上面的配置情况,写入具体的数值,那么对应的管脚也会有相应的电平反映的。。。
好了。。该篇就写到这。。有不明白的,还望留言,我会第一时间回复各位。。谢谢
#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"); //别名