首页 > 系统相关 >DW PCIE Linux驱动整理

DW PCIE Linux驱动整理

时间:2023-04-21 19:37:08浏览次数:49  
标签:pp PCIE dev pcie pci ret Linux DW imx6

1. DTS

以imx6q为例,该SOC的DTS中对PCIE控制器的描述(对应dts文件:linux-4.14.75/arch/arm/boot/dts/imx6qd.dtsi)

        pcie: pcie@1ffc000 {
            compatible = "fsl,imx6q-pcie", "snps,dw-pcie";
            reg = <0x01ffc000 0x04000>,
                  <0x01f00000 0x80000>;
            reg-names = "dbi", "config";
            #address-cells = <3>;
            #size-cells = <2>;
            device_type = "pci";
            bus-range = <0x00 0xff>;
            ranges = <0x81000000 0 0          0x01f80000 0 0x00010000 /* downstream I/O */
                  0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */
            num-lanes = <1>;
            interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
            interrupt-names = "msi";
            #interrupt-cells = <1>;
            interrupt-map-mask = <0 0 0 0x7>;
            interrupt-map = <0 0 0 1 &gpc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
                    <0 0 0 2 &gpc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
                    <0 0 0 3 &gpc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
                    <0 0 0 4 &gpc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&clks IMX6QDL_CLK_PCIE_AXI>,
                 <&clks IMX6QDL_CLK_LVDS1_GATE>,
                 <&clks IMX6QDL_CLK_PCIE_REF_125M>;
            clock-names = "pcie", "pcie_bus", "pcie_phy";
            status = "disabled";
        };

对应的寄存器手册:

 可以看到,0x1FFC0000是处理器中PCIE控制器的基地址,对应了DTS中<reg-names = "dbi">表项。

2.平台驱动

(1)平台驱动的匹配

