gpio and gpio interrupt 一
这篇随笔将记录我对linux gpio库的实现分析及使用 ,作为备忘, 实例代码为高通8660,linux内核3.0。
关于gpio库的文档,kernel 中有一个文档。位于documentation 下的gpio.txt
http://www.linuxidc.com/Linux/2011-07/39632.htm 这个链接是一个网友对这个文档的翻译。可以参考
gpiolib.c中提供了gpio管理的框架。driver/gpio
一 实现自己的gpio库。
在8660 中gpio库的实现在gpio-v2.c中。我们来看一下是如何做的。
postcore_initcall(msm_gpio_init);
这行代码告诉我们从这里开始吧
2 {
3 msm_gpio_probe();
4 register_syscore_ops(&msm_gpio_syscore_ops);
5 return 0;
6 }
第3行是注册gpio chip 用的。第四行注册了电源管理的操作。
2 {
3 int i, irq, ret;
4
5 spin_lock_init(&tlmm_lock);
6 bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
7 bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
8 bitmap_zero(msm_gpio.dual_edge_irqs, NR_MSM_GPIOS);
9 ret = gpiochip_add(&msm_gpio.gpio_chip);
10 if (ret < 0)
11 return ret;
12
13 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
14 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
15 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
16 handle_level_irq);
17 set_irq_flags(irq, IRQF_VALID);
18 }
19
20 ret = request_irq(TLMM_MSM_SUMMARY_IRQ, msm_summary_irq_handler,
21 IRQF_TRIGGER_HIGH, "msmgpio", NULL);
22 if (ret) {
23 pr_err("Request_irq failed for TLMM_MSM_SUMMARY_IRQ - %d\n",
24 ret);
25 return ret;
26 }
27 return 0;
28 }
这个函数虽然不是很长,但完成的功能却使开天劈地的,首先把我们的gpio chip也即是gpio 控制器添加到了系统中。接着把gpio关联在中断中。然后申请了一个中断处理函数。所以要想完整的理解gpio,还需要把中断系统搞明白才行。无论多么复杂,静下心来,仔细分析,思路就会好起来了。
让我们先来看 ret = gpiochip_add(&msm_gpio.gpio_chip);
这个函数的输入是一个叫做gpio_chip的变量
定义为
2 const char *label;
3 struct device *dev;
4 struct module *owner;
5
6 int (*request)(struct gpio_chip *chip,
7 unsigned offset); 请求gpio
8 void (*free)(struct gpio_chip *chip,
9 unsigned offset);释放gpio
10
11 int (*direction_input)(struct gpio_chip *chip,
12 unsigned offset);配置gpio为输入,返回当前gpio状态
13 int (*get)(struct gpio_chip *chip,
14 unsigned offset);获取gpio的状态
15 int (*direction_output)(struct gpio_chip *chip,
16 unsigned offset, int value);配置gpio为输出,并设置为value
17 int (*set_debounce)(struct gpio_chip *chip,
18 unsigned offset, unsigned debounce);设置消抖动时间,尤其是gpio按键时有用
19
20 void (*set)(struct gpio_chip *chip,
21 unsigned offset, int value);设置gpio为value值
22
23 int (*to_irq)(struct gpio_chip *chip,
24 unsigned offset);把gpio号转换为中断号
25
26 void (*dbg_show)(struct seq_file *s,
27 struct gpio_chip *chip);调试用
28 int base; 这个gpio控制器的gpio开始编号
29 u16 ngpio;这个gpio控制器说控制的gpio数
30 const char *const *names;
31 unsigned can_sleep:1;
32 unsigned exported:1;
33
34 #if defined(CONFIG_OF_GPIO)
35 /*
36 * If CONFIG_OF is enabled, then all GPIO controllers described in the
37 * device tree automatically may have an OF translation
38 */
39 struct device_node *of_node;
40 int of_gpio_n_cells;
41 int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
42 const void *gpio_spec, u32 *flags);
43 #endif
44 };
这些函数都是chip级别的函数。linux的gpiolib会勾到这里进行实际的硬件操作。值得一提的是比如(*set)(struct gpio_chip *unsigned offset, int value);设置值是个int二不是bool,所以这个值可以不止是1或0.,从而给我们个机会把gpio配置为其他功能并设置驱动电流,上拉电阻等配置。
当我们定义了gpiochip的结构后,利用gpiochip_add加入gpio构架。以后就可以用linux标准的gpio函数操作gpio了。
现在来分析一下这个gpiochip_add, 部分分析在code中
2 {
3 unsigned long flags;
4 int status = 0;
5 unsigned id;
6 int base = chip->base;
7
8 if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
9 && base >= 0) {
10 status = -EINVAL;
11 goto fail;
12 }
13
14 spin_lock_irqsave(&gpio_lock, flags);
15
16 if (base < 0) {
17 base = gpiochip_find_base(chip->ngpio);
18 if (base < 0) {
19 status = base;
20 goto unlock;
21 }
22 chip->base = base;
23 }
24 以上代码动态的分配gpio的开始索引。
25 /* these GPIO numbers must not be managed by another gpio_chip */
26 for (id = base; id < base + chip->ngpio; id++) {
27 if (gpio_desc[id].chip != NULL) {
28 status = -EBUSY;
29 break;
30 }
31 }
32 if (status == 0) {
33 for (id = base; id < base + chip->ngpio; id++) {
34 gpio_desc[id].chip = chip;
35
36 /* REVISIT: most hardware initializes GPIOs as
37 * inputs (often with pullups enabled) so power
38 * usage is minimized. Linux code should set the
39 * gpio direction first thing; but until it does,
40 * we may expose the wrong direction in sysfs.
41 */
42 gpio_desc[id].flags = !chip->direction_input
43 ? (1 << FLAG_IS_OUT)
44 : 0;
45 }
46 }
47 填充gpio_desc,并设置某些属性
48 of_gpiochip_add(chip);
49
50 unlock:
51 spin_unlock_irqrestore(&gpio_lock, flags);
52
53 if (status)
54 goto fail;
55
56 status = gpiochip_export(chip);这时gpio sysfs的一些code 对调试很有用
57 if (status)
58 goto fail;
59
60 return 0;
61 fail:
62 /* failures here can mean systems won't boot... */
63 pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
64 chip->base, chip->base + chip->ngpio - 1,
65 chip->label ? : "generic");
66 return status;
67 }
对于每一个gpio,都有一个gpio描述符,这个描述符包含了这个gpio所属的控制器即chip和一些标志,label等。在gpiolib 构架中,linux没有使用惯用的链表来管理gpio描述符,二是采用了一个具有ARCH_NR_GPIOS大小的gpio描述符数组。这个描述符数组便代表了系统所有的gpio。于是我们可以通过gpio号找到对应gpio的描述符,通过描述符找到这个gpio对应的chip,利用chip就可以对gpio进行各种操作了。下面典型的分析几个函数
2 {
3 struct gpio_desc *desc;
4 struct gpio_chip *chip;
5 int status = -EINVAL;
6 unsigned long flags;
7
8 spin_lock_irqsave(&gpio_lock, flags);
9
10 if (!gpio_is_valid(gpio))
11 goto done;
12 desc = &gpio_desc[gpio]; gpio描述符是线性的,所以用gpio好取得描述符
13 chip = desc->chip;然后取到chip
14 if (chip == NULL)
15 goto done;
16
17 if (!try_module_get(chip->owner))
18 goto done;
19
20 /* NOTE: gpio_request() can be called in early boot,
21 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
22 */
23
24 if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {设置这个gpio已经被申请了
25 desc_set_label(desc, label ? : "?");
26 status = 0;
27 } else {
28 status = -EBUSY;
29 module_put(chip->owner);
30 goto done;
31 }
32
33 if (chip->request) {调用底层的请求函数,在这里我们可以配置gpio的电流,上下拉电阻等
34 /* chip->request may sleep */
35 spin_unlock_irqrestore(&gpio_lock, flags);
36 status = chip->request(chip, gpio - chip->base);
37 spin_lock_irqsave(&gpio_lock, flags);
38
39 if (status < 0) {
40 desc_set_label(desc, NULL);
41 module_put(chip->owner);
42 clear_bit(FLAG_REQUESTED, &desc->flags);
43 }
44 }
45
46 done:
47 if (status)
48 pr_debug("gpio_request: gpio-%d (%s) status %d\n",
49 gpio, label ? : "?", status);
50 spin_unlock_irqrestore(&gpio_lock, flags);
51 return status;
52 }
其他函数类同