gpio-sunxi驱动应用gpio口终端操作

版权声明:本文为博主原创文章,未经博主允许转载。 https://blog.csdn.net/jklinux/article/details/80855499

内核里gpiolib可以操作gpio口,但基本只能作输入或输出功能。而全志的gpio控制器除这两种功能外,还提供了其它功能,如配置gpio的上下拉,输出电流等级大小等。所以全志提供了类似gpiolib但可以有更多功能的驱动(但现版本的驱动也只实现输入和输出功能).

首先确认内核里是否已选择上gpio-sunxi功能(默认是已选择上的)

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  Device Drivers  --->
    *- GPIO Support  --->
        <M>   SUNXI GPIO USER INTERFACE

编译内核模块后,需要通过make modules_install安装模块到文件系统.


gpio-sunxi会像gpiolib一样提供gpio口操作的属性文件, 但无需提供gpio口的序号。只需要把要操作的gpio口列在script.bin里的gpio_para部分即可,gpio-sunxi驱动会在初始化时会给这些gpio口提供相应的属性文件.
如我的script.bin关于gpio_para部分的内容:

221 [gpio_para]
222 gpio_used = 1
223 gpio_num = 20
224 gpio_pin_1 = port:PA06<1><default><default><0>
225 gpio_pin_2 = port:PA13<1><default><default><0>
226 gpio_pin_3 = port:PA14<1><default><default><0>
227 gpio_pin_4 = port:PA01<1><default><default><0>
228 gpio_pin_5 = port:PD14<1><default><default><0>
229 gpio_pin_6 = port:PA00<1><default><default><0>
230 gpio_pin_7 = port:PA03<1><default><default><0>
231 gpio_pin_8 = port:PC04<1><default><default><0>
232 gpio_pin_9 = port:PC07<1><default><default><0>
233 gpio_pin_10 = port:PA02<1><default><default><0>
234 gpio_pin_11 = port:PA21<1><default><default><0>
235 gpio_pin_12 = port:PA07<1><1><3><0>
236 gpio_pin_13 = port:PA08<1><1><3><0>
237 gpio_pin_14 = port:PG08<1><default><default><0>
238 gpio_pin_15 = port:PA09<1><1><3><0>
239 gpio_pin_16 = port:PA10<1><1><3><0>
240 gpio_pin_17 = port:PG09<1><default><default><0>
241 gpio_pin_18 = port:PG06<1><default><default><0>
242 gpio_pin_19 = port:PG07<1><default><default><0>
243 gpio_pin_20 = port:PA15<1><default><default><0>
244 
245 [leds_para] 
246 leds_used = 0
247 green_led = port:PL10<1><default><default><1>
248 green_led_active_low = 0
249 red_led = port:PA15<1><default><default><0>
250 red_led_active_low = 0

//需要注意的地方, gpio_para里列出的gpio口会作gpio_request操作的,不可复用,所以leds_para的leds_used设0.


操作:

1 加载gpio-sunxi.ko驱动模块:
    modprobe gpio-sunxi.ko

2 加载驱动模块后, 在/sys/class/gpio_sw目录下会看到上面所列的gpio口子目录:
    / # ls /sys/class/gpio_sw/P
    PA0/   PA10/  PA14/  PA2/   PA3/   PA7/   PA9/   PC7/   PG6/   PG8/
    PA1/   PA13/  PA15/  PA21/  PA6/   PA8/   PC4/   PD14/  PG7/   PG9/

3 每个gpio口子目录下又提供了属性文件:
    cfg   ,  data ,  pull,  drv
   // cfg属性文件是用于指定和获取gpio口的功能
   // data属性文件用于提定和获取gpio口的电平作用
   // pull属性文件用于设置gpio口的上下拉功能(现只有接口,没有实现此功能)
   // pull属性文件用于设置gpio口的输出电流等级(现只有接口,没有实现此功能)

以上属性文件具体的可用值,应是根据datasheet来设置,如P15的配置信息:
功能分配(PA15):   000:输入,  001:输出, 010:SPI1_MOSI  
         011:UART3_RTS ,  110:PA_EINT15,  111:IO Disable
        默认值:7

内部电阻状态(PA15): 00: Pull-up/down disable  , 01: Pull-up
          10: Pull-down,      11: Reserved
        默认值:0

驱动能力(指输出的电流大小,等级越高,电流越大):
          00: Level 0 ,  01: Level 1
          10: Level 2 ,  11: Level 3
        默认值: 1

输出电平状态:   当功能选择输出时,1表示输出高电平, 0表示输出低电平
/////////////////////////
如操作PA15引脚(status-led用的gpio口)
    echo 1 > /sys/class/gpio_sw/PA15/cfg   //作输出功能
    echo 1 > /sys/class/gpio_sw/PA15/data  //输出高电平(灯亮)
    echo 0 > /sys/class/gpio_sw/PA15/data  //输出低电平(灯灭)



