ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux字符设备驱动详解六(设备树实现RGB灯驱动)

2021-11-27 23:00:42  阅读:202  来源: 互联网

标签:node led struct RGB Linux device 驱动 节点 red


前言

请先阅读:
Linux字符设备驱动详解
Linux字符设备驱动详解二(使用设备驱动模型)
Linux字符设备驱动详解三(使用class)
Linux字符设备驱动详解四(使用自属的xbus驱动总线)
Linux字符设备驱动详解五(使用platform虚拟平台总线)
本文主要来自正点原子、野火Linux教程及本人理解,若有侵权请及时联系本人删除。

正文

一、DTS设备树基本语法从上到下

背景

硬件设备中种类逐年递增,板级platform平台设备文件越来越多,这么多的设备如果都要自己写platform_device.c文件,那将需要跟每个外设的寄存器打交道,并且需要很多的platform_device.c文件
在这里插入图片描述

设备树简介

在这里插入图片描述

  • DTS(device tree source):设备树源文件,ASCII 格式
  • DTC(device tree compiler):设备树编译工具
  • DTB(device tree blob):二进制设备树
设备树使用

uboot负责加载到内存,内核解析使用

设备树源文件

ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts

二进制设备树

pc:ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dtb
开发板:/boot/dtbs/4.19.71-imx-r1/imx6ull-seeed-npi.dtb

设备树编译工具

内核编译

//进行内核配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
//编译dts
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

手工编译

./scripts/dtc/dtc -I dts -O dtb -o xxx.dtb arch/arm/boot/dts/xxx.dts // 编译 dts 为 dtb
./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb // 反编译 dtb 为 dts
  • -I:指定输入格式
  • -O:指定输出格式
  • -o:指定输出文件
设备树框架
  • 从上到下
    • 头文件
    • 主体
    • 子节点追加内容
  • 从外到内
    • 属性
    • 其他子节点
      • 属性
      • 其他子节点

头文件:

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

主体:

/ {  
    model = "Seeed i.MX6 ULL NPi Board";
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

    aliases {
            pwm0 = &pwm1;
            pwm1 = &pwm2;
            pwm2 = &pwm3;
            pwm3 = &pwm4;
    };
    chosen {
            stdout-path = &uart1;
    };

    memory {
            reg = <0x80000000 0x20000000>;
    };

    reserved-memory {
            #address-cells = <1>;
            #size-cells = <1>;
            ranges;

            linux,cma {
                    compatible = "shared-dma-pool";
                    reusable;
                    size = <0x14000000>;
                    linux,cma-default;
            };
    };
    ...
};
  • 多个根节点合并
  • 根节点下包含多个子节点

子节点追加内容

&cpu0 {
    dc-supply = <&reg_gpio_dvfs>;
    clock-frequency = <800000000>;
};

&clks {
    assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
    assigned-clock-rates = <786432000>;
};


&fec1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet1>;
    phy-mode = "rmii";
    phy-handle = <&ethphy0>;
    status = "okay";
};

节点命令

基本方法

node-name@unit-address{

属性1 = …

属性2 = …

属性3= …

子节点…

}
  • node-name:指定节点的名称
  • “unit-address”用于指定“单元地址”

节点标签

cpu0: cpu@0 {
    compatible = "arm,cortex-a7";
    device_type = "cpu";
    reg = <0>;
}
  • cpu0:为节点名称器一个别名

