[AndroidO] [RK3399] -- GPIO驱动与控制方式

本文总结基本的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

     至此, 驱动可以正常工作.

 

 

你可能感兴趣的:(AndroidO,RK3399)