linux设备树Device Tree

设备树DT是易于阅读的硬件描述文件,它采用类json格式,在这种格式中,设备表示为带有属性的节点。

一、 设备树机制

将选项CONFIG_OF设置为Y即可在内核中支持DT。在驱动程序中使用DT api,必须添加以下头文件:<linux/of.h> <linux/of_device.h>

DT支持以下几种数据类型:

/*注释*/
node_label: nodename@reg {
string-property = "a1";
string-list = "a1", "b1";
one-int-property = <180>;
int-list-property = <0xb3a4 125 0xaa42>; /*每个数字是32位数*/
mixed-list-property = "a str", <0xa3146b36>, <23>, [0x03 0x31 0x17];
byte-array-property = [0x02 0x31 0x23 0xa3];
boolean-property
};

1. 命名约定

每个节点都必须有<name>[@<address>]形式,其中<name>是一个最长31位的字符串,而[@<address>]是可选的,具体取决于节点是否是一个可寻址设备。设备名字示例如下:

i2c@021a0000 {
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg=<021a0000>;
[...]
};

2. 别名、标签和phandle

标签是标记节点的方法,可以用唯一的名称来标识节点,之后就可以用标签来引用节点,在下面的例子中gpio_lable1和gpio_label2都是标签。指针句柄(pointer handle,简写为phandle)是与节点相关联的32位值,用于唯一标识节点,以便可以用另外一个节点引用该节点。

为了不在查找节点时,遍历整颗树,引入了别名的概念,下面例子中的gpio0和gpio1都是别名alias。可以使用函数find_node_by_alias()来查找指定别名的节点。别名是给linux内核使用,而不是给DT使用的。

aliaes {
gpio0 = &gpio_label1;
gpio1 = &gpio_label2;
};

gpio_label1: gpio@0209c000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
[...]
};

gpio_label2: gpio@0209c004 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
[...]
};

3. DT编译

DT有两种形式:文本形式(.dts后缀)和二进制形式(.dtb后缀)。

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx6dl-sabrelite.dtb

二、表示和设备寻址

每个设备在DT中至少有一个节点。某些属性对许多设备是通用的,特别是内核常用总线(SPI、I2C、平台、MDIO等)上的设备。这些属性是reg、#address-cells、#size-cells,其中reg是最主要的寻住属性。

reg格式是<address0size0 [address1size1] ...>形式的元组,其中每个元组代表设备使用的地址范围。一般可寻址设备集成他们父节点的#size-cells和#address-cells属性。

1. SPI和I2c设备寻址示例

&i2c3 {
status="okay";
temperature-sensor@59 {
compatible="national,lm73";
reg=<0x59>
};
pcf8523: rtc@68 {
compatible="nxp,pcf8523";
reg=<0x68>
};
};

@ecspi {
cs-gpios=<&gpio5 17 0>,<&gpio5 17 0>,<&gpio5 17 0>;
sttus="okay";
ad7606r8_0: ad7606r8@1 {
compatible="ad7606-r8";
reg=<1>;
[...];
};
};

2. 平台设备寻址

平台设备是指其内存可有cpu访问,占据总线地址空间的设备。reg属性仍然定义设备的地址,这里是内存区域列表。示例如下:

soc {
#address-cells=<1>;
#size-cells=<1>;
apis-bus@02000000 {
compatible="fsl,aips-bus","simple-bus";
#address-cells=<1>;
#size-cells=<1>;
reg=<0x02000000 0x100000>;
spaba-bus@02000000 {
compatible="fsl,spba-bus","simple-bus";
#address-cells=<1>;
#size-cells=<1>;
reg=<0x02000000 0x100000>;
ecspi1: ecspi@02008000{
compatible="fsl,imx6q-ecspi","fsl,imx51-ecspi";
#address-cells=<1>;
#size-cells=<0>;
reg=<0x02000000 0x4000>;
[...]
};

i2c1: i2c@021a0000 {
compatible="fsl,imx6q-i2c","fsl,imx21-i2c";
#address-cells=<1>;
#size-cells=<0>;
reg=<0x021a0000 0x4000>;
[...]
};

};
};
};

3. 处理资源

这里主要处理的资源有:存储区、中断线、DMA、时钟等

命名资源,为了避免资源列表的顺序不匹配的情况,引入了命名资源(clock, irq, dma, reg)的概念,它由定义资源列表和命名组成。命名资源的相应属性有clock-names, interrupt-names, reg-names, dma-names。

命名资源示例:

fake_device {
compatible = "packt,fake-device";
reg=<0x4a064000 0x800> <0x4a064800 0x200> <0x4a064c00 0x200>;
reg-names="config", "ohci", "ehci";
interrupts = <0 66 TYPE_IRQ_LEVEL_HIGH>, <0 67 TYPE_IRQ_LEVEL_HIGH>;
interrupt-names = "ohci", "ehci";
clocks = <&clks, IMX6QDL_CLK_UARG_IPG>, <&clks, IMX6QDL_CLK_UARG_SERIAL>;
clock-names = "ipg", "per";
dmas = <&sdma, 25 4 0>, <&sdma 26 4 0>;
dma-names = "rx", "tx";
};

/*获取资源的函数*/
struct resource *res1, *res2;
res1 = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci");
res2 = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci");

