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