linux驱动之设备树与GPIO子系统

linux驱动

第一章 linux驱动之设备与驱动
第三章 linux驱动之总线详解


linux驱动之设备树与GPIO子系统

  • linux驱动
  • 一、设备树
    • 1.设备树基本概念作用
    • 2.进程读取的常用of函数
    • 3.实例
  • 二、GPIO系统
    • 1.pinctrl子系统
    • 2.GPIO 子系统


一、设备树

1.设备树基本概念作用

1.DTS 、DTC 和 DTB的关系:dts是设备树的源文件,通过DTC转换中二进制的DTB文件。
2.DTS中描述了一些设备的资源,进程可以通过DTB文件直接读取设备中的一些信息。
linux驱动之设备树与GPIO子系统_第1张图片

2.进程读取的常用of函数

1.dts中的内容
linux驱动之设备树与GPIO子系统_第2张图片

2.通过of函数读取dts中的内容

设备节点结构体
struct device_node { 
const char *name; /* 节点名字 */ 
const char *type; /* 设备类型 */ 
phandle phandle; 
const char *full_name; /* 节点全名 */ 
struct fwnode_handle fwnode; 
struct property *properties; /* 属性 */ 
struct property *deadprops; /* removed 属性 */ 
struct device_node *parent; /* 父节点 */ 
struct device_node *child; /* 子节点 */ 
struct device_node *sibling; 
struct kobject kobj;
unsigned long _flags; 
void *data; 
};
属性结构体
struct property { char *name; /* 属性名字 */ 
int length; /* 属性长度 */
 void *value; /* 属性值 */ 
 struct property *next; /* 下一个属性 */ 
 unsigned long _flags; 
 unsigned int unique_id; 
 struct bin_attribute attr; 
 };
 
进程通过of函数将dts中某个节点或者某个内容返回到进程中定义的struct device_nodestruct property 中。
inline struct device_node *of_find_node_by_path(const char *path)
struct device_node *of_get_parent(const struct device_node *node)//找父节点
property *of_find_property(const struct device_node *np,const char *name,int *lenp)//找名字叫name的属性值
int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)//out_value为读到的数组值
nt of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)//找属性为Proname的数组,//out_value为读到的数组值
void __iomem *of_iomap(struct device_node *np,int index)//index 为寄存器reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置 0。

/**********添加我们要查找的节点的代码*******************************/
	// of_find_node_by_path函数通过路径查找节点
test_device_node = of_find_node_by_path("/test");
/**********获取compatible属性内容的代码****************************/
	// of_find_property函数查找节点属性
test_node_property = of_find_property(test_device_node, "compatible", &size);
/**********获取reg属性内容的代码**********************************/
ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
/*********************映射物理地址**************************/
vir_gpio_dr = of_iomap(pdev->dev.of_node, 0);

3.实例

特别注意:
当驱动和设备配对成功时,会调用int beep_probe(struct platform_device *pdev),而pdev->dev.of_node也指向了设备树的某个节点,所以此时一般不需要调用of_find_node_by_path("/test")去返回节点。

/*
 * @Author: topeet
 * @Description: 设备树下的平台总线驱动,匹配成功后,去设备树文件中获取硬件信息,然后物理地址映射为虚拟地址,接下来可以注册字符设备和杂项设备
 
 */
#include 
#include 
#include 
#include 
#include 
struct device_node *test_device_node;
struct property *test_node_property;
int size;
int ret = 0;
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;
unsigned int *vir_gpio_dr;
int beep_probe(struct platform_device *pdev)
{ //匹配成功以后,进入到probe函数
	printk("beep_probe\n");
	/*********************方法一:直接获取节点**************************/
	//printk("node name is %s\n",pdev->dev.of_node->name);
	/*********************方法二:通过函数获取硬件资源**************************/
	/* test_device_node = of_find_node_by_path("/test");  //获得设备节点
	if(test_device_node == NULL){
	printk("of_find_node_by_path is error \n");
	return -1;
	}*/
	//将此段代码注释掉
	test_node_property = of_find_property(test_device_node, "compatible", &size);
	ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 2);
	if (ret < 0)
	{
		printk("of_property_read_u32_array is error \n");
		return -1;
	}
	printk("out_values[0] is 0x%08x\n", out_values[0]);
	printk("out_values[1] is 0x%08x\n", out_values[1]);
	/*********************映射物理地址**************************/
	vir_gpio_dr = of_iomap(pdev->dev.of_node, 0);
	if (vir_gpio_dr == NULL)
	{
		printk("of_iomap is error \n");
		return -1;
	}
	printk("of_iomap is ok \n");
	return 0;
}
int beep_remove(struct platform_device *pdev)
{
	printk("beep_remove\n");
	return 0;
}
const struct platform_device_id beep_idtable = {
	.name = "beep_test",
};
const struct of_device_id of_match_table_test[] = {
	{.compatible = "test1234"},
	{},
};
struct platform_driver beep_driver = {
	//3. 在beep_driver结构体中完成了beep_probe和beep_remove
	.probe = beep_probe,
	.remove = beep_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "beep_test",
		.of_match_table = of_match_table_test //接下来我们改一下驱动,让他来匹配设备树里面test的节点
	},
	.id_table = &beep_idtable //4 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配
};

