瑞芯微RV1109配置GPIO设备树修改笔记(熟悉新平台从点灯大法开始)

Posted Engineer-Bruce_Yang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了瑞芯微RV1109配置GPIO设备树修改笔记(熟悉新平台从点灯大法开始)相关的知识,希望对你有一定的参考价值。

对RV1109及瑞芯微平台感兴趣的读者,请自行到文末下载SDK:

以下是我对荣品开发板官方资料的补充,本人用的是淘宝购买的荣品RV1109 开发板,那么如何使用设备树来配置一个 GPIO 呢?

1、设备树文件

kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

2、GPIO 设备树节点及描述

//rpgpio init
rp_gpio {
  status = "okay";
  compatible = "rp_gpio";
  ir_led{
    gpio_num = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
    gpio_function = <0>; //0:output 1:input
  };
  bl_led{
    gpio_num = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>;
    gpio_function = <0>; //0:output 1:input
  };
  otg_host{
    gpio_num = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>; //0:otg,1:host
    gpio_function = <0>;
  };
  vdd5v_3v3{
    gpio_num = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;//vdd5v and 3v3
    gpio_function = <0>;
  };
  spk_on{
    gpio_num = <&gpio2 RK_PC5 GPIO_ACTIVE_HIGH>;//spk_on
    gpio_function = <0>;
  };
  spk_mute{
    gpio_num = <&gpio2 RK_PC7 GPIO_ACTIVE_HIGH>;
    S05rp_init.sh
    gpio_function = <0>;
  };
};

status 是一个属性,可以用来描述设备的状态:

compatible 表示兼容的意思,它的作用就是定义一系列的字符串,用来指定硬件是否兼容相应的驱动,其它的部分接下来分析。

3、GPIO节点配置原理

3.1、查看 GPIO 硬件原理图

以配置 bl_led 为例,首先打开官方的底板硬件原理图,可以看到:

可以看到,该LED接在 GPIO2_B6 这个位置。

3.2、 GPIO 驱动实现

3.2.1、设备树节点配置

管脚绑定编号位于:/kernel/include/dt-bindings/pinctrl/rockchip.h

以下是 bl_led 的设备树节点:

bl_led{
  gpio_num = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>;
  gpio_function = <0>; //0:output 1:input
};
3.2.2、 Linux 驱动实现

内核中 rp_gpio 这个驱动可以用来驱动它,经过 linux grep 命令的查看,可以看到该驱动源代码的位置位于drivers/rongpin/rp_gpio.c :

