tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断

  • 硬件平台:tiny4412
  • 系统:linux-4.4
  • 文件系统:busybox-1.25
  • 编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320
  • uboot:友善自带uboot.

一、DTS引入

1.什么是DTS?为什么要引入DTS

DTSDevice Tree Source设备树源码,DeviceTree是一种描述硬件的数据结构,它起源于OpenFirmware (OF)

Linux2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxxarch/arm/mach-xxx,比如板上的platform设备、resourcei2c_board_infospi_board_info以及各种硬件的platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。而采用DeviceTree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。   

2.ARM平台的相关code做出如下相关规范调整

•ARM的核心代码仍然保存在arch/arm目录下
•ARM SoC corearchitecture code保存在arch/arm目录下
•ARMSOC的周边外设模块的驱动保存在drivers目录下
•ARMSOC的特定代码在arch/arm/mach-xxx目录下
ARM SOCboard specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息

本质上,Device Tree改变了原来用hardcode方式将HW配置信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。

3.DTS的加载过程

如果要使用DeviceTree,首先用户要了解自己的硬件配置和系统运行参数,并把这些信息组织成DeviceTree source file。通过DTCDeviceTree Compiler),可以将这些适合人类阅读的DeviceTree source file变成适合机器处理的DeviceTree binary fileDTBdevicetree blob)。在系统启动的时候,bootprogram(例如:firmwarebootloader)可以将保存在flash中的DTB copy到内存(当然也可以通过其他方式,例如可以通过bootloader的交互式命令加载DTB),并把DTB的起始地址传递OSkernel对于计算机系统(computersystem),一般是firmware->bootloader->OS,对于嵌入式系统,一般是bootloader->OS

4.DTS的描述信息

Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在DeviceTree中,可描述的信息包括(原先这些信息大多被hardcode到kernel中):

•CPU的数量和类别

•内存基地址和大小

•总线和桥

•外设连接

•中断控制器和中断使用情况

•GPIO控制器和GPIO使用情况

•Clock控制器和Clock使用情况

  它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi。所有的ARMSoC.dtsi都引用了skeleton.dtsi,即#include"skeleton.dtsi或者 /include/ "skeleton.dtsi"

5.变化

platform之前:

tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第1张图片tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第2张图片


现在:

tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第3张图片tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第4张图片

二、设备树文件

1、修改设备树文件支持GPIO按键中断

(/work/linux-4.4.0/linux-4.4/arch/arm/boot/dts/exynos4412-tiny4412.dts)
    interrupt_demo: interrupt_demo {
            compatible         = "tiny4412,interrupt_demo";
            tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
            tiny4412,int_gpio2 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
            tiny4412,int_gpio3 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
            tiny4412,int_gpio4 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
    };

2、完整的设备树文件:

