本文总结基本的GPIO的驱动开发流程与GPIO口的控制方式
一. 设置DTS
将需要控制的GPIO口配置信息添加到DTS中, dts文件路径为: kernel/arch/arm64/boot/dts/rockchip/rk3399-tve1030g.dtsi
内容如下:
gpio_hp: gpio_hp {
compatible = "rockchip,gpio_hp";
status = "okay";
bb-vb-gpio = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>;//GPIO1_B1
bb-rst-gpio = <&gpio4 RK_PC5 GPIO_ACTIVE_LOW>;//GPIO4_C5
bb-pwr-gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;//GPIO0_A2
bb-ap-gpio = <&gpio1 RK_PD0 GPIO_ACTIVE_LOW>;//GPIO1_D0
switch0-gpio = <&gpio4 RK_PC1 GPIO_ACTIVE_LOW>;//GPIO4_C1
switch1-gpio = <&gpio4 RK_PC0 GPIO_ACTIVE_LOW>;//GPIO4_C0
switch2-gpio = <&gpio3 RK_PD7 GPIO_ACTIVE_HIGH>;//GPIO4_A6
switch3-gpio = <&gpio3 RK_PD6 GPIO_ACTIVE_HIGH>;//GPIO4_A7
switch4-gpio = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>;//GPIO4_A3
short-gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;//GPIO4_C6
open-gpio = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>;//GPIO4_D0
relay-gpio = <&gpio3 RK_PD0 GPIO_ACTIVE_LOW>;//GPIO3_D0
/*
power5V0-gpio = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>;//GPIO1_A1
power3V3-gpio = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>;//GPIO1_A2
power1V2-gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>;//GPIO1_A3
power12V-gpio = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;//GPIO1_A4
*/
};
二. 编写GPIO驱动程序
GPIO驱动程序文件路径为: kernel/drivers/gpio/gpio-hoopluz.c
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct hp_gpio_info {
unsigned bb_vb_gpio;
unsigned bb_rst_gpio;
unsigned bb_ap_ready_gpio;
unsigned bb_pwr_gpio;
unsigned switch_gpio[5];
unsigned open_gpio;
unsigned short_gpio;
unsigned relay_gpio;
int bb_pwr_active_value;
} hp_gpio;
static ssize_t gpio_switch_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "DIN0:%d.DIN1:%d.DIN2:%d.DIN3:%d.DIN4:%d.\n",gpio_get_value(hp_gpio.switch_gpio[0]),
gpio_get_value(hp_gpio.switch_gpio[1]),
gpio_get_value(hp_gpio.switch_gpio[2]),
gpio_get_value(hp_gpio.switch_gpio[3]),
gpio_get_value(hp_gpio.switch_gpio[4]));
}
static ssize_t gpio_status_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "OpenDet:%d.ShortDet:%d.\n",gpio_get_value(hp_gpio.open_gpio),
gpio_get_value(hp_gpio.short_gpio));
}
//modify by wangxiaolong for [relay gpio ctrl] start
static ssize_t relay_gpio_control_store(struct class* cls,
struct class_attribute *attr,
const char *buffer, size_t count)
{
unsigned int i;
int err;
err = kstrtouint(buffer, 10, &i);
if (err)
return err;
if (i == 0)
gpio_set_value(hp_gpio.relay_gpio, 0);
else
gpio_set_value(hp_gpio.relay_gpio, 1);
return count;
}
static ssize_t relay_gpio_control_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "relay gpio status :%s. \n",gpio_get_value(hp_gpio.relay_gpio)?"ON":"OFF");
}
static struct class_attribute hp_class_attrs[] = {
__ATTR(gpio_switch, 0444, gpio_switch_show, NULL),
__ATTR(gpio_status, 0444, gpio_status_show, NULL),
__ATTR(Relay_ctrl, 0660, relay_gpio_control_show, relay_gpio_control_store),
__ATTR_NULL,
};
static struct class hp_class = {
.name = "hp",
.owner = THIS_MODULE,
.class_attrs = hp_class_attrs,
};
static int hp_gpio_probe(struct platform_device *pdev)
{
int ret = 0;
int gpio;
int enable_value = 0;
enum of_gpio_flags flag;
struct hp_gpio_info *gpio_info;
struct device_node *hp_gpio_node = pdev->dev.of_node;
gpio_info = devm_kzalloc(&pdev->dev,sizeof(struct hp_gpio_info *), GFP_KERNEL);
if (!gpio_info) {
dev_err(&pdev->dev, "devm_kzalloc failed!\n");
return -ENOMEM;
}
gpio = of_get_named_gpio_flags(hp_gpio_node, "bb-vb-gpio", 0, &flag);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "bb-vb-gpio")) {
dev_err(&pdev->dev, "bb-vb-gpio: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
hp_gpio.bb_vb_gpio = gpio;
enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
gpio_direction_output(gpio, enable_value);
printk("bb_vb_gpio gpio output: %d\n", enable_value);
ret = class_register(&hp_class);
if (ret < 0)
return ret;
//modify by wangxiaolong for [relay gpio ctrl] start
gpio = of_get_named_gpio_flags(hp_gpio_node, "relay-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if(gpio_request(gpio, "relay-gpio")){
dev_err(&pdev->dev, "relay-gpio: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_output(gpio,0);
hp_gpio.relay_gpio = gpio;
//end
gpio = of_get_named_gpio_flags(hp_gpio_node, "switch0-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "switch0-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.switch_gpio[0] = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "switch1-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "switch1-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.switch_gpio[1] = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "switch2-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "switch2-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.switch_gpio[2] = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "switch3-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "switch3-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.switch_gpio[3] = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "switch4-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "switch4-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.switch_gpio[4] = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "open-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "open-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.open_gpio = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "short-gpio", 0, NULL);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "short-gpio")) {
dev_err(&pdev->dev, "gpio-switch: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
gpio_direction_input(gpio);
hp_gpio.short_gpio = gpio;
gpio = of_get_named_gpio_flags(hp_gpio_node, "bb-ap-gpio", 0, &flag);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "bb-ap-gpio")) {
dev_err(&pdev->dev, "bb-vb-gpio: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
hp_gpio.bb_ap_ready_gpio = gpio;
enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
gpio_direction_output(gpio, enable_value);
printk("bb_ap_ready_gpio gpio output: %d\n", enable_value);
gpio = of_get_named_gpio_flags(hp_gpio_node, "bb-rst-gpio", 0, &flag);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "hp-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "bb-rst-gpio")) {
dev_err(&pdev->dev, "bb-rst-gpio: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
hp_gpio.bb_rst_gpio = gpio;
enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
gpio_direction_output(gpio, enable_value);
printk("bb_rst_gpio gpio output: %d\n", enable_value);
gpio = of_get_named_gpio_flags(hp_gpio_node, "bb-pwr-gpio", 0, &flag);
if (!gpio_is_valid(gpio)) {
dev_err(&pdev->dev, "bb-pwr-gpio: %d is invalid\n", gpio);
return -ENODEV;
}
if (gpio_request(gpio, "bb-pwr-gpio")) {
dev_err(&pdev->dev, "bb-pwr-gpio: %d request failed!\n", gpio);
gpio_free(gpio);
return -ENODEV;
}
hp_gpio.bb_pwr_gpio = gpio;
enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
hp_gpio.bb_pwr_active_value = enable_value;
gpio_direction_output(gpio, enable_value);
mdelay(50);
gpio_direction_output(gpio, !enable_value);
mdelay(200);
gpio_direction_output(gpio, enable_value);
return ret;
}
static struct of_device_id hp_match_table[] = {
{ .compatible = "rockchip,gpio_hp",},
{},
};
static struct platform_driver hp_gpio_driver = {
.driver = {
.name = "gpio_hp",
.owner = THIS_MODULE,
.of_match_table = hp_match_table,
},
.probe = hp_gpio_probe,
};
static int hp_gpio_init(void)
{
return platform_driver_register(&hp_gpio_driver);
}
module_init(hp_gpio_init);
static void hp_gpio_exit(void)
{
platform_driver_unregister(&hp_gpio_driver);
}
module_exit(hp_gpio_exit);
MODULE_DESCRIPTION("Hoopluz GPIO support driver");
MODULE_LICENSE("GPL");
知识点总结: (以 relay_gpio 为主)
1. 设备类 -- struct class
通过调用class_register()函数,根据struct class hp_class 会生成 /sys/class/hp 节点;
2. 设备类属性 -- struct class_attribute
__ATTR宏用来设置类属性数组的一项, 每一项都会在 /sys/class/hp节点下面再生成相应的节点, 在本例中:
a. /sys/class/hp/gpio_switch
b. /sys/class/hp/gpio_status
c. /sys/class/hp/Relay_ctrl
可以通过cat和echo命令对上述节点进行操作, 操作对应函数为 类属性中的 show 和 store;
3. gpio_set_value() 与 gpio_direction_output()
一般只是在这个GPIO口的寄存器上写上某个值,至于这个端口是否设置为输出,它就管不了!而gpio_direction_output (port_num,0/1),在某个GPIO口写上某个值之后,还会把这个端口设置为输出模式。 因此,有人也许就会建议,把gpio_set_value这个函数直接去掉不用,是否可以,显然是可以的。 但是为什么系统还要用呢, 我个人分析是, 系统开发人员在要结合这两者来使用,以便提高效率。 一般某个端口设置好了输入与输出模式后,最好不要经常变动。 首先要调用gpio_direction_output(),以后要设置高低电平时,直接使用gpio_set_value()就可以了,这样可以省却再次调用设置输出模式的操作,从而提高运行效率!
4. platform_driver 与 platform_device的关系; platform_device 与 dts 的关系
略
三. 权限配置; 权限配置文件路径为: /device/rockchip/common/init.rk30board.rc
+ # alarm control
+ chown system system /sys/class/hp/Relay_ctrl
+ chmod 0666 /sys/class/hp/Relay_ctrl
至此, 驱动可以正常工作.