您的位置:首页 > 运维架构 > Linux

ARM Linux设备树

2016-01-21 17:34 281 查看
1.1 ARM设备树起源

在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,很多的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备,resource,i2c_board_info,spi_board_info以及各种硬件的platform_data。

1.2什么是设备树?

设备树保留着存在于系统中的设备信息。当机器引导时,OS通过使用驱动程序和其他组件获得的信息建立此树,并且当添加或删除设备时更新此树。设备树是分级的,总线上的设备代表着总线适配器或驱动控制器的“子集”。设备树的每一个节点是一个设备节点(devnode),一个devnode包括设备驱动程序的设备对象加上有OS所保留的内部信息。

设备树是一种描述硬件的数据结构,采用设备树后,许多硬件的细节可以直接被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,采用设备树后,许多硬件的细节可以直接通过它传递给Linux,而不在需要大量的冗余代码。整个设备树牵扯比较广,既增加了新的用于描述设备硬件信息的文本格式,又增加了编译这个文本的工具,同时Bootloader也需要支持将编译后设备树传递给Linux内核。

设备树由一系列被命名的节点和属性组成,而节点本身可包含子节点。所谓属性,其实就是成对出现的名称和值。在设备树中,可描述的信息包括(原先这些信息大多被硬件编码在内核中): CPU的数量和类别 内存基地址 总线和桥 外设链接 中段控制器和中断使用情况
GPIO控制器和GPIO使用情况 时钟控制器和时钟使用情况

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

1.3 中断连接

设备树还可以包含中断连接信息,对于中断控制器而言:

interrupt-controller-这个属性为空,中断控制器应该加上此属性表明自己的身份:

#Interrpt-cells-比#address-cells和#size-cells相似,它表明连接此中断控制器的设备的中断属性的cell大小。

在整个设备树中,与中断相关的属性还包括:

interrupt-parent-设备节点通过它来指定它所依附的中断控制器的phandle,当节点没有指定interrupt-parent时,则从父级节点继承。根节点指定了interrupt-parent=<&inc>;

其对应于intc,interrupt-controller@10140000,而根节点的子节点并未指定interrupt-parent,因此它们都继承了intc.即位于0x10140000的中断控制器。

interrupts-用到了中断的设备节点,通过它的指定中断号,触发方式等,这个属性具体含有多少个cell,由它依附的中断控制器节点的#interrupt-cells属性决定。而每个cell具体又是什么含义,一般由驱动的实现决定,而且也会在设备树的绑定文档中说明。譬如,对于ARM GIC中断控制器节点的#interrupt-cells属性决定。而每个cell具体又是什么含义,一般由驱动的实现决定,而也会在设备树的绑定文档中说明。另外,值得注意的是,一个设备还可以还可能用到多个中断号。对于ARM GIC而言,若某设备使用了SPI的168号,169号俩个中断,而且都是高电平触发,则该设备节点的中断属性可定义为interrupts
= <0 168 4 >,<0 169 4 >;。

对于平台设备而言,简单的通过如下API就可以指定想取哪一个中断,其中的参数num就是中断的index。

int platform_get_irq(struct platform_device *dev, unsigned int num);

当然在.dts文件中I可以对中断进行命名,而后在驱动中通过platform_get_irq_byname()来获取对应的中断号。

fsl_dema->txirq = platform_get_irq_byname(pdev,"dema-tx");

fsl_dema->errirq= platform_get_irq_byname(pdev,"edma-err");

与.dts中 interrupt-names = “edma-tx” ,"edma-err"是一致的。

1.4 GPIO,时钟,pinmux连接

除了中断以外,在ARM Linux中时钟,GPIO, pinmux 都可以通过.dts中的节点和属性进行描述。

譬如,对于GPIO控制器而言,其对应的设备节点需声明gpio-controller属性,并设置#gpio-cells的大小。譬如,对于兼容性为fsl,imx28-pinctrl的pinctrl驱动而言,其GPIO控制器的设备节点类似于:

pinctrl@80018000{

compatible = "fsl,imx28-pinctrl","simple-bus";

reg = <0x80018000 2000>;

gpio:gpio@0{

compatible = "fsl,imx28-gpio";

interrupts = <127>;

gpio-controller;

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells =<2>;

};

gpio1:gpio@1{

compatible ="fsl,imx28-gpio";

interrupts =<126>;

gpio-controller;

#gpio-cells=<2>;

interrupt-controller;

#interrupt-cells = <2>;

};

......

};

其中,#gpioi- cells为2,第一个cell为gpio号,第二个为gpiio的极性。为0的时候是高电平有效,为1的时候则是低电平有效。

使用GPIO的设备则通过定义命名xxx-gpios属性来引用GPIO控制器的设备节点,如:

sdhc@c8000400{

status = "okay";

cd-gpios = <&gpio01 0>;

wp-gpios =<&gpio02 0>;

power-gpios = <&gpio03 0>;

bus-width = <4>;

};

而具体的设备驱动则通过类似如下的方法来获取GPIO:

cd_gpio = of_get_named_gpio(np,"cd-gpios",0);

wp_gpio = of_get_named_gpio(np,"wp-gpios",0);

power_gpio = of_get_named_gpio(np,"power-gpios",0);

在.dts和设备驱动不关心GPIO名字的情况下,也可以直接通过of_get_gpio()获取GPIO

在没有设备树之前,需要定义platform_driver 和platform_device俩个进行匹配之后再在probe 函数里面进行调用函数

而在转移到设备树之后,platform_data便不喜欢在arch/arm/mach-xxx中了,它需要从设备树的属性中获取。

for_each_child_of_node(node,pp)遍历gpio_keys节点下的所有子节点,并通过of_get_gpio_flags(),of_property_read_u32()等API读取出来与各个子节点对应的GPIO,与每个GPIO对应的键盘键值等

1.5 常用的OF_API

寻找节点

struct device_node *of_find_compatible_node(struct device_node * from, const char *type,const char *compatible);

根据兼容属性,获取设备节点。遍历设备树中的设备节点,看看哪个节点的类,兼容属性与本函数的输入参数匹配,在大多数情况下,from,type为NULL,则表示遍历了所有的节点

读取属性

int of_property_read_u8_array(const struct device_node *np ,const char * propname,u8 *out_value,size_t sz);

读取设备节点np的属性名,为propname,属性类型为8,16,32,64位整形数组。对于32位处理器来讲,最常用的是of_property_read_u32_array().

内存映射

void __iomem *of_iomap(struct device_node *node,int index);

上述API可以直接通过设备节点进行设备内存区间的ioremap(),index是内存段索引。若设备节点的reg属性有多段,可通过Index标示要ioremap()是那一段,在只有一段的情况,index为0。采用设备树之后,一些设备驱动通过of_iomap()而不是通过传统的ioremap() 进行映射,当然,传统的ioremap()的用户也不少。

解析中断

unsigned int irq_of_parse_and_map(struct device_node *dev,int index);

通过设备树获得设备的中断号,实际上是从.dts中的interrupts属性里解析出中断号。若设备使用了多个中断,index指定中断的索引号。

获取与节点对应的platform_device

struct platform_device *of_find_device_by_node(struct device_node *np);

在可以拿到device_node的情况下,如果想反向获取对应的Platform__device,可使用上述API。当然,在已知platform_device的情况下,想获取device_node则易如反掌例如:

static int sirfsoc_dma_probe(struct platform_device *op ){

struct device_node *dn = op->dev.of_node;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: