dts中gpio resource


原文链接: dts中gpio resource

gpiolib及gpio操作
在驱动工作时,有可能好几个驱动同时去操作一个gpio,这会造成混乱。所以内核提供了一些方法来管理gpio资源,这就是gpiolib。 
在涉及到gpio的驱动程序的编写中,gpiolib并不是强制使用的,而是一种约束和规范,如果要让驱动程序规范、可靠,那么gpiolib是必须要使用的。

1.获知gpio号
内核通过gpio号来识别gpio

对于一些经典的soc,比如s5pv210,可以用arch/arm/mach-s5pv210/include/mach/gpio.h 中的宏来获得gpio号
而对于一些比较新的soc,比如imx6,则通常在设备树中以 reset-gpios = <&gpio1 15 1>;来表示gpio,而在驱动中使用gpio号 = of_get_named_gpio(xxxnode, "reset-gpios", 0);函数返回值来得到gpio号,详见设备树详解
2.gpiolib经典接口
gpiolib常用的接口通常有如下几个:

gpio_request:驱动中要想使用某一个gpio,就必须先调用gpio_request接口来向内核申请,得到允许后才可以去使用这个gpio
gpio_free: 对应gpio_request,用来释放申请后用完了的gpio
gpiochip_is_requested: 接口用来判断某一个gpio是否已经被申请了
gpio_direction_input/gpio_direction_output: 接口用来设置GPIO为输入/输出模式(不推荐直接设置寄存器)
典型应用:

/*之前先要完成驱动的注册工作*/
...
 
 
/*正式开始申请gpio资源,申请操作重复了两次,因为用了两个gpio*/
ret = gpio_request(S5PV210_GPJ0(3), "led1_gpj0_3");
if (ret){
        printk(KERN_INFO "gpio_request failed\n");
        goto out_err_1;
}
/*申请完后可以利用接口设置该gpio。也可以直接操作寄存器来设置*/
gpio_direction_output(S5PV210_GPJ0(3), 1);
 
 
 
/*在remove函数中别忘了对应的释放操作*/
gpio_free(S5PV210_GPJ0(3));

一般来说,gpio的资源申请和释放操作应该放在驱动模块的加载与卸载函数内
gpio_request的第一个参数是需要申请的gpio号。第二个参数是我们给该gpio起个名字
申请完了之后对这个gpio进行模式设置,我们这里用gpio_direction_output 设置成输出模式,并且默认输出1让led灭
3.gpiolib新接口
新接口使用非常简便,但是缺点是代码行数少,有时,需要堆砌代码行数就不太适用,呵呵

该函数附带初始化电平功能,并且会在模块卸载时自动释放gpio,十分简便
/申请并将gpio初始为高电平/
ret = devm_gpio_request_one(dev, S5PV210_GPJ0(3),

    GPIOF_OUT_INIT_HIGH, "led1_gpj0_3");

if (ret < 0)

printk(KERN_INFO "gpio_request failed\n");

4.读写gpio
在内核中,应避免使用寄存器操作gpio,而应使用内核的接口来操作gpio,下面这两个函数

int gpio_get_value(unsigned gpio) //读gpio
void gpio_set_value(unsigned gpio, int value)//写gpio

5.控制台中查看当前gpio占用情况的方法
内核中提供了虚拟文件系统debugfs,里面有一个gpio文件,提供了gpio的使用信息

使用 mount -t debugfs debugfs /tmp 把debugfs挂接到/tmp下,再重新进入/tmp后就能看到一个名为gpio的文件
cat /tmp/gpio即可得到gpio的所有信息,使用完后umount /tmp卸载掉debugfs


在linux使用platform_driver_register() 注册 platform_driver 时, 需要在 platform_driver 的probe() 里面知道设备的中断号, 内存地址等资源。

这些资源的描述信息存放在 resource 数据结构中, 相同的资源存放在一个树形树形数据结构中, 通过父节点, 兄弟节点, 子节点相连。 比如中断资源, IO端口资源, IO内存资源, DMA资源有不同资源树。

Linux使用 struct resource 来描述一个resouce

struct resource {

resource_size_t start;      //资源范围的开始
resource_size_t end;        //资源范围的结束
const char *name;       //资源拥有者名
unsigned long flags;    //资源属性标识
struct resource *parent, *sibling, *child;   //资源树的父节点, 兄弟节点, 字节点指针

};

resource_size_t 由系统决定 为uint32_t 或uint64_t 。

在platform机制里, 使用platform_get_resource()来获取指定的资源类型。

比如获取想获取中断号,

irq = platform_get_irq(pdev, 0);

int platform_get_irq(struct platform_device *dev, unsigned int num)
{

struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

return r ? r->start : -ENXIO;

}
EXPORT_SYMBOL_GPL(platform_get_irq);

platform_get_irq() 会返回一个start, 即可用的中断号。

之后便可使用request_irq() 来注册中断服务函数。

再比如想要获取IO内存资源:

struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

即可得到一个IO内存资源节点指针, 包括了地址的开始,结束地址等, 该IO内存的长度可用 resource_size() 来获取, 但这段资源只是一个描述, 想真正使用这段IO内存, 还要经过先申请, 再映射的过程。例如可使用devm_request_mem_region()申请出使用这段IO内存, 再使用ioremap() 将其映射出来, 供用户空间使用。

devm_request_mem_region(&pdev->dev, res_mem->start, resource_size(res_mem),res_mem->name))
addr_start = ioremap(res_mem->start, resource_size(res_mem));

ioremap() 的返回值即为该资源的虚拟地址。

IO内存的资源是在设备树源(Device Tree Source)文件(以.dts结尾)里给出的,.dts文件就是用来描述目标板硬件信息的, 在uboot启动后, 使用uboot提供的特定API将其获取出来, 如fdt_getprop(), fdt_path_offset(), 这些API包含在uboot 的头文件 里面。

uboot将.dts文件里的描述解析出来, 再对相应寄存器赋值, 在linux启动后, 使用 platform_get_resource() 即可获取到这些给定的资源, 在驱动里使用。

例如一个在.dts文件中关于gpio资源的描述:

gpio: gpio-controller@1070000000800 {

        #gpio-cells = <2>;
        compatible = "cavium,octeon-3860-gpio";
        reg = <0x10700 0x00000800 0x0 0x100>;
        gpio-controller;

根据其描述, 可知道gpio控制器的IO内存起始地址为:0x107900000800, 长度为0x100.

即从 0x107900000800 到 0x1079000008ff.

在目标板里使用 cat /proc/iomem 可以看到:

1070000000800-10700000008ff : /soc@0/gpio-controller@1070000000800

关于i2c 的描述:

    twsi0: i2c@1180000001000 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "cavium,octeon-3860-twsi";
        reg = <0x11800 0x00001000 0x0 0x200>;

        interrupts = <0 45>;
        clock-rate = <100000>;

IO内存起始地址为: 0x118000001000, 长度为0x200.

从 0x118000001000 到 0x1180000011ff.

在目标板里使用 cat /proc/iomem 可以看到:

1180000001000-11800000011ff : /soc@0/i2c@1180000001000

`