Detailed explanation of Linux character device driver VI (RGB lamp driver realized by device tree)

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 = <&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 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.

Keywords: Linux

Added by JohnMC on Tue, 30 Nov 2021 19:55:11 +0200