别名子节点

    aliases {

二、DTS设备树基本语法从外到内

  • 从上到下

    • 头文件
    • 主体
    • 子节点追加内容
  • 从外到内

    • 属性
    • 其他子节点
      • 属性
      • 其他子节点

常见节点属性

compatible属性:
值类型:字符串

intc: interrupt-controller@a01000 {
    compatible = "arm,cortex-a7-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0xa01000 0x1000>,
          <0xa02000 0x100>;
};
  • arm:芯片厂商
  • cortex-a7-gic:模块对应的驱动名字

model属性:
值类型:字符串

model = "embedfire i.MX6 ULL NPi Board";
  • 准确描述当前板子型号信息

status属性:
值类型:字符串
在这里插入图片描述
reg属性:
值类型:一系列《地址、长度》对

ocrams: sram@900000 {
      compatible = "fsl,lpm-sram";
      reg = <0x900000 0x4000>;
    };
  • 地址:外设寄存器组的起始地址
  • 长度:外设寄存器组的字节长度

#address-cells和#size-cells属性:
值类型:u32

soc {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "simple-bus";
    interrupt-parent = <&gpc>;
    ranges;
    ocrams: sram@900000 {
            compatible = "fsl,lpm-sram";
            reg = <0x900000>;
    };
};
  • #address-cells :设置子节点中reg地址的数量
  • #size-cells:设置子节点中reg地址的长度的数量

linux系统中查看设备树

ls /sys/firmware/devicetree/base

或者

ls /proc/device-tree
  • 以目录的形式体现设备树结构

添加子节点

test_led{
	#address-cells = <1>;
	#size-cells = <1>;

	rgb_led_red@0x0209C000{
			compatible = "fire,rgb_led_red";
			reg = <0x0209C000 0x00000020>;
			status = "okay";
	};
};

三、获取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; 
    struct  device_node *parent; //父节点
    struct  device_node *child;  //子节点
    struct  device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
    struct  kobject kobj;
#endif
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

查节点

  • 路径/类型/名字/compatible

of_find_node_by_path()函数,根据路径找到节点

struct device_node *of_find_node_by_path(struct device_node *from,const char *path);

参数:

  • from:开始查找的节点,NULL表示从根节点开始查找
  • path:查找的节点名

返回值:
成功:device_node表示的节点
失败:NULL

of_find_node_by_type()函数,根据“device_type“属性来查找节点,不建议使用

struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

of_find_node_by_name()函数,根据"name"属性来查找节点,不建议使用

struct device_node *of_find_node_by_name(struct device_node *from,const char *name);

of_find_compatible_node()函数

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);

参数:

  • from:开始查找的节点,NULL表示从根节点开始查找
  • type:指定 device_type 属性值
  • compat:指定 compatible 属性值

返回值:
成功:device_node表示的节点
失败:NULL

查节点的属性值

struct property {
    char    *name;  	//属性名
    int     length;     //属性长度
    void    *value; 	//属性值
    struct property *next; //下一个属性
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
    unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
    struct bin_attribute attr;
#endif
};

of_find_property()函数

  • 节点+属性名

查找节点中的属性

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);

参数:

  • np:device_node表示的节点
  • name:查找的属性名字
  • lenp:属性值的字节数

返回值:
成功:property表示的属性
失败:NULL

案例:

test_property {
test_name = “hello”;
};

name:“hello”
lenp = 6
of_property_read_u32()函数,读取一个32位无符号整数

static inline int of_property_read_u32(const struct device_node *np,const char *propname,

参数:

  • np:device_node表示的节点
  • propname:查找的属性名字
  • out_value:属性值的整数值

返回值:
成功:0
失败:负值
of_property_read_u32_array()函数,读取32位无符号整数数组

int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz)
  • np:device_node表示的节点
  • name:查找的属性名字
  • out_value:读取到的数组值
  • sz :要读取的数组元素数量
    of_property_read_string()函数,读字符串
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)

参数:

  • np:device_node表示的节点
  • proname:查找的属性名字
  • out_string:读取到的字符串值

返回值:
成功:0
失败:负值

代码示例

以野火设备驱动模型代码为例
get_dts_info.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>


#define DEV_NAME            "get_dts_info"
#define DEV_CNT                 (1)
//定义字符设备的设备号
static dev_t led_devno;
//定义字符设备结构体chr_dev
static struct cdev led_chr_dev;
//创建类
struct class *led_chrdev_class;


struct device_node	*led_device_node; //led的设备树节点
struct device_node  *rgb_led_red_device_node; //rgb_led_red 红灯节点
struct property     *rgb_led_red_property;    //定义属性结构体指针
int size = 0 ;
unsigned int out_values[18];  //保存读取得到的REG 属性值

