diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2021-05-04 14:54:19 +0200 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2021-05-05 23:03:56 +0200 |
commit | a08f3c7add53eaaa4ab453ac3f06c24ec8927579 (patch) | |
tree | d0d794479622b8054d0ae746a5dff0524cfe472c /drivers/pci/controller/dwc/pci-imx6.c | |
parent | 82e97870feb6303ecc71371abc76a606e724b065 (diff) | |
parent | 28910e01c43d9735f06fddbeaa42df3e112d1b3e (diff) |
Merge commit '28910e01c43d9735f06fddbeaa42df3e112d1b3e' into toradex_5.4-2.3.x-imx
This basically contains NXP BSP Patch L5.4.70_2.3.2 plus kernel.org
v5.4.115 from https://github.com/Freescale/linux-fslc/tree/5.4-2.3.x-imx.
Related-to: ELB-3958
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Diffstat (limited to 'drivers/pci/controller/dwc/pci-imx6.c')
-rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 144 |
1 files changed, 104 insertions, 40 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 7963e27b754c..663f41df33ab 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -46,6 +46,7 @@ #define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) #define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) #define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) +#define IMX8MQ_GPR_PCIE_VREG_BYPASS BIT(12) #define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) #define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 #define IMX8_HSIO_PCIEB_BASE_ADDR 0x5f010000 @@ -143,8 +144,10 @@ struct imx6_pcie { u32 hsio_cfg; u32 ext_osc; u32 local_addr; + u32 l1ss_clkreq; int link_gen; struct regulator *vpcie; + struct regulator *vph; void __iomem *phy_base; void __iomem *hsmix_base; @@ -1652,6 +1655,17 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) imx6_pcie_grp_offset(imx6_pcie), IMX8MQ_GPR_PCIE_REF_USE_PAD, IMX8MQ_GPR_PCIE_REF_USE_PAD); + /* + * Regarding to the datasheet, the PCIE_VPH is suggested + * to be 1.8V. If the PCIE_VPH is supplied by 3.3V, the + * VREG_BYPASS should be cleared to zero. + */ + if (imx6_pcie->vph && + regulator_get_voltage(imx6_pcie->vph) > 3000000) + regmap_update_bits(imx6_pcie->iomuxc_gpr, + imx6_pcie_grp_offset(imx6_pcie), + IMX8MQ_GPR_PCIE_VREG_BYPASS, + 0); break; case IMX8MP: case IMX8MP_EP: @@ -1943,6 +1957,8 @@ err_reset_phy: imx6_pcie_clk_disable(imx6_pcie); if (imx6_pcie->vpcie != NULL) regulator_disable(imx6_pcie->vpcie); + if (imx6_pcie->dis_gpiod) + gpiod_set_value_cansleep(imx6_pcie->dis_gpiod, 0); } return ret; @@ -2257,6 +2273,26 @@ static struct attribute_group imx_pcie_attrgroup = { .attrs = imx_pcie_rc_attrs, }; +static void imx6_pcie_clkreq_enable(struct imx6_pcie *imx6_pcie) +{ + /* + * If the L1SS is supported, disable the over ride after link up. + * Let the the CLK_REQ# controlled by HW L1SS automatically. + */ + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + case IMX8MM: + case IMX8MP: + regmap_update_bits(imx6_pcie->iomuxc_gpr, + imx6_pcie_grp_offset(imx6_pcie), + IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, + 0); + break; + default: + break; + }; +} + #ifdef CONFIG_PM_SLEEP static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) { @@ -2389,6 +2425,8 @@ static int imx6_pcie_resume_noirq(struct device *dev) ret = imx6_pcie_establish_link(imx6_pcie); if (ret < 0) dev_info(dev, "pcie link is down after resume.\n"); + if (imx6_pcie->l1ss_clkreq) + imx6_pcie_clkreq_enable(imx6_pcie); } return 0; @@ -2411,7 +2449,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) void __iomem *iomem; struct regmap_config regconfig = imx6_pcie_regconfig; int ret; - u32 reg, val; + u32 val; imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); if (!imx6_pcie) @@ -2484,6 +2522,10 @@ static int imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->ext_osc = 0; if (of_property_read_u32(node, "local-addr", &imx6_pcie->local_addr)) imx6_pcie->local_addr = 0; + if (of_property_read_bool(node, "l1ss-disabled")) + imx6_pcie->l1ss_clkreq = 0; + else + imx6_pcie->l1ss_clkreq = 1; /* Fetch GPIOs */ imx6_pcie->clkreq_gpiod = devm_gpiod_get_optional(dev, "clkreq", @@ -2731,6 +2773,13 @@ static int imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->vpcie = NULL; } + imx6_pcie->vph = devm_regulator_get_optional(&pdev->dev, "vph"); + if (IS_ERR(imx6_pcie->vph)) { + if (PTR_ERR(imx6_pcie->vph) != -ENODEV) + return PTR_ERR(imx6_pcie->vph); + imx6_pcie->vph = NULL; + } + platform_set_drvdata(pdev, imx6_pcie); ret = imx6_pcie_attach_pd(dev); @@ -2742,6 +2791,8 @@ static int imx6_pcie_probe(struct platform_device *pdev) dev_err(dev, "failed to enable the epdev_on regulator\n"); goto err_ret; } + if (imx6_pcie->dis_gpiod) + gpiod_set_value_cansleep(imx6_pcie->dis_gpiod, 1); imx6_pcie_assert_core_reset(imx6_pcie); imx6_pcie_init_phy(imx6_pcie); @@ -2777,45 +2828,6 @@ static int imx6_pcie_probe(struct platform_device *pdev) } pci_imx_set_msi_en(&imx6_pcie->pci->pp); - /* - * If the L1SS is enabled, disable the over ride after link up. - * Let the the CLK_REQ# controlled by HW L1SS automatically. - */ - ret = imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_SUPPORTS_L1SS; - if (IS_ENABLED(CONFIG_PCIEASPM_POWER_SUPERSAVE) && (ret > 0)) { - switch (imx6_pcie->drvdata->variant) { - case IMX8MQ: - case IMX8MM: - case IMX8MP: - case IMX8MQ_EP: - case IMX8MM_EP: - case IMX8MP_EP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, - imx6_pcie_grp_offset(imx6_pcie), - IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, - 0); - break; - case IMX8QXP: - case IMX8QXP_EP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, - IMX8QM_CSR_MISC_OFFSET, - IMX8QM_MISC_CLKREQ_OVERRIDE_EN_1, - 0); - break; - case IMX8QM: - case IMX8QM_EP: - if (imx6_pcie->controller_id) - reg = IMX8QM_MISC_CLKREQ_OVERRIDE_EN_1; - else - reg = IMX8QM_MISC_CLKREQ_OVERRIDE_EN_0; - regmap_update_bits(imx6_pcie->iomuxc_gpr, - IMX8QM_CSR_MISC_OFFSET, - reg, 0); - break; - default: - break; - }; - } break; case DW_PCIE_EP_TYPE: if (!IS_ENABLED(CONFIG_PCI_IMX_EP)) @@ -3012,6 +3024,58 @@ static void imx6_pcie_quirk(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd, PCI_CLASS_BRIDGE_PCI, 8, imx6_pcie_quirk); +static void imx6_pcie_l1ss_quirk(struct pci_dev *dev) +{ + u32 reg, rc_l1sub, ep_l1sub, header; + int ttl, ret; + int pos = PCI_CFG_SPACE_SIZE; + struct pci_bus *bus = dev->bus; + struct pcie_port *pp = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); + + /* Return directly, if the L1SS is not supported by RC */ + if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_SUPPORTS_L1SS)) + return; + + /* Make sure the L1SS is not force disabled. */ + if (imx6_pcie->l1ss_clkreq == 0) + return; + + reg = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS); + rc_l1sub = dw_pcie_readl_dbi(pci, reg + PCI_L1SS_CAP); + + /* minimum 8 bytes per capability */ + ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; + ret = dw_pcie_read(pp->va_cfg0_base + pos, 4, &header); + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) + return; + + while (ttl-- > 0) { + if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_L1SS && pos != 0) + break; + + pos = PCI_EXT_CAP_NEXT(header); + if (pos < PCI_CFG_SPACE_SIZE) + break; + + ret = dw_pcie_read(pp->va_cfg0_base + pos, 4, &header); + } + ret = dw_pcie_read(pp->va_cfg0_base + pos + PCI_L1SS_CAP, 4, &ep_l1sub); + + if ((rc_l1sub && ep_l1sub) && PCI_L1SS_CAP_L1_PM_SS) { + imx6_pcie->l1ss_clkreq = 1; + imx6_pcie_clkreq_enable(imx6_pcie); + } else { + imx6_pcie->l1ss_clkreq = 0; + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SYNOPSYS, 0xabcd, imx6_pcie_l1ss_quirk); + static int __init imx6_pcie_init(void) { #ifdef CONFIG_ARM |