/*获取dma通道*/
struct dma_chan *dma_chan_rx, *dma_chan_tx;
dma_chan_rx = dma_request_slave_channel(&pdev->dev, "rx");
dma_chan_tx = dma_request_slave_channel(&pdev->dev, "tx");
/*获取中断*/
int txirq, rxirq;
txirq = platform_get_irq_by_name(pdev, "ohci");
rxirq = platform_get_irq_by_name(pdev, "ehci");
/*获取clock*/
struct clk *clk_per, *clk_ipg;
clk_per = devm_clk_get(&pdev->dev, "per");
clk_ipg = devm_clk_get(&pdev->dev, "ipg");

寄存器作为资源时的访问方式:

struct resource *res;
void __iomem *base;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);  // 这是ioremap的resource和devm版本;
if (IS_ERR(base)) { return PTR_ERR(base); }

中断在DT中分为两部分:消费者端和控制器端。

中断控制器端需要有以下属性:interrupt-controller是布尔属性,#interrupt-cells表示该中断有多少个cell。

中断消费者端需要以下属性:interrupt-parent的值是其所连接的中断控制器节点的phandle,如未指定则从其父节点集成而来;interrupts是中断说明符。

设备节点中的中断描述符说明interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH> 其中:0代表该中断是共享外设中断还是专有中断。第二个单元代表中断号。

4. 提取特定的应用数据:

/*注释*/
node_label: nodename@reg {
string-property = "a1";
string-list = "a1", "b1";
one-int-property = <180>;
int-list-property = <0xb3a4 125 0xaa42 0xab21>; /*每个数字是32位数*/
mixed-list-property = "a str", <0xa3146b36>, <23>, [0x03 0x31 0x17];
byte-array-property = [0x02 0x31 0x23 0xa3];
boolean-property
};

// 提取字符串
const char *my_string = null;
of_property_read_string(pdev->dev.of_node, "string-property", &my_string);

//提取整数
unsigned int number;
of_property_read_u32(pdev->dev.of_node, "one-int-property", &number);

unsigned int cells_array[4];
of_property_read_u32_array(pdev->dev.of_node, "int-list-property", cells_array, 4);

bool my_bool = of_property_read_bool(pdev->dev.of_node, "boolean-property");

提取子节点

eeprom: ee241c512@55 {
compatible="microchip,24xx512";
partition1 {
compatible = "private";
offset=<0>;
size = <1024>;
};
partition2 {
compatible="data";
offset=<1024>;
size=<1024>;
};
};

struct device_node *np = pdev->dev.of_node;
struct device_node *sub_np;

for_each_child_of_np(np, sub_np) {
    int size, offset;
    of_property_read_u32(sub_np, "offset", &offset);
    of_property_read_u32(sub_np, "size", &size);
}

三、平台驱动程序和DT

平台驱动程序可以使用开发板文件匹配设备,也可以通过DT方式匹配设备。

1. OF匹配风格

使用设备树的节点的compatible属性来匹配驱动程序中of_match_table中的设备想,其中of_match_table是struct driver{}的一个成员变量

/*结构体of_device_id{}结构体*/
/* struct of_device_id {
[...];
char compatible[128];
const void *data;
};*/

static const struct of_device_id imx_uart_ids[] = {
{compatible="fsl,imx6q-uart",},
{compatible="fsl,imx1-uart",},
{compatible="fsl,imx21-uart",},
{/*sentinel*/}
};
/*通知内核该内核模块注册了这些设备*/
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);

/*在驱动程序的driver.of_match_table*/
struct platform_driver serial_imx_driver {
[...];
.driver = { .name="imx-uart";
.of_match_table=imx_uart_dt_ids,
};
};

2. 混合分配,同时支持DT和platform_device_id

static const struct platform_device_id sdma_devtypes[] = {
{.name="imx51-sdma",.driver_data=(unsigned long)&sdma_imx51},
{.name="imx53-sdma",.driver_data=(unsigned long)&sdma_imx53},
{.name="imx6q-sdma",.driver_data=(unsigned long)&sdma_imx6q},
{/*sentinel*/}
};
MODULE_DEVICE_TABLE(platform, sdma_devtypes);

static const of_device_id sdma_dt_ids[] = {
{.compatible="fsl,imx51-sdma",.data=&sdma_imx51},
{.compatible="fsl,imx53-sdma",.data=&sdma_imx53},
{.compatible="fsl,imx6q-sdma",.data=&sdma_imx6q},
{/*sentinel*/}
};
MODULE_DEVICE_TABLE(of, sdma_dt_ids);

static struct platform_driver sdma_driver = {
    .id_table =sdma_devtypes,
    .probe = sdma_probe,
    .driver = {
        .name = "imx-sdma", 
        .of_match_table = of_match_ptr(sdma_dt_ids),
    },
};

static int sdma_probe(struct platform *pdev) {
const struct of_device_id *of_id = of_match_device(of_match_ptr(sdma_dt_ids), &pdev->dev);
struct device_node *np=pdev->dev.of_node;

if (of_id) {
    drv_data = of_id->data;
} else if(pdev->id_entry) { /*使用内核开发板文件进行匹配的设备*/
    drv_data = (void*)pdev->id_entry->driver_data;
}
    return 0;
}


module_platform_driver(sdma_driver);

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值