static const struct of_device_id imx6_pcie_of_match[] = {
    { .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
    { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
    { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
    { .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },
    {},
};

static struct platform_driver imx6_pcie_driver = {
    .driver = {
        .name    = "imx6q-pcie",
        .of_match_table = imx6_pcie_of_match,
        .suppress_bind_attrs = true,
    },
    .probe    = imx6_pcie_probe,
    .shutdown = imx6_pcie_shutdown,
};

static int __init imx6_pcie_init(void)
{
    /*
     * Since probe() can be deferred we need to make sure that
     * hook_fault_code is not called after __init memory is freed
     * by kernel and since imx6q_pcie_abort_handler() is a no-op,
     * we can install the handler here without risking it
     * accessing some uninitialized driver state.
     */
    hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
            "external abort on non-linefetch");

    return platform_driver_register(&imx6_pcie_driver);
}
device_initcall(imx6_pcie_init);

imx6_pcie_of_match匹配表中的"fsl,imx6q-pcie"与dts吻合,即可进入probe函数。

(2)probe函数

linux-4.14.75/drivers/pci/dwc/pci-imx6.c

static int imx6_pcie_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct dw_pcie *pci;
    struct imx6_pcie *imx6_pcie;
    struct resource *dbi_base;
    struct device_node *node = dev->of_node;
    int ret;

    imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); 
    if (!imx6_pcie)
        return -ENOMEM;

    pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); //分配PCI数据结构
    if (!pci)
        return -ENOMEM;

    pci->dev = dev;
    pci->ops = &dw_pcie_ops; //指向DW通用OPS操作接口

    imx6_pcie->pci = pci;
    imx6_pcie->variant =
        (enum imx6_pcie_variants)of_device_get_match_data(dev);

    dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取设备树节点中的第0个reg属性,即0x01ffc000,对应DBI物理基地址
    pci->dbi_base = devm_ioremap_resource(dev, dbi_base);  //将DBI物理基地址通过ioremap映射为虚拟地址,记入pci->dbi_base变量
    if (IS_ERR(pci->dbi_base))
        return PTR_ERR(pci->dbi_base);

    /* Fetch GPIOs */
    imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
    imx6_pcie->gpio_active_high = of_property_read_bool(node,
                        "reset-gpio-active-high");
    if (gpio_is_valid(imx6_pcie->reset_gpio)) {
        ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio,
                imx6_pcie->gpio_active_high ?
                    GPIOF_OUT_INIT_HIGH :
                    GPIOF_OUT_INIT_LOW,
                "PCIe reset");
        if (ret) {
            dev_err(dev, "unable to get reset gpio\n");
            return ret;
        }
    } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) {
        return imx6_pcie->reset_gpio;
    }

    /* Fetch clocks */
    imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
    if (IS_ERR(imx6_pcie->pcie_phy)) {
        dev_err(dev, "pcie_phy clock source missing or invalid\n");
        return PTR_ERR(imx6_pcie->pcie_phy);
    }

    imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus");
    if (IS_ERR(imx6_pcie->pcie_bus)) {
        dev_err(dev, "pcie_bus clock source missing or invalid\n");
        return PTR_ERR(imx6_pcie->pcie_bus);
    }

    imx6_pcie->pcie = devm_clk_get(dev, "pcie");
    if (IS_ERR(imx6_pcie->pcie)) {
        dev_err(dev, "pcie clock source missing or invalid\n");
        return PTR_ERR(imx6_pcie->pcie);
    }

    switch (imx6_pcie->variant) {
    case IMX6SX:
        imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                               "pcie_inbound_axi");
        if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
            dev_err(dev, "pcie_inbound_axi clock missing or invalid\n");
            return PTR_ERR(imx6_pcie->pcie_inbound_axi);
        }
        break;
    case IMX7D:
        imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev,
                                        "pciephy");
        if (IS_ERR(imx6_pcie->pciephy_reset)) {
            dev_err(dev, "Failed to get PCIEPHY reset control\n");
            return PTR_ERR(imx6_pcie->pciephy_reset);
        }

        imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev,
                                     "apps");
        if (IS_ERR(imx6_pcie->apps_reset)) {
            dev_err(dev, "Failed to get PCIE APPS reset control\n");
            return PTR_ERR(imx6_pcie->apps_reset);
        }
        break;
    default:
        break;
    }

    /* Grab GPR config register range */
    imx6_pcie->iomuxc_gpr =
         syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
    if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
        dev_err(dev, "unable to find iomuxc registers\n");
        return PTR_ERR(imx6_pcie->iomuxc_gpr);
    }

    /* Grab PCIe PHY Tx Settings */
    if (of_property_read_u32(node, "fsl,tx-deemph-gen1",
                 &imx6_pcie->tx_deemph_gen1))
        imx6_pcie->tx_deemph_gen1 = 0;

    if (of_property_read_u32(node, "fsl,tx-deemph-gen2-3p5db",
                 &imx6_pcie->tx_deemph_gen2_3p5db))
        imx6_pcie->tx_deemph_gen2_3p5db = 0;

    if (of_property_read_u32(node, "fsl,tx-deemph-gen2-6db",
                 &imx6_pcie->tx_deemph_gen2_6db))
        imx6_pcie->tx_deemph_gen2_6db = 20;

    if (of_property_read_u32(node, "fsl,tx-swing-full",
                 &imx6_pcie->tx_swing_full))
        imx6_pcie->tx_swing_full = 127;

    if (of_property_read_u32(node, "fsl,tx-swing-low",
                 &imx6_pcie->tx_swing_low))
        imx6_pcie->tx_swing_low = 127;

    /* Limit link speed */
    ret = of_property_read_u32(node, "fsl,max-link-speed",
                   &imx6_pcie->link_gen);
    if (ret)
        imx6_pcie->link_gen = 1;

    imx6_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie");
    if (IS_ERR(imx6_pcie->vpcie)) {
        if (PTR_ERR(imx6_pcie->vpcie) == -EPROBE_DEFER)
            return -EPROBE_DEFER;
        imx6_pcie->vpcie = NULL;
    }

    platform_set_drvdata(pdev, imx6_pcie);

    ret = imx6_add_pcie_port(imx6_pcie, pdev); //下面展开
    if (ret < 0)
        return ret;

    return 0;
}

 

static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
                  struct platform_device *pdev)
{
    struct dw_pcie *pci = imx6_pcie->pci;
    struct pcie_port *pp = &pci->pp;
    struct device *dev = &pdev->dev;
    int ret;

    if (IS_ENABLED(CONFIG_PCI_MSI)) {
        pp->msi_irq = platform_get_irq_byname(pdev, "msi"); //获取中断信息
        if (pp->msi_irq <= 0) {
            dev_err(dev, "failed to get MSI irq\n");
            return -ENODEV;
        }

        ret = devm_request_irq(dev, pp->msi_irq,
                       imx6_pcie_msi_handler,
                       IRQF_SHARED | IRQF_NO_THREAD,
                       "mx6-pcie-msi", imx6_pcie);
        if (ret) {
            dev_err(dev, "failed to request MSI irq\n");
            return ret;
        }
    }

    pp->root_bus_nr = -1;
    pp->ops = &imx6_pcie_host_ops;

    ret = dw_pcie_host_init(pp); //调用DW通用host初始化接口,下面展开
    if (ret) {
        dev_err(dev, "failed to initialize host\n");
        return ret;
    }

    return 0;
}

 

linux-4.14.75/drivers/pci/dwc/pcie-designware-host.c

