23_设备树控制LED灯

在设备树中建立led节点,编写简单驱动进行测试;以及使用pinctrl的方式控制led灯

Posted by Jerry Chen on August 16, 2019

获取GPIO编号的方式

建立LED节点

目标设备树文件:linux-4.14.2_iTop-4412_scp/arch/arm/boot/dts/exynos4412-itop-elite.dts

首先屏蔽文件中存在”&gpk1 1”、”&gpl2 0”的键值对。

然后在根节点/{…}内新增如下子节点,注意status属性为”okay”

1
2
3
4
5
6
myled_demo:led_demo{
	compatible = "led_demo";
	status = "okay";
	gpio1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
	gpio2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
};

上面的信息指GPL2[0]初始高电平,GPK1[1]初始高电平。

重新编译设备树烧录到开发板。

编写驱动文件

led_demo_drv.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");

#define DRV_NAME "led_demo"

static int gpio_pin[2] = {-1};//存放gpio编号

static int demo_probe(struct platform_device *pdv){
	struct device_node *node = pdv->dev.of_node;
	int ret;
	
	printk("led probe ok!\n");
	
	gpio_pin[0] = of_get_named_gpio(node, "gpio1", 0);
	gpio_pin[1] = of_get_named_gpio(node, "gpio2", 0);
	
	if((gpio_pin[0] < 0)||(gpio_pin[1] < 0)){
		printk("of_get_named_gpio error!\n");
		return 0;
	}
	printk("gpio_pin[0] is %d\n",gpio_pin[0]);
	printk("gpio_pin[1] is %d\n",gpio_pin[1]);

	ret = gpio_request(gpio_pin[0], "led1");
	if(ret!=0){
		printk("gpio_pin[0] request %d failed.", gpio_pin[0]);
		return ret;
	}

	ret = gpio_request(gpio_pin[1], "led2");
	if(ret!=0){
		printk("gpio_pin[1] request %d failed.", gpio_pin[1]);
		return ret;
	}
	
	gpio_free(gpio_pin[0]);
	gpio_free(gpio_pin[1]);
	
	gpio_direction_output(gpio_pin[0],0);
	gpio_set_value(gpio_pin[0], 1);
	
	gpio_direction_output(gpio_pin[1],0);
	gpio_set_value(gpio_pin[1], 1);
	
	return 0;
}

static int demo_remove(struct platform_device *pdv){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	return 0;
}

static const struct of_device_id of_demo_dt_match[] = {
	{.compatible = DRV_NAME},
	{},
};

MODULE_DEVICE_TABLE(of,of_demo_dt_match);

static struct platform_driver platform_drv_demo = {
	.driver	= {
		.name	= DRV_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = of_demo_dt_match,
	},
	.probe	= demo_probe,
	.remove	= demo_remove,
};

static int __init demo_init(void){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	platform_driver_register(&platform_drv_demo);
	return 0;
}
static void __exit demo_exit(void){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	platform_driver_unregister(&platform_drv_demo);
	return;
}

module_init(demo_init);
module_exit(demo_exit);

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

export ARCH=arm

obj-m += led_demo_drv.o

KDIR := /home/jerry/Projects/itop4412_kernel_4_14_2_bsp/linux-4.14.2_iTop-4412_scp/

PWD ?= $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	rm -rf *.o

交叉编译然后加载到内核后发现probe可以正常执行,LED灯正常点亮。

pinctrl方式

设备树文件修改

1、目标文件pinctrl设备树:linux-4.14.2_iTop-4412_scp/arch/arm/boot/dts/exynos4412-pinctrl.dtsi

目的是新增控制gpl2[0]高低电平的两个子节点。因为gpl2节点在pinctrl_1节点下,所以新增如下节点作为pinctrl_1节点的子节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
leds_gpios1{
    leds_gpios1_on:leds_gpios1-on {
        samsung,pins = "gpl2-0" ;
        samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
        samsung,pin-val = <1>;
        samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
    };
    leds_gpios1_off:leds_gpios1-off {
        samsung,pins = "gpl2-0" ;
        samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
        samsung,pin-val = <0>;
        samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
    };
};

2、目标设备树文件:linux-4.14.2_iTop-4412_scp/arch/arm/boot/dts/exynos4412-itop-elite.dts

在根节点下新增如下节点:

1
2
3
4
5
6
7
leds_test_node {
    compatible = "leds_test";
    status = "okay";
    pinctrl-names = "itop-leds1-on","itop-leds1-off";
    pinctrl-0 = <&leds_gpios1_on>;
    pinctrl-1 = <&leds_gpios1_off>;
};

重新编译设备树烧录到开发板。

驱动思想

使用pinctrl的api函数获取GPIO状态并设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <linux/pinctrl/consumer.h> 
...
static int xxx_probe(struct platform_device *pdev){
    struct pinctrl *leds_pin_ctrl;
    struct pinctrl_state *leds_state;

    leds_pin_ctrl = devm_pinctrl_get(&pdev->dev);		//返回句柄
    leds_state = pinctrl_lookup_state(leds_pin_ctrl,"itop-leds1-on");//获取状态
    pinctrl_select_state(leds_pin_ctrl, leds_state);	//设置状态

    devm_pinctrl_put(leds_pin_ctrl);					//释放句柄
    return 0;
}

编写驱动文件

led_demo_drv.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");

#define DRV_NAME "leds_test"

static int demo_probe(struct platform_device *pdv){
	struct pinctrl *leds_pin_ctrl;
	struct pinctrl_state *leds_state;
	int ret;

	printk("led pinctl probe ok!\n");
	
	leds_pin_ctrl = devm_pinctrl_get(&pdv->dev);
	if (IS_ERR(leds_pin_ctrl)){
		printk("leds_pin_ctrl,failed,%ld\n",PTR_ERR(leds_pin_ctrl));
		return -1;
	}

	leds_state = pinctrl_lookup_state(leds_pin_ctrl,"itop-leds1-on");
	if (IS_ERR(leds_state)) {
		printk("leds_state,failed,%ld\n",PTR_ERR(leds_state));
		return -1;
	}
	
	ret = pinctrl_select_state(leds_pin_ctrl, leds_state);
	if(ret<0){
		printk("pinctrl_select_state,failed\n");
		return -1;	
	}
	devm_pinctrl_put(leds_pin_ctrl);
	
	return 0;
}

static int demo_remove(struct platform_device *pdv){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	return 0;
}

static const struct of_device_id of_demo_dt_match[] = {
	{.compatible = DRV_NAME},
	{},
};

MODULE_DEVICE_TABLE(of,of_demo_dt_match);

static struct platform_driver platform_drv_demo = {
	.driver	= {
		.name	= DRV_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = of_demo_dt_match,
	},
	.probe	= demo_probe,
	.remove	= demo_remove,
};

static int __init demo_init(void){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	platform_driver_register(&platform_drv_demo);
	return 0;
}
static void __exit demo_exit(void){
	printk(KERN_EMERG "%s ok!\n",__FUNCTION__);
	platform_driver_unregister(&platform_drv_demo);
	return;
}

module_init(demo_init);
module_exit(demo_exit);

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

export ARCH=arm

obj-m += led_demo_drv.o

KDIR := /home/jerry/Projects/itop4412_kernel_4_14_2_bsp/linux-4.14.2_iTop-4412_scp/

PWD ?= $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	rm -rf *.o

交叉编译然后加载到内核后发现probe可以正常执行,LED灯正常点亮。