设备树的引入是为了节省代码,平台文件中,对应pin的管理是很糟糕的,在设备树中添加了pinctrl子系统; pinctrl和原来的GPIO子系统同时存在。
一、参考文档
1、 GPIO文档
linux的GPIO系统官方文档:
Documentation/devicetree/bindings/gpio/gpio.txt
参考的内容:
Example of a node using GPIOs:
node {
enable-gpios = <&qe_pio_e 18 GPIO_ACTIVE_HIGH>;
};
设备树的节点,可以包含,互相也可以引用--看到“&”,表示做了引用(也可以理解面向对象编程的重写);
设备树的节点,“xxx :xxx”表示这个节点可以引用
&bank gpioa1 gpiob1
18表示gpio在bank中编号
GPIO_ACTIVE_HIGH 表示高电平(测试无效,是设备树中强制规定必须有2个cells)
三星的GPIO系统官方文档:
Documentation/devicetree/bindings/gpio/gpio-samsung.txt
2、pinctrl文档
linux的pinctrl系统官方文档:
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
三星的pinctrl系统官方文档:
Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
源码文件:
dt-bindings/pinctrl/samsung.h
arch/arm/boot/dts/exynos4412-pinctrl.dtsi
二、硬件电路结构
itop4412的两个led灯
gpl2-0对应<&gpl2 0 GPIO_ACTIVE_HIGH>
gpk1-1对应<&gpk1 1 GPIO_ACTIVE_HIGH>
设备树增加两个属性
gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
屏蔽掉其它代码对它们的引用
三、驱动测试源码
itop4412_of_get_gpios.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SKYFALL");
MODULE_DESCRIPTION("itop4412_of_get_gpios");
#define DRIVER_NAME "seedling"
int gpio_pin[2] = {-1};
static int leds_probe(struct platform_device * pdev)
{
struct device_node *node = pdev->dev.of_node;
int ret;
printk("led init\n");
gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0);
gpio_pin[1] = of_get_named_gpio(node, "gpios2", 0);
ret = gpio_request(gpio_pin[0], "led2");
if(ret != 0)
{
printk("gpio_pin[0] request %d failed.", gpio_pin[0]);
return ret;
}
ret = gpio_request(gpio_pin[1], "led3");
if (gpio_pin[1] < 0)
printk("gpio_pin[1] is not available \n");
if(ret != 0)
{
printk("gpio_pin[1] request %d failed.", gpio_pin[1]);
return ret;
}
printk("gpio_pin[0] is %d\n",gpio_pin[0]);
printk("gpio_pin[1] is %d\n",gpio_pin[1]);
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 leds_remove(struct platform_device * pdev)
{
printk(KERN_ALERT "Goodbye, curel world, this is remove\n");
gpio_set_value(gpio_pin[0], 0);
gpio_set_value(gpio_pin[1], 0);
return 0;
}
static const struct of_device_id of_leds_dt_match[] = {
{.compatible = DRIVER_NAME},
{},
};
MODULE_DEVICE_TABLE(of,of_leds_dt_match);
static struct platform_driver leds_driver = {
.probe = leds_probe,
.remove = leds_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_leds_dt_match,
},
};
static int __init leds_init(void)
{
printk(KERN_ALERT "leds_init!\n");
printk("%s,%d\n",__func__,__LINE__);
platform_driver_register(&leds_driver);
return 0;
}
static void __exit leds_exit(void)
{
printk(KERN_ALERT "leds_exit!\n");
printk("%s,%d\n",__func__,__LINE__);
platform_driver_unregister(&leds_driver);
return ;
}
module_init(leds_init);
module_exit(leds_exit);
四、Makefile
#!/bin/bash
$(warning KERNELRELEASE = $(KERNELRELEASE))
ifeq ($(KERNELRELEASE),)
#内核的源码路径, ?= 条件赋值, uname -r 得到内核版本号
KERNELDIR ?= /home/mint/itop/linux_4.14
#KERNELDIR ?=
# := 立即赋值, 得到当前的绝对路径
PWD := $(shell pwd)
# -C 切换工作路径, $(MAKE) = make
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules clean
else
# 生成模块
obj-m := itop4412_of_get_gpios.o
endif
五、测试
int of_get_named_gpio(struct device_node *np, const char *propname, int index);
功能:of_get_named_gpio,从设备树中提取gpio 口;
参数np:设备节点指针;
参数propname:属性名;
参数index:gpio口引脚标号;
返回值: 成功,得到GPIO口编号;失败,负数,绝对值是错误码。
of_get_named_gpio(node,"gpios1",0)
of_get_named_gpio(node,"gpios2",0)
1、加载驱动
insmod itop4412_of_get_gpios.ko
2、卸载驱动
rmmod itop4412_of_get_gpios
驱动加载的时候能够把灯点亮,驱动卸载的时候能够把LED灯熄灭。