preface
Please read:
Detailed explanation of Linux character device driver
Detailed explanation of Linux character device driver II (using device driver model)
Detailed explanation of Linux character device driver III (using class)
Detailed explanation of Linux character device driver IV (using its own xbus driver bus)
Detailed explanation of Linux character device driver V (using platform virtual platform bus)
This article mainly comes from punctual atom, wildfire Linux tutorial and my understanding. If there is infringement, please contact me in time to delete it.
text
1, DTS device tree basic syntax from top to bottom
background
The types of hardware devices are increasing year by year, and there are more and more board level platform device files. If so many devices have to write their own platform_device.c file, which will need to deal with the registers of each peripheral and require a lot of platform_device.c file
Introduction to equipment tree
- DTS(device tree source): device tree source file in ASCII format
- DTC(device tree compiler): device tree compiler
- DTB(device tree blob): binary device tree
Device tree usage
uboot is responsible for loading into memory, which is used by kernel parsing
Device tree source file
ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts
Binary device tree
pc: ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dtb
Development board: / boot/dtbs/4.19.71-imx-r1/imx6ull-seeed-npi.dtb
Device tree compiler
Kernel compilation
//Kernel configuration make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig //Compiling dts make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
Manual compilation
./scripts/dtc/dtc -I dts -O dtb -o xxx.dtb arch/arm/boot/dts/xxx.dts // Compile dts to dtb ./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb // Decompile dtb to dts
- -1: Specify input format
- -O: Specify output format
- -o: Specify output file
Device tree framework
- From top to bottom
- Header file
- subject
- Child node additions
- From outside to inside
- attribute
- Other child nodes
- attribute
- Other child nodes
- ...
Header file:
#include <dt-bindings/input/input.h> #include "imx6ull.dtsi"
Main body:
/ { 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; }; }; ... };
- Merge multiple root nodes
- The root node contains multiple child nodes
Child node additions
&cpu0 { dc-supply = <®_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 = <ðphy0>; status = "okay"; };
Node command
Basic method
node-name@unit-address{ Attribute 1 = ... Attribute 2 = ... Attribute 3= ... Child nodes }
- Node name: Specifies the name of the node
- Unit address specifies the unit address
Node label
cpu0: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0>; }
- cpu0: give the node name an alias
Alias child node
aliases {
2, DTS device tree basic syntax from outside to inside
-
From top to bottom
- Header file
- subject
- Child node additions
-
From outside to inside
- attribute
- Other child nodes
- attribute
- Other child nodes
- ...
Common node properties
compatible attribute:
Value type: String
intc: interrupt-controller@a01000 { compatible = "arm,cortex-a7-gic"; #interrupt-cells = <3>; interrupt-controller; reg = <0xa01000 0x1000>, <0xa02000 0x100>; };
- arm: chip manufacturer
- cortex-a7-gic: the driver name corresponding to the module
model attribute:
Value type: String
model = "embedfire i.MX6 ULL NPi Board";
- Accurately describe the current board model information
status attribute:
Value type: String
reg attribute:
Value type: a series of address and length pairs
ocrams: sram@900000 { compatible = "fsl,lpm-sram"; reg = <0x900000 0x4000>; };
- Address: the starting address of the peripheral register group
- Length: the byte length of the peripheral register group
#Address cells and #size cells properties:
Value type: 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: sets the number of reg addresses in child nodes
- #Size cells: sets the number of reg addresses in the child node
Viewing device tree in linux system
ls /sys/firmware/devicetree/base
perhaps
ls /proc/device-tree
- Reflect the equipment tree structure in the form of directory
Add child node
test_led{ #address-cells = <1>; #size-cells = <1>; rgb_led_red@0x0209C000{ compatible = "fire,rgb_led_red"; reg = <0x0209C000 0x00000020>; status = "okay"; }; };
3, Get DTS attribute information
- Query the node where the attribute is located
- Query the attribute value of the node
Node representation
struct device_node { const char *name; //Node name const char *type; //Equipment type phandle phandle; const char *full_name; //Full name struct fwnode_handle fwnode; struct property *properties; //attribute struct property *deadprops; struct device_node *parent; //Parent node struct device_node *child; //Child node 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 };
Check node
- Path / type / name / compatible
of_ find_ node_ by_ The path() function finds the node according to the path
struct device_node *of_find_node_by_path(struct device_node *from,const char *path);
Parameters:
- From: the node to start searching. NULL means to start searching from the root node
- path: the node name to find
Return value:
Success: device_node represents the node
Failed: NULL
of_ find_ node_ by_ The type() function is used to find nodes according to the "device_type" attribute. It is not recommended
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
The of_find_node_by_name() function searches for nodes according to the "name" attribute. It is not recommended
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
of_find_compatible_node() function
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
Parameters:
- From: the node to start searching. NULL means to start searching from the root node
- Type: Specifies the value of the device_type attribute
- compat: Specifies the value of the compatible attribute
Return value:
Success: node represented by device_node
Failed: NULL
Query the attribute value of the node
struct property { char *name; //Attribute name int length; //Attribute length void *value; //Attribute value struct property *next; //Next attribute #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() function
- Node + attribute name
Find attributes in nodes
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
Parameters:
- np: node represented by device_node
- Name: the name of the attribute to be searched
- lenp: number of bytes of attribute value
Return value:
Success: the property represented by property
Failed: NULL
Case:
test_property { test_name = "hello"; };
name: "hello"
lenp = 6
The of_property_read_u32() function reads a 32-bit unsigned integer
static inline int of_property_read_u32(const struct device_node *np,const char *propname,
Parameters:
- np: node represented by device_node
- propname: the name of the property to find
- out_value: the integer value of the attribute value
Return value:
Success: 0
Failed: negative value
The of_property_read_u32_array() function reads an array of 32-bit unsigned integers
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz)
- np: node represented by device_node
- Name: the name of the attribute to be searched
- out_value: read array value
- sz: number of array elements to read
of_property_read_string() function, read string
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)
Parameters:
- np: node represented by device_node
- proname: the name of the property to find
- out_string: read string value
Return value:
Success: 0
Failed: negative value
Code example
Take the wild fire equipment driver model code as an example
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) //Defines the device number of the character device static dev_t led_devno; //Define character device structure chr_dev static struct cdev led_chr_dev; //Create class struct class *led_chrdev_class; struct device_node *led_device_node; //led device tree node struct device_node *rgb_led_red_device_node; //rgb_led_red node struct property *rgb_led_red_property; //Define attribute structure pointer int size = 0 ; unsigned int out_values[18]; //Save the read REG attribute value /*.open function*/ static int led_chr_dev_open(struct inode *inode, struct file *filp) { int error_status = -1; printk("\n open form device \n"); /*Get DTS attribute information*/ 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; } /*Output the basic information of the node according to the led_device_node device node structure*/ printk(KERN_ALERT "name: %s",led_device_node->name); //Output node name printk(KERN_ALERT "child name: %s",led_device_node->child->name); //Output the node name of the child node /*Get the child node of 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); //Output node name printk(KERN_ALERT "parent name: %s",rgb_led_red_device_node->parent->name); //Output the node name of the parent node /*Gets the "compatible" attribute of the rgb_led_red_device_node node node */ 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); //Actual read length printk(KERN_ALERT "name: %s",rgb_led_red_property->name); //Output attribute name printk(KERN_ALERT "length: %d",rgb_led_red_property->length); //Output attribute length printk(KERN_ALERT "value : %s",(char*)rgb_led_red_property->value); //Attribute value /*Get reg address properties*/ 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 function*/ static int led_chr_dev_release(struct inode *inode, struct file *filp) { printk("\nrelease\n"); return 0; } /*Character device operation function set*/ static struct file_operations led_chr_dev_fops = { .owner = THIS_MODULE, .open = led_chr_dev_open, .release = led_chr_dev_release, }; /* *Driver initialization function */ static int __init led_chrdev_init(void) { int ret = 0; printk("led chrdev init\n"); //First step //The equipment number is obtained by dynamic allocation, and the secondary equipment number is 0, //The device name is EmbedCharDev, which can be viewed through the command cat / proc / devices //DEV_CNT is 1. Currently, only one equipment number is applied 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"); //Step 2 //Associated character device structure cdev and file operations structure file_operations cdev_init(&led_chr_dev, &led_chr_dev_fops); //Step 3 //Add device to cdev_map hash table ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT); if(ret < 0) { printk("fail to add cdev\n"); goto add_err; } //Create device device_create(led_chrdev_class, NULL, led_devno, NULL, DEV_NAME); return 0; add_err: //When adding a device fails, you need to log off the device number unregister_chrdev_region(led_devno, DEV_CNT); alloc_err: return ret; } /* *Drive logoff function */ static void __exit led_chrdev_exit(void) { printk("chrdev exit\n"); device_destroy(led_chrdev_class, led_devno); //Clear device cdev_del(&led_chr_dev); //Clear device number unregister_chrdev_region(led_devno, DEV_CNT); //Unregister character device class_destroy(led_chrdev_class); //Clear class } module_init(led_chrdev_init); module_exit(led_chrdev_exit); MODULE_LICENSE("GPL");
4, RGB lamp driving based on device tree
Add node information to the device tree
RGB lamp related register
/* *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 */ /*Add led node*/ rgb_led{ #address-cells = <1>; #size-cells = <1>; compatible = "fire,rgb_led"; /*Red node*/ ranges; rgb_led_red@0x020C406C{ reg = <0x020C406C 0x00000004 0x020E006C 0x00000004 0x020E02F8 0x00000004 0x0209C000 0x00000004 0x0209C004 0x00000004>; status = "okay"; }; /*Green node*/ rgb_led_green@0x020C4074{ reg = <0x020C4074 0x00000004 0x020E01E0 0x00000004 0x020E046C 0x00000004 0x020A8000 0x00000004 0x020A8004 0x00000004>; status = "okay"; }; /*Blue light node*/ rgb_led_blue@0x020C4074{ reg = <0x020C4074 0x00000004 0x020E01DC 0x00000004 0x020E0468 0x00000004 0x020A8000 0x00000004 0x020A8004 0x00000004>; status = "okay"; }; };
reg property memory mapping
The of_iomap() function converts the physical address of the reg attribute value into a virtual address
void __iomem *of_iomap(struct device_node *np, int index)
Parameters:
- np: node represented by device_node
- index: generally, the reg attribute contains multiple segments. index is used to specify which segment to map. The label starts from 0.
Code example
Take the wild fire equipment driver model code as an example
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> /*------------------Character device content----------------------*/ #define DEV_NAME "rgb_led" #define DEV_CNT (1) /*Define the led resource structure and save the obtained node information and the converted virtual register address*/ struct led_resource { struct device_node *device_node; //Device tree node of 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; //Defines the device number of the character device static struct cdev led_chr_dev; //Define character device structure chr_dev struct class *class_led; //Save the created class struct device *device; // Save the created device struct device_node *rgb_led_device_node; //rgb_led device tree node structure /*Define the LEDs of the three lamps R, G and B_ The resource structure holds the obtained node information*/ struct led_resource led_red; struct led_resource led_green; struct led_resource led_blue; /*Character device operation function set, open function*/ static int led_chr_dev_open(struct inode *inode, struct file *filp) { printk("\n open form driver \n"); return 0; } /*Character device operation function set, write function*/ 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; //Temporarily store the read register data unsigned char receive_data[10]; //Used to save received data unsigned int write_data; //Used to save received 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; } /*Set GPIO1_04 output level*/ if (write_data & 0x04) { register_data = ioread32(led_red.virtual_DR); register_data &= ~(0x01 << 4); iowrite32(register_data, led_red.virtual_DR); // GPIO1_ Pin 04 outputs low level, and the red light is on } else { register_data = ioread32(led_red.virtual_DR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_DR); // GPIO1_ Pin 04 outputs high level, and the red light goes out } /*Set GPIO4_20 output level*/ if (write_data & 0x02) { register_data = ioread32(led_green.virtual_DR); register_data &= ~(0x01 << 20); iowrite32(register_data, led_green.virtual_DR); // GPIO4_20 pin output low level, green light on } else { register_data = ioread32(led_green.virtual_DR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_DR); // GPIO4_20 pin output high level, green light off } /*Set GPIO4_19 output level*/ if (write_data & 0x01) { register_data = ioread32(led_blue.virtual_DR); register_data &= ~(0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //GPIO4_ Pin 19 output low level, blue light on } else { register_data = ioread32(led_blue.virtual_DR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //GPIO4_ Pin 19 output high level, blue light off } return cnt; } /*Character device operation function set*/ static struct file_operations led_chr_dev_fops = { .owner = THIS_MODULE, .open = led_chr_dev_open, .write = led_chr_dev_write, }; /*----------------Platform driven function set-----------------*/ static int led_probe(struct platform_device *pdv) { int ret = -1; //Save error status code unsigned int register_data = 0; printk(KERN_ALERT "\t match successed \n"); /*Get rgb_led device tree node*/ 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; } /*Get RGB_ Red light child node of LED node*/ 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; } /*Get reg attribute and convert to virtual address*/ 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); /*Initialize red light*/ register_data = ioread32(led_red.virtual_CCM_CCGR); register_data |= (0x03 << 26); iowrite32(register_data, led_red.virtual_CCM_CCGR); //Turn on the clock 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); //Set multiplexing function 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); //Set PAD properties register_data = ioread32(led_red.virtual_GDIR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_GDIR); //Set GPIO1_04 is output mode register_data = ioread32(led_red.virtual_DR); register_data |= (0x01 << 4); iowrite32(register_data, led_red.virtual_DR); //Set GPIO1_04 default output high level /*Get RGB_ Green sub node of LED node*/ 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; } /*Get reg attribute and convert to virtual address*/ 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); /*Initialize green light*/ register_data = ioread32(led_green.virtual_CCM_CCGR); register_data |= (0x03 << 12); iowrite32(register_data, led_green.virtual_CCM_CCGR); //Turn on the clock 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); //Set multiplexing function 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); //Set PAD properties register_data = ioread32(led_green.virtual_GDIR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_GDIR); //Set GPIO4_IO20 is output mode register_data = ioread32(led_green.virtual_DR); register_data |= (0x01 << 20); iowrite32(register_data, led_green.virtual_DR); //Set GPIO4_IO20 default output high level /*Get RGB_ Blue light child node of LED node*/ 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; } /*Get reg attribute and convert to virtual address*/ 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); /*Initialize blue light*/ register_data = ioread32(led_blue.virtual_CCM_CCGR); register_data |= (0x03 << 12); iowrite32(register_data, led_blue.virtual_CCM_CCGR); //Turn on the clock 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); //Set multiplexing function 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); //Set PAD properties register_data = ioread32(led_blue.virtual_GDIR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_GDIR); //Set GPIO4_IO19 is output mode register_data = ioread32(led_blue.virtual_DR); register_data |= (0x01 << 19); iowrite32(register_data, led_blue.virtual_DR); //Set GPIO4_IO19 default output high level /*---------------------Register character device section-----------------*/ //First step //The equipment number is obtained by dynamic allocation, and the secondary equipment number is 0, //The device name is RGB LEDs, which can be viewed through the command cat / proc / devices //DEV_CNT is 1. Currently, only one equipment number is applied ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME); if (ret < 0) { printk("fail to alloc led_devno\n"); goto alloc_err; } //Step 2 //Associated character device structure cdev and file operation structure file_operations led_chr_dev.owner = THIS_MODULE; cdev_init(&led_chr_dev, &led_chr_dev_fops); //Step 3 //Add device to cdev_map hash table ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT); if (ret < 0) { printk("fail to add cdev\n"); goto add_err; } //Step 4 /*Create class */ class_led = class_create(THIS_MODULE, DEV_NAME); /*Create device*/ device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME); return 0; add_err: //When adding a device fails, you need to log off the device number 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 */}}; /*Define platform equipment structure*/ struct platform_driver led_platform_driver = { .probe = led_probe, .driver = { .name = "rgb-leds-platform", .owner = THIS_MODULE, .of_match_table = rgb_led, }}; /* *Driver initialization function */ 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; } /* *Drive logoff function */ static void __exit led_platform_driver_exit(void) { /*Unmap physical address to virtual address*/ 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); /*Delete device*/ device_destroy(class_led, led_devno); //Clear device class_destroy(class_led); //Clear class cdev_del(&led_chr_dev); //Clear device number unregister_chrdev_region(led_devno, DEV_CNT); //Unregister character device /*Unregister character device*/ 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"); /**/
summary
Third, get_dts_info.c only demonstrates the code for obtaining DTS attribute information.