Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

大话PCIe:实现host驱动

$
0
0

最新的4.12内核中对pci host driver进行一些划分,把基于designware IP的主控驱动放到drivers/pci/dwc目录下去了。如果是基于老的kernel开发,想要移植新版的内核的话,要注意,同时多了designware ep驱动框架。

非designware的主控驱动还是位于host目录下,譬如pcie-xilinx.c。

下面就以xilinx pcie主控驱动为例来介绍如何添加自己的host driver。

一、probe和设备树

现在的设备驱动大部分都是通过device tree来给定平台信息的,有了一套框架,其实写驱动就是搭积木一样,把槽位卡准了就行了,具体要如何操作呢?

这里先放一个device tree的模板。

Zynq:
    pci_express: axi-pcie@50000000 {
        #address-cells = <3>;
        #size-cells = <2>;
        #interrupt-cells = <1>;
        compatible = "xlnx,axi-pcie-host-1.00.a";
        reg = < 0x50000000 0x1000000 >;
        device_type = "pci";
        interrupts = < 0 52 4 >;
        interrupt-map-mask = <0 0 0 7>;
        interrupt-map = <0 0 0 1 &pcie_intc 1>,
                <0 0 0 2 &pcie_intc 2>,
                <0 0 0 3 &pcie_intc 3>,
                <0 0 0 4 &pcie_intc 4>;
        ranges = < 0x02000000 0 0x60000000 0x60000000 0 0x10000000 >;

        pcie_intc: interrupt-controller {
            interrupt-controller;
            #address-cells = <0>;
            #interrupt-cells = <1>;
        };
    };

首先定义好platform_driver。

static struct platform_driver xilinx_pcie_driver = {
    .driver = {
        .name = "xilinx-pcie",
        .of_match_table = xilinx_pcie_of_match,
        .suppress_bind_attrs = true,
    },
    .probe = xilinx_pcie_probe,
};
builtin_platform_driver(xilinx_pcie_driver);

这里有一个of_match_table字段,是这么定义的:

static struct of_device_id xilinx_pcie_of_match[] = {
    { .compatible = "xlnx,axi-pcie-host-1.00.a", },
    {}
};

这里的compatible信息是”xlnx,axi-pcie-host-1.00.a”,和device tree中的compatible = "xlnx,axi-pcie-host-1.00.a"是对应的。系统在启动的时候会根据这个compatible字段来probe驱动,这里知道这个就能进到我们的probe函数了。

二、初始化host

要知道进入probe就说明了以下几个事实:

  • struct platform_device结构体已经生成,并且已经和内核绑定好了(通过device结构体)。
  • 设备树节点已经获取到,而且保存在了device结构体中。

所以,进入probe后,先先定义一个struct xilinx_pcie_port结构体,用来保存作为Root Port需要的信息,不仅kernel自己要知道device信息,我们自己也要知道,这样的话还能通过相关的结构和kernel进行交互。

首先,获取最重要的device对象,后续的设备操作都是和这个device结构体绑定的。

port->dev = dev

其次,parse device tree。

err = xilinx_pcie_parse_dt(port);
if (err) {
    dev_err(dev, "Parsing DT failed\n");
    return err;
}

各种device tree接口如下:

  • of_get_property(node, “device_type”, NULL);
  • of_address_to_resource(node, 0, &regs);
  • irq_of_parse_and_map(node, 0);

严格上讲,这个接口并不全是直接操作device tree的接口,详细的可以参考device tree相关文档。

三、初始化Root Port

xilinx_pcie_init_port(port);

链路训练,清中断,使能中断。

四、获取总线资源

包括总线range(因为是Root Port,所以从0~255),获取device tree中定义的range,生成相应的resource。然后通过devm_request_pci_bus_resources(dev, &res)向内核申请。

五、生成pci bus,枚举生成PCI设备树

bus = pci_create_root_bus(dev, 0, &xilinx_pcie_ops, port, &res);
    if (!bus) {
        err = -ENOMEM;
        goto error;
    }

#ifdef CONFIG_PCI_MSI
    xilinx_pcie_msi_chip.dev = dev;
    bus->msi = &xilinx_pcie_msi_chip;
#endif
    // 扫描下级总线,然后申请内核资源
    pci_scan_child_bus(bus);
    pci_assign_unassigned_bus_resources(bus);
#ifndef CONFIG_MICROBLAZE
    pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
#endif
    list_for_each_entry(child, &bus->children, node)
        pcie_bus_configure_settings(child);
    // 扫描PCIe设备
    pci_bus_add_devices(bus);
    return 0;

至此,host的功能已经起来了。

作者:abcamus 发表于2017/9/17 13:42:23 原文链接
阅读:108 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>