十三、GPIO子系统

 

由于之后的触摸屏驱动分析中使用到了GPIO子系统和i2c子系统,因此在分析触摸屏驱动之前我准备把这两个子系统进行简单分析。

 

之前我们使用GPIO引脚的方式并不是推荐的方式,当我们更改某一bit时,很有可能导致另外的bit值发生更改。而GPIO子系统进行了封装,确保每次只对一个GPIO引脚操作,而不会影响到别的GPIO引脚。

 

下面这段代码是我从驱动程序中摘出来的,它首先获取GPIO引脚,之后设置为输出模式。

 1 ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN");
 2 if (ret) {
 3     printk(KERN_ERR "failed to request TP1_EN for I2C control\n");
 4     //return err;
 5 }
 6 
 7 gpio_direction_output(EXYNOS4_GPL0(2), 1);
 8 
 9 s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT);
10 gpio_free(EXYNOS4_GPL0(2));
11 
12 mdelay(5);

 

 

一、gpio_direction_output()分析

gpio_direction_output()方便我们操作GPIO引脚,我们来看一下gpio_direction_output()函数会做些什么。

 1 int gpio_direction_output(unsigned gpio, int value)
 2 {
 3     unsigned long        flags;
 4     struct gpio_chip    *chip;    /* 不同单板对应不同chip,由芯片厂实现 */
 5     struct gpio_desc    *desc = &gpio_desc[gpio];
 6     int            status = -EINVAL;
 7 
 8     spin_lock_irqsave(&gpio_lock, flags);
 9     /* 判断gpio是否可用 */
10     if (!gpio_is_valid(gpio))
11         goto fail;
12     chip = desc->chip;
13     if (!chip || !chip->set || !chip->direction_output)
14         goto fail;
15     gpio -= chip->base;
16     if (gpio >= chip->ngpio)
17         goto fail;
18     status = gpio_ensure_requested(desc, gpio);
19     if (status < 0)
20         goto fail;
21 
22     /* now we know the gpio is valid and chip won't vanish */
23 
24     spin_unlock_irqrestore(&gpio_lock, flags);
25 
26     might_sleep_if(chip->can_sleep);
27 
28     if (status) {
29         status = chip->request(chip, gpio);
30         if (status < 0) {
31             pr_debug("GPIO-%d: chip request fail, %d\n",
32                 chip->base + gpio, status);
33             /* and it's not available to anyone else ...
34              * gpio_request() is the fully clean solution.
35              */
36             goto lose;
37         }
38     }
39     /* 调用chip的direction_output()函数 */
40     status = chip->direction_output(chip, gpio, value);
41     if (status == 0)
42         set_bit(FLAG_IS_OUT, &desc->flags);
43     trace_gpio_value(chip->base + gpio, 0, value);
44     trace_gpio_direction(chip->base + gpio, 0, status);
45 lose:
46     return status;
47 fail:
48     spin_unlock_irqrestore(&gpio_lock, flags);
49     if (status)
50         pr_debug("%s: gpio-%d status %d\n",
51             __func__, gpio, status);
52     return status;
53 }
54 EXPORT_SYMBOL_GPL(gpio_direction_output);

 

通过代码第4行struct gpio_chip,我们可以推断出此结构体是内核提供给各个板商的,用于其实现不同GPIO引脚操作。

在drivers/gpio/目录下,有跟平台相关的文件,这些文件就是由厂商提供的GPIO引脚操作函数。在此我以gpio-omap.c为例进行讨论

