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;
}
在过去的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;
}
相关文章推荐
- JAVA Window、Linux 获取实际IP地址
- Linux下创建和删除用户
- Linux每日学习(十四)
- Linux建立FTP服务器
- Linux每日学习(十三)
- Linux 常用命令之mv
- Linux每日学习(十二)
- Linux每日学习(十一)
- linux下安装部署solr5.4.0详细记录
- Linux驱动设计中相关知识点记录
- 战五渣系列之三(6个命令)
- Linux中/etc/passwd文件与/etc/shadow文件解析.
- centos配置ssh无密码登陆
- linux中断--进程上下文和中断上下文
- Linux 常用命令之rm
- linux系统:rm-rf执行以后,怎么办?我来教你恢复文件。
- gitlab环境搭建
- CentOS7 Zabbix 安装
- Linux: 信息查看
- Centos 6.4 python 2.6 升级到 2.7