static int beep_driver_init(void)
{
	// 1.我们看驱动文件要从init函数开始看
	int ret = 0;
	//2. 在init函数里面注册了platform_driver
	ret = platform_driver_register(&beep_driver);
	if (ret < 0)
	{
		printk("platform_driver_register error \n");
	}
	printk("platform_driver_register ok \n");

	return 0;
}

static void beep_driver_exit(void)
{
	platform_driver_unregister(&beep_driver);
	printk("gooodbye! \n");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");


二、GPIO系统

1.pinctrl子系统

pinctrl子系统就是imx6ul-pinfunc.h,该文件中设置了引脚寄存器的各种可能情况。
驱动开发人员在fsl,pins中设置了引脚的寄存器,就不用把引脚的相关寄存器都找到然后设置了。

/*添加rgb_led节点*/

rgb_led{
    #address-cells = <1>;
    #size-cells = <1>;
    pinctrl-names = "default";
    compatible = "fire,rgb-led";
    pinctrl-0 = <&pinctrl_rgb_led>
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;//设置gpio1中的4号引脚为red。开发人员可以通过of_get_named_gpio得到此处的具体某个引脚。
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
    status = "okay";
};
//在iomuxc节点中添加pinctrl子节点
iomuxc节点:
汇总所需引脚的配置信息
pinctrl子系统预存iomux节点信息
相当于一个寄存器配置器的集合。
所有pin的控制器都属于这一节点。

&iomuxc {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_hog_1>;
    /*----------新添加的内容--------------*/
    pinctrl_rgb_led:rgb_led{
                    fsl,pins = <
                            MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0x000010B1
                            MX6UL_PAD_CSI_HSYNC__GPIO4_IO20     0x000010B1
                            MX6UL_PAD_CSI_VSYNC__GPIO4_IO19     0x000010B1
                    >;
            };

2.GPIO 子系统

GPIO子系统是指一套可以读取设备树引脚信息的函数

通过of_get_named_gpio得到rgb_led_red,rgb_led_green,rgb_led_blue
int of_get_named_gpio(struct device_node *np,const char *propname,int index)
gpio_request(leddev.led0, "led0");//gpio_request 函数用于申请一个 GPIO 管脚
gpio_direction_output(leddev.led0, 1);
gpio_set_value(leddev.led0, 0);
gpio_direction_input(key.irqkeydesc[0].gpio); 
gpio_get_value(keydesc->gpio); gpio_free(leddev.led0);


tatic int led_probe(struct platform_device *pdv)
{
  unsigned int  register_data = 0;  //用于保存读取得到的寄存器值
  int ret = 0;  //用于保存申请设备号的结果

    printk(KERN_EMERG "\t  match successed  \n");

    /*------------------第一部分---------------*/
    /*获取RGB的设备树节点*/
    rgb_led_device_node = of_find_node_by_path("/rgb_led");
    if(rgb_led_device_node == NULL)
    {
        printk(KERN_EMERG "\t  get rgb_led failed!  \n");
    }

    /*------------------第二部分---------------*/
    rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
    rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
    rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);

    printk("rgb_led_red = %d,\n rgb_led_green = %d,\n rgb_led_blue = %d,\n", rgb_led_red,\
    rgb_led_green,rgb_led_blue);

    /*------------------第三部分---------------*/
    gpio_direction_output(rgb_led_red, 1);
    gpio_direction_output(rgb_led_green, 1);
    gpio_direction_output(rgb_led_blue, 1);
使用函数of_get_named_gpio函数获取GPIO号,读取成功则返回读取得到的GPIO号。 “rgb_led_red”指定GPIO
的名字,这个参数要与rgb_led设备树节点中GPIO属性名对应, 参数“0”指定引脚索引,我们的设备树中一条属性中
只定义了一个引脚,我们只有一个所以设置为0/*------------------第四部分---------------*/
    /*---------------------注册 字符设备部分-----------------*/
    //第一步
    //采用动态分配的方式,获取设备编号,次设备号为0,
    //设备名称为rgb-leds,可通过命令cat  /proc/devices查看
    //DEV_CNT为1,当前只申请一个设备编号
    ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
    if(ret < 0){
        printk("fail to alloc led_devno\n");
        goto alloc_err;
    }
    //第二步
    //关联字符设备结构体cdev与文件操作结构体file_operations
    led_chr_dev.owner = THIS_MODULE;
    cdev_init(&led_chr_dev, &led_chr_dev_fops);
    //第三步
    //添加设备至cdev_map散列表中
    ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
    if(ret < 0)
    {
        printk("fail to add cdev\n");
        goto add_err;
    }

    //第四步
    /*创建类 */
    class_led = class_create(THIS_MODULE, DEV_NAME);

    /*创建设备*/
    device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);

    return 0;

add_err:
    //添加设备失败时,需要注销设备号
    unregister_chrdev_region(led_devno, DEV_CNT);
    printk("\n error! \n");
alloc_err:

    return -1;
}

你可能感兴趣的:(linux内核和驱动开发,android)