* [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller
@ 2025-11-18 16:02 Vincent Guittot
2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Vincent Guittot @ 2025-11-18 16:02 UTC (permalink / raw)
To: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas,
jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt,
conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc,
ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel,
linux-pci, devicetree, linux-kernel, imx
Cc: cassel
The S32G SoC family has 2 PCIe controllers based on Designware IP.
Add the support for Host mode.
Change since v4:
- Moved allof position and added interrupt-names' restriction in yaml file
- Removed PCIE_S32G_PE0_LINK_DBG_2 and instead use PCIE_PORT_DEBUG0|1
after receiving confirmation that they are the same.
Change since v3:
- Added Root Port node and reorder irq in binding
- Added Root Port management in driver
- Fix Kconfig PCIE_NXP_S32G position
- Use default pme_turn_off method
- Use ops->init() to simplify init and suspend/resume sequence
- Fix some typos.
- Removed MPS and ERROR config. Let core code configs them.
- Removed s32g_pcie_disable_equalization() from internal team request
- Removed dw_pcie_link_up() from suspend/resume functions with [1]
- I'm still waiting feedback from internal team before removing
.get_ltssm() and .link_up() functions.
[1] https://lore.kernel.org/all/20251107044319.8356-1-manivannan.sadhasivam@oss.qualcomm.com/
Change since v2:
- More cleanup on DT binding to comply with schemas/pci/snps,dw-pcie.yaml
- Added new reg and bit fields in pcie-designware.h
- Rename Kconfig PCIE_NXP_S32G and files to use pcie-nxp-s32g prefix
- Prefixed s32G registers with PCIE_S32G_ and use generic regs otherwise
- Use memblock_start_of_DRAM to set coherency boundary and add comments
- Fixed suspend/resume sequence by adding missing pme_turn_off function
- Added .probe_type = PROBE_PREFER_ASYNCHRONOUS to speedup probe
- Added pm_runtime_no_callbacks() as device doesn't have runtime ops
- Use writel/readl in ctrl function instead of dw_pcie_write/read
- Move Maintainer section in a dedicated entry
Change since v1:
- Cleanup DT binding
- Removed useless description and fixed typo, naming and indentation.
- Removed nxp,phy-mode binding until we agreed on a generic binding.
Default (crnss) mode is used for now. Generic binding wil be discussed
in a separate patch.
- Removed max-link-speed and num-lanes which are coming from
snps,dw-pcie-common.yaml. They are needed only if to restrict from the
the default hw config.
- Added unevaluatedProperties: false
- Keep Phys in host node until dw support Root Port node.
- Removed nxp-s32g-pcie-phy-submode.h until there is a generic clock and
spectrum binding.
- Rename files to start with pcie-s32g instead of pci-s32g
- Cleanup pcie-s32-reg.h and use dw_pcie_find_capability()
- cleanup and rename in s32g-pcie.c in addtion to remove useless check or
duplicate code.
- dw_pcie_suspend/resume_noirq() doesn't woork, need to set child device
to reach lowest state.
- Added L: imx@lists.linux.dev in MAINTAINERS
Vincent Guittot (4):
dt-bindings: PCI: s32g: Add NXP PCIe controller
PCI: dw: Add more registers and bitfield definition
PCI: s32g: Add initial PCIe support (RC)
MAINTAINERS: Add MAINTAINER for NXP S32G PCIe driver
.../bindings/pci/nxp,s32g-pcie.yaml | 130 ++++++
MAINTAINERS | 9 +
drivers/pci/controller/dwc/Kconfig | 10 +
drivers/pci/controller/dwc/Makefile | 1 +
drivers/pci/controller/dwc/pcie-designware.h | 8 +
.../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 +
drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++
7 files changed, 570 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml
create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h
create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c
--
2.43.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP PCIe controller 2025-11-18 16:02 [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller Vincent Guittot @ 2025-11-18 16:02 ` Vincent Guittot 2025-11-18 16:52 ` Frank Li ` (2 more replies) 2025-11-18 16:02 ` [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition Vincent Guittot ` (2 subsequent siblings) 3 siblings, 3 replies; 15+ messages in thread From: Vincent Guittot @ 2025-11-18 16:02 UTC (permalink / raw) To: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx Cc: cassel Describe the PCIe host controller available on the S32G platforms. Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> Co-developed-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> Signed-off-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> --- .../bindings/pci/nxp,s32g-pcie.yaml | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml new file mode 100644 index 000000000000..da3106dfcf58 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/nxp,s32g-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP S32G2xxx/S32G3xxx PCIe Root Complex controller + +maintainers: + - Bogdan Hamciuc <bogdan.hamciuc@nxp.com> + - Ionut Vicovan <ionut.vicovan@nxp.com> + +description: + This PCIe controller is based on the Synopsys DesignWare PCIe IP. + The S32G SoC family has two PCIe controllers, which can be configured as + either Root Complex or Endpoint. + +properties: + compatible: + oneOf: + - enum: + - nxp,s32g2-pcie + - items: + - const: nxp,s32g3-pcie + - const: nxp,s32g2-pcie + + reg: + maxItems: 6 + + reg-names: + items: + - const: dbi + - const: dbi2 + - const: atu + - const: dma + - const: ctrl + - const: config + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + items: + - const: msi + - const: dma + minItems: 1 + + pcie@0: + description: + Describe the S32G Root Port. + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + phys: + maxItems: 1 + + required: + - reg + - phys + + unevaluatedProperties: false + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - ranges + - pcie@0 + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/phy/phy.h> + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie@40400000 { + compatible = "nxp,s32g3-pcie", "nxp,s32g2-pcie"; + reg = <0x00 0x40400000 0x0 0x00001000>, /* dbi registers */ + <0x00 0x40420000 0x0 0x00001000>, /* dbi2 registers */ + <0x00 0x40460000 0x0 0x00001000>, /* atu registers */ + <0x00 0x40470000 0x0 0x00001000>, /* dma registers */ + <0x00 0x40481000 0x0 0x000000f8>, /* ctrl registers */ + <0x5f 0xffffe000 0x0 0x00002000>; /* config space */ + reg-names = "dbi", "dbi2", "atu", "dma", "ctrl", "config"; + dma-coherent; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = + <0x81000000 0x0 0x00000000 0x5f 0xfffe0000 0x0 0x00010000>, + <0x82000000 0x0 0x00000000 0x58 0x00000000 0x0 0x80000000>, + <0x82000000 0x1 0x00000000 0x59 0x00000000 0x6 0xfffe0000>; + + bus-range = <0x0 0xff>; + interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi", "dma"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gic 0 0 GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gic 0 0 GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gic 0 0 GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>; + + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + device_type = "pci"; + phys = <&serdes0 PHY_TYPE_PCIE 0 0>; + }; + }; + }; -- 2.43.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP PCIe controller 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot @ 2025-11-18 16:52 ` Frank Li 2025-11-20 11:27 ` Manivannan Sadhasivam 2025-11-20 15:23 ` Rob Herring (Arm) 2 siblings, 0 replies; 15+ messages in thread From: Frank Li @ 2025-11-18 16:52 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, Nov 18, 2025 at 05:02:35PM +0100, Vincent Guittot wrote: > Describe the PCIe host controller available on the S32G platforms. > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Co-developed-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Signed-off-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- Reviewed-by: Frank Li <Frank.Li@nxp.com> > .../bindings/pci/nxp,s32g-pcie.yaml | 130 ++++++++++++++++++ > 1 file changed, 130 insertions(+) > create mode 100644 Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > > diff --git a/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > new file mode 100644 > index 000000000000..da3106dfcf58 > --- /dev/null > +++ b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > @@ -0,0 +1,130 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/pci/nxp,s32g-pcie.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: NXP S32G2xxx/S32G3xxx PCIe Root Complex controller > + > +maintainers: > + - Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > + - Ionut Vicovan <ionut.vicovan@nxp.com> > + > +description: > + This PCIe controller is based on the Synopsys DesignWare PCIe IP. > + The S32G SoC family has two PCIe controllers, which can be configured as > + either Root Complex or Endpoint. > + > +properties: > + compatible: > + oneOf: > + - enum: > + - nxp,s32g2-pcie > + - items: > + - const: nxp,s32g3-pcie > + - const: nxp,s32g2-pcie > + > + reg: > + maxItems: 6 > + > + reg-names: > + items: > + - const: dbi > + - const: dbi2 > + - const: atu > + - const: dma > + - const: ctrl > + - const: config > + > + interrupts: > + minItems: 1 > + maxItems: 2 > + > + interrupt-names: > + items: > + - const: msi > + - const: dma > + minItems: 1 > + > + pcie@0: > + description: > + Describe the S32G Root Port. > + type: object > + $ref: /schemas/pci/pci-pci-bridge.yaml# > + > + properties: > + reg: > + maxItems: 1 > + > + phys: > + maxItems: 1 > + > + required: > + - reg > + - phys > + > + unevaluatedProperties: false > + > +required: > + - compatible > + - reg > + - reg-names > + - interrupts > + - interrupt-names > + - ranges > + - pcie@0 > + > +allOf: > + - $ref: /schemas/pci/snps,dw-pcie.yaml# > + > +unevaluatedProperties: false > + > +examples: > + - | > + #include <dt-bindings/interrupt-controller/arm-gic.h> > + #include <dt-bindings/phy/phy.h> > + > + bus { > + #address-cells = <2>; > + #size-cells = <2>; > + > + pcie@40400000 { > + compatible = "nxp,s32g3-pcie", "nxp,s32g2-pcie"; > + reg = <0x00 0x40400000 0x0 0x00001000>, /* dbi registers */ > + <0x00 0x40420000 0x0 0x00001000>, /* dbi2 registers */ > + <0x00 0x40460000 0x0 0x00001000>, /* atu registers */ > + <0x00 0x40470000 0x0 0x00001000>, /* dma registers */ > + <0x00 0x40481000 0x0 0x000000f8>, /* ctrl registers */ > + <0x5f 0xffffe000 0x0 0x00002000>; /* config space */ > + reg-names = "dbi", "dbi2", "atu", "dma", "ctrl", "config"; > + dma-coherent; > + #address-cells = <3>; > + #size-cells = <2>; > + device_type = "pci"; > + ranges = > + <0x81000000 0x0 0x00000000 0x5f 0xfffe0000 0x0 0x00010000>, > + <0x82000000 0x0 0x00000000 0x58 0x00000000 0x0 0x80000000>, > + <0x82000000 0x1 0x00000000 0x59 0x00000000 0x6 0xfffe0000>; > + > + bus-range = <0x0 0xff>; > + interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>, > + <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; > + interrupt-names = "msi", "dma"; > + #interrupt-cells = <1>; > + interrupt-map-mask = <0 0 0 0x7>; > + interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>, > + <0 0 0 2 &gic 0 0 GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>, > + <0 0 0 3 &gic 0 0 GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, > + <0 0 0 4 &gic 0 0 GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>; > + > + pcie@0 { > + reg = <0x0 0x0 0x0 0x0 0x0>; > + #address-cells = <3>; > + #size-cells = <2>; > + ranges; > + > + device_type = "pci"; > + phys = <&serdes0 PHY_TYPE_PCIE 0 0>; > + }; > + }; > + }; > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP PCIe controller 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot 2025-11-18 16:52 ` Frank Li @ 2025-11-20 11:27 ` Manivannan Sadhasivam 2025-11-20 15:23 ` Rob Herring (Arm) 2 siblings, 0 replies; 15+ messages in thread From: Manivannan Sadhasivam @ 2025-11-20 11:27 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, Nov 18, 2025 at 05:02:35PM +0100, Vincent Guittot wrote: > Describe the PCIe host controller available on the S32G platforms. > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Co-developed-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Signed-off-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- > .../bindings/pci/nxp,s32g-pcie.yaml | 130 ++++++++++++++++++ > 1 file changed, 130 insertions(+) > create mode 100644 Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > > diff --git a/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > new file mode 100644 > index 000000000000..da3106dfcf58 > --- /dev/null > +++ b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > @@ -0,0 +1,130 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/pci/nxp,s32g-pcie.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: NXP S32G2xxx/S32G3xxx PCIe Root Complex controller > + > +maintainers: > + - Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > + - Ionut Vicovan <ionut.vicovan@nxp.com> > + > +description: > + This PCIe controller is based on the Synopsys DesignWare PCIe IP. > + The S32G SoC family has two PCIe controllers, which can be configured as > + either Root Complex or Endpoint. > + > +properties: > + compatible: > + oneOf: > + - enum: > + - nxp,s32g2-pcie > + - items: > + - const: nxp,s32g3-pcie > + - const: nxp,s32g2-pcie > + > + reg: > + maxItems: 6 > + > + reg-names: > + items: > + - const: dbi > + - const: dbi2 > + - const: atu > + - const: dma > + - const: ctrl > + - const: config > + > + interrupts: > + minItems: 1 > + maxItems: 2 > + > + interrupt-names: > + items: > + - const: msi > + - const: dma > + minItems: 1 > + > + pcie@0: > + description: > + Describe the S32G Root Port. > + type: object > + $ref: /schemas/pci/pci-pci-bridge.yaml# > + > + properties: > + reg: > + maxItems: 1 > + > + phys: > + maxItems: 1 > + > + required: > + - reg > + - phys > + > + unevaluatedProperties: false > + > +required: > + - compatible > + - reg > + - reg-names > + - interrupts > + - interrupt-names > + - ranges > + - pcie@0 > + > +allOf: > + - $ref: /schemas/pci/snps,dw-pcie.yaml# > + > +unevaluatedProperties: false > + > +examples: > + - | > + #include <dt-bindings/interrupt-controller/arm-gic.h> > + #include <dt-bindings/phy/phy.h> > + > + bus { > + #address-cells = <2>; > + #size-cells = <2>; > + > + pcie@40400000 { > + compatible = "nxp,s32g3-pcie", "nxp,s32g2-pcie"; > + reg = <0x00 0x40400000 0x0 0x00001000>, /* dbi registers */ > + <0x00 0x40420000 0x0 0x00001000>, /* dbi2 registers */ > + <0x00 0x40460000 0x0 0x00001000>, /* atu registers */ > + <0x00 0x40470000 0x0 0x00001000>, /* dma registers */ > + <0x00 0x40481000 0x0 0x000000f8>, /* ctrl registers */ > + <0x5f 0xffffe000 0x0 0x00002000>; /* config space */ > + reg-names = "dbi", "dbi2", "atu", "dma", "ctrl", "config"; > + dma-coherent; > + #address-cells = <3>; > + #size-cells = <2>; > + device_type = "pci"; > + ranges = > + <0x81000000 0x0 0x00000000 0x5f 0xfffe0000 0x0 0x00010000>, > + <0x82000000 0x0 0x00000000 0x58 0x00000000 0x0 0x80000000>, > + <0x82000000 0x1 0x00000000 0x59 0x00000000 0x6 0xfffe0000>; Please drop the 'relocatable' flag (bit 31) from all the entries. With that, Reviewed-by: Manivannan Sadhasivam <mani@kernel.org> - Mani -- மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP PCIe controller 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot 2025-11-18 16:52 ` Frank Li 2025-11-20 11:27 ` Manivannan Sadhasivam @ 2025-11-20 15:23 ` Rob Herring (Arm) 2 siblings, 0 replies; 15+ messages in thread From: Rob Herring (Arm) @ 2025-11-20 15:23 UTC (permalink / raw) To: Vincent Guittot Cc: Ionut.Vicovan, linux-arm-kernel, bhelgaas, linux-kernel, ciprianmarian.costea, conor+dt, linux-pci, s32, ghennadi.procopciuc, imx, kwilczynski, jingoohan1, Frank.li, larisa.grigore, chester62515, cassel, bogdan.hamciuc, devicetree, mani, lpieralisi, mbrugger, krzk+dt, Ghennadi.Procopciuc On Tue, 18 Nov 2025 17:02:35 +0100, Vincent Guittot wrote: > Describe the PCIe host controller available on the S32G platforms. > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Co-developed-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Signed-off-by: Bogdan-Gabriel Roman <bogdan-gabriel.roman@nxp.com> > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Co-developed-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- > .../bindings/pci/nxp,s32g-pcie.yaml | 130 ++++++++++++++++++ > 1 file changed, 130 insertions(+) > create mode 100644 Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml > Reviewed-by: Rob Herring (Arm) <robh@kernel.org> ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition 2025-11-18 16:02 [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller Vincent Guittot 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot @ 2025-11-18 16:02 ` Vincent Guittot 2025-11-18 16:54 ` Frank Li 2025-11-18 16:02 ` [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) Vincent Guittot 2025-11-18 16:02 ` [PATCH 4/4 v5] MAINTAINERS: Add MAINTAINER for NXP S32G PCIe driver Vincent Guittot 3 siblings, 1 reply; 15+ messages in thread From: Vincent Guittot @ 2025-11-18 16:02 UTC (permalink / raw) To: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx Cc: cassel Add new registers and bitfield definition: - GEN3_RELATED_OFF_EQ_PHASE_2_3 field of GEN3_RELATED_OFF - 3 Coherency control registers Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> --- drivers/pci/controller/dwc/pcie-designware.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index e995f692a1ec..e60b77f1b5e6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -121,6 +121,7 @@ #define GEN3_RELATED_OFF 0x890 #define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) +#define GEN3_RELATED_OFF_EQ_PHASE_2_3 BIT(9) #define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13) #define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 @@ -138,6 +139,13 @@ #define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10) #define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14) +#define COHERENCY_CONTROL_1_OFF 0x8E0 +#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2) +#define CFG_MEMTYPE_VALUE BIT(0) + +#define COHERENCY_CONTROL_2_OFF 0x8E4 +#define COHERENCY_CONTROL_3_OFF 0x8E8 + #define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 #define PORT_MLTI_UPCFG_SUPPORT BIT(7) -- 2.43.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition 2025-11-18 16:02 ` [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition Vincent Guittot @ 2025-11-18 16:54 ` Frank Li 0 siblings, 0 replies; 15+ messages in thread From: Frank Li @ 2025-11-18 16:54 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, Nov 18, 2025 at 05:02:36PM +0100, Vincent Guittot wrote: > Add new registers and bitfield definition: > - GEN3_RELATED_OFF_EQ_PHASE_2_3 field of GEN3_RELATED_OFF > - 3 Coherency control registers > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- Reviewed-by: Frank Li <Frank.Li@nxp.com> > drivers/pci/controller/dwc/pcie-designware.h | 8 ++++++++ > 1 file changed, 8 insertions(+) > > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h > index e995f692a1ec..e60b77f1b5e6 100644 > --- a/drivers/pci/controller/dwc/pcie-designware.h > +++ b/drivers/pci/controller/dwc/pcie-designware.h > @@ -121,6 +121,7 @@ > > #define GEN3_RELATED_OFF 0x890 > #define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0) > +#define GEN3_RELATED_OFF_EQ_PHASE_2_3 BIT(9) > #define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13) > #define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) > #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 > @@ -138,6 +139,13 @@ > #define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10) > #define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14) > > +#define COHERENCY_CONTROL_1_OFF 0x8E0 > +#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2) > +#define CFG_MEMTYPE_VALUE BIT(0) > + > +#define COHERENCY_CONTROL_2_OFF 0x8E4 > +#define COHERENCY_CONTROL_3_OFF 0x8E8 > + > #define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 > #define PORT_MLTI_UPCFG_SUPPORT BIT(7) > > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-18 16:02 [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller Vincent Guittot 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot 2025-11-18 16:02 ` [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition Vincent Guittot @ 2025-11-18 16:02 ` Vincent Guittot 2025-11-18 17:01 ` Frank Li 2025-11-20 8:22 ` Manivannan Sadhasivam 2025-11-18 16:02 ` [PATCH 4/4 v5] MAINTAINERS: Add MAINTAINER for NXP S32G PCIe driver Vincent Guittot 3 siblings, 2 replies; 15+ messages in thread From: Vincent Guittot @ 2025-11-18 16:02 UTC (permalink / raw) To: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx Cc: cassel Add initial support of the PCIe controller for S32G Soc family. Only host mode is supported. Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> --- drivers/pci/controller/dwc/Kconfig | 10 + drivers/pci/controller/dwc/Makefile | 1 + .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ 4 files changed, 423 insertions(+) create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 349d4657393c..e276956c3fca 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP in order to enable device-specific features PCIE_TEGRA194_EP must be selected. This uses the DesignWare core. +config PCIE_NXP_S32G + tristate "NXP S32G PCIe controller (host mode)" + depends on ARCH_S32 || COMPILE_TEST + select PCIE_DW_HOST + help + Enable support for the PCIe controller in NXP S32G based boards to + work in Host mode. The controller is based on DesignWare IP and + can work either as RC or EP. In order to enable host-specific + features PCIE_NXP_S32G must be selected. + config PCIE_DW_PLAT bool diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 7ae28f3b0fb3..3301bbbad78c 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o +obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h new file mode 100644 index 000000000000..81e35b6227d1 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2016-2023, 2025 NXP + */ + +#ifndef PCIE_S32G_REGS_H +#define PCIE_S32G_REGS_H + +/* PCIe controller Sub-System */ + +/* PCIe controller 0 General Control 1 */ +#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 +#define DEVICE_TYPE_MASK GENMASK(3, 0) +#define SRIS_MODE BIT(8) + +/* PCIe controller 0 General Control 3 */ +#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 +#define LTSSM_EN BIT(0) + +#endif /* PCI_S32G_REGS_H */ diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c new file mode 100644 index 000000000000..eaa6b5363afe --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for NXP S32G SoCs + * + * Copyright 2019-2025 NXP + */ + +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/memblock.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/sizes.h> +#include <linux/types.h> + +#include "pcie-designware.h" +#include "pcie-nxp-s32g-regs.h" + +struct s32g_pcie_port { + struct list_head list; + struct phy *phy; +}; + +struct s32g_pcie { + struct dw_pcie pci; + void __iomem *ctrl_base; + struct list_head ports; +}; + +#define to_s32g_from_dw_pcie(x) \ + container_of(x, struct s32g_pcie, pci) + +static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) +{ + writel(val, s32g_pp->ctrl_base + reg); +} + +static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) +{ + return readl(s32g_pp->ctrl_base + reg); +} + +static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) +{ + u32 reg; + + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); + reg |= LTSSM_EN; + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); +} + +static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) +{ + u32 reg; + + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); + reg &= ~LTSSM_EN; + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); +} + +static int s32g_pcie_start_link(struct dw_pcie *pci) +{ + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); + + s32g_pcie_enable_ltssm(s32g_pp); + + return 0; +} + +static void s32g_pcie_stop_link(struct dw_pcie *pci) +{ + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); + + s32g_pcie_disable_ltssm(s32g_pp); +} + +static struct dw_pcie_ops s32g_pcie_ops = { + .start_link = s32g_pcie_start_link, + .stop_link = s32g_pcie_stop_link, +}; + +/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ +static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci, u64 ddr_base_addr) +{ + u32 ddr_base_low = lower_32_bits(ddr_base_addr); + u32 ddr_base_high = upper_32_bits(ddr_base_addr); + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); + + /* + * Ncore is a cache-coherent interconnect module that enables the + * integration of heterogeneous coherent and non-coherent agents in + * the chip. Ncore Transactions to peripheral should be non-coherent + * or it might drop them. + * + * One example where this is needed are PCIe MSIs, which use NoSnoop=0 + * and might end up routed to Ncore. + * Define the start of DDR as seen by Linux as the boundary between + * "memory" and "peripherals", with peripherals being below. + */ + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, + (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); + dw_pcie_dbi_ro_wr_dis(pci); +} + +static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); + u32 val; + + /* Set RP mode */ + val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); + val &= ~DEVICE_TYPE_MASK; + val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); + + /* Use default CRNS */ + val &= ~SRIS_MODE; + + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); + + /* + * Make sure we use the coherency defaults (just in case the settings + * have been changed from their reset values) + */ + s32g_pcie_reset_mstr_ace(pci, memblock_start_of_DRAM()); + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); + val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); + + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); + + dw_pcie_dbi_ro_wr_dis(pci); + + return 0; +} + +static const struct dw_pcie_host_ops s32g_pcie_host_ops = { + .init = s32g_init_pcie_controller, +}; + +static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) +{ + struct dw_pcie *pci = &s32g_pp->pci; + struct device *dev = pci->dev; + struct s32g_pcie_port *port, *tmp; + int ret; + + list_for_each_entry(port, &s32g_pp->ports, list) { + ret = phy_init(port->phy); + if (ret) { + dev_err(dev, "Failed to init serdes PHY\n"); + goto err_phy_revert; + } + + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); + if (ret) { + dev_err(dev, "Failed to set mode on serdes PHY\n"); + goto err_phy_exit; + } + + ret = phy_power_on(port->phy); + if (ret) { + dev_err(dev, "Failed to power on serdes PHY\n"); + goto err_phy_exit; + } + } + + return 0; + +err_phy_exit: + phy_exit(port->phy); + +err_phy_revert: + list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { + phy_power_off(port->phy); + phy_exit(port->phy); + } + + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) + list_del(&port->list); + + return ret; +} + +static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp) +{ + struct s32g_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) { + phy_power_off(port->phy); + phy_exit(port->phy); + list_del(&port->list); + } +} + +static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) +{ + int ret; + + s32g_pcie_disable_ltssm(s32g_pp); + + ret = s32g_init_pcie_phy(s32g_pp); + if (ret) + return ret; + + return 0; +} + +static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) +{ + s32g_pcie_disable_ltssm(s32g_pp); + + s32g_deinit_pcie_phy(s32g_pp); +} + +static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node) +{ + struct device *dev = s32g_pp->pci.dev; + struct s32g_pcie_port *port; + int num_lanes; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->phy = devm_of_phy_get(dev, node, NULL); + if (IS_ERR(port->phy)) + return dev_err_probe(dev, PTR_ERR(port->phy), + "Failed to get serdes PHY\n"); + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &s32g_pp->ports); + + /* + * The DWC core initialization code cannot parse yet the num-lanes + * attribute in the Root Port node. The S32G only supports one Root + * Port for now so its driver can parse the node and set the num_lanes + * field of struct dwc_pcie before calling dw_pcie_host_init(). + */ + if (!of_property_read_u32(node, "num-lanes", &num_lanes)) + s32g_pp->pci.num_lanes = num_lanes; + + return 0; +} + +static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) +{ + struct s32g_pcie_port *port, *tmp; + int ret = -ENOENT; + + for_each_available_child_of_node_scoped(dev->of_node, of_port) { + if (!of_node_is_type(of_port, "pci")) + continue; + + ret = s32g_pcie_parse_port(s32g_pp, of_port); + if (ret) + goto err_port; + } + +err_port: + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) + list_del(&port->list); + + return ret; +} + +static int s32g_pcie_get_resources(struct platform_device *pdev, + struct s32g_pcie *s32g_pp) +{ + struct dw_pcie *pci = &s32g_pp->pci; + struct device *dev = &pdev->dev; + int ret; + + pci->dev = dev; + pci->ops = &s32g_pcie_ops; + + s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl"); + if (IS_ERR(s32g_pp->ctrl_base)) + return PTR_ERR(s32g_pp->ctrl_base); + + INIT_LIST_HEAD(&s32g_pp->ports); + + ret = s32g_pcie_parse_ports(dev, s32g_pp); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse Root Port: %d\n", ret); + + platform_set_drvdata(pdev, s32g_pp); + + return 0; +} + +static int s32g_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct s32g_pcie *s32g_pp; + struct dw_pcie_rp *pp; + int ret; + + s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL); + if (!s32g_pp) + return -ENOMEM; + + ret = s32g_pcie_get_resources(pdev, s32g_pp); + if (ret) + return ret; + + pm_runtime_no_callbacks(dev); + devm_pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err_pm_runtime_put; + + ret = s32g_pcie_init(dev, s32g_pp); + if (ret) + goto err_pm_runtime_put; + + pp = &s32g_pp->pci.pp; + pp->ops = &s32g_pcie_host_ops; + pp->use_atu_msg = true; + + ret = dw_pcie_host_init(pp); + if (ret) + goto err_pcie_deinit; + + return 0; + +err_pcie_deinit: + s32g_pcie_deinit(s32g_pp); +err_pm_runtime_put: + pm_runtime_put(dev); + + return ret; +} + +static int s32g_pcie_suspend_noirq(struct device *dev) +{ + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); + struct dw_pcie *pci = &s32g_pp->pci; + + return dw_pcie_suspend_noirq(pci); +} + +static int s32g_pcie_resume_noirq(struct device *dev) +{ + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); + struct dw_pcie *pci = &s32g_pp->pci; + + return dw_pcie_resume_noirq(pci); +} + +static const struct dev_pm_ops s32g_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq, + s32g_pcie_resume_noirq) +}; + +static const struct of_device_id s32g_pcie_of_match[] = { + { .compatible = "nxp,s32g2-pcie" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s32g_pcie_of_match); + +static struct platform_driver s32g_pcie_driver = { + .driver = { + .name = "s32g-pcie", + .of_match_table = s32g_pcie_of_match, + .suppress_bind_attrs = true, + .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = s32g_pcie_probe, +}; + +module_platform_driver(s32g_pcie_driver); + +MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>"); +MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-18 16:02 ` [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) Vincent Guittot @ 2025-11-18 17:01 ` Frank Li 2025-11-20 7:25 ` Vincent Guittot 2025-11-20 8:22 ` Manivannan Sadhasivam 1 sibling, 1 reply; 15+ messages in thread From: Frank Li @ 2025-11-18 17:01 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > Add initial support of the PCIe controller for S32G Soc family. Only > host mode is supported. > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- > drivers/pci/controller/dwc/Kconfig | 10 + > drivers/pci/controller/dwc/Makefile | 1 + > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > 4 files changed, 423 insertions(+) > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > index 349d4657393c..e276956c3fca 100644 > --- a/drivers/pci/controller/dwc/Kconfig > +++ b/drivers/pci/controller/dwc/Kconfig > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > in order to enable device-specific features PCIE_TEGRA194_EP must be > selected. This uses the DesignWare core. > ... > + > +static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) > +{ > + int ret; > + > + s32g_pcie_disable_ltssm(s32g_pp); > + > + ret = s32g_init_pcie_phy(s32g_pp); > + if (ret) > + return ret; Small nit: return s32g_init_pcie_phy(s32g_pp); Reviewed-by: Frank Li <Frank.Li@nxp.com> > + > + return 0; > +} > + > +static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) > +{ > + s32g_pcie_disable_ltssm(s32g_pp); > + > + s32g_deinit_pcie_phy(s32g_pp); > +} > + ... > + > +module_platform_driver(s32g_pcie_driver); > + > +MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>"); > +MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver"); > +MODULE_LICENSE("GPL"); > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-18 17:01 ` Frank Li @ 2025-11-20 7:25 ` Vincent Guittot 0 siblings, 0 replies; 15+ messages in thread From: Vincent Guittot @ 2025-11-20 7:25 UTC (permalink / raw) To: Frank Li Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, 18 Nov 2025 at 18:01, Frank Li <Frank.li@nxp.com> wrote: > > On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > > Add initial support of the PCIe controller for S32G Soc family. Only > > host mode is supported. > > > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > > --- > > drivers/pci/controller/dwc/Kconfig | 10 + > > drivers/pci/controller/dwc/Makefile | 1 + > > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > > 4 files changed, 423 insertions(+) > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > > index 349d4657393c..e276956c3fca 100644 > > --- a/drivers/pci/controller/dwc/Kconfig > > +++ b/drivers/pci/controller/dwc/Kconfig > > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > > in order to enable device-specific features PCIE_TEGRA194_EP must be > > selected. This uses the DesignWare core. > > > ... > > + > > +static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) > > +{ > > + int ret; > > + > > + s32g_pcie_disable_ltssm(s32g_pp); > > + > > + ret = s32g_init_pcie_phy(s32g_pp); > > + if (ret) > > + return ret; > > Small nit: > > return s32g_init_pcie_phy(s32g_pp); Yes > > Reviewed-by: Frank Li <Frank.Li@nxp.com> Thanks > > + > > + return 0; > > +} > > + > > +static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) > > +{ > > + s32g_pcie_disable_ltssm(s32g_pp); > > + > > + s32g_deinit_pcie_phy(s32g_pp); > > +} > > + > ... > > + > > +module_platform_driver(s32g_pcie_driver); > > + > > +MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>"); > > +MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver"); > > +MODULE_LICENSE("GPL"); > > -- > > 2.43.0 > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-18 16:02 ` [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) Vincent Guittot 2025-11-18 17:01 ` Frank Li @ 2025-11-20 8:22 ` Manivannan Sadhasivam 2025-11-20 9:06 ` Vincent Guittot 1 sibling, 1 reply; 15+ messages in thread From: Manivannan Sadhasivam @ 2025-11-20 8:22 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > Add initial support of the PCIe controller for S32G Soc family. Only > host mode is supported. > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > --- > drivers/pci/controller/dwc/Kconfig | 10 + > drivers/pci/controller/dwc/Makefile | 1 + > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > 4 files changed, 423 insertions(+) > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > index 349d4657393c..e276956c3fca 100644 > --- a/drivers/pci/controller/dwc/Kconfig > +++ b/drivers/pci/controller/dwc/Kconfig > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > in order to enable device-specific features PCIE_TEGRA194_EP must be > selected. This uses the DesignWare core. > > +config PCIE_NXP_S32G > + tristate "NXP S32G PCIe controller (host mode)" > + depends on ARCH_S32 || COMPILE_TEST > + select PCIE_DW_HOST > + help > + Enable support for the PCIe controller in NXP S32G based boards to > + work in Host mode. The controller is based on DesignWare IP and > + can work either as RC or EP. In order to enable host-specific > + features PCIE_NXP_S32G must be selected. > + > config PCIE_DW_PLAT > bool > > diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile > index 7ae28f3b0fb3..3301bbbad78c 100644 > --- a/drivers/pci/controller/dwc/Makefile > +++ b/drivers/pci/controller/dwc/Makefile > @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o > obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o > obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o > obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > +obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o > obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o > obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o > obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > new file mode 100644 > index 000000000000..81e35b6227d1 > --- /dev/null > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > @@ -0,0 +1,21 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > + * Copyright 2016-2023, 2025 NXP > + */ > + > +#ifndef PCIE_S32G_REGS_H > +#define PCIE_S32G_REGS_H > + > +/* PCIe controller Sub-System */ > + > +/* PCIe controller 0 General Control 1 */ > +#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 > +#define DEVICE_TYPE_MASK GENMASK(3, 0) > +#define SRIS_MODE BIT(8) > + > +/* PCIe controller 0 General Control 3 */ > +#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 > +#define LTSSM_EN BIT(0) > + Since this header is not used by other drivers as of now, I'd prefer moving these definitions inside the driver. > +#endif /* PCI_S32G_REGS_H */ > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > new file mode 100644 > index 000000000000..eaa6b5363afe > --- /dev/null > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > @@ -0,0 +1,391 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * PCIe host controller driver for NXP S32G SoCs > + * > + * Copyright 2019-2025 NXP > + */ > + > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/memblock.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/of_address.h> > +#include <linux/pci.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/sizes.h> > +#include <linux/types.h> > + > +#include "pcie-designware.h" > +#include "pcie-nxp-s32g-regs.h" > + > +struct s32g_pcie_port { > + struct list_head list; > + struct phy *phy; > +}; > + > +struct s32g_pcie { > + struct dw_pcie pci; > + void __iomem *ctrl_base; > + struct list_head ports; > +}; > + > +#define to_s32g_from_dw_pcie(x) \ > + container_of(x, struct s32g_pcie, pci) > + > +static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) > +{ > + writel(val, s32g_pp->ctrl_base + reg); > +} > + > +static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) > +{ > + return readl(s32g_pp->ctrl_base + reg); > +} > + > +static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) > +{ > + u32 reg; > + > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > + reg |= LTSSM_EN; > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > +} > + > +static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) > +{ > + u32 reg; > + > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > + reg &= ~LTSSM_EN; > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > +} > + > +static int s32g_pcie_start_link(struct dw_pcie *pci) > +{ > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > + > + s32g_pcie_enable_ltssm(s32g_pp); > + > + return 0; > +} > + > +static void s32g_pcie_stop_link(struct dw_pcie *pci) > +{ > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > + > + s32g_pcie_disable_ltssm(s32g_pp); > +} > + > +static struct dw_pcie_ops s32g_pcie_ops = { > + .start_link = s32g_pcie_start_link, > + .stop_link = s32g_pcie_stop_link, > +}; > + > +/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ > +static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci, u64 ddr_base_addr) > +{ > + u32 ddr_base_low = lower_32_bits(ddr_base_addr); > + u32 ddr_base_high = upper_32_bits(ddr_base_addr); > + > + dw_pcie_dbi_ro_wr_en(pci); > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); > + > + /* > + * Ncore is a cache-coherent interconnect module that enables the > + * integration of heterogeneous coherent and non-coherent agents in > + * the chip. Ncore Transactions to peripheral should be non-coherent > + * or it might drop them. > + * > + * One example where this is needed are PCIe MSIs, which use NoSnoop=0 > + * and might end up routed to Ncore. I don't think this statement is correct. No Snoop attribute is only applicable to MRd/MWr operations and not applicable to MSIs. Also, you've marked the controller as cache coherent in the binding, but this comment doesn't relate to it. > + * Define the start of DDR as seen by Linux as the boundary between > + * "memory" and "peripherals", with peripherals being below. Please mention what this configuration does and why it is necessary. This is not clearly mentioned in the comment. > + */ > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, > + (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); > + dw_pcie_dbi_ro_wr_dis(pci); > +} > + > +static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) > +{ > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > + u32 val; > + > + /* Set RP mode */ > + val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); > + val &= ~DEVICE_TYPE_MASK; > + val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); > + > + /* Use default CRNS */ SRNS? > + val &= ~SRIS_MODE; > + > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); > + > + /* > + * Make sure we use the coherency defaults (just in case the settings > + * have been changed from their reset values) > + */ > + s32g_pcie_reset_mstr_ace(pci, memblock_start_of_DRAM()); > + > + dw_pcie_dbi_ro_wr_en(pci); > + > + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); > + val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; > + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); > + > + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); > + val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; > + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); > + > + dw_pcie_dbi_ro_wr_dis(pci); > + > + return 0; > +} > + > +static const struct dw_pcie_host_ops s32g_pcie_host_ops = { > + .init = s32g_init_pcie_controller, > +}; > + > +static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) > +{ > + struct dw_pcie *pci = &s32g_pp->pci; > + struct device *dev = pci->dev; > + struct s32g_pcie_port *port, *tmp; > + int ret; > + > + list_for_each_entry(port, &s32g_pp->ports, list) { > + ret = phy_init(port->phy); > + if (ret) { > + dev_err(dev, "Failed to init serdes PHY\n"); > + goto err_phy_revert; > + } > + > + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); > + if (ret) { > + dev_err(dev, "Failed to set mode on serdes PHY\n"); > + goto err_phy_exit; > + } > + > + ret = phy_power_on(port->phy); > + if (ret) { > + dev_err(dev, "Failed to power on serdes PHY\n"); > + goto err_phy_exit; > + } > + } > + > + return 0; > + > +err_phy_exit: > + phy_exit(port->phy); > + > +err_phy_revert: > + list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { > + phy_power_off(port->phy); > + phy_exit(port->phy); > + } > + > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > + list_del(&port->list); Can't you use list_for_each_entry_safe_reverse() to combine both operations? > + > + return ret; > +} > + > +static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp) > +{ > + struct s32g_pcie_port *port, *tmp; > + > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) { > + phy_power_off(port->phy); > + phy_exit(port->phy); > + list_del(&port->list); > + } > +} > + > +static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) > +{ > + int ret; > + > + s32g_pcie_disable_ltssm(s32g_pp); > + > + ret = s32g_init_pcie_phy(s32g_pp); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) > +{ > + s32g_pcie_disable_ltssm(s32g_pp); > + > + s32g_deinit_pcie_phy(s32g_pp); > +} > + > +static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node) > +{ > + struct device *dev = s32g_pp->pci.dev; > + struct s32g_pcie_port *port; > + int num_lanes; > + > + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); > + if (!port) > + return -ENOMEM; > + > + port->phy = devm_of_phy_get(dev, node, NULL); > + if (IS_ERR(port->phy)) > + return dev_err_probe(dev, PTR_ERR(port->phy), > + "Failed to get serdes PHY\n"); > + > + INIT_LIST_HEAD(&port->list); > + list_add_tail(&port->list, &s32g_pp->ports); > + > + /* > + * The DWC core initialization code cannot parse yet the num-lanes > + * attribute in the Root Port node. The S32G only supports one Root > + * Port for now so its driver can parse the node and set the num_lanes > + * field of struct dwc_pcie before calling dw_pcie_host_init(). Please add TODO prefix so that it catches the eyes looking for improvements. > + */ > + if (!of_property_read_u32(node, "num-lanes", &num_lanes)) > + s32g_pp->pci.num_lanes = num_lanes; > + > + return 0; > +} > + > +static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) > +{ > + struct s32g_pcie_port *port, *tmp; > + int ret = -ENOENT; > + > + for_each_available_child_of_node_scoped(dev->of_node, of_port) { > + if (!of_node_is_type(of_port, "pci")) > + continue; > + > + ret = s32g_pcie_parse_port(s32g_pp, of_port); > + if (ret) > + goto err_port; > + } > + > +err_port: > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > + list_del(&port->list); > + > + return ret; > +} > + > +static int s32g_pcie_get_resources(struct platform_device *pdev, > + struct s32g_pcie *s32g_pp) > +{ > + struct dw_pcie *pci = &s32g_pp->pci; > + struct device *dev = &pdev->dev; > + int ret; > + > + pci->dev = dev; > + pci->ops = &s32g_pcie_ops; > + > + s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl"); > + if (IS_ERR(s32g_pp->ctrl_base)) > + return PTR_ERR(s32g_pp->ctrl_base); > + > + INIT_LIST_HEAD(&s32g_pp->ports); > + > + ret = s32g_pcie_parse_ports(dev, s32g_pp); > + if (ret) > + return dev_err_probe(dev, ret, > + "Failed to parse Root Port: %d\n", ret); > + > + platform_set_drvdata(pdev, s32g_pp); nit: move this to probe() > + > + return 0; > +} > + > +static int s32g_pcie_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct s32g_pcie *s32g_pp; > + struct dw_pcie_rp *pp; > + int ret; > + > + s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL); > + if (!s32g_pp) > + return -ENOMEM; > + > + ret = s32g_pcie_get_resources(pdev, s32g_pp); > + if (ret) > + return ret; > + > + pm_runtime_no_callbacks(dev); > + devm_pm_runtime_enable(dev); > + ret = pm_runtime_get_sync(dev); > + if (ret < 0) > + goto err_pm_runtime_put; > + > + ret = s32g_pcie_init(dev, s32g_pp); > + if (ret) > + goto err_pm_runtime_put; > + > + pp = &s32g_pp->pci.pp; > + pp->ops = &s32g_pcie_host_ops; > + pp->use_atu_msg = true; > + > + ret = dw_pcie_host_init(pp); > + if (ret) > + goto err_pcie_deinit; > + > + return 0; > + > +err_pcie_deinit: > + s32g_pcie_deinit(s32g_pp); > +err_pm_runtime_put: > + pm_runtime_put(dev); > + > + return ret; > +} > + > +static int s32g_pcie_suspend_noirq(struct device *dev) > +{ > + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); > + struct dw_pcie *pci = &s32g_pp->pci; > + > + return dw_pcie_suspend_noirq(pci); > +} > + > +static int s32g_pcie_resume_noirq(struct device *dev) > +{ > + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); > + struct dw_pcie *pci = &s32g_pp->pci; > + > + return dw_pcie_resume_noirq(pci); > +} > + > +static const struct dev_pm_ops s32g_pcie_pm_ops = { > + NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq, > + s32g_pcie_resume_noirq) > +}; > + > +static const struct of_device_id s32g_pcie_of_match[] = { > + { .compatible = "nxp,s32g2-pcie" }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, s32g_pcie_of_match); > + > +static struct platform_driver s32g_pcie_driver = { > + .driver = { > + .name = "s32g-pcie", > + .of_match_table = s32g_pcie_of_match, > + .suppress_bind_attrs = true, > + .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), > + .probe_type = PROBE_PREFER_ASYNCHRONOUS, > + }, > + .probe = s32g_pcie_probe, > +}; > + > +module_platform_driver(s32g_pcie_driver); builtin_platform_driver() since this controller implements MSI controller. - Mani -- மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-20 8:22 ` Manivannan Sadhasivam @ 2025-11-20 9:06 ` Vincent Guittot 2025-11-20 10:25 ` Manivannan Sadhasivam 0 siblings, 1 reply; 15+ messages in thread From: Vincent Guittot @ 2025-11-20 9:06 UTC (permalink / raw) To: Manivannan Sadhasivam Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Thu, 20 Nov 2025 at 09:22, Manivannan Sadhasivam <mani@kernel.org> wrote: > > On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > > Add initial support of the PCIe controller for S32G Soc family. Only > > host mode is supported. > > > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > > --- > > drivers/pci/controller/dwc/Kconfig | 10 + > > drivers/pci/controller/dwc/Makefile | 1 + > > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > > 4 files changed, 423 insertions(+) > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > > index 349d4657393c..e276956c3fca 100644 > > --- a/drivers/pci/controller/dwc/Kconfig > > +++ b/drivers/pci/controller/dwc/Kconfig > > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > > in order to enable device-specific features PCIE_TEGRA194_EP must be > > selected. This uses the DesignWare core. > > > > +config PCIE_NXP_S32G > > + tristate "NXP S32G PCIe controller (host mode)" > > + depends on ARCH_S32 || COMPILE_TEST > > + select PCIE_DW_HOST > > + help > > + Enable support for the PCIe controller in NXP S32G based boards to > > + work in Host mode. The controller is based on DesignWare IP and > > + can work either as RC or EP. In order to enable host-specific > > + features PCIE_NXP_S32G must be selected. > > + > > config PCIE_DW_PLAT > > bool > > > > diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile > > index 7ae28f3b0fb3..3301bbbad78c 100644 > > --- a/drivers/pci/controller/dwc/Makefile > > +++ b/drivers/pci/controller/dwc/Makefile > > @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o > > obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o > > obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o > > obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > > +obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o > > obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o > > obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o > > obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > new file mode 100644 > > index 000000000000..81e35b6227d1 > > --- /dev/null > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > @@ -0,0 +1,21 @@ > > +/* SPDX-License-Identifier: GPL-2.0+ */ > > +/* > > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > > + * Copyright 2016-2023, 2025 NXP > > + */ > > + > > +#ifndef PCIE_S32G_REGS_H > > +#define PCIE_S32G_REGS_H > > + > > +/* PCIe controller Sub-System */ > > + > > +/* PCIe controller 0 General Control 1 */ > > +#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 > > +#define DEVICE_TYPE_MASK GENMASK(3, 0) > > +#define SRIS_MODE BIT(8) > > + > > +/* PCIe controller 0 General Control 3 */ > > +#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 > > +#define LTSSM_EN BIT(0) > > + > > Since this header is not used by other drivers as of now, I'd prefer moving > these definitions inside the driver. I would prefer to keep it separate. It makes reg easier to parse and more registers will be added with new coming features > > > +#endif /* PCI_S32G_REGS_H */ > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > new file mode 100644 > > index 000000000000..eaa6b5363afe > > --- /dev/null > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > @@ -0,0 +1,391 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * PCIe host controller driver for NXP S32G SoCs > > + * > > + * Copyright 2019-2025 NXP > > + */ > > + > > +#include <linux/interrupt.h> > > +#include <linux/io.h> > > +#include <linux/memblock.h> > > +#include <linux/module.h> > > +#include <linux/of_device.h> > > +#include <linux/of_address.h> > > +#include <linux/pci.h> > > +#include <linux/phy/phy.h> > > +#include <linux/platform_device.h> > > +#include <linux/pm_runtime.h> > > +#include <linux/sizes.h> > > +#include <linux/types.h> > > + > > +#include "pcie-designware.h" > > +#include "pcie-nxp-s32g-regs.h" > > + > > +struct s32g_pcie_port { > > + struct list_head list; > > + struct phy *phy; > > +}; > > + > > +struct s32g_pcie { > > + struct dw_pcie pci; > > + void __iomem *ctrl_base; > > + struct list_head ports; > > +}; > > + > > +#define to_s32g_from_dw_pcie(x) \ > > + container_of(x, struct s32g_pcie, pci) > > + > > +static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) > > +{ > > + writel(val, s32g_pp->ctrl_base + reg); > > +} > > + > > +static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) > > +{ > > + return readl(s32g_pp->ctrl_base + reg); > > +} > > + > > +static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) > > +{ > > + u32 reg; > > + > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > + reg |= LTSSM_EN; > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > +} > > + > > +static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) > > +{ > > + u32 reg; > > + > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > + reg &= ~LTSSM_EN; > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > +} > > + > > +static int s32g_pcie_start_link(struct dw_pcie *pci) > > +{ > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > + > > + s32g_pcie_enable_ltssm(s32g_pp); > > + > > + return 0; > > +} > > + > > +static void s32g_pcie_stop_link(struct dw_pcie *pci) > > +{ > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > + > > + s32g_pcie_disable_ltssm(s32g_pp); > > +} > > + > > +static struct dw_pcie_ops s32g_pcie_ops = { > > + .start_link = s32g_pcie_start_link, > > + .stop_link = s32g_pcie_stop_link, > > +}; > > + > > +/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ > > +static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci, u64 ddr_base_addr) > > +{ > > + u32 ddr_base_low = lower_32_bits(ddr_base_addr); > > + u32 ddr_base_high = upper_32_bits(ddr_base_addr); > > + > > + dw_pcie_dbi_ro_wr_en(pci); > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); > > + > > + /* > > + * Ncore is a cache-coherent interconnect module that enables the > > + * integration of heterogeneous coherent and non-coherent agents in > > + * the chip. Ncore Transactions to peripheral should be non-coherent > > + * or it might drop them. > > + * > > + * One example where this is needed are PCIe MSIs, which use NoSnoop=0 > > + * and might end up routed to Ncore. > > I don't think this statement is correct. No Snoop attribute is only applicable > to MRd/MWr operations and not applicable to MSIs. Also, you've marked the The Ncore makes the bridge between the PCIe and the NoC and can decide to drop some transactions based in this boundary > controller as cache coherent in the binding, but this comment doesn't relate to > it. More details of the issue: PCIe coherent traffic (e.g. MSIs) that targets peripheral space will be dropped by Ncore because peripherals on S32G are not coherent as slaves. We add a hard boundary in the PCIe controller coherency control registers to separate physical memory space from peripheral space. > > > + * Define the start of DDR as seen by Linux as the boundary between > > + * "memory" and "peripherals", with peripherals being below. > > Please mention what this configuration does and why it is necessary. This is not > clearly mentioned in the comment. > > > + */ > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, > > + (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); > > + dw_pcie_dbi_ro_wr_dis(pci); > > +} > > + > > +static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) > > +{ > > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > + u32 val; > > + > > + /* Set RP mode */ > > + val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); > > + val &= ~DEVICE_TYPE_MASK; > > + val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); > > + > > + /* Use default CRNS */ > > SRNS? it's CRNS > > > + val &= ~SRIS_MODE; > > + > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); > > + > > + /* > > + * Make sure we use the coherency defaults (just in case the settings > > + * have been changed from their reset values) > > + */ > > + s32g_pcie_reset_mstr_ace(pci, memblock_start_of_DRAM()); > > + > > + dw_pcie_dbi_ro_wr_en(pci); > > + > > + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); > > + val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; > > + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); > > + > > + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); > > + val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; > > + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); > > + > > + dw_pcie_dbi_ro_wr_dis(pci); > > + > > + return 0; > > +} > > + > > +static const struct dw_pcie_host_ops s32g_pcie_host_ops = { > > + .init = s32g_init_pcie_controller, > > +}; > > + > > +static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) > > +{ > > + struct dw_pcie *pci = &s32g_pp->pci; > > + struct device *dev = pci->dev; > > + struct s32g_pcie_port *port, *tmp; > > + int ret; > > + > > + list_for_each_entry(port, &s32g_pp->ports, list) { > > + ret = phy_init(port->phy); > > + if (ret) { > > + dev_err(dev, "Failed to init serdes PHY\n"); > > + goto err_phy_revert; > > + } > > + > > + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); > > + if (ret) { > > + dev_err(dev, "Failed to set mode on serdes PHY\n"); > > + goto err_phy_exit; > > + } > > + > > + ret = phy_power_on(port->phy); > > + if (ret) { > > + dev_err(dev, "Failed to power on serdes PHY\n"); > > + goto err_phy_exit; > > + } > > + } > > + > > + return 0; > > + > > +err_phy_exit: > > + phy_exit(port->phy); > > + > > +err_phy_revert: > > + list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { > > + phy_power_off(port->phy); > > + phy_exit(port->phy); > > + } > > + > > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > > + list_del(&port->list); > > Can't you use list_for_each_entry_safe_reverse() to combine both operations? No, it goes over all elements of the list whereas I only want to power off and exit only those which have been init and powered on above. > > > + > > + return ret; > > +} > > + > > +static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp) > > +{ > > + struct s32g_pcie_port *port, *tmp; > > + > > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) { > > + phy_power_off(port->phy); > > + phy_exit(port->phy); > > + list_del(&port->list); > > + } > > +} > > + > > +static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) > > +{ > > + int ret; > > + > > + s32g_pcie_disable_ltssm(s32g_pp); > > + > > + ret = s32g_init_pcie_phy(s32g_pp); > > + if (ret) > > + return ret; > > + > > + return 0; > > +} > > + > > +static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) > > +{ > > + s32g_pcie_disable_ltssm(s32g_pp); > > + > > + s32g_deinit_pcie_phy(s32g_pp); > > +} > > + > > +static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node) > > +{ > > + struct device *dev = s32g_pp->pci.dev; > > + struct s32g_pcie_port *port; > > + int num_lanes; > > + > > + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); > > + if (!port) > > + return -ENOMEM; > > + > > + port->phy = devm_of_phy_get(dev, node, NULL); > > + if (IS_ERR(port->phy)) > > + return dev_err_probe(dev, PTR_ERR(port->phy), > > + "Failed to get serdes PHY\n"); > > + > > + INIT_LIST_HEAD(&port->list); > > + list_add_tail(&port->list, &s32g_pp->ports); > > + > > + /* > > + * The DWC core initialization code cannot parse yet the num-lanes > > + * attribute in the Root Port node. The S32G only supports one Root > > + * Port for now so its driver can parse the node and set the num_lanes > > + * field of struct dwc_pcie before calling dw_pcie_host_init(). > > Please add TODO prefix so that it catches the eyes looking for improvements. Okay > > > + */ > > + if (!of_property_read_u32(node, "num-lanes", &num_lanes)) > > + s32g_pp->pci.num_lanes = num_lanes; > > + > > + return 0; > > +} > > + > > +static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) > > +{ > > + struct s32g_pcie_port *port, *tmp; > > + int ret = -ENOENT; > > + > > + for_each_available_child_of_node_scoped(dev->of_node, of_port) { > > + if (!of_node_is_type(of_port, "pci")) > > + continue; > > + > > + ret = s32g_pcie_parse_port(s32g_pp, of_port); > > + if (ret) > > + goto err_port; > > + } > > + > > +err_port: > > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > > + list_del(&port->list); > > + > > + return ret; > > +} > > + > > +static int s32g_pcie_get_resources(struct platform_device *pdev, > > + struct s32g_pcie *s32g_pp) > > +{ > > + struct dw_pcie *pci = &s32g_pp->pci; > > + struct device *dev = &pdev->dev; > > + int ret; > > + > > + pci->dev = dev; > > + pci->ops = &s32g_pcie_ops; > > + > > + s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl"); > > + if (IS_ERR(s32g_pp->ctrl_base)) > > + return PTR_ERR(s32g_pp->ctrl_base); > > + > > + INIT_LIST_HEAD(&s32g_pp->ports); > > + > > + ret = s32g_pcie_parse_ports(dev, s32g_pp); > > + if (ret) > > + return dev_err_probe(dev, ret, > > + "Failed to parse Root Port: %d\n", ret); > > + > > + platform_set_drvdata(pdev, s32g_pp); > > nit: move this to probe() > > > + > > + return 0; > > +} > > + > > +static int s32g_pcie_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct s32g_pcie *s32g_pp; > > + struct dw_pcie_rp *pp; > > + int ret; > > + > > + s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL); > > + if (!s32g_pp) > > + return -ENOMEM; > > + > > + ret = s32g_pcie_get_resources(pdev, s32g_pp); > > + if (ret) > > + return ret; > > + > > + pm_runtime_no_callbacks(dev); > > + devm_pm_runtime_enable(dev); > > + ret = pm_runtime_get_sync(dev); > > + if (ret < 0) > > + goto err_pm_runtime_put; > > + > > + ret = s32g_pcie_init(dev, s32g_pp); > > + if (ret) > > + goto err_pm_runtime_put; > > + > > + pp = &s32g_pp->pci.pp; > > + pp->ops = &s32g_pcie_host_ops; > > + pp->use_atu_msg = true; > > + > > + ret = dw_pcie_host_init(pp); > > + if (ret) > > + goto err_pcie_deinit; > > + > > + return 0; > > + > > +err_pcie_deinit: > > + s32g_pcie_deinit(s32g_pp); > > +err_pm_runtime_put: > > + pm_runtime_put(dev); > > + > > + return ret; > > +} > > + > > +static int s32g_pcie_suspend_noirq(struct device *dev) > > +{ > > + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); > > + struct dw_pcie *pci = &s32g_pp->pci; > > + > > + return dw_pcie_suspend_noirq(pci); > > +} > > + > > +static int s32g_pcie_resume_noirq(struct device *dev) > > +{ > > + struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); > > + struct dw_pcie *pci = &s32g_pp->pci; > > + > > + return dw_pcie_resume_noirq(pci); > > +} > > + > > +static const struct dev_pm_ops s32g_pcie_pm_ops = { > > + NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq, > > + s32g_pcie_resume_noirq) > > +}; > > + > > +static const struct of_device_id s32g_pcie_of_match[] = { > > + { .compatible = "nxp,s32g2-pcie" }, > > + { /* sentinel */ }, > > +}; > > +MODULE_DEVICE_TABLE(of, s32g_pcie_of_match); > > + > > +static struct platform_driver s32g_pcie_driver = { > > + .driver = { > > + .name = "s32g-pcie", > > + .of_match_table = s32g_pcie_of_match, > > + .suppress_bind_attrs = true, > > + .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), > > + .probe_type = PROBE_PREFER_ASYNCHRONOUS, > > + }, > > + .probe = s32g_pcie_probe, > > +}; > > + > > +module_platform_driver(s32g_pcie_driver); > > builtin_platform_driver() since this controller implements MSI controller. Can you elaborate ? > > - Mani > > -- > மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-20 9:06 ` Vincent Guittot @ 2025-11-20 10:25 ` Manivannan Sadhasivam 2025-11-20 17:57 ` Vincent Guittot 0 siblings, 1 reply; 15+ messages in thread From: Manivannan Sadhasivam @ 2025-11-20 10:25 UTC (permalink / raw) To: Vincent Guittot Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Thu, Nov 20, 2025 at 10:06:53AM +0100, Vincent Guittot wrote: > On Thu, 20 Nov 2025 at 09:22, Manivannan Sadhasivam <mani@kernel.org> wrote: > > > > On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > > > Add initial support of the PCIe controller for S32G Soc family. Only > > > host mode is supported. > > > > > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > > > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > > > --- > > > drivers/pci/controller/dwc/Kconfig | 10 + > > > drivers/pci/controller/dwc/Makefile | 1 + > > > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > > > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > > > 4 files changed, 423 insertions(+) > > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > > > index 349d4657393c..e276956c3fca 100644 > > > --- a/drivers/pci/controller/dwc/Kconfig > > > +++ b/drivers/pci/controller/dwc/Kconfig > > > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > > > in order to enable device-specific features PCIE_TEGRA194_EP must be > > > selected. This uses the DesignWare core. > > > > > > +config PCIE_NXP_S32G > > > + tristate "NXP S32G PCIe controller (host mode)" > > > + depends on ARCH_S32 || COMPILE_TEST > > > + select PCIE_DW_HOST > > > + help > > > + Enable support for the PCIe controller in NXP S32G based boards to > > > + work in Host mode. The controller is based on DesignWare IP and > > > + can work either as RC or EP. In order to enable host-specific > > > + features PCIE_NXP_S32G must be selected. > > > + > > > config PCIE_DW_PLAT > > > bool > > > > > > diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile > > > index 7ae28f3b0fb3..3301bbbad78c 100644 > > > --- a/drivers/pci/controller/dwc/Makefile > > > +++ b/drivers/pci/controller/dwc/Makefile > > > @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o > > > obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o > > > obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o > > > obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > > > +obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o > > > obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o > > > obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o > > > obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o > > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > new file mode 100644 > > > index 000000000000..81e35b6227d1 > > > --- /dev/null > > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > @@ -0,0 +1,21 @@ > > > +/* SPDX-License-Identifier: GPL-2.0+ */ > > > +/* > > > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > > > + * Copyright 2016-2023, 2025 NXP > > > + */ > > > + > > > +#ifndef PCIE_S32G_REGS_H > > > +#define PCIE_S32G_REGS_H > > > + > > > +/* PCIe controller Sub-System */ > > > + > > > +/* PCIe controller 0 General Control 1 */ > > > +#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 > > > +#define DEVICE_TYPE_MASK GENMASK(3, 0) > > > +#define SRIS_MODE BIT(8) > > > + > > > +/* PCIe controller 0 General Control 3 */ > > > +#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 > > > +#define LTSSM_EN BIT(0) > > > + > > > > Since this header is not used by other drivers as of now, I'd prefer moving > > these definitions inside the driver. > > I would prefer to keep it separate. It makes reg easier to parse and > more registers will be added with new coming features > The convention we follow is to mostly add register definitions within the driver itself if they are not shared. > > > > > +#endif /* PCI_S32G_REGS_H */ > > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > new file mode 100644 > > > index 000000000000..eaa6b5363afe > > > --- /dev/null > > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > @@ -0,0 +1,391 @@ > > > +// SPDX-License-Identifier: GPL-2.0 > > > +/* > > > + * PCIe host controller driver for NXP S32G SoCs > > > + * > > > + * Copyright 2019-2025 NXP > > > + */ > > > + > > > +#include <linux/interrupt.h> > > > +#include <linux/io.h> > > > +#include <linux/memblock.h> > > > +#include <linux/module.h> > > > +#include <linux/of_device.h> > > > +#include <linux/of_address.h> > > > +#include <linux/pci.h> > > > +#include <linux/phy/phy.h> > > > +#include <linux/platform_device.h> > > > +#include <linux/pm_runtime.h> > > > +#include <linux/sizes.h> > > > +#include <linux/types.h> > > > + > > > +#include "pcie-designware.h" > > > +#include "pcie-nxp-s32g-regs.h" > > > + > > > +struct s32g_pcie_port { > > > + struct list_head list; > > > + struct phy *phy; > > > +}; > > > + > > > +struct s32g_pcie { > > > + struct dw_pcie pci; > > > + void __iomem *ctrl_base; > > > + struct list_head ports; > > > +}; > > > + > > > +#define to_s32g_from_dw_pcie(x) \ > > > + container_of(x, struct s32g_pcie, pci) > > > + > > > +static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) > > > +{ > > > + writel(val, s32g_pp->ctrl_base + reg); > > > +} > > > + > > > +static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) > > > +{ > > > + return readl(s32g_pp->ctrl_base + reg); > > > +} > > > + > > > +static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) > > > +{ > > > + u32 reg; > > > + > > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > > + reg |= LTSSM_EN; > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > > +} > > > + > > > +static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) > > > +{ > > > + u32 reg; > > > + > > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > > + reg &= ~LTSSM_EN; > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > > +} > > > + > > > +static int s32g_pcie_start_link(struct dw_pcie *pci) > > > +{ > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > + > > > + s32g_pcie_enable_ltssm(s32g_pp); > > > + > > > + return 0; > > > +} > > > + > > > +static void s32g_pcie_stop_link(struct dw_pcie *pci) > > > +{ > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > + > > > + s32g_pcie_disable_ltssm(s32g_pp); > > > +} > > > + > > > +static struct dw_pcie_ops s32g_pcie_ops = { > > > + .start_link = s32g_pcie_start_link, > > > + .stop_link = s32g_pcie_stop_link, > > > +}; > > > + > > > +/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ > > > +static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci, u64 ddr_base_addr) > > > +{ > > > + u32 ddr_base_low = lower_32_bits(ddr_base_addr); > > > + u32 ddr_base_high = upper_32_bits(ddr_base_addr); > > > + > > > + dw_pcie_dbi_ro_wr_en(pci); > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); > > > + > > > + /* > > > + * Ncore is a cache-coherent interconnect module that enables the > > > + * integration of heterogeneous coherent and non-coherent agents in > > > + * the chip. Ncore Transactions to peripheral should be non-coherent > > > + * or it might drop them. > > > + * > > > + * One example where this is needed are PCIe MSIs, which use NoSnoop=0 > > > + * and might end up routed to Ncore. > > > > I don't think this statement is correct. No Snoop attribute is only applicable > > to MRd/MWr operations and not applicable to MSIs. Also, you've marked the > > The Ncore makes the bridge between the PCIe and the NoC and can decide > to drop some transactions based in this boundary > > > controller as cache coherent in the binding, but this comment doesn't relate to > > it. > > More details of the issue: > PCIe coherent traffic (e.g. MSIs) that targets peripheral space will > be dropped by Ncore because peripherals on S32G are not coherent as > slaves. We add a hard boundary in the PCIe controller coherency > control registers to separate physical memory space from peripheral > space. > Ok, this clarifies. Please add it to the comment. > > > > > + * Define the start of DDR as seen by Linux as the boundary between > > > + * "memory" and "peripherals", with peripherals being below. > > > > Please mention what this configuration does and why it is necessary. This is not > > clearly mentioned in the comment. > > > > > + */ > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, > > > + (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); > > > + dw_pcie_dbi_ro_wr_dis(pci); > > > +} > > > + > > > +static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) > > > +{ > > > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > + u32 val; > > > + > > > + /* Set RP mode */ > > > + val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); > > > + val &= ~DEVICE_TYPE_MASK; > > > + val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); > > > + > > > + /* Use default CRNS */ > > > > SRNS? > > it's CRNS > I'm not aware of CRNS, but only SRNS. Care to expand it? > > > > > + val &= ~SRIS_MODE; > > > + > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); > > > + > > > + /* > > > + * Make sure we use the coherency defaults (just in case the settings > > > + * have been changed from their reset values) > > > + */ > > > + s32g_pcie_reset_mstr_ace(pci, memblock_start_of_DRAM()); > > > + > > > + dw_pcie_dbi_ro_wr_en(pci); > > > + > > > + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); > > > + val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; > > > + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); > > > + > > > + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); > > > + val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; > > > + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); > > > + > > > + dw_pcie_dbi_ro_wr_dis(pci); > > > + > > > + return 0; > > > +} > > > + > > > +static const struct dw_pcie_host_ops s32g_pcie_host_ops = { > > > + .init = s32g_init_pcie_controller, > > > +}; > > > + > > > +static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) > > > +{ > > > + struct dw_pcie *pci = &s32g_pp->pci; > > > + struct device *dev = pci->dev; > > > + struct s32g_pcie_port *port, *tmp; > > > + int ret; > > > + > > > + list_for_each_entry(port, &s32g_pp->ports, list) { > > > + ret = phy_init(port->phy); > > > + if (ret) { > > > + dev_err(dev, "Failed to init serdes PHY\n"); > > > + goto err_phy_revert; > > > + } > > > + > > > + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); > > > + if (ret) { > > > + dev_err(dev, "Failed to set mode on serdes PHY\n"); > > > + goto err_phy_exit; > > > + } > > > + > > > + ret = phy_power_on(port->phy); > > > + if (ret) { > > > + dev_err(dev, "Failed to power on serdes PHY\n"); > > > + goto err_phy_exit; > > > + } > > > + } > > > + > > > + return 0; > > > + > > > +err_phy_exit: > > > + phy_exit(port->phy); > > > + > > > +err_phy_revert: > > > + list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { > > > + phy_power_off(port->phy); > > > + phy_exit(port->phy); > > > + } > > > + > > > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > > > + list_del(&port->list); > > > > Can't you use list_for_each_entry_safe_reverse() to combine both operations? > > No, it goes over all elements of the list whereas I only want to power > off and exit only those which have been init and powered on above. > Sorry, I misread the kdoc... > > > > > + > > > + return ret; > > > +} > > > + [...] > > > +static struct platform_driver s32g_pcie_driver = { > > > + .driver = { > > > + .name = "s32g-pcie", > > > + .of_match_table = s32g_pcie_of_match, > > > + .suppress_bind_attrs = true, > > > + .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), > > > + .probe_type = PROBE_PREFER_ASYNCHRONOUS, > > > + }, > > > + .probe = s32g_pcie_probe, > > > +}; > > > + > > > +module_platform_driver(s32g_pcie_driver); > > > > builtin_platform_driver() since this controller implements MSI controller. > > Can you elaborate ? > If a PCI controller is implementing an irqchip, it is not supposed to be removed during runtime due to IRQ disposal concern. So we encourage the driver to be tristate, but use builtin_platform_driver() so that it can be loaded as a module, but not removed dynamically. This limitation comes from the irqchip framework. - Mani -- மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) 2025-11-20 10:25 ` Manivannan Sadhasivam @ 2025-11-20 17:57 ` Vincent Guittot 0 siblings, 0 replies; 15+ messages in thread From: Vincent Guittot @ 2025-11-20 17:57 UTC (permalink / raw) To: Manivannan Sadhasivam Cc: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx, cassel On Thu, 20 Nov 2025 at 11:26, Manivannan Sadhasivam <mani@kernel.org> wrote: > > On Thu, Nov 20, 2025 at 10:06:53AM +0100, Vincent Guittot wrote: > > On Thu, 20 Nov 2025 at 09:22, Manivannan Sadhasivam <mani@kernel.org> wrote: > > > > > > On Tue, Nov 18, 2025 at 05:02:37PM +0100, Vincent Guittot wrote: > > > > Add initial support of the PCIe controller for S32G Soc family. Only > > > > host mode is supported. > > > > > > > > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > > > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com> > > > > Co-developed-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > > > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@nxp.com> > > > > Co-developed-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > > > Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@nxp.com> > > > > Co-developed-by: Larisa Grigore <larisa.grigore@nxp.com> > > > > Signed-off-by: Larisa Grigore <larisa.grigore@nxp.com> > > > > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> > > > > --- > > > > drivers/pci/controller/dwc/Kconfig | 10 + > > > > drivers/pci/controller/dwc/Makefile | 1 + > > > > .../pci/controller/dwc/pcie-nxp-s32g-regs.h | 21 + > > > > drivers/pci/controller/dwc/pcie-nxp-s32g.c | 391 ++++++++++++++++++ > > > > 4 files changed, 423 insertions(+) > > > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > > create mode 100644 drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > > > > > diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig > > > > index 349d4657393c..e276956c3fca 100644 > > > > --- a/drivers/pci/controller/dwc/Kconfig > > > > +++ b/drivers/pci/controller/dwc/Kconfig > > > > @@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP > > > > in order to enable device-specific features PCIE_TEGRA194_EP must be > > > > selected. This uses the DesignWare core. > > > > > > > > +config PCIE_NXP_S32G > > > > + tristate "NXP S32G PCIe controller (host mode)" > > > > + depends on ARCH_S32 || COMPILE_TEST > > > > + select PCIE_DW_HOST > > > > + help > > > > + Enable support for the PCIe controller in NXP S32G based boards to > > > > + work in Host mode. The controller is based on DesignWare IP and > > > > + can work either as RC or EP. In order to enable host-specific > > > > + features PCIE_NXP_S32G must be selected. > > > > + > > > > config PCIE_DW_PLAT > > > > bool > > > > > > > > diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile > > > > index 7ae28f3b0fb3..3301bbbad78c 100644 > > > > --- a/drivers/pci/controller/dwc/Makefile > > > > +++ b/drivers/pci/controller/dwc/Makefile > > > > @@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o > > > > obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o > > > > obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o > > > > obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > > > > +obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o > > > > obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o > > > > obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o > > > > obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o > > > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > > new file mode 100644 > > > > index 000000000000..81e35b6227d1 > > > > --- /dev/null > > > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g-regs.h > > > > @@ -0,0 +1,21 @@ > > > > +/* SPDX-License-Identifier: GPL-2.0+ */ > > > > +/* > > > > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > > > > + * Copyright 2016-2023, 2025 NXP > > > > + */ > > > > + > > > > +#ifndef PCIE_S32G_REGS_H > > > > +#define PCIE_S32G_REGS_H > > > > + > > > > +/* PCIe controller Sub-System */ > > > > + > > > > +/* PCIe controller 0 General Control 1 */ > > > > +#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 > > > > +#define DEVICE_TYPE_MASK GENMASK(3, 0) > > > > +#define SRIS_MODE BIT(8) > > > > + > > > > +/* PCIe controller 0 General Control 3 */ > > > > +#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 > > > > +#define LTSSM_EN BIT(0) > > > > + > > > > > > Since this header is not used by other drivers as of now, I'd prefer moving > > > these definitions inside the driver. > > > > I would prefer to keep it separate. It makes reg easier to parse and > > more registers will be added with new coming features > > > > The convention we follow is to mostly add register definitions within the driver > itself if they are not shared. It will be shared with EP driver later on but I supposed it can be merged in the meantime > > > > > > > > +#endif /* PCI_S32G_REGS_H */ > > > > diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > new file mode 100644 > > > > index 000000000000..eaa6b5363afe > > > > --- /dev/null > > > > +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c > > > > @@ -0,0 +1,391 @@ > > > > +// SPDX-License-Identifier: GPL-2.0 > > > > +/* > > > > + * PCIe host controller driver for NXP S32G SoCs > > > > + * > > > > + * Copyright 2019-2025 NXP > > > > + */ > > > > + > > > > +#include <linux/interrupt.h> > > > > +#include <linux/io.h> > > > > +#include <linux/memblock.h> > > > > +#include <linux/module.h> > > > > +#include <linux/of_device.h> > > > > +#include <linux/of_address.h> > > > > +#include <linux/pci.h> > > > > +#include <linux/phy/phy.h> > > > > +#include <linux/platform_device.h> > > > > +#include <linux/pm_runtime.h> > > > > +#include <linux/sizes.h> > > > > +#include <linux/types.h> > > > > + > > > > +#include "pcie-designware.h" > > > > +#include "pcie-nxp-s32g-regs.h" > > > > + > > > > +struct s32g_pcie_port { > > > > + struct list_head list; > > > > + struct phy *phy; > > > > +}; > > > > + > > > > +struct s32g_pcie { > > > > + struct dw_pcie pci; > > > > + void __iomem *ctrl_base; > > > > + struct list_head ports; > > > > +}; > > > > + > > > > +#define to_s32g_from_dw_pcie(x) \ > > > > + container_of(x, struct s32g_pcie, pci) > > > > + > > > > +static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) > > > > +{ > > > > + writel(val, s32g_pp->ctrl_base + reg); > > > > +} > > > > + > > > > +static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) > > > > +{ > > > > + return readl(s32g_pp->ctrl_base + reg); > > > > +} > > > > + > > > > +static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) > > > > +{ > > > > + u32 reg; > > > > + > > > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > > > + reg |= LTSSM_EN; > > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > > > +} > > > > + > > > > +static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) > > > > +{ > > > > + u32 reg; > > > > + > > > > + reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); > > > > + reg &= ~LTSSM_EN; > > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); > > > > +} > > > > + > > > > +static int s32g_pcie_start_link(struct dw_pcie *pci) > > > > +{ > > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > > + > > > > + s32g_pcie_enable_ltssm(s32g_pp); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static void s32g_pcie_stop_link(struct dw_pcie *pci) > > > > +{ > > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > > + > > > > + s32g_pcie_disable_ltssm(s32g_pp); > > > > +} > > > > + > > > > +static struct dw_pcie_ops s32g_pcie_ops = { > > > > + .start_link = s32g_pcie_start_link, > > > > + .stop_link = s32g_pcie_stop_link, > > > > +}; > > > > + > > > > +/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ > > > > +static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci, u64 ddr_base_addr) > > > > +{ > > > > + u32 ddr_base_low = lower_32_bits(ddr_base_addr); > > > > + u32 ddr_base_high = upper_32_bits(ddr_base_addr); > > > > + > > > > + dw_pcie_dbi_ro_wr_en(pci); > > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); > > > > + > > > > + /* > > > > + * Ncore is a cache-coherent interconnect module that enables the > > > > + * integration of heterogeneous coherent and non-coherent agents in > > > > + * the chip. Ncore Transactions to peripheral should be non-coherent > > > > + * or it might drop them. > > > > + * > > > > + * One example where this is needed are PCIe MSIs, which use NoSnoop=0 > > > > + * and might end up routed to Ncore. > > > > > > I don't think this statement is correct. No Snoop attribute is only applicable > > > to MRd/MWr operations and not applicable to MSIs. Also, you've marked the > > > > The Ncore makes the bridge between the PCIe and the NoC and can decide > > to drop some transactions based in this boundary > > > > > controller as cache coherent in the binding, but this comment doesn't relate to > > > it. > > > > More details of the issue: > > PCIe coherent traffic (e.g. MSIs) that targets peripheral space will > > be dropped by Ncore because peripherals on S32G are not coherent as > > slaves. We add a hard boundary in the PCIe controller coherency > > control registers to separate physical memory space from peripheral > > space. > > > > Ok, this clarifies. Please add it to the comment. > > > > > > > > + * Define the start of DDR as seen by Linux as the boundary between > > > > + * "memory" and "peripherals", with peripherals being below. > > > > > > Please mention what this configuration does and why it is necessary. This is not > > > clearly mentioned in the comment. > > > > > > > + */ > > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, > > > > + (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); > > > > + dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); > > > > + dw_pcie_dbi_ro_wr_dis(pci); > > > > +} > > > > + > > > > +static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) > > > > +{ > > > > + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); > > > > + struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); > > > > + u32 val; > > > > + > > > > + /* Set RP mode */ > > > > + val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); > > > > + val &= ~DEVICE_TYPE_MASK; > > > > + val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); > > > > + > > > > + /* Use default CRNS */ > > > > > > SRNS? > > > > it's CRNS > > > > I'm not aware of CRNS, but only SRNS. Care to expand it? - crns # Common Reference Clock, No Spread Spectrum - crss # Common Reference Clock, Spread Spectrum - srns # Separate reference Clock, No Spread Spectrum - sris # Separate Reference Clock, Independent Spread Spectrum s32g can support all modes but we only use the default crns for now until there is a generic way to describe this in DT for all platforms. This will be discussed in a separate thread https://lore.kernel.org/all/aMp0hNnBUwTV5cbp@ryzen/ > > > > > > > > + val &= ~SRIS_MODE; > > > > + > > > > + s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); > > > > + > > > > + /* > > > > + * Make sure we use the coherency defaults (just in case the settings > > > > + * have been changed from their reset values) > > > > + */ > > > > + s32g_pcie_reset_mstr_ace(pci, memblock_start_of_DRAM()); > > > > + > > > > + dw_pcie_dbi_ro_wr_en(pci); > > > > + > > > > + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); > > > > + val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; > > > > + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); > > > > + > > > > + val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); > > > > + val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; > > > > + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); > > > > + > > > > + dw_pcie_dbi_ro_wr_dis(pci); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static const struct dw_pcie_host_ops s32g_pcie_host_ops = { > > > > + .init = s32g_init_pcie_controller, > > > > +}; > > > > + > > > > +static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) > > > > +{ > > > > + struct dw_pcie *pci = &s32g_pp->pci; > > > > + struct device *dev = pci->dev; > > > > + struct s32g_pcie_port *port, *tmp; > > > > + int ret; > > > > + > > > > + list_for_each_entry(port, &s32g_pp->ports, list) { > > > > + ret = phy_init(port->phy); > > > > + if (ret) { > > > > + dev_err(dev, "Failed to init serdes PHY\n"); > > > > + goto err_phy_revert; > > > > + } > > > > + > > > > + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); > > > > + if (ret) { > > > > + dev_err(dev, "Failed to set mode on serdes PHY\n"); > > > > + goto err_phy_exit; > > > > + } > > > > + > > > > + ret = phy_power_on(port->phy); > > > > + if (ret) { > > > > + dev_err(dev, "Failed to power on serdes PHY\n"); > > > > + goto err_phy_exit; > > > > + } > > > > + } > > > > + > > > > + return 0; > > > > + > > > > +err_phy_exit: > > > > + phy_exit(port->phy); > > > > + > > > > +err_phy_revert: > > > > + list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { > > > > + phy_power_off(port->phy); > > > > + phy_exit(port->phy); > > > > + } > > > > + > > > > + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) > > > > + list_del(&port->list); > > > > > > Can't you use list_for_each_entry_safe_reverse() to combine both operations? > > > > No, it goes over all elements of the list whereas I only want to power > > off and exit only those which have been init and powered on above. > > > > Sorry, I misread the kdoc... > > > > > > > > + > > > > + return ret; > > > > +} > > > > + > > [...] > > > > > +static struct platform_driver s32g_pcie_driver = { > > > > + .driver = { > > > > + .name = "s32g-pcie", > > > > + .of_match_table = s32g_pcie_of_match, > > > > + .suppress_bind_attrs = true, > > > > + .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), > > > > + .probe_type = PROBE_PREFER_ASYNCHRONOUS, > > > > + }, > > > > + .probe = s32g_pcie_probe, > > > > +}; > > > > + > > > > +module_platform_driver(s32g_pcie_driver); > > > > > > builtin_platform_driver() since this controller implements MSI controller. > > > > Can you elaborate ? > > > > If a PCI controller is implementing an irqchip, it is not supposed to be removed > during runtime due to IRQ disposal concern. So we encourage the driver to be > tristate, but use builtin_platform_driver() so that it can be loaded as a > module, but not removed dynamically. > > This limitation comes from the irqchip framework. okay Anyway, I just realized that memblock_start_of_DRAM() is not exported so it can't be a module > > - Mani > > -- > மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/4 v5] MAINTAINERS: Add MAINTAINER for NXP S32G PCIe driver 2025-11-18 16:02 [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller Vincent Guittot ` (2 preceding siblings ...) 2025-11-18 16:02 ` [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) Vincent Guittot @ 2025-11-18 16:02 ` Vincent Guittot 3 siblings, 0 replies; 15+ messages in thread From: Vincent Guittot @ 2025-11-18 16:02 UTC (permalink / raw) To: chester62515, mbrugger, ghennadi.procopciuc, s32, bhelgaas, jingoohan1, lpieralisi, kwilczynski, mani, robh, krzk+dt, conor+dt, Ionut.Vicovan, larisa.grigore, Ghennadi.Procopciuc, ciprianmarian.costea, bogdan.hamciuc, Frank.li, linux-arm-kernel, linux-pci, devicetree, linux-kernel, imx Cc: cassel Add a new entry for S32G PCIe driver. Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e64b94e6b5a9..bec5d5792a5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3137,6 +3137,15 @@ F: arch/arm64/boot/dts/freescale/s32g*.dts* F: drivers/pinctrl/nxp/ F: drivers/rtc/rtc-s32g.c +ARM/NXP S32G PCIE CONTROLLER DRIVER +M: Ghennadi Procopciuc <ghennadi.procopciuc@oss.nxp.com> +R: NXP S32 Linux Team <s32@nxp.com> +L: imx@lists.linux.dev +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml +F: drivers/pci/controller/dwc/pcie-nxp-s32g* + ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER M: Jan Petrous <jan.petrous@oss.nxp.com> R: s32@nxp.com -- 2.43.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-11-20 17:57 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-11-18 16:02 [PATCH 0/4 v5] PCI: s32g: Add support for PCIe controller Vincent Guittot 2025-11-18 16:02 ` [PATCH 1/4 v5] dt-bindings: PCI: s32g: Add NXP " Vincent Guittot 2025-11-18 16:52 ` Frank Li 2025-11-20 11:27 ` Manivannan Sadhasivam 2025-11-20 15:23 ` Rob Herring (Arm) 2025-11-18 16:02 ` [PATCH 2/4 v5] PCI: dw: Add more registers and bitfield definition Vincent Guittot 2025-11-18 16:54 ` Frank Li 2025-11-18 16:02 ` [PATCH 3/4 v5] PCI: s32g: Add initial PCIe support (RC) Vincent Guittot 2025-11-18 17:01 ` Frank Li 2025-11-20 7:25 ` Vincent Guittot 2025-11-20 8:22 ` Manivannan Sadhasivam 2025-11-20 9:06 ` Vincent Guittot 2025-11-20 10:25 ` Manivannan Sadhasivam 2025-11-20 17:57 ` Vincent Guittot 2025-11-18 16:02 ` [PATCH 4/4 v5] MAINTAINERS: Add MAINTAINER for NXP S32G PCIe driver Vincent Guittot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).