int dw_pcie_host_init(struct pcie_port *pp)
{
    struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
    struct device *dev = pci->dev;
    struct device_node *np = dev->of_node;
    struct platform_device *pdev = to_platform_device(dev);
    struct pci_bus *bus, *child;
    struct pci_host_bridge *bridge;
    struct resource *cfg_res;
    int i, ret;
    struct resource_entry *win, *tmp;

    cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); //获取设备树节点中的config reg属性,即0x01f00000,对应cfg基地址,即配置空间基地址
    if (cfg_res) {
        pp->cfg0_size = resource_size(cfg_res) / 2; //将cfg空间一分为二
        pp->cfg1_size = resource_size(cfg_res) / 2;
        pp->cfg0_base = cfg_res->start;
        pp->cfg1_base = cfg_res->start + pp->cfg0_size;
    } else if (!pp->va_cfg0_base) {
        dev_err(dev, "missing *config* reg space\n");
    }

    bridge = pci_alloc_host_bridge(0);
    if (!bridge)
        return -ENOMEM;

    ret = of_pci_get_host_bridge_resources(np, 0, 0xff,
                    &bridge->windows, &pp->io_base);
    if (ret)
        return ret;

    ret = devm_request_pci_bus_resources(dev, &bridge->windows);
    if (ret)
        goto error;

    /* Get the I/O and memory ranges from DT */
    resource_list_for_each_entry_safe(win, tmp, &bridge->windows) {
        switch (resource_type(win->res)) {
        case IORESOURCE_IO:
            ret = pci_remap_iospace(win->res, pp->io_base);
            if (ret) {
                dev_warn(dev, "error %d: failed to map resource %pR\n",
                     ret, win->res);
                resource_list_destroy_entry(win);
            } else {
                pp->io = win->res;
                pp->io->name = "I/O";
                pp->io_size = resource_size(pp->io);
                pp->io_bus_addr = pp->io->start - win->offset;
            }
            break;
        case IORESOURCE_MEM:
            pp->mem = win->res;
            pp->mem->name = "MEM";
            pp->mem_size = resource_size(pp->mem);
            pp->mem_bus_addr = pp->mem->start - win->offset;
            break;
        case 0:
            pp->cfg = win->res;
            pp->cfg0_size = resource_size(pp->cfg) / 2;
            pp->cfg1_size = resource_size(pp->cfg) / 2;
            pp->cfg0_base = pp->cfg->start;
            pp->cfg1_base = pp->cfg->start + pp->cfg0_size;
            break;
        case IORESOURCE_BUS:
            pp->busn = win->res;
            break;
        }
    }

    if (!pci->dbi_base) {
        pci->dbi_base = devm_pci_remap_cfgspace(dev,  //如果dbi_base地址没有被映射,重新进行映射
                        pp->cfg->start,
                        resource_size(pp->cfg));
        if (!pci->dbi_base) {
            dev_err(dev, "error with ioremap\n");
            ret = -ENOMEM;
            goto error;
        }
    }

    pp->mem_base = pp->mem->start;

    if (!pp->va_cfg0_base) {
        pp->va_cfg0_base = devm_pci_remap_cfgspace(dev,   //将cfg0物理基地址通过ioremap映射为虚拟地址,记入pp->va_cfg0_base变量
                    pp->cfg0_base, pp->cfg0_size);
        if (!pp->va_cfg0_base) {
            dev_err(dev, "error with ioremap in function\n");
            ret = -ENOMEM;
            goto error;
        }
    }

    if (!pp->va_cfg1_base) {
        pp->va_cfg1_base = devm_pci_remap_cfgspace(dev,   //将cfg0物理基地址通过ioremap映射为虚拟地址,记入pp->va_cfg1_base变量
                        pp->cfg1_base,
                        pp->cfg1_size);
        if (!pp->va_cfg1_base) {
            dev_err(dev, "error with ioremap\n");
            ret = -ENOMEM;
            goto error;
        }
    }

    ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport);
    if (ret)
        pci->num_viewport = 2;

    if (IS_ENABLED(CONFIG_PCI_MSI)) {
        if (!pp->ops->msi_host_init) {
            pp->irq_domain = irq_domain_add_linear(dev->of_node,
                        MAX_MSI_IRQS, &msi_domain_ops,
                        &dw_pcie_msi_chip);
            if (!pp->irq_domain) {
                dev_err(dev, "irq domain init failed\n");
                ret = -ENXIO;
                goto error;
            }

            for (i = 0; i < MAX_MSI_IRQS; i++)
                irq_create_mapping(pp->irq_domain, i);
        } else {
            ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip);
            if (ret < 0)
                goto error;
        }
    }

    if (pp->ops->host_init) {
        ret = pp->ops->host_init(pp);
        if (ret)
            goto error;
    }

    pp->root_bus_nr = pp->busn->start;

    bridge->dev.parent = dev;
    bridge->sysdata = pp;
    bridge->busnr = pp->root_bus_nr;
    bridge->ops = &dw_pcie_ops;
    bridge->map_irq = of_irq_parse_and_map_pci;
    bridge->swizzle_irq = pci_common_swizzle;
    if (IS_ENABLED(CONFIG_PCI_MSI)) {
        bridge->msi = &dw_pcie_msi_chip;
        dw_pcie_msi_chip.dev = dev;
    }

    ret = pci_scan_root_bus_bridge(bridge); //开始扫描PCI总线
    if (ret)
        goto error;

    bus = bridge->bus;

    if (pp->ops->scan_bus)
        pp->ops->scan_bus(pp);

    pci_bus_size_bridges(bus);
    pci_bus_assign_resources(bus);

    list_for_each_entry(child, &bus->children, node)
        pcie_bus_configure_settings(child);

    pci_bus_add_devices(bus);  //添加PCI设备
    return 0;

