* [PATCH 0/3] PCIe controller driver for Marvell Armada 3700 @ 2016-06-02 9:09 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: Bjorn Helgaas, linux-pci Cc: linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas, Thomas Petazzoni Hello, This small patch series adds a new driver for the PCIe controller found on Marvell Armada 3700 ARM64 SoCs. Like any other new driver submission, this patch series includes: a Device Tree binding documentation patch, a patch adding the driver itself, and a patch adding the Device Tree description of the hardware. Thanks in advance for your reviews! Thomas Thomas Petazzoni (3): dt-bindings: add DT binding for the Aardvark PCIe controller PCI: host: new PCI host controller driver for Marvell Armada 3700 arm64: dts: marvell: PCIe support for Armada 3700 .../devicetree/bindings/pci/aardvark-pci.txt | 51 + MAINTAINERS | 7 + arch/arm64/boot/dts/marvell/armada-3720-db.dts | 5 + arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 23 + drivers/pci/host/Kconfig | 9 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-aardvark.c | 1029 ++++++++++++++++++++ 7 files changed, 1125 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/aardvark-pci.txt create mode 100644 drivers/pci/host/pci-aardvark.c -- 2.7.4 ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/3] PCIe controller driver for Marvell Armada 3700 @ 2016-06-02 9:09 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: linux-arm-kernel Hello, This small patch series adds a new driver for the PCIe controller found on Marvell Armada 3700 ARM64 SoCs. Like any other new driver submission, this patch series includes: a Device Tree binding documentation patch, a patch adding the driver itself, and a patch adding the Device Tree description of the hardware. Thanks in advance for your reviews! Thomas Thomas Petazzoni (3): dt-bindings: add DT binding for the Aardvark PCIe controller PCI: host: new PCI host controller driver for Marvell Armada 3700 arm64: dts: marvell: PCIe support for Armada 3700 .../devicetree/bindings/pci/aardvark-pci.txt | 51 + MAINTAINERS | 7 + arch/arm64/boot/dts/marvell/armada-3720-db.dts | 5 + arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 23 + drivers/pci/host/Kconfig | 9 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-aardvark.c | 1029 ++++++++++++++++++++ 7 files changed, 1125 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/aardvark-pci.txt create mode 100644 drivers/pci/host/pci-aardvark.c -- 2.7.4 ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 9:09 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: Bjorn Helgaas, linux-pci Cc: linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas, Thomas Petazzoni This commit adds the documentation for the Device Tree binding used to describe the Aardvark PCIe controller, found on Marvell Armada 3700 ARM64 SoCs. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- .../devicetree/bindings/pci/aardvark-pci.txt | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/aardvark-pci.txt diff --git a/Documentation/devicetree/bindings/pci/aardvark-pci.txt b/Documentation/devicetree/bindings/pci/aardvark-pci.txt new file mode 100644 index 0000000..3517234 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/aardvark-pci.txt @@ -0,0 +1,51 @@ +Aardvark PCIe controller + +This PCIe controller is used on the Marvell Armada 3700 ARM64 SoC. + +The Device Tree node describing an Aardvark PCIe controller must +contain the following properties: + + - compatible: Should be "marvell,armada-3700-pcie" + - reg: range of registers for the PCIe controller + - interrupts: the interrupt line of the PCIe controller + - #address-cells: set to <3> + - #size-cells: set to <2> + - device_type: set to "pci" + - ranges: ranges for the PCI memory and I/O regions + - #interrupt-cells: set to <1> + - interrupt-map-mask and interrupt-map: standard PCI properties to + define the mapping of the PCIe interface to interrupt numbers. + - bus-range: PCI bus numbers covered + +In addition, the Device Tree describing an Aardvark PCIe controller +must include a sub-node that describes the legacy interrupt controller +built into the PCIe controller. This sub-node must have the following +properties: + + - interrupt-controller + - #interrupt-cells: set to <1> + +Example: + + pcie0: pcie@d0070000 { + compatible = "marvell,armada-3700-pcie"; + device_type = "pci"; + status = "disabled"; + reg = <0 0xd0070000 0 0x20000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; + }; -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 9:09 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: linux-arm-kernel This commit adds the documentation for the Device Tree binding used to describe the Aardvark PCIe controller, found on Marvell Armada 3700 ARM64 SoCs. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- .../devicetree/bindings/pci/aardvark-pci.txt | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/aardvark-pci.txt diff --git a/Documentation/devicetree/bindings/pci/aardvark-pci.txt b/Documentation/devicetree/bindings/pci/aardvark-pci.txt new file mode 100644 index 0000000..3517234 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/aardvark-pci.txt @@ -0,0 +1,51 @@ +Aardvark PCIe controller + +This PCIe controller is used on the Marvell Armada 3700 ARM64 SoC. + +The Device Tree node describing an Aardvark PCIe controller must +contain the following properties: + + - compatible: Should be "marvell,armada-3700-pcie" + - reg: range of registers for the PCIe controller + - interrupts: the interrupt line of the PCIe controller + - #address-cells: set to <3> + - #size-cells: set to <2> + - device_type: set to "pci" + - ranges: ranges for the PCI memory and I/O regions + - #interrupt-cells: set to <1> + - interrupt-map-mask and interrupt-map: standard PCI properties to + define the mapping of the PCIe interface to interrupt numbers. + - bus-range: PCI bus numbers covered + +In addition, the Device Tree describing an Aardvark PCIe controller +must include a sub-node that describes the legacy interrupt controller +built into the PCIe controller. This sub-node must have the following +properties: + + - interrupt-controller + - #interrupt-cells: set to <1> + +Example: + + pcie0: pcie at d0070000 { + compatible = "marvell,armada-3700-pcie"; + device_type = "pci"; + status = "disabled"; + reg = <0 0xd0070000 0 0x20000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; + }; -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 9:35 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 9:35 UTC (permalink / raw) To: linux-arm-kernel Cc: Thomas Petazzoni, Bjorn Helgaas, linux-pci, Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, Hanna Hawa, Nadav Haklai, Gregory Clement, Marcin Wojtas, Sebastian Hesselbarth On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > Any reason for not having a 64-bit MEM prefetchable area in the example? Does the host not support that? Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 9:35 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 9:35 UTC (permalink / raw) To: linux-arm-kernel On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > Any reason for not having a 64-bit MEM prefetchable area in the example? Does the host not support that? Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 9:35 ` Arnd Bergmann @ 2016-06-08 14:27 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 14:27 UTC (permalink / raw) To: Arnd Bergmann Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth Hello, Thanks for your review! On Thu, 02 Jun 2016 11:35:38 +0200, Arnd Bergmann wrote: > On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > > > > Any reason for not having a 64-bit MEM prefetchable area in the example? > Does the host not support that? I'll have to admit I am not sure how to find this out from the datasheet. My datasheet says about the PCIe controller: """ 64-bit PCIe address and system address space for outbound transactions """ So I guess this would indicate that a 64-bit MEM area is possible. However, since anyway the area used above is at 0xe8000000 for a length of 0x1000000, what would be the benefit of declaring this range as a 64-bit one ? Regarding the prefetchable aspect, I couldn't find any reference in the datasheet. However, the original driver code explicitly errors out if there is no non-prefetchable memory area, so I guess prefetchable areas is not supported. In of_bus_pci_get_flags(), both the 32-bit and 64-bit cases are handled in the same way, so is this distinction actually being used by the kernel? Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-08 14:27 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 14:27 UTC (permalink / raw) To: linux-arm-kernel Hello, Thanks for your review! On Thu, 02 Jun 2016 11:35:38 +0200, Arnd Bergmann wrote: > On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > > > > Any reason for not having a 64-bit MEM prefetchable area in the example? > Does the host not support that? I'll have to admit I am not sure how to find this out from the datasheet. My datasheet says about the PCIe controller: """ 64-bit PCIe address and system address space for outbound transactions """ So I guess this would indicate that a 64-bit MEM area is possible. However, since anyway the area used above is at 0xe8000000 for a length of 0x1000000, what would be the benefit of declaring this range as a 64-bit one ? Regarding the prefetchable aspect, I couldn't find any reference in the datasheet. However, the original driver code explicitly errors out if there is no non-prefetchable memory area, so I guess prefetchable areas is not supported. In of_bus_pci_get_flags(), both the 32-bit and 64-bit cases are handled in the same way, so is this distinction actually being used by the kernel? Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-08 14:27 ` Thomas Petazzoni @ 2016-06-08 15:34 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-08 15:34 UTC (permalink / raw) To: Thomas Petazzoni Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth On Wednesday, June 8, 2016 4:27:50 PM CEST Thomas Petazzoni wrote: > Hello, > > Thanks for your review! > > On Thu, 02 Jun 2016 11:35:38 +0200, Arnd Bergmann wrote: > > On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > > > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > > > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > > > > > > > Any reason for not having a 64-bit MEM prefetchable area in the example? > > Does the host not support that? > > I'll have to admit I am not sure how to find this out from the > datasheet. My datasheet says about the PCIe controller: > > """ > 64-bit PCIe address and system address space for outbound transactions > """ > > So I guess this would indicate that a 64-bit MEM area is possible. > However, since anyway the area used above is at 0xe8000000 for a length > of 0x1000000, what would be the benefit of declaring this range as a > 64-bit one ? > > Regarding the prefetchable aspect, I couldn't find any reference in the > datasheet. However, the original driver code explicitly errors out if > there is no non-prefetchable memory area, so I guess prefetchable > areas is not supported. > > In of_bus_pci_get_flags(), both the 32-bit and 64-bit cases are handled > in the same way, so is this distinction actually being used by the > kernel? Each device needs to have at least one non-prefetcheable range, which is why some drivers check for that. Non-prefetchable BARs always use 32-bit addressing, so 64-bit BARs are by definition prefetchable, but you can also have prefetchable registers in the first 4GB in some cases. Some devices require large prefetchable BARs (hundreds of MB, or more), so if you have the option, you should enable that in the host, as the 16MB you have available for non-prefetchable devices is not going to be sufficient. Arnd _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-08 15:34 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-08 15:34 UTC (permalink / raw) To: linux-arm-kernel On Wednesday, June 8, 2016 4:27:50 PM CEST Thomas Petazzoni wrote: > Hello, > > Thanks for your review! > > On Thu, 02 Jun 2016 11:35:38 +0200, Arnd Bergmann wrote: > > On Thursday, June 2, 2016 11:09:43 AM CEST Thomas Petazzoni wrote: > > > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > > > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > > > > > > > Any reason for not having a 64-bit MEM prefetchable area in the example? > > Does the host not support that? > > I'll have to admit I am not sure how to find this out from the > datasheet. My datasheet says about the PCIe controller: > > """ > 64-bit PCIe address and system address space for outbound transactions > """ > > So I guess this would indicate that a 64-bit MEM area is possible. > However, since anyway the area used above is at 0xe8000000 for a length > of 0x1000000, what would be the benefit of declaring this range as a > 64-bit one ? > > Regarding the prefetchable aspect, I couldn't find any reference in the > datasheet. However, the original driver code explicitly errors out if > there is no non-prefetchable memory area, so I guess prefetchable > areas is not supported. > > In of_bus_pci_get_flags(), both the 32-bit and 64-bit cases are handled > in the same way, so is this distinction actually being used by the > kernel? Each device needs to have at least one non-prefetcheable range, which is why some drivers check for that. Non-prefetchable BARs always use 32-bit addressing, so 64-bit BARs are by definition prefetchable, but you can also have prefetchable registers in the first 4GB in some cases. Some devices require large prefetchable BARs (hundreds of MB, or more), so if you have the option, you should enable that in the host, as the 16MB you have available for non-prefetchable devices is not going to be sufficient. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 12:24 ` Andrew Lunn -1 siblings, 0 replies; 42+ messages in thread From: Andrew Lunn @ 2016-06-02 12:24 UTC (permalink / raw) To: Thomas Petazzoni Cc: Bjorn Helgaas, linux-pci, linux-arm-kernel, Jason Cooper, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas > +In addition, the Device Tree describing an Aardvark PCIe controller > +must include a sub-node that describes the legacy interrupt controller > +built into the PCIe controller. This sub-node must have the following > +properties: > + > + - interrupt-controller > + - #interrupt-cells: set to <1> > + > +Example: > + > + pcie0: pcie@d0070000 { > + compatible = "marvell,armada-3700-pcie"; > + device_type = "pci"; > + status = "disabled"; > + reg = <0 0xd0070000 0 0x20000>; > + #address-cells = <3>; > + #size-cells = <2>; > + bus-range = <0x00 0xff>; > + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; > + #interrupt-cells = <1>; > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > + interrupt-map-mask = <0 0 0 7>; > + interrupt-map = <0 0 0 1 &pcie_intc 0>, > + <0 0 0 2 &pcie_intc 1>, > + <0 0 0 3 &pcie_intc 2>, > + <0 0 0 4 &pcie_intc 3>; > + pcie_intc: interrupt-controller { > + interrupt-controller; > + #interrupt-cells = <1>; > + }; Hi Thomas It is possible to list PCIe devices on the bus here as child nodes. I've done this when i needed a phandle to an intel ethernet controller on the PCIe bus, which i know is soldered onto the board. I think your current implementation simply uses the first child node. It would be good to document that ordering is important. It must be the first child node, and any pcie devices children must come afterwards. Andrew ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 12:24 ` Andrew Lunn 0 siblings, 0 replies; 42+ messages in thread From: Andrew Lunn @ 2016-06-02 12:24 UTC (permalink / raw) To: linux-arm-kernel > +In addition, the Device Tree describing an Aardvark PCIe controller > +must include a sub-node that describes the legacy interrupt controller > +built into the PCIe controller. This sub-node must have the following > +properties: > + > + - interrupt-controller > + - #interrupt-cells: set to <1> > + > +Example: > + > + pcie0: pcie at d0070000 { > + compatible = "marvell,armada-3700-pcie"; > + device_type = "pci"; > + status = "disabled"; > + reg = <0 0xd0070000 0 0x20000>; > + #address-cells = <3>; > + #size-cells = <2>; > + bus-range = <0x00 0xff>; > + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; > + #interrupt-cells = <1>; > + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ > + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ > + interrupt-map-mask = <0 0 0 7>; > + interrupt-map = <0 0 0 1 &pcie_intc 0>, > + <0 0 0 2 &pcie_intc 1>, > + <0 0 0 3 &pcie_intc 2>, > + <0 0 0 4 &pcie_intc 3>; > + pcie_intc: interrupt-controller { > + interrupt-controller; > + #interrupt-cells = <1>; > + }; Hi Thomas It is possible to list PCIe devices on the bus here as child nodes. I've done this when i needed a phandle to an intel ethernet controller on the PCIe bus, which i know is soldered onto the board. I think your current implementation simply uses the first child node. It would be good to document that ordering is important. It must be the first child node, and any pcie devices children must come afterwards. Andrew ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 12:24 ` Andrew Lunn @ 2016-06-02 12:34 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 12:34 UTC (permalink / raw) To: linux-arm-kernel Cc: Andrew Lunn, Thomas Petazzoni, Lior Amsalem, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, Sebastian Hesselbarth On Thursday, June 2, 2016 2:24:05 PM CEST Andrew Lunn wrote: > > It is possible to list PCIe devices on the bus here as child > nodes. I've done this when i needed a phandle to an intel ethernet > controller on the PCIe bus, which i know is soldered onto the board. > > I think your current implementation simply uses the first child > node. It would be good to document that ordering is important. It must > be the first child node, and any pcie devices children must come > afterwards. I think some other PCI hosts just move the interrupt-controller and #interrupt-cells properties into the PCI host node itself, which avoids the ambiguity here. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 12:34 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 12:34 UTC (permalink / raw) To: linux-arm-kernel On Thursday, June 2, 2016 2:24:05 PM CEST Andrew Lunn wrote: > > It is possible to list PCIe devices on the bus here as child > nodes. I've done this when i needed a phandle to an intel ethernet > controller on the PCIe bus, which i know is soldered onto the board. > > I think your current implementation simply uses the first child > node. It would be good to document that ordering is important. It must > be the first child node, and any pcie devices children must come > afterwards. I think some other PCI hosts just move the interrupt-controller and #interrupt-cells properties into the PCI host node itself, which avoids the ambiguity here. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 12:34 ` Arnd Bergmann @ 2016-06-02 12:45 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 12:45 UTC (permalink / raw) To: Arnd Bergmann Cc: linux-arm-kernel, Andrew Lunn, Lior Amsalem, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, Sebastian Hesselbarth Hello, On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > I think some other PCI hosts just move the interrupt-controller > and #interrupt-cells properties into the PCI host node itself, > which avoids the ambiguity here. Do you have an example of this? I have modeled my DT binding after the one used by the pci-dra7xx driver, which is in the same situation as I am in terms of interrupt handling. But I can indeed try to make the top-level PCIe controller node the interrupt controller. Let me try that quickly. Best regards, Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 12:45 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 12:45 UTC (permalink / raw) To: linux-arm-kernel Hello, On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > I think some other PCI hosts just move the interrupt-controller > and #interrupt-cells properties into the PCI host node itself, > which avoids the ambiguity here. Do you have an example of this? I have modeled my DT binding after the one used by the pci-dra7xx driver, which is in the same situation as I am in terms of interrupt handling. But I can indeed try to make the top-level PCIe controller node the interrupt controller. Let me try that quickly. Best regards, Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 12:45 ` Thomas Petazzoni @ 2016-06-02 13:53 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 13:53 UTC (permalink / raw) To: Thomas Petazzoni Cc: linux-arm-kernel, Andrew Lunn, Lior Amsalem, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, Sebastian Hesselbarth On Thursday, June 2, 2016 2:45:42 PM CEST Thomas Petazzoni wrote: > > On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > > > I think some other PCI hosts just move the interrupt-controller > > and #interrupt-cells properties into the PCI host node itself, > > which avoids the ambiguity here. > > Do you have an example of this? I have modeled my DT binding after the > one used by the pci-dra7xx driver, which is in the same situation as I > am in terms of interrupt handling. > > But I can indeed try to make the top-level PCIe controller node the > interrupt controller. Let me try that quickly. > Looking at the binding files, I see only this one: Documentation/devicetree/bindings/pci/altera-pcie.txt and a couple of others using a child node. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-02 13:53 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 13:53 UTC (permalink / raw) To: linux-arm-kernel On Thursday, June 2, 2016 2:45:42 PM CEST Thomas Petazzoni wrote: > > On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > > > I think some other PCI hosts just move the interrupt-controller > > and #interrupt-cells properties into the PCI host node itself, > > which avoids the ambiguity here. > > Do you have an example of this? I have modeled my DT binding after the > one used by the pci-dra7xx driver, which is in the same situation as I > am in terms of interrupt handling. > > But I can indeed try to make the top-level PCIe controller node the > interrupt controller. Let me try that quickly. > Looking at the binding files, I see only this one: Documentation/devicetree/bindings/pci/altera-pcie.txt and a couple of others using a child node. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 12:34 ` Arnd Bergmann @ 2016-06-08 14:28 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 14:28 UTC (permalink / raw) To: Arnd Bergmann Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Bjorn Helgaas, Gregory Clement, Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth Hello, On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > I think some other PCI hosts just move the interrupt-controller > and #interrupt-cells properties into the PCI host node itself, > which avoids the ambiguity here. I've tried that, and it works fine. Makes the code simpler, the binding simpler, so: adopted! Thanks for suggesting! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-08 14:28 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 14:28 UTC (permalink / raw) To: linux-arm-kernel Hello, On Thu, 02 Jun 2016 14:34:14 +0200, Arnd Bergmann wrote: > I think some other PCI hosts just move the interrupt-controller > and #interrupt-cells properties into the PCI host node itself, > which avoids the ambiguity here. I've tried that, and it works fine. Makes the code simpler, the binding simpler, so: adopted! Thanks for suggesting! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-10 15:44 ` Bjorn Helgaas -1 siblings, 0 replies; 42+ messages in thread From: Bjorn Helgaas @ 2016-06-10 15:44 UTC (permalink / raw) To: Thomas Petazzoni Cc: Bjorn Helgaas, linux-pci, linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas On Thu, Jun 02, 2016 at 11:09:43AM +0200, Thomas Petazzoni wrote: > This commit adds the documentation for the Device Tree binding used to > describe the Aardvark PCIe controller, found on Marvell Armada 3700 > ARM64 SoCs. > > Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Hi Thomas, If you post a v2 of these, can you align the subject lines of these binding, DT, and driver patches so it's easier to find the related pieces, e.g., add "Armada 3700" to this one or "Aardvark" to the others? It looks like maybe Aardvark could be used in several different systems, so maybe this patch should stay the same and the drivers/pci patch should mention Aardvark instead of only "Marvell Armada 3700"? Bjorn ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-10 15:44 ` Bjorn Helgaas 0 siblings, 0 replies; 42+ messages in thread From: Bjorn Helgaas @ 2016-06-10 15:44 UTC (permalink / raw) To: linux-arm-kernel On Thu, Jun 02, 2016 at 11:09:43AM +0200, Thomas Petazzoni wrote: > This commit adds the documentation for the Device Tree binding used to > describe the Aardvark PCIe controller, found on Marvell Armada 3700 > ARM64 SoCs. > > Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Hi Thomas, If you post a v2 of these, can you align the subject lines of these binding, DT, and driver patches so it's easier to find the related pieces, e.g., add "Armada 3700" to this one or "Aardvark" to the others? It looks like maybe Aardvark could be used in several different systems, so maybe this patch should stay the same and the drivers/pci patch should mention Aardvark instead of only "Marvell Armada 3700"? Bjorn ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller 2016-06-10 15:44 ` Bjorn Helgaas @ 2016-06-10 15:47 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-10 15:47 UTC (permalink / raw) To: Bjorn Helgaas Cc: Bjorn Helgaas, linux-pci, linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas Hello, On Fri, 10 Jun 2016 10:44:14 -0500, Bjorn Helgaas wrote: > If you post a v2 of these, can you align the subject lines of these > binding, DT, and driver patches so it's easier to find the related > pieces, e.g., add "Armada 3700" to this one or "Aardvark" to the > others? It looks like maybe Aardvark could be used in several > different systems, so maybe this patch should stay the same and the > drivers/pci patch should mention Aardvark instead of only "Marvell > Armada 3700"? Sure thing. I was about to send a v2, so I've fixed up the commit titles so that they all include Aardvark. Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller @ 2016-06-10 15:47 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-10 15:47 UTC (permalink / raw) To: linux-arm-kernel Hello, On Fri, 10 Jun 2016 10:44:14 -0500, Bjorn Helgaas wrote: > If you post a v2 of these, can you align the subject lines of these > binding, DT, and driver patches so it's easier to find the related > pieces, e.g., add "Armada 3700" to this one or "Aardvark" to the > others? It looks like maybe Aardvark could be used in several > different systems, so maybe this patch should stay the same and the > drivers/pci patch should mention Aardvark instead of only "Marvell > Armada 3700"? Sure thing. I was about to send a v2, so I've fixed up the commit titles so that they all include Aardvark. Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 9:09 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: Bjorn Helgaas, linux-pci Cc: linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas, Thomas Petazzoni This commit adds a new driver for a PCIe controller called Aardvark, which is used on the Marvell Armada 3700 ARM64 SoC. This patch is based on work done by Hezi Shahmoon <hezi.shahmoon@marvell.com> and Marcin Wojtas <mw@semihalf.com>. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- MAINTAINERS | 7 + drivers/pci/host/Kconfig | 9 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-aardvark.c | 1029 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 1046 insertions(+) create mode 100644 drivers/pci/host/pci-aardvark.c diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..373215c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8740,6 +8740,13 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/pci/host/*mvebu* +PCI DRIVER FOR AARDVARK (Marvell Armada 3700) +M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> +L: linux-pci@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/pci/host/pci-aardvark.c + PCI DRIVER FOR NVIDIA TEGRA M: Thierry Reding <thierry.reding@gmail.com> L: linux-tegra@vger.kernel.org diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 5d2374e..5f085fd 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -16,6 +16,15 @@ config PCI_MVEBU depends on ARM depends on OF +config PCI_AARDVARK + bool "Aardvark PCIe controller" + depends on ARCH_MVEBU && ARM64 + depends on OF + select PCI_MSI + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada + 3700 SoC. config PCIE_XILINX_NWL bool "NWL PCIe Core" diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 9c8698e..66f45b6 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o +obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c new file mode 100644 index 0000000..c204bc0 --- /dev/null +++ b/drivers/pci/host/pci-aardvark.c @@ -0,0 +1,1029 @@ +/* + * Driver for the Aardvark PCIe controller, used on Marvell Armada + * 3700. + * + * Copyright (C) 2016 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> + +/* PCIe core registers */ +#define PCIE_CORE_CMD_STATUS_REG 0x4 +#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) +#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) +#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) +#define PCIE_CORE_DEV_CTRL_STATS_REG 0xC8 +#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5 +#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12 +#define PCIE_CORE_LINK_CTRL_STAT_REG 0xD0 +#define PCIE_CORE_LINK_L0S_ENTRY BIT(0) +#define PCIE_CORE_LINK_TRAINING BIT(5) +#define PCIE_CORE_LINK_WIDTH_SHIFT 20 +#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) + +/* PIO registers base address and register offsets */ +#define PIO_BASE_ADDR 0x4000 +#define PIO_CTRL (PIO_BASE_ADDR + 0x0) +#define PIO_STAT (PIO_BASE_ADDR + 0x4) +#define PIO_COMPLETION_STATUS_SHIFT 7 +#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7) +#define PIO_COMPLETION_STATUS_OK 0 +#define PIO_COMPLETION_STATUS_UR 1 +#define PIO_COMPLETION_STATUS_CRS 2 +#define PIO_COMPLETION_STATUS_CA 4 +#define PIO_NON_POSTED_REQ BIT(0) +#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8) +#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc) +#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10) +#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14) +#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18) +#define PIO_START (PIO_BASE_ADDR + 0x1c) +#define PIO_ISR (PIO_BASE_ADDR + 0x20) +#define PIO_ISRM (PIO_BASE_ADDR + 0x24) + +/* Aardvark Control registers */ +#define CONTROL_BASE_ADDR 0x4800 +#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0) +#define PCIE_GEN_SEL_MSK 0x3 +#define PCIE_GEN_SEL_SHIFT 0x0 +#define SPEED_GEN_1 0 +#define SPEED_GEN_2 1 +#define SPEED_GEN_3 2 +#define IS_RC_MSK 1 +#define IS_RC_SHIFT 2 +#define LANE_CNT_MSK 0x18 +#define LANE_CNT_SHIFT 0x3 +#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT) +#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT) +#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT) +#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT) +#define LINK_TRAINING_EN BIT(6) +#define LEGACY_INTA BIT(28) +#define LEGACY_INTB BIT(29) +#define LEGACY_INTC BIT(30) +#define LEGACY_INTD BIT(31) +#define PCIE_CORE_CTRL1_REG (CONTROL_BASE_ADDR + 0x4) +#define HOT_RESET_GEN BIT(0) +#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8) +#define PCIE_CORE_CTRL2_RESERVED 0x7 +#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4) +#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5) +#define PCIE_CORE_CTRL2_OB_WIN_ENABLE BIT(6) +#define PCIE_CORE_CTRL2_MSI_ENABLE BIT(10) +#define PCIE_ISR0_REG (CONTROL_BASE_ADDR + 0x40) +#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) +#define PCIE_ISR0_FLR_INT BIT(26) +#define PCIE_ISR0_MSG_LTR BIT(25) +#define PCIE_ISR0_MSI_INT_PENDING BIT(24) +#define PCIE_ISR0_INTD_DEASSERT BIT(23) +#define PCIE_ISR0_INTC_DEASSERT BIT(22) +#define PCIE_ISR0_INTB_DEASSERT BIT(21) +#define PCIE_ISR0_INTA_DEASSERT BIT(20) +#define PCIE_ISR0_INTD_ASSERT BIT(19) +#define PCIE_ISR0_INTC_ASSERT BIT(18) +#define PCIE_ISR0_INTB_ASSERT BIT(17) +#define PCIE_ISR0_INTA_ASSERT BIT(16) +#define PCIE_ISR0_FAT_ERR BIT(13) +#define PCIE_ISR0_NFAT_ERR BIT(12) +#define PCIE_ISR0_CORR_ERR BIT(11) +#define PCIE_ISR0_LMI_LOCAL_INT BIT(10) +#define PCIE_ISR0_LEGACY_INT_SENT BIT(9) +#define PCIE_ISR0_MSG_PM_ACTIVE_STATE_NAK BIT(8) +#define PCIE_ISR0_MSG_PM_PME BIT(7) +#define PCIE_ISR0_MSG_PM_TURN_OFF BIT(6) +#define PCIE_ISR0_MSG_PME_TO_ACK BIT(5) +#define PCIE_ISR0_INB_DP_FERR_PERR_IRQ BIT(4) +#define PCIE_ISR0_OUTB_DP_FERR_PERR_IRQ BIT(3) +#define PCIE_ISR0_INBOUND_MSG BIT(2) +#define PCIE_ISR0_LINK_DOWN BIT(1) +#define PCIE_ISR0_HOT_RESET BIT(0) +#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val)) +#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val)) +#define PCIE_ISR0_INTX_MASK GENMASK(23, 16) +#define PCIE_ISR0_ALL_MASK GENMASK(26, 0) +#define PCIE_ISR1_REG (CONTROL_BASE_ADDR + 0x48) +#define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C) +#define PCIE_ISR1_POWER_STATE_CHANGE BIT(4) +#define PCIE_ISR1_FLUSH BIT(5) +#define PCIE_ISR1_ALL_MASK GENMASK(5, 4) +#define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50) +#define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) +#define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) +#define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C) +#define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C) + +/* PCIe window configuration */ +#define OB_WIN_BASE_ADDR 0x4c00 +#define OB_WIN_BLOCK_SIZE 0x20 +#define OB_WIN_REG_ADDR(win, offset) (OB_WIN_BASE_ADDR + \ + OB_WIN_BLOCK_SIZE * (win) + \ + (offset)) +#define OB_WIN_MATCH_LS(win) OB_WIN_REG_ADDR(win, 0x00) +#define OB_WIN_MATCH_MS(win) OB_WIN_REG_ADDR(win, 0x04) +#define OB_WIN_REMAP_LS(win) OB_WIN_REG_ADDR(win, 0x08) +#define OB_WIN_REMAP_MS(win) OB_WIN_REG_ADDR(win, 0x0c) +#define OB_WIN_MASK_LS(win) OB_WIN_REG_ADDR(win, 0x10) +#define OB_WIN_MASK_MS(win) OB_WIN_REG_ADDR(win, 0x14) +#define OB_WIN_ACTIONS(win) OB_WIN_REG_ADDR(win, 0x18) + +/* PCIe window types */ +#define OB_PCIE_MEM 0x0 +#define OB_PCIE_IO 0x4 +#define OB_PCIE_CONFIG0 0x8 +#define OB_PCIE_CONFIG1 0x9 +#define OB_PCIE_MSG 0xc +#define OB_PCIE_MSG_VENDOR 0xd + +/* LMI registers base address and register offsets */ +#define LMI_BASE_ADDR 0x6000 +#define CFG_REG (LMI_BASE_ADDR + 0x0) +#define LTSSM_SHIFT 24 +#define LTSSM_MASK 0x3f +#define LTSSM_L0 0x10 +#define RC_BAR_CONFIG 0x300 + +/* PCIe core controller registers */ +#define CTRL_CORE_BASE_ADDR 0x18000 +#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0) +#define CTRL_MODE_SHIFT 0x0 +#define CTRL_MODE_MASK 0x1 +#define PCIE_CORE_MODE_DIRECT 0x0 +#define PCIE_CORE_MODE_COMMAND 0x1 + +/* PCIe Central Interrupts Registers */ +#define CENTRAL_INT_BASE_ADDR 0x1b000 +#define HOST_CTRL_INT_STATUS_REG (CENTRAL_INT_BASE_ADDR + 0x0) +#define HOST_CTRL_INT_MASK_REG (CENTRAL_INT_BASE_ADDR + 0x4) +#define PCIE_IRQ_CMDQ_INT BIT(0) +#define PCIE_IRQ_MSI_STATUS_INT BIT(1) +#define PCIE_IRQ_CMD_SENT_DONE BIT(3) +#define PCIE_IRQ_DMA_INT BIT(4) +#define PCIE_IRQ_IB_DXFERDONE BIT(5) +#define PCIE_IRQ_OB_DXFERDONE BIT(6) +#define PCIE_IRQ_OB_RXFERDONE BIT(7) +#define PCIE_IRQ_COMPQ_INT BIT(12) +#define PCIE_IRQ_DIR_RD_DDR_DET BIT(13) +#define PCIE_IRQ_DIR_WR_DDR_DET BIT(14) +#define PCIE_IRQ_CORE_INT BIT(16) +#define PCIE_IRQ_CORE_INT_PIO BIT(17) +#define PCIE_IRQ_DPMU_INT BIT(18) +#define PCIE_IRQ_PCIE_MIS_INT BIT(19) +#define PCIE_IRQ_MSI_INT1_DET BIT(20) +#define PCIE_IRQ_MSI_INT2_DET BIT(21) +#define PCIE_IRQ_RC_DBELL_DET BIT(22) +#define PCIE_IRQ_EP_STATUS BIT(23) +#define PCIE_IRQ_ALL_MASK 0xfff0fb +#define PCIE_IRQ_ENABLE_INTS_MASK PCIE_IRQ_CORE_INT + +/* Transaction types */ +#define PCIE_CONFIG_RD_TYPE0 0x8 +#define PCIE_CONFIG_RD_TYPE1 0x9 +#define PCIE_CONFIG_WR_TYPE0 0xa +#define PCIE_CONFIG_WR_TYPE1 0xb + +/* PCI_BDF shifts 8bit, so we need extra 4bit shift */ +#define PCIE_BDF(dev) (dev << 4) +#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) +#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) +#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) +#define PCIE_CONF_REG(reg) ((reg) & 0xffc) +#define PCIE_CONF_ADDR(bus, devfn, where) \ + (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) + +#define PIO_TIMEOUT_NUM (1000) +#define PCIE_LINKUP_TIMEOUT (200) + +#define LEGACY_IRQ_NUM 4 +#define MSI_IRQ_NUM 32 + +struct advk_pcie { + struct platform_device *pdev; + void __iomem *base; + struct list_head resources; + struct irq_domain *irq_domain; + struct irq_chip irq_chip; + struct msi_controller msi; + struct irq_domain *msi_domain; + struct irq_chip msi_irq_chip; + DECLARE_BITMAP(msi_irq_in_use, MSI_IRQ_NUM); + struct mutex msi_used_lock; + u16 msi_msg; +}; + +static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg) +{ + writel(val, pcie->base + reg); +} + +static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg) +{ + return readl(pcie->base + reg); +} + +static bool advk_pcie_link_up(struct advk_pcie *pcie) +{ + int timeout; + u32 ltssm_state; + u32 val; + + timeout = PCIE_LINKUP_TIMEOUT; + do { + val = advk_readl(pcie, CFG_REG); + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; + timeout--; + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ + } while (ltssm_state < LTSSM_L0 && timeout > 0); + + return (timeout > 0); +} + +/* + * Set PCIe address window register which could be used for memory + * mapping. + */ +static void advk_pcie_set_ob_win(struct advk_pcie *pcie, + u32 win_num, u32 match_ms, + u32 match_ls, u32 mask_ms, + u32 mask_ls, u32 remap_ms, + u32 remap_ls, u32 action) +{ + advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num)); + advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num)); + advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num)); + advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num)); + advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num)); + advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num)); + advk_writel(pcie, action, OB_WIN_ACTIONS(win_num)); + advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num)); +} + +static void advk_pcie_setup_hw(struct advk_pcie *pcie) +{ + u32 reg; + int i; + + /* Point PCIe unit MBUS decode windows to DRAM space. */ + for (i = 0; i < 8; i++) + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); + + /* Set to Direct mode. */ + reg = advk_readl(pcie, CTRL_CONFIG_REG); + reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT); + reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT); + advk_writel(pcie, reg, CTRL_CONFIG_REG); + + /* Set PCI global control register to RC mode */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= (IS_RC_MSK << IS_RC_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* + * Set Advanced Error Capabilities and Control PF0 register + */ + reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | + PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; + advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); + + /* + * Set PCIe Device Control and Status 1 PF0 register + */ + reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | + (7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) | + PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE | + PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT; + advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); + + /* + * Program PCIe Control 2 to disable strict ordering. + */ + reg = PCIE_CORE_CTRL2_RESERVED | + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Set GEN2 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~PCIE_GEN_SEL_MSK; + reg |= SPEED_GEN_2; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; + reg |= LANE_COUNT_1; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable link training */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= LINK_TRAINING_EN; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable MSI */ + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + reg |= PCIE_CORE_CTRL2_MSI_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Clear all interrupts. */ + advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); + advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); + + /* Disable All ISR0/1 Sources */ + reg = PCIE_ISR0_ALL_MASK; + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); + + /* Unmask all MSI's */ + advk_writel(pcie, 0, PCIE_MSI_MASK_REG); + + /* Enable summary interrupt for GIC SPI source */ + reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); + advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG); + + /* Start link training */ + reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); + reg |= PCIE_CORE_LINK_TRAINING; + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + advk_pcie_link_up(pcie); + + reg = PCIE_CORE_LINK_L0S_ENTRY | + (1 << PCIE_CORE_LINK_WIDTH_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); + reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | + PCIE_CORE_CMD_IO_ACCESS_EN | + PCIE_CORE_CMD_MEM_IO_REQ_EN; + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); +} + +/* + * This routine is used to enable or disable AXI address window + * location generation. + * Disabled: No address window mapping. Use AXI user fields + * provided by the AXI fabric. + * Enabled: Enable the address window mapping. The HAL bridge + * generates the AXI user field locally. Use the local generated AXI user + * fields. + * It should be disabled when accessing the PCIe device by PIO. + * It should be enabled when accessing the PCIe device by memory + * access directly. + */ +static void advk_pcie_enable_axi_addr_gen(struct advk_pcie *pcie, bool enable) +{ + u32 reg; + + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + if (enable) + reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE; + else + reg &= ~PCIE_CORE_CTRL2_OB_WIN_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); +} + +static void advk_pcie_check_pio_status(struct advk_pcie *pcie) +{ + u32 reg; + unsigned int status; + char *strcomp_status, *str_posted; + + reg = advk_readl(pcie, PIO_STAT); + status = (reg & PIO_COMPLETION_STATUS_MASK) >> + PIO_COMPLETION_STATUS_SHIFT; + + if (!status) + return; + + switch (status) { + case PIO_COMPLETION_STATUS_UR: + strcomp_status = "UR"; + break; + case PIO_COMPLETION_STATUS_CRS: + strcomp_status = "CRS"; + break; + case PIO_COMPLETION_STATUS_CA: + strcomp_status = "CA"; + break; + default: + strcomp_status = "Unknown"; + break; + } + + if (reg & PIO_NON_POSTED_REQ) + str_posted = "Non-posted"; + else + str_posted = "Posted"; + + dev_err(&pcie->pdev->dev, + "%s PIO Response Status: %s, %#x @ %#x\n", + str_posted, strcomp_status, reg, + advk_readl(pcie, PIO_ADDR_LS)); +} + +static int advk_pcie_wait_pio(struct advk_pcie *pcie) +{ + int i; + u32 reg, is_done; + + for (i = 0; i < PIO_TIMEOUT_NUM; i++) { + reg = advk_readl(pcie, PIO_START); + is_done = advk_readl(pcie, PIO_ISR); + if ((!reg) && is_done) + break; + } + + if (i == PIO_TIMEOUT_NUM) { + dev_err(&pcie->pdev->dev, "config read/write timed out\n"); + return -ETIME; + } + + return 0; +} + +static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 *val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + int ret; + + if (PCI_SLOT(devfn) != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + advk_pcie_enable_axi_addr_gen(pcie, false); + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + if (bus->number == 0) + advk_writel(pcie, PCIE_CONFIG_RD_TYPE0, PIO_CTRL); + else + advk_writel(pcie, PCIE_CONFIG_RD_TYPE1, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + /* Program the data strobe */ + advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + advk_pcie_check_pio_status(pcie); + + /* Get the read result */ + *val = advk_readl(pcie, PIO_RD_DATA); + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + advk_pcie_enable_axi_addr_gen(pcie, true); + + return PCIBIOS_SUCCESSFUL; +} + +static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + u32 data_strobe = 0x0; + int offset; + int ret; + + if (PCI_SLOT(devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + advk_pcie_enable_axi_addr_gen(pcie, false); + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + if (bus->number == 0) + advk_writel(pcie, PCIE_CONFIG_WR_TYPE0, PIO_CTRL); + else + advk_writel(pcie, PCIE_CONFIG_WR_TYPE1, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_CONF_ADDR(bus->number, devfn, where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + if (where % size) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + /* Calculate the write strobe */ + offset = where & 0x3; + reg = val << (8 * offset); + data_strobe = GENMASK(size - 1, 0) << offset; + + /* Program the data register */ + advk_writel(pcie, reg, PIO_WR_DATA); + + /* Program the data strobe */ + advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + advk_pcie_check_pio_status(pcie); + + advk_pcie_enable_axi_addr_gen(pcie, true); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops advk_pcie_ops = { + .read = advk_pcie_rd_conf, + .write = advk_pcie_wr_conf, +}; + +static int advk_pcie_alloc_msi(struct advk_pcie *pcie) +{ + int hwirq; + + mutex_lock(&pcie->msi_used_lock); + hwirq = find_first_zero_bit(pcie->msi_irq_in_use, MSI_IRQ_NUM); + if (hwirq >= MSI_IRQ_NUM) + hwirq = -ENOSPC; + else + set_bit(hwirq, pcie->msi_irq_in_use); + mutex_unlock(&pcie->msi_used_lock); + + return hwirq; +} + +static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq) +{ + mutex_lock(&pcie->msi_used_lock); + if (!test_bit(hwirq, pcie->msi_irq_in_use)) + pr_err("trying to free unused MSI#%d\n", hwirq); + else + clear_bit(hwirq, pcie->msi_irq_in_use); + mutex_unlock(&pcie->msi_used_lock); +} + +static int advk_pcie_setup_msi_irq(struct msi_controller *chip, + struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct advk_pcie *pcie = pdev->bus->sysdata; + struct msi_msg msg; + int virq, hwirq; + phys_addr_t msi_msg_phys; + + /* We support MSI, but not MSI-X */ + if (desc->msi_attrib.is_msix) + return -EINVAL; + + hwirq = advk_pcie_alloc_msi(pcie); + if (hwirq < 0) + return hwirq; + + virq = irq_create_mapping(pcie->msi_domain, hwirq); + if (!virq) { + advk_pcie_free_msi(pcie, hwirq); + return -EINVAL; + } + + irq_set_msi_desc(virq, desc); + + msi_msg_phys = virt_to_phys(&pcie->msi_msg); + + msg.address_lo = lower_32_bits(msi_msg_phys); + msg.address_hi = upper_32_bits(msi_msg_phys); + msg.data = virq; + + pci_write_msi_msg(virq, &msg); + + return 0; +} + +static void advk_pcie_teardown_msi_irq(struct msi_controller *chip, + unsigned int irq) +{ + struct irq_data *d = irq_get_irq_data(irq); + struct msi_desc *msi = irq_data_get_msi_desc(d); + struct advk_pcie *pcie = msi_desc_to_pci_sysdata(msi); + unsigned long hwirq = d->hwirq; + + irq_dispose_mapping(irq); + advk_pcie_free_msi(pcie, hwirq); +} + +static int advk_pcie_msi_map(struct irq_domain *domain, + unsigned int virq, irq_hw_number_t hw) +{ + struct advk_pcie *pcie = domain->host_data; + + irq_set_chip_and_handler(virq, &pcie->msi_irq_chip, + handle_simple_irq); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_msi_irq_ops = { + .map = advk_pcie_msi_map, +}; + +static void advk_pcie_irq_mask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + mask |= PCIE_ISR0_INTX_ASSERT(hwirq) | + PCIE_ISR0_INTX_DEASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); +} + +static void advk_pcie_irq_unmask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + mask &= ~(PCIE_ISR0_INTX_ASSERT(hwirq) | + PCIE_ISR0_INTX_DEASSERT(hwirq)); + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); +} + +static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) +{ + struct advk_pcie *pcie = h->host_data; + + advk_pcie_irq_mask(irq_get_irq_data(virq)); + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &pcie->irq_chip, + handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_irq_domain_ops = { + .map = advk_pcie_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int advk_pcie_init_irq(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + struct irq_chip *irq_chip, *msi_irq_chip; + struct msi_controller *msi; + phys_addr_t msi_msg_phys; + + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return PTR_ERR(pcie_intc_node); + } + + irq_chip = &pcie->irq_chip; + + irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", + dev_name(dev)); + if (!irq_chip->name) + return -ENOMEM; + irq_chip->irq_mask = advk_pcie_irq_mask; + irq_chip->irq_mask_ack = advk_pcie_irq_mask; + irq_chip->irq_unmask = advk_pcie_irq_unmask; + + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, LEGACY_IRQ_NUM, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return PTR_ERR(pcie->irq_domain); + } + + msi_irq_chip = &pcie->msi_irq_chip; + + msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", + dev_name(dev)); + if (!msi_irq_chip->name) { + irq_domain_remove(pcie->irq_domain); + return -ENOMEM; + } + + msi_irq_chip->irq_enable = pci_msi_unmask_irq; + msi_irq_chip->irq_disable = pci_msi_mask_irq; + msi_irq_chip->irq_mask = pci_msi_mask_irq; + msi_irq_chip->irq_unmask = pci_msi_unmask_irq; + + msi = &pcie->msi; + + msi->setup_irq = advk_pcie_setup_msi_irq; + msi->teardown_irq = advk_pcie_teardown_msi_irq; + msi->of_node = node; + + mutex_init(&pcie->msi_used_lock); + + msi_msg_phys = virt_to_phys(&pcie->msi_msg); + + advk_writel(pcie, lower_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_LOW_REG); + advk_writel(pcie, upper_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_HIGH_REG); + + pcie->msi_domain = + irq_domain_add_linear(NULL, MSI_IRQ_NUM, + &advk_pcie_msi_irq_ops, pcie); + if (!pcie->msi_domain) { + irq_domain_remove(pcie->irq_domain); + return -ENOMEM; + } + + return 0; +} + +static void advk_pcie_handle_msi(struct advk_pcie *pcie) +{ + u32 msi_val, msi_mask, msi_status, msi_idx; + u16 msi_data; + + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); + msi_status = msi_val & ~msi_mask; + + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { + if (!(BIT(msi_idx) & msi_status)) + continue; + + advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); + msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; + generic_handle_irq(msi_data); + } + + advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, + PCIE_ISR0_REG); +} + +static void advk_pcie_handle_int(struct advk_pcie *pcie) +{ + u32 val, mask, status; + + val = advk_readl(pcie, PCIE_ISR0_REG); + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + status = val & ((~mask) & PCIE_ISR0_ALL_MASK); + + if (!status) { + advk_writel(pcie, val, PCIE_ISR0_REG); + return; + } + + /* Process MSI interrupts */ + if (status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); + + /* Process legacy interrupts */ + do { + int i; + + /* + * As documented in the datasheet, we need to raise an + * interrupt for each ASSERT(i) bit that is set, and + * continue to deliver the interrupt until the + * DEASSERT(i) bit gets set + */ + for (i = 0; i < LEGACY_IRQ_NUM; i++) { + if (!(status & PCIE_ISR0_INTX_ASSERT(i))) + continue; + + do { + int virq; + + advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), + PCIE_ISR0_REG); + virq = irq_find_mapping(pcie->irq_domain, i); + generic_handle_irq(virq); + status = advk_readl(pcie, PCIE_ISR0_REG); + } while (!(status & PCIE_ISR0_INTX_DEASSERT(i))); + + advk_writel(pcie, PCIE_ISR0_INTX_DEASSERT(i), + PCIE_ISR0_REG); + } + + status = advk_readl(pcie, PCIE_ISR0_REG); + } while (status & PCIE_ISR0_INTX_MASK); +} + +static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) +{ + struct advk_pcie *pcie = arg; + u32 status; + + status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + if (!(status & PCIE_IRQ_CORE_INT)) + return IRQ_NONE; + + advk_pcie_handle_int(pcie); + + /* Clear interrupt */ + advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); + + return IRQ_HANDLED; +} + +static void advk_pcie_release_of_pci_ranges(struct advk_pcie *pcie) +{ + pci_free_resource_list(&pcie->resources); +} + +static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie) +{ + int err, res_valid = 0; + struct device *dev = &pcie->pdev->dev; + struct device_node *np = dev->of_node; + struct resource_entry *win; + resource_size_t iobase; + + INIT_LIST_HEAD(&pcie->resources); + + err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources, + &iobase); + if (err) + return err; + + resource_list_for_each_entry(win, &pcie->resources) { + struct resource *parent, *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + parent = &ioport_resource; + advk_pcie_set_ob_win(pcie, 1, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0, 0xF8000000, 0, + lower_32_bits(res->start), + OB_PCIE_IO); + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + continue; + } + break; + case IORESOURCE_MEM: + parent = &iomem_resource; + advk_pcie_set_ob_win(pcie, 0, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0x0, 0xF8000000, 0, + lower_32_bits(res->start), + (2 << 20) | OB_PCIE_MEM); + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + break; + default: + continue; + } + + err = devm_request_resource(dev, parent, res); + if (err) + goto out_release_res; + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + goto out_release_res; + } + + return 0; + +out_release_res: + advk_pcie_release_of_pci_ranges(pcie); + return err; +} + +static int advk_pcie_probe(struct platform_device *pdev) +{ + struct advk_pcie *pcie; + struct resource *res; + struct pci_bus *bus, *child; + int ret, irq; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie), + GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie->base)) { + dev_err(&pdev->dev, "failed to map registers\n"); + return PTR_ERR(pcie->base); + } + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", + pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to register interrupt\n"); + return ret; + } + + ret = advk_pcie_parse_request_of_pci_ranges(pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to parse resources\n"); + return ret; + } + + advk_pcie_setup_hw(pcie); + advk_pcie_enable_axi_addr_gen(pcie, true); + + ret = advk_pcie_init_irq(pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize irq\n"); + return ret; + } + + bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops, + pcie, &pcie->resources, &pcie->msi); + if (!bus) + return -ENOMEM; + + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(bus); + + return 0; +} + +static const struct of_device_id advk_pcie_of_match_table[] = { + { .compatible = "marvell,armada-3700-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, advk_pcie_of_match_table); + +static struct platform_driver advk_pcie_driver = { + .driver = { + .name = "advk-pcie", + .of_match_table = advk_pcie_of_match_table, + /* Driver unloading/unbinding currently not supported */ + .suppress_bind_attrs = true, + }, + .probe = advk_pcie_probe, +}; +module_platform_driver(advk_pcie_driver); + +MODULE_AUTHOR("Hezi Shahmoon <hezi.shahmoon@marvell.com>"); +MODULE_DESCRIPTION("Aardvark PCIe driver"); +MODULE_LICENSE("GPL v2"); -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-02 9:09 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: linux-arm-kernel This commit adds a new driver for a PCIe controller called Aardvark, which is used on the Marvell Armada 3700 ARM64 SoC. This patch is based on work done by Hezi Shahmoon <hezi.shahmoon@marvell.com> and Marcin Wojtas <mw@semihalf.com>. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- MAINTAINERS | 7 + drivers/pci/host/Kconfig | 9 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-aardvark.c | 1029 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 1046 insertions(+) create mode 100644 drivers/pci/host/pci-aardvark.c diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..373215c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8740,6 +8740,13 @@ L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/pci/host/*mvebu* +PCI DRIVER FOR AARDVARK (Marvell Armada 3700) +M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> +L: linux-pci at vger.kernel.org +L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/pci/host/pci-aardvark.c + PCI DRIVER FOR NVIDIA TEGRA M: Thierry Reding <thierry.reding@gmail.com> L: linux-tegra at vger.kernel.org diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 5d2374e..5f085fd 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -16,6 +16,15 @@ config PCI_MVEBU depends on ARM depends on OF +config PCI_AARDVARK + bool "Aardvark PCIe controller" + depends on ARCH_MVEBU && ARM64 + depends on OF + select PCI_MSI + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada + 3700 SoC. config PCIE_XILINX_NWL bool "NWL PCIe Core" diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 9c8698e..66f45b6 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o +obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c new file mode 100644 index 0000000..c204bc0 --- /dev/null +++ b/drivers/pci/host/pci-aardvark.c @@ -0,0 +1,1029 @@ +/* + * Driver for the Aardvark PCIe controller, used on Marvell Armada + * 3700. + * + * Copyright (C) 2016 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> + +/* PCIe core registers */ +#define PCIE_CORE_CMD_STATUS_REG 0x4 +#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) +#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) +#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) +#define PCIE_CORE_DEV_CTRL_STATS_REG 0xC8 +#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT 5 +#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) +#define PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT 12 +#define PCIE_CORE_LINK_CTRL_STAT_REG 0xD0 +#define PCIE_CORE_LINK_L0S_ENTRY BIT(0) +#define PCIE_CORE_LINK_TRAINING BIT(5) +#define PCIE_CORE_LINK_WIDTH_SHIFT 20 +#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) + +/* PIO registers base address and register offsets */ +#define PIO_BASE_ADDR 0x4000 +#define PIO_CTRL (PIO_BASE_ADDR + 0x0) +#define PIO_STAT (PIO_BASE_ADDR + 0x4) +#define PIO_COMPLETION_STATUS_SHIFT 7 +#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7) +#define PIO_COMPLETION_STATUS_OK 0 +#define PIO_COMPLETION_STATUS_UR 1 +#define PIO_COMPLETION_STATUS_CRS 2 +#define PIO_COMPLETION_STATUS_CA 4 +#define PIO_NON_POSTED_REQ BIT(0) +#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8) +#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc) +#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10) +#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14) +#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18) +#define PIO_START (PIO_BASE_ADDR + 0x1c) +#define PIO_ISR (PIO_BASE_ADDR + 0x20) +#define PIO_ISRM (PIO_BASE_ADDR + 0x24) + +/* Aardvark Control registers */ +#define CONTROL_BASE_ADDR 0x4800 +#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0) +#define PCIE_GEN_SEL_MSK 0x3 +#define PCIE_GEN_SEL_SHIFT 0x0 +#define SPEED_GEN_1 0 +#define SPEED_GEN_2 1 +#define SPEED_GEN_3 2 +#define IS_RC_MSK 1 +#define IS_RC_SHIFT 2 +#define LANE_CNT_MSK 0x18 +#define LANE_CNT_SHIFT 0x3 +#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT) +#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT) +#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT) +#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT) +#define LINK_TRAINING_EN BIT(6) +#define LEGACY_INTA BIT(28) +#define LEGACY_INTB BIT(29) +#define LEGACY_INTC BIT(30) +#define LEGACY_INTD BIT(31) +#define PCIE_CORE_CTRL1_REG (CONTROL_BASE_ADDR + 0x4) +#define HOT_RESET_GEN BIT(0) +#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8) +#define PCIE_CORE_CTRL2_RESERVED 0x7 +#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4) +#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5) +#define PCIE_CORE_CTRL2_OB_WIN_ENABLE BIT(6) +#define PCIE_CORE_CTRL2_MSI_ENABLE BIT(10) +#define PCIE_ISR0_REG (CONTROL_BASE_ADDR + 0x40) +#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) +#define PCIE_ISR0_FLR_INT BIT(26) +#define PCIE_ISR0_MSG_LTR BIT(25) +#define PCIE_ISR0_MSI_INT_PENDING BIT(24) +#define PCIE_ISR0_INTD_DEASSERT BIT(23) +#define PCIE_ISR0_INTC_DEASSERT BIT(22) +#define PCIE_ISR0_INTB_DEASSERT BIT(21) +#define PCIE_ISR0_INTA_DEASSERT BIT(20) +#define PCIE_ISR0_INTD_ASSERT BIT(19) +#define PCIE_ISR0_INTC_ASSERT BIT(18) +#define PCIE_ISR0_INTB_ASSERT BIT(17) +#define PCIE_ISR0_INTA_ASSERT BIT(16) +#define PCIE_ISR0_FAT_ERR BIT(13) +#define PCIE_ISR0_NFAT_ERR BIT(12) +#define PCIE_ISR0_CORR_ERR BIT(11) +#define PCIE_ISR0_LMI_LOCAL_INT BIT(10) +#define PCIE_ISR0_LEGACY_INT_SENT BIT(9) +#define PCIE_ISR0_MSG_PM_ACTIVE_STATE_NAK BIT(8) +#define PCIE_ISR0_MSG_PM_PME BIT(7) +#define PCIE_ISR0_MSG_PM_TURN_OFF BIT(6) +#define PCIE_ISR0_MSG_PME_TO_ACK BIT(5) +#define PCIE_ISR0_INB_DP_FERR_PERR_IRQ BIT(4) +#define PCIE_ISR0_OUTB_DP_FERR_PERR_IRQ BIT(3) +#define PCIE_ISR0_INBOUND_MSG BIT(2) +#define PCIE_ISR0_LINK_DOWN BIT(1) +#define PCIE_ISR0_HOT_RESET BIT(0) +#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val)) +#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val)) +#define PCIE_ISR0_INTX_MASK GENMASK(23, 16) +#define PCIE_ISR0_ALL_MASK GENMASK(26, 0) +#define PCIE_ISR1_REG (CONTROL_BASE_ADDR + 0x48) +#define PCIE_ISR1_MASK_REG (CONTROL_BASE_ADDR + 0x4C) +#define PCIE_ISR1_POWER_STATE_CHANGE BIT(4) +#define PCIE_ISR1_FLUSH BIT(5) +#define PCIE_ISR1_ALL_MASK GENMASK(5, 4) +#define PCIE_MSI_ADDR_LOW_REG (CONTROL_BASE_ADDR + 0x50) +#define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) +#define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) +#define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C) +#define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C) + +/* PCIe window configuration */ +#define OB_WIN_BASE_ADDR 0x4c00 +#define OB_WIN_BLOCK_SIZE 0x20 +#define OB_WIN_REG_ADDR(win, offset) (OB_WIN_BASE_ADDR + \ + OB_WIN_BLOCK_SIZE * (win) + \ + (offset)) +#define OB_WIN_MATCH_LS(win) OB_WIN_REG_ADDR(win, 0x00) +#define OB_WIN_MATCH_MS(win) OB_WIN_REG_ADDR(win, 0x04) +#define OB_WIN_REMAP_LS(win) OB_WIN_REG_ADDR(win, 0x08) +#define OB_WIN_REMAP_MS(win) OB_WIN_REG_ADDR(win, 0x0c) +#define OB_WIN_MASK_LS(win) OB_WIN_REG_ADDR(win, 0x10) +#define OB_WIN_MASK_MS(win) OB_WIN_REG_ADDR(win, 0x14) +#define OB_WIN_ACTIONS(win) OB_WIN_REG_ADDR(win, 0x18) + +/* PCIe window types */ +#define OB_PCIE_MEM 0x0 +#define OB_PCIE_IO 0x4 +#define OB_PCIE_CONFIG0 0x8 +#define OB_PCIE_CONFIG1 0x9 +#define OB_PCIE_MSG 0xc +#define OB_PCIE_MSG_VENDOR 0xd + +/* LMI registers base address and register offsets */ +#define LMI_BASE_ADDR 0x6000 +#define CFG_REG (LMI_BASE_ADDR + 0x0) +#define LTSSM_SHIFT 24 +#define LTSSM_MASK 0x3f +#define LTSSM_L0 0x10 +#define RC_BAR_CONFIG 0x300 + +/* PCIe core controller registers */ +#define CTRL_CORE_BASE_ADDR 0x18000 +#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0) +#define CTRL_MODE_SHIFT 0x0 +#define CTRL_MODE_MASK 0x1 +#define PCIE_CORE_MODE_DIRECT 0x0 +#define PCIE_CORE_MODE_COMMAND 0x1 + +/* PCIe Central Interrupts Registers */ +#define CENTRAL_INT_BASE_ADDR 0x1b000 +#define HOST_CTRL_INT_STATUS_REG (CENTRAL_INT_BASE_ADDR + 0x0) +#define HOST_CTRL_INT_MASK_REG (CENTRAL_INT_BASE_ADDR + 0x4) +#define PCIE_IRQ_CMDQ_INT BIT(0) +#define PCIE_IRQ_MSI_STATUS_INT BIT(1) +#define PCIE_IRQ_CMD_SENT_DONE BIT(3) +#define PCIE_IRQ_DMA_INT BIT(4) +#define PCIE_IRQ_IB_DXFERDONE BIT(5) +#define PCIE_IRQ_OB_DXFERDONE BIT(6) +#define PCIE_IRQ_OB_RXFERDONE BIT(7) +#define PCIE_IRQ_COMPQ_INT BIT(12) +#define PCIE_IRQ_DIR_RD_DDR_DET BIT(13) +#define PCIE_IRQ_DIR_WR_DDR_DET BIT(14) +#define PCIE_IRQ_CORE_INT BIT(16) +#define PCIE_IRQ_CORE_INT_PIO BIT(17) +#define PCIE_IRQ_DPMU_INT BIT(18) +#define PCIE_IRQ_PCIE_MIS_INT BIT(19) +#define PCIE_IRQ_MSI_INT1_DET BIT(20) +#define PCIE_IRQ_MSI_INT2_DET BIT(21) +#define PCIE_IRQ_RC_DBELL_DET BIT(22) +#define PCIE_IRQ_EP_STATUS BIT(23) +#define PCIE_IRQ_ALL_MASK 0xfff0fb +#define PCIE_IRQ_ENABLE_INTS_MASK PCIE_IRQ_CORE_INT + +/* Transaction types */ +#define PCIE_CONFIG_RD_TYPE0 0x8 +#define PCIE_CONFIG_RD_TYPE1 0x9 +#define PCIE_CONFIG_WR_TYPE0 0xa +#define PCIE_CONFIG_WR_TYPE1 0xb + +/* PCI_BDF shifts 8bit, so we need extra 4bit shift */ +#define PCIE_BDF(dev) (dev << 4) +#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) +#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) +#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) +#define PCIE_CONF_REG(reg) ((reg) & 0xffc) +#define PCIE_CONF_ADDR(bus, devfn, where) \ + (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) + +#define PIO_TIMEOUT_NUM (1000) +#define PCIE_LINKUP_TIMEOUT (200) + +#define LEGACY_IRQ_NUM 4 +#define MSI_IRQ_NUM 32 + +struct advk_pcie { + struct platform_device *pdev; + void __iomem *base; + struct list_head resources; + struct irq_domain *irq_domain; + struct irq_chip irq_chip; + struct msi_controller msi; + struct irq_domain *msi_domain; + struct irq_chip msi_irq_chip; + DECLARE_BITMAP(msi_irq_in_use, MSI_IRQ_NUM); + struct mutex msi_used_lock; + u16 msi_msg; +}; + +static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg) +{ + writel(val, pcie->base + reg); +} + +static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg) +{ + return readl(pcie->base + reg); +} + +static bool advk_pcie_link_up(struct advk_pcie *pcie) +{ + int timeout; + u32 ltssm_state; + u32 val; + + timeout = PCIE_LINKUP_TIMEOUT; + do { + val = advk_readl(pcie, CFG_REG); + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; + timeout--; + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ + } while (ltssm_state < LTSSM_L0 && timeout > 0); + + return (timeout > 0); +} + +/* + * Set PCIe address window register which could be used for memory + * mapping. + */ +static void advk_pcie_set_ob_win(struct advk_pcie *pcie, + u32 win_num, u32 match_ms, + u32 match_ls, u32 mask_ms, + u32 mask_ls, u32 remap_ms, + u32 remap_ls, u32 action) +{ + advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num)); + advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num)); + advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num)); + advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num)); + advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num)); + advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num)); + advk_writel(pcie, action, OB_WIN_ACTIONS(win_num)); + advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num)); +} + +static void advk_pcie_setup_hw(struct advk_pcie *pcie) +{ + u32 reg; + int i; + + /* Point PCIe unit MBUS decode windows to DRAM space. */ + for (i = 0; i < 8; i++) + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); + + /* Set to Direct mode. */ + reg = advk_readl(pcie, CTRL_CONFIG_REG); + reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT); + reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT); + advk_writel(pcie, reg, CTRL_CONFIG_REG); + + /* Set PCI global control register to RC mode */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= (IS_RC_MSK << IS_RC_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* + * Set Advanced Error Capabilities and Control PF0 register + */ + reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | + PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | + PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; + advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); + + /* + * Set PCIe Device Control and Status 1 PF0 register + */ + reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | + (7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) | + PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE | + PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT; + advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); + + /* + * Program PCIe Control 2 to disable strict ordering. + */ + reg = PCIE_CORE_CTRL2_RESERVED | + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Set GEN2 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~PCIE_GEN_SEL_MSK; + reg |= SPEED_GEN_2; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; + reg |= LANE_COUNT_1; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable link training */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= LINK_TRAINING_EN; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable MSI */ + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + reg |= PCIE_CORE_CTRL2_MSI_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Clear all interrupts. */ + advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); + advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); + + /* Disable All ISR0/1 Sources */ + reg = PCIE_ISR0_ALL_MASK; + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); + + /* Unmask all MSI's */ + advk_writel(pcie, 0, PCIE_MSI_MASK_REG); + + /* Enable summary interrupt for GIC SPI source */ + reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); + advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG); + + /* Start link training */ + reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); + reg |= PCIE_CORE_LINK_TRAINING; + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + advk_pcie_link_up(pcie); + + reg = PCIE_CORE_LINK_L0S_ENTRY | + (1 << PCIE_CORE_LINK_WIDTH_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); + reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | + PCIE_CORE_CMD_IO_ACCESS_EN | + PCIE_CORE_CMD_MEM_IO_REQ_EN; + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); +} + +/* + * This routine is used to enable or disable AXI address window + * location generation. + * Disabled: No address window mapping. Use AXI user fields + * provided by the AXI fabric. + * Enabled: Enable the address window mapping. The HAL bridge + * generates the AXI user field locally. Use the local generated AXI user + * fields. + * It should be disabled when accessing the PCIe device by PIO. + * It should be enabled when accessing the PCIe device by memory + * access directly. + */ +static void advk_pcie_enable_axi_addr_gen(struct advk_pcie *pcie, bool enable) +{ + u32 reg; + + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + if (enable) + reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE; + else + reg &= ~PCIE_CORE_CTRL2_OB_WIN_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); +} + +static void advk_pcie_check_pio_status(struct advk_pcie *pcie) +{ + u32 reg; + unsigned int status; + char *strcomp_status, *str_posted; + + reg = advk_readl(pcie, PIO_STAT); + status = (reg & PIO_COMPLETION_STATUS_MASK) >> + PIO_COMPLETION_STATUS_SHIFT; + + if (!status) + return; + + switch (status) { + case PIO_COMPLETION_STATUS_UR: + strcomp_status = "UR"; + break; + case PIO_COMPLETION_STATUS_CRS: + strcomp_status = "CRS"; + break; + case PIO_COMPLETION_STATUS_CA: + strcomp_status = "CA"; + break; + default: + strcomp_status = "Unknown"; + break; + } + + if (reg & PIO_NON_POSTED_REQ) + str_posted = "Non-posted"; + else + str_posted = "Posted"; + + dev_err(&pcie->pdev->dev, + "%s PIO Response Status: %s, %#x @ %#x\n", + str_posted, strcomp_status, reg, + advk_readl(pcie, PIO_ADDR_LS)); +} + +static int advk_pcie_wait_pio(struct advk_pcie *pcie) +{ + int i; + u32 reg, is_done; + + for (i = 0; i < PIO_TIMEOUT_NUM; i++) { + reg = advk_readl(pcie, PIO_START); + is_done = advk_readl(pcie, PIO_ISR); + if ((!reg) && is_done) + break; + } + + if (i == PIO_TIMEOUT_NUM) { + dev_err(&pcie->pdev->dev, "config read/write timed out\n"); + return -ETIME; + } + + return 0; +} + +static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 *val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + int ret; + + if (PCI_SLOT(devfn) != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + advk_pcie_enable_axi_addr_gen(pcie, false); + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + if (bus->number == 0) + advk_writel(pcie, PCIE_CONFIG_RD_TYPE0, PIO_CTRL); + else + advk_writel(pcie, PCIE_CONFIG_RD_TYPE1, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + /* Program the data strobe */ + advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + advk_pcie_check_pio_status(pcie); + + /* Get the read result */ + *val = advk_readl(pcie, PIO_RD_DATA); + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + advk_pcie_enable_axi_addr_gen(pcie, true); + + return PCIBIOS_SUCCESSFUL; +} + +static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct advk_pcie *pcie = bus->sysdata; + u32 reg; + u32 data_strobe = 0x0; + int offset; + int ret; + + if (PCI_SLOT(devfn) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + advk_pcie_enable_axi_addr_gen(pcie, false); + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + if (bus->number == 0) + advk_writel(pcie, PCIE_CONFIG_WR_TYPE0, PIO_CTRL); + else + advk_writel(pcie, PCIE_CONFIG_WR_TYPE1, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_CONF_ADDR(bus->number, devfn, where); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + if (where % size) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + /* Calculate the write strobe */ + offset = where & 0x3; + reg = val << (8 * offset); + data_strobe = GENMASK(size - 1, 0) << offset; + + /* Program the data register */ + advk_writel(pcie, reg, PIO_WR_DATA); + + /* Program the data strobe */ + advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + ret = advk_pcie_wait_pio(pcie); + if (ret < 0) { + advk_pcie_enable_axi_addr_gen(pcie, true); + return PCIBIOS_SET_FAILED; + } + + advk_pcie_check_pio_status(pcie); + + advk_pcie_enable_axi_addr_gen(pcie, true); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops advk_pcie_ops = { + .read = advk_pcie_rd_conf, + .write = advk_pcie_wr_conf, +}; + +static int advk_pcie_alloc_msi(struct advk_pcie *pcie) +{ + int hwirq; + + mutex_lock(&pcie->msi_used_lock); + hwirq = find_first_zero_bit(pcie->msi_irq_in_use, MSI_IRQ_NUM); + if (hwirq >= MSI_IRQ_NUM) + hwirq = -ENOSPC; + else + set_bit(hwirq, pcie->msi_irq_in_use); + mutex_unlock(&pcie->msi_used_lock); + + return hwirq; +} + +static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq) +{ + mutex_lock(&pcie->msi_used_lock); + if (!test_bit(hwirq, pcie->msi_irq_in_use)) + pr_err("trying to free unused MSI#%d\n", hwirq); + else + clear_bit(hwirq, pcie->msi_irq_in_use); + mutex_unlock(&pcie->msi_used_lock); +} + +static int advk_pcie_setup_msi_irq(struct msi_controller *chip, + struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct advk_pcie *pcie = pdev->bus->sysdata; + struct msi_msg msg; + int virq, hwirq; + phys_addr_t msi_msg_phys; + + /* We support MSI, but not MSI-X */ + if (desc->msi_attrib.is_msix) + return -EINVAL; + + hwirq = advk_pcie_alloc_msi(pcie); + if (hwirq < 0) + return hwirq; + + virq = irq_create_mapping(pcie->msi_domain, hwirq); + if (!virq) { + advk_pcie_free_msi(pcie, hwirq); + return -EINVAL; + } + + irq_set_msi_desc(virq, desc); + + msi_msg_phys = virt_to_phys(&pcie->msi_msg); + + msg.address_lo = lower_32_bits(msi_msg_phys); + msg.address_hi = upper_32_bits(msi_msg_phys); + msg.data = virq; + + pci_write_msi_msg(virq, &msg); + + return 0; +} + +static void advk_pcie_teardown_msi_irq(struct msi_controller *chip, + unsigned int irq) +{ + struct irq_data *d = irq_get_irq_data(irq); + struct msi_desc *msi = irq_data_get_msi_desc(d); + struct advk_pcie *pcie = msi_desc_to_pci_sysdata(msi); + unsigned long hwirq = d->hwirq; + + irq_dispose_mapping(irq); + advk_pcie_free_msi(pcie, hwirq); +} + +static int advk_pcie_msi_map(struct irq_domain *domain, + unsigned int virq, irq_hw_number_t hw) +{ + struct advk_pcie *pcie = domain->host_data; + + irq_set_chip_and_handler(virq, &pcie->msi_irq_chip, + handle_simple_irq); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_msi_irq_ops = { + .map = advk_pcie_msi_map, +}; + +static void advk_pcie_irq_mask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + mask |= PCIE_ISR0_INTX_ASSERT(hwirq) | + PCIE_ISR0_INTX_DEASSERT(hwirq); + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); +} + +static void advk_pcie_irq_unmask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u32 mask; + + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + mask &= ~(PCIE_ISR0_INTX_ASSERT(hwirq) | + PCIE_ISR0_INTX_DEASSERT(hwirq)); + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); +} + +static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) +{ + struct advk_pcie *pcie = h->host_data; + + advk_pcie_irq_mask(irq_get_irq_data(virq)); + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &pcie->irq_chip, + handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_irq_domain_ops = { + .map = advk_pcie_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int advk_pcie_init_irq(struct advk_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + struct irq_chip *irq_chip, *msi_irq_chip; + struct msi_controller *msi; + phys_addr_t msi_msg_phys; + + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return PTR_ERR(pcie_intc_node); + } + + irq_chip = &pcie->irq_chip; + + irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", + dev_name(dev)); + if (!irq_chip->name) + return -ENOMEM; + irq_chip->irq_mask = advk_pcie_irq_mask; + irq_chip->irq_mask_ack = advk_pcie_irq_mask; + irq_chip->irq_unmask = advk_pcie_irq_unmask; + + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, LEGACY_IRQ_NUM, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return PTR_ERR(pcie->irq_domain); + } + + msi_irq_chip = &pcie->msi_irq_chip; + + msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", + dev_name(dev)); + if (!msi_irq_chip->name) { + irq_domain_remove(pcie->irq_domain); + return -ENOMEM; + } + + msi_irq_chip->irq_enable = pci_msi_unmask_irq; + msi_irq_chip->irq_disable = pci_msi_mask_irq; + msi_irq_chip->irq_mask = pci_msi_mask_irq; + msi_irq_chip->irq_unmask = pci_msi_unmask_irq; + + msi = &pcie->msi; + + msi->setup_irq = advk_pcie_setup_msi_irq; + msi->teardown_irq = advk_pcie_teardown_msi_irq; + msi->of_node = node; + + mutex_init(&pcie->msi_used_lock); + + msi_msg_phys = virt_to_phys(&pcie->msi_msg); + + advk_writel(pcie, lower_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_LOW_REG); + advk_writel(pcie, upper_32_bits(msi_msg_phys), + PCIE_MSI_ADDR_HIGH_REG); + + pcie->msi_domain = + irq_domain_add_linear(NULL, MSI_IRQ_NUM, + &advk_pcie_msi_irq_ops, pcie); + if (!pcie->msi_domain) { + irq_domain_remove(pcie->irq_domain); + return -ENOMEM; + } + + return 0; +} + +static void advk_pcie_handle_msi(struct advk_pcie *pcie) +{ + u32 msi_val, msi_mask, msi_status, msi_idx; + u16 msi_data; + + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); + msi_status = msi_val & ~msi_mask; + + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { + if (!(BIT(msi_idx) & msi_status)) + continue; + + advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); + msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; + generic_handle_irq(msi_data); + } + + advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, + PCIE_ISR0_REG); +} + +static void advk_pcie_handle_int(struct advk_pcie *pcie) +{ + u32 val, mask, status; + + val = advk_readl(pcie, PCIE_ISR0_REG); + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); + status = val & ((~mask) & PCIE_ISR0_ALL_MASK); + + if (!status) { + advk_writel(pcie, val, PCIE_ISR0_REG); + return; + } + + /* Process MSI interrupts */ + if (status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); + + /* Process legacy interrupts */ + do { + int i; + + /* + * As documented in the datasheet, we need to raise an + * interrupt for each ASSERT(i) bit that is set, and + * continue to deliver the interrupt until the + * DEASSERT(i) bit gets set + */ + for (i = 0; i < LEGACY_IRQ_NUM; i++) { + if (!(status & PCIE_ISR0_INTX_ASSERT(i))) + continue; + + do { + int virq; + + advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), + PCIE_ISR0_REG); + virq = irq_find_mapping(pcie->irq_domain, i); + generic_handle_irq(virq); + status = advk_readl(pcie, PCIE_ISR0_REG); + } while (!(status & PCIE_ISR0_INTX_DEASSERT(i))); + + advk_writel(pcie, PCIE_ISR0_INTX_DEASSERT(i), + PCIE_ISR0_REG); + } + + status = advk_readl(pcie, PCIE_ISR0_REG); + } while (status & PCIE_ISR0_INTX_MASK); +} + +static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) +{ + struct advk_pcie *pcie = arg; + u32 status; + + status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + if (!(status & PCIE_IRQ_CORE_INT)) + return IRQ_NONE; + + advk_pcie_handle_int(pcie); + + /* Clear interrupt */ + advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); + + return IRQ_HANDLED; +} + +static void advk_pcie_release_of_pci_ranges(struct advk_pcie *pcie) +{ + pci_free_resource_list(&pcie->resources); +} + +static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie) +{ + int err, res_valid = 0; + struct device *dev = &pcie->pdev->dev; + struct device_node *np = dev->of_node; + struct resource_entry *win; + resource_size_t iobase; + + INIT_LIST_HEAD(&pcie->resources); + + err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources, + &iobase); + if (err) + return err; + + resource_list_for_each_entry(win, &pcie->resources) { + struct resource *parent, *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + parent = &ioport_resource; + advk_pcie_set_ob_win(pcie, 1, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0, 0xF8000000, 0, + lower_32_bits(res->start), + OB_PCIE_IO); + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + continue; + } + break; + case IORESOURCE_MEM: + parent = &iomem_resource; + advk_pcie_set_ob_win(pcie, 0, + upper_32_bits(res->start), + lower_32_bits(res->start), + 0x0, 0xF8000000, 0, + lower_32_bits(res->start), + (2 << 20) | OB_PCIE_MEM); + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + break; + default: + continue; + } + + err = devm_request_resource(dev, parent, res); + if (err) + goto out_release_res; + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + goto out_release_res; + } + + return 0; + +out_release_res: + advk_pcie_release_of_pci_ranges(pcie); + return err; +} + +static int advk_pcie_probe(struct platform_device *pdev) +{ + struct advk_pcie *pcie; + struct resource *res; + struct pci_bus *bus, *child; + int ret, irq; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie), + GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie->base)) { + dev_err(&pdev->dev, "failed to map registers\n"); + return PTR_ERR(pcie->base); + } + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", + pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to register interrupt\n"); + return ret; + } + + ret = advk_pcie_parse_request_of_pci_ranges(pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to parse resources\n"); + return ret; + } + + advk_pcie_setup_hw(pcie); + advk_pcie_enable_axi_addr_gen(pcie, true); + + ret = advk_pcie_init_irq(pcie); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize irq\n"); + return ret; + } + + bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops, + pcie, &pcie->resources, &pcie->msi); + if (!bus) + return -ENOMEM; + + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + pci_bus_add_devices(bus); + + return 0; +} + +static const struct of_device_id advk_pcie_of_match_table[] = { + { .compatible = "marvell,armada-3700-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, advk_pcie_of_match_table); + +static struct platform_driver advk_pcie_driver = { + .driver = { + .name = "advk-pcie", + .of_match_table = advk_pcie_of_match_table, + /* Driver unloading/unbinding currently not supported */ + .suppress_bind_attrs = true, + }, + .probe = advk_pcie_probe, +}; +module_platform_driver(advk_pcie_driver); + +MODULE_AUTHOR("Hezi Shahmoon <hezi.shahmoon@marvell.com>"); +MODULE_DESCRIPTION("Aardvark PCIe driver"); +MODULE_LICENSE("GPL v2"); -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 9:46 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 9:46 UTC (permalink / raw) To: linux-arm-kernel Cc: Thomas Petazzoni, Bjorn Helgaas, linux-pci, Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, Hanna Hawa, Nadav Haklai, Gregory Clement, Marcin Wojtas, Sebastian Hesselbarth On Thursday, June 2, 2016 11:09:44 AM CEST Thomas Petazzoni wrote: > +static bool advk_pcie_link_up(struct advk_pcie *pcie) > +{ > + int timeout; > + u32 ltssm_state; > + u32 val; > + > + timeout = PCIE_LINKUP_TIMEOUT; > + do { > + val = advk_readl(pcie, CFG_REG); > + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; > + timeout--; > + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ > + } while (ltssm_state < LTSSM_L0 && timeout > 0); > + > + return (timeout > 0); > +} Maybe wait for some amount of time to have passed here (using time_before(jiffies, end)) rather than a number of retries, for robustness and determinism. > + /* Point PCIe unit MBUS decode windows to DRAM space. */ > + for (i = 0; i < 8; i++) > + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); Is this actually mbus based, or is the comment copied from some other Marvell driver? > +static int advk_pcie_setup_msi_irq(struct msi_controller *chip, > + struct pci_dev *pdev, > + struct msi_desc *desc) > +{ > + struct advk_pcie *pcie = pdev->bus->sysdata; > + struct msi_msg msg; > + int virq, hwirq; > + phys_addr_t msi_msg_phys; > + > + /* We support MSI, but not MSI-X */ > + if (desc->msi_attrib.is_msix) > + return -EINVAL; > + > + hwirq = advk_pcie_alloc_msi(pcie); > + if (hwirq < 0) > + return hwirq; > + > + virq = irq_create_mapping(pcie->msi_domain, hwirq); > + if (!virq) { > + advk_pcie_free_msi(pcie, hwirq); > + return -EINVAL; > + } What is the version of the GIC in the Armada 3700? If you have GICv3 or GICv2m, could you use that instead of the built-in MSI logic? We typically handle this using the msi-map or msi-parent properties pointing to either the gic or the PCI host, depending on which one you want to use, but either of them should work, and the GIC should be more efficient because you can distribute the interrupts of the PCI devices over all CPUs by workload, rather than having to multiplex all MSI through a single GIC interrupt. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-02 9:46 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-02 9:46 UTC (permalink / raw) To: linux-arm-kernel On Thursday, June 2, 2016 11:09:44 AM CEST Thomas Petazzoni wrote: > +static bool advk_pcie_link_up(struct advk_pcie *pcie) > +{ > + int timeout; > + u32 ltssm_state; > + u32 val; > + > + timeout = PCIE_LINKUP_TIMEOUT; > + do { > + val = advk_readl(pcie, CFG_REG); > + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; > + timeout--; > + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ > + } while (ltssm_state < LTSSM_L0 && timeout > 0); > + > + return (timeout > 0); > +} Maybe wait for some amount of time to have passed here (using time_before(jiffies, end)) rather than a number of retries, for robustness and determinism. > + /* Point PCIe unit MBUS decode windows to DRAM space. */ > + for (i = 0; i < 8; i++) > + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); Is this actually mbus based, or is the comment copied from some other Marvell driver? > +static int advk_pcie_setup_msi_irq(struct msi_controller *chip, > + struct pci_dev *pdev, > + struct msi_desc *desc) > +{ > + struct advk_pcie *pcie = pdev->bus->sysdata; > + struct msi_msg msg; > + int virq, hwirq; > + phys_addr_t msi_msg_phys; > + > + /* We support MSI, but not MSI-X */ > + if (desc->msi_attrib.is_msix) > + return -EINVAL; > + > + hwirq = advk_pcie_alloc_msi(pcie); > + if (hwirq < 0) > + return hwirq; > + > + virq = irq_create_mapping(pcie->msi_domain, hwirq); > + if (!virq) { > + advk_pcie_free_msi(pcie, hwirq); > + return -EINVAL; > + } What is the version of the GIC in the Armada 3700? If you have GICv3 or GICv2m, could you use that instead of the built-in MSI logic? We typically handle this using the msi-map or msi-parent properties pointing to either the gic or the PCI host, depending on which one you want to use, but either of them should work, and the GIC should be more efficient because you can distribute the interrupts of the PCI devices over all CPUs by workload, rather than having to multiplex all MSI through a single GIC interrupt. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-02 9:46 ` Arnd Bergmann @ 2016-06-09 9:19 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-09 9:19 UTC (permalink / raw) To: Arnd Bergmann Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, Marcin Wojtas, linux-arm-kernel, Sebastian Hesselbarth Hello, On Thu, 02 Jun 2016 11:46:04 +0200, Arnd Bergmann wrote: > > + timeout = PCIE_LINKUP_TIMEOUT; > > + do { > > + val = advk_readl(pcie, CFG_REG); > > + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; > > + timeout--; > > + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ > > + } while (ltssm_state < LTSSM_L0 && timeout > 0); > > + > > + return (timeout > 0); > > +} > > Maybe wait for some amount of time to have passed here (using > time_before(jiffies, end)) rather than a number of retries, > for robustness and determinism. Will. There was another similar loop in the driver which was doing a number of retries rather than a timeout-bounded loop, so I've fixed that one as well. > > + /* Point PCIe unit MBUS decode windows to DRAM space. */ > > + for (i = 0; i < 8; i++) > > + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); > > Is this actually mbus based, or is the comment copied from some > other Marvell driver? No, it's not MBUS based, the address decoding logic is very different from previous mvebu SoCs, I guess it's just a "familiar" name to say that it's configuring address decoding. I'll remove the comment. > What is the version of the GIC in the Armada 3700? If you have GICv3 > or GICv2m, could you use that instead of the built-in MSI logic? > > We typically handle this using the msi-map or msi-parent properties > pointing to either the gic or the PCI host, depending on which one > you want to use, but either of them should work, and the GIC should > be more efficient because you can distribute the interrupts of the > PCI devices over all CPUs by workload, rather than having to > multiplex all MSI through a single GIC interrupt. There is a GIC-500, but Marcin told me that attempts to use MSI with it have not been successful so far. There will be investigation on this topic in the future, but for the moment, we'd like to have the MSI functionality built into the PCIe driver supported. We can migrate later to GIC-500 powered MSIs once working. Thanks! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-09 9:19 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-09 9:19 UTC (permalink / raw) To: linux-arm-kernel Hello, On Thu, 02 Jun 2016 11:46:04 +0200, Arnd Bergmann wrote: > > + timeout = PCIE_LINKUP_TIMEOUT; > > + do { > > + val = advk_readl(pcie, CFG_REG); > > + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; > > + timeout--; > > + /* Use ltssm < LTSSM_L0 instead of ltssm != LTSSM_L0 */ > > + } while (ltssm_state < LTSSM_L0 && timeout > 0); > > + > > + return (timeout > 0); > > +} > > Maybe wait for some amount of time to have passed here (using > time_before(jiffies, end)) rather than a number of retries, > for robustness and determinism. Will. There was another similar loop in the driver which was doing a number of retries rather than a timeout-bounded loop, so I've fixed that one as well. > > + /* Point PCIe unit MBUS decode windows to DRAM space. */ > > + for (i = 0; i < 8; i++) > > + advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0); > > Is this actually mbus based, or is the comment copied from some > other Marvell driver? No, it's not MBUS based, the address decoding logic is very different from previous mvebu SoCs, I guess it's just a "familiar" name to say that it's configuring address decoding. I'll remove the comment. > What is the version of the GIC in the Armada 3700? If you have GICv3 > or GICv2m, could you use that instead of the built-in MSI logic? > > We typically handle this using the msi-map or msi-parent properties > pointing to either the gic or the PCI host, depending on which one > you want to use, but either of them should work, and the GIC should > be more efficient because you can distribute the interrupts of the > PCI devices over all CPUs by workload, rather than having to > multiplex all MSI through a single GIC interrupt. There is a GIC-500, but Marcin told me that attempts to use MSI with it have not been successful so far. There will be investigation on this topic in the future, but for the moment, we'd like to have the MSI functionality built into the PCIe driver supported. We can migrate later to GIC-500 powered MSIs once working. Thanks! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-09 9:19 ` Thomas Petazzoni @ 2016-06-09 12:19 ` Arnd Bergmann -1 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-09 12:19 UTC (permalink / raw) To: Thomas Petazzoni Cc: linux-arm-kernel, Bjorn Helgaas, linux-pci, Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, Hanna Hawa, Nadav Haklai, Gregory Clement, Marcin Wojtas, Sebastian Hesselbarth On Thursday, June 9, 2016 11:19:21 AM CEST Thomas Petazzoni wrote: > > What is the version of the GIC in the Armada 3700? If you have GICv3 > > or GICv2m, could you use that instead of the built-in MSI logic? > > > > We typically handle this using the msi-map or msi-parent properties > > pointing to either the gic or the PCI host, depending on which one > > you want to use, but either of them should work, and the GIC should > > be more efficient because you can distribute the interrupts of the > > PCI devices over all CPUs by workload, rather than having to > > multiplex all MSI through a single GIC interrupt. > > There is a GIC-500, but Marcin told me that attempts to use MSI with it > have not been successful so far. There will be investigation on this > topic in the future, but for the moment, we'd like to have the MSI > functionality built into the PCIe driver supported. We can migrate > later to GIC-500 powered MSIs once working. I think you should still add the msi-parent or msi-map properties then, just have them point at the PCI host node instead of the GIC-500, and evaluate that to get the built-in controller. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-09 12:19 ` Arnd Bergmann 0 siblings, 0 replies; 42+ messages in thread From: Arnd Bergmann @ 2016-06-09 12:19 UTC (permalink / raw) To: linux-arm-kernel On Thursday, June 9, 2016 11:19:21 AM CEST Thomas Petazzoni wrote: > > What is the version of the GIC in the Armada 3700? If you have GICv3 > > or GICv2m, could you use that instead of the built-in MSI logic? > > > > We typically handle this using the msi-map or msi-parent properties > > pointing to either the gic or the PCI host, depending on which one > > you want to use, but either of them should work, and the GIC should > > be more efficient because you can distribute the interrupts of the > > PCI devices over all CPUs by workload, rather than having to > > multiplex all MSI through a single GIC interrupt. > > There is a GIC-500, but Marcin told me that attempts to use MSI with it > have not been successful so far. There will be investigation on this > topic in the future, but for the moment, we'd like to have the MSI > functionality built into the PCIe driver supported. We can migrate > later to GIC-500 powered MSIs once working. I think you should still add the msi-parent or msi-map properties then, just have them point at the PCI host node instead of the GIC-500, and evaluate that to get the built-in controller. Arnd ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-09 12:19 ` Arnd Bergmann @ 2016-06-09 12:36 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-09 12:36 UTC (permalink / raw) To: Arnd Bergmann Cc: linux-arm-kernel, Bjorn Helgaas, linux-pci, Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, Hanna Hawa, Nadav Haklai, Gregory Clement, Marcin Wojtas, Sebastian Hesselbarth Hello, On Thu, 09 Jun 2016 14:19:07 +0200, Arnd Bergmann wrote: > > There is a GIC-500, but Marcin told me that attempts to use MSI with it > > have not been successful so far. There will be investigation on this > > topic in the future, but for the moment, we'd like to have the MSI > > functionality built into the PCIe driver supported. We can migrate > > later to GIC-500 powered MSIs once working. > > I think you should still add the msi-parent or msi-map properties then, > just have them point at the PCI host node instead of the GIC-500, > and evaluate that to get the built-in controller. Sure, I'll have a look at doing that. Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-09 12:36 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-09 12:36 UTC (permalink / raw) To: linux-arm-kernel Hello, On Thu, 09 Jun 2016 14:19:07 +0200, Arnd Bergmann wrote: > > There is a GIC-500, but Marcin told me that attempts to use MSI with it > > have not been successful so far. There will be investigation on this > > topic in the future, but for the moment, we'd like to have the MSI > > functionality built into the PCIe driver supported. We can migrate > > later to GIC-500 powered MSIs once working. > > I think you should still add the msi-parent or msi-map properties then, > just have them point at the PCI host node instead of the GIC-500, > and evaluate that to get the built-in controller. Sure, I'll have a look at doing that. Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-04 0:24 ` kbuild test robot -1 siblings, 0 replies; 42+ messages in thread From: kbuild test robot @ 2016-06-04 0:24 UTC (permalink / raw) To: Thomas Petazzoni Cc: kbuild-all, Bjorn Helgaas, linux-pci, linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas, Thomas Petazzoni [-- Attachment #1: Type: text/plain, Size: 4804 bytes --] Hi, [auto build test ERROR on pci/next] [also build test ERROR on v4.7-rc1 next-20160603] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Thomas-Petazzoni/PCIe-controller-driver-for-Marvell-Armada-3700/20160602-171515 base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next config: x86_64-lkp (attached as .config) compiler: gcc-4.9 (Debian 4.9.3-14) 4.9.3 reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All errors (new ones prefixed by >>): >> drivers/of/Kconfig:4:error: recursive dependency detected! For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/of/Kconfig:4: symbol OF is selected by X86_INTEL_CE For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:501: symbol X86_INTEL_CE depends on X86_IO_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:970: symbol X86_IO_APIC depends on X86_LOCAL_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:964: symbol X86_LOCAL_APIC depends on X86_UP_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:939: symbol X86_UP_APIC depends on PCI_MSI For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/Kconfig:11: symbol PCI_MSI is selected by PCI_AARDVARK For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/host/Kconfig:19: symbol PCI_AARDVARK depends on OF -- >> drivers/of/Kconfig:4:error: recursive dependency detected! For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/of/Kconfig:4: symbol OF is selected by X86_INTEL_CE For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:501: symbol X86_INTEL_CE depends on X86_IO_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:970: symbol X86_IO_APIC depends on X86_LOCAL_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:964: symbol X86_LOCAL_APIC depends on X86_UP_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:939: symbol X86_UP_APIC depends on PCI_MSI For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/Kconfig:11: symbol PCI_MSI is selected by PCI_AARDVARK For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/host/Kconfig:19: symbol PCI_AARDVARK depends on OF .config:429:warning: symbol value 'm' invalid for MICROCODE .config:2991:warning: symbol value 'm' invalid for FAULT_INJECTION vim +4 drivers/of/Kconfig 5ab5fc7e Grant Likely 2010-07-05 1 config DTC 5ab5fc7e Grant Likely 2010-07-05 2 bool 5ab5fc7e Grant Likely 2010-07-05 3 0166dc11 Rob Herring 2015-05-28 @4 menuconfig OF 0166dc11 Rob Herring 2015-05-28 5 bool "Device Tree and Open Firmware support" 0166dc11 Rob Herring 2015-05-28 6 help 0166dc11 Rob Herring 2015-05-28 7 This option enables the device tree infrastructure. 0166dc11 Rob Herring 2015-05-28 8 It is automatically selected by platforms that need it or can 0166dc11 Rob Herring 2015-05-28 9 be enabled manually for unittests, overlays or 0166dc11 Rob Herring 2015-05-28 10 compile-coverage. bcbefae2 Stephen Rothwell 2010-06-29 11 0166dc11 Rob Herring 2015-05-28 12 if OF :::::: The code at line 4 was first introduced by commit :::::: 0166dc11be911213e0b1b764488c671be4c48cf3 of: make CONFIG_OF user selectable :::::: TO: Rob Herring <robh@kernel.org> :::::: CC: Rob Herring <robh@kernel.org> --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/octet-stream, Size: 23344 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-04 0:24 ` kbuild test robot 0 siblings, 0 replies; 42+ messages in thread From: kbuild test robot @ 2016-06-04 0:24 UTC (permalink / raw) To: linux-arm-kernel Hi, [auto build test ERROR on pci/next] [also build test ERROR on v4.7-rc1 next-20160603] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Thomas-Petazzoni/PCIe-controller-driver-for-Marvell-Armada-3700/20160602-171515 base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next config: x86_64-lkp (attached as .config) compiler: gcc-4.9 (Debian 4.9.3-14) 4.9.3 reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All errors (new ones prefixed by >>): >> drivers/of/Kconfig:4:error: recursive dependency detected! For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/of/Kconfig:4: symbol OF is selected by X86_INTEL_CE For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:501: symbol X86_INTEL_CE depends on X86_IO_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:970: symbol X86_IO_APIC depends on X86_LOCAL_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:964: symbol X86_LOCAL_APIC depends on X86_UP_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:939: symbol X86_UP_APIC depends on PCI_MSI For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/Kconfig:11: symbol PCI_MSI is selected by PCI_AARDVARK For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/host/Kconfig:19: symbol PCI_AARDVARK depends on OF -- >> drivers/of/Kconfig:4:error: recursive dependency detected! For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/of/Kconfig:4: symbol OF is selected by X86_INTEL_CE For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:501: symbol X86_INTEL_CE depends on X86_IO_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:970: symbol X86_IO_APIC depends on X86_LOCAL_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:964: symbol X86_LOCAL_APIC depends on X86_UP_APIC For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" arch/x86/Kconfig:939: symbol X86_UP_APIC depends on PCI_MSI For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/Kconfig:11: symbol PCI_MSI is selected by PCI_AARDVARK For a resolution refer to Documentation/kbuild/kconfig-language.txt subsection "Kconfig recursive dependency limitations" drivers/pci/host/Kconfig:19: symbol PCI_AARDVARK depends on OF .config:429:warning: symbol value 'm' invalid for MICROCODE .config:2991:warning: symbol value 'm' invalid for FAULT_INJECTION vim +4 drivers/of/Kconfig 5ab5fc7e Grant Likely 2010-07-05 1 config DTC 5ab5fc7e Grant Likely 2010-07-05 2 bool 5ab5fc7e Grant Likely 2010-07-05 3 0166dc11 Rob Herring 2015-05-28 @4 menuconfig OF 0166dc11 Rob Herring 2015-05-28 5 bool "Device Tree and Open Firmware support" 0166dc11 Rob Herring 2015-05-28 6 help 0166dc11 Rob Herring 2015-05-28 7 This option enables the device tree infrastructure. 0166dc11 Rob Herring 2015-05-28 8 It is automatically selected by platforms that need it or can 0166dc11 Rob Herring 2015-05-28 9 be enabled manually for unittests, overlays or 0166dc11 Rob Herring 2015-05-28 10 compile-coverage. bcbefae2 Stephen Rothwell 2010-06-29 11 0166dc11 Rob Herring 2015-05-28 12 if OF :::::: The code at line 4 was first introduced by commit :::::: 0166dc11be911213e0b1b764488c671be4c48cf3 of: make CONFIG_OF user selectable :::::: TO: Rob Herring <robh@kernel.org> :::::: CC: Rob Herring <robh@kernel.org> --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation -------------- next part -------------- A non-text attachment was scrubbed... Name: .config.gz Type: application/octet-stream Size: 23344 bytes Desc: not available URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160604/1aa84bf0/attachment-0001.obj> ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-08 15:15 ` Marcin Wojtas -1 siblings, 0 replies; 42+ messages in thread From: Marcin Wojtas @ 2016-06-08 15:15 UTC (permalink / raw) To: Thomas Petazzoni Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, linux-arm-kernel, Sebastian Hesselbarth Hi Thomas, > +static const struct irq_domain_ops advk_pcie_msi_irq_ops = { > + .map = advk_pcie_msi_map, > +}; > + > +static void advk_pcie_irq_mask(struct irq_data *d) > +{ > + struct advk_pcie *pcie = d->domain->host_data; > + irq_hw_number_t hwirq = irqd_to_hwirq(d); > + u32 mask; > + > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + mask |= PCIE_ISR0_INTX_ASSERT(hwirq) | > + PCIE_ISR0_INTX_DEASSERT(hwirq); > + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); > +} > + > +static void advk_pcie_irq_unmask(struct irq_data *d) > +{ > + struct advk_pcie *pcie = d->domain->host_data; > + irq_hw_number_t hwirq = irqd_to_hwirq(d); > + u32 mask; > + > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + mask &= ~(PCIE_ISR0_INTX_ASSERT(hwirq) | > + PCIE_ISR0_INTX_DEASSERT(hwirq)); Have you checked my latest improvements of interrupt handling in this controller? As DEASSERT is polled in advk_pcie_handle_int() I don't think it should be able to trigger root complex interrupt at all. Hence I think it shouldn't be unmasked. > + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); > +} > + > +static int advk_pcie_irq_map(struct irq_domain *h, > + unsigned int virq, irq_hw_number_t hwirq) > +{ > + struct advk_pcie *pcie = h->host_data; > + > + advk_pcie_irq_mask(irq_get_irq_data(virq)); > + irq_set_status_flags(virq, IRQ_LEVEL); > + irq_set_chip_and_handler(virq, &pcie->irq_chip, > + handle_level_irq); > + irq_set_chip_data(virq, pcie); > + > + return 0; > +} > + > +static const struct irq_domain_ops advk_pcie_irq_domain_ops = { > + .map = advk_pcie_irq_map, > + .xlate = irq_domain_xlate_onecell, > +}; > + > +static int advk_pcie_init_irq(struct advk_pcie *pcie) > +{ > + struct device *dev = &pcie->pdev->dev; > + struct device_node *node = dev->of_node; > + struct device_node *pcie_intc_node; > + struct irq_chip *irq_chip, *msi_irq_chip; > + struct msi_controller *msi; > + phys_addr_t msi_msg_phys; > + > + pcie_intc_node = of_get_next_child(node, NULL); > + if (!pcie_intc_node) { > + dev_err(dev, "No PCIe Intc node found\n"); > + return PTR_ERR(pcie_intc_node); > + } > + > + irq_chip = &pcie->irq_chip; > + > + irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", > + dev_name(dev)); > + if (!irq_chip->name) > + return -ENOMEM; > + irq_chip->irq_mask = advk_pcie_irq_mask; > + irq_chip->irq_mask_ack = advk_pcie_irq_mask; > + irq_chip->irq_unmask = advk_pcie_irq_unmask; > + > + pcie->irq_domain = > + irq_domain_add_linear(pcie_intc_node, LEGACY_IRQ_NUM, > + &advk_pcie_irq_domain_ops, pcie); > + if (!pcie->irq_domain) { > + dev_err(dev, "Failed to get a INTx IRQ domain\n"); > + return PTR_ERR(pcie->irq_domain); > + } > + > + msi_irq_chip = &pcie->msi_irq_chip; > + > + msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", > + dev_name(dev)); > + if (!msi_irq_chip->name) { > + irq_domain_remove(pcie->irq_domain); > + return -ENOMEM; > + } > + > + msi_irq_chip->irq_enable = pci_msi_unmask_irq; > + msi_irq_chip->irq_disable = pci_msi_mask_irq; > + msi_irq_chip->irq_mask = pci_msi_mask_irq; > + msi_irq_chip->irq_unmask = pci_msi_unmask_irq; > + > + msi = &pcie->msi; Do we consciously resign from CONFIG_MSI dependency? I know it should always be enabled, but just making sure. > + > + msi->setup_irq = advk_pcie_setup_msi_irq; > + msi->teardown_irq = advk_pcie_teardown_msi_irq; > + msi->of_node = node; > + > + mutex_init(&pcie->msi_used_lock); > + > + msi_msg_phys = virt_to_phys(&pcie->msi_msg); > + > + advk_writel(pcie, lower_32_bits(msi_msg_phys), > + PCIE_MSI_ADDR_LOW_REG); > + advk_writel(pcie, upper_32_bits(msi_msg_phys), > + PCIE_MSI_ADDR_HIGH_REG); > + > + pcie->msi_domain = > + irq_domain_add_linear(NULL, MSI_IRQ_NUM, > + &advk_pcie_msi_irq_ops, pcie); > + if (!pcie->msi_domain) { > + irq_domain_remove(pcie->irq_domain); > + return -ENOMEM; > + } > + > + return 0; > +} > + > +static void advk_pcie_handle_msi(struct advk_pcie *pcie) > +{ > + u32 msi_val, msi_mask, msi_status, msi_idx; > + u16 msi_data; > + > + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); > + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); > + msi_status = msi_val & ~msi_mask; > + > + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { > + if (!(BIT(msi_idx) & msi_status)) > + continue; > + > + advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); > + msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; > + generic_handle_irq(msi_data); > + } > + > + advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, > + PCIE_ISR0_REG); > +} > + > +static void advk_pcie_handle_int(struct advk_pcie *pcie) > +{ > + u32 val, mask, status; > + > + val = advk_readl(pcie, PCIE_ISR0_REG); > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + status = val & ((~mask) & PCIE_ISR0_ALL_MASK); > + > + if (!status) { > + advk_writel(pcie, val, PCIE_ISR0_REG); > + return; > + } > + > + /* Process MSI interrupts */ > + if (status & PCIE_ISR0_MSI_INT_PENDING) > + advk_pcie_handle_msi(pcie); > + > + /* Process legacy interrupts */ > + do { > + int i; > + > + /* > + * As documented in the datasheet, we need to raise an > + * interrupt for each ASSERT(i) bit that is set, and > + * continue to deliver the interrupt until the > + * DEASSERT(i) bit gets set > + */ Below code will result in spurious interrupt flood (I can provide test details with SATA3.0 card). I know this part of specs very well, but expected EP operation flow should look like this: a. EP interrupt b. INTX_ASSERT signal c. root complex interrupt d. clear INTX_ASSERT bit e. execute EP handler f. INTX_DEASSERT signal g. clear INTX_DEASSERT bit h. exit interrupt Now between d. and f., in case of any shortest delay we will force executing EP handler numerous of times, which IMO is not desired. All of them for e.g. the SATA3.0 card are registered as spurious interrupts, pretty quickly resulting in "switching to irq poll" failure after 100k of such happenings. Actually I think that after executing generic_handle_irq(virq); we should leave root complex isr asap, the polling for DEASSERT in such case is not resulting in any benefits. In my code I ignored DEASSERT signal existence - it neither triggers interrupt nor we don't poll for it. Under big stress nothing wrong happens. We rely only on interrupts triggered by INTX_ASSERT signal, so we care only for its status. > + for (i = 0; i < LEGACY_IRQ_NUM; i++) { > + if (!(status & PCIE_ISR0_INTX_ASSERT(i))) > + continue; > + > + do { > + int virq; > + > + advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), > + PCIE_ISR0_REG); > + virq = irq_find_mapping(pcie->irq_domain, i); > + generic_handle_irq(virq); > + status = advk_readl(pcie, PCIE_ISR0_REG); > + } while (!(status & PCIE_ISR0_INTX_DEASSERT(i))); > + > + advk_writel(pcie, PCIE_ISR0_INTX_DEASSERT(i), > + PCIE_ISR0_REG); > + } > + > + status = advk_readl(pcie, PCIE_ISR0_REG); > + } while (status & PCIE_ISR0_INTX_MASK); How about simplifying code a bit and remove this do-while loop? IMO it's enough to do single iterating through INTA/B/C/D and exit - in such case we wouldn't spent too much time in single top half handler. I'm looking forward to your feedback. Best regards, Marcin _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-08 15:15 ` Marcin Wojtas 0 siblings, 0 replies; 42+ messages in thread From: Marcin Wojtas @ 2016-06-08 15:15 UTC (permalink / raw) To: linux-arm-kernel Hi Thomas, > +static const struct irq_domain_ops advk_pcie_msi_irq_ops = { > + .map = advk_pcie_msi_map, > +}; > + > +static void advk_pcie_irq_mask(struct irq_data *d) > +{ > + struct advk_pcie *pcie = d->domain->host_data; > + irq_hw_number_t hwirq = irqd_to_hwirq(d); > + u32 mask; > + > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + mask |= PCIE_ISR0_INTX_ASSERT(hwirq) | > + PCIE_ISR0_INTX_DEASSERT(hwirq); > + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); > +} > + > +static void advk_pcie_irq_unmask(struct irq_data *d) > +{ > + struct advk_pcie *pcie = d->domain->host_data; > + irq_hw_number_t hwirq = irqd_to_hwirq(d); > + u32 mask; > + > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + mask &= ~(PCIE_ISR0_INTX_ASSERT(hwirq) | > + PCIE_ISR0_INTX_DEASSERT(hwirq)); Have you checked my latest improvements of interrupt handling in this controller? As DEASSERT is polled in advk_pcie_handle_int() I don't think it should be able to trigger root complex interrupt at all. Hence I think it shouldn't be unmasked. > + advk_writel(pcie, mask, PCIE_ISR0_MASK_REG); > +} > + > +static int advk_pcie_irq_map(struct irq_domain *h, > + unsigned int virq, irq_hw_number_t hwirq) > +{ > + struct advk_pcie *pcie = h->host_data; > + > + advk_pcie_irq_mask(irq_get_irq_data(virq)); > + irq_set_status_flags(virq, IRQ_LEVEL); > + irq_set_chip_and_handler(virq, &pcie->irq_chip, > + handle_level_irq); > + irq_set_chip_data(virq, pcie); > + > + return 0; > +} > + > +static const struct irq_domain_ops advk_pcie_irq_domain_ops = { > + .map = advk_pcie_irq_map, > + .xlate = irq_domain_xlate_onecell, > +}; > + > +static int advk_pcie_init_irq(struct advk_pcie *pcie) > +{ > + struct device *dev = &pcie->pdev->dev; > + struct device_node *node = dev->of_node; > + struct device_node *pcie_intc_node; > + struct irq_chip *irq_chip, *msi_irq_chip; > + struct msi_controller *msi; > + phys_addr_t msi_msg_phys; > + > + pcie_intc_node = of_get_next_child(node, NULL); > + if (!pcie_intc_node) { > + dev_err(dev, "No PCIe Intc node found\n"); > + return PTR_ERR(pcie_intc_node); > + } > + > + irq_chip = &pcie->irq_chip; > + > + irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", > + dev_name(dev)); > + if (!irq_chip->name) > + return -ENOMEM; > + irq_chip->irq_mask = advk_pcie_irq_mask; > + irq_chip->irq_mask_ack = advk_pcie_irq_mask; > + irq_chip->irq_unmask = advk_pcie_irq_unmask; > + > + pcie->irq_domain = > + irq_domain_add_linear(pcie_intc_node, LEGACY_IRQ_NUM, > + &advk_pcie_irq_domain_ops, pcie); > + if (!pcie->irq_domain) { > + dev_err(dev, "Failed to get a INTx IRQ domain\n"); > + return PTR_ERR(pcie->irq_domain); > + } > + > + msi_irq_chip = &pcie->msi_irq_chip; > + > + msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi", > + dev_name(dev)); > + if (!msi_irq_chip->name) { > + irq_domain_remove(pcie->irq_domain); > + return -ENOMEM; > + } > + > + msi_irq_chip->irq_enable = pci_msi_unmask_irq; > + msi_irq_chip->irq_disable = pci_msi_mask_irq; > + msi_irq_chip->irq_mask = pci_msi_mask_irq; > + msi_irq_chip->irq_unmask = pci_msi_unmask_irq; > + > + msi = &pcie->msi; Do we consciously resign from CONFIG_MSI dependency? I know it should always be enabled, but just making sure. > + > + msi->setup_irq = advk_pcie_setup_msi_irq; > + msi->teardown_irq = advk_pcie_teardown_msi_irq; > + msi->of_node = node; > + > + mutex_init(&pcie->msi_used_lock); > + > + msi_msg_phys = virt_to_phys(&pcie->msi_msg); > + > + advk_writel(pcie, lower_32_bits(msi_msg_phys), > + PCIE_MSI_ADDR_LOW_REG); > + advk_writel(pcie, upper_32_bits(msi_msg_phys), > + PCIE_MSI_ADDR_HIGH_REG); > + > + pcie->msi_domain = > + irq_domain_add_linear(NULL, MSI_IRQ_NUM, > + &advk_pcie_msi_irq_ops, pcie); > + if (!pcie->msi_domain) { > + irq_domain_remove(pcie->irq_domain); > + return -ENOMEM; > + } > + > + return 0; > +} > + > +static void advk_pcie_handle_msi(struct advk_pcie *pcie) > +{ > + u32 msi_val, msi_mask, msi_status, msi_idx; > + u16 msi_data; > + > + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); > + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); > + msi_status = msi_val & ~msi_mask; > + > + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { > + if (!(BIT(msi_idx) & msi_status)) > + continue; > + > + advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); > + msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF; > + generic_handle_irq(msi_data); > + } > + > + advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, > + PCIE_ISR0_REG); > +} > + > +static void advk_pcie_handle_int(struct advk_pcie *pcie) > +{ > + u32 val, mask, status; > + > + val = advk_readl(pcie, PCIE_ISR0_REG); > + mask = advk_readl(pcie, PCIE_ISR0_MASK_REG); > + status = val & ((~mask) & PCIE_ISR0_ALL_MASK); > + > + if (!status) { > + advk_writel(pcie, val, PCIE_ISR0_REG); > + return; > + } > + > + /* Process MSI interrupts */ > + if (status & PCIE_ISR0_MSI_INT_PENDING) > + advk_pcie_handle_msi(pcie); > + > + /* Process legacy interrupts */ > + do { > + int i; > + > + /* > + * As documented in the datasheet, we need to raise an > + * interrupt for each ASSERT(i) bit that is set, and > + * continue to deliver the interrupt until the > + * DEASSERT(i) bit gets set > + */ Below code will result in spurious interrupt flood (I can provide test details with SATA3.0 card). I know this part of specs very well, but expected EP operation flow should look like this: a. EP interrupt b. INTX_ASSERT signal c. root complex interrupt d. clear INTX_ASSERT bit e. execute EP handler f. INTX_DEASSERT signal g. clear INTX_DEASSERT bit h. exit interrupt Now between d. and f., in case of any shortest delay we will force executing EP handler numerous of times, which IMO is not desired. All of them for e.g. the SATA3.0 card are registered as spurious interrupts, pretty quickly resulting in "switching to irq poll" failure after 100k of such happenings. Actually I think that after executing generic_handle_irq(virq); we should leave root complex isr asap, the polling for DEASSERT in such case is not resulting in any benefits. In my code I ignored DEASSERT signal existence - it neither triggers interrupt nor we don't poll for it. Under big stress nothing wrong happens. We rely only on interrupts triggered by INTX_ASSERT signal, so we care only for its status. > + for (i = 0; i < LEGACY_IRQ_NUM; i++) { > + if (!(status & PCIE_ISR0_INTX_ASSERT(i))) > + continue; > + > + do { > + int virq; > + > + advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), > + PCIE_ISR0_REG); > + virq = irq_find_mapping(pcie->irq_domain, i); > + generic_handle_irq(virq); > + status = advk_readl(pcie, PCIE_ISR0_REG); > + } while (!(status & PCIE_ISR0_INTX_DEASSERT(i))); > + > + advk_writel(pcie, PCIE_ISR0_INTX_DEASSERT(i), > + PCIE_ISR0_REG); > + } > + > + status = advk_readl(pcie, PCIE_ISR0_REG); > + } while (status & PCIE_ISR0_INTX_MASK); How about simplifying code a bit and remove this do-while loop? IMO it's enough to do single iterating through INTA/B/C/D and exit - in such case we wouldn't spent too much time in single top half handler. I'm looking forward to your feedback. Best regards, Marcin ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 2016-06-08 15:15 ` Marcin Wojtas @ 2016-06-08 15:47 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 15:47 UTC (permalink / raw) To: Marcin Wojtas Cc: Lior Amsalem, Andrew Lunn, Yehuda Yitschak, Jason Cooper, linux-pci, Hanna Hawa, Nadav Haklai, Gregory Clement, Bjorn Helgaas, linux-arm-kernel, Sebastian Hesselbarth Hello, Thanks for your feedback! On Wed, 8 Jun 2016 17:15:08 +0200, Marcin Wojtas wrote: > Have you checked my latest improvements of interrupt handling in this > controller? As DEASSERT is polled in advk_pcie_handle_int() I don't > think it should be able to trigger root complex interrupt at all. > Hence I think it shouldn't be unmasked. Correct. Makes sense. Though you are suggesting below to no longer poll the DEASSERT signal in the interrupt handler, but you're instead saying to ignore it entirely. > > + msi = &pcie->msi; > > Do we consciously resign from CONFIG_MSI dependency? I know it should > always be enabled, but just making sure. Yes, I discussed this with Arnd, and we decided to simply make MSI support a hard dependency. You can always disable it by passing pci=nomsi on the kernel command line (tested to be working). > Below code will result in spurious interrupt flood (I can provide test > details with SATA3.0 card). I know this part of specs very well, but > expected EP operation flow should look like this: > a. EP interrupt > b. INTX_ASSERT signal > c. root complex interrupt > d. clear INTX_ASSERT bit > e. execute EP handler > f. INTX_DEASSERT signal > g. clear INTX_DEASSERT bit > h. exit interrupt > > Now between d. and f., in case of any shortest delay we will force > executing EP handler numerous of times, which IMO is not desired. All > of them for e.g. the SATA3.0 card are registered as spurious > interrupts, pretty quickly resulting in "switching to irq poll" > failure after 100k of such happenings. Actually I think that after > executing generic_handle_irq(virq); we should leave root complex isr > asap, the polling for DEASSERT in such case is not resulting in any > benefits. In my code I ignored DEASSERT signal existence - it neither > triggers interrupt nor we don't poll for it. Under big stress nothing > wrong happens. We rely only on interrupts triggered by INTX_ASSERT > signal, so we care only for its status. Thanks for the detailed explanation. So in practice, the code should be just: for (i = 0; i < LEGACY_IRQ_NUM; i++) { int virq; if (!(status & PCIE_ISR0_INTX_ASSERT(i))) continue; advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), PCIE_ISR0_REG); virq = irq_find_mapping(pcie->irq_domain, i); generic_handle_irq(virq); } and that's it. Is that what you suggest? Thanks! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 @ 2016-06-08 15:47 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-08 15:47 UTC (permalink / raw) To: linux-arm-kernel Hello, Thanks for your feedback! On Wed, 8 Jun 2016 17:15:08 +0200, Marcin Wojtas wrote: > Have you checked my latest improvements of interrupt handling in this > controller? As DEASSERT is polled in advk_pcie_handle_int() I don't > think it should be able to trigger root complex interrupt at all. > Hence I think it shouldn't be unmasked. Correct. Makes sense. Though you are suggesting below to no longer poll the DEASSERT signal in the interrupt handler, but you're instead saying to ignore it entirely. > > + msi = &pcie->msi; > > Do we consciously resign from CONFIG_MSI dependency? I know it should > always be enabled, but just making sure. Yes, I discussed this with Arnd, and we decided to simply make MSI support a hard dependency. You can always disable it by passing pci=nomsi on the kernel command line (tested to be working). > Below code will result in spurious interrupt flood (I can provide test > details with SATA3.0 card). I know this part of specs very well, but > expected EP operation flow should look like this: > a. EP interrupt > b. INTX_ASSERT signal > c. root complex interrupt > d. clear INTX_ASSERT bit > e. execute EP handler > f. INTX_DEASSERT signal > g. clear INTX_DEASSERT bit > h. exit interrupt > > Now between d. and f., in case of any shortest delay we will force > executing EP handler numerous of times, which IMO is not desired. All > of them for e.g. the SATA3.0 card are registered as spurious > interrupts, pretty quickly resulting in "switching to irq poll" > failure after 100k of such happenings. Actually I think that after > executing generic_handle_irq(virq); we should leave root complex isr > asap, the polling for DEASSERT in such case is not resulting in any > benefits. In my code I ignored DEASSERT signal existence - it neither > triggers interrupt nor we don't poll for it. Under big stress nothing > wrong happens. We rely only on interrupts triggered by INTX_ASSERT > signal, so we care only for its status. Thanks for the detailed explanation. So in practice, the code should be just: for (i = 0; i < LEGACY_IRQ_NUM; i++) { int virq; if (!(status & PCIE_ISR0_INTX_ASSERT(i))) continue; advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i), PCIE_ISR0_REG); virq = irq_find_mapping(pcie->irq_domain, i); generic_handle_irq(virq); } and that's it. Is that what you suggest? Thanks! Thomas -- Thomas Petazzoni, CTO, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/3] arm64: dts: marvell: PCIe support for Armada 3700 2016-06-02 9:09 ` Thomas Petazzoni @ 2016-06-02 9:09 ` Thomas Petazzoni -1 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: Bjorn Helgaas, linux-pci Cc: linux-arm-kernel, Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Nadav Haklai, Lior Amsalem, Hanna Hawa, Yehuda Yitschak, Marcin Wojtas, Thomas Petazzoni This commit adds the SoC-level description of the PCIe controller found on the Marvell Armada 3700, and also enables this PCIe controller on the development board for this SoC. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- arch/arm64/boot/dts/marvell/armada-3720-db.dts | 5 +++++ arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts index 86110a6..1372e9a6 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts @@ -76,3 +76,8 @@ &usb3 { status = "okay"; }; + +/* CON17 (PCIe) / CON12 (mini-PCIe) */ +&pcie0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index 9e2efb8..01d3b20 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -141,5 +141,28 @@ <0x1d40000 0x40000>; /* GICR */ }; }; + + pcie0: pcie@d0070000 { + compatible = "marvell,armada-3700-pcie"; + device_type = "pci"; + status = "disabled"; + reg = <0 0xd0070000 0 0x20000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; + }; }; }; -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 3/3] arm64: dts: marvell: PCIe support for Armada 3700 @ 2016-06-02 9:09 ` Thomas Petazzoni 0 siblings, 0 replies; 42+ messages in thread From: Thomas Petazzoni @ 2016-06-02 9:09 UTC (permalink / raw) To: linux-arm-kernel This commit adds the SoC-level description of the PCIe controller found on the Marvell Armada 3700, and also enables this PCIe controller on the development board for this SoC. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- arch/arm64/boot/dts/marvell/armada-3720-db.dts | 5 +++++ arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts index 86110a6..1372e9a6 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts @@ -76,3 +76,8 @@ &usb3 { status = "okay"; }; + +/* CON17 (PCIe) / CON12 (mini-PCIe) */ +&pcie0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index 9e2efb8..01d3b20 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -141,5 +141,28 @@ <0x1d40000 0x40000>; /* GICR */ }; }; + + pcie0: pcie at d0070000 { + compatible = "marvell,armada-3700-pcie"; + device_type = "pci"; + status = "disabled"; + reg = <0 0xd0070000 0 0x20000>; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0xe8000000 0 0xe8000000 0 0x1000000 /* Port 0 MEM */ + 0x81000000 0 0xe9000000 0 0xe9000000 0 0x10000>; /* Port 0 IO*/ + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + }; + }; }; }; -- 2.7.4 ^ permalink raw reply related [flat|nested] 42+ messages in thread
end of thread, other threads:[~2016-06-10 15:47 UTC | newest] Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-06-02 9:09 [PATCH 0/3] PCIe controller driver for Marvell Armada 3700 Thomas Petazzoni 2016-06-02 9:09 ` Thomas Petazzoni 2016-06-02 9:09 ` [PATCH 1/3] dt-bindings: add DT binding for the Aardvark PCIe controller Thomas Petazzoni 2016-06-02 9:09 ` Thomas Petazzoni 2016-06-02 9:35 ` Arnd Bergmann 2016-06-02 9:35 ` Arnd Bergmann 2016-06-08 14:27 ` Thomas Petazzoni 2016-06-08 14:27 ` Thomas Petazzoni 2016-06-08 15:34 ` Arnd Bergmann 2016-06-08 15:34 ` Arnd Bergmann 2016-06-02 12:24 ` Andrew Lunn 2016-06-02 12:24 ` Andrew Lunn 2016-06-02 12:34 ` Arnd Bergmann 2016-06-02 12:34 ` Arnd Bergmann 2016-06-02 12:45 ` Thomas Petazzoni 2016-06-02 12:45 ` Thomas Petazzoni 2016-06-02 13:53 ` Arnd Bergmann 2016-06-02 13:53 ` Arnd Bergmann 2016-06-08 14:28 ` Thomas Petazzoni 2016-06-08 14:28 ` Thomas Petazzoni 2016-06-10 15:44 ` Bjorn Helgaas 2016-06-10 15:44 ` Bjorn Helgaas 2016-06-10 15:47 ` Thomas Petazzoni 2016-06-10 15:47 ` Thomas Petazzoni 2016-06-02 9:09 ` [PATCH 2/3] PCI: host: new PCI host controller driver for Marvell Armada 3700 Thomas Petazzoni 2016-06-02 9:09 ` Thomas Petazzoni 2016-06-02 9:46 ` Arnd Bergmann 2016-06-02 9:46 ` Arnd Bergmann 2016-06-09 9:19 ` Thomas Petazzoni 2016-06-09 9:19 ` Thomas Petazzoni 2016-06-09 12:19 ` Arnd Bergmann 2016-06-09 12:19 ` Arnd Bergmann 2016-06-09 12:36 ` Thomas Petazzoni 2016-06-09 12:36 ` Thomas Petazzoni 2016-06-04 0:24 ` kbuild test robot 2016-06-04 0:24 ` kbuild test robot 2016-06-08 15:15 ` Marcin Wojtas 2016-06-08 15:15 ` Marcin Wojtas 2016-06-08 15:47 ` Thomas Petazzoni 2016-06-08 15:47 ` Thomas Petazzoni 2016-06-02 9:09 ` [PATCH 3/3] arm64: dts: marvell: PCIe support for " Thomas Petazzoni 2016-06-02 9:09 ` Thomas Petazzoni
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.