Message ID | 20241024-imx95_lut-v3-2-7509c9bbab86@nxp.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | PCI: add enabe(disable)_device() hook for bridge | expand |
On 2024-10-24 11:34 pm, Frank Li wrote: [...] > +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) > +{ > + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev); > + struct device_node *target; > + struct imx_pcie *imx_pcie; > + struct device *dev; > + int err_i, err_m; > + > + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); > + dev = imx_pcie->pci->dev; > + > + target = NULL; > + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); No, you still need to actually check "target" at this point - if it is now set, a mapping was found and "sid_i" is valid, otherwise if it still NULL, no mapping exists even if "err_i" is 0 (i.e. an "iommu-map" property was found, but did not contain any entries matching "rid" as input). Note that the target node is returned with a reference held, so needs an of_node_put() as well. Thanks, Robin. > + target = NULL; > + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m);
On Thu, Oct 24, 2024 at 06:34:45PM -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 callback function enable_device() and > disable_device() to config LUT when enable a new PCI device. > Callbacks are not *addition*, but it is how you are implementing the LUT configuration. Please reword it so. > Signed-off-by: Frank Li <Frank.Li@nxp.com> > --- > Change from v2 to v3 > - Use the "target" argument of of_map_id() > - Check if rid already in lut table when enable device > > change from v1 to v2 > - set callback to pci_host_bridge instead pci->ops. > --- > drivers/pci/controller/dwc/pci-imx6.c | 159 +++++++++++++++++++++++++++++++++- > 1 file changed, 158 insertions(+), 1 deletion(-) > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c > index 94f3411352bf0..95f06bfb9fc5e 100644 > --- a/drivers/pci/controller/dwc/pci-imx6.c > +++ b/drivers/pci/controller/dwc/pci-imx6.c > @@ -55,6 +55,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 { > @@ -82,6 +98,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_HAS_LUT BIT(8) > > #define imx_check_flag(pci, val) (pci->drvdata->flags & val) > > @@ -134,6 +151,7 @@ struct imx_pcie { > struct device *pd_pcie_phy; > struct phy *phy; > const struct imx_pcie_drvdata *drvdata; > + struct mutex lock; Please add a comment on what the lock is guarding. > }; > > /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ > @@ -925,6 +943,137 @@ static void imx_pcie_stop_link(struct dw_pcie *pci) > imx_pcie_ltssm_disable(dev); > } > > +static int imx_pcie_add_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid) s/reqid/rid > +{ > + 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; > + > + regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, &data2); > + > + /* Needn't add duplicated Request ID */ > + if (reqid == FIELD_GET(IMX95_PE0_LUT_REQID, data2)) So this means LUT entry is already present for the given RID (a buggy DT maybe). Don't you need to emit a warning here? > + return 0; > + } > + You need to bail out here if no free LUT entry is available. But I'd recommend to combine two loops to avoid having duplicated IMX95_PE0_LUT_VLD checks and program LUT only if there is any free entry available. > + 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 = IMX95_PE0_LUT_MASK; Also add a comment on why the mask is added along with the RID. > + 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"); "LUT entry is not available" > + return -EINVAL; > +} > + > +static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid) s/reqid/rid > +{ > + u32 data2 = 0; No need to initialize. > + 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); > + > + break; > + } > + } > +} > + > +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) > +{ > + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev); > + struct device_node *target; > + struct imx_pcie *imx_pcie; > + struct device *dev; > + int err_i, err_m; > + > + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); > + dev = imx_pcie->pci->dev; You can assign these at initialization time. > + > + target = NULL; > + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); > + target = NULL; What is the point in passing 'target' here? > + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); > + > + > + /* > + * msi-map iommu-map > + * Y Y ITS + SMMU, require the same sid > + * Y N ITS > + * N Y DWC MSI Ctrl + SMMU > + * N N DWC MSI Ctrl > + */ > + if (!err_i && !err_m) > + 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"); "iommu-map and msi-map entries mismatch!" - Mani
On Sat, Nov 02, 2024 at 05:19:37PM +0530, Manivannan Sadhasivam wrote: > On Thu, Oct 24, 2024 at 06:34:45PM -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 callback function enable_device() and > > disable_device() to config LUT when enable a new PCI device. > > > > Callbacks are not *addition*, but it is how you are implementing the LUT > configuration. Please reword it so. > > > Signed-off-by: Frank Li <Frank.Li@nxp.com> > > --- > > Change from v2 to v3 > > - Use the "target" argument of of_map_id() > > - Check if rid already in lut table when enable device > > > > change from v1 to v2 > > - set callback to pci_host_bridge instead pci->ops. > > --- > > drivers/pci/controller/dwc/pci-imx6.c | 159 +++++++++++++++++++++++++++++++++- > > 1 file changed, 158 insertions(+), 1 deletion(-) > > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c > > index 94f3411352bf0..95f06bfb9fc5e 100644 > > --- a/drivers/pci/controller/dwc/pci-imx6.c > > +++ b/drivers/pci/controller/dwc/pci-imx6.c > > @@ -55,6 +55,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 { > > @@ -82,6 +98,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_HAS_LUT BIT(8) > > > > #define imx_check_flag(pci, val) (pci->drvdata->flags & val) > > > > @@ -134,6 +151,7 @@ struct imx_pcie { > > struct device *pd_pcie_phy; > > struct phy *phy; > > const struct imx_pcie_drvdata *drvdata; > > + struct mutex lock; > > Please add a comment on what the lock is guarding. > > > }; > > > > /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ > > @@ -925,6 +943,137 @@ static void imx_pcie_stop_link(struct dw_pcie *pci) > > imx_pcie_ltssm_disable(dev); > > } > > > > +static int imx_pcie_add_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid) > > s/reqid/rid > > > +{ > > + 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; > > + > > + regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, &data2); > > + > > + /* Needn't add duplicated Request ID */ > > + if (reqid == FIELD_GET(IMX95_PE0_LUT_REQID, data2)) > > So this means LUT entry is already present for the given RID (a buggy DT maybe). > Don't you need to emit a warning here? > > > + return 0; > > + } > > + > > You need to bail out here if no free LUT entry is available. But I'd recommend > to combine two loops to avoid having duplicated IMX95_PE0_LUT_VLD checks and > program LUT only if there is any free entry available. > > > + 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 = IMX95_PE0_LUT_MASK; > > Also add a comment on why the mask is added along with the RID. > > > + 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"); > > "LUT entry is not available" > > > + return -EINVAL; > > +} > > + > > +static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid) > > s/reqid/rid > > > +{ > > + u32 data2 = 0; > > No need to initialize. > > > + 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); > > + > > + break; > > + } > > + } > > +} > > + > > +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) > > +{ > > + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev); > > + struct device_node *target; > > + struct imx_pcie *imx_pcie; > > + struct device *dev; > > + int err_i, err_m; > > + > > + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); > > + dev = imx_pcie->pci->dev; > > You can assign these at initialization time. > > > + > > + target = NULL; > > + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); > > + target = NULL; > > What is the point in passing 'target' here? See https://lore.kernel.org/imx/b479cad6-e0c5-48fb-bb8f-a70f7582cfd5@arm.com/ Marc Zyngier's comments: "Perhaps it is reasonable to assume that i.MX95 will never have SMMU/ITS mappings for low-numbered devices on bus 0, but in general this isn't very robust, and either way it's certainly not all that clear at first glance what assmuption is actually being made here. If it's significant whether a mapping actually exists or not for the given ID then you should really use the "target" argument of of_map_id() to determine that." See v4 https://lore.kernel.org/imx/20241101-imx95_lut-v4-2-0fdf9a2fe754@nxp.com/ + target = NULL; + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); + + /* + * Return failure if msi-map exist and no entry for rid because dwc common + * driver will skip setting up built-in MSI controller if msi-map existed. + * + * err_m target + * 0 NULL Return failure, function not work. + * !0 NULL msi-map not exist, use built-in MSI. + * 0 !NULL Find one entry. + * !0 !NULL Invalidate case. + */ > > > + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); > > + > > + > > + /* > > + * msi-map iommu-map > > + * Y Y ITS + SMMU, require the same sid > > + * Y N ITS > > + * N Y DWC MSI Ctrl + SMMU > > + * N N DWC MSI Ctrl > > + */ > > + if (!err_i && !err_m) > > + 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"); > > "iommu-map and msi-map entries mismatch!" > > - Mani > > -- > மணிவண்ணன் சதாசிவம்
On Sat, 02 Nov 2024 17:26:46 +0000, Frank Li <Frank.li@nxp.com> wrote: > > On Sat, Nov 02, 2024 at 05:19:37PM +0530, Manivannan Sadhasivam wrote: > > On Thu, Oct 24, 2024 at 06:34:45PM -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 callback function enable_device() and > > > disable_device() to config LUT when enable a new PCI device. > > > > > > > Callbacks are not *addition*, but it is how you are implementing the LUT > > configuration. Please reword it so. > > > > > Signed-off-by: Frank Li <Frank.Li@nxp.com> > > > --- > > > Change from v2 to v3 > > > - Use the "target" argument of of_map_id() > > > - Check if rid already in lut table when enable device > > > > > > change from v1 to v2 > > > - set callback to pci_host_bridge instead pci->ops. > > > --- > > > drivers/pci/controller/dwc/pci-imx6.c | 159 +++++++++++++++++++++++++++++++++- > > > 1 file changed, 158 insertions(+), 1 deletion(-) > > > > > > diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c > > > index 94f3411352bf0..95f06bfb9fc5e 100644 > > > --- a/drivers/pci/controller/dwc/pci-imx6.c > > > +++ b/drivers/pci/controller/dwc/pci-imx6.c > > > @@ -55,6 +55,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 { > > > @@ -82,6 +98,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_HAS_LUT BIT(8) > > > > > > #define imx_check_flag(pci, val) (pci->drvdata->flags & val) > > > > > > @@ -134,6 +151,7 @@ struct imx_pcie { > > > struct device *pd_pcie_phy; > > > struct phy *phy; > > > const struct imx_pcie_drvdata *drvdata; > > > + struct mutex lock; > > > > Please add a comment on what the lock is guarding. > > > > > }; > > > > > > /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ > > > @@ -925,6 +943,137 @@ static void imx_pcie_stop_link(struct dw_pcie *pci) > > > imx_pcie_ltssm_disable(dev); > > > } > > > > > > +static int imx_pcie_add_lut(struct imx_pcie *imx_pcie, u16 reqid, u8 sid) > > > > s/reqid/rid > > > > > +{ > > > + 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; > > > + > > > + regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, &data2); > > > + > > > + /* Needn't add duplicated Request ID */ > > > + if (reqid == FIELD_GET(IMX95_PE0_LUT_REQID, data2)) > > > > So this means LUT entry is already present for the given RID (a buggy DT maybe). > > Don't you need to emit a warning here? > > > > > + return 0; > > > + } > > > + > > > > You need to bail out here if no free LUT entry is available. But I'd recommend > > to combine two loops to avoid having duplicated IMX95_PE0_LUT_VLD checks and > > program LUT only if there is any free entry available. > > > > > + 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 = IMX95_PE0_LUT_MASK; > > > > Also add a comment on why the mask is added along with the RID. > > > > > + 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"); > > > > "LUT entry is not available" > > > > > + return -EINVAL; > > > +} > > > + > > > +static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 reqid) > > > > s/reqid/rid > > > > > +{ > > > + u32 data2 = 0; > > > > No need to initialize. > > > > > + 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); > > > + > > > + break; > > > + } > > > + } > > > +} > > > + > > > +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) > > > +{ > > > + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev); > > > + struct device_node *target; > > > + struct imx_pcie *imx_pcie; > > > + struct device *dev; > > > + int err_i, err_m; > > > + > > > + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); > > > + dev = imx_pcie->pci->dev; > > > > You can assign these at initialization time. > > > > > + > > > + target = NULL; > > > + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); > > > + target = NULL; > > > > What is the point in passing 'target' here? > > See https://lore.kernel.org/imx/b479cad6-e0c5-48fb-bb8f-a70f7582cfd5@arm.com/ > Marc Zyngier's comments: Not quite. That's Robin's email (I never commented on that particular patch). Nevertheless, "I approve this message". M.
On Sat, Nov 02, 2024 at 01:26:46PM -0400, Frank Li wrote: [...] > > > + > > > + target = NULL; > > > + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); > > > + target = NULL; > > > > What is the point in passing 'target' here? > > See https://lore.kernel.org/imx/b479cad6-e0c5-48fb-bb8f-a70f7582cfd5@arm.com/ > Marc Zyngier's comments: > > "Perhaps it is reasonable to assume that i.MX95 will never have SMMU/ITS > mappings for low-numbered devices on bus 0, but in general this isn't > very robust, and either way it's certainly not all that clear at first > glance what assmuption is actually being made here. If it's significant > whether a mapping actually exists or not for the given ID then you > should really use the "target" argument of of_map_id() to determine that." > > See v4 https://lore.kernel.org/imx/20241101-imx95_lut-v4-2-0fdf9a2fe754@nxp.com/ > Okay, thanks! I was confused by the fact that you never used 'target' in this version. But v4 clears it up. - Mani > + target = NULL; > + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); > + > + /* > + * Return failure if msi-map exist and no entry for rid because dwc common > + * driver will skip setting up built-in MSI controller if msi-map existed. > + * > + * err_m target > + * 0 NULL Return failure, function not work. > + * !0 NULL msi-map not exist, use built-in MSI. > + * 0 !NULL Find one entry. > + * !0 !NULL Invalidate case. > + */ > > > > > > > + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); > > > + > > > + > > > + /* > > > + * msi-map iommu-map > > > + * Y Y ITS + SMMU, require the same sid > > > + * Y N ITS > > > + * N Y DWC MSI Ctrl + SMMU > > > + * N N DWC MSI Ctrl > > > + */ > > > + if (!err_i && !err_m) > > > + 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"); > > > > "iommu-map and msi-map entries mismatch!" > > > > - Mani > > > > -- > > மணிவண்ணன் சதாசிவம்
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 94f3411352bf0..95f06bfb9fc5e 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -55,6 +55,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 { @@ -82,6 +98,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_HAS_LUT BIT(8) #define imx_check_flag(pci, val) (pci->drvdata->flags & val) @@ -134,6 +151,7 @@ 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 */ @@ -925,6 +943,137 @@ static void imx_pcie_stop_link(struct dw_pcie *pci) imx_pcie_ltssm_disable(dev); } +static int imx_pcie_add_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; + + regmap_read(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, &data2); + + /* Needn't add duplicated Request ID */ + if (reqid == FIELD_GET(IMX95_PE0_LUT_REQID, data2)) + return 0; + } + + 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); + + break; + } + } +} + +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) +{ + u32 sid_i = 0, sid_m = 0, rid = pci_dev_id(pdev); + struct device_node *target; + struct imx_pcie *imx_pcie; + struct device *dev; + int err_i, err_m; + + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); + dev = imx_pcie->pci->dev; + + target = NULL; + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", &target, &sid_i); + target = NULL; + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", &target, &sid_m); + + + /* + * msi-map iommu-map + * Y Y ITS + SMMU, require the same sid + * Y N ITS + * N Y DWC MSI Ctrl + SMMU + * N N DWC MSI Ctrl + */ + if (!err_i && !err_m) + 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; + } + + /* + * Both iommu-map and msi-map not exist, use dwc built-in MSI + * controller, do nothing here. + */ + if (err_i && err_m) + return 0; + + if (!err_i) + return imx_pcie_add_lut(imx_pcie, rid, sid_i); + + if (!err_m) + /* Hardware auto add 2 bit controller id ahead of stream ID */ + return imx_pcie_add_lut(imx_pcie, rid, sid_m & IMX95_SID_MASK); + + return 0; +} + +static void imx_pcie_disable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) +{ + struct imx_pcie *imx_pcie; + + imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); + imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); +} + static int imx_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -941,6 +1090,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } } + if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) { + pp->bridge->enable_device = imx_pcie_enable_device; + pp->bridge->disable_device = imx_pcie_disable_device; + } + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->drvdata->init_phy) @@ -1292,6 +1446,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) { @@ -1587,7 +1743,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_HAS_LUT, .clk_names = imx8mq_clks, .clks_cnt = ARRAY_SIZE(imx8mq_clks), .ltssm_off = IMX95_PE0_GEN_CTRL_3,
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 callback function enable_device() and disable_device() to config LUT when enable a new PCI device. Signed-off-by: Frank Li <Frank.Li@nxp.com> --- Change from v2 to v3 - Use the "target" argument of of_map_id() - Check if rid already in lut table when enable device change from v1 to v2 - set callback to pci_host_bridge instead pci->ops. --- drivers/pci/controller/dwc/pci-imx6.c | 159 +++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-)