我们先来查看probe()函数:

 1 static int __devinit omap_gpio_probe(struct platform_device *pdev)
 2 {
 3     static int gpio_init_done;
 4     struct omap_gpio_platform_data *pdata;
 5     struct resource *res;
 6     int id;
 7     struct gpio_bank *bank;
 8 ...
 9     pdata = pdev->dev.platform_data;
10 ...
11     ret = init_gpio_info(pdev);
12 ...
13     id = pdev->id;
14     bank = &gpio_bank[id];
15     
16     res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
17 ...
18     bank->irq = res->start;
19     bank->virtual_irq_start = pdata->virtual_irq_start;
20     bank->method = pdata->bank_type;
21     bank->dev = &pdev->dev;
22     bank->dbck_flag = pdata->dbck_flag;
23     bank->stride = pdata->bank_stride;
24     bank_width = pdata->bank_width;
25 
26     spin_lock_init(&bank->lock);
27 
28     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
29 
30     bank->base = ioremap(res->start, resource_size(res));
31 ...
32     omap_gpio_mod_init(bank, id);
33     omap_gpio_chip_init(bank);
34     omap_gpio_show_rev(bank);
35 
36     if (!gpio_init_done)
37         gpio_init_done = 1;
38 
39     return 0;
40 }
View Code

此函数首先填充了struct gpio_bank,之后使用ioremap()映射,最后调用omap_gpio_chip_init()初始化并注册chip。

 1 static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
 2 {
 3     int j;
 4     static int gpio;
 5     /* 填充chip成员函数 */
 6     bank->mod_usage = 0;
 7     bank->chip.request = omap_gpio_request;
 8     bank->chip.free = omap_gpio_free;
 9     bank->chip.direction_input = gpio_input;
10     bank->chip.get = gpio_get;
11     bank->chip.direction_output = gpio_output;
12     bank->chip.set_debounce = gpio_debounce;
13     bank->chip.set = gpio_set;
14     bank->chip.to_irq = gpio_2irq;
15     if (bank_is_mpuio(bank)) {
16         bank->chip.label = "mpuio";
17 #ifdef CONFIG_ARCH_OMAP16XX
18         bank->chip.dev = &omap_mpuio_device.dev;
19 #endif
20         bank->chip.base = OMAP_MPUIO(0);
21     } else {
22         bank->chip.label = "gpio";
23         bank->chip.base = gpio;
24         gpio += bank_width;
25     }
26     bank->chip.ngpio = bank_width;
27 
28     gpiochip_add(&bank->chip);
29 ...
30     irq_set_chained_handler(bank->irq, gpio_irq_handler);
31     irq_set_handler_data(bank->irq, bank);
32 }

 

接下来我们来查看chip是如何注册到内核中的:

 1 int gpiochip_add(struct gpio_chip *chip)
 2 {
 3     unsigned long    flags;
 4     int        status = 0;
 5     unsigned    id;
 6     int        base = chip->base;
 7     /* 判断gpio是否可用 */
 8     if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) && base >= 0) {
 9 ...
10     }
11 ...
12     if (status == 0) {
13         for (id = base; id < base + chip->ngpio; id++) {
14             gpio_desc[id].chip = chip;    /* 放入全局变量中 */
15 
16             gpio_desc[id].flags = !chip->direction_input
17                 ? (1 << FLAG_IS_OUT)
18                 : 0;
19         }
20     }
21 ...
22 unlock:
23     spin_unlock_irqrestore(&gpio_lock, flags);
24 
25     status = gpiochip_export(chip);
26 ...
27     return status;
28 }
29 EXPORT_SYMBOL_GPL(gpiochip_add);

 