/*.open 函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
    int error_status = -1;

    printk("\n open form device \n");

    /*获取DTS属性信息*/
    led_device_node = of_find_node_by_path("/test_led");
    if(led_device_node == NULL)
    {
        printk(KERN_ALERT "\n get led_device_node failed ! \n");
        return -1;
    }
    /*根据 led_device_node 设备节点结构体输出节点的基本信息*/
    printk(KERN_ALERT "name: %s",led_device_node->name); //输出节点名
    printk(KERN_ALERT "child name: %s",led_device_node->child->name);  //输出子节点的节点名


    /*获取 rgb_led_red_device_node 的子节点*/ 
    rgb_led_red_device_node = of_get_next_child(led_device_node,NULL); 
    if(rgb_led_red_device_node == NULL)
    {
        printk(KERN_ALERT "\n get rgb_led_red_device_node failed ! \n");
        return -1;
    }
    printk(KERN_ALERT "name: %s",rgb_led_red_device_node->name); //输出节点名
    printk(KERN_ALERT "parent name: %s",rgb_led_red_device_node->parent->name);  //输出父节点的节点名


    /*获取 rgb_led_red_device_node 节点  的"compatible" 属性 */ 
    rgb_led_red_property = of_find_property(rgb_led_red_device_node,"compatible",&size);
    if(rgb_led_red_property == NULL)
    {
        printk(KERN_ALERT "\n get rgb_led_red_property failed ! \n");
        return -1;
    }
    printk(KERN_ALERT "size = : %d",size);                      //实际读取得到的长度
    printk(KERN_ALERT "name: %s",rgb_led_red_property->name);   //输出属性名
    printk(KERN_ALERT "length: %d",rgb_led_red_property->length);        //输出属性长度
    printk(KERN_ALERT "value : %s",(char*)rgb_led_red_property->value);  //属性值


    /*获取 reg 地址属性*/
    error_status = of_property_read_u32_array(rgb_led_red_device_node,"reg",out_values, 2);
    if(error_status != 0)
    {
        printk(KERN_ALERT "\n get out_values failed ! \n");
        return -1;
    }
    printk(KERN_ALERT"0x%08X ", out_values[0]);
    printk(KERN_ALERT"0x%08X ", out_values[1]);

    return 0;
}

/*.release 函数*/
static int led_chr_dev_release(struct inode *inode, struct file *filp)
{
    printk("\nrelease\n");
    return 0;
}


/*字符设备操作函数集*/
static struct file_operations  led_chr_dev_fops = 
{
    .owner = THIS_MODULE,
    .open = led_chr_dev_open,
    .release = led_chr_dev_release,
};



/*
*驱动初始化函数
*/
static int __init led_chrdev_init(void)
{
    int ret = 0;
    printk("led chrdev init\n");
    //第一步
    //采用动态分配的方式,获取设备编号,次设备号为0,
    //设备名称为EmbedCharDev,可通过命令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;
    }

    led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");
    //第二步
    //关联字符设备结构体cdev与文件操作结构体file_operations
    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;
    }

    //创建设备
    device_create(led_chrdev_class, NULL, led_devno, NULL,
			      DEV_NAME);
    return 0;

add_err:
    //添加设备失败时,需要注销设备号
    unregister_chrdev_region(led_devno, DEV_CNT);
alloc_err:
    return ret;
}





/*
*驱动注销函数
*/

static void __exit led_chrdev_exit(void)
{
    printk("chrdev exit\n");
   
    device_destroy(led_chrdev_class, led_devno);   //清除设备
    cdev_del(&led_chr_dev);                        //清除设备号
    unregister_chrdev_region(led_devno, DEV_CNT);  //取消注册字符设备
    class_destroy(led_chrdev_class);               //清除类
}

module_init(led_chrdev_init);
module_exit(led_chrdev_exit);

MODULE_LICENSE("GPL");

四、 设备树实现RGB灯驱动

设备树添加节点信息

RGB灯的相关寄存器

/*
*CCM_CCGR1                         0x020C406C
*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04  0x020E006C
*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04  0x020E02F8
*GPIO1_GD                          0x0209C000
*GPIO1_GDIR                        0x0209C004
*/


/*
*CCM_CCGR3                         0x020C4074
*IOMUXC_SW_MUX_CTL_PAD_CSI_HSYNC   0x020E01E0
*IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC   0x020E046C
*GPIO4_GD                          0x020A8000
*GPIO4_GDIR                        0x020A8004
*/