驱动源码在drivers/gpio/gpio-sunxi.c里
工作原理分析:

//已声明好4个属性文件, 每个属性文件都指定好读写操作的函数
279 static struct device_attribute gpio_sw_class_attrs[] = {
280     __ATTR(cfg,  0664, cfg_sel_show, cfg_sel_store),
281     __ATTR(pull, 0664, pull_show, pull_store),
282     __ATTR(drv,  0664, drv_level_show, drv_level_store),
283     __ATTR(data, 0664, data_show, data_store),
284     __ATTR_NULL,
285 };

653 module_init(gpio_sw_init); //驱动模块的初始化函数为gpio_sw_init

515 static int __init gpio_sw_init(void)
516 {
        ...
525     gpio_sw_class = class_create(THIS_MODULE, "gpio_sw"); //创建class成功后会在/sys/class/目录下生成一个gpio_sw子目录
        ...
531     gpio_sw_class->dev_attrs    = gpio_sw_class_attrs; //指定了gpio_sw的子目录属性文件(cfg,pull,data,drv)
        ...
545     cnt = script_get_pio_list("gpio_para", &list); //获取script.bin里gpio_para里指定的所有gpio口

563     for(i=0;i<cnt;i++){ //遍历gpio_para里的gpio口,进行gpio_request操作和给每个gpio口生成一个platform_device对象,初始化platform_device对象及注册.
564         printk("gpio_pin_%d(%d) gpio_request\n",i+1, list[i].gpio.gpio);
565         if(gpio_request(list[i].gpio.gpio, NULL)){  //请求操作
566             printk("gpio_pin_%d(%d) gpio_request fail \n",i+1, list[i].gpio.gpio);
567             continue;
568         }
569         sw_pdata[i] = kzalloc(sizeof(struct sw_gpio_pd), GFP_KERNEL); //准备platform_data空间
570         if(!sw_pdata[i]){
571             printk(KERN_ERR "kzalloc fail for sw_pdata[%d]\n",i);
572             return -1;
573         }
574 
575         gpio_sw_dev[i] = kzalloc(sizeof(struct platform_device), GFP_KERNEL); //创建platform_device对象
576         if(!gpio_sw_dev[i]){
577             printk(KERN_ERR "kzalloc fail for gpio_sw_dev[%d]\n",i);
578             return -1;
579         }
580 
581         sprintf(sw_pdata[i]->name,"gpio_pin_%d",i+1); 
582         if (normal_led_pin.str && !strcmp(sw_pdata[i]->name, normal_led_pin.str)) {
583             sprintf(sw_pdata[i]->link,"%s", "normal_led");
584         } else if (standby_led_pin.str && !strcmp(sw_pdata[i]->name, standby_led_pin.str)) {
585             sprintf(sw_pdata[i]->link,"%s", "standby_led");
586         }
587 
    //初始化platform_device对象
588         gpio_sw_dev[i]->name = "gpio_sw";
589         gpio_sw_dev[i]->id   = i;
590         gpio_sw_dev[i]->dev.platform_data   = sw_pdata[i];
591         gpio_sw_dev[i]->dev.release         = gpio_sw_release;
592 
593         if(platform_device_register(gpio_sw_dev[i])){ //注册platform_device对象
594             printk(KERN_ERR "%s platform_device_register fail\n",sw_pdata[i]->name);
595             goto INIT_ERR_FREE;
596         }
597     }
598     if(platform_driver_register(&gpio_sw_driver)){ //注册platform_driver对象
599         printk(KERN_ERR "gpio user platform_driver_register  fail\n");
600         for(i=0;i<cnt;i++)
601             platform_device_unregister(gpio_sw_dev[i]);
602         goto INIT_ERR_FREE;
603     }

gpio_sw_driver对象:
504 static struct platform_driver gpio_sw_driver = {
505     .probe      = gpio_sw_probe,
506     .remove     = gpio_sw_remove,
507     .suspend    = gpio_suspend,
508     .resume     = gpio_resume,
509     .driver     = {
510         .name       = "gpio_sw",  //按此名字进行匹配
511         .owner      = THIS_MODULE,
512     },
513 };


416 static int __devinit gpio_sw_probe(struct platform_device *dev)
417 {
418     struct sw_gpio              *sw_gpio_entry;
419     struct sw_gpio_pd           *pdata = dev->dev.platform_data;
420     int                         ret;
421     unsigned long               flags;
422     script_item_value_type_e    type;
423     char io_area[16];
424 
425     sw_gpio_entry = kzalloc(sizeof(struct sw_gpio), GFP_KERNEL); //驱动给每个设备准备的数据
426     if(!sw_gpio_entry)
427         return -ENOMEM;
428     sw_gpio_entry->class.item = \
429         kzalloc(sizeof(script_item_u), GFP_KERNEL);
430     if(!sw_gpio_entry->class.item) {
431         kfree(sw_gpio_entry);
432         return -ENOMEM;
433     }
434     //在script.bin的gpio_para里获取"port:PA15<1><default><default><0>"指定的配置功能,上下拉功能,输出电流等级等
435     type = script_get_item("gpio_para", pdata->name, sw_gpio_entry->class.item);
436     if(SCIRPT_ITEM_VALUE_TYPE_PIO != type){
437         printk(KERN_ERR "get config err!\n");
438         kfree(sw_gpio_entry->class.item);
439         kfree(sw_gpio_entry);
440         return -ENOMEM;
441     }
442 
443     ret = mapGpioToName(io_area,sw_gpio_entry->class.item->gpio.gpio);
444     printk("gpio name is %s, ret = %d\n",io_area, ret); //获取名称: PA15
445 
446     platform_set_drvdata(dev,sw_gpio_entry); //把给设备准备的数据绑定到设备
            ...
    //初始化里的操作函数,在上面的属性文件操作函数里会调用这些gpio_sw_xxx_xxx函数来完成实际功能.
457     sw_gpio_entry->class.gpio_sw_cfg_set = gpio_sw_cfg_set;
458     sw_gpio_entry->class.gpio_sw_cfg_get = gpio_sw_cfg_get;
459     sw_gpio_entry->class.gpio_sw_pull_set = gpio_sw_pull_set;
460     sw_gpio_entry->class.gpio_sw_pull_get = gpio_sw_pull_get;
461     sw_gpio_entry->class.gpio_sw_drv_set = gpio_sw_drv_set;
462     sw_gpio_entry->class.gpio_sw_drv_get = gpio_sw_drv_get;
463     sw_gpio_entry->class.gpio_sw_data_set = gpio_sw_data_set;
464     sw_gpio_entry->class.gpio_sw_data_get = gpio_sw_data_get;
        ...
    // gpio_sw_classdev_register其实就是在/sys/class/gpio_sw目录下创建子目录,并在每个子目录都会生成cfg,pull,drv,data等属性文件
472     ret = gpio_sw_classdev_register(&dev->dev, &sw_gpio_entry->class);
        ...
486     return 0;
487 }

如对cfg进行写操作时,则触发调用cfg_sel_store函数
188 static ssize_t cfg_sel_store(struct device *dev,
189         struct device_attribute *attr, const char *buf, size_t size)
190 {
191     struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev); //获取驱动里给设备准备的数据
192     ssize_t ret = -EINVAL;
193     char *after;
194     int cfg = simple_strtoul(buf, &after, 10);
195     size_t count = after - buf;
196 
197     if (isspace(*after))
198         count++;
199 
200     if (count == size){
201         ret = count;
202         gpio_sw_cdev->gpio_sw_cfg_set(gpio_sw_cdev, cfg); //调用457行初始化的函数
203     }
204 
205     return ret;
206 }


