summaryrefslogtreecommitdiff
path: root/drivers/pci/controller/dwc/pci-imx6.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/controller/dwc/pci-imx6.c')
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c144
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