/*
*CCM_CCGR3                         0x020C4074
*IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC   0x020E01DC
*IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC   0x020E0468
*GPIO4_GD                          0x020A8000
*GPIO4_GDIR                        0x020A8004
*/
	/*添加led节点*/
	rgb_led{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "fire,rgb_led";

		/*红灯节点*/
		ranges;
		rgb_led_red@0x020C406C{
			reg = <0x020C406C 0x00000004
			       0x020E006C 0x00000004
			       0x020E02F8 0x00000004
				   0x0209C000 0x00000004
			       0x0209C004 0x00000004>;
			status = "okay";
		};

		/*绿灯节点*/
		rgb_led_green@0x020C4074{
			reg = <0x020C4074 0x00000004
			       0x020E01E0 0x00000004
			       0x020E046C 0x00000004
				   0x020A8000 0x00000004
			       0x020A8004 0x00000004>;
			status = "okay";
		};

		/*蓝灯节点*/
		rgb_led_blue@0x020C4074{
			reg = <0x020C4074 0x00000004
			       0x020E01DC 0x00000004
			       0x020E0468 0x00000004
				   0x020A8000 0x00000004
			       0x020A8004 0x00000004>;
			status = "okay";
		};
	};

reg属性内存映射

of_iomap()函数将reg属性值的物理地址转化为虚拟地址

void __iomem *of_iomap(struct device_node *np,
int index)

参数:

  • np:device_node表示的节点
  • index:通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。

代码示例

以野火设备驱动模型代码为例
dts_led.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>

#include <linux/platform_device.h>

/*------------------字符设备内容----------------------*/
#define DEV_NAME "rgb_led"
#define DEV_CNT (1)

/*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
struct led_resource
{
	struct device_node *device_node; //rgb_led_red的设备树节点
	void __iomem *virtual_CCM_CCGR;
	void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
	void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
	void __iomem *virtual_DR;
	void __iomem *virtual_GDIR;
};

static dev_t led_devno;					 //定义字符设备的设备号
static struct cdev led_chr_dev;			 //定义字符设备结构体chr_dev
struct class *class_led;				 //保存创建的类
struct device *device;					 // 保存创建的设备
struct device_node *rgb_led_device_node; //rgb_led的设备树节点结构体

/*定义 R G B 三个灯的led_resource 结构体,保存获取得到的节点信息*/
struct led_resource led_red;
struct led_resource led_green;
struct led_resource led_blue;

/*字符设备操作函数集,open函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
	printk("\n open form driver \n");
	return 0;
}

/*字符设备操作函数集,write函数*/
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{

	int ret,error;
	unsigned int register_data = 0; //暂存读取得到的寄存器数据
	unsigned char receive_data[10]; //用于保存接收到的数据
	unsigned int write_data; //用于保存接收到的数据

	if(cnt>10)
			cnt =10;

	error = copy_from_user(receive_data, buf, cnt);
	if (error < 0)
	{
		return -1;
	}

	ret = kstrtoint(receive_data, 16, &write_data);
	if (ret) {
		return -1;
        }

	/*设置 GPIO1_04 输出电平*/
	if (write_data & 0x04)
	{
		register_data = ioread32(led_red.virtual_DR);
		register_data &= ~(0x01 << 4);
		iowrite32(register_data, led_red.virtual_DR); // GPIO1_04引脚输出低电平,红灯亮
	}
	else
	{
		register_data = ioread32(led_red.virtual_DR);
		register_data |= (0x01 << 4);
		iowrite32(register_data, led_red.virtual_DR); // GPIO1_04引脚输出高电平,红灯灭
	}

	/*设置 GPIO4_20 输出电平*/
	if (write_data & 0x02)
	{
		register_data = ioread32(led_green.virtual_DR);
		register_data &= ~(0x01 << 20);
		iowrite32(register_data, led_green.virtual_DR); // GPIO4_20引脚输出低电平,绿灯亮
	}
	else
	{
		register_data = ioread32(led_green.virtual_DR);
		register_data |= (0x01 << 20);
		iowrite32(register_data, led_green.virtual_DR); // GPIO4_20引脚输出高电平,绿灯灭
	}

	/*设置 GPIO4_19 输出电平*/
	if (write_data & 0x01)
	{
		register_data = ioread32(led_blue.virtual_DR);
		register_data &= ~(0x01 << 19);
		iowrite32(register_data, led_blue.virtual_DR); //GPIO4_19引脚输出低电平,蓝灯亮
	}
	else
	{
		register_data = ioread32(led_blue.virtual_DR);
		register_data |= (0x01 << 19);
		iowrite32(register_data, led_blue.virtual_DR); //GPIO4_19引脚输出高电平,蓝灯灭
	}

	return cnt;
}

