X-025-KERNEL-Linux gpio driver的移植之基本功能

作者:wowo 发布于:2017-9-13 22:18 分类:X Project

1. 前言

本文将基于本站GPIO subsystem[1]相关的文章,结合”X Project”的开发过程,实现一个简单的gpio driver,并利用gpiolib提供的sysfs api进行简单的测试,进而加深对gpio相关概念的理解。

注1:本文后续的描述,kernel基于本站“X Project”所使用的kernel版本,硬件基于 ”X Project”所使用的“Bubbugum-96”平台[2]

2. 添加gpio driver的驱动框架

kernel中所有驱动的开发都是一个套路,包括如下几个步骤:

a)新建driver文件(例如drivers/gpio/gpio-owl.c)并从其它地方copy一个架子(例如从drivers/pinctrl/pinctrl-owl.c中,并将pinctrl替换为gpio)。

b)修改drivers/gpio/Kconfig和drivers/gpio/Makefile,将这个新的driver添加到编译体系中:

$ git diff drivers/gpio/
...
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -342,6 +342,12 @@ config GPIO_OMAP
        help
          Say yes here to enable GPIO support for TI OMAP SoCs.
+config GPIO_OWL
+       bool "GPIO driver for Actions OWL soc"
+       default y if ARCH_OWL
+       help
+         This selects the gpio driver for Actions OWL soc.
+

...

--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
...
+obj-$(CONFIG_GPIO_OWL)         += gpio-owl.o
obj-$(CONFIG_GPIO_PCA953X)     += gpio-pca953x.o
...

注2:参考drivers/gpio/Kconfig中有关的注释,为了使我们的driver看着更专业、更规范,我们需要以字母为顺序把我们新的driver放到合适的位置:

config GPIO_GENERIC
        tristate
# put drivers in the right section, in alphabetical order
# This symbol is selected by both I2C and SPI expanders

config GPIO_MAX730X
        tristate

c)修改arch/arm64/Kconfig.platforms,选中ARCH_REQUIRE_GPIOLIB配置项,这样就会默认编译gpio有关的代码。

vim@ubuntu:~/work/xprj/linux$ git diff arch/arm64/Kconfig.platforms
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index b03d2538..48c4ae2 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -178,6 +178,7 @@ config ARCH_OWL
        bool "Actions OWL 64-bit SoC Family"
        select PINCTRL
        select PINMUX
+       select ARCH_REQUIRE_GPIOLIB
        help
          This enables support for Actions OWL based SoCs like the S900.

d)编译kernel,发现新的driver已经编译进来了

vim@ubuntu:~/work/xprj/build$ make kernel
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    /home/vim/work/xprj/linux/scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  CC      drivers/gpio/gpio-owl.o
  LD      drivers/gpio/built-in.o
  CC      drivers/irqchip/irqchip.o
  CC      drivers/irqchip/irq-gic.o

至此GPIO driver的驱动框架已经成型(具体的patch如下),后续根据实际的硬件,往里面填充东西使其慢慢完善即可。

https://github.com/wowotechX/linux/commit/2e8b86d7f2e4e892d4a9e1d82cbaa6d3051cfa56

3. 添加gpiochip

由[3]中的介绍可知,编写gpio driver驱动的主要工作就是使用gpiochip抽象gpio控制器,一般情况下,有两种抽象策略:

a)使用一个gpiochip抽象并管理系统中所有的gpio。

b)每个bank(有关bank的概念,熟悉gpio的同学应该都了解,这里就不解释了)使用一个gpiochip抽象(一个实现,多个实例)。

至于使用哪种策略,主要是由代码编写的方便与否决定的,可以自行发挥,这里以第二种为例。

gpiochip的实现比较简单,主要包括(可参考后面的patch):

a)实现request和free接口,可交给pinctrl[4](可以先什么都不做)。

b)实现direction_input、direction_output、get 、set等接口。

c)根据实际情况,填充其它字段。

patch:https://github.com/wowotechX/linux/commit/d05448f039f961f09226f8ef30cba6b706aa8c15

4. 结合gpiolib提供的sysfs api调试

实现基本的功能之后,我们可以借助gpiolib提供的sysfs api进行简单的验证和测试,步骤如下:

4.1 打开SYSFS配置项(CONFIG_SYSFS)和GPIO_SYSFS配置项(CONFIG_GPIO_SYSFS)

make kernel-config

File systems  --->
    Pseudo filesystems  --->
        d[*] sysfs file system support 

Device Drivers  --->
    -*- GPIO Support  --->
        [*]   /sys/class/gpio/... (sysfs interface)

结果如下:

