瑞芯微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
往期精彩
如何添加APP到Buildroot里(以瑞芯微rv1126为例)
Buildroot系统构建学习笔记(以百问网imx6ull开发板为例)
觉得本次分享的文章对您有帮助,随手点[在看]
并转发分享,也是对我的支持。
以上是关于瑞芯微RV1109配置GPIO设备树修改笔记(熟悉新平台从点灯大法开始)的主要内容,如果未能解决你的问题,请参考以下文章