重新回到gpio_direction_output()函数,我们继续向下分析。代码第40行:chip->direction_output(chip, gpio, value);最终会调用chip的direction_output()函数。我们来看一下gpio-omap.c中的direction_output()函数是怎么实现的:

 1 static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
 2 {
 3     void __iomem *reg = bank->base;
 4     u32 l;
 5 
 6     switch (bank->method) {
 7 #ifdef CONFIG_ARCH_OMAP1
 8     case METHOD_MPUIO:
 9         reg += OMAP_MPUIO_IO_CNTL / bank->stride;
10         break;
11 #endif
12 #ifdef CONFIG_ARCH_OMAP15XX
13     case METHOD_GPIO_1510:
14         reg += OMAP1510_GPIO_DIR_CONTROL;
15         break;
16 #endif
17 #ifdef CONFIG_ARCH_OMAP16XX
18     case METHOD_GPIO_1610:
19         reg += OMAP1610_GPIO_DIRECTION;
20         break;
21 #endif
22 #if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
23     case METHOD_GPIO_7XX:
24         reg += OMAP7XX_GPIO_DIR_CONTROL;
25         break;
26 #endif
27 #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
28     case METHOD_GPIO_24XX:
29         reg += OMAP24XX_GPIO_OE;
30         break;
31 #endif
32 #if defined(CONFIG_ARCH_OMAP4)
33     case METHOD_GPIO_44XX:
34         reg += OMAP4_GPIO_OE;
35         break;
36 #endif
37     default:
38         WARN_ON(1);
39         return;
40     }
41     l = __raw_readl(reg);
42     if (is_input)
43         l |= 1 << gpio;
44     else
45         l &= ~(1 << gpio);
46     __raw_writel(l, reg);
47 }
View Code

最终它会通过readl()和writel()等操作实现对寄存器的读写操作,对于这两个函数,读者可以参考:Linux驱动函数解读第四节。

 

 

二、内核中GPIO的使用函数

1. 检测GPIO引脚是否有效

bool gpio_is_valid(int number)

2. 获取或释放GPIO引脚。如果GPIO引脚正在使用,则无法获取。原理类似于原子操作

int gpio_request(unsigned gpio, const char *label)

void gpio_free(unsigned gpio)

3. 设置GPIO引脚为输入或输出

int gpio_direction_input(unsigned gpio)    /* 输入 */

int gpio_direction_output(unsigned gpio, int value)    /* 输出 */

4. 获取或设置GPIO引脚的值

int gpio_get_value(unsigned gpio)

void gpio_set_value(unsigned gpio, int value)

5. 设置GPIO引脚为中断引脚

int gpio_to_irq(unsigned gpio)    /* 返回值为request_irq()使用的中断号 */

6. 导出或撤销GPIO引脚到用户空间

int gpio_export(unsigned gpio, bool direction_may_change)
/* 参数direction_may_change表示用户程序是否可以修改GPIO的方向,
    如果可以,则参数direction_may_change为真 */

void gpio_unexport(unsigned gpio)    /* 撤销GPIO */

 

 

下面我们可以使用GPIO子系统优化第二章的LED程序。

三、LED驱动优化