--- a/arch/arm64/configs/xprj_defconfig
+++ b/arch/arm64/configs/xprj_defconfig
...
+CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
...
-# CONFIG_SYSFS is not set
# CONFIG_MISC_FILESYSTEMS is not set

4.2 修改dts,增加gpio的node

注2:“Bubbugum-96”平台[2]平台中,pinctrl和gpio是同一个模块,控制寄存器都在一起,这里偷懒,不想区分哪个是pinctrl的、哪个是gpio的了,就使用相同的寄存器范围就行了。不过呢,这一来我们在代码中就不能使用devm_ioremap_resource进行寄存器映射(devm_xxx类的接口具有排他性),要换成ioremap,具体请参考下面的改动。

    

+       gpioa: gpio@0xe01b0000 {
+               compatible = "actions,s900-gpio";
+               reg = <0 0="" 12="" 0xe01b0000="">;
+       };
+       gpiob: gpio@0xe01b000c {
+               compatible = "actions,s900-gpio";
+               reg = <0 0="" 12="" 0xe01b000c="">;
+       };
...

--- a/drivers/pinctrl/pinctrl-owl.c
+++ b/drivers/pinctrl/pinctrl-owl.c
...
-       priv->membase = devm_ioremap_resource(dev, resource); 
+      priv->membase = ioremap(resource->start, resource_size(resource));

以上4.1和4.2的修改可参考下面的patch:https://github.com/wowotechX/linux/commit/3c8fa7f1076cc6786166b7c3c742ce19ec640c53

4.3 编译并在板子上运行

跑起来后发现有如下错误:

[    0.218562] gpio gpiochip1: GPIO integer space overlap, cannot add chip
[    0.225124] gpiochip_add_data: GPIOs 0..31 (owl-gpio) failed to register
[    0.231781] owl_gpio e01b000c.gpio: Failed to add gpiochip
[    0.237249] owl_gpio: probe of e01b000c.gpio failed with error -16
[    0.243437] owl_gpio e01b0018.gpio: owl_gpio_probe
[    0.248187] gpio gpiochip1: GPIO integer space overlap, cannot add chip
[    0.254749] gpiochip_add_data: GPIOs 0..31 (owl-gpio) failed to register
[    0.261437] owl_gpio e01b0018.gpio: Failed to add gpiochip
[    0.266906] owl_gpio: probe of e01b0018.gpio failed with error -16

总结来说,原因有二:

1) 每一个gpio bank(4.2中的dts node)要对应一个gpiochip结构,我们上面的移植忽略了这个事情。

2)每一个gpiochip的base要小心维护,要在整个gpio subsystem中唯一,因为sysfs api以base命名相应的gpiochip(可参考后面的介绍),并以base和nrgpio计算逻辑上的gpio number,我们可以简单的将base设置为0、32、64、等等。

修改的方法如下:

diff --git a/arch/arm64/boot/dts/actions/s900-bubblegum.dts b/arch/arm64/boot/dts/actions/s900-bubblegum.dts
index 2c959d2..6119dbc 100644
--- a/arch/arm64/boot/dts/actions/s900-bubblegum.dts
+++ b/arch/arm64/boot/dts/actions/s900-bubblegum.dts
@@ -55,26 +55,32 @@
        gpioa: gpio@0xe01b0000 {
                compatible = "actions,s900-gpio";
                reg = <0 0="" 12="" 0xe01b0000="">;
+               base = <0>;
        };
        gpiob: gpio@0xe01b000c {
                compatible = "actions,s900-gpio";
                 reg = <0 0="" 12="" 0xe01b000c="">;
+               base = <32>;
        };            
        ...

diff --git a/drivers/gpio/gpio-owl.c b/drivers/gpio/gpio-owl.c
index f71bc7a..e32e292 100644
--- a/drivers/gpio/gpio-owl.c
+++ b/drivers/gpio/gpio-owl.c
@@ -20,6 +20,7 @@
  #include
  #include
  #include
+#include
  #include
  #include

@@ -34,6 +35,8 @@

struct owl_gpio_priv {
         void __iomem            *membase;
+
+       struct gpio_chip        gpiochip;

  };

...

@@ -166,7 +169,10 @@ static int owl_gpio_probe(struct platform_device *pdev)
                return PTR_ERR(priv->membase);
        }

-       ret = devm_gpiochip_add_data(dev, &owl_gpio_chip, priv);
+       memcpy(&priv->gpiochip, &owl_gpio_chip, sizeof(struct gpio_chip));
+       of_property_read_u32(dev->of_node, "base", &priv->gpiochip.base);