/*字符设备操作函数集*/
static struct file_operations led_chr_dev_fops =
	{
		.owner = THIS_MODULE,
		.open = led_chr_dev_open,
		.write = led_chr_dev_write,
};

/*----------------平台驱动函数集-----------------*/
static int led_probe(struct platform_device *pdv)
{

	int ret = -1; //保存错误状态码
	unsigned int register_data = 0;

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

	/*获取rgb_led的设备树节点*/
	rgb_led_device_node = of_find_node_by_path("/rgb_led");
	if (rgb_led_device_node == NULL)
	{
		printk(KERN_ERR "\t  get rgb_led failed!  \n");
		return -1;
	}

	/*获取rgb_led节点的红灯子节点*/
	led_red.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_red");
	if (led_red.device_node == NULL)
	{
		printk(KERN_ERR "\n get rgb_led_red_device_node failed ! \n");
		return -1;
	}


	/*获取 reg 属性并转化为虚拟地址*/
	led_red.virtual_CCM_CCGR = of_iomap(led_red.device_node, 0);
	led_red.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_red.device_node, 1);
	led_red.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_red.device_node, 2);
	led_red.virtual_DR = of_iomap(led_red.device_node, 3);
	led_red.virtual_GDIR = of_iomap(led_red.device_node, 4);

	/*初始化红灯*/
	register_data = ioread32(led_red.virtual_CCM_CCGR);
	register_data |= (0x03 << 26);
	iowrite32(register_data, led_red.virtual_CCM_CCGR); //开启时钟

	register_data = ioread32(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
	register_data &= ~(0xf << 0);
	register_data |= (0x05 << 0);
	iowrite32(register_data, led_red.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能

	register_data = ioread32(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
	register_data = (0x10B0);
	iowrite32(register_data, led_red.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性

	register_data = ioread32(led_red.virtual_GDIR);
	register_data |= (0x01 << 4);
	iowrite32(register_data, led_red.virtual_GDIR); //设置GPIO1_04 为输出模式

	register_data = ioread32(led_red.virtual_DR);
	register_data |= (0x01 << 4);
	iowrite32(register_data, led_red.virtual_DR); //设置 GPIO1_04 默认输出高电平






	/*获取rgb_led节点的绿灯子节点*/
	led_green.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_green");
	if (led_green.device_node == NULL)
	{
		printk(KERN_ERR "\n get rgb_led_green_device_node failed ! \n");
		return -1;
	}

	/*获取 reg 属性并转化为虚拟地址*/
	led_green.virtual_CCM_CCGR = of_iomap(led_green.device_node, 0);
	led_green.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_green.device_node, 1);
	led_green.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_green.device_node, 2);
	led_green.virtual_DR = of_iomap(led_green.device_node, 3);
	led_green.virtual_GDIR = of_iomap(led_green.device_node, 4);

	/*初始化绿灯*/
	register_data = ioread32(led_green.virtual_CCM_CCGR);
	register_data |= (0x03 << 12);
	iowrite32(register_data, led_green.virtual_CCM_CCGR); //开启时钟

	register_data = ioread32(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
	register_data &= ~(0xf << 0);
	register_data |= (0x05 << 0);
	iowrite32(register_data, led_green.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能

	register_data = ioread32(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
	register_data = (0x10B0);
	iowrite32(register_data, led_green.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性

	register_data = ioread32(led_green.virtual_GDIR);
	register_data |= (0x01 << 20);
	iowrite32(register_data, led_green.virtual_GDIR); //设置GPIO4_IO20 为输出模式

	register_data = ioread32(led_green.virtual_DR);
	register_data |= (0x01 << 20);
	iowrite32(register_data, led_green.virtual_DR); //设置 GPIO4_IO20 默认输出高电平






	/*获取rgb_led节点的蓝灯子节点*/
	led_blue.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_blue");
	if (led_blue.device_node == NULL)
	{
		printk(KERN_ERR "\n get rgb_led_blue_device_node failed ! \n");
		return -1;
	}

	/*获取 reg 属性并转化为虚拟地址*/
	led_blue.virtual_CCM_CCGR = of_iomap(led_blue.device_node, 0);
	led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_blue.device_node, 1);
	led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_blue.device_node, 2);
	led_blue.virtual_DR = of_iomap(led_blue.device_node, 3);
	led_blue.virtual_GDIR = of_iomap(led_blue.device_node, 4);

	/*初始化蓝灯*/
	register_data = ioread32(led_blue.virtual_CCM_CCGR);
	register_data |= (0x03 << 12);
	iowrite32(register_data, led_blue.virtual_CCM_CCGR); //开启时钟

	register_data = ioread32(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
	register_data &= ~(0xf << 0);
	register_data |= (0x05 << 0);
	iowrite32(register_data, led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD); //设置复用功能

	register_data = ioread32(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
	register_data = (0x10B0);
	iowrite32(register_data, led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD); //设置PAD 属性

	register_data = ioread32(led_blue.virtual_GDIR);
	register_data |= (0x01 << 19);
	iowrite32(register_data, led_blue.virtual_GDIR); //设置GPIO4_IO19 为输出模式

	register_data = ioread32(led_blue.virtual_DR);
	register_data |= (0x01 << 19);
	iowrite32(register_data, led_blue.virtual_DR); //设置 GPIO4_IO19 默认输出高电平








	/*---------------------注册 字符设备部分-----------------*/

	//第一步
	//采用动态分配的方式,获取设备编号,次设备号为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;
}

static const struct of_device_id rgb_led[] = {
	{.compatible = "fire,rgb_led"},
	{/* sentinel */}};

/*定义平台设备结构体*/
struct platform_driver led_platform_driver = {
	.probe = led_probe,
	.driver = {
		.name = "rgb-leds-platform",
		.owner = THIS_MODULE,
		.of_match_table = rgb_led,
	}};

/*
*驱动初始化函数
*/
static int __init led_platform_driver_init(void)
{
	int DriverState;
	DriverState = platform_driver_register(&led_platform_driver);
	printk(KERN_ALERT "\tDriverState is %d\n", DriverState);
	return 0;
}

/*
*驱动注销函数
*/
static void __exit led_platform_driver_exit(void)
{
	/*取消物理地址映射到虚拟地址*/
	iounmap(led_green.virtual_CCM_CCGR);
	iounmap(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
	iounmap(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
	iounmap(led_green.virtual_DR);
	iounmap(led_green.virtual_GDIR);

	iounmap(led_red.virtual_CCM_CCGR);
	iounmap(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
	iounmap(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
	iounmap(led_red.virtual_DR);
	iounmap(led_red.virtual_GDIR);

	iounmap(led_blue.virtual_CCM_CCGR);
	iounmap(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
	iounmap(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
	iounmap(led_blue.virtual_DR);
	iounmap(led_blue.virtual_GDIR);

	/*删除设备*/
	device_destroy(class_led, led_devno);		  //清除设备
	class_destroy(class_led);					  //清除类
	cdev_del(&led_chr_dev);						  //清除设备号
	unregister_chrdev_region(led_devno, DEV_CNT); //取消注册字符设备

	/*注销字符设备*/
	platform_driver_unregister(&led_platform_driver);

	printk(KERN_ALERT "led_platform_driver exit!\n");
}

module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);

MODULE_LICENSE("GPL");

/**/

总结

第三点get_dts_info.c仅为获取DTS属性信息演示代码。

标签:node,led,struct,RGB,Linux,device,驱动,节点,red
来源: https://blog.csdn.net/weixin_45905650/article/details/121583530

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有