led源代码:

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/init.h>
  4 #include <linux/cdev.h>
  5 #include <linux/slab.h>
  6 #include <linux/device.h>
  7 #include <linux/gpio.h>
  8 #include <linux/io.h>
  9 
 10 #include <asm/uaccess.h>
 11 #include <asm/io.h>
 12 
 13 #include <plat/gpio-cfg.h>
 14 
 15 /* 定义文件内私有结构体 */
 16 struct led_device {
 17     struct cdev cdev;
 18     int stat;            /* 用于保存LED状态,0为灭,1为亮 */
 19 };
 20 
 21 static int g_major;
 22 module_param(g_major, int, S_IRUGO);
 23 
 24 static struct led_device*    dev;
 25 static struct class*        scls;
 26 static struct device*        sdev;
 27 
 28 /* LED write()函数 */
 29 static ssize_t led_write(struct file *filep, const char __user * buf, size_t len, loff_t *ppos)
 30 {
 31     struct led_device *dev = filep->private_data;
 32 
 33     if (copy_from_user(&(dev->stat), buf, 1))
 34         return -EFAULT;
 35 
 36     if (dev->stat == 1)
 37         gpio_set_value(EXYNOS4_GPK1(1), 1);
 38     else
 39         gpio_set_value(EXYNOS4_GPK1(1), 0);
 40 
 41     return 1;
 42 }
 43 
 44 /* LED open()函数 */
 45 static int led_open(struct inode *inodep, struct file *filep)
 46 {
 47     struct led_device *dev;
 48     int ret = -1;
 49     
 50     dev = container_of(inodep->i_cdev, struct led_device, cdev);
 51     // 放入私有数据中
 52     filep->private_data = dev;
 53     
 54     // 设为输出引脚,灭灯
 55     ret = gpio_request(EXYNOS4_GPK1(1), "LED3");
 56     if (ret)
 57         printk(KERN_ERR "failed to request LED3\n");
 58     
 59     gpio_direction_output(EXYNOS4_GPK1(1), 1);
 60     s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT);
 61 
 62     gpio_set_value(EXYNOS4_GPK1(1), 1);
 63 
 64     return 0;
 65 }
 66 
 67 static int led_close(struct inode *inodep, struct file *filep)
 68 {
 69     gpio_free(EXYNOS4_GPK1(1));
 70     
 71     return 0;
 72 }
 73 
 74 /* 把定义的函数接口集合起来,方便系统调用 */
 75 static const struct file_operations led_fops = {
 76     .write = led_write,
 77     .open  = led_open,
 78     .release = led_close,
 79 };
 80 
 81 static int __init led_init(void)
 82 {
 83     int ret;
 84     dev_t devt;
 85 
 86     /* 1. 申请设备号 */
 87     if (g_major) {
 88         devt = MKDEV(g_major, 0);
 89         ret = register_chrdev_region(devt, 1, "led");
 90     }
 91     else {
 92         ret = alloc_chrdev_region(&devt, 0, 1, "led");
 93         g_major = MAJOR(devt);
 94     }
 95     if (ret)
 96         return ret;
 97 
 98     /* 2. 申请文件内私有结构体 */
 99     dev = kzalloc(sizeof(struct led_device), GFP_KERNEL);
100     if (dev == NULL) {
101         ret = -ENOMEM;
102         goto fail_malloc;
103     }
104 
105     /* 3. 注册字符设备驱动 */
106     cdev_init(&dev->cdev, &led_fops);    /* 初始化cdev并链接file_operations和cdev */
107     ret = cdev_add(&dev->cdev, devt, 1);    /* 注册cdev */
108     if (ret)
109         return ret;
110 
111     /* 4. 创建类设备,insmod后会生成/dev/led设备文件 */
112     scls = class_create(THIS_MODULE, "led");
113     sdev = device_create(scls, NULL, devt, NULL, "led");
114 
115     return 0;
116 
117 fail_malloc:
118     unregister_chrdev_region(devt, 1);
119 
120     return ret;
121 }
122  
123 static void __exit led_exit(void)
124 {
125     /* 镜像注销 */
126     dev_t devt = MKDEV(g_major, 0);
127 
128     device_destroy(scls, devt);
129     class_destroy(scls);
130 
131     cdev_del(&(dev->cdev));
132     kfree(dev);
133 
134     unregister_chrdev_region(devt, 1);
135 }
136  
137 /* 声明段属性 */
138 module_init(led_init);
139 module_exit(led_exit);
140 
141 MODULE_LICENSE("GPL");
View Code

Makefile:

 1 KERN_DIR = /work/itop4412/tools/linux-3.5
 2 
 3 all:
 4     make -C $(KERN_DIR) M=`pwd` modules 
 5 
 6 clean:
 7     make -C $(KERN_DIR) M=`pwd` modules clean
 8     rm -rf modules.order
 9 
10 obj-m    += led.o
View Code

测试文件:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char** argv)
 9 {
10     if (argc != 2) {
11         printf("Usage: \n");
12         printf("%s <on|off>\n", argv[0]);
13         return -1;
14     }
15 
16     int fd;
17     fd = open("/dev/led", O_RDWR);
18     if (fd < 0) {
19         printf("can't open /dev/led\n");
20         return -1;
21     }
22 
23     char stat;
24     if (0 == strcmp(argv[1], "off")) {
25         stat = 0;
26         write(fd, &stat, 1);
27     } else {
28         stat = 1;
29         write(fd, &stat, 1);        
30     }
31     close(fd);
32 
33     return 0;
34 }
View Code

 

 

下一章  十四、i2c子系统

 

转载于:https://www.cnblogs.com/Lioker/p/11000687.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值