Device Tree有自己的独立的语法,它的源文件为.dts,编译后得到.dtb,Bootloader在引导Linux内核的时候会将.dtb地址告知内核。之后内核会展开Device Tree并创建和注册相关的设备,因此arch/arm/mach-xxx和arch/arm/plat-xxx中大量的用于注册platform、I2C、SPI板级信息的代码被删除,而驱动也以新的方式和.dts中定义的设备结点进行匹配。
/*
 * FriendlyARM's Exynos4412 based TINY4412 board device tree source
 *
 * Copyright (c) 2013 Alex Ling 
 *
 * Device tree source file for FriendlyARM's TINY4412 board which is based on
 * Samsung's Exynos4412 SoC.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

/dts-v1/;
#include "exynos4412.dtsi"
#include 

/ {  //root结点"/"
	model = "FriendlyARM TINY4412 board based on Exynos4412";
	//root结点"/"的属性compatible,组织形式为:,
	//Linux内核透过root结点"/"的compatible 属性即可判断它启动的是什么machine
	//compatible 属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,
	//形式为",",其后的字符串表征可兼容的其他设备。
	//可以说前面的是特指,后面的则涵盖更广的范围。
	compatible = "friendlyarm,tiny4412", "samsung,exynos4412", "samsung,exynos4";

  //以下开始为子节点
	//子结点的命名,它们遵循的组织形式为:[@],
	//<>中的内容是必选项,[]中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如memory;
	//多个相同类型设备结点的name可以一样,只要unit-address不同即可
	
	//chosen节点并不代表一个真正的设备,而是用来在Firmware与操作系统间传递数据,如启动参数。
	chosen {                         //子结点"chosen"
		stdout-path = &serial_0;
		bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 init=/linuxrc earlyprintk";
	};

	memory {                         //子结点"memory"
		reg = <0x40000000 0x40000000>;
	};

	leds {                           //子结点"leds"
		compatible = "gpio-leds";

		led1 {                         //子子结点"led1"
			label = "led1";
			gpios = <&gpm4 0 GPIO_ACTIVE_LOW>;
			default-state = "off";
			linux,default-trigger = "heartbeat";
		};

		led2 {
			label = "led2";
			gpios = <&gpm4 1 GPIO_ACTIVE_LOW>;
			default-state = "off";
		};

		led3 {
			label = "led3";
			gpios = <&gpm4 2 GPIO_ACTIVE_LOW>;
			default-state = "off";
		};

		led4 {
			label = "led4";
			gpios = <&gpm4 3 GPIO_ACTIVE_LOW>;
			default-state = "off";
			linux,default-trigger = "mmc0";
		};
	};

	fixed-rate-clocks {
		xxti {
			compatible = "samsung,clock-xxti";
			clock-frequency = <0>;
		};

		xusbxti {
			compatible = "samsung,clock-xusbxti";
			clock-frequency = <24000000>;
		};
	};
	interrupt_demo: interrupt_demo {
     compatible         = "tiny4412,interrupt_demo";
     tiny4412,int_gpio1 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
     tiny4412,int_gpio2 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
     tiny4412,int_gpio3 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
     tiny4412,int_gpio4 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
    };
};

&rtc {
	status = "okay";
};

&sdhci_2 {
	bus-width = <4>;
	pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
	pinctrl-names = "default";
	#status = "okay";	
	status = "disabled";
};

&serial_0 {
	status = "okay";
};

&serial_1 {
	status = "okay";
};

&serial_2 {
	status = "okay";
};

&serial_3 {
	status = "okay";
};


三、设备树驱动

设备树引来的驱动变化
#include 
#include 
#include 
#include 
#include 
#include 
#include 

typedef struct 
{
    int gpio;
    int irq;
    char name[20];
}int_demo_data_t;

static irqreturn_t int_demo_isr(int irq, void *dev_id)
{
    int_demo_data_t *data = dev_id;

    printk("%s enter, %s: gpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq);

    return IRQ_HANDLED;
}

static int int_demo_probe(struct platform_device *pdev) {
    struct device *dev = &pdev->dev;
    int irq_gpio = -1;
    int irq = -1;
    int ret = 0;
    int i = 0;
    int_demo_data_t *data = NULL;

    printk("%s enter.\n", __func__);

    if (!dev->of_node) {
        dev_err(dev, "no platform data.\n");
        goto err1;
    }

    data = devm_kmalloc(dev, sizeof(*data)*4, GFP_KERNEL);
    if (!data) {
        dev_err(dev, "no memory.\n");
        goto err0;
    }

#if 1
    for (i = 3; i >= 0; i--) {
        sprintf(data[i].name, "tiny4412,int_gpio%d", i+1);
#else
    for (i = 0; i < 4; i++) {
#endif
        irq_gpio = of_get_named_gpio(dev->of_node, data[i].name, 0);//通过名字获取gpio
        if (irq_gpio < 0) {
            dev_err(dev, "Looking up %s property in node %s failed %d\n",
                data[i].name, dev->of_node->full_name, irq_gpio);
            goto err1;
        }

        data[i].gpio = irq_gpio;

        irq = gpio_to_irq(irq_gpio);    //将gpio转换成对应的中断号
        if (irq < 0) {
            dev_err(dev,
                "Unable to get irq number for GPIO %d, error %d\n",
                irq_gpio, irq);
            goto err1;
        }

        data[i].irq = irq;

        printk("%s: gpio: %d ---> irq (%d)\n", __func__, irq_gpio, irq);
    //注册中断
        ret = devm_request_any_context_irq(dev, irq, int_demo_isr, IRQF_TRIGGER_FALLING, data[i].name, data+i);
        if (ret < 0) {
            dev_err(dev, "Unable to claim irq %d; error %d\n",
                irq, ret);
            goto err1;
        }
    }

    return 0;

err1:
    devm_kfree(dev, data);
err0:
    return -EINVAL;
}

static int int_demo_remove(struct platform_device *pdev) {

    printk("%s enter.\n", __func__);

    return 0;
}

static const struct of_device_id int_demo_dt_ids[] = {
    { .compatible = "tiny4412,interrupt_demo", },
    {},
};

MODULE_DEVICE_TABLE(of, int_demo_dt_ids);

static struct platform_driver int_demo_driver = {
    .driver        = {
        .name      = "interrupt_demo",
        .of_match_table    = of_match_ptr(int_demo_dt_ids),
    },
    .probe         = int_demo_probe,
    .remove        = int_demo_remove,
};

static int __init int_demo_init(void)
{
    int ret;

    ret = platform_driver_register(&int_demo_driver);
    if (ret)
        printk(KERN_ERR "int demo: probe failed: %d\n", ret);

    return ret;
}
module_init(int_demo_init);

static void __exit int_demo_exit(void)
{
    platform_driver_unregister(&int_demo_driver);
}
module_exit(int_demo_exit);

MODULE_LICENSE("GPL");

编译驱动

KERN_DIR = /work/system/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= mykey.o

采用了platform平台设备驱动的方式

platform_driver_register(&int_demo_driver);-->

.of_match_table    = of_match_ptr(int_demo_dt_ids),-->

.probe         = int_demo_probe,-->

of_get_named_gpio(dev->of_node, data[i].name, 0):dev->of_node节点上的data[i].name的值取下。-->

irq = gpio_to_irq(irq_gpio);    //将gpio转换成对应的中断号-->

ret = devm_request_any_context_irq(dev, irq, int_demo_isr, IRQF_TRIGGER_FALLING, data[i].name, data+i);

//注册中断-->

中断发生-->

执行中断处理函数int_demo_isr-->

等待中断发生。

四、下载测试

#u-boot
setenv bootargs  'root=/dev/nfs  rw  nfsroot=192.168.1.123:/work/nfs/rootfs_for_tiny4412/rootfs ethmac=1C:6F:65:34:51:7E  ip=192.168.1.125:192.168.1.123:192.168.1.1:255.255.255.0::eth0:off console=ttySAC0,115200  init=/linuxrc'
#u-bootsave
#u-bootdnw 0x40600000
dnw arch/arm/boot/uImage
#u-bootdnw 0x42000000
dnw  arch/arm/boot/dts/exynos4412-tiny4412.dtb
bootm 0x40600000 - 0x42000000

tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第5张图片tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断_第6张图片

内核:
git clone https://github.com/fengyuwuzu0519/linux4_forTiny4412.git
文件系统:
git clone https://github.com/fengyuwuzu0519/rootfs_forTiny4412
文件系统git下了少东西,则创建如下:
(mkdir dev  proc  sys  tmp  var    mknod  dev/console  c  5  1)
uboot:
git clone https://github.com/fengyuwuzu0519/u-boot_forTiny4412
make distclean
make tiny4412_config
make

你可能感兴趣的:(tiny4412学习(四)之移植linux-设备树(1)设备树基础知识及GPIO中断)