#define IMX_PCIE_FLAG_IMX_PHY BIT(0) #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) #define IMX_PCIE_FLAG_HAS_PHY_RESET BIT(5) #define IMX_PCIE_FLAG_HAS_SERDES BIT(6) #define IMX_PCIE_FLAG_SUPPORT_64BIT BIT(7) #define IMX_PCIE_FLAG_CPU_ADDR_FIXUP BIT(8) /* * Because of ERR005723 (PCIe does not support L2 power down) we need to * workaround suspend resume on some devices which are affected by this errata.
*/ #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)
/* 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 */ struct device *pd_pcie_phy; struct phy *phy; conststruct imx_pcie_drvdata *drvdata;
/* Ensure that only one device's LUT is configured at any given time */ struct mutex lock;
};
/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ #define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 #define PHY_PLL_LOCK_WAIT_TIMEOUT (2000 * PHY_PLL_LOCK_WAIT_USLEEP_MAX)
/* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700
staticint 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);
ret = pcie_phy_poll_ack(imx_pcie, true); if (ret) return ret;
*data = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT);
/* deassert Read signal */
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00);
return pcie_phy_poll_ack(imx_pcie, false);
}
staticint pcie_phy_write(struct imx_pcie *imx_pcie, int addr, u16 data)
{ struct dw_pcie *pci = imx_pcie->pci;
u32 var; int ret;
/* write addr */ /* cap addr */
ret = pcie_phy_wait_ack(imx_pcie, addr); if (ret) return ret;
var = PCIE_PHY_CTRL_DATA(data);
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* capture data */
var |= PCIE_PHY_CTRL_CAP_DAT;
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
ret = pcie_phy_poll_ack(imx_pcie, true); if (ret) return ret;
/* deassert cap data */
var = PCIE_PHY_CTRL_DATA(data);
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx_pcie, false); if (ret) return ret;
/* assert wr signal */
var = PCIE_PHY_CTRL_WR;
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack */
ret = pcie_phy_poll_ack(imx_pcie, true); if (ret) return ret;
/* deassert wr signal */
var = PCIE_PHY_CTRL_DATA(data);
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx_pcie, false); if (ret) return ret;
dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0);
return0;
}
staticint imx8mq_pcie_init_phy(struct imx_pcie *imx_pcie)
{ /* TODO: This code assumes external oscillator is being used */
regmap_update_bits(imx_pcie->iomuxc_gpr,
imx_pcie_grp_offset(imx_pcie),
IMX8MQ_GPR_PCIE_REF_USE_PAD,
IMX8MQ_GPR_PCIE_REF_USE_PAD); /* * Per 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 (imx_pcie->vph && regulator_get_voltage(imx_pcie->vph) > 3000000)
regmap_update_bits(imx_pcie->iomuxc_gpr,
imx_pcie_grp_offset(imx_pcie),
IMX8MQ_GPR_PCIE_VREG_BYPASS, 0);
if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_IMX_PHY)) return0;
for (i = 0; i < imx_pcie->num_clks; i++) if (strncmp(clks[i].id, "pcie_phy", 8) == 0)
phy_rate = clk_get_rate(clks[i].clk);
switch (phy_rate) { case125000000: /* * The default settings of the MPLL are for a 125MHz input * clock, so no need to reconfigure anything in that case.
*/ return0; case100000000:
mult = 25;
div = 0; break; case200000000:
mult = 25;
div = 1; break; default:
dev_err(imx_pcie->pci->dev, "Unsupported PHY reference clock rate %lu\n", phy_rate); return -EINVAL;
}
pcie_phy_read(imx_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, &val);
val &= ~(PCIE_PHY_MPLL_MULTIPLIER_MASK <<
PCIE_PHY_MPLL_MULTIPLIER_SHIFT);
val |= mult << PCIE_PHY_MPLL_MULTIPLIER_SHIFT;
val |= PCIE_PHY_MPLL_MULTIPLIER_OVRD;
pcie_phy_write(imx_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, val);
pcie_phy_read(imx_pcie, PCIE_PHY_ATEOVRD, &val);
val &= ~(PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK <<
PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT);
val |= div << PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT;
val |= PCIE_PHY_ATEOVRD_EN;
pcie_phy_write(imx_pcie, PCIE_PHY_ATEOVRD, val);
/* * If the instruction being executed was a read, * make it look like it read all-ones.
*/ if ((instr & 0x0c100000) == 0x04100000) { unsignedlong val;
/* Do nothing when in a single power domain */ if (dev->pm_domain) return0;
imx_pcie->pd_pcie = dev_pm_domain_attach_by_name(dev, "pcie"); if (IS_ERR(imx_pcie->pd_pcie)) return PTR_ERR(imx_pcie->pd_pcie); /* Do nothing when power domain missing */ if (!imx_pcie->pd_pcie) return0;
link = device_link_add(dev, imx_pcie->pd_pcie,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE); if (!link) {
dev_err(dev, "Failed to add device_link to pcie pd\n"); return -EINVAL;
}
imx_pcie->pd_pcie_phy = dev_pm_domain_attach_by_name(dev, "pcie_phy"); if (IS_ERR(imx_pcie->pd_pcie_phy)) return PTR_ERR(imx_pcie->pd_pcie_phy);
link = device_link_add(dev, imx_pcie->pd_pcie_phy,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE); if (!link) {
dev_err(dev, "Failed to add device_link to pcie_phy pd\n"); return -EINVAL;
}
staticint imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{ if (enable) { /* power up core phy and enable ref clock */
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); /* * The async reset input need ref clock to sync internally, * when the ref clock comes after reset, internal synced * reset time is too short, cannot meet the requirement. * Add a ~10us delay here.
*/
usleep_range(10, 100);
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
} else {
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
}
ret = clk_bulk_prepare_enable(imx_pcie->num_clks, imx_pcie->clks); if (ret) return ret;
if (imx_pcie->drvdata->enable_ref_clk) {
ret = imx_pcie->drvdata->enable_ref_clk(imx_pcie, true); if (ret) {
dev_err(dev, "Failed to enable PCIe REFCLK\n"); goto err_ref_clk;
}
}
/* allow the clocks to stabilize */
usleep_range(200, 500); return0;
/* * Workaround for ERR010728 (IMX7DS_2N09P, Rev. 1.1, 4/2023): * * PCIe: PLL may fail to lock under corner conditions. * * Initial VCO oscillation may fail under corner conditions such as * cold temperature which will cause the PCIe PLL fail to lock in the * initialization phase. * * The Duty-cycle Corrector calibration must be disabled. * * 1. De-assert the G_RST signal by clearing * SRC_PCIEPHY_RCR[PCIEPHY_G_RST]. * 2. De-assert DCC_FB_EN by writing data “0x29” to the register * address 0x306d0014 (PCIE_PHY_CMN_REG4). * 3. Assert RX_EQS, RX_EQ_SEL by writing data “0x48” to the register * address 0x306d0090 (PCIE_PHY_CMN_REG24). * 4. Assert ATT_MODE by writing data “0xbc” to the register * address 0x306d0098 (PCIE_PHY_CMN_REG26). * 5. De-assert the CMN_RST signal by clearing register bit * SRC_PCIEPHY_RCR[PCIEPHY_BTN]
*/
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);
}
if (!(imx_pcie->drvdata->flags &
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND)) {
imx_pcie_ltssm_enable(dev); return0;
}
/* * Force Gen1 operation when starting the link. In case the link is * started in Gen2 mode, there is a possibility the devices on the * bus will not be detected at all. This happens with PCIe switches.
*/
dw_pcie_dbi_ro_wr_en(pci);
tmp = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
tmp &= ~PCI_EXP_LNKCAP_SLS;
tmp |= PCI_EXP_LNKCAP_SLS_2_5GB;
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, tmp);
dw_pcie_dbi_ro_wr_dis(pci);
/* Start LTSSM. */
imx_pcie_ltssm_enable(dev);
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);
tmp &= ~PCI_EXP_LNKCAP_SLS;
tmp |= pci->max_link_speed;
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, tmp);
/* * Start Directed Speed Change so the best possible * speed both link partners support can be negotiated.
*/
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
dw_pcie_dbi_ro_wr_dis(pci);
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");
}
if (sid >= 64) {
dev_err(dev, "Invalid SID for index %d\n", sid); return -EINVAL;
}
guard(mutex)(&imx_pcie->lock);
/* * Iterate through all LUT entries to check for duplicate RID and * identify the first available entry. Configure this available entry * immediately after verification to avoid rescanning it.
*/ 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);
if (!(data1 & IMX95_PE0_LUT_VLD)) { if (free < 0)
free = i; continue;
}
/* Do not add duplicate RID */ if (rid == FIELD_GET(IMX95_PE0_LUT_REQID, data2)) {
dev_warn(dev, "Existing LUT entry available for RID (%d)", rid); return0;
}
}
if (free < 0) {
dev_err(dev, "LUT entry is not available\n"); return -ENOSPC;
}
if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE)
data2 = 0x7; /* In the EP mode, only 'Device ID' is required */ else
data2 = IMX95_PE0_LUT_MASK; /* Match all bits of RID */
data2 |= FIELD_PREP(IMX95_PE0_LUT_REQID, rid);
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, data2);
target = NULL;
err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask",
&target, &sid_i); if (target) {
of_node_put(target);
} else { /* * "target == NULL && err_i == 0" means RID out of map range. * Use 1:1 map RID to streamID. Hardware can't support this * because the streamID is only 6 bits
*/
err_i = -EINVAL;
}
/* * err_m target * 0 NULL RID out of range. Use 1:1 map RID to * streamID, Current hardware can't * support it, so return -EINVAL. * != 0 NULL msi-map does not exist, use built-in MSI * 0 != NULL Get correct streamID from RID * != 0 != NULL Invalid combination
*/ if (!err_m && !target) return -EINVAL; elseif (target)
of_node_put(target); /* Find streamID map entry for RID in msi-map */
/* * msi-map iommu-map * N N DWC MSI Ctrl * Y Y ITS + SMMU, require the same SID * Y N ITS * N Y DWC MSI Ctrl + SMMU
*/ if (err_i && err_m) return0;
if (!err_i && !err_m) { /* * Glue Layer * <==========> * ┌─────┐ ┌──────────┐ * │ LUT │ 6-bit streamID │ │ * │ │─────────────────►│ MSI │ * └─────┘ 2-bit ctrl ID │ │ * ┌───────────►│ │ * (i.MX95) │ │ │ * 00 PCIe0 │ │ │ * 01 ENETC │ │ │ * 10 PCIe1 │ │ │ * │ └──────────┘ * The MSI glue layer auto adds 2 bits controller ID ahead of * streamID, so mask these 2 bits to get streamID. The * IOMMU glue layer doesn't do that.
*/ if (sid_i != (sid_m & IMX95_SID_MASK)) {
dev_err(dev, "iommu-map and msi-map entries mismatch!\n"); return -EINVAL;
}
}
if (imx_pcie->phy) { if (phy_power_off(imx_pcie->phy))
dev_err(pci->dev, "unable to power off PHY\n");
phy_exit(imx_pcie->phy);
}
imx_pcie_clk_disable(imx_pcie);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
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 * PME_Turn_Off message using a dummy MMIO write cannot be used.
*/ staticvoid imx_pcie_pme_turn_off(struct dw_pcie_rp *pp)
{ struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx_pcie *imx_pcie = to_imx_pcie(pci);
if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND)) return0;
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 * set the PCIE_TEST_PD flag. However, we can also disable the * clock which saves some power.
*/
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie->drvdata->enable_ref_clk(imx_pcie, false);
} else { return dw_pcie_suspend_noirq(imx_pcie->pci);
}
if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND)) return0;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_BROKEN_SUSPEND)) {
ret = imx_pcie->drvdata->enable_ref_clk(imx_pcie, true); if (ret) return ret;
ret = imx_pcie_deassert_core_reset(imx_pcie); if (ret) return ret;
/* * Using PCIE_TEST_PD seems to disable MSI and powers down the * root complex. This is why we have to setup the rc again and * why we have to restore the MSI register.
*/
ret = dw_pcie_setup_rc(&imx_pcie->pci->pp); if (ret) return ret;
} else {
ret = dw_pcie_resume_noirq(imx_pcie->pci); 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);
if (imx_pcie->drvdata->ops)
pci->pp.ops = imx_pcie->drvdata->ops; else
pci->pp.ops = &imx_pcie_host_dw_pme_ops;
/* Find the PHY if one is defined, only imx7d uses it */
np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); if (np) { struct resource res;
ret = of_address_to_resource(np, 0, &res); if (ret) {
dev_err(dev, "Unable to map PCIe PHY\n"); return ret;
}
imx_pcie->phy_base = devm_ioremap_resource(dev, &res); if (IS_ERR(imx_pcie->phy_base)) return PTR_ERR(imx_pcie->phy_base);
}
/* Fetch GPIOs */
imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(imx_pcie->reset_gpiod)) return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod), "unable to get reset gpio\n");
gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset");
/* Fetch clocks */
imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks); if (imx_pcie->num_clks < 0) return dev_err_probe(dev, imx_pcie->num_clks, "failed to get clocks\n");
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) {
imx_pcie->phy = devm_phy_get(dev, "pcie-phy"); if (IS_ERR(imx_pcie->phy)) return dev_err_probe(dev, PTR_ERR(imx_pcie->phy), "failed to get pcie phy\n");
}
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_APP_RESET)) {
imx_pcie->apps_reset = devm_reset_control_get_exclusive(dev, "apps"); if (IS_ERR(imx_pcie->apps_reset)) return dev_err_probe(dev, PTR_ERR(imx_pcie->apps_reset), "failed to get pcie apps reset control\n");
}
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHY_RESET)) {
imx_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, "pciephy"); if (IS_ERR(imx_pcie->pciephy_reset)) return dev_err_probe(dev, PTR_ERR(imx_pcie->pciephy_reset), "Failed to get PCIEPHY reset control\n");
}
switch (imx_pcie->drvdata->variant) { case IMX8MQ: case IMX8MQ_EP:
domain = of_get_pci_domain_nr(node); if (domain < 0 || domain > 1) return dev_err_probe(dev, -ENODEV, "no \"linux,pci-domain\" property in devicetree\n");
ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); if (ret < 0 && ret != -ENODEV) return dev_err_probe(dev, ret, "failed to enable Vaux supply\n");
imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); if (IS_ERR(imx_pcie->vpcie)) { if (PTR_ERR(imx_pcie->vpcie) != -ENODEV) return PTR_ERR(imx_pcie->vpcie);
imx_pcie->vpcie = NULL;
}
imx_pcie->vph = devm_regulator_get_optional(&pdev->dev, "vph"); if (IS_ERR(imx_pcie->vph)) { if (PTR_ERR(imx_pcie->vph) != -ENODEV) return PTR_ERR(imx_pcie->vph);
imx_pcie->vph = NULL;
}
platform_set_drvdata(pdev, imx_pcie);
ret = imx_pcie_attach_pd(dev); if (ret) return ret;
pci->use_parent_dt_ranges = true; if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
ret = imx_add_pcie_ep(imx_pcie, pdev); if (ret < 0) return ret;
/* * FIXME: Only single Device (EPF) is supported due to the * Endpoint framework limitation.
*/
imx_pcie_add_lut_by_rid(imx_pcie, 0);
} else {
pci->pp.use_atu_msg = true;
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);
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);
}
}
/* * Limit config length to avoid the kernel reading beyond * the register set and causing an abort on i.MX 6Quad
*/ if (imx_pcie->drvdata->dbi_length) {
dev->cfg_size = imx_pcie->drvdata->dbi_length;
dev_info(&dev->dev, "Limiting cfg_size to %d\n",
dev->cfg_size);
}
}
}
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd,
PCI_CLASS_BRIDGE_PCI, 8, imx_pcie_quirk);
np = of_find_matching_node(NULL, imx_pcie_of_match); if (!np) return -ENODEV;
of_node_put(np);
/* * 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"); #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.