//然后通过阅读以下代码就可以发现有pull, drv接口,但根本没有实现实际功能.
 79 static int
 80 gpio_sw_cfg_set(struct gpio_sw_classdev *gpio_sw_cdev,int  mul_cfg){
 81 
 82     if (mul_cfg==0)
 83         gpio_direction_input(gpio_sw_cdev->item->gpio.gpio);
 84     else
 85         gpio_direction_output(gpio_sw_cdev->item->gpio.gpio, 0);
 86 
 87     return 0;
 88 }
 89 
 90 static int
 91 gpio_sw_cfg_get(struct gpio_sw_classdev *gpio_sw_cdev){
 92 
 93     return 0;
 94 }
 95 
 96 static int
 97 gpio_sw_pull_set(struct gpio_sw_classdev *gpio_sw_cdev,int  pull){
 98 
 99     return 0;
100 }
101 
102 static int
103 gpio_sw_pull_get(struct gpio_sw_classdev *gpio_sw_cdev){
104 
105     return 0;
106 }
107 
108 static int
109 gpio_sw_drv_set(struct gpio_sw_classdev *gpio_sw_cdev,int  drv){
110 
111     return 0;
112 }
113 
114 static int
115 gpio_sw_drv_get(struct gpio_sw_classdev *gpio_sw_cdev){
116 
117     return 0;
118 }
119 
120 static int
121 gpio_sw_data_set(struct gpio_sw_classdev *gpio_sw_cdev,int  data){
122     __gpio_set_value(gpio_sw_cdev->item->gpio.gpio,data);
123     return 0;
124 }
125 
126 static int
127 gpio_sw_data_get(struct gpio_sw_classdev *gpio_sw_cdev){
128     return __gpio_get_value(gpio_sw_cdev->item->gpio.gpio);
129 }

猜你喜欢

转载自blog.csdn.net/jklinux/article/details/80855499