+
+       ret = devm_gpiochip_add_data(dev, &priv->gpiochip, priv);
        if (ret < 0) {
                dev_err(dev, "Failed to add gpiochip\n");
                return ret; 

修改后再次编译运行,已经可以正确probe了:

[    0.209124] owl_gpio e01b0000.gpio: owl_gpio_probe
[    0.213968] owl_gpio e01b000c.gpio: owl_gpio_probe
[    0.218718] owl_gpio e01b0018.gpio: owl_gpio_probe
[    0.223468] owl_gpio e01b0024.gpio: owl_gpio_probe
[    0.228249] owl_gpio e01b0030.gpio: owl_gpio_probe
[    0.232999] owl_gpio e01b00f0.gpio: owl_gpio_probe

4.4 通过sysfs api控制gpio点亮、熄灭LED灯

由于当前测试环境(bubblegum96)的rootfs的是在ramdisk中,因此在系统启动后,我们需要手动创建/sys目录并挂载sysfs,如下:

mkdir /sys
mount -t sysfs /sys /sys

sysfs挂在后可以在gpio class下看到gpiolib提供的sysfs api,如下:

ls /sys/class/gpio
export       gpiochip128  gpiochip32   gpiochip96
gpiochip0    gpiochip160  gpiochip64   unexport

参考[5]中的介绍,我们可以将需要使用的gpio number(逻辑number)写入到export文件,这样就可以将该gpio申请出来,并体现在/sys/class/gpio/目录中。以GPIOA19(LED0)为例(gpio的base是0,因此逻辑number也是19):   

/ # echo 19 > /sys/class/gpio/export
sh: write error: No such device

又出现错误,经过排查,原来我们的request接口还没有正确实现,没关系,先返回OK(0):

static int owl_gpio_request(struct gpio_chip *chip, unsigned offset)
{
        - return pinctrl_request_gpio(offset);
        + //return pinctrl_request_gpio(offset);
        + return 0;
}
...

再次测试,OK:

echo 19 > /sys/class/gpio/export
/ # ls /sys/class/gpio
lass/gpio
export       gpiochip0    gpiochip160  gpiochip64   unexport
gpio19       gpiochip128  gpiochip32   gpiochip96

然后我们可以向“/sys/class/gpio/gpio19/direction ”中写入“out”字符串,将gpio的方向设置为输出,然后向/sys/class/gpio/gpio19/value中写入“1”或者“0”控制gpio的输出电平,如下:  

/ # echo out > /sys/class/gpio/gpio19/direction
echo out > /sys/class/gpio/gpio19/di[   36.780156] owl_gpio e01b0000.gpio: offset 19, value 0
rection

/ # echo 1 > /sys/class/gpio/gpio19/value
echo 1 > /sys/class/gpio/gpio19/val[   54.310562] owl_gpio e01b0000.gpio: offset 19, value 1
ue
/ # echo 0 > /sys/class/gpio/gpio19/value
> /sys/class/gpio/gpio19/valu[   60.714062] owl_gpio e01b0000.gpio: offset 19, value 0
e

然后观察LED灯,检查是否可以正确控制,如果可以则测试成功。

以上patch可参考:https://github.com/wowotechX/linux/commit/30fcb89aabc7eef0043034262f76d3ebe4a9c789

5. 参考文档

[1] http://www.wowotech.net/sort/gpio_subsystem

[2] https://github.com/96boards/documentation/blob/master/ConsumerEdition/Bubblegum-96/HardwareDocs/SoC_bubblegum96.pdf

[3] linux内核中的GPIO系统之(1):软件框架

[4] linux内核中的GPIO系统之(5):gpio subsysem和pinctrl subsystem之间的耦合

[5] Documentation/gpio/sysfs.txt

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net

标签: sysfs driver GPIO porting gpiolib

评论:

小白
2017-09-21 00:22
我看了代码,devm_ioremap_resource具有排他性,这个devm_ioremap好像是没有排他性的!
wowo
2017-09-21 10:18
@小白:多谢提醒,好像是这个样子的,devm_ioremap_resource中会调用__request_region申请一个“busy resource region”,所谓的busy,就是这段memory区域只能自己使用。用devm_ioremap/ioremap取代就没有这个限制了。

/**                                                                            
* __request_region - create a new busy resource region                        
* @parent: parent resource descriptor                                          
* @start: resource start address                                              
* @n: resource region size                                                    
* @name: reserving caller's ID string                                          
* @flags: IO resource flags                                                    
*/                                                                            
struct resource * __request_region(struct resource *parent,                    
                                   resource_size_t start, resource_size_t n,    
                                   const char *name, int flags)

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog