zoukankan      html  css  js  c++  java
  • 全志_基于dts设备树驱动开发

    基于设备树开发
    
    
    平台:A64
    源码:Linux3.10
    
    路径:Linux/arch/arm(arm64)/boot/dts/    ***.dts    Makefile    //A64 是64位,选择arm64
    
    
    Linux2.6 之前没有dts(arch/arm/plat-xxx和arch/arm/mach-xxx存在大量垃圾代码)
    
    dts:结点+属性(name=value)
    dts---(dtc)--->dtb
    
    
    DT
    DTS
    DTSI
    OF:open firmware 开源硬件
    FDT
    DTB
    DTC
    
    语法:
    1.根节点    /{
    2.节点-子节点    <名称>[@<设备地址>]    cpu@1 []可省,每个节点都需要一个compatible属性
    3.属性
    compatible属性    作用:匹配    compatible = "jk,leds";
    #address-cells和#size-cells属性    作用:地址占cell个数和长度占cell个数    #address-cells = <2> #size-cells = <1> 描述子节点
    reg属性    作用:地址    reg = <address1 length1 [address2 length2][address3 length3]>
    中断信息属性--interrupts    中断号
    #interrupt-cells    中断控制器字节属性
    
    添加节点:
    node@12345{
    compatible = "jk,leds";
    reg = <0x1111 0x111>
    
    
    
    1.内核启动会加载dtb文件,加载信息到系统
    2.节点位置:ls proc/device-tree    (设备树节点信息)
    
     
    
     
    
    
    static int__init dt_drv_init(void)
    {
    /*
    在代码中获取整个节点信息
    
    
    
    */
    //1.先获取节点
    of_find_node_by_path("/text@123");
    
    //2.获取节点中属性
    of_find_property();
    
    
    
    return 0;
    }
    
    
    static void__exit dt_drv_init(void)
    {
    
    
    }
    
     
    
     
    
    module_init(dt_drv_init);
    module_exit(de_drv_exit);
    modules_license("GPL");
    
     
    
    
    #gpio-cells = <6>”表示在设备树里描述使用一个gpio口需要提供6个指定的参数.
    
    gpio = <&pio 1 1 1 1 1 0>;
    | | | | | | | |-------------------表示有效电平
    | | | | | | |----------------------上下拉, 0关闭功能, 1上拉, 2下拉, 3保留
    | | | | | |--------------------------驱动力,电流等级(0 - 3),级别越高,输出电流越大
    | | | | |------------------------------gpio功能类型(复用类型),0输入, 1输出, 6和外部中断,7关闭功能(具体查手册)
    | | | |----------------------------------pin bank 内偏移(即组内第几个io口).
    | | |--------------------------------------哪组gpio, PA(0),PB(1),PC(2),PD(3),PE(4),PF(5),PG(6),PH(7),PI(8),PJ(9),PK(10),PL(11)
    | |-------------------------------------------指向哪个gpio控制器, pio / r_pio(PL组)
    |--------------------------------------------------属性名字(随便命名)
    
    
    1.两个GPIO控制器    r_pio pio
    
    ___________________________________________________________________________________
    
    获取设备树里设备节点的gpio口信息:
    
    #include <linux/of_gpio.h>
    
    //只需一个函数即可
    int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags);
    
    /*
    功能:函数用于获取指定名称的 gpio 信息
    np: 需要查找 GPIO 的节点;
    propname: GPIO 信息的属性名字;
    index:    属性 propname 中属性值的索引(表示获取属性里的第几个值);    
    flags: 存放 gpio 的 flags;
    返回值: 成功:返回 gpio 编号, flags 存放 gpio 配置信息;失败:返回 null
    */
    
     
    
    
    //其中flags一定得注意,按文档里的说明应就是一个int类型的值,但根本就不能为int参数(会导致kernel panic),
    //通过阅读内核里的代码得出, flags的参数应为struct gpio_config类型. 定义在下面文件:
    
    "include/linux/sys_config.h"
    struct gpio_config {
    u32 gpio; /* gpio global index, must be unique */
    u32 mul_sel; /* multi sel val: 0 - input, 1 - output... */
    u32 pull; /* pull val: 0 - pull up/down disable, 1 - pull up... */
    u32 drv_level; /* driver level val: 0 - level 0, 1 - level 1... */
    u32 data; /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */
    };
    
    
    /* 节点 */
    struct device_node {
    const char *name; /* 节点中属性为name的值 */
    const char *type; /* 节点中属性为device_type的值 */
    char    *full_name; /* 节点的名字,在device_node结构体后面放一个字符串,full_name指向它 */
    struct    property *properties; /* 链表,连接该节点的所有属性 */
    struct    device_node *parent; /* 指向父节点 */
    struct    device_node *child; /* 指向孩子节点 */
    struct    device_node *sibling; /* 指向兄弟节点 */
    };
    
    /* 属性 */
    struct property {
    char    *name; /* 属性的名字,指向设备树文件的string block */
    int    length; /* 属性名字的字节数 */
    void    *value; /* 属性的值,指向struct block */
    struct property *next; /* 链表,连接下一个属性 */
    };
    
     
    
    Linux 系统为 device tree 提供了标准的 API 接口:
    (OF:    API)
    
    1.    unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
    /*
    功能:函数用于获取中断号
    dev: 要解析中断号的设备;
    index: dts 源文件中节点 interrupt 属性值索引
    返回值:解析成功返回中断号,失败:返回 0
    */
    
    2.    void __iomem *of_iomap(struct device_node *np, int index);
    /*
    功能:函数用于获取映射内存
    np: 映射内存的设备节点;
    index: dts 源文件中节点 interrupt 属性值索引;
    返回值:映射成功 IO memory 的虚拟地址,失败:返回 NULL
    */
    
    
    3.    static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value)
    /*
    功能:函数用于获取节点中的属性值
    np: 获取属性值的节点
    propname: 属性名称;
    out_value:属性值
    返回值: 映射成功 IO memory 的虚拟地址,失败:返回 NULL
    */
    
    
    4.    static inline int of_property_read_string_index(struct device_node *np, const char *propname, const char **output)
    /*
    功能:函数用于获取节点中属性值
    np: 获取属性值的节点
    propname: 属性名称;
    output: 存放返回字符串;
    返回值: 取值成功,返回 0
    */
    
    
    5.    static inline int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output)
    /*
    功能:函数用于获取节点中属性值
    np: 获取属性值的节点
    propname: 属性名称;
    index: 索引配置在 dts 中属性为 propname 的值;
    output:    存放返回字符串
    返回值: 取值成功,返回 0
    */
    
    
    6.    extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
    /*
    功能:函数用于获取指定名称的节点
    from: 开始查找节点,如果为NULL,则从根节点开始;
    name: 节点名字;
    返回值: 成功:得到节点的结构体首地址;失败:NUL
    */
    
    
    7.    extern struct device_node *of_find_node_by_name(struct device_node *from, const char *type);
    /*
    功能:函数用于获取指定 device_type 的节点
    from: 开始查找节点,如果为NULL,则从根节点开始;
    type: 节点名字;
    返回值: 成功:得到节点的结构体首地址;失败:NUL
    */
    
    
    8.    extern struct device_node *of_find_node_by_path(const char *path);
    /*
    功能:函数用于获取指定路径的节点
    path: 通过指定路径查找节点;
    返回值: 成功:得到节点的结构体首地址;失败:NUL
    */
    
    
    9.    int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags)
    /*
    功能:函数用于获取指定名称的 gpio 信息
    np: 需要查找 GPIO 的节点;
    propname: GPIO 信息的属性;
    index:    属性 propname 中属性值的索引;    
    flags: 存放 gpio 的 flags;
    返回值: 成功:返回 gpio 编号, flags 存放 gpio 配置信息;失败:返回 null
    */
    
     
    
    ___________________________________________________________________________________
    
    
    获取到int类型的gpio口后,就可以使用linux/gpio.h里的gpio口操作函数:
    
    #include <linux/gpio.h> //里面声明io口的操作函数
    
    int gpio_request(unsigned gpio, const char *label); //每个io只能被请求一次,可防止多个驱动来控制同一个IO口
    void gpio_free(unsigned gpio);    //释放已请求的io口
    int gpio_direction_input(unsigned gpio); //作输入功能, gpio用于指定具体哪个io口
    int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平
    int gpio_get_value(unsigned gpio); //获取指定IO口的电平
    void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)
    int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
    
    
    ___________________________________________________________________________________
    
    设备树里的描述:
    
    jkbuzzer {
    compatible = "jk,buzzer";
    gpios = <&pio 3 24 1 1 1 1>; 
    };
    
    jkleds {
    compatible = "jk,leds";
    gpios = <&r_pio 11 10 1 1 1 1>, <&r_pio 11 12 1 1 1 1>;
    };
    
    
    //PH8
    text {
    compatible = "text_";
    gpios = <&pio 7 9 1 1 1 0>;
    };
    
     
    
    
    ___________________________________________________________________________________
    
    代码测试:
    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/gpio.h>
    #include <linux/sys_config.h>
    #include <linux/delay.h>
    
    int myprobe(struct platform_device *pdev)
    {
    struct device_node *nd = pdev->dev.of_node;
    int gpio; 
    struct gpio_config config;
    
    
    printk("gpio count:%d
    ", of_gpio_named_count(nd, "gpios"));
    gpio = of_get_named_gpio_flags(nd, "gpios", 0, (enum of_gpio_flags *)&config);
    if (!gpio_is_valid(gpio))
    printk("gpio isn't valid
    ");
    if (gpio_request(gpio, pdev->name) < 0)
    printk("gpio request failed %d
    ", gpio);
    
    gpio_direction_output(gpio, 1);
    msleep(3000);
    gpio_direction_input(gpio);
    gpio_free(gpio);
    return 0;
    }
    
    int myremove(struct platform_device *pdev)
    {
    printk("in myremove ...
    ");
    return 0;
    }
    
    struct of_device_id ids[] = {
    {.compatible = "jk,buzzer"},
    {},
    };
    
    struct platform_driver mydrv = {
    .probe = myprobe,
    .remove = myremove,
    
    .driver = {
    .owner = THIS_MODULE,
    .name = "mydrv" ,
    
    .of_match_table = ids,
    },
    };
    
    module_platform_driver(mydrv);
    MODULE_LICENSE("GPL");
    
     
    
     
    
    代码测试:
    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/gpio.h>
    #include <linux/sys_config.h>
    #include <linux/delay.h>
    
    int myprobe(struct platform_device *pdev)
    {
    struct device_node *nd = pdev->dev.of_node;
    int gpio, n, i; 
    struct gpio_config config;
    
    
    n = of_gpio_named_count(nd, "gpios");
    for (i = 0; i < n ; i++)
    {
    gpio = of_get_named_gpio_flags(nd, "gpios", i, (enum of_gpio_flags *)&config);
    if (!gpio_is_valid(gpio))
    printk("gpio isn't valid
    ");
    if (gpio_request(gpio, pdev->name) < 0)
    printk("gpio request failed %d
    ", gpio);
    
    gpio_direction_output(gpio, 1);
    msleep(3000);
    gpio_direction_input(gpio);
    gpio_free(gpio);
    }
    return 0;
    }
    
    int myremove(struct platform_device *pdev)
    {
    printk("in myremove ...
    ");
    return 0;
    }
    
    struct of_device_id ids[] = {
    {.compatible = "jk,leds"},
    {},
    };
    
    struct platform_driver mydrv = {
    .probe = myprobe,
    .remove = myremove,
    
    .driver = {
    .owner = THIS_MODULE,
    .name = "mydrv" ,
    
    .of_match_table = ids,
    },
    };
    
    module_platform_driver(mydrv);
    MODULE_LICENSE("GPL");
    
     
    
     
    
     
    
     
    
     
    
     
    
     
    
    优质博客:
    https://blog.csdn.net/jklinux/article/details/82391923
    http://www.pianshen.com/article/520912458/
    https://www.linuxidc.com/Linux/2017-02/140818.htm
    
     
  • 相关阅读:
    前端面试日更解答 interview-answe 1+1 2020-04-05
    Kafka学习系列----- 消费时序图
    JVM 垃圾回收算法简析
    ORM 框架选型对比
    Spring 中的设计模式之单例模式实现
    Synchroinzed 与lock 锁的区别
    Spring源码分析之ApplicationContextAware
    Springboot 启动简析
    HTTP/2.0 简单分析
    HTTPS 原理简要分析
  • 原文地址:https://www.cnblogs.com/panda-w/p/11759151.html
Copyright © 2011-2022 走看看