基于Zynq平台的设备树指南 --第四部分
来源:互联网 发布:删除数据库冗余信息 编辑:程序博客网 时间:2024/06/07 20:59
获取资源
当内核模块驱动被加载时,就是获取硬件资源的控制权的时机。比如:读取和写入对应的寄存器,并且获取硬件的中断号。
我们仍然分析设备树中的同一个节点
xillybus_0: xillybus@50000000 {
compatible ="xlnx,xillybus-1.00.a";
reg = < 0x50000000 0x1000>;
interrupts = < 0 59 1 >;
interrupt-parent =<&gic>;
xlnx,max-burst-len = <0x10>;
xlnx,native-data-width = <0x20>;
xlnx,slv-awidth = <0x20>;
xlnx,slv-dwidth = <0x20>;
xlnx,use-wstrb = <0x1>;
} ;
现在我们集中关注上面被标注成粗体的部分。
在探测函数中,驱动通常获取硬件内存段的控制权。(探测函数通常在 platform_driver结构体(为驱动定义)中的“probe”节点项指向的函数。这里xilly_drv_probe() 就是 Xillybus驱动的探测函数)。
我们这里将会看看典型的探测函数代码(不要直接使用这里的代码,最好使用一个真实,正常工作的驱动)
static int __devinit xilly_drv_probe(struct platform_device *op)
{
const struct of_device_id *match;
match = of_match_device(xillybus_of_match,&op->dev);
if (!match)
return -EINVAL;
第一个操作是健全检测,验证该探测函数是被相关的设备调用的,这个检测确实不怎么必要,但是很多驱动中都使用了这种检测。
访问寄存器
下一步,就是分配物理内存段并把物理内存映射到虚拟内存地址
int rc = 0;
struct resource res;
void *registers;
rc = of_address_to_resource(&op->dev.of_node,0, &res);
if (rc) {
/* Fail */
}
if (!request_mem_region(res.start, resource_size(&res),"xillybus")) {
/* Fail */
}
registers = of_iomap(op->dev.of_node, 0);
if (!registers) {
/* Fail */
}
of_address_to_resource() 函数使用外围设备设备树节点的第一个“reg”赋值语句(因此第二个参数为0)中的内存段初始化“res”结构体。在例子中,“reg = < 0x50000000 0x1000 >”, 意思是分配的内存块起始物理地址是0x50000000,并且长度是0x1000字节。of_address_to_resource() 函数就会设置res.start= 0x50000000 和 res.end = 0x50000fff.
由于没有为Zynq生成设备树的自动化工具,地址和长度都需要手动从XPS中定义好的地址映射表中复制出来(在XPS中点击“Addresses” 标签项)。(译者注:目前已经有了Zynq平台自动生成设备树的工具参考这里:http://www.wiki.xilinx.com/Build+Device+Tree+Blob)
就像其他设备驱动一样,调用 request_mem_region()是为了注册特定的内存段。这样做是为了避免两个驱动访问同一寄存器空间(这种情况应该从不发生)。正如大家所希望的resource_size() 这个内联函数返回段的长度(目前的例子返回0x1000 )。
of_iomap()函数是of_address_to_resource()和 ioremap()这两个函数的组合,of_iomap() 函数本质上等同于 ioremap(res.start,resource_size(&res)),它完成了物理地址到虚拟地址的映射,并且返回了虚拟内存段的起始虚拟地址。
无需多说,在模块从内行卸载之前或者后面的操作有错误发生,上面进行的操作都要进行回退。
我们很容易像使用其他指针一样使用“register”指针,“好”一点的,作为一个volatile指针。Linux内核编程的经验法则是:如果你感觉应该使用“volatile”关键字,你就在做错误的事情。访问硬件寄存器的正确方法是使用 iowrite32(),ioread32() 及其他io操作的函数和宏定义,所有的设备驱动已经演示了这些。
注意一点,当我们把 “register” 变量作为一个普通指针使用去访问硬件寄存器时,即使什么都没有做,都可能出现死机,这样很有可能引发缓存一致性问题,尤其是在ARM平台上,比如Zynq可扩展平台。
添加中断处理程序
驱动方面添加中断处理程序的代码比较简单,就像下面的代码:
irq = irq_of_parse_and_map(op->dev.of_node, 0);
rc = request_irq(irq, xillybus_isr, 0, "xillybus", op->dev);
irq_of_parse_and_map() 函数仅仅是从设备树中查询出中断的参数并返回该中断的中断号。中断号是request_irq() 需要使用的(”irq”和 /proc/interrupts 这里列出的一样)。第二个参数是0,表示应该获取的是设备树中第一个中断的信息。
request_irq() 函数是注册中断处理程序。这个函数在Linux设备驱动程序第三版( LDD3 book.)里有解释。
设备树中的声明如下面格式(从上面的DTS中复制)
interrupts = < 0 59 1 >;
interrupt-parent = <&gic>;
为什么有三个数字需要赋值给中断。
第一个数字(0)是一个标志,表明该中断是否是一个SPI(共享外围设备中断)。 非零值表明它是一个SPI.事实上,根据Zynq技术参考手册(TRM),这些中断都是SPI中断。这个领域的共同约定是:写成零值,表示他们不是SPI。因为这些错误的声明是如此常见:考虑到中断号,尤其是声明这些中断为SPI就会导致一些困惑,推荐还是严格遵守这些约定。这里有关于该问题的讨论。
第二个数字就是和中断号相关的。简而言之,在XPS主窗口中Zynq项中点击“GIC”框,查看赋给该中断的中断号(Xillinxu 中赋值给xillybus是91)并把该值减去32(91-32 =59)。
第三个数字是中断的类型。有三种可能的值:
u 0——不需要任何修改就会触发该中断(如果该中断产生,应该是加电后自动产生或者bootloader设置触发该中断)
u 1——上升沿触发
u 4——电平敏感,高电平触发。
其他值都是不允许的。由于硬件时不支持这些模式的,下降沿和低电平时不支持的。如果你需要这些,在逻辑中添加一个非门。
需要注意的是:在“官方”的设备树中,第三个数字经常是零。因此Linux内核就保持中断状态为它已经设置成的状态,这种情况通常是高电平触发, 而且,这种情况使Linux驱动依赖于bootlaoder 不会弄乱中断的状态。
最后,讨论中断控制器赋值语句,它通常指向中断控制器,在这里,&gic是中断控制器的引用。当设备树是从DTB反编译出来的时候,一个数字就会替换这个引用,通常这个数字是0x1.
- 基于Zynq平台的设备树指南 --第四部分
- 基于Zynq平台的设备树指南 --第一部分
- 基于Zynq平台的设备树指南 --第二部分
- 基于Zynq平台的设备树指南 --第三部分
- 基于Zynq平台的设备树指南 --第五部分
- 译文:基于Zynq平台的设备树指南
- ZYNQ平台学习--(4)设备树生成
- zynq设备树详解
- 一种新的基于Windows Mobile平台的移动开发N层架构(第四部分)
- 转载有关zynq的linux设备树相关知识
- Zynq的PL-PS中断在设备树中的描述
- ZYNQ的Linux开发--使用SDK生成设备树
- 一步一步学ZedBoard & Zynq(四):基于AXI Lite 总线的从设备IP设计
- 一步一步学ZedBoard & Zynq(四):基于AXI Lite 总线的从设备IP设计
- Zynq设备树教程(四)
- zynq设备树历史最详解
- 基于zynq的pwm ip核设计
- 基于Zynq的光流法软硬件协同实现
- 多任务临界段(原语操作)讨论
- 人品计算机2.0
- 通俗易懂:编译器的工作过程
- 第一章 对象导论
- 开源CEGUI编辑器之二(MFC重写的ImagesetEditor)
- 基于Zynq平台的设备树指南 --第四部分
- 实时操作系统的优秀文章
- Apache与Nginx的优缺点比较
- Android Virtual Device调试步骤
- 第十四周项目三:多科成绩单(一)
- Android学习之 Manifest中meta-data扩展元素数据的配置与获取
- sgu146:The Runner
- 基于Zynq平台的设备树指南 --第五部分
- 晋升后的项目经理如何与同事相处