基于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.

0 0
原创粉丝点击