mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
Merge branch 'pci/controller/imx6'
- Apply link training workaround only on IMX6Q, IMX6SX, IMX6SP (Richard Zhu) - Remove redundant dw_pcie_wait_for_link() from imx_pcie_start_link(); since the DWC core does this, imx6 only needs it when retraining for a faster link speed (Richard Zhu) - Toggle i.MX95 core reset to align with PHY powerup (Richard Zhu) - Set SYS_AUX_PWR_DET to work around i.MX95 ERR051624 erratum: in some cases, the controller can't exit 'L23 Ready' through Beacon or PERST# deassertion (Richard Zhu) - Clear GEN3_ZRXDC_NONCOMPL to work around i.MX95 ERR051586 erratum: controller can't meet 2.5 GT/s ZRX-DC timing when operating at 8 GT/s, causing timeouts in L1 (Richard Zhu) - Wait for i.MX95 PLL lock before enabling controller (Richard Zhu) - Save/restore i.MX95 LUT for suspend/resume (Richard Zhu) * pci/controller/imx6: PCI: imx6: Save and restore the LUT setting during suspend/resume for i.MX95 SoC PCI: imx6: Add PLL lock check for i.MX95 SoC PCI: imx6: Add workaround for errata ERR051586 PCI: imx6: Add workaround for errata ERR051624 PCI: imx6: Toggle the core reset for i.MX95 PCIe PCI: imx6: Call dw_pcie_wait_for_link() from start_link() callback only when required PCI: imx6: Skip link up workaround for newer platforms
This commit is contained in:
commit
f4ff0b0ed2
|
|
@ -45,9 +45,14 @@
|
|||
#define IMX95_PCIE_PHY_GEN_CTRL 0x0
|
||||
#define IMX95_PCIE_REF_USE_PAD BIT(17)
|
||||
|
||||
#define IMX95_PCIE_PHY_MPLLA_CTRL 0x10
|
||||
#define IMX95_PCIE_PHY_MPLL_STATE BIT(30)
|
||||
|
||||
#define IMX95_PCIE_SS_RW_REG_0 0xf0
|
||||
#define IMX95_PCIE_REF_CLKEN BIT(23)
|
||||
#define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9)
|
||||
#define IMX95_PCIE_SS_RW_REG_1 0xf4
|
||||
#define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31)
|
||||
|
||||
#define IMX95_PE0_GEN_CTRL_1 0x1050
|
||||
#define IMX95_PCIE_DEVICE_TYPE GENMASK(3, 0)
|
||||
|
|
@ -71,6 +76,9 @@
|
|||
#define IMX95_SID_MASK GENMASK(5, 0)
|
||||
#define IMX95_MAX_LUT 32
|
||||
|
||||
#define IMX95_PCIE_RST_CTRL 0x3010
|
||||
#define IMX95_PCIE_COLD_RST BIT(0)
|
||||
|
||||
#define to_imx_pcie(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
enum imx_pcie_variants {
|
||||
|
|
@ -91,7 +99,7 @@ enum imx_pcie_variants {
|
|||
};
|
||||
|
||||
#define IMX_PCIE_FLAG_IMX_PHY BIT(0)
|
||||
#define IMX_PCIE_FLAG_IMX_SPEED_CHANGE BIT(1)
|
||||
#define IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND BIT(1)
|
||||
#define IMX_PCIE_FLAG_SUPPORTS_SUSPEND BIT(2)
|
||||
#define IMX_PCIE_FLAG_HAS_PHYDRV BIT(3)
|
||||
#define IMX_PCIE_FLAG_HAS_APP_RESET BIT(4)
|
||||
|
|
@ -105,6 +113,7 @@ enum imx_pcie_variants {
|
|||
*/
|
||||
#define IMX_PCIE_FLAG_BROKEN_SUSPEND BIT(9)
|
||||
#define IMX_PCIE_FLAG_HAS_LUT BIT(10)
|
||||
#define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11)
|
||||
|
||||
#define imx_check_flag(pci, val) (pci->drvdata->flags & val)
|
||||
|
||||
|
|
@ -126,9 +135,15 @@ struct imx_pcie_drvdata {
|
|||
int (*init_phy)(struct imx_pcie *pcie);
|
||||
int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
|
||||
int (*core_reset)(struct imx_pcie *pcie, bool assert);
|
||||
int (*wait_pll_lock)(struct imx_pcie *pcie);
|
||||
const struct dw_pcie_host_ops *ops;
|
||||
};
|
||||
|
||||
struct imx_lut_data {
|
||||
u32 data1;
|
||||
u32 data2;
|
||||
};
|
||||
|
||||
struct imx_pcie {
|
||||
struct dw_pcie *pci;
|
||||
struct gpio_desc *reset_gpiod;
|
||||
|
|
@ -148,6 +163,8 @@ struct imx_pcie {
|
|||
struct regulator *vph;
|
||||
void __iomem *phy_base;
|
||||
|
||||
/* LUT data for pcie */
|
||||
struct imx_lut_data luts[IMX95_MAX_LUT];
|
||||
/* power domain for pcie */
|
||||
struct device *pd_pcie;
|
||||
/* power domain for pcie phy */
|
||||
|
|
@ -224,6 +241,19 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie)
|
|||
|
||||
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
/*
|
||||
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
|
||||
* Through Beacon or PERST# De-assertion
|
||||
*
|
||||
* When the auxiliary power is not available, the controller
|
||||
* cannot exit from L23 Ready with beacon or PERST# de-assertion
|
||||
* when main power is not removed.
|
||||
*
|
||||
* Workaround: Set SS_RW_REG_1[SYS_AUX_PWR_DET] to 1.
|
||||
*/
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1,
|
||||
IMX95_PCIE_SYS_AUX_PWR_DET);
|
||||
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr,
|
||||
IMX95_PCIE_SS_RW_REG_0,
|
||||
IMX95_PCIE_PHY_CR_PARA_SEL,
|
||||
|
|
@ -460,6 +490,23 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx_pcie *imx_pcie)
|
|||
dev_err(dev, "PCIe PLL lock timeout\n");
|
||||
}
|
||||
|
||||
static int imx95_pcie_wait_for_phy_pll_lock(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
u32 val;
|
||||
struct device *dev = imx_pcie->pci->dev;
|
||||
|
||||
if (regmap_read_poll_timeout(imx_pcie->iomuxc_gpr,
|
||||
IMX95_PCIE_PHY_MPLLA_CTRL, val,
|
||||
val & IMX95_PCIE_PHY_MPLL_STATE,
|
||||
PHY_PLL_LOCK_WAIT_USLEEP_MAX,
|
||||
PHY_PLL_LOCK_WAIT_TIMEOUT)) {
|
||||
dev_err(dev, "PCIe PLL lock timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_setup_phy_mpll(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
unsigned long phy_rate = 0;
|
||||
|
|
@ -773,6 +820,43 @@ static int imx7d_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int imx95_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (assert) {
|
||||
/*
|
||||
* From i.MX95 PCIe PHY perspective, the COLD reset toggle
|
||||
* should be complete after power-up by the following sequence.
|
||||
* > 10us(at power-up)
|
||||
* > 10ns(warm reset)
|
||||
* |<------------>|
|
||||
* ______________
|
||||
* phy_reset ____/ \________________
|
||||
* ____________
|
||||
* ref_clk_en_______________________/
|
||||
* Toggle COLD reset aligned with this sequence for i.MX95 PCIe.
|
||||
*/
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
|
||||
IMX95_PCIE_COLD_RST);
|
||||
/*
|
||||
* Make sure the write to IMX95_PCIE_RST_CTRL is flushed to the
|
||||
* hardware by doing a read. Otherwise, there is no guarantee
|
||||
* that the write has reached the hardware before udelay().
|
||||
*/
|
||||
regmap_read_bypassed(imx_pcie->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
|
||||
&val);
|
||||
udelay(15);
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
|
||||
IMX95_PCIE_COLD_RST);
|
||||
regmap_read_bypassed(imx_pcie->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
|
||||
&val);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
reset_control_assert(imx_pcie->pciephy_reset);
|
||||
|
|
@ -860,6 +944,12 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
|
|||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
if (!(imx_pcie->drvdata->flags &
|
||||
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND)) {
|
||||
imx_pcie_ltssm_enable(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force Gen1 operation when starting the link. In case the link is
|
||||
* started in Gen2 mode, there is a possibility the devices on the
|
||||
|
|
@ -875,11 +965,11 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
|
|||
/* Start LTSSM. */
|
||||
imx_pcie_ltssm_enable(dev);
|
||||
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
if (ret)
|
||||
goto err_reset_phy;
|
||||
|
||||
if (pci->max_link_speed > 1) {
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
if (ret)
|
||||
goto err_reset_phy;
|
||||
|
||||
/* Allow faster modes after the link is up */
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
tmp = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
|
||||
|
|
@ -896,34 +986,15 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
|
|||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
if (imx_pcie->drvdata->flags &
|
||||
IMX_PCIE_FLAG_IMX_SPEED_CHANGE) {
|
||||
|
||||
/*
|
||||
* On i.MX7, DIRECT_SPEED_CHANGE behaves differently
|
||||
* from i.MX6 family when no link speed transition
|
||||
* occurs and we go Gen1 -> yep, Gen1. The difference
|
||||
* is that, in such case, it will not be cleared by HW
|
||||
* which will cause the following code to report false
|
||||
* failure.
|
||||
*/
|
||||
ret = imx_pcie_wait_for_speed_change(imx_pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to bring link up!\n");
|
||||
goto err_reset_phy;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure link training is finished as well! */
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
if (ret)
|
||||
ret = imx_pcie_wait_for_speed_change(imx_pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to bring link up!\n");
|
||||
goto err_reset_phy;
|
||||
}
|
||||
} else {
|
||||
dev_info(dev, "Link: Only Gen1 is enabled\n");
|
||||
}
|
||||
|
||||
tmp = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
|
||||
dev_info(dev, "Link up, Gen%i\n", tmp & PCI_EXP_LNKSTA_CLS);
|
||||
return 0;
|
||||
|
||||
err_reset_phy:
|
||||
|
|
@ -1182,6 +1253,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
goto err_phy_off;
|
||||
}
|
||||
|
||||
if (imx_pcie->drvdata->wait_pll_lock) {
|
||||
ret = imx_pcie->drvdata->wait_pll_lock(imx_pcie);
|
||||
if (ret < 0)
|
||||
goto err_phy_off;
|
||||
}
|
||||
|
||||
imx_setup_phy_mpll(imx_pcie);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1214,6 +1291,32 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
|
|||
regulator_disable(imx_pcie->vpcie);
|
||||
}
|
||||
|
||||
static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
|
||||
u32 val;
|
||||
|
||||
if (imx_pcie->drvdata->flags & IMX_PCIE_FLAG_8GT_ECN_ERR051586) {
|
||||
/*
|
||||
* ERR051586: Compliance with 8GT/s Receiver Impedance ECN
|
||||
*
|
||||
* The default value of GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
|
||||
* is 1 which makes receiver non-compliant with the ZRX-DC
|
||||
* parameter for 2.5 GT/s when operating at 8 GT/s or higher.
|
||||
* It causes unnecessary timeout in L1.
|
||||
*
|
||||
* Workaround: Program GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
|
||||
* to 0.
|
||||
*/
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In old DWC implementations, PCIE_ATU_INHIBIT_PAYLOAD in iATU Ctrl2
|
||||
* register is reserved, so the generic DWC implementation of sending the
|
||||
|
|
@ -1239,6 +1342,7 @@ static const struct dw_pcie_host_ops imx_pcie_host_ops = {
|
|||
static const struct dw_pcie_host_ops imx_pcie_host_dw_pme_ops = {
|
||||
.init = imx_pcie_host_init,
|
||||
.deinit = imx_pcie_host_exit,
|
||||
.post_init = imx_pcie_host_post_init,
|
||||
};
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
|
|
@ -1350,6 +1454,7 @@ static int imx_add_pcie_ep(struct imx_pcie *imx_pcie,
|
|||
dev_err(dev, "failed to initialize endpoint\n");
|
||||
return ret;
|
||||
}
|
||||
imx_pcie_host_post_init(pp);
|
||||
|
||||
ret = dw_pcie_ep_init_registers(ep);
|
||||
if (ret) {
|
||||
|
|
@ -1386,6 +1491,42 @@ static void imx_pcie_msi_save_restore(struct imx_pcie *imx_pcie, bool save)
|
|||
}
|
||||
}
|
||||
|
||||
static void imx_pcie_lut_save(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
u32 data1, data2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMX95_MAX_LUT; i++) {
|
||||
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL,
|
||||
IMX95_PEO_LUT_RWA | i);
|
||||
regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, &data1);
|
||||
regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, &data2);
|
||||
if (data1 & IMX95_PE0_LUT_VLD) {
|
||||
imx_pcie->luts[i].data1 = data1;
|
||||
imx_pcie->luts[i].data2 = data2;
|
||||
} else {
|
||||
imx_pcie->luts[i].data1 = 0;
|
||||
imx_pcie->luts[i].data2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_pcie_lut_restore(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMX95_MAX_LUT; i++) {
|
||||
if ((imx_pcie->luts[i].data1 & IMX95_PE0_LUT_VLD) == 0)
|
||||
continue;
|
||||
|
||||
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1,
|
||||
imx_pcie->luts[i].data1);
|
||||
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2,
|
||||
imx_pcie->luts[i].data2);
|
||||
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_pcie_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
|
||||
|
|
@ -1394,6 +1535,8 @@ static int imx_pcie_suspend_noirq(struct device *dev)
|
|||
return 0;
|
||||
|
||||
imx_pcie_msi_save_restore(imx_pcie, true);
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT))
|
||||
imx_pcie_lut_save(imx_pcie);
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_BROKEN_SUSPEND)) {
|
||||
/*
|
||||
* The minimum for a workaround would be to set PERST# and to
|
||||
|
|
@ -1438,6 +1581,8 @@ static int imx_pcie_resume_noirq(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT))
|
||||
imx_pcie_lut_restore(imx_pcie);
|
||||
imx_pcie_msi_save_restore(imx_pcie, false);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1649,7 +1794,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
|
|||
[IMX6Q] = {
|
||||
.variant = IMX6Q,
|
||||
.flags = IMX_PCIE_FLAG_IMX_PHY |
|
||||
IMX_PCIE_FLAG_IMX_SPEED_CHANGE |
|
||||
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
|
||||
IMX_PCIE_FLAG_BROKEN_SUSPEND |
|
||||
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
|
||||
.dbi_length = 0x200,
|
||||
|
|
@ -1665,7 +1810,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
|
|||
[IMX6SX] = {
|
||||
.variant = IMX6SX,
|
||||
.flags = IMX_PCIE_FLAG_IMX_PHY |
|
||||
IMX_PCIE_FLAG_IMX_SPEED_CHANGE |
|
||||
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
|
||||
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
|
||||
.gpr = "fsl,imx6q-iomuxc-gpr",
|
||||
.ltssm_off = IOMUXC_GPR12,
|
||||
|
|
@ -1680,7 +1825,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
|
|||
[IMX6QP] = {
|
||||
.variant = IMX6QP,
|
||||
.flags = IMX_PCIE_FLAG_IMX_PHY |
|
||||
IMX_PCIE_FLAG_IMX_SPEED_CHANGE |
|
||||
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
|
||||
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
|
||||
.dbi_length = 0x200,
|
||||
.gpr = "fsl,imx6q-iomuxc-gpr",
|
||||
|
|
@ -1747,12 +1892,15 @@ static const struct imx_pcie_drvdata drvdata[] = {
|
|||
.variant = IMX95,
|
||||
.flags = IMX_PCIE_FLAG_HAS_SERDES |
|
||||
IMX_PCIE_FLAG_HAS_LUT |
|
||||
IMX_PCIE_FLAG_8GT_ECN_ERR051586 |
|
||||
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
|
||||
.ltssm_off = IMX95_PE0_GEN_CTRL_3,
|
||||
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
|
||||
.mode_off[0] = IMX95_PE0_GEN_CTRL_1,
|
||||
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
|
||||
.core_reset = imx95_pcie_core_reset,
|
||||
.init_phy = imx95_pcie_init_phy,
|
||||
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
|
||||
},
|
||||
[IMX8MQ_EP] = {
|
||||
.variant = IMX8MQ_EP,
|
||||
|
|
@ -1799,12 +1947,15 @@ static const struct imx_pcie_drvdata drvdata[] = {
|
|||
[IMX95_EP] = {
|
||||
.variant = IMX95_EP,
|
||||
.flags = IMX_PCIE_FLAG_HAS_SERDES |
|
||||
IMX_PCIE_FLAG_8GT_ECN_ERR051586 |
|
||||
IMX_PCIE_FLAG_SUPPORT_64BIT,
|
||||
.ltssm_off = IMX95_PE0_GEN_CTRL_3,
|
||||
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
|
||||
.mode_off[0] = IMX95_PE0_GEN_CTRL_1,
|
||||
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
|
||||
.init_phy = imx95_pcie_init_phy,
|
||||
.core_reset = imx95_pcie_core_reset,
|
||||
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
|
||||
.epc_features = &imx95_pcie_epc_features,
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user