diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2023-02-22 13:47:29 -0600 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2023-02-22 13:47:29 -0600 |
commit | a9cd360245af188f29e3b5cd1d1c7a60f8249b1f (patch) | |
tree | 7de0bf4bec42154ae5c26231c984a9448cdd0250 | |
parent | 5256d493809d4e741ba813989e392ed96d1e236a (diff) | |
parent | c435669a41dd4ee063572e17b48c14806ad31f72 (diff) |
Merge branch 'pci/controller/imx6'
- Add i.MX8MM, i.MX8MQ, i.MX8MP endpoint mode DT binding and driver support
(Richard Zhu)
* pci/controller/imx6:
PCI: imx6: Add i.MX8MP PCIe EP support
PCI: imx6: Add i.MX8MM PCIe EP support
PCI: imx6: Add i.MX8MQ PCIe EP support
PCI: imx6: Add i.MX PCIe EP mode support
misc: pci_endpoint_test: Add i.MX8 PCIe EP device support
dt-bindings: imx6q-pcie: Add i.MX8MP PCIe EP mode compatible string
dt-bindings: imx6q-pcie: Add i.MX8MQ PCIe EP mode compatible string
dt-bindings: imx6q-pcie: Add i.MX8MM PCIe EP mode compatible string
-rw-r--r-- | Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 3 | ||||
-rw-r--r-- | drivers/misc/pci_endpoint_test.c | 2 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 23 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 200 |
4 files changed, 209 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index bad980902f66..f13f87fddb3d 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -24,6 +24,9 @@ properties: - fsl,imx8mq-pcie - fsl,imx8mm-pcie - fsl,imx8mp-pcie + - fsl,imx8mm-pcie-ep + - fsl,imx8mq-pcie-ep + - fsl,imx8mp-pcie-ep reg: items: diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 11530b4ec389..e2687229955d 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -72,6 +72,7 @@ #define PCI_DEVICE_ID_TI_J7200 0xb00f #define PCI_DEVICE_ID_TI_AM64 0xb010 #define PCI_DEVICE_ID_LS1088A 0x80c0 +#define PCI_DEVICE_ID_IMX8 0x0808 #define is_am654_pci_dev(pdev) \ ((pdev)->device == PCI_DEVICE_ID_TI_AM654) @@ -980,6 +981,7 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0), .driver_data = (kernel_ulong_t)&default_data, }, + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_IMX8),}, { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_LS1088A), .driver_data = (kernel_ulong_t)&default_data, }, diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index a0d2713f0e88..dffd7fbdfb98 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -92,10 +92,31 @@ config PCI_EXYNOS functions to implement the driver. config PCI_IMX6 - bool "Freescale i.MX6/7/8 PCIe controller" + bool + +config PCI_IMX6_HOST + bool "Freescale i.MX6/7/8 PCIe controller host mode" depends on ARCH_MXC || COMPILE_TEST depends on PCI_MSI select PCIE_DW_HOST + select PCI_IMX6 + help + Enables support for the PCIe controller in the i.MX SoCs to + work in Root Complex mode. The PCI controller on i.MX is based + on DesignWare hardware and therefore the driver re-uses the + DesignWare core functions to implement the driver. + +config PCI_IMX6_EP + bool "Freescale i.MX6/7/8 PCIe controller endpoint mode" + depends on ARCH_MXC || COMPILE_TEST + depends on PCI_ENDPOINT + select PCIE_DW_EP + select PCI_IMX6 + help + Enables support for the PCIe controller in the i.MX SoCs to + work in endpoint mode. The PCI controller on i.MX is based + on DesignWare hardware and therefore the driver re-uses the + DesignWare core functions to implement the driver. config PCIE_SPEAR13XX bool "STMicroelectronics SPEAr PCIe controller" diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 1dde5c579edc..55a0405b921d 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -52,6 +52,9 @@ enum imx6_pcie_variants { IMX8MQ, IMX8MM, IMX8MP, + IMX8MQ_EP, + IMX8MM_EP, + IMX8MP_EP, }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) @@ -60,6 +63,7 @@ enum imx6_pcie_variants { struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; + enum dw_pcie_device_mode mode; u32 flags; int dbi_length; const char *gpr; @@ -152,24 +156,39 @@ struct imx6_pcie { static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) { WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ && + imx6_pcie->drvdata->variant != IMX8MQ_EP && imx6_pcie->drvdata->variant != IMX8MM && - imx6_pcie->drvdata->variant != IMX8MP); + imx6_pcie->drvdata->variant != IMX8MM_EP && + imx6_pcie->drvdata->variant != IMX8MP && + imx6_pcie->drvdata->variant != IMX8MP_EP); return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) { - unsigned int mask, val; + unsigned int mask, val, mode; - if (imx6_pcie->drvdata->variant == IMX8MQ && - imx6_pcie->controller_id == 1) { - mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; - val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, - PCI_EXP_TYPE_ROOT_PORT); - } else { + if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE) + mode = PCI_EXP_TYPE_ENDPOINT; + else + mode = PCI_EXP_TYPE_ROOT_PORT; + + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + case IMX8MQ_EP: + if (imx6_pcie->controller_id == 1) { + mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; + val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + mode); + } else { + mask = IMX6Q_GPR12_DEVICE_TYPE; + val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode); + } + break; + default: mask = IMX6Q_GPR12_DEVICE_TYPE; - val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, - PCI_EXP_TYPE_ROOT_PORT); + val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode); + break; } regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val); @@ -304,13 +323,16 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->drvdata->variant) { case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: /* * The PHY initialization had been done in the PHY * driver, break here directly. */ break; case IMX8MQ: + case IMX8MQ_EP: /* * TODO: Currently this code assumes external * oscillator is being used @@ -561,8 +583,11 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) case IMX7D: break; case IMX8MM: + case IMX8MM_EP: case IMX8MQ: + case IMX8MQ_EP: case IMX8MP: + case IMX8MP_EP: ret = clk_prepare_enable(imx6_pcie->pcie_aux); if (ret) { dev_err(dev, "unable to enable pcie_aux clock\n"); @@ -606,8 +631,11 @@ static void imx6_pcie_disable_ref_clk(struct imx6_pcie *imx6_pcie) IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); break; case IMX8MM: + case IMX8MM_EP: case IMX8MQ: + case IMX8MQ_EP: case IMX8MP: + case IMX8MP_EP: clk_disable_unprepare(imx6_pcie->pcie_aux); break; default: @@ -672,10 +700,13 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) switch (imx6_pcie->drvdata->variant) { case IMX7D: case IMX8MQ: + case IMX8MQ_EP: reset_control_assert(imx6_pcie->pciephy_reset); fallthrough; case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: reset_control_assert(imx6_pcie->apps_reset); break; case IMX6SX: @@ -713,6 +744,7 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) switch (imx6_pcie->drvdata->variant) { case IMX8MQ: + case IMX8MQ_EP: reset_control_deassert(imx6_pcie->pciephy_reset); break; case IMX7D: @@ -751,7 +783,9 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) break; case IMX6Q: /* Nothing to do */ case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: break; } @@ -800,8 +834,11 @@ static void imx6_pcie_ltssm_enable(struct device *dev) break; case IMX7D: case IMX8MQ: + case IMX8MQ_EP: case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: reset_control_deassert(imx6_pcie->apps_reset); break; } @@ -820,8 +857,11 @@ static void imx6_pcie_ltssm_disable(struct device *dev) break; case IMX7D: case IMX8MQ: + case IMX8MQ_EP: case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: reset_control_assert(imx6_pcie->apps_reset); break; } @@ -1003,8 +1043,104 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = { static const struct dw_pcie_ops dw_pcie_ops = { .start_link = imx6_pcie_start_link, + .stop_link = imx6_pcie_stop_link, +}; + +static void imx6_pcie_ep_init(struct dw_pcie_ep *ep) +{ + enum pci_barno bar; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + for (bar = BAR_0; bar <= BAR_5; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int imx6_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + enum pci_epc_irq_type type, + u16 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_EPC_IRQ_LEGACY: + return dw_pcie_ep_raise_legacy_irq(ep, func_no); + case PCI_EPC_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + case PCI_EPC_IRQ_MSIX: + return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + return -EINVAL; + } + + return 0; +} + +static const struct pci_epc_features imx8m_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, + .reserved_bar = 1 << BAR_1 | 1 << BAR_3, + .align = SZ_64K, +}; + +static const struct pci_epc_features* +imx6_pcie_ep_get_features(struct dw_pcie_ep *ep) +{ + return &imx8m_pcie_epc_features; +} + +static const struct dw_pcie_ep_ops pcie_ep_ops = { + .ep_init = imx6_pcie_ep_init, + .raise_irq = imx6_pcie_ep_raise_irq, + .get_features = imx6_pcie_ep_get_features, }; +static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, + struct platform_device *pdev) +{ + int ret; + unsigned int pcie_dbi2_offset; + struct dw_pcie_ep *ep; + struct resource *res; + struct dw_pcie *pci = imx6_pcie->pci; + struct dw_pcie_rp *pp = &pci->pp; + struct device *dev = pci->dev; + + imx6_pcie_host_init(pp); + ep = &pci->ep; + ep->ops = &pcie_ep_ops; + + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ_EP: + case IMX8MM_EP: + case IMX8MP_EP: + pcie_dbi2_offset = SZ_1M; + break; + default: + pcie_dbi2_offset = SZ_4K; + break; + } + pci->dbi_base2 = pci->dbi_base + pcie_dbi2_offset; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) + return -EINVAL; + + ep->phys_base = res->start; + ep->addr_size = resource_size(res); + ep->page_size = SZ_64K; + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize endpoint\n"); + return ret; + } + /* Start LTSSM. */ + imx6_pcie_ltssm_enable(dev); + + return 0; +} + static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) { struct device *dev = imx6_pcie->pci->dev; @@ -1166,6 +1302,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) "pcie_inbound_axi clock missing or invalid\n"); break; case IMX8MQ: + case IMX8MQ_EP: imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); if (IS_ERR(imx6_pcie->pcie_aux)) return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux), @@ -1190,7 +1327,9 @@ static int imx6_pcie_probe(struct platform_device *pdev) } break; case IMX8MM: + case IMX8MM_EP: case IMX8MP: + case IMX8MP_EP: imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); if (IS_ERR(imx6_pcie->pcie_aux)) return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux), @@ -1279,15 +1418,22 @@ static int imx6_pcie_probe(struct platform_device *pdev) if (ret) return ret; - ret = dw_pcie_host_init(&pci->pp); - if (ret < 0) - return ret; + if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE) { + ret = imx6_add_pcie_ep(imx6_pcie, pdev); + if (ret < 0) + return ret; + } else { + ret = dw_pcie_host_init(&pci->pp); + if (ret < 0) + return ret; + + if (pci_msi_enabled()) { + u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); - if (pci_msi_enabled()) { - u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); - val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS); - val |= PCI_MSI_FLAGS_ENABLE; - dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val); + val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS); + val |= PCI_MSI_FLAGS_ENABLE; + dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val); + } } return 0; @@ -1343,6 +1489,21 @@ static const struct imx6_pcie_drvdata drvdata[] = { .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx8mp-iomuxc-gpr", }, + [IMX8MQ_EP] = { + .variant = IMX8MQ_EP, + .mode = DW_PCIE_EP_TYPE, + .gpr = "fsl,imx8mq-iomuxc-gpr", + }, + [IMX8MM_EP] = { + .variant = IMX8MM_EP, + .mode = DW_PCIE_EP_TYPE, + .gpr = "fsl,imx8mm-iomuxc-gpr", + }, + [IMX8MP_EP] = { + .variant = IMX8MP_EP, + .mode = DW_PCIE_EP_TYPE, + .gpr = "fsl,imx8mp-iomuxc-gpr", + }, }; static const struct of_device_id imx6_pcie_of_match[] = { @@ -1353,6 +1514,9 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], }, { .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], }, { .compatible = "fsl,imx8mp-pcie", .data = &drvdata[IMX8MP], }, + { .compatible = "fsl,imx8mq-pcie-ep", .data = &drvdata[IMX8MQ_EP], }, + { .compatible = "fsl,imx8mm-pcie-ep", .data = &drvdata[IMX8MM_EP], }, + { .compatible = "fsl,imx8mp-pcie-ep", .data = &drvdata[IMX8MP_EP], }, {}, }; |