./drivers/rongpin/rp_gpio.c:275:static int rp_gpio_remove(struct platform_device
*pdev)
./drivers/rongpin/rp_gpio.c:281:static const struct of_device_id
rp_gpio_of_match[] = {
./drivers/rongpin/rp_gpio.c:282:  { .compatible = "rp_gpio" },
./drivers/rongpin/rp_gpio.c:286:static struct platform_driver rp_gpio_driver = {
./drivers/rongpin/rp_gpio.c:287:  .probe = rp_gpio_probe,
./drivers/rongpin/rp_gpio.c:288:  .remove = rp_gpio_remove,
./drivers/rongpin/rp_gpio.c:290:        .name      = "rp_gpio",
./drivers/rongpin/rp_gpio.c:291:        .of_match_table =
of_match_ptr(rp_gpio_of_match),
./drivers/rongpin/rp_gpio.c:295:module_platform_driver(rp_gpio_driver);

这部分是厂商自己实现的 gpio 驱动,驱动代码提供了普适的 GPIO 操作接口:open、write、read:

static const struct file_operations gpio_ops = {
   .owner      = THIS_MODULE,
   .open      = gpio_open,
   .write      = gpio_write,
   .read      = gpio_read,
};

驱动实现也很简单,使用的是 GPIOLIB 框架来控制这些 GPIO ,驱动会去设备树中搜索 compatible 属性:rp_gpio ,找到了这个属性设备树和驱动就匹配上了,具体定义如下:

static const struct of_device_id rp_gpio_of_match[] =
{
    { .compatible = "rp_gpio" },
    { }
};
static struct platform_driver rp_gpio_driver =
{
    .probe = rp_gpio_probe,
    .remove = rp_gpio_remove,
    .driver = {
        .name = "rp_gpio",
        .of_match_table = of_match_ptr(rp_gpio_of_match),
    },
};
module_platform_driver(rp_gpio_driver);
MODULE_LICENSE("GPL");

驱动匹配成功正常工作时最先开始运行的是 probe 函数,也就是最先调用的是 rp_gpio_probe ,实现如下:

static int rp_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *child_np;
    struct device *dev = &pdev->dev;
    static struct proc_dir_entry *root_entry_gpio;
    enum of_gpio_flags  gpio_flags;
    int ret = 0;
    int gpio_cnt = 0;
    char gpio_name_num[20];
    int gpio_in_cnt = 0;
    int cnt = 0;
    //向内核申请内容给gpio_data变量
    gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct
                             rp_gpio_data), GFP_KERNEL);

    if (!gpio_data)
    {
        dev_err(&pdev->dev, "failed to allocate memory\\n");
        return -ENOMEM;
    }

    //获取子节点的数量
    gpio_data->gpio_dts_num = of_get_child_count(np);
    printk("rp_gpio prepare build %d gpio\\n", gpio_data->gpio_dts_num);

    if (gpio_data->gpio_dts_num == 0)
    {
        dev_info(&pdev->dev, "no gpio defined\\n");
    }

    /* create node */
    //在proc目录下创建rp_gpio节点
    root_entry_gpio = proc_mkdir("rp_gpio", NULL);
    /*遍历所有节点*/
    for_each_child_of_node(np, child_np)
    {
        /* parse dts */
        /*从设备树中获取节点对应的GPIO编号以及标志*/
        gpio_data->rp_gpio_num[gpio_cnt].gpio_num =
            of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);

        if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num))
        {
            return -1;
        }

        gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
        gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
        gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
        /*读取节点中的 32 位整数的值*/
        of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));
        printk("rp_gpio request %s\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

        /*通过获取到的gpio_function的数值进行判断*/
        switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function)
        {
            /*如果配置为输入,则走这个分支*/
            case GPIO_FUNCTION_INPUT : /* init input gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }
                else
                {
                    printk("success request gpio %d in\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    /*设置GPIO方向为输入*/
                    gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    //gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
                    event_flag = gpio_flags;
                    of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
                    of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
                    gpio_in_cnt++;
                }

                break;

            /*如果配置为输出,则走这个分支*/
            case GPIO_FUNCTION_OUTPUT : /* init output gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    ret = gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d out\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }

                break;

            /*这个是厂商自己定义的,暂时不清楚是什么作用,应该是预留的,和GPIO_FUNCTION_OUTPUT的功
            能一致*/
            case GPIO_FUNCTION_FLASH :
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d flash\\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    gpio_cnt++;
                }

                break;
        }

        /*拼接GPIO名称以及编号*/
        sprintf(gpio_name_num, gpio_data -> rp_gpio_num[gpio_cnt].gpio_name, gpio_cnt);
        /*在PROC目录下创建设备节点*/
        proc_create(gpio_name_num, 0666, root_entry_gpio, &gpio_ops);
        gpio_cnt++;
    }

    /*如果GPIO的功能被定义为输入的作用*/
    if (gpio_in_cnt > 0)
    {
        /*定义一个定时器*/
        timer_setup(&(gpio_data->mytimer), send_event, 0);
        gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
        add_timer(&(gpio_data->mytimer));
        /*注册input子系统,将这个功能描述成一个输入设备*/
        /* init struct input_dev */
        gpio_data->input = devm_input_allocate_device(dev);
        gpio_data->input->name = "gpio_event";
        gpio_data->input->phys = "gpio_event/input1";
        gpio_data->input->dev.parent = dev;
        gpio_data->input->id.bustype = BUS_HOST;
        gpio_data->input->id.vendor = 0x0001;
        gpio_data->input->id.product = 0x0001;
        gpio_data->input->id.version = 0x0100;

        for(cnt = 0; cnt < gpio_cnt; cnt++)
        {
            if (gpio_data->rp_gpio_num[cnt].gpio_function == 1)
            {
                input_set_capability(gpio_data->input, EV_KEY, gpio_data -> rp_gpio_num[cnt].gpio_event);
            }
        }

        ret = input_register_device(gpio_data->input);
    }

    /*设置platform驱动数据,将数据挂载到pdev->dev.driver_data*/
    platform_set_drvdata(pdev, gpio_data);
    return 0;
}

4、GPIO 节点配置

只需要在 2、GPIO设备树节点及描述 中继续添加你想要的节点即可,假设配置 GPIO2 PB7 为输出功能:

rp_gpio{
  status = "okay";
  compatible = "rp_gpio";
  gpio2b7 {  
    gpio_num = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>;  //默认输出高电平
    gpio_function = <0>;
 };
};

5、如何操作控制 GPIO

以控制摄像头补光灯亮灭为例,摄像头的补光灯对应的 proc 设备节点为:bl_led

开启摄像头补光灯:

在/proc/rp_gpio目录下:
echo 1 > bl_led

关闭摄像头补光灯:

在/proc/rp_gpio目录下:
echo 0 > bl_led

RV1109 SDK源码及文档:

链接:https://pan.baidu.com/s/1QKkQ3SKwuaTImnH3CbpYkg 
提取码:5sg0 

往期精彩

全志A64 设备树里的gpio应用开发

如何添加APP到Buildroot里(以瑞芯微rv1126为例)

Buildroot系统构建学习笔记(以百问网imx6ull开发板为例)

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

以上是关于瑞芯微RV1109配置GPIO设备树修改笔记(熟悉新平台从点灯大法开始)的主要内容,如果未能解决你的问题,请参考以下文章

一个强大的音视频编解码库-rkmedia的应用

瑞芯微RV1109配置7寸电容触摸屏的方向修改笔记

瑞芯微RV1109配置7寸电容触摸屏的方向修改笔记

瑞芯微RV1109 SDK定制与调试总结笔记

瑞芯微RV1109 SDK定制与调试总结笔记

瑞芯微RV1109 SDK定制与调试总结笔记