Fixed 8mp EP mode problem.
imx6 actaully for all imx chips (imx6*, imx7*, imx8*, imx9*). To avoid
confuse, rename all imx6_* to imx_*, IMX6_* to IMX_*. pci-imx6.c to
pci-imx.c to avoid confuse.
Using callback to reduce switch case for core reset and refclk.
Add imx95 iommux and its stream id information.
Base on linux-pci/controller/imx
To: Richard Zhu <[email protected]>
To: Lucas Stach <[email protected]>
To: Lorenzo Pieralisi <[email protected]>
To: Krzysztof Wilczyński <[email protected]>
To: Rob Herring <[email protected]>
To: Bjorn Helgaas <[email protected]>
To: Shawn Guo <[email protected]>
To: Sascha Hauer <[email protected]>
To: Pengutronix Kernel Team <[email protected]>
To: Fabio Estevam <[email protected]>
To: NXP Linux Team <[email protected]>
To: Philipp Zabel <[email protected]>
To: Liam Girdwood <[email protected]>
To: Mark Brown <[email protected]>
To: Manivannan Sadhasivam <[email protected]>
To: Krzysztof Kozlowski <[email protected]>
To: Conor Dooley <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Frank Li <[email protected]>
Changes in v5:
- Rebase to linux-pci next. fix conflict with gpiod change
- Add rob and cornor's review tag
- Link to v4: https://lore.kernel.org/r/[email protected]
Changes in v4:
- Improve comment message for patch 1 and 2.
- Rework commit message for patch 3 and add mani's review tag
- Remove file rename patch and update maintainer patch
- [PATCH v3 06/11] PCI: imx: Simplify switch-case logic by involve set_ref_clk callback
remove extra space.
keep original comments format (wrap at 80 column width)
update error message "'Failed to enable PCIe REFCLK'"
- PATCH v3 07/11] PCI: imx: Simplify switch-case logic by involve core_reset callback
keep exact the logic as original code
- Add patch to update comment about workaround ERR010728
- Add patch about help function imx_pcie_match_device()
- Using bus device notify to update LUT information for imx95 to avoid
parse iommu-map and msi-map in driver code. Bus notify will better and
only update lut when device added.
- split patch call PHY interface function.
- Improve commit message for imx8q. remove local-address dts proptery. and
use standard "range" to convert cpu address to bus address.
- Check entry in cpu_fix function is too late. Check it at probe
- Link to v3: https://lore.kernel.org/r/[email protected]
Changes in v3:
- Add an EP fixed patch
PCI: imx6: Fix PCIe link down when i.MX8MM and i.MX8MP PCIe is EP mode
PCI: imx6: Fix i.MX8MP PCIe EP can not trigger MSI
- Add 8qxp rc support
dt-bing yaml pass binding check
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 dt_binding_check DT_SCHEMA_FILES=fsl,imx6q-pcie.yaml
LINT Documentation/devicetree/bindings
DTEX Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.example.dts
CHKDT Documentation/devicetree/bindings/processed-schema.json
SCHEMA Documentation/devicetree/bindings/processed-schema.json
DTC_CHK Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.example.dtb
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- remove file to 'pcie-imx.c'
- keep CONFIG unchange.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Frank Li (8):
PCI: imx6: Rename imx6_* with imx_*
PCI: imx6: Introduce SoC specific callbacks for controlling REFCLK
PCI: imx6: Simplify switch-case logic by involve core_reset callback
PCI: imx6: Improve comment for workaround ERR010728
PCI: imx6: Add help function imx_pcie_match_device()
PCI: imx6: Config look up table(LUT) to support MSI ITS and IOMMU for i.MX95
PCI: imx6: Consolidate redundant if-checks
PCI: imx6: Call: Common PHY API to set mode, speed, and submode
Richard Zhu (4):
PCI: imx6: Fix establish link failure in EP mode for iMX8MM and iMX8MP
PCI: imx6: Fix i.MX8MP PCIe EP's occasional failure to trigger MSI
dt-bindings: imx6q-pcie: Add i.MX8Q pcie compatible string
PCI: imx6: Add i.MX8Q PCIe root complex (RC) support
.../devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 16 +
drivers/pci/controller/dwc/pci-imx6.c | 1181 ++++++++++++--------
2 files changed, 730 insertions(+), 467 deletions(-)
---
base-commit: 50d96936b7b1be01bcc7b9ff898191214ee72697
change-id: 20240227-pci2_upstream-0cdd19a15163
Best regards,
---
Frank Li <[email protected]>
Instead of using the switch case statement to enable/disable the reference
clock handled by this driver itself, let's introduce a new callback
set_ref_clk() and define it for platforms that require it. This simplifies
the code.
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 112 ++++++++++++++++------------------
1 file changed, 52 insertions(+), 60 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 72e973312d203..c5d490afa981e 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -102,6 +102,7 @@ struct imx_pcie_drvdata {
const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
const struct pci_epc_features *epc_features;
int (*init_phy)(struct imx_pcie *pcie);
+ int (*set_ref_clk)(struct imx_pcie *pcie, bool enable);
};
struct imx_pcie {
@@ -583,21 +584,19 @@ static int imx_pcie_attach_pd(struct device *dev)
return 0;
}
-static int imx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie)
+static int imx6sx_pcie_set_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
- unsigned int offset;
- int ret = 0;
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
+ enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
- switch (imx_pcie->drvdata->variant) {
- case IMX6SX:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
- break;
- case IMX6QP:
- case IMX6Q:
+ return 0;
+}
+
+static int imx6q_pcie_set_ref_clk(struct imx_pcie *imx_pcie, bool enable)
+{
+ if (enable) {
/* power up core phy and enable ref clock */
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, 0);
/*
* the async reset input need ref clock to sync internally,
* when the ref clock comes after reset, internal synced
@@ -606,54 +605,34 @@ static int imx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie)
*/
usleep_range(10, 100);
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
- break;
- case IMX7D:
- case IMX95:
- case IMX95_EP:
- break;
- case IMX8MM:
- case IMX8MM_EP:
- case IMX8MQ:
- case IMX8MQ_EP:
- case IMX8MP:
- case IMX8MP_EP:
- offset = imx_pcie_grp_offset(imx_pcie);
- /*
- * Set the over ride low and enabled
- * make sure that REF_CLK is turned on.
- */
- regmap_update_bits(imx_pcie->iomuxc_gpr, offset,
- IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
- 0);
- regmap_update_bits(imx_pcie->iomuxc_gpr, offset,
- IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
- IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN);
- break;
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, IMX6Q_GPR1_PCIE_REF_CLK_EN);
+ } else {
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN, 0);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD, IMX6Q_GPR1_PCIE_TEST_PD);
}
- return ret;
+ return 0;
}
-static void imx_pcie_disable_ref_clk(struct imx_pcie *imx_pcie)
+static int imx8mm_pcie_set_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
- switch (imx_pcie->drvdata->variant) {
- case IMX6QP:
- case IMX6Q:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_REF_CLK_EN, 0);
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_TEST_PD,
- IMX6Q_GPR1_PCIE_TEST_PD);
- break;
- case IMX7D:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
- IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
- break;
- default:
- break;
- }
+ int offset = imx_pcie_grp_offset(imx_pcie);
+
+ /* Set the over ride low and enabled make sure that REF_CLK is turned on.*/
+ regmap_update_bits(imx_pcie->iomuxc_gpr, offset, IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE,
+ enable ? 0 : IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, offset, IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
+ enable ? IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0);
+ return 0;
+}
+
+static int imx7d_pcie_set_ref_clk(struct imx_pcie *imx_pcie, bool enable)
+{
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
+ enable ? 0 : IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
+ return 0;
}
static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie)
@@ -666,10 +645,12 @@ static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie)
if (ret)
return ret;
- ret = imx_pcie_enable_ref_clk(imx_pcie);
- if (ret) {
- dev_err(dev, "unable to enable pcie ref clock\n");
- goto err_ref_clk;
+ if (imx_pcie->drvdata->set_ref_clk) {
+ ret = imx_pcie->drvdata->set_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 */
@@ -684,7 +665,8 @@ static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie)
static void imx_pcie_clk_disable(struct imx_pcie *imx_pcie)
{
- imx_pcie_disable_ref_clk(imx_pcie);
+ if (imx_pcie->drvdata->set_ref_clk)
+ imx_pcie->drvdata->set_ref_clk(imx_pcie, false);
clk_bulk_disable_unprepare(imx_pcie->drvdata->clks_cnt, imx_pcie->clks);
}
@@ -1459,6 +1441,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx_pcie_init_phy,
+ .set_ref_clk = imx6q_pcie_set_ref_clk,
},
[IMX6SX] = {
.variant = IMX6SX,
@@ -1473,6 +1456,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx6sx_pcie_init_phy,
+ .set_ref_clk = imx6sx_pcie_set_ref_clk,
},
[IMX6QP] = {
.variant = IMX6QP,
@@ -1488,6 +1472,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx_pcie_init_phy,
+ .set_ref_clk = imx6q_pcie_set_ref_clk,
},
[IMX7D] = {
.variant = IMX7D,
@@ -1500,6 +1485,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx7d_pcie_init_phy,
+ .set_ref_clk = imx7d_pcie_set_ref_clk,
},
[IMX8MQ] = {
.variant = IMX8MQ,
@@ -1513,6 +1499,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[1] = IOMUXC_GPR12,
.mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
.init_phy = imx8mq_pcie_init_phy,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX8MM] = {
.variant = IMX8MM,
@@ -1524,6 +1511,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.clks_cnt = ARRAY_SIZE(imx8mm_clks),
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX8MP] = {
.variant = IMX8MP,
@@ -1535,6 +1523,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.clks_cnt = ARRAY_SIZE(imx8mm_clks),
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX95] = {
.variant = IMX95,
@@ -1561,6 +1550,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
.epc_features = &imx8m_pcie_epc_features,
.init_phy = imx8mq_pcie_init_phy,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX8MM_EP] = {
.variant = IMX8MM_EP,
@@ -1573,6 +1563,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.epc_features = &imx8m_pcie_epc_features,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX8MP_EP] = {
.variant = IMX8MP_EP,
@@ -1585,6 +1576,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.epc_features = &imx8m_pcie_epc_features,
+ .set_ref_clk = imx8mm_pcie_set_ref_clk,
},
[IMX95_EP] = {
.variant = IMX95_EP,
--
2.34.1
Instead of using the switch case statement to assert/dassert the core reset
handled by this driver itself, let's introduce a new callback core_reset()
and define it for platforms that require it. This simplifies the code.
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 134 ++++++++++++++++++----------------
1 file changed, 71 insertions(+), 63 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index c5d490afa981e..5e21fc942e90e 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -103,6 +103,7 @@ struct imx_pcie_drvdata {
const struct pci_epc_features *epc_features;
int (*init_phy)(struct imx_pcie *pcie);
int (*set_ref_clk)(struct imx_pcie *pcie, bool enable);
+ int (*core_reset)(struct imx_pcie *pcie, bool assert);
};
struct imx_pcie {
@@ -670,35 +671,75 @@ static void imx_pcie_clk_disable(struct imx_pcie *imx_pcie)
clk_bulk_disable_unprepare(imx_pcie->drvdata->clks_cnt, imx_pcie->clks);
}
+static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
+{
+ if (assert)
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
+
+ /* Force PCIe PHY reset */
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
+ assert ? IMX6SX_GPR5_PCIE_BTNRST_RESET : 0);
+ return 0;
+}
+
+static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
+{
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST,
+ assert ? IMX6Q_GPR1_PCIE_SW_RST : 0);
+ if (!assert)
+ usleep_range(200, 500);
+
+ return 0;
+}
+
+static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
+{
+ if (!assert)
+ return 0;
+
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
+
+ return 0;
+}
+
+static int imx7d_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
+{
+ struct dw_pcie *pci = imx_pcie->pci;
+ struct device *dev = pci->dev;
+
+ if (assert)
+ return 0;
+
+ /*
+ * Workaround for ERR010728, failure of PCI-e PLL VCO to
+ * oscillate, especially when cold. This turns off "Duty-cycle
+ * Corrector" and other mysterious undocumented things.
+ */
+
+ if (likely(imx_pcie->phy_base)) {
+ /* De-assert DCC_FB_EN */
+ writel(PCIE_PHY_CMN_REG4_DCC_FB_EN, imx_pcie->phy_base + PCIE_PHY_CMN_REG4);
+ /* Assert RX_EQS and RX_EQS_SEL */
+ writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL | PCIE_PHY_CMN_REG24_RX_EQ,
+ imx_pcie->phy_base + PCIE_PHY_CMN_REG24);
+ /* Assert ATT_MODE */
+ writel(PCIE_PHY_CMN_REG26_ATT_MODE, imx_pcie->phy_base + PCIE_PHY_CMN_REG26);
+ } else {
+ dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n");
+ }
+ imx7d_pcie_wait_for_phy_pll_lock(imx_pcie);
+ return 0;
+}
+
static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie)
{
reset_control_assert(imx_pcie->pciephy_reset);
reset_control_assert(imx_pcie->apps_reset);
- switch (imx_pcie->drvdata->variant) {
- case IMX6SX:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
- IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
- /* Force PCIe PHY reset */
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5,
- IMX6SX_GPR5_PCIE_BTNRST_RESET,
- IMX6SX_GPR5_PCIE_BTNRST_RESET);
- break;
- case IMX6QP:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_SW_RST,
- IMX6Q_GPR1_PCIE_SW_RST);
- break;
- case IMX6Q:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
- break;
- default:
- break;
- }
+ if (imx_pcie->drvdata->core_reset)
+ imx_pcie->drvdata->core_reset(imx_pcie, true);
/* Some boards don't have PCIe reset GPIO. */
gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1);
@@ -706,47 +747,10 @@ static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie)
static int imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie)
{
- struct dw_pcie *pci = imx_pcie->pci;
- struct device *dev = pci->dev;
-
reset_control_deassert(imx_pcie->pciephy_reset);
- switch (imx_pcie->drvdata->variant) {
- case IMX7D:
- /* Workaround for ERR010728, failure of PCI-e PLL VCO to
- * oscillate, especially when cold. This turns off "Duty-cycle
- * Corrector" and other mysterious undocumented things.
- */
- if (likely(imx_pcie->phy_base)) {
- /* De-assert DCC_FB_EN */
- writel(PCIE_PHY_CMN_REG4_DCC_FB_EN,
- imx_pcie->phy_base + PCIE_PHY_CMN_REG4);
- /* Assert RX_EQS and RX_EQS_SEL */
- writel(PCIE_PHY_CMN_REG24_RX_EQ_SEL
- | PCIE_PHY_CMN_REG24_RX_EQ,
- imx_pcie->phy_base + PCIE_PHY_CMN_REG24);
- /* Assert ATT_MODE */
- writel(PCIE_PHY_CMN_REG26_ATT_MODE,
- imx_pcie->phy_base + PCIE_PHY_CMN_REG26);
- } else {
- dev_warn(dev, "Unable to apply ERR010728 workaround. DT missing fsl,imx7d-pcie-phy phandle ?\n");
- }
-
- imx7d_pcie_wait_for_phy_pll_lock(imx_pcie);
- break;
- case IMX6SX:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5,
- IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
- break;
- case IMX6QP:
- regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
- IMX6Q_GPR1_PCIE_SW_RST, 0);
-
- usleep_range(200, 500);
- break;
- default:
- break;
- }
+ if (imx_pcie->drvdata->core_reset)
+ imx_pcie->drvdata->core_reset(imx_pcie, false);
/* Some boards don't have PCIe reset GPIO. */
if (imx_pcie->reset_gpiod) {
@@ -1442,6 +1446,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx_pcie_init_phy,
.set_ref_clk = imx6q_pcie_set_ref_clk,
+ .core_reset = imx6q_pcie_core_reset,
},
[IMX6SX] = {
.variant = IMX6SX,
@@ -1457,6 +1462,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx6sx_pcie_init_phy,
.set_ref_clk = imx6sx_pcie_set_ref_clk,
+ .core_reset = imx6sx_pcie_core_reset,
},
[IMX6QP] = {
.variant = IMX6QP,
@@ -1473,6 +1479,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx_pcie_init_phy,
.set_ref_clk = imx6q_pcie_set_ref_clk,
+ .core_reset = imx6qp_pcie_core_reset,
},
[IMX7D] = {
.variant = IMX7D,
@@ -1486,6 +1493,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx7d_pcie_init_phy,
.set_ref_clk = imx7d_pcie_set_ref_clk,
+ .core_reset = imx7d_pcie_core_reset,
},
[IMX8MQ] = {
.variant = IMX8MQ,
--
2.34.1
Consolidated redundant if-checks pertaining to imx_pcie->phy. Instead of
two separate checks, merged them into one to improve code readability.
if (imx_pcie->phy) {
... code 1
}
if (imx_pcie->phy) {
... code 2
}
Merge into one if block.
if (imx_pcie->phy) {
... code 1
... code 2
}
Reviewed-by: Manivannan Sadhasivam <[email protected]>
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 8ecc00049e20b..c8d58481f80dc 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1028,9 +1028,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
dev_err(dev, "pcie PHY power up failed\n");
goto err_clk_disable;
}
- }
- if (imx_pcie->phy) {
ret = phy_power_on(imx_pcie->phy);
if (ret) {
dev_err(dev, "waiting for PHY ready timeout!\n");
--
2.34.1
Improve comment about workaround ERR010727 by using official errata
document content.
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 5e21fc942e90e..5533b7ad0f092 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -713,9 +713,25 @@ static int imx7d_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
return 0;
/*
- * Workaround for ERR010728, failure of PCI-e PLL VCO to
- * oscillate, especially when cold. This turns off "Duty-cycle
- * Corrector" and other mysterious undocumented things.
+ * 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 (likely(imx_pcie->phy_base)) {
--
2.34.1
For the i.MX95, configuration of a LUT is necessary to convert Bus Device
Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
This involves examining the msi-map and smmu-map to ensure consistent
mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
registers are configured. In the absence of an msi-map, the built-in MSI
controller is utilized as a fallback.
Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
upon the appearance of a new PCI device and when the bus is an iMX6 PCI
controller. This function configures the correct LUT based on Device Tree
Settings (DTS).
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 175 +++++++++++++++++++++++++++++++++-
1 file changed, 174 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 29309ad0e352b..8ecc00049e20b 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -54,6 +54,22 @@
#define IMX95_PE0_GEN_CTRL_3 0x1058
#define IMX95_PCIE_LTSSM_EN BIT(0)
+#define IMX95_PE0_LUT_ACSCTRL 0x1008
+#define IMX95_PEO_LUT_RWA BIT(16)
+#define IMX95_PE0_LUT_ENLOC GENMASK(4, 0)
+
+#define IMX95_PE0_LUT_DATA1 0x100c
+#define IMX95_PE0_LUT_VLD BIT(31)
+#define IMX95_PE0_LUT_DAC_ID GENMASK(10, 8)
+#define IMX95_PE0_LUT_STREAM_ID GENMASK(5, 0)
+
+#define IMX95_PE0_LUT_DATA2 0x1010
+#define IMX95_PE0_LUT_REQID GENMASK(31, 16)
+#define IMX95_PE0_LUT_MASK GENMASK(15, 0)
+
+#define IMX95_SID_MASK GENMASK(5, 0)
+#define IMX95_MAX_LUT 32
+
#define to_imx_pcie(x) dev_get_drvdata((x)->dev)
enum imx_pcie_variants {
@@ -79,6 +95,7 @@ enum imx_pcie_variants {
#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_MONITOR_DEV BIT(8)
#define imx_check_flag(pci, val) (pci->drvdata->flags & val)
@@ -132,6 +149,8 @@ struct imx_pcie {
struct device *pd_pcie_phy;
struct phy *phy;
const struct imx_pcie_drvdata *drvdata;
+
+ struct mutex lock;
};
/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
@@ -215,6 +234,66 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
return 0;
}
+static int imx_pcie_config_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid)
+{
+ struct dw_pcie *pci = imx_pcie->pci;
+ struct device *dev = pci->dev;
+ u32 data1, data2;
+ int i;
+
+ if (sid >= 64) {
+ dev_err(dev, "Invalid SID for index %d\n", sid);
+ return -EINVAL;
+ }
+
+ guard(mutex)(&imx_pcie->lock);
+
+ 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)
+ continue;
+
+ data1 = FIELD_PREP(IMX95_PE0_LUT_DAC_ID, 0);
+ data1 |= FIELD_PREP(IMX95_PE0_LUT_STREAM_ID, sid);
+ data1 |= IMX95_PE0_LUT_VLD;
+
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, data1);
+
+ data2 = 0xffff;
+ data2 |= FIELD_PREP(IMX95_PE0_LUT_REQID, reqid);
+
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, data2);
+
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
+
+ return 0;
+ }
+
+ dev_err(dev, "All lut already used\n");
+ return -EINVAL;
+}
+
+static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid)
+{
+ u32 data2 = 0;
+ int i;
+
+ guard(mutex)(&imx_pcie->lock);
+
+ 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_DATA2, &data2);
+ if (FIELD_GET(IMX95_PE0_LUT_REQID, data2) == reqid) {
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, 0);
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, 0);
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
+ }
+ }
+}
+
static void imx_pcie_configure_type(struct imx_pcie *imx_pcie)
{
const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
@@ -1232,6 +1311,85 @@ static int imx_pcie_resume_noirq(struct device *dev)
return 0;
}
+static bool imx_pcie_match_device(struct pci_bus *bus);
+
+static int imx_pcie_add_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
+{
+ u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev);
+ struct device *dev = imx_pcie->pci->dev;
+ int err;
+
+ err = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", NULL, &sid_i);
+ if (err)
+ return err;
+
+ err = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", NULL, &sid_m);
+ if (err)
+ return err;
+
+ if (sid_i != rid && sid_m != rid)
+ if ((sid_i & IMX95_SID_MASK) != (sid_m & IMX95_SID_MASK)) {
+ dev_err(dev, "its and iommu stream id miss match, please check dts file\n");
+ return -EINVAL;
+ }
+
+ /* if iommu-map is not existed then use msi-map's stream id*/
+ if (sid_i == rid)
+ sid_i = sid_m;
+
+ sid_i &= IMX95_SID_MASK;
+
+ if (sid_i != rid)
+ return imx_pcie_config_lut(imx_pcie, rid, sid_i);
+
+ /* Use dwc built-in MSI controller */
+ return 0;
+}
+
+static void imx_pcie_del_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
+{
+ imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
+}
+
+
+static int imx_pcie_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
+{
+ struct pci_host_bridge *host;
+ struct imx_pcie *imx_pcie;
+ struct pci_dev *pdev;
+ int err;
+
+ pdev = to_pci_dev(data);
+ host = pci_find_host_bridge(pdev->bus);
+
+ if (!imx_pcie_match_device(host->bus))
+ return NOTIFY_OK;
+
+ imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(host->sysdata));
+
+ if (!imx_check_flag(imx_pcie, IMX_PCIE_FLAG_MONITOR_DEV))
+ return NOTIFY_OK;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ err = imx_pcie_add_device(imx_pcie, pdev);
+ if (err)
+ return notifier_from_errno(err);
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ imx_pcie_del_device(imx_pcie, pdev);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block imx_pcie_nb = {
+ .notifier_call = imx_pcie_bus_notifier,
+};
+
static const struct dev_pm_ops imx_pcie_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_pcie_suspend_noirq,
imx_pcie_resume_noirq)
@@ -1264,6 +1422,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
imx_pcie->pci = pci;
imx_pcie->drvdata = of_device_get_match_data(dev);
+ mutex_init(&imx_pcie->lock);
+
/* Find the PHY if one is defined, only imx7d uses it */
np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
if (np) {
@@ -1551,7 +1711,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
},
[IMX95] = {
.variant = IMX95,
- .flags = IMX_PCIE_FLAG_HAS_SERDES,
+ .flags = IMX_PCIE_FLAG_HAS_SERDES |
+ IMX_PCIE_FLAG_MONITOR_DEV,
.clk_names = imx8mq_clks,
.clks_cnt = ARRAY_SIZE(imx8mq_clks),
.ltssm_off = IMX95_PE0_GEN_CTRL_3,
@@ -1687,6 +1848,8 @@ DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd,
static int __init imx_pcie_init(void)
{
+ int ret;
+
#ifdef CONFIG_ARM
struct device_node *np;
@@ -1705,7 +1868,17 @@ static int __init imx_pcie_init(void)
hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
"external abort on non-linefetch");
#endif
+ ret = bus_register_notifier(&pci_bus_type, &imx_pcie_nb);
+ if (ret)
+ return ret;
return platform_driver_register(&imx_pcie_driver);
}
+
+static void __exit imx_pcie_exit(void)
+{
+ bus_unregister_notifier(&pci_bus_type, &imx_pcie_nb);
+}
+
device_initcall(imx_pcie_init);
+__exitcall(imx_pcie_exit);
--
2.34.1
Invoke the common PHY API to configure mode, speed, and submode. While
these functions are optional in the PHY interface, they are necessary for
certain PHY drivers. Lack of support for these functions in a PHY driver
does not cause harm.
Signed-off-by: Frank Li <[email protected]>
---
drivers/pci/controller/dwc/pci-imx6.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index c8d58481f80dc..5a725ef6ed0cb 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -29,6 +29,7 @@
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
+#include <linux/phy/pcie.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -306,6 +307,10 @@ static void imx_pcie_configure_type(struct imx_pcie *imx_pcie)
id = imx_pcie->controller_id;
+ /* If mode_mask[0] is 0, means use phy driver to set mode */
+ if (!drvdata->mode_mask[0])
+ return;
+
/* If mode_mask[id] is zero, means each controller have its individual gpr */
if (!drvdata->mode_mask[id])
id = 0;
@@ -882,6 +887,7 @@ static void imx_pcie_ltssm_enable(struct device *dev)
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
+ phy_set_speed(imx_pcie->phy, PCI_EXP_LNKCAP_SLS_2_5GB);
if (drvdata->ltssm_mask)
regmap_update_bits(imx_pcie->iomuxc_gpr, drvdata->ltssm_off, drvdata->ltssm_mask,
drvdata->ltssm_mask);
@@ -894,6 +900,7 @@ static void imx_pcie_ltssm_disable(struct device *dev)
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
+ phy_set_speed(imx_pcie->phy, 0);
if (drvdata->ltssm_mask)
regmap_update_bits(imx_pcie->iomuxc_gpr, drvdata->ltssm_off,
drvdata->ltssm_mask, 0);
@@ -1029,6 +1036,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
goto err_clk_disable;
}
+ ret = phy_set_mode_ext(imx_pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
+ if (ret) {
+ dev_err(dev, "unable to set pcie PHY mode\n");
+ goto err_phy_off;
+ }
+
ret = phy_power_on(imx_pcie->phy);
if (ret) {
dev_err(dev, "waiting for PHY ready timeout!\n");
--
2.34.1
On Tue, May 28, 2024 at 03:39:13PM -0400, Frank Li wrote:
> Fixed 8mp EP mode problem.
>
> imx6 actaully for all imx chips (imx6*, imx7*, imx8*, imx9*). To avoid
> confuse, rename all imx6_* to imx_*, IMX6_* to IMX_*. pci-imx6.c to
> pci-imx.c to avoid confuse.
>
> Using callback to reduce switch case for core reset and refclk.
>
> Add imx95 iommux and its stream id information.
>
> Base on linux-pci/controller/imx
This applies cleanly to the pci/controller/gpio branch, which has some
minor rework in pci-imx6.c.
When we apply this, I think we should do it on a a pci/controller/imx6
branch that is based on "main" (v6.10-rc1).
I can resolve the conflicts with pci/controller/gpio when building
pci/next.
Bjorn
On Tue, May 28, 2024 at 05:31:36PM -0500, Bjorn Helgaas wrote:
> On Tue, May 28, 2024 at 03:39:13PM -0400, Frank Li wrote:
> > Fixed 8mp EP mode problem.
> >
> > imx6 actaully for all imx chips (imx6*, imx7*, imx8*, imx9*). To avoid
> > confuse, rename all imx6_* to imx_*, IMX6_* to IMX_*. pci-imx6.c to
> > pci-imx.c to avoid confuse.
> >
> > Using callback to reduce switch case for core reset and refclk.
> >
> > Add imx95 iommux and its stream id information.
> >
> > Base on linux-pci/controller/imx
>
> This applies cleanly to the pci/controller/gpio branch, which has some
> minor rework in pci-imx6.c.
>
> When we apply this, I think we should do it on a a pci/controller/imx6
> branch that is based on "main" (v6.10-rc1).
>
> I can resolve the conflicts with pci/controller/gpio when building
> pci/next.
Sorry, I forget update this. It should be base on linux-pci/next
(e3fca37312892122d73f8c5293c0d1cc8c34500b).
commit e3fca37312892122d73f8c5293c0d1cc8c34500b (pci/next, linux-pci/next)
Merge: 86e0cd3da71b5 d19a86d584e04
Author: Bjorn Helgaas <[email protected]>
Date: Tue May 28 12:34:12 2024 -0500
Merge branch 'pci/controller/tegra194'
- Ensure Tegra194 and Tegra234 inbound ATU entries are 64KB-aligned to
match the hardware restriction (Jon Hunter)
* pci/controller/tegra194:
PCI: tegra194: Set EP alignment restriction for inbound ATU
>
> Bjorn
On Wed, May 29, 2024 at 11:00:49AM -0400, Frank Li wrote:
> On Tue, May 28, 2024 at 05:31:36PM -0500, Bjorn Helgaas wrote:
> > On Tue, May 28, 2024 at 03:39:13PM -0400, Frank Li wrote:
> ...
> > > Base on linux-pci/controller/imx
> >
> > This applies cleanly to the pci/controller/gpio branch, which has some
> > minor rework in pci-imx6.c.
> >
> > When we apply this, I think we should do it on a a pci/controller/imx6
> > branch that is based on "main" (v6.10-rc1).
> >
> > I can resolve the conflicts with pci/controller/gpio when building
> > pci/next.
>
> Sorry, I forget update this. It should be base on linux-pci/next
> (e3fca37312892122d73f8c5293c0d1cc8c34500b).
I prefer patches that are based on -rc1, i.e., the pci/main branch,
not on the pci/next branch.
If a series *requires* functionality that is already on a topic
branch, you can base it on that branch instead of on pci/main.
This series happens to touch some of the same code as
pci/controller/gpio, but it doesn't require those gpio changes, so it
does not need to be based on pci/controller/gpio.
Having this series based on pci/main means that if we update or drop
the gpio branch for some reason, this series will still make sense.
Bjorn
On Thu, May 30, 2024 at 12:27:49PM -0500, Bjorn Helgaas wrote:
> On Wed, May 29, 2024 at 11:00:49AM -0400, Frank Li wrote:
> > On Tue, May 28, 2024 at 05:31:36PM -0500, Bjorn Helgaas wrote:
> > > On Tue, May 28, 2024 at 03:39:13PM -0400, Frank Li wrote:
> > ...
>
> > > > Base on linux-pci/controller/imx
> > >
> > > This applies cleanly to the pci/controller/gpio branch, which has some
> > > minor rework in pci-imx6.c.
> > >
> > > When we apply this, I think we should do it on a a pci/controller/imx6
> > > branch that is based on "main" (v6.10-rc1).
> > >
> > > I can resolve the conflicts with pci/controller/gpio when building
> > > pci/next.
> >
> > Sorry, I forget update this. It should be base on linux-pci/next
> > (e3fca37312892122d73f8c5293c0d1cc8c34500b).
>
> I prefer patches that are based on -rc1, i.e., the pci/main branch,
> not on the pci/next branch.
>
> If a series *requires* functionality that is already on a topic
> branch, you can base it on that branch instead of on pci/main.
>
> This series happens to touch some of the same code as
> pci/controller/gpio, but it doesn't require those gpio changes, so it
> does not need to be based on pci/controller/gpio.
>
> Having this series based on pci/main means that if we update or drop
> the gpio branch for some reason, this series will still make sense.
v4 should cleanly apply to pci/main. It is not big deal. Do you have any
additional comments for this series?
We still have some other works (such as simply suspend/resume by using
common PME turn off method), which base on this series.
Frank
>
> Bjorn
[+cc IOMMU and pcie-apple.c folks for comment]
On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> This involves examining the msi-map and smmu-map to ensure consistent
> mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> registers are configured. In the absence of an msi-map, the built-in MSI
> controller is utilized as a fallback.
>
> Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> controller. This function configures the correct LUT based on Device Tree
> Settings (DTS).
This scheme is pretty similar to apple_pcie_bus_notifier(). If we
have to do this, I wish it were *more* similar, i.e., copy the
function names, bitmap tracking, code structure, etc.
I don't really know how stream IDs work, but I assume they are used on
most or all arm64 platforms, so I'm a little surprised that of all the
PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
this notifier.
There's this path, which is pretty generic and does at least the
of_map_id() part of what you're doing in imx_pcie_add_device():
__driver_probe_device
really_probe
pci_dma_configure # pci_bus_type.dma_configure
of_dma_configure
of_dma_configure_id
of_iommu_configure
of_pci_iommu_init
of_iommu_configure_dev_id
of_map_id
of_iommu_xlate
ops = iommu_ops_from_fwnode
iommu_fwspec_init
ops->of_xlate(dev, iommu_spec)
Maybe this needs to be extended somehow with a hook to do the
device-specific work like updating the LUT? Just speculating here,
the IOMMU folks will know how this is expected to work.
Some typos and minor comments below.
> Signed-off-by: Frank Li <[email protected]>
> ---
> drivers/pci/controller/dwc/pci-imx6.c | 175 +++++++++++++++++++++++++++++++++-
> 1 file changed, 174 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 29309ad0e352b..8ecc00049e20b 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -54,6 +54,22 @@
> #define IMX95_PE0_GEN_CTRL_3 0x1058
> #define IMX95_PCIE_LTSSM_EN BIT(0)
>
> +#define IMX95_PE0_LUT_ACSCTRL 0x1008
> +#define IMX95_PEO_LUT_RWA BIT(16)
> +#define IMX95_PE0_LUT_ENLOC GENMASK(4, 0)
> +
> +#define IMX95_PE0_LUT_DATA1 0x100c
> +#define IMX95_PE0_LUT_VLD BIT(31)
> +#define IMX95_PE0_LUT_DAC_ID GENMASK(10, 8)
> +#define IMX95_PE0_LUT_STREAM_ID GENMASK(5, 0)
> +
> +#define IMX95_PE0_LUT_DATA2 0x1010
> +#define IMX95_PE0_LUT_REQID GENMASK(31, 16)
> +#define IMX95_PE0_LUT_MASK GENMASK(15, 0)
> +
> +#define IMX95_SID_MASK GENMASK(5, 0)
> +#define IMX95_MAX_LUT 32
> +
> #define to_imx_pcie(x) dev_get_drvdata((x)->dev)
>
> enum imx_pcie_variants {
> @@ -79,6 +95,7 @@ enum imx_pcie_variants {
> #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_MONITOR_DEV BIT(8)
>
> #define imx_check_flag(pci, val) (pci->drvdata->flags & val)
>
> @@ -132,6 +149,8 @@ struct imx_pcie {
> struct device *pd_pcie_phy;
> struct phy *phy;
> const struct imx_pcie_drvdata *drvdata;
> +
> + struct mutex lock;
> };
>
> /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
> @@ -215,6 +234,66 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
> return 0;
> }
>
> +static int imx_pcie_config_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid)
> +{
> + struct dw_pcie *pci = imx_pcie->pci;
> + struct device *dev = pci->dev;
> + u32 data1, data2;
> + int i;
> +
> + if (sid >= 64) {
> + dev_err(dev, "Invalid SID for index %d\n", sid);
> + return -EINVAL;
> + }
> +
> + guard(mutex)(&imx_pcie->lock);
> +
> + 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)
> + continue;
> +
> + data1 = FIELD_PREP(IMX95_PE0_LUT_DAC_ID, 0);
> + data1 |= FIELD_PREP(IMX95_PE0_LUT_STREAM_ID, sid);
> + data1 |= IMX95_PE0_LUT_VLD;
> +
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, data1);
> +
> + data2 = 0xffff;
> + data2 |= FIELD_PREP(IMX95_PE0_LUT_REQID, reqid);
> +
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, data2);
> +
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
> +
> + return 0;
> + }
> +
> + dev_err(dev, "All lut already used\n");
> + return -EINVAL;
> +}
> +
> +static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid)
> +{
> + u32 data2 = 0;
> + int i;
> +
> + guard(mutex)(&imx_pcie->lock);
> +
> + 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_DATA2, &data2);
> + if (FIELD_GET(IMX95_PE0_LUT_REQID, data2) == reqid) {
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, 0);
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, 0);
> + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
> + }
> + }
> +}
> +
> static void imx_pcie_configure_type(struct imx_pcie *imx_pcie)
> {
> const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
> @@ -1232,6 +1311,85 @@ static int imx_pcie_resume_noirq(struct device *dev)
> return 0;
> }
>
> +static bool imx_pcie_match_device(struct pci_bus *bus);
Can you add the imx_pcie_match_device() earlier in the file so we
don't need this forward declaration?
> +static int imx_pcie_add_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
> +{
> + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev);
> + struct device *dev = imx_pcie->pci->dev;
> + int err;
> +
> + err = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", NULL, &sid_i);
> + if (err)
> + return err;
> +
> + err = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", NULL, &sid_m);
> + if (err)
> + return err;
> +
> + if (sid_i != rid && sid_m != rid)
> + if ((sid_i & IMX95_SID_MASK) != (sid_m & IMX95_SID_MASK)) {
> + dev_err(dev, "its and iommu stream id miss match, please check dts file\n");
> + return -EINVAL;
> + }
> +
> + /* if iommu-map is not existed then use msi-map's stream id*/
Capitalize consistently, e.g., the most comments in this file start
with a capital letter.
s/is not existed/does not exist/
Add space before closing */
> + if (sid_i == rid)
> + sid_i = sid_m;
> +
> + sid_i &= IMX95_SID_MASK;
> +
> + if (sid_i != rid)
> + return imx_pcie_config_lut(imx_pcie, rid, sid_i);
> +
> + /* Use dwc built-in MSI controller */
> + return 0;
> +}
> +
> +static void imx_pcie_del_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
> +{
> + imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
> +}
> +
> +
> +static int imx_pcie_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
> +{
> + struct pci_host_bridge *host;
> + struct imx_pcie *imx_pcie;
> + struct pci_dev *pdev;
> + int err;
> +
> + pdev = to_pci_dev(data);
> + host = pci_find_host_bridge(pdev->bus);
> +
> + if (!imx_pcie_match_device(host->bus))
> + return NOTIFY_OK;
> +
> + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(host->sysdata));
> +
> + if (!imx_check_flag(imx_pcie, IMX_PCIE_FLAG_MONITOR_DEV))
> + return NOTIFY_OK;
> +
> + switch (action) {
> + case BUS_NOTIFY_ADD_DEVICE:
> + err = imx_pcie_add_device(imx_pcie, pdev);
> + if (err)
> + return notifier_from_errno(err);
> + break;
> + case BUS_NOTIFY_DEL_DEVICE:
> + imx_pcie_del_device(imx_pcie, pdev);
> + break;
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block imx_pcie_nb = {
> + .notifier_call = imx_pcie_bus_notifier,
> +};
> +
> static const struct dev_pm_ops imx_pcie_pm_ops = {
> NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_pcie_suspend_noirq,
> imx_pcie_resume_noirq)
> @@ -1264,6 +1422,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
> imx_pcie->pci = pci;
> imx_pcie->drvdata = of_device_get_match_data(dev);
>
> + mutex_init(&imx_pcie->lock);
> +
> /* Find the PHY if one is defined, only imx7d uses it */
> np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
> if (np) {
> @@ -1551,7 +1711,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
> },
> [IMX95] = {
> .variant = IMX95,
> - .flags = IMX_PCIE_FLAG_HAS_SERDES,
> + .flags = IMX_PCIE_FLAG_HAS_SERDES |
> + IMX_PCIE_FLAG_MONITOR_DEV,
> .clk_names = imx8mq_clks,
> .clks_cnt = ARRAY_SIZE(imx8mq_clks),
> .ltssm_off = IMX95_PE0_GEN_CTRL_3,
> @@ -1687,6 +1848,8 @@ DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd,
>
> static int __init imx_pcie_init(void)
> {
> + int ret;
> +
> #ifdef CONFIG_ARM
> struct device_node *np;
>
> @@ -1705,7 +1868,17 @@ static int __init imx_pcie_init(void)
> hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
> "external abort on non-linefetch");
> #endif
> + ret = bus_register_notifier(&pci_bus_type, &imx_pcie_nb);
> + if (ret)
> + return ret;
I think this should go in imx6_pcie_probe().
> return platform_driver_register(&imx_pcie_driver);
> }
> +
> +static void __exit imx_pcie_exit(void)
> +{
> + bus_unregister_notifier(&pci_bus_type, &imx_pcie_nb);
It looks like this driver is removable?
What happens when an external abort occurs after the
imx6q_pcie_abort_handler() text is removed?
> +}
> +
> device_initcall(imx_pcie_init);
> +__exitcall(imx_pcie_exit);
>
> --
> 2.34.1
>
On Fri, 31 May 2024 00:08:32 +0100,
Bjorn Helgaas <[email protected]> wrote:
>
> [+cc IOMMU and pcie-apple.c folks for comment]
>
> On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > This involves examining the msi-map and smmu-map to ensure consistent
> > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > registers are configured. In the absence of an msi-map, the built-in MSI
> > controller is utilized as a fallback.
> >
> > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > controller. This function configures the correct LUT based on Device Tree
> > Settings (DTS).
>
> This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> have to do this, I wish it were *more* similar, i.e., copy the
> function names, bitmap tracking, code structure, etc.
>
> I don't really know how stream IDs work, but I assume they are used on
> most or all arm64 platforms, so I'm a little surprised that of all the
> PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> this notifier.
That's because on sane platforms, PCI's RID and the IOMMU's SID are
the same thing, and there is no need to do anything bizarre. In the
worse case, there is a static transformation that can be applied (IORT
for ACPI, or iommu-map for DT).
Some "creative" systems such as the Apple stuff require some extra
side-band configuration because they don't know what a RID is at all
(the RID isn't conveyed to the IOMMU), nor is there a static (baked in
HW) transformation between RID and SID.
So there is a widget on the side that performs the conversion, and
this widget needs to be programmed. The way it works is that the
driver parses the iommu-map to find the expected and arbitrary) SID
on the IOMMU side for a given RID, and programs the association in the
RID-to-SID mapper. It does so at device probe time in order to make
sure the widget is alive (it seems to be part of the port power
domain).
Yes, this is a terrible hack, and I wish we didn't have to deal with
this sort of crap.
>
> There's this path, which is pretty generic and does at least the
> of_map_id() part of what you're doing in imx_pcie_add_device():
>
> __driver_probe_device
> really_probe
> pci_dma_configure # pci_bus_type.dma_configure
> of_dma_configure
> of_dma_configure_id
> of_iommu_configure
> of_pci_iommu_init
> of_iommu_configure_dev_id
> of_map_id
> of_iommu_xlate
> ops = iommu_ops_from_fwnode
> iommu_fwspec_init
> ops->of_xlate(dev, iommu_spec)
>
> Maybe this needs to be extended somehow with a hook to do the
> device-specific work like updating the LUT? Just speculating here,
> the IOMMU folks will know how this is expected to work.
That'd be a possibility. But this would be adding extra complexity to
the IOMMU core, and I'm not sure it is worth it given that these
systems are thankfully "rare".
It is also something that is conceptually part of the PCIe root port,
and not really the IOMMU, so I'm a bit reluctant to shove things
there. In any case, it would still require callbacks into the PCIe
host driver, and I have the feeling that we'd be reinventing the bus
notifier wheel, only with pointier angles...
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> [+cc IOMMU and pcie-apple.c folks for comment]
>
> On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
>> For the i.MX95, configuration of a LUT is necessary to convert Bus Device
>> Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
>> This involves examining the msi-map and smmu-map to ensure consistent
>> mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
>> registers are configured. In the absence of an msi-map, the built-in MSI
>> controller is utilized as a fallback.
>>
>> Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
>> upon the appearance of a new PCI device and when the bus is an iMX6 PCI
>> controller. This function configures the correct LUT based on Device Tree
>> Settings (DTS).
>
> This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> have to do this, I wish it were *more* similar, i.e., copy the
> function names, bitmap tracking, code structure, etc.
>
> I don't really know how stream IDs work, but I assume they are used on
> most or all arm64 platforms, so I'm a little surprised that of all the
> PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> this notifier.
This is one of those things that's mostly at the mercy of the PCIe root
complex implementation. Typically the SMMU StreamID and/or GIC ITS
DeviceID is derived directly from the PCI RID, sometimes with additional
high-order bits hard-wired to disambiguate PCI segments. I believe this
RID-translation LUT is a particular feature of the the Synopsys IP - I
know there's also one on the NXP Layerscape platforms, but on those it's
programmed by the bootloader, which also generates the appropriate
"msi-map" and "iommu-map" properties to match. Ideally that's what i.MX
should do as well, but hey.
> There's this path, which is pretty generic and does at least the
> of_map_id() part of what you're doing in imx_pcie_add_device():
>
> __driver_probe_device
> really_probe
> pci_dma_configure # pci_bus_type.dma_configure
> of_dma_configure
> of_dma_configure_id
> of_iommu_configure
> of_pci_iommu_init
> of_iommu_configure_dev_id
> of_map_id
> of_iommu_xlate
> ops = iommu_ops_from_fwnode
> iommu_fwspec_init
> ops->of_xlate(dev, iommu_spec)
>
> Maybe this needs to be extended somehow with a hook to do the
> device-specific work like updating the LUT? Just speculating here,
> the IOMMU folks will know how this is expected to work.
Note that that particular code path has fundamental issues and much of
it needs to go away (I'm working on it, but it's a rich ~8-year-old pile
of technical debt...). IOMMU configuration needs to be happening at
device_add() time via the IOMMU layer's own bus notifier.
If it's really necessary to do this programming from Linux, then there's
still no point in it being dynamic - the mappings cannot ever change,
since the rest of the kernel believes that what the DT said at boot time
was already a property of the hardware. It would be a lot more logical,
and likely simpler, for the driver to just read the relevant map
property and program the entire LUT to match, all in one go at
controller probe time. Rather like what's already commonly done with the
parsing of "dma-ranges" to program address-translation LUTs for inbound
windows.
Plus that would also give a chance of safely dealing with bad DTs
specifying invalid ID mappings (by refusing to probe at all). As it is,
returning an error from a child's BUS_NOTIFY_ADD_DEVICE does nothing
except prevent any further notifiers from running at that point - the
device will still be added, allowed to bind a driver, and able to start
sending DMA/MSI traffic without the controller being correctly
programmed, which at best won't work and at worst may break the whole
system.
Thanks,
Robin.
On Thu, May 30, 2024 at 06:08:32PM -0500, Bjorn Helgaas wrote:
> [+cc IOMMU and pcie-apple.c folks for comment]
>
> On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > This involves examining the msi-map and smmu-map to ensure consistent
> > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > registers are configured. In the absence of an msi-map, the built-in MSI
> > controller is utilized as a fallback.
> >
> > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > controller. This function configures the correct LUT based on Device Tree
> > Settings (DTS).
>
> This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> have to do this, I wish it were *more* similar, i.e., copy the
> function names, bitmap tracking, code structure, etc.
Actually, I refer apple_pcie_bus_notifier(). I can't direct use apple's
implement because in imx95 have difference PCI host controller, another one
is PCI ECAM netc controller. At lease function name should be similar with
apple.
>
> I don't really know how stream IDs work, but I assume they are used on
> most or all arm64 platforms, so I'm a little surprised that of all the
> PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> this notifier.
>
> There's this path, which is pretty generic and does at least the
> of_map_id() part of what you're doing in imx_pcie_add_device():
>
> __driver_probe_device
> really_probe
> pci_dma_configure # pci_bus_type.dma_configure
> of_dma_configure
> of_dma_configure_id
> of_iommu_configure
> of_pci_iommu_init
> of_iommu_configure_dev_id
> of_map_id
> of_iommu_xlate
> ops = iommu_ops_from_fwnode
> iommu_fwspec_init
> ops->of_xlate(dev, iommu_spec)
>
> Maybe this needs to be extended somehow with a hook to do the
> device-specific work like updating the LUT? Just speculating here,
> the IOMMU folks will know how this is expected to work.
Let me do more study. But I think ITS also need stream ID, not sure if
only hook IOMMU can work. Some configuration, IOMMU have not enabled. but
ITS need it.
Ideally, pci system can provide a hook function to host bridge when new
device add and remove.
Frank
>
> Some typos and minor comments below.
>
> > Signed-off-by: Frank Li <[email protected]>
> > ---
> > drivers/pci/controller/dwc/pci-imx6.c | 175 +++++++++++++++++++++++++++++++++-
> > 1 file changed, 174 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> > index 29309ad0e352b..8ecc00049e20b 100644
> > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > @@ -54,6 +54,22 @@
> > #define IMX95_PE0_GEN_CTRL_3 0x1058
> > #define IMX95_PCIE_LTSSM_EN BIT(0)
> >
> > +#define IMX95_PE0_LUT_ACSCTRL 0x1008
> > +#define IMX95_PEO_LUT_RWA BIT(16)
> > +#define IMX95_PE0_LUT_ENLOC GENMASK(4, 0)
> > +
> > +#define IMX95_PE0_LUT_DATA1 0x100c
> > +#define IMX95_PE0_LUT_VLD BIT(31)
> > +#define IMX95_PE0_LUT_DAC_ID GENMASK(10, 8)
> > +#define IMX95_PE0_LUT_STREAM_ID GENMASK(5, 0)
> > +
> > +#define IMX95_PE0_LUT_DATA2 0x1010
> > +#define IMX95_PE0_LUT_REQID GENMASK(31, 16)
> > +#define IMX95_PE0_LUT_MASK GENMASK(15, 0)
> > +
> > +#define IMX95_SID_MASK GENMASK(5, 0)
> > +#define IMX95_MAX_LUT 32
> > +
> > #define to_imx_pcie(x) dev_get_drvdata((x)->dev)
> >
> > enum imx_pcie_variants {
> > @@ -79,6 +95,7 @@ enum imx_pcie_variants {
> > #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_MONITOR_DEV BIT(8)
> >
> > #define imx_check_flag(pci, val) (pci->drvdata->flags & val)
> >
> > @@ -132,6 +149,8 @@ struct imx_pcie {
> > struct device *pd_pcie_phy;
> > struct phy *phy;
> > const struct imx_pcie_drvdata *drvdata;
> > +
> > + struct mutex lock;
> > };
> >
> > /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
> > @@ -215,6 +234,66 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
> > return 0;
> > }
> >
> > +static int imx_pcie_config_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid)
> > +{
> > + struct dw_pcie *pci = imx_pcie->pci;
> > + struct device *dev = pci->dev;
> > + u32 data1, data2;
> > + int i;
> > +
> > + if (sid >= 64) {
> > + dev_err(dev, "Invalid SID for index %d\n", sid);
> > + return -EINVAL;
> > + }
> > +
> > + guard(mutex)(&imx_pcie->lock);
> > +
> > + 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)
> > + continue;
> > +
> > + data1 = FIELD_PREP(IMX95_PE0_LUT_DAC_ID, 0);
> > + data1 |= FIELD_PREP(IMX95_PE0_LUT_STREAM_ID, sid);
> > + data1 |= IMX95_PE0_LUT_VLD;
> > +
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, data1);
> > +
> > + data2 = 0xffff;
> > + data2 |= FIELD_PREP(IMX95_PE0_LUT_REQID, reqid);
> > +
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, data2);
> > +
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
> > +
> > + return 0;
> > + }
> > +
> > + dev_err(dev, "All lut already used\n");
> > + return -EINVAL;
> > +}
> > +
> > +static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid)
> > +{
> > + u32 data2 = 0;
> > + int i;
> > +
> > + guard(mutex)(&imx_pcie->lock);
> > +
> > + 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_DATA2, &data2);
> > + if (FIELD_GET(IMX95_PE0_LUT_REQID, data2) == reqid) {
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, 0);
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, 0);
> > + regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_ACSCTRL, i);
> > + }
> > + }
> > +}
> > +
> > static void imx_pcie_configure_type(struct imx_pcie *imx_pcie)
> > {
> > const struct imx_pcie_drvdata *drvdata = imx_pcie->drvdata;
> > @@ -1232,6 +1311,85 @@ static int imx_pcie_resume_noirq(struct device *dev)
> > return 0;
> > }
> >
> > +static bool imx_pcie_match_device(struct pci_bus *bus);
>
> Can you add the imx_pcie_match_device() earlier in the file so we
> don't need this forward declaration?
imx_pcie_match_device() use global veriable imx_pcie_driver, which close to
end of the file. There will be bigger change if move imx_pcie_driver ahead.
>
> > +static int imx_pcie_add_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
> > +{
> > + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev);
> > + struct device *dev = imx_pcie->pci->dev;
> > + int err;
> > +
> > + err = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", NULL, &sid_i);
> > + if (err)
> > + return err;
> > +
> > + err = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", NULL, &sid_m);
> > + if (err)
> > + return err;
> > +
> > + if (sid_i != rid && sid_m != rid)
> > + if ((sid_i & IMX95_SID_MASK) != (sid_m & IMX95_SID_MASK)) {
> > + dev_err(dev, "its and iommu stream id miss match, please check dts file\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* if iommu-map is not existed then use msi-map's stream id*/
>
> Capitalize consistently, e.g., the most comments in this file start
> with a capital letter.
>
> s/is not existed/does not exist/
>
> Add space before closing */
>
> > + if (sid_i == rid)
> > + sid_i = sid_m;
> > +
> > + sid_i &= IMX95_SID_MASK;
> > +
> > + if (sid_i != rid)
> > + return imx_pcie_config_lut(imx_pcie, rid, sid_i);
> > +
> > + /* Use dwc built-in MSI controller */
> > + return 0;
> > +}
> > +
> > +static void imx_pcie_del_device(struct imx_pcie *imx_pcie, struct pci_dev *pdev)
> > +{
> > + imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
> > +}
> > +
> > +
> > +static int imx_pcie_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
> > +{
> > + struct pci_host_bridge *host;
> > + struct imx_pcie *imx_pcie;
> > + struct pci_dev *pdev;
> > + int err;
> > +
> > + pdev = to_pci_dev(data);
> > + host = pci_find_host_bridge(pdev->bus);
> > +
> > + if (!imx_pcie_match_device(host->bus))
> > + return NOTIFY_OK;
> > +
> > + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(host->sysdata));
> > +
> > + if (!imx_check_flag(imx_pcie, IMX_PCIE_FLAG_MONITOR_DEV))
> > + return NOTIFY_OK;
> > +
> > + switch (action) {
> > + case BUS_NOTIFY_ADD_DEVICE:
> > + err = imx_pcie_add_device(imx_pcie, pdev);
> > + if (err)
> > + return notifier_from_errno(err);
> > + break;
> > + case BUS_NOTIFY_DEL_DEVICE:
> > + imx_pcie_del_device(imx_pcie, pdev);
> > + break;
> > + default:
> > + return NOTIFY_DONE;
> > + }
> > +
> > + return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block imx_pcie_nb = {
> > + .notifier_call = imx_pcie_bus_notifier,
> > +};
> > +
> > static const struct dev_pm_ops imx_pcie_pm_ops = {
> > NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_pcie_suspend_noirq,
> > imx_pcie_resume_noirq)
> > @@ -1264,6 +1422,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
> > imx_pcie->pci = pci;
> > imx_pcie->drvdata = of_device_get_match_data(dev);
> >
> > + mutex_init(&imx_pcie->lock);
> > +
> > /* Find the PHY if one is defined, only imx7d uses it */
> > np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
> > if (np) {
> > @@ -1551,7 +1711,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
> > },
> > [IMX95] = {
> > .variant = IMX95,
> > - .flags = IMX_PCIE_FLAG_HAS_SERDES,
> > + .flags = IMX_PCIE_FLAG_HAS_SERDES |
> > + IMX_PCIE_FLAG_MONITOR_DEV,
> > .clk_names = imx8mq_clks,
> > .clks_cnt = ARRAY_SIZE(imx8mq_clks),
> > .ltssm_off = IMX95_PE0_GEN_CTRL_3,
> > @@ -1687,6 +1848,8 @@ DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, 0xabcd,
> >
> > static int __init imx_pcie_init(void)
> > {
> > + int ret;
> > +
> > #ifdef CONFIG_ARM
> > struct device_node *np;
> >
> > @@ -1705,7 +1868,17 @@ static int __init imx_pcie_init(void)
> > hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0,
> > "external abort on non-linefetch");
> > #endif
> > + ret = bus_register_notifier(&pci_bus_type, &imx_pcie_nb);
> > + if (ret)
> > + return ret;
>
> I think this should go in imx6_pcie_probe().
The same nb only register once. If move to probe, bus_register_notifier()
will return error and dump error message when second pci controller
instance probe.
>
> > return platform_driver_register(&imx_pcie_driver);
> > }
> > +
> > +static void __exit imx_pcie_exit(void)
> > +{
> > + bus_unregister_notifier(&pci_bus_type, &imx_pcie_nb);
>
> It looks like this driver is removable?
Actually, I think not. I am not sure how to prevent driver remove. There
are raise condition when driver remove and a pci device hot plug at the
same time although hot plug pci have not implement yet in imx platform.
>
> What happens when an external abort occurs after the
> imx6q_pcie_abort_handler() text is removed?
>
> > +}
> > +
> > device_initcall(imx_pcie_init);
> > +__exitcall(imx_pcie_exit);
> >
> > --
> > 2.34.1
> >
On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > [+cc IOMMU and pcie-apple.c folks for comment]
> >
> > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > This involves examining the msi-map and smmu-map to ensure consistent
> > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > controller is utilized as a fallback.
> > >
> > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > controller. This function configures the correct LUT based on Device Tree
> > > Settings (DTS).
> >
> > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > have to do this, I wish it were *more* similar, i.e., copy the
> > function names, bitmap tracking, code structure, etc.
> >
> > I don't really know how stream IDs work, but I assume they are used on
> > most or all arm64 platforms, so I'm a little surprised that of all the
> > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > this notifier.
>
> This is one of those things that's mostly at the mercy of the PCIe root
> complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> is derived directly from the PCI RID, sometimes with additional high-order
> bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> LUT is a particular feature of the the Synopsys IP - I know there's also one
> on the NXP Layerscape platforms, but on those it's programmed by the
> bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> properties to match. Ideally that's what i.MX should do as well, but hey.
Actually, I think it is not good for uboot config it because PCIe device is
hotplug, such as SD7.0. SD7.0 card may plug after system boot. uboot miss
config it because uboot scan nothing when it run.
>
> > There's this path, which is pretty generic and does at least the
> > of_map_id() part of what you're doing in imx_pcie_add_device():
> >
> > __driver_probe_device
> > really_probe
> > pci_dma_configure # pci_bus_type.dma_configure
> > of_dma_configure
> > of_dma_configure_id
> > of_iommu_configure
> > of_pci_iommu_init
> > of_iommu_configure_dev_id
> > of_map_id
> > of_iommu_xlate
> > ops = iommu_ops_from_fwnode
> > iommu_fwspec_init
> > ops->of_xlate(dev, iommu_spec)
> >
> > Maybe this needs to be extended somehow with a hook to do the
> > device-specific work like updating the LUT? Just speculating here,
> > the IOMMU folks will know how this is expected to work.
>
> Note that that particular code path has fundamental issues and much of it
> needs to go away (I'm working on it, but it's a rich ~8-year-old pile of
> technical debt...). IOMMU configuration needs to be happening at
> device_add() time via the IOMMU layer's own bus notifier.
>
> If it's really necessary to do this programming from Linux, then there's
> still no point in it being dynamic - the mappings cannot ever change, since
> the rest of the kernel believes that what the DT said at boot time was
> already a property of the hardware. It would be a lot more logical, and
> likely simpler, for the driver to just read the relevant map property and
> program the entire LUT to match, all in one go at controller probe time.
> Rather like what's already commonly done with the parsing of "dma-ranges" to
> program address-translation LUTs for inbound windows.
Do you means prefer v3's method?
https://lore.kernel.org/imx/[email protected]/
>
> Plus that would also give a chance of safely dealing with bad DTs specifying
> invalid ID mappings (by refusing to probe at all). As it is, returning an
> error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> further notifiers from running at that point - the device will still be
> added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> without the controller being correctly programmed, which at best won't work
> and at worst may break the whole system.
>
> Thanks,
> Robin.
On Fri, May 31, 2024 at 12:14:18PM -0400, Frank Li wrote:
> On Thu, May 30, 2024 at 06:08:32PM -0500, Bjorn Helgaas wrote:
> > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > This involves examining the msi-map and smmu-map to ensure consistent
> > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > controller is utilized as a fallback.
> > >
> > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > controller. This function configures the correct LUT based on Device Tree
> > > Settings (DTS).
> >
> > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > have to do this, I wish it were *more* similar, i.e., copy the
> > function names, bitmap tracking, code structure, etc.
>
> Actually, I refer apple_pcie_bus_notifier(). I can't direct use apple's
> implement because in imx95 have difference PCI host controller, another one
> is PCI ECAM netc controller. At lease function name should be similar with
> apple.
I know it's different hardware, so obviously it can't be exactly the
same. These are the differences that looked possibly unnecessary:
- registering from initcall instead of .probe():
apple_pcie_probe # .probe() method
bus_register_notifier(&pci_bus_type, &apple_pcie_nb)
imx_pcie_init # device_initcall()
bus_register_notifier(&pci_bus_type, &imx_pcie_nb)
- naming BUS_NOTIFY_DEL_DEVICE function:
apple_pcie_release_device()
imx_pcie_del_device()
- tracking entries in use via bitmap vs scanning hardware for
invalid entries:
bitmap_find_free_region # apple
imx_pcie_config_lut # imx
for (i = 0; i < IMX95_MAX_LUT; i++)
regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, &data1)
if (data1 & IMX95_PE0_LUT_VLD)
continue
When we fix a bug in one driver, it's easier to check whether other
drivers also need the fix if they use the same structure and names.
Bjorn
On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > [+cc IOMMU and pcie-apple.c folks for comment]
> >
> > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > This involves examining the msi-map and smmu-map to ensure consistent
> > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > controller is utilized as a fallback.
> > >
> > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > controller. This function configures the correct LUT based on Device Tree
> > > Settings (DTS).
> >
> > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > have to do this, I wish it were *more* similar, i.e., copy the
> > function names, bitmap tracking, code structure, etc.
> >
> > I don't really know how stream IDs work, but I assume they are used on
> > most or all arm64 platforms, so I'm a little surprised that of all the
> > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > this notifier.
>
> This is one of those things that's mostly at the mercy of the PCIe root
> complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> is derived directly from the PCI RID, sometimes with additional high-order
> bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> LUT is a particular feature of the the Synopsys IP - I know there's also one
> on the NXP Layerscape platforms, but on those it's programmed by the
> bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> properties to match. Ideally that's what i.MX should do as well, but hey.
Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
see that the LUT CSR accesses use IMX95_* definitions.
> If it's really necessary to do this programming from Linux, then there's
> still no point in it being dynamic - the mappings cannot ever change, since
> the rest of the kernel believes that what the DT said at boot time was
> already a property of the hardware. It would be a lot more logical, and
> likely simpler, for the driver to just read the relevant map property and
> program the entire LUT to match, all in one go at controller probe time.
> Rather like what's already commonly done with the parsing of "dma-ranges" to
> program address-translation LUTs for inbound windows.
>
> Plus that would also give a chance of safely dealing with bad DTs specifying
> invalid ID mappings (by refusing to probe at all). As it is, returning an
> error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> further notifiers from running at that point - the device will still be
> added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> without the controller being correctly programmed, which at best won't work
> and at worst may break the whole system.
Frank, could the imx LUT be programmed once at boot-time instead of at
device-add time? I'm guessing maybe not because apparently there is a
risk of running out of LUT entries?
It sounds like the consequences of running out of LUT entries are
catastrophic, e.g., memory corruption from mis-directed DMA? If
that's possible, I think we need to figure out how to prevent the
device from being used, not just dev_warn() about it.
Bjorn
On Mon, Jun 03, 2024 at 12:11:49PM -0500, Bjorn Helgaas wrote:
> On Fri, May 31, 2024 at 12:14:18PM -0400, Frank Li wrote:
> > On Thu, May 30, 2024 at 06:08:32PM -0500, Bjorn Helgaas wrote:
> > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > controller is utilized as a fallback.
> > > >
> > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > controller. This function configures the correct LUT based on Device Tree
> > > > Settings (DTS).
> > >
> > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > have to do this, I wish it were *more* similar, i.e., copy the
> > > function names, bitmap tracking, code structure, etc.
> >
> > Actually, I refer apple_pcie_bus_notifier(). I can't direct use apple's
> > implement because in imx95 have difference PCI host controller, another one
> > is PCI ECAM netc controller. At lease function name should be similar with
> > apple.
>
> I know it's different hardware, so obviously it can't be exactly the
> same. These are the differences that looked possibly unnecessary:
>
> - registering from initcall instead of .probe():
>
> apple_pcie_probe # .probe() method
> bus_register_notifier(&pci_bus_type, &apple_pcie_nb)
>
> imx_pcie_init # device_initcall()
> bus_register_notifier(&pci_bus_type, &imx_pcie_nb)
Maybe apple only one PCIe controller with multi ports. One nb just register
once.
If there are multi PCIe controller instance, second bus_register_notifier()
report error.
Of course, if you like, I can use global reference counter to handle this.
>
> - naming BUS_NOTIFY_DEL_DEVICE function:
>
> apple_pcie_release_device()
> imx_pcie_del_device()
Okay, I can change to release_device().
>
> - tracking entries in use via bitmap vs scanning hardware for
> invalid entries:
>
> bitmap_find_free_region # apple
>
> imx_pcie_config_lut # imx
> for (i = 0; i < IMX95_MAX_LUT; i++)
> regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, &data1)
> if (data1 & IMX95_PE0_LUT_VLD)
> continue
Okay, I can change to use bitmap.
>
> When we fix a bug in one driver, it's easier to check whether other
> drivers also need the fix if they use the same structure and names.
>
> Bjorn
On Mon, Jun 03, 2024 at 12:19:21PM -0500, Bjorn Helgaas wrote:
> On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > [+cc IOMMU and pcie-apple.c folks for comment]
> > >
> > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > controller is utilized as a fallback.
> > > >
> > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > controller. This function configures the correct LUT based on Device Tree
> > > > Settings (DTS).
> > >
> > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > have to do this, I wish it were *more* similar, i.e., copy the
> > > function names, bitmap tracking, code structure, etc.
> > >
> > > I don't really know how stream IDs work, but I assume they are used on
> > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > this notifier.
> >
> > This is one of those things that's mostly at the mercy of the PCIe root
> > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > is derived directly from the PCI RID, sometimes with additional high-order
> > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > on the NXP Layerscape platforms, but on those it's programmed by the
> > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > properties to match. Ideally that's what i.MX should do as well, but hey.
>
> Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> see that the LUT CSR accesses use IMX95_* definitions.
Yes, it convert 16bit RID to 6bit stream id.
>
> > If it's really necessary to do this programming from Linux, then there's
> > still no point in it being dynamic - the mappings cannot ever change, since
> > the rest of the kernel believes that what the DT said at boot time was
> > already a property of the hardware. It would be a lot more logical, and
> > likely simpler, for the driver to just read the relevant map property and
> > program the entire LUT to match, all in one go at controller probe time.
> > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > program address-translation LUTs for inbound windows.
> >
> > Plus that would also give a chance of safely dealing with bad DTs specifying
> > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > further notifiers from running at that point - the device will still be
> > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > without the controller being correctly programmed, which at best won't work
> > and at worst may break the whole system.
>
> Frank, could the imx LUT be programmed once at boot-time instead of at
> device-add time? I'm guessing maybe not because apparently there is a
> risk of running out of LUT entries?
It is not good idea to depend on boot loader so much. Some hot plug devics
(SD7.0) may plug after system boot. Two PCIe instances shared one set
of 6bits stream id (total 64). Assume total 16 assign to two PCIe
controllers. each have 8 stream id. If use uboot assign it static, each
PCIe controller have below 8 devices. It will be failrue one controller
connect 7, another connect 9. but if dynamtic alloc when devices add, both
controller can work.
Although we have not so much devices now, this way give us possility to
improve it in future.
>
> It sounds like the consequences of running out of LUT entries are
> catastrophic, e.g., memory corruption from mis-directed DMA? If
> that's possible, I think we need to figure out how to prevent the
> device from being used, not just dev_warn() about it.
Yes, but so far, we have not met such problem now. We can improve it when
we really face such problem.
>
> Bjorn
On Mon, Jun 03, 2024 at 02:42:45PM -0400, Frank Li wrote:
> On Mon, Jun 03, 2024 at 12:19:21PM -0500, Bjorn Helgaas wrote:
> > On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > > [+cc IOMMU and pcie-apple.c folks for comment]
> > > >
> > > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > > controller is utilized as a fallback.
> > > > >
> > > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > > controller. This function configures the correct LUT based on Device Tree
> > > > > Settings (DTS).
> > > >
> > > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > > have to do this, I wish it were *more* similar, i.e., copy the
> > > > function names, bitmap tracking, code structure, etc.
> > > >
> > > > I don't really know how stream IDs work, but I assume they are used on
> > > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > > this notifier.
> > >
> > > This is one of those things that's mostly at the mercy of the PCIe root
> > > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > > is derived directly from the PCI RID, sometimes with additional high-order
> > > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > > on the NXP Layerscape platforms, but on those it's programmed by the
> > > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > > properties to match. Ideally that's what i.MX should do as well, but hey.
> >
> > Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> > see that the LUT CSR accesses use IMX95_* definitions.
>
> Yes, it convert 16bit RID to 6bit stream id.
IIUC, you're saying this is not a Synopsys feature, it's an i.MX
feature.
> > > If it's really necessary to do this programming from Linux, then there's
> > > still no point in it being dynamic - the mappings cannot ever change, since
> > > the rest of the kernel believes that what the DT said at boot time was
> > > already a property of the hardware. It would be a lot more logical, and
> > > likely simpler, for the driver to just read the relevant map property and
> > > program the entire LUT to match, all in one go at controller probe time.
> > > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > > program address-translation LUTs for inbound windows.
> > >
> > > Plus that would also give a chance of safely dealing with bad DTs specifying
> > > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > > further notifiers from running at that point - the device will still be
> > > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > > without the controller being correctly programmed, which at best won't work
> > > and at worst may break the whole system.
> >
> > Frank, could the imx LUT be programmed once at boot-time instead of at
> > device-add time? I'm guessing maybe not because apparently there is a
> > risk of running out of LUT entries?
>
> It is not good idea to depend on boot loader so much.
I meant "could this be programmed once when the Linux imx host
controller driver is probed?" But from the below, it sounds like
that's not possible in general because you don't have enough stream
IDs to do that.
> Some hot plug devics
> (SD7.0) may plug after system boot. Two PCIe instances shared one set
> of 6bits stream id (total 64). Assume total 16 assign to two PCIe
> controllers. each have 8 stream id. If use uboot assign it static, each
> PCIe controller have below 8 devices. It will be failrue one controller
> connect 7, another connect 9. but if dynamtic alloc when devices add, both
> controller can work.
>
> Although we have not so much devices now, this way give us possility to
> improve it in future.
>
> > It sounds like the consequences of running out of LUT entries are
> > catastrophic, e.g., memory corruption from mis-directed DMA? If
> > that's possible, I think we need to figure out how to prevent the
> > device from being used, not just dev_warn() about it.
>
> Yes, but so far, we have not met such problem now. We can improve it when
> we really face such problem.
If this controller can only support DMA from a limited number of
endpoints below it, I think we should figure out how to enforce that
directly. Maybe we can prevent drivers from enabling bus mastering or
something. I'm not happy with the idea of waiting for and debugging a
report of data corruption.
Bjorn
On Mon, Jun 03, 2024 at 01:56:27PM -0500, Bjorn Helgaas wrote:
> On Mon, Jun 03, 2024 at 02:42:45PM -0400, Frank Li wrote:
> > On Mon, Jun 03, 2024 at 12:19:21PM -0500, Bjorn Helgaas wrote:
> > > On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > > > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > > > [+cc IOMMU and pcie-apple.c folks for comment]
> > > > >
> > > > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > > > controller is utilized as a fallback.
> > > > > >
> > > > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > > > controller. This function configures the correct LUT based on Device Tree
> > > > > > Settings (DTS).
> > > > >
> > > > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > > > have to do this, I wish it were *more* similar, i.e., copy the
> > > > > function names, bitmap tracking, code structure, etc.
> > > > >
> > > > > I don't really know how stream IDs work, but I assume they are used on
> > > > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > > > this notifier.
> > > >
> > > > This is one of those things that's mostly at the mercy of the PCIe root
> > > > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > > > is derived directly from the PCI RID, sometimes with additional high-order
> > > > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > > > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > > > on the NXP Layerscape platforms, but on those it's programmed by the
> > > > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > > > properties to match. Ideally that's what i.MX should do as well, but hey.
> > >
> > > Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> > > see that the LUT CSR accesses use IMX95_* definitions.
> >
> > Yes, it convert 16bit RID to 6bit stream id.
>
> IIUC, you're saying this is not a Synopsys feature, it's an i.MX
> feature.
Yes, it is i.MX feature. But I think other vendor should have similar
situation if use old arm smmu.
>
> > > > If it's really necessary to do this programming from Linux, then there's
> > > > still no point in it being dynamic - the mappings cannot ever change, since
> > > > the rest of the kernel believes that what the DT said at boot time was
> > > > already a property of the hardware. It would be a lot more logical, and
> > > > likely simpler, for the driver to just read the relevant map property and
> > > > program the entire LUT to match, all in one go at controller probe time.
> > > > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > > > program address-translation LUTs for inbound windows.
> > > >
> > > > Plus that would also give a chance of safely dealing with bad DTs specifying
> > > > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > > > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > > > further notifiers from running at that point - the device will still be
> > > > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > > > without the controller being correctly programmed, which at best won't work
> > > > and at worst may break the whole system.
> > >
> > > Frank, could the imx LUT be programmed once at boot-time instead of at
> > > device-add time? I'm guessing maybe not because apparently there is a
> > > risk of running out of LUT entries?
> >
> > It is not good idea to depend on boot loader so much.
>
> I meant "could this be programmed once when the Linux imx host
> controller driver is probed?" But from the below, it sounds like
> that's not possible in general because you don't have enough stream
> IDs to do that.
Oh! sorry miss understand what your means. It is possible like what I did
at v3 version. But I think it is not good enough.
>
> > Some hot plug devics
> > (SD7.0) may plug after system boot. Two PCIe instances shared one set
> > of 6bits stream id (total 64). Assume total 16 assign to two PCIe
> > controllers. each have 8 stream id. If use uboot assign it static, each
> > PCIe controller have below 8 devices. It will be failrue one controller
> > connect 7, another connect 9. but if dynamtic alloc when devices add, both
> > controller can work.
> >
> > Although we have not so much devices now, this way give us possility to
> > improve it in future.
> >
> > > It sounds like the consequences of running out of LUT entries are
> > > catastrophic, e.g., memory corruption from mis-directed DMA? If
> > > that's possible, I think we need to figure out how to prevent the
> > > device from being used, not just dev_warn() about it.
> >
> > Yes, but so far, we have not met such problem now. We can improve it when
> > we really face such problem.
>
> If this controller can only support DMA from a limited number of
> endpoints below it, I think we should figure out how to enforce that
> directly. Maybe we can prevent drivers from enabling bus mastering or
> something. I'm not happy with the idea of waiting for and debugging a
> report of data corruption.
It may add a pre-add hook function to pci bridge. let me do more research.
Frank
>
> Bjorn
On 2024-06-03 6:19 pm, Bjorn Helgaas wrote:
> On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
>> On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
>>> [+cc IOMMU and pcie-apple.c folks for comment]
>>>
>>> On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
>>>> For the i.MX95, configuration of a LUT is necessary to convert Bus Device
>>>> Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
>>>> This involves examining the msi-map and smmu-map to ensure consistent
>>>> mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
>>>> registers are configured. In the absence of an msi-map, the built-in MSI
>>>> controller is utilized as a fallback.
>>>>
>>>> Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
>>>> upon the appearance of a new PCI device and when the bus is an iMX6 PCI
>>>> controller. This function configures the correct LUT based on Device Tree
>>>> Settings (DTS).
>>>
>>> This scheme is pretty similar to apple_pcie_bus_notifier(). If we
>>> have to do this, I wish it were *more* similar, i.e., copy the
>>> function names, bitmap tracking, code structure, etc.
>>>
>>> I don't really know how stream IDs work, but I assume they are used on
>>> most or all arm64 platforms, so I'm a little surprised that of all the
>>> PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
>>> this notifier.
>>
>> This is one of those things that's mostly at the mercy of the PCIe root
>> complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
>> is derived directly from the PCI RID, sometimes with additional high-order
>> bits hard-wired to disambiguate PCI segments. I believe this RID-translation
>> LUT is a particular feature of the the Synopsys IP - I know there's also one
>> on the NXP Layerscape platforms, but on those it's programmed by the
>> bootloader, which also generates the appropriate "msi-map" and "iommu-map"
>> properties to match. Ideally that's what i.MX should do as well, but hey.
>
> Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> see that the LUT CSR accesses use IMX95_* definitions.
Well, it's not unreasonable to call things "IMX95" in this context if
they are only relevant to the configuration used by i.MX95, and not to
the other i.MX SoCs which this driver also supports. However the data
register fields certainly look suspiciously similar to those used on
Layerscape[1], although I guess that still doesn't rule out it being
NXP's own widget either. Anyway, the exact details aren't really
significant, the point was really just to say don't expect this to
generalise much beyond what you've seen already, and that there's
precedent for bootloaders doing this for us.
>> If it's really necessary to do this programming from Linux, then there's
>> still no point in it being dynamic - the mappings cannot ever change, since
>> the rest of the kernel believes that what the DT said at boot time was
>> already a property of the hardware. It would be a lot more logical, and
>> likely simpler, for the driver to just read the relevant map property and
>> program the entire LUT to match, all in one go at controller probe time.
>> Rather like what's already commonly done with the parsing of "dma-ranges" to
>> program address-translation LUTs for inbound windows.
>>
>> Plus that would also give a chance of safely dealing with bad DTs specifying
>> invalid ID mappings (by refusing to probe at all). As it is, returning an
>> error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
>> further notifiers from running at that point - the device will still be
>> added, allowed to bind a driver, and able to start sending DMA/MSI traffic
>> without the controller being correctly programmed, which at best won't work
>> and at worst may break the whole system.
>
> Frank, could the imx LUT be programmed once at boot-time instead of at
> device-add time? I'm guessing maybe not because apparently there is a
> risk of running out of LUT entries?
The risk still exists just as much either way - if we have a bogus DT
and/or just more PCI RIDs present than we can handle, we're going to
have a bad time. There's no advantage to only finding that out once we
try to add the 33rd device and it's too late to even do anything about it.
In fact if anything, this notifier approach exacerbates that risk the
most by consuming one LUT entry per PCI RID regardless of whether an
"iommu-map-mask" is involved. Assuming the IMX95_PE0_LUT_MASK field is
the same as its Layerscape counterpart, we could support >32 RIDs if the
map and mask are constructed to squash multiple RIDs onto each StreamID
(the SMMU driver supports this), and we have the up-front information to
easily configure hardware masking in the LUT itself. It's not
necessarily possible to reconstruct such mappings from only seeing
individual input and output values one-by-one.
Thanks,
Robin.
[1]
https://source.denx.de/u-boot/u-boot/-/blob/master/drivers/pci/pcie_layerscape_fixup.c?ref_type=heads#L83
> It sounds like the consequences of running out of LUT entries are
> catastrophic, e.g., memory corruption from mis-directed DMA? If
> that's possible, I think we need to figure out how to prevent the
> device from being used, not just dev_warn() about it.
>
> Bjorn
On 5/31/24 17:58, Robin Murphy wrote:
> On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
>> [+cc IOMMU and pcie-apple.c folks for comment]
>>
>> On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
>>> For the i.MX95, configuration of a LUT is necessary to convert Bus
>>> Device
>>> Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
>>> This involves examining the msi-map and smmu-map to ensure consistent
>>> mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
>>> registers are configured. In the absence of an msi-map, the built-in MSI
>>> controller is utilized as a fallback.
>>>
>>> Additionally, register a PCI bus notifier to trigger
>>> imx_pcie_add_device()
>>> upon the appearance of a new PCI device and when the bus is an iMX6 PCI
>>> controller. This function configures the correct LUT based on Device
>>> Tree
>>> Settings (DTS).
>>
>> This scheme is pretty similar to apple_pcie_bus_notifier(). If we
>> have to do this, I wish it were *more* similar, i.e., copy the
>> function names, bitmap tracking, code structure, etc.
>>
>> I don't really know how stream IDs work, but I assume they are used on
>> most or all arm64 platforms, so I'm a little surprised that of all the
>> PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
>> this notifier.
>
> This is one of those things that's mostly at the mercy of the PCIe root
> complex implementation. Typically the SMMU StreamID and/or GIC ITS
> DeviceID is derived directly from the PCI RID, sometimes with additional
> high-order bits hard-wired to disambiguate PCI segments. I believe this
> RID-translation LUT is a particular feature of the the Synopsys IP - I
> know there's also one on the NXP Layerscape platforms, but on those it's
> programmed by the bootloader, which also generates the appropriate
> "msi-map" and "iommu-map" properties to match. Ideally that's what i.MX
> should do as well, but hey.
That's usually fine, except when SRIOV and/or hotplug devices (that is,
not discoverable at bootloader time) come into play. We came up with
this "solution" to cover these more dynamic scenarios.
https://source.denx.de/u-boot/u-boot/-/commit/2a5bbb13cc39102a68fcc31056925427ab44b591
---
Best Regards, Laurentiu
>> There's this path, which is pretty generic and does at least the
>> of_map_id() part of what you're doing in imx_pcie_add_device():
>>
>> __driver_probe_device
>> really_probe
>> pci_dma_configure #
>> pci_bus_type.dma_configure
>> of_dma_configure
>> of_dma_configure_id
>> of_iommu_configure
>> of_pci_iommu_init
>> of_iommu_configure_dev_id
>> of_map_id
>> of_iommu_xlate
>> ops = iommu_ops_from_fwnode
>> iommu_fwspec_init
>> ops->of_xlate(dev, iommu_spec)
>>
>> Maybe this needs to be extended somehow with a hook to do the
>> device-specific work like updating the LUT? Just speculating here,
>> the IOMMU folks will know how this is expected to work.
>
> Note that that particular code path has fundamental issues and much of
> it needs to go away (I'm working on it, but it's a rich ~8-year-old pile
> of technical debt...). IOMMU configuration needs to be happening at
> device_add() time via the IOMMU layer's own bus notifier.
>
> If it's really necessary to do this programming from Linux, then there's
> still no point in it being dynamic - the mappings cannot ever change,
> since the rest of the kernel believes that what the DT said at boot time
> was already a property of the hardware. It would be a lot more logical,
> and likely simpler, for the driver to just read the relevant map
> property and program the entire LUT to match, all in one go at
> controller probe time. Rather like what's already commonly done with the
> parsing of "dma-ranges" to program address-translation LUTs for inbound
> windows.
>
> Plus that would also give a chance of safely dealing with bad DTs
> specifying invalid ID mappings (by refusing to probe at all). As it is,
> returning an error from a child's BUS_NOTIFY_ADD_DEVICE does nothing
> except prevent any further notifiers from running at that point - the
> device will still be added, allowed to bind a driver, and able to start
> sending DMA/MSI traffic without the controller being correctly
> programmed, which at best won't work and at worst may break the whole
> system.
>
> Thanks,
> Robin.
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Mon, Jun 03, 2024 at 09:20:03PM +0100, Robin Murphy wrote:
> On 2024-06-03 6:19 pm, Bjorn Helgaas wrote:
> > On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > > [+cc IOMMU and pcie-apple.c folks for comment]
> > > >
> > > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > > controller is utilized as a fallback.
> > > > >
> > > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > > controller. This function configures the correct LUT based on Device Tree
> > > > > Settings (DTS).
> > > >
> > > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > > have to do this, I wish it were *more* similar, i.e., copy the
> > > > function names, bitmap tracking, code structure, etc.
> > > >
> > > > I don't really know how stream IDs work, but I assume they are used on
> > > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > > this notifier.
> > >
> > > This is one of those things that's mostly at the mercy of the PCIe root
> > > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > > is derived directly from the PCI RID, sometimes with additional high-order
> > > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > > on the NXP Layerscape platforms, but on those it's programmed by the
> > > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > > properties to match. Ideally that's what i.MX should do as well, but hey.
> >
> > Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> > see that the LUT CSR accesses use IMX95_* definitions.
>
> Well, it's not unreasonable to call things "IMX95" in this context if they
> are only relevant to the configuration used by i.MX95, and not to the other
> i.MX SoCs which this driver also supports. However the data register fields
> certainly look suspiciously similar to those used on Layerscape[1], although
> I guess that still doesn't rule out it being NXP's own widget either.
> Anyway, the exact details aren't really significant, the point was really
> just to say don't expect this to generalise much beyond what you've seen
> already, and that there's precedent for bootloaders doing this for us.
It is re-used layerscape design at this point. I don't know the history,
why choose use bootloader to config it, supposed it should be done at
kernel. We found some problem by using bootloader to config LUT. Most
layserscape system PCI devices are fixed during boot.
During I debug layerscape PCI, I was trapped by uboot many times. It is
quite anoise, especially using difference version uboot.
>
> > > If it's really necessary to do this programming from Linux, then there's
> > > still no point in it being dynamic - the mappings cannot ever change, since
> > > the rest of the kernel believes that what the DT said at boot time was
> > > already a property of the hardware. It would be a lot more logical, and
> > > likely simpler, for the driver to just read the relevant map property and
> > > program the entire LUT to match, all in one go at controller probe time.
> > > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > > program address-translation LUTs for inbound windows.
> > >
> > > Plus that would also give a chance of safely dealing with bad DTs specifying
> > > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > > further notifiers from running at that point - the device will still be
> > > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > > without the controller being correctly programmed, which at best won't work
> > > and at worst may break the whole system.
> >
> > Frank, could the imx LUT be programmed once at boot-time instead of at
> > device-add time? I'm guessing maybe not because apparently there is a
> > risk of running out of LUT entries?
>
> The risk still exists just as much either way - if we have a bogus DT and/or
> just more PCI RIDs present than we can handle, we're going to have a bad
> time. There's no advantage to only finding that out once we try to add the
> 33rd device and it's too late to even do anything about it.
>
> In fact if anything, this notifier approach exacerbates that risk the most
> by consuming one LUT entry per PCI RID regardless of whether an
> "iommu-map-mask" is involved. Assuming the IMX95_PE0_LUT_MASK field is the
> same as its Layerscape counterpart, we could support >32 RIDs if the map and
> mask are constructed to squash multiple RIDs onto each StreamID (the SMMU
> driver supports this), and we have the up-front information to easily
> configure hardware masking in the LUT itself. It's not necessarily possible
> to reconstruct such mappings from only seeing individual input and output
> values one-by-one.
iommu may share one stream id for multi-devices. but ITS MSI can't. each
device's MSI index start from 0. It needs difference stream id for each
device.
>
> Thanks,
> Robin.
>
> [1] https://source.denx.de/u-boot/u-boot/-/blob/master/drivers/pci/pcie_layerscape_fixup.c?ref_type=heads#L83
Thanks, but It can't resolve device hot-plug problem.
>
> > It sounds like the consequences of running out of LUT entries are
> > catastrophic, e.g., memory corruption from mis-directed DMA? If
> > that's possible, I think we need to figure out how to prevent the
> > device from being used, not just dev_warn() about it.
> >
> > Bjorn
On Mon, Jun 03, 2024 at 11:29:38PM +0300, Laurentiu Tudor wrote:
>
>
> On 5/31/24 17:58, Robin Murphy wrote:
> > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > [+cc IOMMU and pcie-apple.c folks for comment]
> > >
> > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > For the i.MX95, configuration of a LUT is necessary to convert
> > > > Bus Device
> > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > controller is utilized as a fallback.
> > > >
> > > > Additionally, register a PCI bus notifier to trigger
> > > > imx_pcie_add_device()
> > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > controller. This function configures the correct LUT based on
> > > > Device Tree
> > > > Settings (DTS).
> > >
> > > This scheme is pretty similar to apple_pcie_bus_notifier().? If we
> > > have to do this, I wish it were *more* similar, i.e., copy the
> > > function names, bitmap tracking, code structure, etc.
> > >
> > > I don't really know how stream IDs work, but I assume they are used on
> > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > this notifier.
> >
> > This is one of those things that's mostly at the mercy of the PCIe root
> > complex implementation. Typically the SMMU StreamID and/or GIC ITS
> > DeviceID is derived directly from the PCI RID, sometimes with additional
> > high-order bits hard-wired to disambiguate PCI segments. I believe this
> > RID-translation LUT is a particular feature of the the Synopsys IP - I
> > know there's also one on the NXP Layerscape platforms, but on those it's
> > programmed by the bootloader, which also generates the appropriate
> > "msi-map" and "iommu-map" properties to match. Ideally that's what i.MX
> > should do as well, but hey.
>
> That's usually fine, except when SRIOV and/or hotplug devices (that is, not
> discoverable at bootloader time) come into play. We came up with this
> "solution" to cover these more dynamic scenarios.
>
> https://source.denx.de/u-boot/u-boot/-/commit/2a5bbb13cc39102a68fcc31056925427ab44b591
If my understand is correct, basic it was still pre-allocate method. Just
reserve more streams id for SRIOV and hotplugs. It was not really depend
on what devices in system.
Frank
>
> ---
> Best Regards, Laurentiu
>
> > > There's this path, which is pretty generic and does at least the
> > > of_map_id() part of what you're doing in imx_pcie_add_device():
> > >
> > > ???? __driver_probe_device
> > > ?????? really_probe
> > > ???????? pci_dma_configure?????????????????????? #
> > > pci_bus_type.dma_configure
> > > ?????????? of_dma_configure
> > > ???????????? of_dma_configure_id
> > > ?????????????? of_iommu_configure
> > > ???????????????? of_pci_iommu_init
> > > ?????????????????? of_iommu_configure_dev_id
> > > ???????????????????? of_map_id
> > > ???????????????????? of_iommu_xlate
> > > ?????????????????????? ops = iommu_ops_from_fwnode
> > > ?????????????????????? iommu_fwspec_init
> > > ?????????????????????? ops->of_xlate(dev, iommu_spec)
> > >
> > > Maybe this needs to be extended somehow with a hook to do the
> > > device-specific work like updating the LUT?? Just speculating here,
> > > the IOMMU folks will know how this is expected to work.
> >
> > Note that that particular code path has fundamental issues and much of
> > it needs to go away (I'm working on it, but it's a rich ~8-year-old pile
> > of technical debt...). IOMMU configuration needs to be happening at
> > device_add() time via the IOMMU layer's own bus notifier.
> >
> > If it's really necessary to do this programming from Linux, then there's
> > still no point in it being dynamic - the mappings cannot ever change,
> > since the rest of the kernel believes that what the DT said at boot time
> > was already a property of the hardware. It would be a lot more logical,
> > and likely simpler, for the driver to just read the relevant map
> > property and program the entire LUT to match, all in one go at
> > controller probe time. Rather like what's already commonly done with the
> > parsing of "dma-ranges" to program address-translation LUTs for inbound
> > windows.
> >
> > Plus that would also give a chance of safely dealing with bad DTs
> > specifying invalid ID mappings (by refusing to probe at all). As it is,
> > returning an error from a child's BUS_NOTIFY_ADD_DEVICE does nothing
> > except prevent any further notifiers from running at that point - the
> > device will still be added, allowed to bind a driver, and able to start
> > sending DMA/MSI traffic without the controller being correctly
> > programmed, which at best won't work and at worst may break the whole
> > system.
> >
> > Thanks,
> > Robin.
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Mon, 03 Jun 2024 22:04:23 +0100,
Frank Li <[email protected]> wrote:
>
> iommu may share one stream id for multi-devices. but ITS MSI can't. each
> device's MSI index start from 0. It needs difference stream id for each
> device.
That's not quite true. We go through all sort of hoops to find about
device aliasing on PCI and allow devices that translate into the same
DID to get MSIs.
Of course, just like the IOMMU, you lose any form of isolation, but
you get what you pay for.
M.
--
Without deviation from the norm, progress is not possible.
On Tue, Jun 04, 2024 at 04:25:51PM +0100, Marc Zyngier wrote:
> On Mon, 03 Jun 2024 22:04:23 +0100,
> Frank Li <[email protected]> wrote:
> >
> > iommu may share one stream id for multi-devices. but ITS MSI can't. each
> > device's MSI index start from 0. It needs difference stream id for each
> > device.
>
> That's not quite true. We go through all sort of hoops to find about
> device aliasing on PCI and allow devices that translate into the same
> DID to get MSIs.
Could you please point me the code location? I remember I met error when I
try to enable MSI for EP support one year ago.
https://lore.kernel.org/imx/[email protected]/
I remember that it is failure try to allocate more irqs with the same DID.
Frank
>
> Of course, just like the IOMMU, you lose any form of isolation, but
> you get what you pay for.
Anyways, if there are one quirk in PCI system, bridge driver can detect
PCI devices add/remove. it will be better if assign stream ID as need,
compared with static allocate.
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
On Mon, Jun 03, 2024 at 04:07:55PM -0400, Frank Li wrote:
> On Mon, Jun 03, 2024 at 01:56:27PM -0500, Bjorn Helgaas wrote:
> > On Mon, Jun 03, 2024 at 02:42:45PM -0400, Frank Li wrote:
> > > On Mon, Jun 03, 2024 at 12:19:21PM -0500, Bjorn Helgaas wrote:
> > > > On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > > > > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > > > > [+cc IOMMU and pcie-apple.c folks for comment]
> > > > > >
> > > > > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > > > > controller is utilized as a fallback.
> > > > > > >
> > > > > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > > > > controller. This function configures the correct LUT based on Device Tree
> > > > > > > Settings (DTS).
> > > > > >
> > > > > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > > > > have to do this, I wish it were *more* similar, i.e., copy the
> > > > > > function names, bitmap tracking, code structure, etc.
> > > > > >
> > > > > > I don't really know how stream IDs work, but I assume they are used on
> > > > > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > > > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > > > > this notifier.
> > > > >
> > > > > This is one of those things that's mostly at the mercy of the PCIe root
> > > > > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > > > > is derived directly from the PCI RID, sometimes with additional high-order
> > > > > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > > > > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > > > > on the NXP Layerscape platforms, but on those it's programmed by the
> > > > > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > > > > properties to match. Ideally that's what i.MX should do as well, but hey.
> > > >
> > > > Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> > > > see that the LUT CSR accesses use IMX95_* definitions.
> > >
> > > Yes, it convert 16bit RID to 6bit stream id.
> >
> > IIUC, you're saying this is not a Synopsys feature, it's an i.MX
> > feature.
>
> Yes, it is i.MX feature. But I think other vendor should have similar
> situation if use old arm smmu.
>
> >
> > > > > If it's really necessary to do this programming from Linux, then there's
> > > > > still no point in it being dynamic - the mappings cannot ever change, since
> > > > > the rest of the kernel believes that what the DT said at boot time was
> > > > > already a property of the hardware. It would be a lot more logical, and
> > > > > likely simpler, for the driver to just read the relevant map property and
> > > > > program the entire LUT to match, all in one go at controller probe time.
> > > > > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > > > > program address-translation LUTs for inbound windows.
> > > > >
> > > > > Plus that would also give a chance of safely dealing with bad DTs specifying
> > > > > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > > > > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > > > > further notifiers from running at that point - the device will still be
> > > > > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > > > > without the controller being correctly programmed, which at best won't work
> > > > > and at worst may break the whole system.
> > > >
> > > > Frank, could the imx LUT be programmed once at boot-time instead of at
> > > > device-add time? I'm guessing maybe not because apparently there is a
> > > > risk of running out of LUT entries?
> > >
> > > It is not good idea to depend on boot loader so much.
> >
> > I meant "could this be programmed once when the Linux imx host
> > controller driver is probed?" But from the below, it sounds like
> > that's not possible in general because you don't have enough stream
> > IDs to do that.
>
> Oh! sorry miss understand what your means. It is possible like what I did
> at v3 version. But I think it is not good enough.
>
> >
> > > Some hot plug devics
> > > (SD7.0) may plug after system boot. Two PCIe instances shared one set
> > > of 6bits stream id (total 64). Assume total 16 assign to two PCIe
> > > controllers. each have 8 stream id. If use uboot assign it static, each
> > > PCIe controller have below 8 devices. It will be failrue one controller
> > > connect 7, another connect 9. but if dynamtic alloc when devices add, both
> > > controller can work.
> > >
> > > Although we have not so much devices now, this way give us possility to
> > > improve it in future.
> > >
> > > > It sounds like the consequences of running out of LUT entries are
> > > > catastrophic, e.g., memory corruption from mis-directed DMA? If
> > > > that's possible, I think we need to figure out how to prevent the
> > > > device from being used, not just dev_warn() about it.
> > >
> > > Yes, but so far, we have not met such problem now. We can improve it when
> > > we really face such problem.
> >
> > If this controller can only support DMA from a limited number of
> > endpoints below it, I think we should figure out how to enforce that
> > directly. Maybe we can prevent drivers from enabling bus mastering or
> > something. I'm not happy with the idea of waiting for and debugging a
> > report of data corruption.
>
> It may add a pre-add hook function to pci bridge. let me do more research.
Hi Bjorn:
int pci_setup_device(struct pci_dev *dev)
{
dev->error_state = pci_channel_io_normal;
...
pci_fixup_device(pci_fixup_early, dev);
^^^ I can add fixup hook for pci_fixup_early. If not resource,
I can set dev->error_state to pci_channel_io_frozen or
pci_channel_io_perm_failure
And add below check here after call hook function.
if (dev->error_state != pci_channel_io_normal)
return -EIO;
}
How do you think this method? If you agree, I can continue search device
remove hook up.
Frank
>
> Frank
>
> >
> > Bjorn
On Tue, May 28, 2024 at 03:39:13PM -0400, Frank Li wrote:
> Fixed 8mp EP mode problem.
hi Manivannan:
Do you have any additional comments except for the patch
Config look up table(LUT) to support MSI ITS and IOMMU for i.MX95?
Frank Li
>
> imx6 actaully for all imx chips (imx6*, imx7*, imx8*, imx9*). To avoid
> confuse, rename all imx6_* to imx_*, IMX6_* to IMX_*. pci-imx6.c to
> pci-imx.c to avoid confuse.
>
> Using callback to reduce switch case for core reset and refclk.
>
> Add imx95 iommux and its stream id information.
>
> Base on linux-pci/controller/imx
>
> To: Richard Zhu <[email protected]>
> To: Lucas Stach <[email protected]>
> To: Lorenzo Pieralisi <[email protected]>
> To: Krzysztof Wilczyński <[email protected]>
> To: Rob Herring <[email protected]>
> To: Bjorn Helgaas <[email protected]>
> To: Shawn Guo <[email protected]>
> To: Sascha Hauer <[email protected]>
> To: Pengutronix Kernel Team <[email protected]>
> To: Fabio Estevam <[email protected]>
> To: NXP Linux Team <[email protected]>
> To: Philipp Zabel <[email protected]>
> To: Liam Girdwood <[email protected]>
> To: Mark Brown <[email protected]>
> To: Manivannan Sadhasivam <[email protected]>
> To: Krzysztof Kozlowski <[email protected]>
> To: Conor Dooley <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Signed-off-by: Frank Li <[email protected]>
>
> Changes in v5:
> - Rebase to linux-pci next. fix conflict with gpiod change
> - Add rob and cornor's review tag
> - Link to v4: https://lore.kernel.org/r/[email protected]
>
> Changes in v4:
> - Improve comment message for patch 1 and 2.
> - Rework commit message for patch 3 and add mani's review tag
> - Remove file rename patch and update maintainer patch
> - [PATCH v3 06/11] PCI: imx: Simplify switch-case logic by involve set_ref_clk callback
> remove extra space.
> keep original comments format (wrap at 80 column width)
> update error message "'Failed to enable PCIe REFCLK'"
> - PATCH v3 07/11] PCI: imx: Simplify switch-case logic by involve core_reset callback
> keep exact the logic as original code
> - Add patch to update comment about workaround ERR010728
> - Add patch about help function imx_pcie_match_device()
> - Using bus device notify to update LUT information for imx95 to avoid
> parse iommu-map and msi-map in driver code. Bus notify will better and
> only update lut when device added.
> - split patch call PHY interface function.
> - Improve commit message for imx8q. remove local-address dts proptery. and
> use standard "range" to convert cpu address to bus address.
> - Check entry in cpu_fix function is too late. Check it at probe
> - Link to v3: https://lore.kernel.org/r/[email protected]
>
> Changes in v3:
> - Add an EP fixed patch
> PCI: imx6: Fix PCIe link down when i.MX8MM and i.MX8MP PCIe is EP mode
> PCI: imx6: Fix i.MX8MP PCIe EP can not trigger MSI
> - Add 8qxp rc support
> dt-bing yaml pass binding check
> make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 dt_binding_check DT_SCHEMA_FILES=fsl,imx6q-pcie.yaml
> LINT Documentation/devicetree/bindings
> DTEX Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.example.dts
> CHKDT Documentation/devicetree/bindings/processed-schema.json
> SCHEMA Documentation/devicetree/bindings/processed-schema.json
> DTC_CHK Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.example.dtb
>
> - Link to v2: https://lore.kernel.org/r/[email protected]
>
> Changes in v2:
> - remove file to 'pcie-imx.c'
> - keep CONFIG unchange.
> - Link to v1: https://lore.kernel.org/r/[email protected]
>
> ---
> Frank Li (8):
> PCI: imx6: Rename imx6_* with imx_*
> PCI: imx6: Introduce SoC specific callbacks for controlling REFCLK
> PCI: imx6: Simplify switch-case logic by involve core_reset callback
> PCI: imx6: Improve comment for workaround ERR010728
> PCI: imx6: Add help function imx_pcie_match_device()
> PCI: imx6: Config look up table(LUT) to support MSI ITS and IOMMU for i.MX95
> PCI: imx6: Consolidate redundant if-checks
> PCI: imx6: Call: Common PHY API to set mode, speed, and submode
>
> Richard Zhu (4):
> PCI: imx6: Fix establish link failure in EP mode for iMX8MM and iMX8MP
> PCI: imx6: Fix i.MX8MP PCIe EP's occasional failure to trigger MSI
> dt-bindings: imx6q-pcie: Add i.MX8Q pcie compatible string
> PCI: imx6: Add i.MX8Q PCIe root complex (RC) support
>
> .../devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 16 +
> drivers/pci/controller/dwc/pci-imx6.c | 1181 ++++++++++++--------
> 2 files changed, 730 insertions(+), 467 deletions(-)
> ---
> base-commit: 50d96936b7b1be01bcc7b9ff898191214ee72697
> change-id: 20240227-pci2_upstream-0cdd19a15163
>
> Best regards,
> ---
> Frank Li <[email protected]>
>
On Thu, Jun 06, 2024 at 04:24:17PM -0400, Frank Li wrote:
> On Mon, Jun 03, 2024 at 04:07:55PM -0400, Frank Li wrote:
> > On Mon, Jun 03, 2024 at 01:56:27PM -0500, Bjorn Helgaas wrote:
> > > On Mon, Jun 03, 2024 at 02:42:45PM -0400, Frank Li wrote:
> > > > On Mon, Jun 03, 2024 at 12:19:21PM -0500, Bjorn Helgaas wrote:
> > > > > On Fri, May 31, 2024 at 03:58:49PM +0100, Robin Murphy wrote:
> > > > > > On 2024-05-31 12:08 am, Bjorn Helgaas wrote:
> > > > > > > [+cc IOMMU and pcie-apple.c folks for comment]
> > > > > > >
> > > > > > > On Tue, May 28, 2024 at 03:39:21PM -0400, Frank Li wrote:
> > > > > > > > For the i.MX95, configuration of a LUT is necessary to convert Bus Device
> > > > > > > > Function (BDF) to stream IDs, which are utilized by both IOMMU and ITS.
> > > > > > > > This involves examining the msi-map and smmu-map to ensure consistent
> > > > > > > > mapping of PCI BDF to the same stream IDs. Subsequently, LUT-related
> > > > > > > > registers are configured. In the absence of an msi-map, the built-in MSI
> > > > > > > > controller is utilized as a fallback.
> > > > > > > >
> > > > > > > > Additionally, register a PCI bus notifier to trigger imx_pcie_add_device()
> > > > > > > > upon the appearance of a new PCI device and when the bus is an iMX6 PCI
> > > > > > > > controller. This function configures the correct LUT based on Device Tree
> > > > > > > > Settings (DTS).
> > > > > > >
> > > > > > > This scheme is pretty similar to apple_pcie_bus_notifier(). If we
> > > > > > > have to do this, I wish it were *more* similar, i.e., copy the
> > > > > > > function names, bitmap tracking, code structure, etc.
> > > > > > >
> > > > > > > I don't really know how stream IDs work, but I assume they are used on
> > > > > > > most or all arm64 platforms, so I'm a little surprised that of all the
> > > > > > > PCI host drivers used on arm64, only pcie-apple.c and pci-imx6.c need
> > > > > > > this notifier.
> > > > > >
> > > > > > This is one of those things that's mostly at the mercy of the PCIe root
> > > > > > complex implementation. Typically the SMMU StreamID and/or GIC ITS DeviceID
> > > > > > is derived directly from the PCI RID, sometimes with additional high-order
> > > > > > bits hard-wired to disambiguate PCI segments. I believe this RID-translation
> > > > > > LUT is a particular feature of the the Synopsys IP - I know there's also one
> > > > > > on the NXP Layerscape platforms, but on those it's programmed by the
> > > > > > bootloader, which also generates the appropriate "msi-map" and "iommu-map"
> > > > > > properties to match. Ideally that's what i.MX should do as well, but hey.
> > > > >
> > > > > Maybe this RID-translation is a feature of i.MX, not of Synopsys? I
> > > > > see that the LUT CSR accesses use IMX95_* definitions.
> > > >
> > > > Yes, it convert 16bit RID to 6bit stream id.
> > >
> > > IIUC, you're saying this is not a Synopsys feature, it's an i.MX
> > > feature.
> >
> > Yes, it is i.MX feature. But I think other vendor should have similar
> > situation if use old arm smmu.
> >
> > >
> > > > > > If it's really necessary to do this programming from Linux, then there's
> > > > > > still no point in it being dynamic - the mappings cannot ever change, since
> > > > > > the rest of the kernel believes that what the DT said at boot time was
> > > > > > already a property of the hardware. It would be a lot more logical, and
> > > > > > likely simpler, for the driver to just read the relevant map property and
> > > > > > program the entire LUT to match, all in one go at controller probe time.
> > > > > > Rather like what's already commonly done with the parsing of "dma-ranges" to
> > > > > > program address-translation LUTs for inbound windows.
> > > > > >
> > > > > > Plus that would also give a chance of safely dealing with bad DTs specifying
> > > > > > invalid ID mappings (by refusing to probe at all). As it is, returning an
> > > > > > error from a child's BUS_NOTIFY_ADD_DEVICE does nothing except prevent any
> > > > > > further notifiers from running at that point - the device will still be
> > > > > > added, allowed to bind a driver, and able to start sending DMA/MSI traffic
> > > > > > without the controller being correctly programmed, which at best won't work
> > > > > > and at worst may break the whole system.
> > > > >
> > > > > Frank, could the imx LUT be programmed once at boot-time instead of at
> > > > > device-add time? I'm guessing maybe not because apparently there is a
> > > > > risk of running out of LUT entries?
> > > >
> > > > It is not good idea to depend on boot loader so much.
> > >
> > > I meant "could this be programmed once when the Linux imx host
> > > controller driver is probed?" But from the below, it sounds like
> > > that's not possible in general because you don't have enough stream
> > > IDs to do that.
> >
> > Oh! sorry miss understand what your means. It is possible like what I did
> > at v3 version. But I think it is not good enough.
> >
> > >
> > > > Some hot plug devics
> > > > (SD7.0) may plug after system boot. Two PCIe instances shared one set
> > > > of 6bits stream id (total 64). Assume total 16 assign to two PCIe
> > > > controllers. each have 8 stream id. If use uboot assign it static, each
> > > > PCIe controller have below 8 devices. It will be failrue one controller
> > > > connect 7, another connect 9. but if dynamtic alloc when devices add, both
> > > > controller can work.
> > > >
> > > > Although we have not so much devices now, this way give us possility to
> > > > improve it in future.
> > > >
> > > > > It sounds like the consequences of running out of LUT entries are
> > > > > catastrophic, e.g., memory corruption from mis-directed DMA? If
> > > > > that's possible, I think we need to figure out how to prevent the
> > > > > device from being used, not just dev_warn() about it.
> > > >
> > > > Yes, but so far, we have not met such problem now. We can improve it when
> > > > we really face such problem.
> > >
> > > If this controller can only support DMA from a limited number of
> > > endpoints below it, I think we should figure out how to enforce that
> > > directly. Maybe we can prevent drivers from enabling bus mastering or
> > > something. I'm not happy with the idea of waiting for and debugging a
> > > report of data corruption.
> >
> > It may add a pre-add hook function to pci bridge. let me do more research.
>
> Hi Bjorn:
>
> int pci_setup_device(struct pci_dev *dev)
> {
> dev->error_state = pci_channel_io_normal;
> ...
> pci_fixup_device(pci_fixup_early, dev);
>
> ^^^ I can add fixup hook for pci_fixup_early. If not resource,
> I can set dev->error_state to pci_channel_io_frozen or
> pci_channel_io_perm_failure
>
> And add below check here after call hook function.
>
> if (dev->error_state != pci_channel_io_normal)
> return -EIO;
>
> }
>
> How do you think this method? If you agree, I can continue search device
> remove hook up.
I think this would mean the device would not appear to be enumerated
at all, right? I.e., it wouldn't show up in lspci? And we couldn't
use even a pure programmed IO driver with no DMA or MSI?
I wonder if we should have a function pointer in struct
pci_host_bridge, kind of like the existing ->map_irq(), where we could
do host bridge-specific setup when enumerating a PCI device.
We'd still have to solve the issue of preventing DMA, but a hook like
that might avoid the need for a quirk or the bus notifier approach.
Bjorn