Preface
Device tree is a data structure used to describe hardware, similar to a plate-level description language, originating from OpenFirmware (of). In the current widely used Linux kernel 2.6.x version, for different platforms, different hardware, there is often a large number of different, poor portability of the board level Description code to achieve these different platforms and different hardware special adaptation needs. But the plethora of platforms, the different hardware that led to the growing number of such code, eventually led to Linux founder Linus dissatisfaction, and a strong call for change. The introduction of Device tree brings great convenience to the driver adaptation, a complete set of Device tree can put a PCB in front of you. Device tree can describe the CPU and can describe any existing drive units such as clocks, interrupt controllers, IO controllers, SPI Bus controllers, I2C controllers, storage devices, and so on. The specific device can describe to use which interrupt, memory mapping space is how much and so on.
For the data structure and detailed use of device tree, please check out a blog from teacher Song Baohua:
HTTP://BLOG.CSDN.NET/AIRK000/ARTICLE/DETAILS/2 1 Drive development based on device tree mechanism kernel-case study
In this chapter, the author talks about the driver development case of the kernel using the device tree mechanism based on linux-3.2.x. The driver development case for this article is the keyboard driver code written by the author during his work. The CPU platform uses the I.mx6ul of NXP (Freescale). The summary information is described below:
Hardware platform: NXP (Freescale)-i.mx6ul
Software Development Platform: Ubuntu-12.04
Kernel version: Linux-3.14.38
Compilation environment: Yocto Project 1.1 Drive development based on the device tree mechanism-systems such as Hegazai and parsing DTB files
Based on the drive development of device tree mechanism, the hardware resources used in the drive are configured on the DTS file of the corresponding CPU platform, and then the DtB file is compiled and placed after the U-boot partition, before the kernel partition. Here, by the way, how the kernel resolves dtb files. The approximate process is as follows:
After the system is started, the u-boot loads DTB, passes the DtB file to the kernel through the u-boot and the Linux kernel, then the kernel resolves the dtb file, according to the configuration in device tree (DTB file) to initialize the CPU pins of the device, the state of each peripheral. Device tree in the configuration is mainly played the role of initializing hardware resources, later can be in the drive to modify the device's hardware resources, such as in the device in the initialization of a gpio pin for the pull state, you can modify the state of the pin after the driver load. 1.2 Drive development based on device tree mechanism configuration and compilation of-dts files
This section starts with a specific driver example to explain how to configure DTS files in drive development. Here use I. Several gpio ports used in matrix keyboard drive under Mx6ul platform explain how to configure DTS files and compile. This tutorial case is used to compile the drive kernel is Linux-3.14.38. First, let's look at how to find the DTS files in the kernel for their respective CPU platforms:
1.dts files are located in the kernel of arch/arm/boot/dts/$ (board). DTS, where $ (board) refers to the corresponding CPU platform, such as I. The DTS files for the Mx6ul platform are as follows:
imx6ul/linux-3.14.38-v2$ vim Arch/arm/boot/dts/imx6ul-14x14-evk.dts (partial content)
#include <dt-bindings/input/input.h> #include "Imx6ul.dtsi"/{model = "Freescale i.mx6 UltraLite NewLand Board";
compatible = "Fsl,imx6ul-14x14-evk", "Fsl,imx6ul";
chosen {Stdout-path = &uart1;
};
Memory {reg = <0x80000000 0x20000000>;
};
PXP_V4L2 {compatible = "fsl,imx6ul-pxp-v4l2", "Fsl,imx6sx-pxp-v4l2", "FSL,IMX6SL-PXP-V4L2";
Status = "Okay";
};
Keyboard {compatible = "Max-keypad";
Pinctrl-names = "Default";
pinctrl-0 = <&pinctrl_keypad>; In-gpios = <&gpio2 3 gpio_active_high>,//key_in0 <&gpio2 4 gpio_active_hig H>,//key_in1 <&gpio2 5 gpio_active_high>; Key_in2 Out-gpios = <&gpio2 6 gpio_active_high>,//key_out0 <&A Mp;gpio2 2 gpio_active_high>,//key_out1 <&gpio2 7 GPIO_ACTIVE_HIGH≫,//key_out2 <&gpio4 gpio_active_high>,//key_out3 <&gpio4 gpio_active_high>;
KEY_OUT4 status = "Okay";
};
};
&cpu0 {arm-supply = <®_arm>;
Soc-supply = <®_soc>;
};
&clks {assigned-clocks = <&clks imx6ul_clk_pll4_audio_div>;
Assigned-clock-rates = <786432000>;
};
&TSC {pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc>;
Status = "Okay";
Xnur-gpio = <&gpio1 3 0>;
Measure_delay_time = <0xffff>;
Pre_charge_time = <0xfff>;
};
&GPMI {pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpmi_nand_1>;
Status = "Okay";
NAND-ON-FLASH-BBT;
};
&lcdif {pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat &pinctrl_lcdif_ctrl>;
Lcd_reset = <&gpio3 gpio_active_high>;
display = <&display0>; Status= "Okay";
Display0:display {bits-per-pixel = <16>;
Bus-width = <8>;
display-timings {Native-mode = <&timing0>;
TIMING0:TIMING0 {clock-frequency = <9200000>;
Hactive = <240>;
Vactive = <320>;
Hfront-porch = <8>;
Hback-porch = <4>;
Hsync-len = <41>;
Vback-porch = <2>;
Vfront-porch = <4>;
Vsync-len = <10>;
Hsync-active = <0>;
Vsync-active = <0>;
De-active = <1>;
Pixelclk-active = <0>;
};
};
};
};
&IOMUXC {pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>; Imx6ul-evk {pinctrl_uart1:uart1grp {fsl,pins = < Mx6ul_pad_uart1_tx_data__uart1_dce_tx 0x1b0b1 MX6UL_PA
D_uart1_rx_data__uart1_dce_rx 0x1b0b1 >;
}; pinctrl_tsc:tscgrp {fsl,pins = < mx6ul_pad_gpio1_io01__gpio1_io01 0xb0 mx6ul_pad_gpio1_io02__gpio1_io02 0x B0 mx6ul_pad_gpio1_io03__gpio1_io03 0xb0 Mx6ul_pad_gpio1_io04__gpio1_io04 0xb0 >;
}; pinctrl_lcdif_dat:lcdifdatgrp {fsl,pins = < Mx6ul_pad_lcd_data00__lcdif_data00 0x79 MX6UL_PAD_LCD_DATA01_ _lcdif_data01 0x79 mx6ul_pad_lcd_data02__lcdif_data02 0x79 mx6ul_pad_lcd_data03__lcdif_data03 0x79
D_lcd_data04__lcdif_data04 0x79 mx6ul_pad_lcd_data05__lcdif_data05 0x79 mx6ul_pad_lcd_data06__lcdif_data06 0x79
Mx6ul_pad_lcd_data07__lcdif_data07 0x79 >;
}; pinctrl_lcdif_ctrl:lcdifctrlgrp {fsl,pins = < Mx6ul_pad_lcd_clk__lcdif_wr_rwn 0x79 MX6UL_PAD_LCD_ENABL E__lcdif_rd_e 0x79 mx6ul_pad_lcd_hsync__lcdif_rs 0x79 mx6ul_pad_lcd_reset__lcdif_cs 0x79/* Use
D for LCD reset */mx6ul_pad_lcd_data09__gpio3_io14 0x79 >;
};
pinctrl_keypad:keypadgrp {fsl,pins = < mx6ul_pad_enet1_rx_en__gpio2_io02 0x70a0
Mx6ul_pad_enet1_tx_data0__gpio2_io03 0x70a0 mx6ul_pad_enet1_tx_data1__gpio2_io04 0x70a0 mx6ul_pad_enet1_tx_en__gpio2_io05 0x70a0
Mx6ul_pad_enet1_tx_clk__gpio2_io06 0x70a0 mx6ul_pad_enet1_rx_er__gpio2_io07 0x70a0 Mx6ul_pad_csi_data04__gpio4_io25 0x70a0 mx6ul_pad_csi_data05__gpio4_io26 0x70
A0 >;
}; pinctrl_gpmi_nand_1:gpmi-nand-1 {fsl,pins = < mx6ul_pad_nand_cle__rawnand_cle 0xb0b1 MX6UL_PAD_NAN D_ale__rawnand_ale 0xb0b1 mx6ul_pad_nand_wp_b__rawnand_wp_b 0xb0b1 mx6ul_pad_nand_ready_b__rawnand_r Eady_b 0xb000 mx6ul_pad_nand_ce0_b__rawnand_ce0_b 0xb0b1 mx6ul_pad_nand_ce1_b__rawnand_ce1_b MX 6ul_pad_nand_re_b__rawnand_re_b 0xb0b1 mx6ul_pad_nand_we_b__rawnand_we_b 0xb0b1 MX6UL_PAD_NAND_DATA00_ _rawnand_data00 0xb0b1 mx6ul_pad_nand_data01__rawnand_data01 0xb0b1 mx6ul_pad_nand_data02__rawnand_dATA02 0xb0b1 mx6ul_pad_nand_data03__rawnand_data03 0xb0b1 mx6ul_pad_nand_data04__rawnand_data04 0xb0b1 M X6ul_pad_nand_data05__rawnand_data05 0xb0b1 mx6ul_pad_nand_data06__rawnand_data06 0xb0b1 MX6UL_PAD_NAND_DATA07
__rawnand_data07 0xb0b1 >;
};
}; };
2. According to their development needs to configure DTS files, this article matrix keyboard drivers used in the GPIO pin resources are: gpio2-2, gpio2-3, Gpio2-4, gpio2-5, gpio2-6, gpio2-7, gpio4-25, gpio4-26. The DTS files are configured as follows:
~/yangfile/imx6ul/linux-3.14.38-v2$ Vim Arch/arm/boot/dts/imx6ul-newland.dts
2.1 In the DTS file to add a device node, such as we are matrix keyboard driver, then add a device named "Keyboard" node;
The 2.2 compatible property is used for the of_find_node_compatible function to acquire the device node, which traverses device tree to find the matching device node through the "Max-keypad" string;
2.3 pinctrl-0 = <&pinctrl_keypad> is mainly used to explain where the hardware resources of the equipment are obtained, for example, this is to get IO resources inside the IOMUXC
The 2.4 IOMUXC device node defines all IO resources for the CPU, including the initialization state of each IO port, such as: Mx6ul_pad_enet1_rx_en_gpio2_io02 0x70a0, Mx6ul_pad_enet1_ here The RX_EN_GPIO2_IO02 macro represents the register group of the IO port (IO multiplexing Register, IO Direction control register, IO input output Value setting register), 0x70a0 this value according to its own drive development requirements, lookup CPU manual definition, not unique.
keyboard {compatible = "Max-keypad"; Pinctrl-names = "Default";//This setting defaults to default is OK, there is no special request pinctrl-0 = <&pinctrl_keypad>;// Inside the IOMUXC. Gets the initialization state of the corresponding IO resource In-gpios = <&gpio2 3 gpio_active_high>,//"In-gpios" string can be defined by itself, mainly for the purpose of obtaining GPIO resources The <&gpio2 4 Gpio_active_high>,//gpio_active_high: Logical High Level effective <&gpio2 5 gpio_active_high>; Key_in2 Out-gpios = <&gpio2 6 gpio_active_high>,//"Out<span style=" font-family:arial, Helvetica, Sans-serif; " >-gpios "strings can be defined by themselves, mainly to obtain GPIO resources when matching the </span> <&gpio2 2 gpio_active_high>//key_o UT1 <&gpio2 7 Gpio_active_high>,//key_out2 <&gpio4 H>,//key_out3 <&gpio4 gpio_active_high>;
KEY_OUT4 status = "Okay";//enable GPIO resources to be used}; };
&IOMUXC {
pinctrl-names = "Default";
pinctrl-0 = <&pinctrl_uart1>; ...
pinctrl_keypad:keypadgrp {
Fsl,pins = <
mx6ul_pad_enet1_rx_en__gpio2_io02 0x70a0
mx6ul_ pad_enet1_tx_data0__gpio2_io03 0x70a0
mx6ul_pad_enet1_tx_data1__gpio2_io04 0x70a0
MX6UL_PAD_ enet1_tx_en__gpio2_io05 0x70a0
mx6ul_pad_enet1_tx_clk__gpio2_io06 0x70a0
mx6ul_pad_enet1_ Rx_er__gpio2_io07 0x70a0
mx6ul_pad_csi_data04__gpio4_io25 0x70a0
mx6ul_pad_csi_data05__ Gpio4_io26 0x70a0
>;
};
3. Compile the DTS file, and execute the following command in the kernel root directory:
~/yangfile/imx6ul/linux-3.14.38-v2$ make Arch=arm CROSS_COMPILE=ARM-LINUX-GCC IMX6UL-NEWLAND.DTB
(The ARM-LINUX-GCC here is just a logo representing the cross compiler, depending on the actual situation)
4. Burn the configuration, compiled DTB files to the DTB partition of the device Flash (or SD card).
2 How to register a device in a DTS file in the driver code after the drive development of the device tree mechanism is contacted, the device tree mechanism is the optimized version of the platform bus mechanism in linux-2.6.x. OK, let's talk about the process of registering the device based on the device tree mechanism, here I write the matrix keyboard driver code device registration process: 1. Call the of_get_** or of_find_** function in the probe function to obtain the device resources from the DTB:
static int max_keypad_probe (struct platform_device *pdev) {int i,ret;
struct device *dev; struct Device_node *dev_node = NULL;
Add by Zengxiany dev = &pdev->dev;
。。。。。。
Omit part of code Dev_node = Of_find_compatible_node (Null,null, "Fsl,imx6ul-gpio");
if (!of_device_is_compatible (Dev_node, "Fsl,imx6ul-gpio")) {PRINTK ("Get Keypad device node error!\n");
Return-einval;
} Dev_node = Of_find_compatible_node (Dev_node,null, "Max-keypad");
if (!of_device_is_compatible (Dev_node, "Max-keypad")) {PRINTK ("failure to find Max-keypad device");
Return-einval;
for (i=0; i< keypad_rows; i++) {Gpio_map_rowkey[i] = Of_get_named_gpio (Dev_node, "In-gpios", I);
Set_key_input (Gpio_map_rowkey[i]);
for (i=0; i< keypad_cols; i++) {Gpio_map_colkey0[i] = Of_get_named_gpio (Dev_node, "Out-gpios", I);
Set_key_input (Gpio_map_colkey0[i]); }
}
2. Register the device in the INIT function:
Add by Zengxiany for platform device register
static struct of_device_id max_keypad_of_match[] = {
{. Compatible = "Max-keypad",},
{},
};
static struct Platform_driver Max_keypad_device_driver = {
. Probe = Max_keypad_probe,
. Remove = Max_ Keypad_remove,
. Driver = {
. Name = "Max-keypad",
. Owner = this_module,.
of_ match_table = Of_match_ptr (Max_keypad_of_match),
}
};
static int __init keypad_module_init (void)
{
int ret;
ret = Platform_driver_register (&max_keypad_device_driver);//modify by Zengxiany
if (Ret < 0)
{
PRINTK ("Max_keypad_device driver init error!\n");
Return-enodev;
}
return 0;
}
static void __exit keypad_module_exit (void)
{
platform_driver_unregister (&max_keypad_device_driver) ;
}
OK, this completes the registration of the device.