error:
    pci_free_host_bridge(bridge);
    return ret;
}

 

标签:pp,PCIE,dev,pcie,pci,ret,Linux,DW,imx6
From: https://www.cnblogs.com/DF11G/p/17341493.html

相关文章

  • Linux权限维持-隐藏总结
    攻击者在获取服务器权限后,会通过一些技巧来隐藏自己的踪迹和后门文件,本文总结下Linux的一些隐藏手段。隐藏文件Linux下创建一个隐藏文件:touch.test.txttouch命令可以创建一个文件,文件名前面加一个. 就代表是隐藏文件查看Linux下的隐藏文件需要用到命令:ls-al这里,我们可以......
  • Linux-命令
    Linux-命令主机名:centos8用户名:root密码:root12345基本命令操作cd命令几个特殊的目录. 当前的工作目录.. 上一级的工作目录- 上一次的工作目录~ 当前系统登录的用户家目录ls命令list列出文件夹中的内容ls可选参数 可选的文件夹对象-a#all显示出所有的......
  • Linux系统文件加密的详细教程
    Linux系统一切皆为文件而且Linux系统又是一个多用户系统,所以数据的安全性非常重要,有些情况需要对文件进行加密,那么Linux系统中如何对文件进行加密呢?下面良许教程网为大家分享一下Linux系统文件加密的方法。方法一:gzexe加密 这种加密方式不是非常保险的方法,但是能够满足一般的加......
  • Linux部署知途云课进行测试上传查看
    一键部署web网页:注意:本次实验由阿里云官方提供资源由于实验所需centos版本比较低为centos6.8所以7以上版本可能会出现报错情况自行选择即可实验所需压缩包可以根据官网进行自取:链接地址:http://opensource.chinamoocs.com/实验资源:使用ssh远程工具连接公网:解压一键部署安装包:[r......
  • linux makeAndCp 脚本编写执行 make 和 cp步骤
    0、makeAndCp.txt*(每行对应一个操作)makecprknn_personKp_demo/mnt/hgfs/shareFile1/rv1126/202210261、makeAndCp.sh#!/bin/bashwhilereadlinedoecho"startW:"echo$line$lineecho"endW!"done<makeAndCp.txt3、......
  • linux 命令使用11--lozone(文件)
    1.IOzone简介  IOzone是一个开源文件系统基准工具,用来测试文件系统的读写性能,也可以进行测试磁盘读写性能。Iozone能够运行于许多平台。这份文档涵盖Iozone所执行的许多不同类型的操作和它的所有命令行参数。2. 安装  ubuntu直接安装:    apt-getinstalliozone33.......
  • Linux安装基于rsyslog+loganalyzer的日志系统
    一、 关闭防火墙和selinuxsetenforce0vim/etc/selinux/config将配置文件中的SELINUX=enforcing 修改为systemctl stop firewalldsystemctl status firewalldsystemctl disable firewalld二、安装LAMPyuminstallmysql-servermysql-develhttpdphp-mysql phpphp-gdp......
  • Linux 常用命令
     Linux常用命令_linux常用命令_Lifenyencr的博客-CSDN博客1、cd命令这是一个非常基本,也是大家经常需要使用的命令,它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径。如:[plain]viewplaincopycd/root/Docements#切换到目录/root/Docem......
  • 【Linux】linux修改文件夹下所有文件的权限
    linux修改文件夹权限sudochmod600×××(只有所有者有读和写的权限)sudochmod644×××(所有者有读和写的权限,组用户只有读的权限)sudochmod700×××(只有所有者有读和写以及执行的权限)sudochmod666×××(每个人都有读和写的权限)sudochmod777×××(每个人都......
  • Linux Shell常用shell命令
    Linux常识Linux目录介绍/usr系统级的目录,可以理解为C:/Windows/,/usr/lib可理解为C:/Windows/System32。/usr/local用户级的程序目录,可以理解为C:/ProgremFiles/。用户自己编译的软件默认会安装到这个目录下。/usr/bin:存放的是系统使用的应用程序。/opt用户级的程......