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

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

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

640.png

以下是我对荣品开发板官方资料的补充,本人用的是淘宝购买的荣品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 是一个属性,可以用来描述设备的状态:

640.png

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

3、GPIO节点配置原理

3.1、查看 GPIO 硬件原理图

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

640.png

640.png

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

3.2、 GPIO 驱动实现

3.2.1、设备树节点配置

管脚绑定编号位于:

/kernel/include/dt-bindings/pinctrl/rockchip.h

640.png

以下是 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

640.png

开启摄像头补光灯:

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

640.png

关闭摄像头补光灯:

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


640.png

RV1109 SDK源码及文档:

640.png

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

往期精彩

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


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


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

目录
相关文章
|
编解码 Linux
基于瑞芯微RV1109 Linux串口驱动调试心得(给正点原子DS100示波器点赞!)
基于瑞芯微RV1109 Linux串口驱动调试心得(给正点原子DS100示波器点赞!)
292 0
|
缓存 Linux 芯片
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(中)
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(中)
1037 0
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(中)
|
Ubuntu Linux 网络架构
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(下)
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(下)
611 0
|
Ubuntu Linux Shell
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)(上)
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)
521 0
|
Windows
nxp官方uboot移植到野火开发板PRO(无任何代码逻辑的修改)
nxp官方uboot移植到野火开发板PRO(无任何代码逻辑的修改)
162 0
|
Linux 开发工具 git
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
338 1
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
|
Linux 开发工具 git
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十)LED模板驱动程序的改造:总线设备驱动模型
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十)LED模板驱动程序的改造:总线设备驱动模型
227 1
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十)LED模板驱动程序的改造:总线设备驱动模型
|
Linux 开发工具 git
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)具体单板的按键驱动程序(查询方式)
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)具体单板的按键驱动程序(查询方式)
255 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)具体单板的按键驱动程序(查询方式)
|
Ubuntu Linux 编译器
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十七)具体单板的LED驱动程序
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十七)具体单板的LED驱动程序
196 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十七)具体单板的LED驱动程序
|
Linux 芯片
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十五)最简单的LED驱动程序
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十五)最简单的LED驱动程序
156 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(二十五)最简单的LED驱动程序