Skip to contentSkip to conversation

PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

PATCH Superseded Duc Dang 9 participants 106 messages Jan 6, 2015 – Jun 5, 2015 Page 1 of 2

Subsystems (4)
lore
Share & tools
lore

Duc Dang

author
#0 lore raw

cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  34 ++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  21 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 370 +++++++++++++++++++++
 6 files changed, 438 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#1 lore raw

PATCH 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 370 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +4 -0
index c4b6568..650fd1d 100644
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
drivers/pci/host/Makefile +1 -0
index 44c2699..c261cf7 100644
@@ -11,4 +11,5 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
drivers/pci/host/pci-xgene-msi.c new file +370 -0

370 lines not on this page · raw message

Signature · 1 line
1.9.1
author
#102 lore raw

PATCH 2/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +21 -0
index f1ad9c2..70ba721 100644
@@ -469,6 +469,27 @@
 			clocks = <&pcie4clk 0>;
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		serial0: serial at 1c020000 {
 			status = "disabled";
 			device_type = "serial";
Signature · 1 line
1.9.1
author
#103 lore raw

PATCH 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.

In reply to Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +34 -0
new file mode 100644
index 0000000..386782c
@@ -0,0 +1,34 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
+	to identify the core.
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Examples:
+
+SoC DTSI:
+
+	msi at 79000000 {
+	     compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+	     reg = <0x00 0x79000000 0x0 0x900000>;
+	     interrupts = <  0x0 0x10 0x4
+		     0x0 0x11 0x4
+		     0x0 0x12 0x4
+		     0x0 0x13 0x4
+		     0x0 0x14 0x4
+		     0x0 0x15 0x4
+		     0x0 0x16 0x4
+		     0x0 0x17 0x4
+		     0x0 0x18 0x4
+		     0x0 0x19 0x4
+		     0x0 0x1a 0x4
+		     0x0 0x1b 0x4
+		     0x0 0x1c 0x4
+		     0x0 0x1d 0x4
+		     0x0 0x1e 0x4
+		     0x0 0x1f 0x4>;
+	};
Signature · 1 line
1.9.1
author
#105 lore raw

PATCH 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddb9ac8..283f09a 100644
@@ -7303,6 +7303,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
#104 lore raw

PATCH 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.

In reply to Duc Dang
Quoted 27 lines · On Tuesday 06 January 2015 08:15:43 Duc Dang wrote:
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +24 -0
@@ -0,0 +1,34 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene-pcie-msi" or "apm,xgene-storm-pcie-msi"
+       to identify the core.
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Examples:
+
+SoC DTSI:
+
+       msi at 79000000 {
+            compatible = "apm,xgene-storm-pcie-msi", "apm,xgene-pcie-msi";
+            reg = <0x00 0x79000000 0x0 0x900000>;
+            interrupts = <  0x0 0x10 0x4
+                    0x0 0x11 0x4
+                    0x0 0x12 0x4
+                    0x0 0x13 0x4
+                    0x0 0x14 0x4
+                    0x0 0x15 0x4
+                    0x0 0x16 0x4

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 34

Quoted 10 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +10 -0
+                    0x0 0x17 0x4
+                    0x0 0x18 0x4
+                    0x0 0x19 0x4
+                    0x0 0x1a 0x4
+                    0x0 0x1b 0x4
+                    0x0 0x1c 0x4
+                    0x0 0x1d 0x4
+                    0x0 0x1e 0x4
+                    0x0 0x1f 0x4>;
+       };

I think the example should contain the pci controller with the respective msi-parent property.

Arnd

author
#3 lore raw

PATCH 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Arnd Bergmann
Quoted 10 lines · Arnd Bergmann
Quoted 6 lines · On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I might be a little behind the latest development, but why is this not a struct msi_controller?

X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared among 5 PCIe ports.
So in this driver for this MSI block, we implement X-Gene v1 sepcific arch_teardown_msi_irqs,
arch_setup_msi_irqs and not using msi_controller struct.

Please let me know if this approach needs to be changed to follow other implementations of MSI
drivers in latest kernel.

Quoted 2 lines

Arnd

Regards,
Duc Dang.

#4 lore raw

PATCH 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 9 lines · On Monday 12 January 2015 10:53:14 Duc Dang wrote:
Quoted 8 lines · Arnd Bergmann
Quoted 6 lines · On Tuesday 06 January 2015 08:15:41 Duc Dang wrote:

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

Quoted 8 lines
Quoted 2 lines

I might be a little behind the latest development, but why is this not a struct msi_controller?

X-Gene V1 have a separate MSI block to handle MSI/MSIX and is shared among 5 PCIe ports.
So in this driver for this MSI block, we implement X-Gene v1 sepcific arch_teardown_msi_irqs,
arch_setup_msi_irqs and not using msi_controller struct.

I see.

Quoted 3 lines

Please let me know if this approach needs to be changed to follow other implementations of MSI
drivers in latest kernel.

Yes, your approach does not work on distro kernels which will always enable multiple targets. You cannot override generic weak functions from a device driver.

Arnd

author
#5 lore raw

v2 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#6 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  25 +++
 4 files changed, 423 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +4 -0
index 7b892a9..6c0f98c 100644
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +393 -0

393 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +25 -0
index ee082c0..63d58e6 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = xgene_pcie_msi_enable(bus);
+		if (ret) {
+			dev_err(port->dev, "failed to enable MSI\n");
+			return ret;
+		}
+	}
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#95 lore raw

PATCH v2 2/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node.

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..0fe05dc 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent= <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#96 lore raw

PATCH v2 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node.

In reply to Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +61 -0
new file mode 100644
index 0000000..fdd55d6
@@ -0,0 +1,61 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#97 lore raw

PATCH v2 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
author
#98 lore raw

v2 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

In reply to Duc Dang
Quoted 23 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Quoted 3 lines

--
1.9.1

Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1 MSI support?

Thanks,
Duc Dang.

#7 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 5 lines · Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks,

	M.
Quoted 482 lines
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  25 +++
 4 files changed, 423 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +4 -0
index 7b892a9..6c0f98c 100644
@@ -84,11 +84,15 @@ config PCIE_XILINX
 	  Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
 	  Host Bridge driver.
 
+config PCI_XGENE_MSI
+	bool
+
 config PCI_XGENE
 	bool "X-Gene PCIe controller"
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +393 -0
new file mode 100644
index 0000000..e1cab39
@@ -0,0 +1,393 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *	   Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0		0x000000
+#define MSI_INT0		0x800000
+
+struct xgene_msi_settings {
+	u32	index_per_group;
+	u32	irqs_per_index;
+	u32	nr_msi_vec;
+	u32	nr_hw_irqs;
+};
+
+struct xgene_msi {
+	struct irq_domain		*irqhost;
+	struct msi_controller		msi_chip;
+	struct xgene_msi_settings	*settings;
+	u32				msi_addr_lo;
+	u32				msi_addr_hi;
+	void __iomem			*msi_regs;
+	unsigned long			*bitmap;
+	struct mutex			bitmap_lock;
+	int				*msi_virqs;
+};
+
+static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
+{
+	return container_of(msi_chip, struct xgene_msi, msi_chip);
+}
+
+struct xgene_msi_settings storm_msi_settings = {
+	.index_per_group	= 8,
+	.irqs_per_index		= 21,
+	.nr_msi_vec		= 2688,
+	.nr_hw_irqs		= 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+	xgene_msi->settings = &storm_msi_settings;
+	return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+	.name		= "X-Gene-1 MSI",
+	.irq_enable	= pci_msi_unmask_irq,
+	.irq_disable	= pci_msi_mask_irq,
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+			      irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+	.map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	int msi;
+
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+	if (msi < msi_irq_count)
+		set_bit(msi, xgene_msi->bitmap);
+	else
+		msi = -ENOSPC;
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+
+	return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+	mutex_lock(&xgene_msi->bitmap_lock);
+
+	if (!test_bit(irq, xgene_msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", irq);
+	else
+		clear_bit(irq, xgene_msi->bitmap);
+
+	mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+	return 0;
+}
+
+static void xgene_msi_teardown_irq(struct msi_controller *chip,
+						unsigned int irq)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+
+	irq_set_msi_desc(irq, NULL);
+	xgene_msi_free(xgene_msi, virq_to_hw(irq));
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+				  struct msi_msg *msg,
+				  struct xgene_msi *xgene_msi)
+{
+	u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+	u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+	u32 group = hwirq % nr_hw_irqs;
+
+	msg->address_hi = xgene_msi->msi_addr_hi;
+	msg->address_lo = xgene_msi->msi_addr_lo +
+			  (((8 * group) + reg_set) << 16);
+	msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_setup_irq(struct msi_controller *chip,
+				struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+	struct msi_msg msg;
+	unsigned long virq, gic_irq;
+	int hwirq;
+
+	hwirq = xgene_msi_alloc(xgene_msi);
+	if (hwirq < 0) {
+		dev_err(&pdev->dev, "failed to allocate MSI\n");
+		return -ENOSPC;
+	}
+
+	virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+	if (virq == 0) {
+		dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+		return -ENOSPC;
+	}
+
+	gic_irq = xgene_msi->msi_virqs[hwirq %
+			xgene_msi->settings->nr_hw_irqs];
+	pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+		 hwirq, gic_irq, virq);
+	irq_set_msi_desc(virq, desc);
+	xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+	irq_set_handler_data(virq, (void *)gic_irq);
+	write_msi_msg(virq, &msg);
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	irqs_per_index = xgene_msi->settings->irqs_per_index;
+	index_per_group = xgene_msi->settings->index_per_group;
+
+	grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+	while (grp_select) {
+		msir_index = ffs(grp_select) - 1;
+		msir_reg = (msi_grp << 19) + (msir_index << 16);
+		msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+		while (msir_val) {
+			intr_index = ffs(msir_val) - 1;
+			hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+				 nr_hw_irqs) + msi_grp;
+			virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+			if (virq != 0)
+				generic_handle_irq(virq);
+			msir_val &= ~(1 << intr_index);
+			processed++;
+		}
+		grp_select &= ~(1 << msir_index);
+	}
+
+	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+	int virq, i;
+	struct xgene_msi *msi = platform_get_drvdata(pdev);
+	u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+	for (i = 0; i < nr_hw_irqs; i++) {
+		virq = msi->msi_virqs[i];
+		if (virq != 0)
+			free_irq(virq, msi);
+	}
+
+	kfree(msi->bitmap);
+	msi->bitmap = NULL;
+
+	return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+				 struct platform_device *pdev,
+				 int irq_index)
+{
+	int virt_msir;
+	cpumask_var_t mask;
+	int err;
+
+	virt_msir = platform_get_irq(pdev, irq_index);
+	if (virt_msir < 0) {
+		dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+			irq_index);
+		return -EINVAL;
+	}
+
+	err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+	if (err) {
+		dev_err(&pdev->dev, "request irq failed\n");
+		return err;
+	}
+
+	if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+		cpumask_setall(mask);
+		irq_set_affinity(virt_msir, mask);
+		free_cpumask_var(mask);
+	}
+
+	msi->msi_virqs[irq_index] = virt_msir;
+
+	return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+	{.compatible = "apm,xgene1-msi",
+	 .data = xgene_msi_init_storm_settings},
+	{},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int rc, irq_index;
+	struct device_node *np;
+	const struct of_device_id *matched_np;
+	struct xgene_msi *xgene_msi;
+	xgene_msi_initcall_t init_fn;
+	u32 nr_hw_irqs, nr_msi_vecs;
+
+	np = of_find_matching_node_and_match(NULL,
+			     xgene_msi_match_table, &matched_np);
+	if (!np)
+		return -ENODEV;
+
+	xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+	if (!xgene_msi) {
+		dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+		return -ENOMEM;
+	}
+
+	init_fn = (xgene_msi_initcall_t) matched_np->data;
+	rc = init_fn(xgene_msi);
+	if (rc)
+		return rc;
+
+	platform_set_drvdata(pdev, xgene_msi);
+
+	nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+	xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+			     nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+	if (!xgene_msi->irqhost) {
+		dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xgene_msi->msi_regs)) {
+		dev_err(&pdev->dev, "no reg space\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+	xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+	rc = xgene_msi_init_allocator(xgene_msi);
+	if (rc) {
+		dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+		goto error;
+	}
+
+	nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+	for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+		rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+		if (rc)
+			goto error;
+	}
+
+	xgene_msi->msi_chip.dev = &pdev->dev;
+	xgene_msi->msi_chip.of_node = np;
+	xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
+	xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
+
+	rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+	return 0;
+error:
+	xgene_msi_remove(pdev);
+	return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+	.driver = {
+		.name = "xgene-msi",
+		.owner = THIS_MODULE,
+		.of_match_table = xgene_msi_match_table,
+	},
+	.probe = xgene_msi_probe,
+	.remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+	return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <[email protected]>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
drivers/pci/host/pci-xgene.c +25 -0
index ee082c0..63d58e6 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		ret = xgene_pcie_msi_enable(bus);
+		if (ret) {
+			dev_err(port->dev, "failed to enable MSI\n");
+			return ret;
+		}
+	}
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
Jazz is not dead. It just smells funny...
author
#8 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 12 lines · Marc Zyngier
Quoted 5 lines · Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

Quoted 8 lines

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer to what you did for Tegra MSI, but I don't see your latest changes in 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Regards,
Duc Dang.

Quoted 491 lines

Thanks,

        M.
Quoted 472 lines
---
 drivers/pci/host/Kconfig         |   4 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 393 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  25 +++
 4 files changed, 423 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +4 -0
index 7b892a9..6c0f98c 100644
@@ -84,11 +84,15 @@ config PCIE_XILINX
        Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
        Host Bridge driver.
 
+config PCI_XGENE_MSI
+     bool
+
 config PCI_XGENE
      bool "X-Gene PCIe controller"
      depends on ARCH_XGENE
      depends on OF
      select PCIEPORTBUS
+     select PCI_XGENE_MSI if PCI_MSI
      help
        Say Y here if you want internal PCI support on APM X-Gene SoC.
        There are 5 internal PCIe ports available. Each port is GEN3 capable
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +393 -0
new file mode 100644
index 0000000..e1cab39
@@ -0,0 +1,393 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *      Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0           0x000000
+#define MSI_INT0             0x800000
+
+struct xgene_msi_settings {
+     u32     index_per_group;
+     u32     irqs_per_index;
+     u32     nr_msi_vec;
+     u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+     struct irq_domain               *irqhost;
+     struct msi_controller           msi_chip;
+     struct xgene_msi_settings       *settings;
+     u32                             msi_addr_lo;
+     u32                             msi_addr_hi;
+     void __iomem                    *msi_regs;
+     unsigned long                   *bitmap;
+     struct mutex                    bitmap_lock;
+     int                             *msi_virqs;
+};
+
+static inline struct xgene_msi *to_xgene_msi(struct msi_controller *msi_chip)
+{
+     return container_of(msi_chip, struct xgene_msi, msi_chip);
+}
+
+struct xgene_msi_settings storm_msi_settings = {
+     .index_per_group        = 8,
+     .irqs_per_index         = 21,
+     .nr_msi_vec             = 2688,
+     .nr_hw_irqs             = 16,
+};
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static inline irq_hw_number_t virq_to_hw(unsigned int virq)
+{
+     struct irq_data *irq_data = irq_get_irq_data(virq);
+
+     return WARN_ON(!irq_data) ? 0 : irq_data->hwirq;
+}
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+     xgene_msi->settings = &storm_msi_settings;
+     return 0;
+}
+
+static struct irq_chip xgene_msi_chip = {
+     .name           = "X-Gene-1 MSI",
+     .irq_enable     = pci_msi_unmask_irq,
+     .irq_disable    = pci_msi_mask_irq,
+     .irq_mask       = pci_msi_mask_irq,
+     .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static int xgene_msi_host_map(struct irq_domain *h, unsigned int virq,
+                           irq_hw_number_t hw)
+{
+     irq_set_chip_and_handler(virq, &xgene_msi_chip, handle_simple_irq);
+     irq_set_chip_data(virq, h->host_data);
+     set_irq_flags(irq, IRQF_VALID);
+
+     return 0;
+}
+
+static const struct irq_domain_ops xgene_msi_host_ops = {
+     .map = xgene_msi_host_map,
+};
+
+static int xgene_msi_alloc(struct xgene_msi *xgene_msi)
+{
+     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+     int msi;
+
+     mutex_lock(&xgene_msi->bitmap_lock);
+
+     msi = find_first_zero_bit(xgene_msi->bitmap, msi_irq_count);
+     if (msi < msi_irq_count)
+             set_bit(msi, xgene_msi->bitmap);
+     else
+             msi = -ENOSPC;
+
+     mutex_unlock(&xgene_msi->bitmap_lock);
+
+     return msi;
+}
+
+static void xgene_msi_free(struct xgene_msi *xgene_msi, unsigned long irq)
+{
+     mutex_lock(&xgene_msi->bitmap_lock);
+
+     if (!test_bit(irq, xgene_msi->bitmap))
+             pr_err("trying to free unused MSI#%lu\n", irq);
+     else
+             clear_bit(irq, xgene_msi->bitmap);
+
+     mutex_unlock(&xgene_msi->bitmap_lock);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+     if (!xgene_msi->bitmap)
+             return -ENOMEM;
+     mutex_init(&xgene_msi->bitmap_lock);
+
+     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+     if (!xgene_msi->msi_virqs)
+             return -ENOMEM;
+     return 0;
+}
+
+static void xgene_msi_teardown_irq(struct msi_controller *chip,
+                                             unsigned int irq)
+{
+     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+
+     irq_set_msi_desc(irq, NULL);
+     xgene_msi_free(xgene_msi, virq_to_hw(irq));
+}
+
+static void xgene_compose_msi_msg(struct pci_dev *dev, int hwirq,
+                               struct msi_msg *msg,
+                               struct xgene_msi *xgene_msi)
+{
+     u32 nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+     u32 irqs_per_index = xgene_msi->settings->irqs_per_index;
+     u32 reg_set = hwirq / (nr_hw_irqs * irqs_per_index);
+     u32 group = hwirq % nr_hw_irqs;
+
+     msg->address_hi = xgene_msi->msi_addr_hi;
+     msg->address_lo = xgene_msi->msi_addr_lo +
+                       (((8 * group) + reg_set) << 16);
+     msg->data = (hwirq / nr_hw_irqs) % irqs_per_index;
+}
+
+static int xgene_msi_setup_irq(struct msi_controller *chip,
+                             struct pci_dev *pdev, struct msi_desc *desc)
+{
+     struct xgene_msi *xgene_msi = to_xgene_msi(chip);
+     struct msi_msg msg;
+     unsigned long virq, gic_irq;
+     int hwirq;
+
+     hwirq = xgene_msi_alloc(xgene_msi);
+     if (hwirq < 0) {
+             dev_err(&pdev->dev, "failed to allocate MSI\n");
+             return -ENOSPC;
+     }
+
+     virq = irq_create_mapping(xgene_msi->irqhost, hwirq);
+     if (virq == 0) {
+             dev_err(&pdev->dev, "failed to map hwirq %i\n", hwirq);
+             return -ENOSPC;
+     }
+
+     gic_irq = xgene_msi->msi_virqs[hwirq %
+                     xgene_msi->settings->nr_hw_irqs];
+     pr_debug("Mapp HWIRQ %d on GIC IRQ %lu TO VIRQ %lu\n",
+              hwirq, gic_irq, virq);
+     irq_set_msi_desc(virq, desc);
+     xgene_compose_msi_msg(pdev, hwirq, &msg, xgene_msi);
+     irq_set_handler_data(virq, (void *)gic_irq);
+     write_msi_msg(virq, &msg);
+
+     return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+     unsigned int virq;
+     int msir_index, msir_reg, msir_val, hw_irq;
+     u32 intr_index, grp_select, msi_grp, processed = 0;
+     u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+     msi_grp = irq - xgene_msi->msi_virqs[0];
+     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+             pr_err("invalid msi received\n");
+             return IRQ_NONE;
+     }
+
+     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+     irqs_per_index = xgene_msi->settings->irqs_per_index;
+     index_per_group = xgene_msi->settings->index_per_group;
+
+     grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+     while (grp_select) {
+             msir_index = ffs(grp_select) - 1;
+             msir_reg = (msi_grp << 19) + (msir_index << 16);
+             msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+             while (msir_val) {
+                     intr_index = ffs(msir_val) - 1;
+                     hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+                              nr_hw_irqs) + msi_grp;
+                     virq = irq_find_mapping(xgene_msi->irqhost, hw_irq);
+                     if (virq != 0)
+                             generic_handle_irq(virq);
+                     msir_val &= ~(1 << intr_index);
+                     processed++;
+             }
+             grp_select &= ~(1 << msir_index);
+     }
+
+     return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+     int virq, i;
+     struct xgene_msi *msi = platform_get_drvdata(pdev);
+     u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+     for (i = 0; i < nr_hw_irqs; i++) {
+             virq = msi->msi_virqs[i];
+             if (virq != 0)
+                     free_irq(virq, msi);
+     }
+
+     kfree(msi->bitmap);
+     msi->bitmap = NULL;
+
+     return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+                              struct platform_device *pdev,
+                              int irq_index)
+{
+     int virt_msir;
+     cpumask_var_t mask;
+     int err;
+
+     virt_msir = platform_get_irq(pdev, irq_index);
+     if (virt_msir < 0) {
+             dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                     irq_index);
+             return -EINVAL;
+     }
+
+     err = request_irq(virt_msir, xgene_msi_isr, 0, "xgene-msi", msi);
+     if (err) {
+             dev_err(&pdev->dev, "request irq failed\n");
+             return err;
+     }
+
+     if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+             cpumask_setall(mask);
+             irq_set_affinity(virt_msir, mask);
+             free_cpumask_var(mask);
+     }
+
+     msi->msi_virqs[irq_index] = virt_msir;
+
+     return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+     {.compatible = "apm,xgene1-msi",
+      .data = xgene_msi_init_storm_settings},
+     {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+     struct resource *res;
+     int rc, irq_index;
+     struct device_node *np;
+     const struct of_device_id *matched_np;
+     struct xgene_msi *xgene_msi;
+     xgene_msi_initcall_t init_fn;
+     u32 nr_hw_irqs, nr_msi_vecs;
+
+     np = of_find_matching_node_and_match(NULL,
+                          xgene_msi_match_table, &matched_np);
+     if (!np)
+             return -ENODEV;
+
+     xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+     if (!xgene_msi) {
+             dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+             return -ENOMEM;
+     }
+
+     init_fn = (xgene_msi_initcall_t) matched_np->data;
+     rc = init_fn(xgene_msi);
+     if (rc)
+             return rc;
+
+     platform_set_drvdata(pdev, xgene_msi);
+
+     nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+     xgene_msi->irqhost = irq_domain_add_linear(pdev->dev.of_node,
+                          nr_msi_vecs, &xgene_msi_host_ops, xgene_msi);
+     if (!xgene_msi->irqhost) {
+             dev_err(&pdev->dev, "No memory for MSI irqhost\n");
+             rc = -ENOMEM;
+             goto error;
+     }
+
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+     if (IS_ERR(xgene_msi->msi_regs)) {
+             dev_err(&pdev->dev, "no reg space\n");
+             rc = -EINVAL;
+             goto error;
+     }
+
+     xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+     xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+     rc = xgene_msi_init_allocator(xgene_msi);
+     if (rc) {
+             dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+             goto error;
+     }
+
+     nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+     for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+             rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+             if (rc)
+                     goto error;
+     }
+
+     xgene_msi->msi_chip.dev = &pdev->dev;
+     xgene_msi->msi_chip.of_node = np;
+     xgene_msi->msi_chip.setup_irq = xgene_msi_setup_irq;
+     xgene_msi->msi_chip.teardown_irq = xgene_msi_teardown_irq;
+
+     rc = of_pci_msi_chip_add(&xgene_msi->msi_chip);
+     if (rc) {
+             dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+             goto error;
+     }
+
+     dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+     return 0;
+error:
+     xgene_msi_remove(pdev);
+     return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+     .driver = {
+             .name = "xgene-msi",
+             .owner = THIS_MODULE,
+             .of_match_table = xgene_msi_match_table,
+     },
+     .probe = xgene_msi_probe,
+     .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+     return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <[email protected]>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
drivers/pci/host/pci-xgene.c +18 -0
index ee082c0..63d58e6 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
      return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+     struct device_node *msi_node;
+
+     msi_node = of_parse_phandle(bus->dev.of_node,
+                                     "msi-parent", 0);
+     if (!msi_node)
+             return -ENODEV;
+
+     bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+     if (bus->msi)
+             bus->msi->dev = &bus->dev;
+     else
+             return -ENODEV;
+     return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
      struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,14 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
      if (!bus)
              return -ENOMEM;
 
+     if (IS_ENABLED(CONFIG_PCI_MSI)) {

drivers/pci/host/pci-xgene.c static int xgene_pcie_probe_bridge(struct platform_device *pdev) · line 534

Quoted 10 lines
drivers/pci/host/pci-xgene.c +7 -0
+             ret = xgene_pcie_msi_enable(bus);
+             if (ret) {
+                     dev_err(port->dev, "failed to enable MSI\n");
+                     return ret;
+             }
+     }
+
      pci_scan_child_bus(bus);
      pci_assign_unassigned_bus_resources(bus);
      pci_bus_add_devices(bus);

--
Jazz is not dead. It just smells funny...

#9 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 19 lines · Duc Dang
Quoted 18 lines · Marc Zyngier
Quoted 5 lines · Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Quoted 7 lines
Quoted 2 lines

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked domain on top of the GIC. But what I want to see is the use of a generic pci_msi_domain on top of an irq_domain, just like we have on v2m and v3. Thomas has also been vocal enough about that in the past, and x86 is going down that road as well.

Quoted 2 lines

to what you did for Tegra MSI, but I don't see your latest changes in 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other cleanups. But you can already convert most of your driver and put it in the shape that matches what we have for v2m and v3. Once the required cleanups are in, I'll remove the last traces of msi_controller myself if necessary.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
maintainer
#99 lore raw

v2 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

In reply to Duc Dang

[+cc Marc]

Quoted 24 lines · Duc Dang
Quoted 23 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Quoted 7 lines
Quoted 3 lines

--
1.9.1

Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1 MSI support?

Marc had some comments, and as far as I can tell, you haven't addressed them yet. Am I mistaken?

Bjorn

author
#100 lore raw

v2 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

Quoted 30 lines · Bjorn Helgaas

[+cc Marc]

Quoted 27 lines · Duc Dang
Quoted 26 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c

--
1.9.1

Quoted 8 lines
Quoted 4 lines

Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1 MSI support?

Marc had some comments, and as far as I can tell, you haven't addressed them yet. Am I mistaken?

Hi Bjorn,

You are correct. I am making the changes as Marc pointed out. Will update a new version soon.

Regards,
Duc Dang.

Quoted 1 line

Bjorn

maintainer
#101 lore raw

v2 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

Quoted 36 lines · Duc Dang
Quoted 35 lines · Bjorn Helgaas

[+cc Marc]

Quoted 31 lines · Duc Dang
Quoted 23 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  61 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   4 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 393 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  25 ++
 7 files changed, 519 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Quoted 3 lines

--
1.9.1

Hi Bjorn, Arnd, and All,

Did you have a chance to take a look at this v2 patch set for X-Gene 1 MSI support?

Quoted 7 lines
Quoted 3 lines

Marc had some comments, and as far as I can tell, you haven't addressed them yet. Am I mistaken?

Hi Bjorn,

You are correct. I am making the changes as Marc pointed out. Will update a new version soon.

OK, thanks. If other people provide feedback, I usually wait until that's addressed before I spend much time looking at it, because I don't want to spend time rediscovering things people have already commented on, and I can't keep up with the list as it is :)

Bjorn

author
#10 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 34 lines · Marc Zyngier
Quoted 19 lines · Duc Dang
Quoted 18 lines · Marc Zyngier
Quoted 5 lines · Duc Dang

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Quoted 7 lines
Quoted 2 lines

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked domain on top of the GIC. But what I want to see is the use of a generic pci_msi_domain on top of an irq_domain, just like we have on v2m and v3. Thomas has also been vocal enough about that in the past, and x86 is going down that road as well.

Quoted 9 lines
Quoted 2 lines

to what you did for Tegra MSI, but I don't see your latest changes in 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other cleanups. But you can already convert most of your driver and put it in the shape that matches what we have for v2m and v3. Once the required cleanups are in, I'll remove the last traces of msi_controller myself if necessary.

Hi Marc, Bjorn,

I follow Marc's suggestion and convert my X-Gene 1 MSI driver to remove msi_controller struct and use generic pci_msi_domain on top of an irq_domain. The code requires Marc's changes in irq/kill-msi-controller branch to be compiled and function correctly, so I plan to post the patch on top of Marc's tree. Please let me know if you think I should have different approach to post this patch.

Another question I have is when do you plan to roll out this hierarchy irq domain implementation for MSI, as I see some of Marc's changes to kill msi_controller structure and implement MSI irq domain still does not get into Bjorn's tree.

Regards,
Duc Dang.

Quoted 5 lines

Thanks,

        M.

--
Jazz is not dead. It just smells funny...

#11 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

On Tue, 7 Apr 2015 20:56:48 +0100
Duc Dang <[email protected]> wrote:

Hi Duc,

Quoted 45 lines · Marc Zyngier
Quoted 34 lines · Duc Dang
Quoted 19 lines · Marc Zyngier
Quoted 18 lines · Duc Dang
Quoted 5 lines

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Quoted 7 lines
Quoted 2 lines

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked domain on top of the GIC. But what I want to see is the use of a generic pci_msi_domain on top of an irq_domain, just like we have on v2m and v3. Thomas has also been vocal enough about that in the past, and x86 is going down that road as well.

Quoted 9 lines
Quoted 2 lines

to what you did for Tegra MSI, but I don't see your latest changes in 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other cleanups. But you can already convert most of your driver and put it in the shape that matches what we have for v2m and v3. Once the required cleanups are in, I'll remove the last traces of msi_controller myself if necessary.

Quoted 8 lines

Hi Marc, Bjorn,

I follow Marc's suggestion and convert my X-Gene 1 MSI driver to remove msi_controller struct and use generic pci_msi_domain on top of an irq_domain. The code requires Marc's changes in irq/kill-msi-controller branch to be compiled and function correctly, so I plan to post the patch on top of Marc's tree. Please let me know if you think I should have different approach to post this patch.

I don't think you should rely on this branch just yet (this is why I suggested matching what we currently have for GICv2m and v3). Keeping msi_controller is fine at the moment, even if we only use to store the MSI domain. If and when this series makes it to mainline, I'll go over the individual drivers to convert them to the new scheme, just like I did for GIC and Tegra.

Quoted 4 lines

Another question I have is when do you plan to roll out this hierarchy irq domain implementation for MSI, as I see some of Marc's changes to kill msi_controller structure and implement MSI irq domain still does not get into Bjorn's tree.

I'm still tinkering with it (I have some long standing comments from Bjorn to address), and I'm working on non-PCI MSI support at the moment.

But the core MSI irq domain stuff is already in (this is what GICv2m and GICv3 ITS are using), and this should be an easy thing to convert your code.

Please let me know if I can be of any help.

Thanks,

	M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
author
#12 lore raw

v3 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2688 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain
v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 407 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 533 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#13 lore raw

PATCH v3 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +407 -0

407 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#91 lore raw

PATCH v3 2/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#92 lore raw

PATCH v3 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

In reply to Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +63 -0
new file mode 100644
index 0000000..0ffdcb3
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#93 lore raw

PATCH v3 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
author
#94 lore raw

PATCH v2 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier

Hi Marc,

Quoted 71 lines · Marc Zyngier

On Tue, 7 Apr 2015 20:56:48 +0100
Duc Dang <[email protected]> wrote:

Hi Duc,

Quoted 45 lines · Marc Zyngier
Quoted 34 lines · Duc Dang
Quoted 19 lines · Marc Zyngier
Quoted 18 lines · Duc Dang
Quoted 5 lines

X-Gene v1 SOC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

I just had a quick look at this, and this seems to be going in the exact opposite direction compared to what we now have on arm64, where we move away from using struct msi_controller for most thing, and implement PCI MSI/MSIX in a generic way, using MSI domains.

I suggest you have a look at how GICv2m and GICv3 ITS implement the MSI support. You can also have a look at what I did for the Tegra MSI controller in this patch:

https://git.kernel.org/cgit/linux/kernel/git/maz/arm-platforms.git/commit/?h=irq/kill-msi-controller&id=83b3602fcee7972b9d549ed729b56ec28de16081

Quoted 7 lines
Quoted 2 lines

Eventually, the plan is to kill msi_controller entirely, so introducing new drivers that rely on it is not something I'm eager to see.

Thanks, Marc.

X-Gene 1 MSI is handled by separate MSI controller block, so its
driver implementation is different from GICv2m and GICv3. I will refer

It will certainly be different in the sense that you won't use a stacked domain on top of the GIC. But what I want to see is the use of a generic pci_msi_domain on top of an irq_domain, just like we have on v2m and v3. Thomas has also been vocal enough about that in the past, and x86 is going down that road as well.

Quoted 9 lines
Quoted 2 lines

to what you did for Tegra MSI, but I don't see your latest changes in 4.0-rc4. Is the change you made for Tegra MSI going to mainline soon?

Not yet. As you can see in this branch, this relies on some other cleanups. But you can already convert most of your driver and put it in the shape that matches what we have for v2m and v3. Once the required cleanups are in, I'll remove the last traces of msi_controller myself if necessary.

Quoted 8 lines

Hi Marc, Bjorn,

I follow Marc's suggestion and convert my X-Gene 1 MSI driver to remove msi_controller struct and use generic pci_msi_domain on top of an irq_domain. The code requires Marc's changes in irq/kill-msi-controller branch to be compiled and function correctly, so I plan to post the patch on top of Marc's tree. Please let me know if you think I should have different approach to post this patch.

I don't think you should rely on this branch just yet (this is why I suggested matching what we currently have for GICv2m and v3). Keeping msi_controller is fine at the moment, even if we only use to store the MSI domain. If and when this series makes it to mainline, I'll go over the individual drivers to convert them to the new scheme, just like I did for GIC and Tegra.

Quoted 4 lines

Another question I have is when do you plan to roll out this hierarchy irq domain implementation for MSI, as I see some of Marc's changes to kill msi_controller structure and implement MSI irq domain still does not get into Bjorn's tree.

Quoted 9 lines

I'm still tinkering with it (I have some long standing comments from Bjorn to address), and I'm working on non-PCI MSI support at the moment.

But the core MSI irq domain stuff is already in (this is what GICv2m and GICv3 ITS are using), and this should be an easy thing to convert your code.

Please let me know if I can be of any help.

Thanks for your suggestion. I converted my code using the core MSI irq domain (similar to GICv2m, still have msi_controller structure) and posted
a new version (v3) of this patch. Can you please take a look when you have some time?

Regards,
Duc Dang.

Quoted 5 lines

Thanks,

        M.

--
Without deviation from the norm, progress is not possible.

maintainer
#14 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 12 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

...

drivers/pci/host/pci-xgene-msi.c new file +0 -0
@@ -0,0 +1,407 @@
...
 

drivers/pci/host/pci-xgene-msi.c line 150

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static void xgene_irq_domain_free(struct irq_domain *domain,
+				  unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&msi->bitmap_lock);
+
+	if (!test_bit(d->hwirq, msi->bitmap))
+		pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to see more information in this and the other pr_errs below. The text "failed to create parent MSI domain" in the dmesg log is not enough. Ideally it would identify the relevant host bridge.

Quoted 67 lines
drivers/pci/host/pci-xgene-msi.c new file +67 -0
+	else
+		clear_bit(d->hwirq, msi->bitmap);
+
+	mutex_unlock(&msi->bitmap_lock);
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+	.alloc  = xgene_irq_domain_alloc,
+	.free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+	msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+					    &msi_domain_ops, msi);
+
+	if (!msi->domain) {
+		pr_err("failed to create parent MSI domain\n");
+		return -ENOMEM;
+	}
+
+	msi->mchip.of_node = msi->node;
+	msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+						      &xgene_msi_domain_info,
+						      msi->domain);
+
+	if (!msi->mchip.domain) {
+		pr_err("failed to create MSI domain\n");
+		irq_domain_remove(msi->domain);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+	if (msi->mchip.domain)
+		irq_domain_remove(msi->mchip.domain);
+	if (msi->domain)
+		irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+	u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+	u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+	int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+	xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!xgene_msi->bitmap)
+		return -ENOMEM;
+
+	mutex_init(&xgene_msi->bitmap_lock);
+
+	xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+	if (!xgene_msi->msi_virqs)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+	struct xgene_msi *xgene_msi = (struct xgene_msi *) data;

drivers/pci/host/pci-xgene-msi.c line 227

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+	unsigned int virq;
+	int msir_index, msir_reg, msir_val, hw_irq;
+	u32 intr_index, grp_select, msi_grp, processed = 0;
+	u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+	msi_grp = irq - xgene_msi->msi_virqs[0];
+	if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+		pr_err("invalid msi received\n");
+		return IRQ_NONE;
+	}

Bjorn

author
#15 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 13 lines · Bjorn Helgaas
Quoted 11 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

...

drivers/pci/host/pci-xgene-msi.c new file +0 -0
@@ -0,0 +1,407 @@
...
Quoted 16 lines

drivers/pci/host/pci-xgene-msi.c line 150

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                               unsigned int virq, unsigned int nr_irqs)
+{
+     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+     mutex_lock(&msi->bitmap_lock);
+
+     if (!test_bit(d->hwirq, msi->bitmap))
+             pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to see more information in this and the other pr_errs below. The text "failed to create parent MSI domain" in the dmesg log is not enough. Ideally it would identify the relevant host bridge.

Hi Bjorn,

X-Gene has single MSI block that serves 5 PCIe ports. so in this implementation, 5 X-Gene PCIe host bridges share the same MSI domain.

I can add X-Gene 1 MSI driver name and device tree node name into these messages, but the host bridge information is not available.

As I understand, in case of error, the upper layer MSI core and driver code will check and report the error with additional device information (bus number, function number, etc)?

Regards,
Duc Dang.

Quoted 79 lines
Quoted 67 lines
drivers/pci/host/pci-xgene-msi.c new file +67 -0
+     else
+             clear_bit(d->hwirq, msi->bitmap);
+
+     mutex_unlock(&msi->bitmap_lock);
+
+     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+     .alloc  = xgene_irq_domain_alloc,
+     .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                         &msi_domain_ops, msi);
+
+     if (!msi->domain) {
+             pr_err("failed to create parent MSI domain\n");
+             return -ENOMEM;
+     }
+
+     msi->mchip.of_node = msi->node;
+     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                   &xgene_msi_domain_info,
+                                                   msi->domain);
+
+     if (!msi->mchip.domain) {
+             pr_err("failed to create MSI domain\n");
+             irq_domain_remove(msi->domain);
+             return -ENOMEM;
+     }
+
+     return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+     if (msi->mchip.domain)
+             irq_domain_remove(msi->mchip.domain);
+     if (msi->domain)
+             irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+     if (!xgene_msi->bitmap)
+             return -ENOMEM;
+
+     mutex_init(&xgene_msi->bitmap_lock);
+
+     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+     if (!xgene_msi->msi_virqs)
+             return -ENOMEM;
+
+     return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;

drivers/pci/host/pci-xgene-msi.c line 227

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+     unsigned int virq;
+     int msir_index, msir_reg, msir_val, hw_irq;
+     u32 intr_index, grp_select, msi_grp, processed = 0;
+     u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+     msi_grp = irq - xgene_msi->msi_virqs[0];
+     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+             pr_err("invalid msi received\n");
+             return IRQ_NONE;
+     }

Bjorn

maintainer
#16 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 35 lines · Duc Dang
Quoted 13 lines · Bjorn Helgaas
Quoted 11 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

...

drivers/pci/host/pci-xgene-msi.c new file +0 -0
@@ -0,0 +1,407 @@
...
Quoted 16 lines

drivers/pci/host/pci-xgene-msi.c line 150

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                               unsigned int virq, unsigned int nr_irqs)
+{
+     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+     mutex_lock(&msi->bitmap_lock);
+
+     if (!test_bit(d->hwirq, msi->bitmap))
+             pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to see more information in this and the other pr_errs below. The text "failed to create parent MSI domain" in the dmesg log is not enough. Ideally it would identify the relevant host bridge.

Hi Bjorn,

X-Gene has single MSI block that serves 5 PCIe ports. so in this implementation, 5 X-Gene PCIe host bridges share the same MSI domain.

Quoted 6 lines

I can add X-Gene 1 MSI driver name and device tree node name into these messages, but the host bridge information is not available.

As I understand, in case of error, the upper layer MSI core and driver code will check and report the error with additional device information (bus number, function number, etc)?

If you think the upper layers already report the error, maybe you could just drop the output from the X-Gene code.

I just think it's pointless to print messages that are constant strings with no hook for the user to associate them with a device or event.

Quoted 84 lines
Quoted 71 lines
Quoted 71 lines
drivers/pci/host/pci-xgene-msi.c new file +71 -0
+     else
+             clear_bit(d->hwirq, msi->bitmap);
+
+     mutex_unlock(&msi->bitmap_lock);
+
+     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+     .alloc  = xgene_irq_domain_alloc,
+     .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                         &msi_domain_ops, msi);
+
+     if (!msi->domain) {
+             pr_err("failed to create parent MSI domain\n");
+             return -ENOMEM;
+     }
+
+     msi->mchip.of_node = msi->node;
+     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                   &xgene_msi_domain_info,
+                                                   msi->domain);
+
+     if (!msi->mchip.domain) {
+             pr_err("failed to create MSI domain\n");
+             irq_domain_remove(msi->domain);
+             return -ENOMEM;
+     }
+
+     return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+     if (msi->mchip.domain)
+             irq_domain_remove(msi->mchip.domain);
+     if (msi->domain)
+             irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+     if (!xgene_msi->bitmap)
+             return -ENOMEM;
+
+     mutex_init(&xgene_msi->bitmap_lock);
+
+     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+     if (!xgene_msi->msi_virqs)
+             return -ENOMEM;
+
+     return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+     unsigned int virq;
+     int msir_index, msir_reg, msir_val, hw_irq;
+     u32 intr_index, grp_select, msi_grp, processed = 0;
+     u32 nr_hw_irqs, irqs_per_index, index_per_group;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 227

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+
+     msi_grp = irq - xgene_msi->msi_virqs[0];
+     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+             pr_err("invalid msi received\n");
+             return IRQ_NONE;
+     }

Bjorn

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

author
#17 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 43 lines · Bjorn Helgaas
Quoted 35 lines · Duc Dang
Quoted 13 lines · Bjorn Helgaas
Quoted 11 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

...

drivers/pci/host/pci-xgene-msi.c new file +0 -0
@@ -0,0 +1,407 @@
...
Quoted 16 lines

drivers/pci/host/pci-xgene-msi.c line 150

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                               unsigned int virq, unsigned int nr_irqs)
+{
+     struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+     struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+     mutex_lock(&msi->bitmap_lock);
+
+     if (!test_bit(d->hwirq, msi->bitmap))
+             pr_err("trying to free unused MSI#%lu\n", d->hwirq);

I'll let Marc comment on the substance of this patch, but I'd sure like to see more information in this and the other pr_errs below. The text "failed to create parent MSI domain" in the dmesg log is not enough. Ideally it would identify the relevant host bridge.

Hi Bjorn,

X-Gene has single MSI block that serves 5 PCIe ports. so in this implementation, 5 X-Gene PCIe host bridges share the same MSI domain.

Quoted 6 lines

I can add X-Gene 1 MSI driver name and device tree node name into these messages, but the host bridge information is not available.

As I understand, in case of error, the upper layer MSI core and driver code will check and report the error with additional device information (bus number, function number, etc)?

Quoted 6 lines

If you think the upper layers already report the error, maybe you could just drop the output from the X-Gene code.

I just think it's pointless to print messages that are constant strings with no hook for the user to associate them with a device or event.

Hi Bjorn,

Yes, I will do so.

Regards,
Duc Dang.

Quoted 85 lines
Quoted 85 lines
Quoted 72 lines
Quoted 72 lines
drivers/pci/host/pci-xgene-msi.c new file +71 -0
+     else
+             clear_bit(d->hwirq, msi->bitmap);
+
+     mutex_unlock(&msi->bitmap_lock);
+
+     irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+     .alloc  = xgene_irq_domain_alloc,
+     .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+     msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                         &msi_domain_ops, msi);
+
+     if (!msi->domain) {
+             pr_err("failed to create parent MSI domain\n");
+             return -ENOMEM;
+     }
+
+     msi->mchip.of_node = msi->node;
+     msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                   &xgene_msi_domain_info,
+                                                   msi->domain);
+
+     if (!msi->mchip.domain) {
+             pr_err("failed to create MSI domain\n");
+             irq_domain_remove(msi->domain);
+             return -ENOMEM;
+     }
+
+     return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+     if (msi->mchip.domain)
+             irq_domain_remove(msi->mchip.domain);
+     if (msi->domain)
+             irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+     u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+     u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+     int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+     xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+     if (!xgene_msi->bitmap)
+             return -ENOMEM;
+
+     mutex_init(&xgene_msi->bitmap_lock);
+
+     xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+     if (!xgene_msi->msi_virqs)
+             return -ENOMEM;
+
+     return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)
+{
+     struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+     unsigned int virq;
+     int msir_index, msir_reg, msir_val, hw_irq;
+     u32 intr_index, grp_select, msi_grp, processed = 0;
+     u32 nr_hw_irqs, irqs_per_index, index_per_group;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 227

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+
+     msi_grp = irq - xgene_msi->msi_virqs[0];
+     if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+             pr_err("invalid msi received\n");
+             return IRQ_NONE;
+     }

Bjorn

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#18 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 84 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +32 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

drivers/pci/host/pci-xgene-msi.c line 54

Quoted 12 lines
drivers/pci/host/pci-xgene-msi.c new file +12 -0
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+       .index_per_group        = 8,
+       .irqs_per_index         = 21,
+       .nr_msi_vec             = 2688,
+       .nr_hw_irqs             = 16,
+};

It would really help understanding how index, group and hw irq lines are structured. nr_msi_vec is obviously the product of these three numbers, so maybe you can loose it (we have computers, remember... ;-).

Do you expect more than this single "storm" implementation? If so, maybe they should be described in the DT. If not, why do we need a separate structure with an indirection if we know these are constants?

Quoted 25 lines
drivers/pci/host/pci-xgene-msi.c new file +25 -0
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+       xgene_msi->settings = &storm_msi_settings;
+       return 0;
+}
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{

drivers/pci/host/pci-xgene-msi.c line 89

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+       u32 irqs_per_index = msi->settings->irqs_per_index;
+       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
+       u32 group = data->hwirq % nr_hw_irqs;
+
+       msg->address_hi = msi->msi_addr_hi;
+       msg->address_lo = msi->msi_addr_lo +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse engineer the hardware by looking at the code...

Please give us some clue on how interrupts are spread across the various pages, how the various indexes are combined to give an actual address.

drivers/pci/host/pci-xgene-msi.c line 100

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       unsigned int gic_irq;
+       int ret;
+
+       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
+       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 105 lines
drivers/pci/host/pci-xgene-msi.c new file +105 -0
+       if (ret == IRQ_SET_MASK_OK)
+               ret = IRQ_SET_MASK_OK_DONE;
+
+       return ret;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+
+       struct xgene_msi *msi = domain->host_data;
+       u32 msi_irq_count = msi->settings->nr_msi_vec;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
+       if (msi_irq < msi_irq_count)
+               set_bit(msi_irq, msi->bitmap);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+       mutex_lock(&msi->bitmap_lock);
+
+       if (!test_bit(d->hwirq, msi->bitmap))
+               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+       else
+               clear_bit(d->hwirq, msi->bitmap);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                           &msi_domain_ops, msi);
+
+       if (!msi->domain) {
+               pr_err("failed to create parent MSI domain\n");
+               return -ENOMEM;
+       }
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               pr_err("failed to create MSI domain\n");
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+

drivers/pci/host/pci-xgene-msi.c line 215

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained interrupt controller. Please use the proper infrastructure for this (irq_set_chained_handler, chained_irq_enter, chained_irq_exit). Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

Quoted 16 lines
drivers/pci/host/pci-xgene-msi.c new file +16 -0
+{
+       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp, processed = 0;
+       u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+       msi_grp = irq - xgene_msi->msi_virqs[0];
+       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+               pr_err("invalid msi received\n");
+               return IRQ_NONE;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       irqs_per_index = xgene_msi->settings->irqs_per_index;
+       index_per_group = xgene_msi->settings->index_per_group;

drivers/pci/host/pci-xgene-msi.c line 241

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

Quoted 39 lines
drivers/pci/host/pci-xgene-msi.c new file +39 -0
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+       for (i = 0; i < nr_hw_irqs; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)
+                       free_irq(virq, msi);
+       }
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+                                struct platform_device *pdev,
+                                int irq_index)
+{
+       int virt_msir;
+       cpumask_var_t mask;
+       int err;

drivers/pci/host/pci-xgene-msi.c line 290

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+       virt_msir = platform_get_irq(pdev, irq_index);
+       if (virt_msir < 0) {
+               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                       irq_index);
+               return -EINVAL;
+       }
+
+       err = request_irq(virt_msir, xgene_msi_isr, 0,
+                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

Quoted 157 lines
drivers/pci/host/pci-xgene-msi.c new file +117 -0
+       if (err) {
+               dev_err(&pdev->dev, "request irq failed\n");
+               return err;
+       }
+
+       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+               cpumask_setall(mask);
+               irq_set_affinity(virt_msir, mask);
+               free_cpumask_var(mask);
+       }
+
+       msi->msi_virqs[irq_index] = virt_msir;
+
+       return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi",
+        .data = xgene_msi_init_storm_settings},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       struct xgene_msi *xgene_msi;
+       xgene_msi_initcall_t init_fn;
+       u32 nr_hw_irqs, nr_msi_vecs;
+
+       np = of_find_matching_node_and_match(NULL,
+                            xgene_msi_match_table, &matched_np);
+       if (!np)
+               return -ENODEV;
+
+       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+       if (!xgene_msi) {
+               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+               return -ENOMEM;
+       }
+
+       xgene_msi->node = np;
+
+       init_fn = (xgene_msi_initcall_t) matched_np->data;
+       rc = init_fn(xgene_msi);
+       if (rc)
+               return rc;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+
+       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+               if (rc)
+                       goto error;
+       }
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <[email protected]>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#89 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang

Just a nit about a license mismatch.

Quoted 14 lines · Duc Dang
drivers/pci/host/Kconfig +3 -0
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
drivers/pci/host/Makefile +1 -0
 
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o

drivers/pci/host/pci-xgene-msi.c line 16

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
 
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.

This states the license of this file is GPL v2 or later.

Quoted 1 line

+MODULE_LICENSE("GPL v2");

And, according to include/linux/module.h, this states the license of this driver is GPL v2. So either the comment at the top of this file or the license ident used in this macro needs to change.

(Now, as far as I can tell, this driver is built-in only. So I think this macro - and all other module specific code in this file - is actually unneeded. But I already know Bjorn's position here, so I won't bother you about that.)

Thanks,

Paul Bolle

author
#19 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 91 lines · Marc Zyngier
Quoted 90 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +38 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       struct irq_domain               *domain;
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 18 lines

drivers/pci/host/pci-xgene-msi.c line 54

Quoted 13 lines
drivers/pci/host/pci-xgene-msi.c new file +12 -0
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+       .index_per_group        = 8,
+       .irqs_per_index         = 21,
+       .nr_msi_vec             = 2688,
+       .nr_hw_irqs             = 16,
+};

It would really help understanding how index, group and hw irq lines are structured. nr_msi_vec is obviously the product of these three numbers, so maybe you can loose it (we have computers, remember... ;-).

Quoted 4 lines

Do you expect more than this single "storm" implementation? If so, maybe they should be described in the DT. If not, why do we need a separate structure with an indirection if we know these are constants?

Yes, I will add description about index, group, and hw irq lines structure and also replace the fields in this storm_msi_settings structure with constant dedinitions.

Quoted 39 lines
Quoted 25 lines
drivers/pci/host/pci-xgene-msi.c new file +25 -0
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+       xgene_msi->settings = &storm_msi_settings;
+       return 0;
+}
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{

drivers/pci/host/pci-xgene-msi.c line 89

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+       u32 irqs_per_index = msi->settings->irqs_per_index;
+       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
+       u32 group = data->hwirq % nr_hw_irqs;
+
+       msg->address_hi = msi->msi_addr_hi;
+       msg->address_lo = msi->msi_addr_lo +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse engineer the hardware by looking at the code...

Quoted 2 lines

Please give us some clue on how interrupts are spread across the various pages, how the various indexes are combined to give an actual address.

I will add description about this.

Quoted 16 lines

drivers/pci/host/pci-xgene-msi.c line 100

Quoted 12 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       unsigned int gic_irq;
+       int ret;
+
+       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
+       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it. So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 113 lines
Quoted 113 lines
drivers/pci/host/pci-xgene-msi.c new file +112 -0
+       if (ret == IRQ_SET_MASK_OK)
+               ret = IRQ_SET_MASK_OK_DONE;
+
+       return ret;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+
+       struct xgene_msi *msi = domain->host_data;
+       u32 msi_irq_count = msi->settings->nr_msi_vec;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
+       if (msi_irq < msi_irq_count)
+               set_bit(msi_irq, msi->bitmap);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+       mutex_lock(&msi->bitmap_lock);
+
+       if (!test_bit(d->hwirq, msi->bitmap))
+               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+       else
+               clear_bit(d->hwirq, msi->bitmap);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                           &msi_domain_ops, msi);
+
+       if (!msi->domain) {
+               pr_err("failed to create parent MSI domain\n");
+               return -ENOMEM;
+       }
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               pr_err("failed to create MSI domain\n");
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
Quoted 9 lines

drivers/pci/host/pci-xgene-msi.c line 215

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained interrupt controller. Please use the proper infrastructure for this (irq_set_chained_handler, chained_irq_enter, chained_irq_exit). Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

Yes, I will change this function to protect it with chained_irq_enter, chained_irq_exit

Quoted 20 lines
Quoted 20 lines
drivers/pci/host/pci-xgene-msi.c new file +20 -0
+{
+       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp, processed = 0;
+       u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+       msi_grp = irq - xgene_msi->msi_virqs[0];
+       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+               pr_err("invalid msi received\n");
+               return IRQ_NONE;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       irqs_per_index = xgene_msi->settings->irqs_per_index;
+       index_per_group = xgene_msi->settings->index_per_group;
+
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 241

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

I will have description for this.

Quoted 44 lines
Quoted 44 lines
drivers/pci/host/pci-xgene-msi.c new file +43 -0
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+       for (i = 0; i < nr_hw_irqs; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)
+                       free_irq(virq, msi);
+       }
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+                                struct platform_device *pdev,
+                                int irq_index)
+{
+       int virt_msir;
+       cpumask_var_t mask;
+       int err;
+
+       virt_msir = platform_get_irq(pdev, irq_index);
+       if (virt_msir < 0) {
+               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 290

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+                       irq_index);
+               return -EINVAL;
+       }
+
+       err = request_irq(virt_msir, xgene_msi_isr, 0,
+                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

I will replace request_irq with irq_set_chained_handler and irq_set_handler_data

Quoted 163 lines
Quoted 158 lines
drivers/pci/host/pci-xgene-msi.c new file +117 -0
+       if (err) {
+               dev_err(&pdev->dev, "request irq failed\n");
+               return err;
+       }
+
+       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+               cpumask_setall(mask);
+               irq_set_affinity(virt_msir, mask);
+               free_cpumask_var(mask);
+       }
+
+       msi->msi_virqs[irq_index] = virt_msir;
+
+       return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi",
+        .data = xgene_msi_init_storm_settings},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       struct xgene_msi *xgene_msi;
+       xgene_msi_initcall_t init_fn;
+       u32 nr_hw_irqs, nr_msi_vecs;
+
+       np = of_find_matching_node_and_match(NULL,
+                            xgene_msi_match_table, &matched_np);
+       if (!np)
+               return -ENODEV;
+
+       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+       if (!xgene_msi) {
+               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+               return -ENOMEM;
+       }
+
+       xgene_msi->node = np;
+
+       init_fn = (xgene_msi_initcall_t) matched_np->data;
+       rc = init_fn(xgene_msi);
+       if (rc)
+               return rc;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+
+       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+               if (rc)
+                       goto error;
+       }
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <[email protected]>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

Thanks,

Quoted 3 lines
        M.

--
Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

author
#90 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Paul Bolle
Quoted 28 lines · Paul Bolle

Just a nit about a license mismatch.

Quoted 14 lines · Duc Dang
drivers/pci/host/Kconfig +3 -0
 
+config PCI_XGENE_MSI
+     bool "X-Gene v1 PCIe MSI feature"
+     depends on PCI_XGENE && PCI_MSI
drivers/pci/host/Makefile +1 -0
 
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o

drivers/pci/host/pci-xgene-msi.c line 16

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
 
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
Quoted 8 lines

This states the license of this file is GPL v2 or later.

Quoted 1 line

+MODULE_LICENSE("GPL v2");

And, according to include/linux/module.h, this states the license of this driver is GPL v2. So either the comment at the top of this file or the license ident used in this macro needs to change.

Thanks, I will fix this.

Quoted 10 lines

(Now, as far as I can tell, this driver is built-in only. So I think this macro - and all other module specific code in this file - is actually unneeded. But I already know Bjorn's position here, so I won't bother you about that.)

Thanks,

Paul Bolle

Regards,
Duc Dang.

#86 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier

Hi Marc:

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the GICv2m seems to support OF model of discovery for msi controller. X-Gene1 uses this driver and X-Gene2 uses GICv2m, there needs to be a way to associate msi controller with the PCIe bus. I haven't
found a standard way of doing finding "msi-parent" for ACPI. Do you have any suggestion.

Sorry for top posting.

Quoted 550 lines · Marc Zyngier
Quoted 84 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +32 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

drivers/pci/host/pci-xgene-msi.c line 54

Quoted 12 lines
drivers/pci/host/pci-xgene-msi.c new file +12 -0
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+};
+
+struct xgene_msi_settings storm_msi_settings = {
+       .index_per_group        = 8,
+       .irqs_per_index         = 21,
+       .nr_msi_vec             = 2688,
+       .nr_hw_irqs             = 16,
+};

It would really help understanding how index, group and hw irq lines are structured. nr_msi_vec is obviously the product of these three numbers, so maybe you can loose it (we have computers, remember... ;-).

Do you expect more than this single "storm" implementation? If so, maybe they should be described in the DT. If not, why do we need a separate structure with an indirection if we know these are constants?

Quoted 25 lines
drivers/pci/host/pci-xgene-msi.c new file +25 -0
+
+typedef int (*xgene_msi_initcall_t)(struct xgene_msi *);
+
+static int xgene_msi_init_storm_settings(struct xgene_msi *xgene_msi)
+{
+       xgene_msi->settings = &storm_msi_settings;
+       return 0;
+}
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{

drivers/pci/host/pci-xgene-msi.c line 89

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+       u32 irqs_per_index = msi->settings->irqs_per_index;
+       u32 reg_set = data->hwirq / (nr_hw_irqs * irqs_per_index);
+       u32 group = data->hwirq % nr_hw_irqs;
+
+       msg->address_hi = msi->msi_addr_hi;
+       msg->address_lo = msi->msi_addr_lo +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / nr_hw_irqs) % irqs_per_index;

... (sound of head exploding...). I hate it when I have to reverse engineer the hardware by looking at the code...

Please give us some clue on how interrupts are spread across the various pages, how the various indexes are combined to give an actual address.

drivers/pci/host/pci-xgene-msi.c line 100

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       unsigned int gic_irq;
+       int ret;
+
+       gic_irq = msi->msi_virqs[irq_data->hwirq % msi->settings->nr_hw_irqs];
+       ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 105 lines
drivers/pci/host/pci-xgene-msi.c new file +105 -0
+       if (ret == IRQ_SET_MASK_OK)
+               ret = IRQ_SET_MASK_OK_DONE;
+
+       return ret;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+
+       struct xgene_msi *msi = domain->host_data;
+       u32 msi_irq_count = msi->settings->nr_msi_vec;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = find_first_zero_bit(msi->bitmap, msi_irq_count);
+       if (msi_irq < msi_irq_count)
+               set_bit(msi_irq, msi->bitmap);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq, &xgene_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+
+       mutex_lock(&msi->bitmap_lock);
+
+       if (!test_bit(d->hwirq, msi->bitmap))
+               pr_err("trying to free unused MSI#%lu\n", d->hwirq);
+       else
+               clear_bit(d->hwirq, msi->bitmap);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, msi->settings->nr_msi_vec,
+                                           &msi_domain_ops, msi);
+
+       if (!msi->domain) {
+               pr_err("failed to create parent MSI domain\n");
+               return -ENOMEM;
+       }
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               pr_err("failed to create MSI domain\n");
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       u32 msi_irq_count = xgene_msi->settings->nr_msi_vec;
+       u32 hw_irq_count = xgene_msi->settings->nr_hw_irqs;
+       int size = BITS_TO_LONGS(msi_irq_count) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+

drivers/pci/host/pci-xgene-msi.c line 215

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(hw_irq_count, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static irqreturn_t xgene_msi_isr(int irq, void *data)

This is not a "normal" interrupt handler. This is really a chained interrupt controller. Please use the proper infrastructure for this (irq_set_chained_handler, chained_irq_enter, chained_irq_exit). Otherwise, your usage of irq_find_mapping is illegal wrt RCU.

Quoted 16 lines
drivers/pci/host/pci-xgene-msi.c new file +16 -0
+{
+       struct xgene_msi *xgene_msi = (struct xgene_msi *) data;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp, processed = 0;
+       u32 nr_hw_irqs, irqs_per_index, index_per_group;
+
+       msi_grp = irq - xgene_msi->msi_virqs[0];
+       if (msi_grp >= xgene_msi->settings->nr_hw_irqs) {
+               pr_err("invalid msi received\n");
+               return IRQ_NONE;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       irqs_per_index = xgene_msi->settings->irqs_per_index;
+       index_per_group = xgene_msi->settings->index_per_group;

drivers/pci/host/pci-xgene-msi.c line 241

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_INDEX0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       hw_irq = (((msir_index * irqs_per_index) + intr_index) *
+                                nr_hw_irqs) + msi_grp;

Same thing here. This requires some explaination on how the HW is organised.

Quoted 39 lines
drivers/pci/host/pci-xgene-msi.c new file +39 -0
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+       u32 nr_hw_irqs = msi->settings->nr_hw_irqs;
+
+       for (i = 0; i < nr_hw_irqs; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)
+                       free_irq(virq, msi);
+       }
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+                                struct platform_device *pdev,
+                                int irq_index)
+{
+       int virt_msir;
+       cpumask_var_t mask;
+       int err;

drivers/pci/host/pci-xgene-msi.c line 290

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+       virt_msir = platform_get_irq(pdev, irq_index);
+       if (virt_msir < 0) {
+               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                       irq_index);
+               return -EINVAL;
+       }
+
+       err = request_irq(virt_msir, xgene_msi_isr, 0,
+                         xgene_msi_top_irq_chip.name, msi);

This is where you should use irq_set_chained_handler.

Quoted 157 lines
drivers/pci/host/pci-xgene-msi.c new file +117 -0
+       if (err) {
+               dev_err(&pdev->dev, "request irq failed\n");
+               return err;
+       }
+
+       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+               cpumask_setall(mask);
+               irq_set_affinity(virt_msir, mask);
+               free_cpumask_var(mask);
+       }
+
+       msi->msi_virqs[irq_index] = virt_msir;
+
+       return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi",
+        .data = xgene_msi_init_storm_settings},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       struct xgene_msi *xgene_msi;
+       xgene_msi_initcall_t init_fn;
+       u32 nr_hw_irqs, nr_msi_vecs;
+
+       np = of_find_matching_node_and_match(NULL,
+                            xgene_msi_match_table, &matched_np);
+       if (!np)
+               return -ENODEV;
+
+       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+       if (!xgene_msi) {
+               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+               return -ENOMEM;
+       }
+
+       xgene_msi->node = np;
+
+       init_fn = (xgene_msi_initcall_t) matched_np->data;
+       rc = init_fn(xgene_msi);
+       if (rc)
+               return rc;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       nr_msi_vecs = xgene_msi->settings->nr_msi_vec;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+
+       xgene_msi->msi_addr_hi = upper_32_bits(res->start);
+       xgene_msi->msi_addr_lo = lower_32_bits(res->start);
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       nr_hw_irqs = xgene_msi->settings->nr_hw_irqs;
+       for (irq_index = 0; irq_index < nr_hw_irqs; irq_index++) {
+               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+               if (rc)
+                       goto error;
+       }
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
+MODULE_AUTHOR("Duc Dang <[email protected]>");
+MODULE_DESCRIPTION("APM X-Gene PCIe MSI driver");
+MODULE_LICENSE("GPL v2");
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

Thanks,

        M.

--
Jazz is not dead. It just smells funny...
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#20 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 111 lines · Duc Dang
Quoted 100 lines · Marc Zyngier
Quoted 99 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407 
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene 

SoC.
There are 5 internal PCIe ports available. Each port is
GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or 

modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +23 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       struct xgene_msi_settings       *settings;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 2 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when
assigning it to the message (you seem to play all kind of tricks on the
address anyway).

Quoted 2 lines

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

[...]

Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool 

force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi = 

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect...
What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Well, that's a significant departure from the expected behaviour. In other
words, I wonder how useful this is. Could you instead reconfigure the MSI
itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce
your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Thanks,

         M.
Signature · 1 line
Fast, cheap, reliable. Pick two.
#87 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Feng Kan

Hi Feng,

Quoted 9 lines · Feng Kan

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the
GICv2m seems to support OF model of discovery for msi controller. X-Gene1
uses this driver and X-Gene2 uses GICv2m, there needs to be a way to associate msi controller with the PCIe bus. I haven't found a standard way of doing finding "msi-parent" for ACPI. Do you have
any suggestion.

This is remarkably off topic! ;-)

ACPI only provides information for GICv2m and GICv3. No other interrupt controller,
MSI widget or fairy dust provider will be supported on arm64 with ACPI. So this driver
will be DT only.

GICv2m, being described in the ACPI spec, is growing some support. My understanding is
that this is done by AMD (Suravee Suthikulpanit) using my irq/msi-domain branch as a
base (I know that Suravee is looking at expanding the irqdomain matching code to
accept a pointer to an ACPI table instead of a device node to match the target
domain).

Hope this helps,

         M.
Signature · 1 line
Fast, cheap, reliable. Pick two.
#88 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Feng Kan
Quoted 10 lines · On Friday 10 April 2015 17:16:32 Feng Kan wrote:

Hi Marc:

Is there any plans to support ACPI for GICv2m MSI? Both this driver and the GICv2m seems to support OF model of discovery for msi controller. X-Gene1 uses this driver and X-Gene2 uses GICv2m, there needs to be a way to associate msi controller with the PCIe bus. I haven't
found a standard way of doing finding "msi-parent" for ACPI. Do you have any suggestion.

Sorry for top posting.

Doing gicv2m in ACPI should be straightforward, and the ACPI code can simply assume that there is only one GIC instance (whichever version) in the system and use that for all standard PCI hosts.

Arnd

author
#21 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 114 lines · Marc Zyngier
Quoted 113 lines · Duc Dang
Quoted 108 lines · Marc Zyngier
Quoted 91 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3

capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or

modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +15 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

Quoted 7 lines
Quoted 2 lines

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).
If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

Quoted 28 lines

[...]

Quoted 18 lines
Quoted 8 lines
Quoted 8 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool

force)

drivers/pci/host/pci-xgene-msi.c new file +5 -0
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

Please let me know your opinion.

Quoted 6 lines

Thanks,

        M.

--
Fast, cheap, reliable. Pick two.

#22 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny.
author
#23 lore raw

v4 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain
v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 ++++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 410 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 ++
 7 files changed, 536 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#24 lore raw

PATCH v4 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

X-Gene v1 SoC supports total 2048 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 410 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 438 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +410 -0

410 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#30 lore raw

PATCH v4 2/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#31 lore raw

PATCH v4 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

In reply to Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +63 -0
new file mode 100644
index 0000000..0ffdcb3
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#32 lore raw

PATCH v4 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
author
#33 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 148 lines · Marc Zyngier
Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 42

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

Quoted 7 lines

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

I changed to use u64 for msi_addr and split it when composing MSI messages. The change is in v4 of the patch set that I just posted.

Quoted 50 lines
Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

Quoted 8 lines

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

Thanks Marc. This is a very good idea.

But to move MSIs around, I need to change MSI termination address and data and write them to device configuration space. This may cause problems if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

Quoted 11 lines

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

Quoted 9 lines

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

I have it disabled (set_affinity does nothing) in my v4 patch.

Quoted 5 lines

Thanks,

        M.

--
Jazz is not dead. It just smells funny.

#34 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 216 lines · Duc Dang
Quoted 148 lines · Marc Zyngier
Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

Quoted 7 lines

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

I changed to use u64 for msi_addr and split it when composing MSI messages. The change is in v4 of the patch set that I just posted.

Quoted 50 lines
Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

Quoted 8 lines

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

Quoted 8 lines

Thanks Marc. This is a very good idea.

But to move MSIs around, I need to change MSI termination address and data and write them to device configuration space. This may cause problems if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

There is an inherent race when changing the affinity of any interrupt, whether that's an MSI or not. The kernel is perfectly prepared to handle such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either hit the old *or* the new CPU, but that race is only during the write itself (you can read back from the config space if you're really paranoid). By the time you return from this read/write, the device will be reconfigured.

Quoted 17 lines
Quoted 17 lines

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

Quoted 4 lines
Quoted 3 lines

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

I have it disabled (set_affinity does nothing) in my v4 patch.

It would be good if you could try the above approach. It shouldn't be hard to write, and it would be a lot better than just ignoring the problem.

I'll try to review the patches soon(-ish)...

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
author
#35 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 253 lines · Marc Zyngier
Quoted 216 lines · Duc Dang
Quoted 148 lines · Marc Zyngier
Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

Quoted 7 lines

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

I changed to use u64 for msi_addr and split it when composing MSI messages. The change is in v4 of the patch set that I just posted.

Quoted 50 lines
Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

Quoted 8 lines

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

Quoted 8 lines

Thanks Marc. This is a very good idea.

But to move MSIs around, I need to change MSI termination address and data and write them to device configuration space. This may cause problems if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

There is an inherent race when changing the affinity of any interrupt, whether that's an MSI or not. The kernel is perfectly prepared to handle such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either hit the old *or* the new CPU, but that race is only during the write itself (you can read back from the config space if you're really paranoid). By the time you return from this read/write, the device will be reconfigured.

Quoted 17 lines
Quoted 17 lines

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

Quoted 8 lines
Quoted 4 lines
Quoted 3 lines

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

I have it disabled (set_affinity does nothing) in my v4 patch.

It would be good if you could try the above approach. It shouldn't be hard to write, and it would be a lot better than just ignoring the problem.

Yes. I am working on this change.

Quoted 5 lines

I'll try to review the patches soon(-ish)...

        M.

--
Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

#36 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 257 lines · Duc Dang
Quoted 256 lines · Marc Zyngier
Quoted 216 lines · Duc Dang
Quoted 148 lines · Marc Zyngier
Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

Quoted 7 lines

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

I changed to use u64 for msi_addr and split it when composing MSI messages. The change is in v4 of the patch set that I just posted.

Quoted 50 lines
Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

Quoted 8 lines

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

Quoted 8 lines

Thanks Marc. This is a very good idea.

But to move MSIs around, I need to change MSI termination address and data and write them to device configuration space. This may cause problems if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

There is an inherent race when changing the affinity of any interrupt, whether that's an MSI or not. The kernel is perfectly prepared to handle such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either hit the old *or* the new CPU, but that race is only during the write itself (you can read back from the config space if you're really paranoid). By the time you return from this read/write, the device will be reconfigured.

Quoted 20 lines
Quoted 20 lines

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

Quoted 6 lines
Quoted 5 lines
Quoted 1 line

I have it disabled (set_affinity does nothing) in my v4 patch.

It would be good if you could try the above approach. It shouldn't be hard to write, and it would be a lot better than just ignoring the problem.

Yes. I am working on this change.

In which case, I'll wait for a v5. No need to spend time on something that is going to change anyway.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#25 lore raw

Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 24 lines · On Friday 17 April 2015 02:50:07 Duc Dang wrote:
drivers/pci/host/pci-xgene-msi.c new file +23 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */

drivers/pci/host/pci-xgene-msi.c line 268

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }

As the MSI is forwarded to the GIC here, how do you maintain ordering between DMA data getting forwarded from the PCI host bridge to RAM with regard to the MSI handler getting entered from this code?

Arnd

author
#26 lore raw

Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 36 lines · Arnd Bergmann
Quoted 24 lines · On Friday 17 April 2015 02:50:07 Duc Dang wrote:
drivers/pci/host/pci-xgene-msi.c new file +23 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */

drivers/pci/host/pci-xgene-msi.c line 268

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
Quoted 3 lines

As the MSI is forwarded to the GIC here, how do you maintain ordering between DMA data getting forwarded from the PCI host bridge to RAM with regard to the MSI handler getting entered from this code?

When device perform a DMA transfer, the order of PCIE inbound requests will be like this:
1. DMA data get transferred via PCIe inbound request
2. After devices issue DMA transfer request, the device fires an MSI interrupt by issuing another inbound write to write MSI data to MSI termination address.

As these 2 requests are transferred via PCIe bus in order, the DMA data will be all in DDR before the MSI data hit the termination address to trigger the MSI handler in interrupt handler code.

Quoted 2 lines

Arnd

Regards,
Duc Dang.

#27 lore raw

Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 48 lines · On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
Quoted 36 lines · Arnd Bergmann
Quoted 24 lines · On Friday 17 April 2015 02:50:07 Duc Dang wrote:
drivers/pci/host/pci-xgene-msi.c new file +23 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */

drivers/pci/host/pci-xgene-msi.c line 268

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
Quoted 3 lines

As the MSI is forwarded to the GIC here, how do you maintain ordering between DMA data getting forwarded from the PCI host bridge to RAM with regard to the MSI handler getting entered from this code?

When device perform a DMA transfer, the order of PCIE inbound requests will be like this:
1. DMA data get transferred via PCIe inbound request
2. After devices issue DMA transfer request, the device fires an MSI interrupt by issuing another inbound write to write MSI data to MSI termination address.

Quoted 3 lines

As these 2 requests are transferred via PCIe bus in order, the DMA data will be all in DDR before the MSI data hit the termination address to trigger the MSI handler in interrupt handler code.

Obviously they appear on the PCI host bridge in order, because that is a how PCI works. My question was about what happens then. On a lot of SoCs, there is something like an AXI bus that uses posted transactions between PCI and RAM, so you have a do a full manual syncronization of ongoing PIC DMAs when the MSI catcher signals the top-level interrupt. Do you have a bus between PCI and RAM that does not require this, or does the MSI catcher have logic to flush all DMAs?

Arnd

#28 lore raw

Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

Quoted 53 lines · Arnd Bergmann
Quoted 48 lines · On Sunday 19 April 2015 11:40:09 Duc Dang wrote:
Quoted 36 lines · Arnd Bergmann
Quoted 24 lines · On Friday 17 April 2015 02:50:07 Duc Dang wrote:
drivers/pci/host/pci-xgene-msi.c new file +23 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */

drivers/pci/host/pci-xgene-msi.c line 268

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;
+               }
+               grp_select &= ~(1 << msir_index);
+       }
Quoted 3 lines

As the MSI is forwarded to the GIC here, how do you maintain ordering between DMA data getting forwarded from the PCI host bridge to RAM with regard to the MSI handler getting entered from this code?

When device perform a DMA transfer, the order of PCIE inbound requests will be like this:
1. DMA data get transferred via PCIe inbound request
2. After devices issue DMA transfer request, the device fires an MSI interrupt by issuing another inbound write to write MSI data to MSI termination address.

Quoted 3 lines

As these 2 requests are transferred via PCIe bus in order, the DMA data will be all in DDR before the MSI data hit the termination address to trigger the MSI handler in interrupt handler code.

Quoted 7 lines

Obviously they appear on the PCI host bridge in order, because that is a how PCI works. My question was about what happens then. On a lot of SoCs, there is something like an AXI bus that uses posted transactions between PCI and RAM, so you have a do a full manual syncronization of ongoing PIC DMAs when the MSI catcher signals the top-level interrupt. Do you have a bus between PCI and RAM that does not require this, or does the MSI catcher have logic to flush all DMAs?

Our hardware has an automatic mechanism to flush the content to DRAM before the MSI write is committed.

Quoted 6 lines

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#37 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 260 lines · Marc Zyngier
Quoted 259 lines · Duc Dang
Quoted 258 lines · Marc Zyngier
Quoted 216 lines · Duc Dang
Quoted 148 lines · Marc Zyngier
Quoted 122 lines · On Tue, 14 Apr 2015 19:20:19 +0100 Duc Dang <[email protected]> wrote:
Quoted 120 lines · Marc Zyngier
Quoted 117 lines · Duc Dang
Quoted 102 lines · Marc Zyngier
Quoted 100 lines · Duc Dang

X-Gene v1 SoC supports total 2688 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 407
+++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 435 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +2 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM

X-Gene SoC. There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.

drivers/pci/host/Kconfig +4 -0
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
(quoted diff) +0 -0

b/drivers/pci/host/Makefile index e61d91c..f39bde3 100644

drivers/pci/host/Makefile +0 -0
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) +=

pcie-spear13xx.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o

drivers/pci/host/Makefile +1 -0
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +8 -0
new file mode 100644
index 0000000..4f0ff42
@@ -0,0 +1,407 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it

and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */

drivers/pci/host/pci-xgene-msi.c new file +22 -0
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_INDEX0             0x000000
+#define MSI_INT0               0x800000
+
+struct xgene_msi_settings {
+       u32     index_per_group;
+       u32     irqs_per_index;
+       u32     nr_msi_vec;
+       u32     nr_hw_irqs;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       struct xgene_msi_settings       *settings;
+       u32                             msi_addr_lo;
+       u32                             msi_addr_hi;

I'd rather see the mailbox address directly, and only do the split when assigning it to the message (you seem to play all kind of tricks on the address anyway).

msi_addr_lo and msi_addr_hi store the physical base address of MSI controller registers. I will add comment to clarify this.

Quoted 8 lines
Quoted 3 lines

What I mean is that there is no point in keeping this around as a pair of 32bit variables. You'd better keep it as a single 64bit, and do the split when assigning it the the MSI message.

Hi Marc,

These came from device-tree (which describes 64-bit address number as 2 32-bit words).

... and converted to a resource as a 64bit word, on which you apply {upper,lower}_32_bit(). So much for DT...

Quoted 3 lines

If I store them this way, I don't need CPU cycles to do the split every time assigning them to the MSI message. Please let me know what do you think about it.

This is getting absolutely silly.

How many cycles does it take to execute "lsr x1, x0, #32" on X-Gene? If it takes so long that it is considered to be a bottleneck, I suggest you go and design a better CPU (hint: the answer is probably 1 cycle absolutely everywhere).

Quoted 7 lines

How often are you configuring MSIs in the face of what is happening in the rest of the kernel? Almost never!

So, given that "never" times 1 is still never, I'll consider that readability of the code trumps it anytime (I can't believe we're having that kind of conversation...).

I changed to use u64 for msi_addr and split it when composing MSI messages. The change is in v4 of the patch set that I just posted.

Quoted 50 lines
Quoted 37 lines
Quoted 29 lines

[...]

Quoted 19 lines
Quoted 9 lines
Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask,

bool force)

drivers/pci/host/pci-xgene-msi.c new file +2 -0
+{
+       struct xgene_msi *msi =

irq_data_get_irq_chip_data(irq_data);

drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       unsigned int gic_irq;
+       int ret;
+
Quoted 8 lines
Quoted 3 lines

+ gic_irq = msi->msi_virqs[irq_data->hwirq %
msi->settings->nr_hw_irqs];
+ ret = irq_set_affinity(gic_irq, mask);

Erm... This as the effect of moving *all* the MSIs hanging off this interrupt to another CPU. I'm not sure that's an acceptable effect... What if another MSI requires a different affinity?

Quoted 5 lines

We have 16 'real' hardware IRQs. Each of these has multiple MSIs attached to it.
So this will move all MSIs handing off this interrupt to another CPU; and we don't support different affinity settings for different MSIs that are attached to the same hardware IRQ.

Quoted 7 lines

Well, that's a significant departure from the expected behaviour. In other words, I wonder how useful this is. Could you instead reconfigure the MSI itself to hit the right CPU (assuming you don't have more than 16 CPUs and if
that's only for XGene-1, this will only be 8 at most)? This would reduce your number of possible MSIs, but at least the semantics of set_afinity would be preserved.

Quoted 3 lines

X-Gene-1 supports 2688 MSIs that are divided into 16 groups, each group has 168 MSIs that are mapped to 1 hardware GIC IRQ (so we have 16 hardware GIC IRQs for 2688 MSIs).

We've already established that.

Quoted 6 lines

Setting affinity of single MSI to deliver it to a target CPU will move all the MSIs mapped to the same GIC IRQ to that CPU as well. This is not a standard behavior, but limiting the total number of MSIs will cause a lot of devices to fall back to INTx (with huge performance penalty) or even fail to load their driver as these devices request more than 16 MSIs during driver initialization.

Quoted 8 lines

No, I think you got it wrong. If you have 168 MSIs per GIC IRQ, and provided that you have 8 CPUs (XGene-1), you end up with 336 MSIs per CPU (having statically assigned 2 IRQs per CPU).

Assuming you adopt my scheme, you still have a grand total of 336 MSIs that can be freely moved around without breaking any userspace expectations.

Quoted 8 lines

Thanks Marc. This is a very good idea.

But to move MSIs around, I need to change MSI termination address and data and write them to device configuration space. This may cause problems if the device
fires an interrupt at the same time when I do the config write?

What is your opinion here?

There is an inherent race when changing the affinity of any interrupt, whether that's an MSI or not. The kernel is perfectly prepared to handle such a situation (to be honest, the kernel doesn't really care).

The write to the config space shouldn't be a concern. You will either hit the old *or* the new CPU, but that race is only during the write itself (you can read back from the config space if you're really paranoid). By the time you return from this read/write, the device will be reconfigured.

Quoted 21 lines
Quoted 11 lines

I think that 336 MSIs is a fair number (nowhere near the 16 you claim). Most platforms are doing quite well with that kind of numbers. Also, you don't have to allocate all the MSIs a device can possibly claim (up to 2048 MSI-X per device), as they are all perfectly capable of using less MSI without having to fallback to INTx).

Quoted 4 lines

I can document the limitation in affinity setting of X-Gene-1 MSI in the driver to hopefully not make people surprise and hope to keep the total number of supported MSI as 2688 so that we can support as many cards that require MSI/MSI-X as possible.

Quoted 9 lines

I don't think this is a valid approach. This breaks userspace (think of things like irqbalance), and breaks the SMP affinity model that Linux uses. No amount of documentation is going to solve it, so I think you just have to admit that the HW is mis-designed and do the best you can to make it work like Linux expect it to work.

The alternative would to disable affinity setting altogether instead of introducing these horrible side effects.

I have it disabled (set_affinity does nothing) in my v4 patch.

Quoted 7 lines
Quoted 4 lines
Quoted 3 lines

It would be good if you could try the above approach. It shouldn't be hard to write, and it would be a lot better than just ignoring the problem.

Yes. I am working on this change.

In which case, I'll wait for a v5. No need to spend time on something that is going to change anyway.

Marc:

Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI driver. Or is this going to be removed in the future. Thanks

Quoted 6 lines

Thanks,

        M.

--
Jazz is not dead. It just smells funny...

author
#39 lore raw

v5 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

In reply to Marc Zyngier

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

v5 changes:
1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain
v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  63 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   6 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 477 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 603 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#40 lore raw

PATCH v5 1/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 505 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +477 -0

477 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#77 lore raw

PATCH v5 2/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#80 lore raw

PATCH v5 3/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

In reply to Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +63 -0
new file mode 100644
index 0000000..0ffdcb3
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.
+- interrupts: A list of interrupt outputs of the controller.
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#83 lore raw

PATCH v5 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
#29 lore raw

Re: [PATCH v4 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Feng Kan
Quoted 11 lines · On Monday 20 April 2015 11:49:37 Feng Kan wrote:
Quoted 8 lines

Obviously they appear on the PCI host bridge in order, because that is a how PCI works. My question was about what happens then. On a lot of SoCs, there is something like an AXI bus that uses posted transactions between PCI and RAM, so you have a do a full manual syncronization of ongoing PIC DMAs when the MSI catcher signals the top-level interrupt. Do you have a bus between PCI and RAM that does not require this, or does the MSI catcher have logic to flush all DMAs?

Our hardware has an automatic mechanism to flush the content to DRAM before the MSI write is committed.

Ok, excellent!

Arnd

#38 lore raw

Re: [PATCH v3 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Feng Kan
Quoted 2 lines · Feng Kan

Is it possible to use the pci_msi_create_default_irq_domain for the X-Gene MSI driver. Or is this going to be removed in the future. Thanks

This is not the preferred way. pci_msi_create_default_irq_domain is only there for legacy purposes, so that we can lookup an MSI without specifying the domain. New code shouldn't have to rely on it.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#41 lore raw

Re: [PATCH v5 1/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 2 lines · Duc Dang

X-Gene v1 SoC supports total 256 MSI/MSIX vectors coalesced into 16 HW IRQ lines.

This commit message could be beefed up to describe what this really does.

Quoted 156 lines
---
 drivers/pci/host/Kconfig         |   6 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 477 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 505 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +6 -0
index 7b892a9..c9b61fa 100644
@@ -89,11 +89,17 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +107 -0
new file mode 100644
index 0000000..910f5db
@@ -0,0 +1,477 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+       int                             num_cpus;
+};
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       u32 group = data->hwirq % NR_HW_IRQS;
+

drivers/pci/host/pci-xgene-msi.c line 117

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       msg->address_hi = upper_32_bits(msi->msi_addr);
+       msg->address_lo = lower_32_bits(msi->msi_addr) +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocate to 8 X-Gene

nit: allocated

drivers/pci/host/pci-xgene-msi.c line 118

Quoted 1 line
drivers/pci/host/pci-xgene-msi.c new file +1 -0
+ * v1 cores (2 GIC IRQs for each cores). The MSI vector is moved fom 1

nit: core

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)

drivers/pci/host/pci-xgene-msi.c line 134

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+       struct msi_msg msg;
+       u32 reg_set, group;
+
+       if (!desc)
+               return IRQ_SET_MASK_OK_DONE;

That can't be right. You really should return an error code here.

Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+       reg_set = irq_data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       group = irq_data->hwirq % NR_HW_IRQS;
+
+       /* Prepare new MSI msg to steer MSI to target CPU */

drivers/pci/host/pci-xgene-msi.c line 154

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       msg.address_hi = upper_32_bits(msi->msi_addr);
+       msg.address_lo = lower_32_bits(msi->msi_addr) +
+               (((8 * group) + reg_set) << 16);
+       msg.data = (irq_data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+       /*
+        * Write new MSI termination address and data into
+        * PCIe device configuration space
+        */
+       pci_write_msi_msg(irq_data->irq, &msg);
+       return IRQ_SET_MASK_OK_DONE;

That feels like the wrong thing to do. You are duplicating a functionality that already exists in msi_domain_set_affinity.

You should probably just update hwirq to reflect the new absolute MSI number, return IRQ_SET_MASK_OK and let compose_msi_msg/write_msi_msg do the right thing.

Quoted 20 lines
drivers/pci/host/pci-xgene-msi.c new file +20 -0
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+       int i;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = find_first_zero_bit(msi->bitmap, NR_MSI_VEC);
+       if (msi_irq < NR_MSI_VEC)
+               set_bit(msi_irq, msi->bitmap);

drivers/pci/host/pci-xgene-msi.c line 184

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       else
+               msi_irq = -ENOSPC;
+
+       /* Check if we found a valid MSI */
+       if ((msi_irq >= 0) && ((msi_irq % msi->num_cpus) == 0)) {
+               /* Allocate other vectors as well */
+               for (i = 1; i < msi->num_cpus; i++) {
+                       if ((msi_irq + i) >= NR_MSI_VEC) {
+                               msi_irq = -ENOSPC;
+                               break;

Eh? Right in the middle of the allocation? You end up with an inconsistent bitmap, don't you?

Consider using bitmap_find_next_zero_area() to allocate a chunk of 8 consecutive bits, which will give you the MSI for your 8 CPUs.

drivers/pci/host/pci-xgene-msi.c line 190

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+                       }
+                       if (!test_bit((msi_irq + i), msi->bitmap))
+                               set_bit(msi_irq + i, msi->bitmap);
+                       else {
+                               msi_irq = -ENOSPC;
+                               break;

Same breakage.

Quoted 21 lines
drivers/pci/host/pci-xgene-msi.c new file +21 -0
+                       }
+               }
+       }
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);

drivers/pci/host/pci-xgene-msi.c line 221

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       int i;
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+       for (i = 0; i < msi->num_cpus; i++)
+               if (test_bit(hwirq + i, msi->bitmap))
+                       clear_bit(hwirq + i, msi->bitmap);

bitmap_release_region()?

Quoted 58 lines
drivers/pci/host/pci-xgene-msi.c new file +58 -0
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);

drivers/pci/host/pci-xgene-msi.c line 290

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp, processed = 0;
+
+       chained_irq_enter(chip, desc);
+
+       xgene_msi = irq_desc_get_handler_data(desc);
+
+       msi_grp = irq - xgene_msi->msi_virqs[0];

Arghh. Absolutely nothing guarantees that your 16 IRQs are contiguous in the virtual space. This only works by pure luck. Why don't you use the handler data to contain your group? Here, you're storing you xgene_msi structure, which is unique anyway.

drivers/pci/host/pci-xgene-msi.c line 301

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+       if (msi_grp >= NR_HW_IRQS) {
+               chained_irq_exit(chip, desc);
+               return;
+       }
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl(xgene_msi->msi_regs + MSI_INT0 + (msi_grp << 16));

Can't you use readl_relaxed?

drivers/pci/host/pci-xgene-msi.c line 310

Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl(xgene_msi->msi_regs + MSI_IR0 + msir_reg);

Same here?

drivers/pci/host/pci-xgene-msi.c line 321

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);

Maybe a comment indicating that the domain is always looked up with the hwirq as seen from CPU0?

drivers/pci/host/pci-xgene-msi.c line 325

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+                       processed++;

What's the purpose of processed? Seems fairly useless to me.

Quoted 5 lines
drivers/pci/host/pci-xgene-msi.c new file +5 -0
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       chained_irq_exit(chip, desc);

drivers/pci/host/pci-xgene-msi.c line 340

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)

Under which circumstances could that be zero?

drivers/pci/host/pci-xgene-msi.c line 347

Quoted 7 lines
drivers/pci/host/pci-xgene-msi.c new file +7 -0
+                       free_irq(virq, msi);
+       }
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);

Who frees msi->msi_virqs?

Quoted 24 lines
drivers/pci/host/pci-xgene-msi.c new file +24 -0
+       return 0;
+}
+
+static int xgene_msi_setup_hwirq(struct xgene_msi *msi,
+                                struct platform_device *pdev,
+                                int irq_index)
+{
+       int virt_msir;
+       cpumask_var_t mask;
+       int err;
+
+       virt_msir = platform_get_irq(pdev, irq_index);
+       if (virt_msir < 0) {
+               dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                       irq_index);
+               return -EINVAL;
+       }
+
+       irq_set_chained_handler(virt_msir, xgene_msi_isr);
+       err = irq_set_handler_data(virt_msir, msi);
+       if (err) {
+               dev_err(&pdev->dev, "request irq failed\n");
+               return err;
+       }

drivers/pci/host/pci-xgene-msi.c line 382

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+       /*
+        * Statically allocate MSI GIC IRQs to each CPU core
+        * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+        * to each core.
+        */
+       if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+               cpumask_clear(mask);
+               cpumask_set_cpu(irq_index % msi->num_cpus, mask);
+               irq_set_affinity(virt_msir, mask);

How about testing the return value of irq_set_affinity?

Quoted 15 lines
drivers/pci/host/pci-xgene-msi.c new file +15 -0
+               free_cpumask_var(mask);
+       }
+
+       msi->msi_virqs[irq_index] = virt_msir;
+
+       return 0;
+}
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{

drivers/pci/host/pci-xgene-msi.c line 407

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       struct resource *res;
+       int rc, irq_index;
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       struct xgene_msi *xgene_msi;
+
+       np = of_find_matching_node_and_match(NULL,
+                            xgene_msi_match_table, &matched_np);
+       if (!np)
+               return -ENODEV;

This is getting silly, really. You already have the device node pointed to by pdev...

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+
+       xgene_msi = kzalloc(sizeof(struct xgene_msi), GFP_KERNEL);
+       if (!xgene_msi) {
+               dev_err(&pdev->dev, "failed to allocate X-Gene MSI data\n");
+               return -ENOMEM;
+       }
+
+       xgene_msi->node = np;
+
+       platform_set_drvdata(pdev, xgene_msi);
+

drivers/pci/host/pci-xgene-msi.c line 428

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_online_cpus();

How are you going to support CPU hotplug? This probably should be num_possible_cpus(), and you will need a CPU notifier to handle CPUs being brought up.

Quoted 88 lines
drivers/pci/host/pci-xgene-msi.c new file +48 -0
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               rc = xgene_msi_setup_hwirq(xgene_msi, pdev, irq_index);
+               if (rc)
+                       goto error;
+       }
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

Looks like there is some more work to do...

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#78 lore raw

Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang
Quoted 13 lines · Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +0 -0
index f1ad9c2..4b719c9 100644

arch/arm64/boot/dts/apm/apm-storm.dtsi line 360

Quoted 7 lines
arch/arm64/boot/dts/apm/apm-storm.dtsi +4 -0
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;

I've been repeatedly puzzled by the size of this region. In patch 1, you say:

+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:

+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000

which implies that the size of the region is 0x800000. Or is there something hidden in the last 16 64k pages?

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#81 lore raw

Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

In reply to Duc Dang
Quoted 1 line · Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Please provide a bit of description of what this device is, and please place the binding patch _before_ the driver and DTS patches.

Quoted 13 lines
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +0 -0
new file mode 100644
index 0000000..0ffdcb3

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 5

Quoted 5 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +5 -0
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.

What does the core have to do with the MSI controller?

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 8

Quoted 3 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +3 -0
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.

How many? Which ones? In which order? Do you need reg-names?

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 9

Quoted 1 line
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +1 -0
+- interrupts: A list of interrupt outputs of the controller.

How many? Which ones? In which order? Do you need interrupt-names?

You need to define these for *this particular binding*, in order for them to actually define the contract. An abstract definition is completely useless for writing or parsing a DT, and as such this document is just noise.

Please think about the purpose of this document, and write something appropriate.

[...]

Quoted 6 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +6 -0
+		interrupts = <  0x0 0x10 0x4
+				0x0 0x11 0x4
+				0x0 0x12 0x4
+				0x0 0x13 0x4
+				0x0 0x14 0x4
+				0x0 0x15 0x4

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 37

Quoted 10 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +10 -0
+				0x0 0x16 0x4
+				0x0 0x17 0x4
+				0x0 0x18 0x4
+				0x0 0x19 0x4
+				0x0 0x1a 0x4
+				0x0 0x1b 0x4
+				0x0 0x1c 0x4
+				0x0 0x1d 0x4
+				0x0 0x1e 0x4
+				0x0 0x1f 0x4>;

Nit: please bracket list entries individually.

Mark.

author
#82 lore raw

Re: [PATCH v5 3/4] documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

Quoted 6 lines · Mark Rutland
Quoted 1 line · Duc Dang

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

Please provide a bit of description of what this device is, and please place the binding patch _before_ the driver and DTS patches.

I will add in next version of the patch.

Quoted 9 lines
Quoted 9 lines
---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Quoted 13 lines

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 5

Quoted 11 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +5 -0
new file mode 100644
index 0000000..0ffdcb3
@@ -0,0 +1,63 @@
+* AppliedMicro X-Gene PCIe MSI interface
+
+Required properties:
+
+- compatible: should contain "apm,xgene1-msi" to identify the core.

What does the core have to do with the MSI controller?

'core' here is the MSI controller block. I will use different word to avoid confusion with CPU core.

Quoted 6 lines

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 8

Quoted 4 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +3 -0
+- msi-controller: indicates that this is X-Gene1 PCIe MSI controller node
+- reg: A list of physical base address and length for each set of controller
+       registers.

How many? Which ones? In which order? Do you need reg-names?

I will add these details in next version of the patch.

Quoted 2 lines

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 9

Quoted 2 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +1 -0
+- interrupts: A list of interrupt outputs of the controller.

I will add these details in next version of the patch.

Quoted 2 lines

How many? Which ones? In which order? Do you need interrupt-names?

I will add these details in next version of the patch.

Quoted 28 lines

You need to define these for *this particular binding*, in order for them to actually define the contract. An abstract definition is completely useless for writing or parsing a DT, and as such this document is just noise.

Please think about the purpose of this document, and write something appropriate.

[...]

Quoted 6 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +6 -0
+             interrupts = <  0x0 0x10 0x4
+                             0x0 0x11 0x4
+                             0x0 0x12 0x4
+                             0x0 0x13 0x4
+                             0x0 0x14 0x4
+                             0x0 0x15 0x4

Documentation/devicetree/bindings/pci/xgene-pci-msi.txt line 37

Quoted 10 lines
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +10 -0
+                             0x0 0x16 0x4
+                             0x0 0x17 0x4
+                             0x0 0x18 0x4
+                             0x0 0x19 0x4
+                             0x0 0x1a 0x4
+                             0x0 0x1b 0x4
+                             0x0 0x1c 0x4
+                             0x0 0x1d 0x4
+                             0x0 0x1e 0x4
+                             0x0 0x1f 0x4>;
Quoted 1 line

Nit: please bracket list entries individually.

Thanks for your comment, I will address them on next version of the patch.

Quoted 2 lines

Mark.

Regards,
Duc Dang.

author
#79 lore raw

Re: [PATCH v5 2/4] arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Marc Zyngier
Quoted 44 lines · Marc Zyngier
Quoted 13 lines · Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +0 -0
index f1ad9c2..4b719c9 100644

arch/arm64/boot/dts/apm/apm-storm.dtsi line 360

Quoted 7 lines
arch/arm64/boot/dts/apm/apm-storm.dtsi +4 -0
@@ -354,6 +354,28 @@
                      };
              };
 
+             msi: msi at 79000000 {
+                     compatible = "apm,xgene1-msi";
+                     msi-controller;
+                     reg = <0x00 0x79000000 0x0 0x900000>;

I've been repeatedly puzzled by the size of this region. In patch 1, you say:

+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:

+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
Quoted 2 lines

which implies that the size of the region is 0x800000. Or is there something hidden in the last 16 64k pages?

The registers listed in the first patch are termination registers.

Quoted 1 line

From offset 0x800000 to 0x8f0000, the MSI controller provides status

registers for software to check if there is pending MSI interrupt in each MSI group (in the code we read MSI_INTx registers starting from 0x800000 to check if there is an MSI interrupt pending).

Quoted 6 lines

Thanks,

        M.

--
Jazz is not dead. It just smells funny...

Regards,
Duc Dang.

#84 lore raw
In reply to Duc Dang
Quoted 9 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

Quick note that I have tested the previous several versions of this patch series on several platforms and will test v6 once available with the pending feedback addressed.

Jon.

#85 lore raw
In reply to Duc Dang
Quoted 9 lines · Duc Dang

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

Quick note that I have tested the previous several versions of this patch series on several platforms and will test v6 once available with the pending feedback addressed.

Jon.

author
#42 lore raw

v6 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

In reply to Marc Zyngier

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

v6 changes:
1. Correctly allocate MSI with bitmap_find_next_zero_area
2. Add notifier for CPU hotplug case
3. Driver clean up
4. Add more detailed information into device tree binding document and move the device tree binding patch before the driver/dts patch

v5 changes:
1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain
v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 ++
 drivers/pci/host/Kconfig                           |   8 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 504 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 637 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#43 lore raw

PATCH v6 1/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +68 -0
new file mode 100644
index 0000000..36d881c
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#44 lore raw

PATCH v6 2/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |   8 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 534 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +8 -0
index 7b892a9..9f1e2b5 100644
@@ -89,11 +89,19 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +504 -0

504 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#75 lore raw

PATCH v6 3/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#76 lore raw

PATCH v6 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
#45 lore raw

Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 190 lines · On Wed, 22 Apr 2015 07:15:09 +0100 Duc Dang <[email protected]> wrote:

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |   8 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 534 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +8 -0
index 7b892a9..9f1e2b5 100644
@@ -89,11 +89,19 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       help
+         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +126 -0
new file mode 100644
index 0000000..8bbf925
@@ -0,0 +1,504 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+       int                             num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       u32 group = data->hwirq % NR_HW_IRQS;
+
+       msg->address_hi = upper_32_bits(msi->msi_addr);
+       msg->address_lo = lower_32_bits(msi->msi_addr) +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */

drivers/pci/host/pci-xgene-msi.c line 136

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       if (!desc)
+               return -EINVAL;

I still don't understand under which circumstances this could ever be NULL. You got here because someone has created this interrupt, and inserted it in the domain. Someone also found a way to reference it, and call this code. It must have been initialized the first place.

Also, nothing uses this variable in what follows, so please drop it unless you can prove that desc can be NULL is that it is unsafe to proceed further.

Quoted 121 lines
drivers/pci/host/pci-xgene-msi.c new file +121 -0
+
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+       int i;
+

drivers/pci/host/pci-xgene-msi.c line 267

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       chained_irq_enter(chip, desc);
+
+       xgene_msi = irq_desc_get_handler_data(desc);
+
+       msi_grp = NR_HW_IRQS;
+       for (i = 0; i < NR_HW_IRQS; i++)
+               if (irq == xgene_msi->msi_virqs[i]) {
+                       msi_grp = i;
+                       break;
+               }

Oh, please!!! In v3, you were giving silly reasons to shave one cycle off a slow path, but you now seem pretty happy to run this loop on the bloody hot path!

I already suggested that you actually save the group in handler_data, and reference the global xgene_msi, since it is guaranteed to be unique. If you really want to pointlessly follow pointers, you could replace your msi_virqs array with an array of:

struct xgene_msi_group {
	struct xgene_msi	*xgene_msi;
	int			gic_irq;
	u32			msi_grp;
};

and make the relevant structure pointer to by desc->handler_data. That will give you the information you need once and for all, without any silly, cycle wasting loop.

drivers/pci/host/pci-xgene-msi.c line 271

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       if (msi_grp >= NR_HW_IRQS) {
+               chained_irq_exit(chip, desc);
+               return;
+       }

And I really can't see how this can ever be valid.

Quoted 26 lines
drivers/pci/host/pci-xgene-msi.c new file +26 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl_relaxed(xgene_msi->msi_regs +
+                                  MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl_relaxed(xgene_msi->msi_regs +
+                                        MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *

drivers/pci/host/pci-xgene-msi.c line 307

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0
+                        */
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);

The (virq == 0) case certainly deserves a warning, because it indicates something went horribly wrong somewhere.

Quoted 33 lines
drivers/pci/host/pci-xgene-msi.c new file +33 -0
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)
+                       free_irq(virq, msi);
+       }
+       kfree(msi->msi_virqs);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static void xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       cpumask_var_t mask;
+       int i;

drivers/pci/host/pci-xgene-msi.c line 350

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       int err;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;
+               if (!msi->msi_virqs[i])
+                       continue;
+
+               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
+               err = irq_set_handler_data(msi->msi_virqs[i], msi);

See? msi is *always* set to &xgene_msi_ctrl. So just encode the group or use a structure similar to the one I've outlined above.

drivers/pci/host/pci-xgene-msi.c line 352

Quoted 2 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+               if (err)
+                       continue;

So we got a critical error that just makes the driver unusable, and yet we silently continue? Sounds like a plan...

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated

drivers/pci/host/pci-xgene-msi.c line 366

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi->msi_virqs[i], mask);
+                       if (err) {
+                               free_irq(msi->msi_virqs[i], msi);
+                               free_cpumask_var(mask);
+                               continue;

Same here...

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+                       }
+                       free_cpumask_var(mask);
+               }
+       }

drivers/pci/host/pci-xgene-msi.c line 380

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       /*
+        * Kernel takes care of moving MSI interrupts that
+        * are steered to this CPU to another online CPU
+        * and freeing the interrupt handlers of GIC IRQs
+        * allocated for this CPU, so simply return here.
+        */

Two things:
- I couldn't find the code that backs this statement. MSIs (actually all interrupts) will indeed be moved, but I can't see how the kernel is going to free GIC interrupt. Care to point me to it?
- Assuming this statement is true, why do we need this function at all?

Quoted 33 lines
drivers/pci/host/pci-xgene-msi.c new file +33 -0
+       return;
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+

drivers/pci/host/pci-xgene-msi.c line 423

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
+       int virt_msir;
+
+       xgene_msi = &xgene_msi_ctrl;
+       xgene_msi->node = pdev->dev.of_node;

The only purpose the xgene_msi->node seems to be an intermediate storage for set mchip.of_node. Why don't you set the latter directly, and drop the former?

Quoted 31 lines
drivers/pci/host/pci-xgene-msi.c new file +31 -0
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);

drivers/pci/host/pci-xgene-msi.c line 464

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_virqs[irq_index] = virt_msir;
+       }
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               xgene_msi_hwirq_alloc(cpu);

Why does this need to be in the cpu_notifier critical section? What will you do when xgene_msi_hwirq_alloc() fails?

drivers/pci/host/pci-xgene-msi.c line 476

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {

Assuming we fail here...

drivers/pci/host/pci-xgene-msi.c line 485

Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);

... we end-up here. But xgene_msi_remove doesn't unregister the notifier...

Quoted 59 lines
drivers/pci/host/pci-xgene-msi.c new file +19 -0
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

In somehow related news, this is my last review on this code for quite some time. You've posted it 4 times in less than two weeks, right in the middle of the merge window, I've given it a lot of attention, and you've just run out of credits on the reviewing arcade game.

I suggest you spend some quality time with it, polish it as much as you can. and post it again in about three weeks. Don't just make it work. Make it beautiful. Make it something I become madly in love with. By that time, I'll have forgotten about it and maybe I'll be swept off my feet when I come back from holiday.

Thanks,

	M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
author
#46 lore raw

v7 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

v7 changes:
1. Add more error handling cases
2. Clear spurious interrupts that may happen during driver probe
3. Not using free_irq for chained irqs
4. Improve GIC IRQ number look up in chained handler

v6 changes:
1. Correctly allocate MSI with bitmap_find_next_zero_area
2. Add notifier for CPU hotplug case
3. Driver clean up
4. Add more detailed information into device tree binding document and move the device tree binding patch before the driver/dts patch

v5 changes:
1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain
v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 550 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 685 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#47 lore raw

PATCH v7 1/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +68 -0
new file mode 100644
index 0000000..36d881c
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#48 lore raw

PATCH v7 2/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 582 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 7b892a9..0932118 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +550 -0

550 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#72 lore raw

PATCH v7 3/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#73 lore raw

PATCH v7 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
author
#74 lore raw

Re: [PATCH v6 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 210 lines · Marc Zyngier
Quoted 190 lines · On Wed, 22 Apr 2015 07:15:09 +0100 Duc Dang <[email protected]> wrote:

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |   8 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 504 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 534 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +8 -0
index 7b892a9..9f1e2b5 100644
@@ -89,11 +89,19 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       help
+         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +126 -0
new file mode 100644
index 0000000..8bbf925
@@ -0,0 +1,504 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       int                             *msi_virqs;
+       int                             num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       u32 group = data->hwirq % NR_HW_IRQS;
+
+       msg->address_hi = upper_32_bits(msi->msi_addr);
+       msg->address_lo = lower_32_bits(msi->msi_addr) +
+                         (((8 * group) + reg_set) << 16);
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */

drivers/pci/host/pci-xgene-msi.c line 136

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       struct msi_desc *desc = irq_get_msi_desc(irq_data->irq);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       if (!desc)
+               return -EINVAL;

I still don't understand under which circumstances this could ever be NULL. You got here because someone has created this interrupt, and inserted it in the domain. Someone also found a way to reference it, and call this code. It must have been initialized the first place.

Quoted 4 lines

Also, nothing uses this variable in what follows, so please drop it unless you can prove that desc can be NULL is that it is unsafe to proceed further.

I drop this in v7 patch.

Quoted 141 lines
Quoted 121 lines
drivers/pci/host/pci-xgene-msi.c new file +121 -0
+
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.of_node = msi->node;
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_virqs = kcalloc(NR_HW_IRQS, sizeof(int), GFP_KERNEL);
+       if (!xgene_msi->msi_virqs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+       int i;
+

drivers/pci/host/pci-xgene-msi.c line 267

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       chained_irq_enter(chip, desc);
+
+       xgene_msi = irq_desc_get_handler_data(desc);
+
+       msi_grp = NR_HW_IRQS;
+       for (i = 0; i < NR_HW_IRQS; i++)
+               if (irq == xgene_msi->msi_virqs[i]) {
+                       msi_grp = i;
+                       break;
+               }

Oh, please!!! In v3, you were giving silly reasons to shave one cycle off a slow path, but you now seem pretty happy to run this loop on the bloody hot path!

I already suggested that you actually save the group in handler_data, and reference the global xgene_msi, since it is guaranteed to be unique. If you really want to pointlessly follow pointers, you could replace your msi_virqs array with an array of:

Quoted 6 lines
struct xgene_msi_group {
        struct xgene_msi        *xgene_msi;
        int                     gic_irq;
        u32                     msi_grp;
};

I changed this part as you suggested.

Quoted 10 lines

and make the relevant structure pointer to by desc->handler_data. That will give you the information you need once and for all, without any silly, cycle wasting loop.

drivers/pci/host/pci-xgene-msi.c line 271

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       if (msi_grp >= NR_HW_IRQS) {
+               chained_irq_exit(chip, desc);
+               return;
+       }

And I really can't see how this can ever be valid.

This is removed with the use of msi_grp above.

Quoted 38 lines
Quoted 27 lines
drivers/pci/host/pci-xgene-msi.c new file +26 -0
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl_relaxed(xgene_msi->msi_regs +
+                                  MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl_relaxed(xgene_msi->msi_regs +
+                                        MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *

drivers/pci/host/pci-xgene-msi.c line 307

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0
+                        */
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
Quoted 3 lines

The (virq == 0) case certainly deserves a warning, because it indicates something went horribly wrong somewhere.

A WARN_ON() is added in v7 patch.

Quoted 47 lines
Quoted 33 lines
drivers/pci/host/pci-xgene-msi.c new file +33 -0
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_virqs[i];
+               if (virq != 0)
+                       free_irq(virq, msi);
+       }
+       kfree(msi->msi_virqs);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static void xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       cpumask_var_t mask;
+       int i;

drivers/pci/host/pci-xgene-msi.c line 350

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       int err;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;
+               if (!msi->msi_virqs[i])
+                       continue;
+
+               irq_set_chained_handler(msi->msi_virqs[i], xgene_msi_isr);
+               err = irq_set_handler_data(msi->msi_virqs[i], msi);

See? msi is *always* set to &xgene_msi_ctrl. So just encode the group or use a structure similar to the one I've outlined above.

Quoted 6 lines

drivers/pci/host/pci-xgene-msi.c line 352

Quoted 2 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+               if (err)
+                       continue;

So we got a critical error that just makes the driver unusable, and yet we silently continue? Sounds like a plan...

I added additional error checks in v7 patch.

Quoted 29 lines
Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated

drivers/pci/host/pci-xgene-msi.c line 366

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi->msi_virqs[i], mask);
+                       if (err) {
+                               free_irq(msi->msi_virqs[i], msi);
+                               free_cpumask_var(mask);
+                               continue;

Same here...

Quoted 12 lines
drivers/pci/host/pci-xgene-msi.c new file +12 -0
+                       }
+                       free_cpumask_var(mask);
+               }
+       }
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       /*
+        * Kernel takes care of moving MSI interrupts that
+        * are steered to this CPU to another online CPU
+        * and freeing the interrupt handlers of GIC IRQs
Quoted 9 lines

drivers/pci/host/pci-xgene-msi.c line 380

Quoted 2 lines
drivers/pci/host/pci-xgene-msi.c new file +2 -0
+        * allocated for this CPU, so simply return here.
+        */

Two things:
- I couldn't find the code that backs this statement. MSIs (actually all interrupts) will indeed be moved, but I can't see how the kernel is going to free GIC interrupt. Care to point me to it?
- Assuming this statement is true, why do we need this function at all?

I was wrong here. I was using free_irq for chained irq and got an compain about freeing already freed irq.

I changed to use irq_set_chained_handler(virq, NULL) to mask chained irq in v7 patch.

Quoted 40 lines
Quoted 40 lines
drivers/pci/host/pci-xgene-msi.c new file +39 -0
+       return;
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
Quoted 9 lines

drivers/pci/host/pci-xgene-msi.c line 423

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       int virt_msir;
+
+       xgene_msi = &xgene_msi_ctrl;
+       xgene_msi->node = pdev->dev.of_node;

The only purpose the xgene_msi->node seems to be an intermediate storage for set mchip.of_node. Why don't you set the latter directly, and drop the former?

This assignment is dropped in v7 patch.

Quoted 36 lines
Quoted 36 lines
drivers/pci/host/pci-xgene-msi.c new file +36 -0
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_virqs[irq_index] = virt_msir;
+       }
Quoted 9 lines

drivers/pci/host/pci-xgene-msi.c line 464

Quoted 5 lines
drivers/pci/host/pci-xgene-msi.c new file +5 -0
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               xgene_msi_hwirq_alloc(cpu);

Why does this need to be in the cpu_notifier critical section? What will you do when xgene_msi_hwirq_alloc() fails?

I followed the instruction in Documentation/cpu-hotplug.txt to register a hotplug callback, as well as perform initialization for CPUs that are already online. xgene_msi_hwirq_alloc registers and assigns IRQs to each CPU separately, so it is also safe to move it out of the cpu_notifier critical section.

I add the error check if xgene_msi_hwirq_alloc fails in v7 patch.

Quoted 14 lines

drivers/pci/host/pci-xgene-msi.c line 476

Quoted 11 lines
drivers/pci/host/pci-xgene-msi.c new file +11 -0
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {

Assuming we fail here...

Quoted 12 lines

drivers/pci/host/pci-xgene-msi.c line 485

Quoted 9 lines
drivers/pci/host/pci-xgene-msi.c new file +9 -0
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+error:
+       xgene_msi_remove(pdev);

... we end-up here. But xgene_msi_remove doesn't unregister the notifier...

Code to unregister the notifier is added in v7 patch.

Quoted 74 lines
Quoted 60 lines
drivers/pci/host/pci-xgene-msi.c new file +19 -0
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

In somehow related news, this is my last review on this code for quite some time. You've posted it 4 times in less than two weeks, right in the middle of the merge window, I've given it a lot of attention, and you've just run out of credits on the reviewing arcade game.

I suggest you spend some quality time with it, polish it as much as you can. and post it again in about three weeks. Don't just make it work. Make it beautiful. Make it something I become madly in love with. By that time, I'll have forgotten about it and maybe I'll be swept off my feet when I come back from holiday.

Quoted 5 lines

Thanks,

        M.

--
Without deviation from the norm, progress is not possible.

Regards,
Duc Dang.

#49 lore raw

Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 176 lines · On Mon, 18 May 2015 10:55:19 +0100 Duc Dang <[email protected]> wrote:

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 582 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 7b892a9..0932118 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       help
+         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+         This MSI driver will provide MSI support for 5 PCIe ports of
+         APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +110 -0
new file mode 100644
index 0000000..648bc8f
@@ -0,0 +1,550 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+       struct xgene_msi                *msi;
+       int                             gic_irq;
+       u32                             msi_grp;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       struct xgene_msi_group          *msi_groups;
+       int                             num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x

drivers/pci/host/pci-xgene-msi.c line 120

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       u32 group = data->hwirq % NR_HW_IRQS;
+
+       msg->address_hi = upper_32_bits(msi->msi_addr);
+       msg->address_lo = lower_32_bits(msi->msi_addr) +
+                         (((8 * group) + reg_set) << 16);

It would be safer to compute the offset from msi_addr, and apply {upper,lower}_32_bits() to the resulting value. Otherwise, you end-up with the wrong upper bits if you cross a 4GB boundary. I know this is not the case here, but it doesn't hurt to be correct.

Quoted 176 lines
drivers/pci/host/pci-xgene-msi.c new file +176 -0
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+                                       sizeof(struct xgene_msi_group),
+                                       GFP_KERNEL);
+       if (!xgene_msi->msi_groups)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi_group *msi_groups;
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+
+       chained_irq_enter(chip, desc);
+
+       msi_groups = irq_desc_get_handler_data(desc);
+       xgene_msi = msi_groups->msi;
+       msi_grp = msi_groups->msi_grp;
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl_relaxed(xgene_msi->msi_regs +
+                                  MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl_relaxed(xgene_msi->msi_regs +
+                                        MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0

drivers/pci/host/pci-xgene-msi.c line 306

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                        */
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       WARN_ON(!virq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+       }

I wonder if you wouldn't be better off resampling MSIINTn here, just in case something else has been made pending in the meantime.

Quoted 27 lines
drivers/pci/host/pci-xgene-msi.c new file +27 -0
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_groups[i].gic_irq;
+               if (virq != 0) {
+                       irq_set_chained_handler(virq, NULL);
+                       irq_set_handler_data(virq, NULL);
+               }
+       }
+       kfree(msi->msi_groups);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)

drivers/pci/host/pci-xgene-msi.c line 343

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       cpumask_var_t mask;
+       int i;
+       int err;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;

Oh please...

	for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {

does the same thing.

Quoted 21 lines
drivers/pci/host/pci-xgene-msi.c new file +21 -0
+
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq,
+                                       xgene_msi_isr);
+               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+               if (err) {
+                       pr_err("failed to register GIC IRQ handler\n");
+                       return -EINVAL;
+               }
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi_group->gic_irq, mask);

drivers/pci/host/pci-xgene-msi.c line 374

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       if (err) {
+                               irq_set_chained_handler(msi_group->gic_irq,
+                                                       NULL);
+                               irq_set_handler_data(msi_group->gic_irq, NULL);
+                               free_cpumask_var(mask);
+                               pr_err("failed to set affinity for GIC IRQ");
+                               return -EINVAL;
+                       }
+                       free_cpumask_var(mask);
+               }

And what happens if alloc_cpumask_var fails?

Quoted 4 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       }
+
+       return 0;
+}

drivers/pci/host/pci-xgene-msi.c line 388

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       int i;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;

Same remark as above.

Quoted 89 lines
drivers/pci/host/pci-xgene-msi.c new file +89 -0
+
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq, NULL);
+               irq_set_handler_data(msi_group->gic_irq, NULL);
+       }
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
+       int virt_msir;
+       u32 msi_reg, msi_val, msi_idx;
+
+       xgene_msi = &xgene_msi_ctrl;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+       }
+

drivers/pci/host/pci-xgene-msi.c line 487

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       /*
+        * MSInIRx registers are read-to-clear, before registering interrupt
+        * handlers, reading all of them to clear all spurious interrupts
+        * that may occur before the driver is probed.
+        */
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
+                       msi_reg = (irq_index << 19) + (msi_idx << 16);
+                       msi_val = readl(xgene_msi->msi_regs +
+                                        MSI_IR0 + msi_reg);

Feels like the above two line could be turned into a small helper (you have the same in your handling code).

Quoted 103 lines
drivers/pci/host/pci-xgene-msi.c new file +63 -0
+               }
+               /* Read MSIINTn to confirm */
+               msi_val = readl(xgene_msi->msi_regs +
+                               MSI_INT0 + (irq_index << 16));
+               if (msi_val) {
+                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+                       rc = -EINVAL;
+                       goto error;
+               }
+       }
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               if (xgene_msi_hwirq_alloc(cpu)) {
+                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+                       cpu_notifier_register_done();
+                       goto error;
+               }
+
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       xgene_msi->mchip.of_node = pdev->dev.of_node;
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error_notifier;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+
+error_notifier:
+       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

This is finally starting to look better (that, or my pink-tainted holiday sunglasses are keeping me in a good mood). Please fix the few nits above, and hopefully this can make it into 4.2 (unless someone else spots something nasty here).

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny.
author
#50 lore raw

v8 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

v8 changes:
1. Add helper to read MSI registers
2. Resample interrupt status of the same group after handling interrupts in that group
3. Error handling in case fail to allocate CPU mask for affinity setting
4. Calculate MSI message address_hi in safer way

v7 changes:
1. Add more error handling cases
2. Clear spurious interrupts that may happen during driver probe
3. Not using free_irq for chained irqs
4. Improve GIC IRQ number look up in chained handler

v6 changes:
1. Correctly allocate MSI with bitmap_find_next_zero_area
2. Add notifier for CPU hotplug case
3. Driver clean up
4. Add more detailed information into device tree binding document and move the device tree binding patch before the driver/dts patch

v5 changes:
1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 573 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 708 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#51 lore raw

PATCH v8 1/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +68 -0
new file mode 100644
index 0000000..36d881c
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#52 lore raw

PATCH v8 2/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 605 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 7b892a9..0932118 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	select PCI_XGENE_MSI if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +573 -0

573 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#69 lore raw

PATCH v8 3/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index f1ad9c2..4b719c9 100644
@@ -354,6 +354,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -375,6 +397,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -398,6 +421,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -421,6 +445,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -444,6 +469,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -467,6 +493,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#70 lore raw

PATCH v8 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index ddc5a8c..a1b119b 100644
@@ -7490,6 +7490,14 @@ L:	linux-pci at vger.kernel.org
 S:	Maintained
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1
author
#71 lore raw

Re: [PATCH v7 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Marc Zyngier
Quoted 186 lines · Marc Zyngier
Quoted 183 lines · On Mon, 18 May 2015 10:55:19 +0100 Duc Dang <[email protected]> wrote:

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 550 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 582 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 7b892a9..0932118 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       help
+         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+         This MSI driver will provide MSI support for 5 PCIe ports of
+         APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +117 -0
new file mode 100644
index 0000000..648bc8f
@@ -0,0 +1,550 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+       struct xgene_msi                *msi;
+       int                             gic_irq;
+       u32                             msi_grp;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       struct xgene_msi_group          *msi_groups;
+       int                             num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);
+       u32 group = data->hwirq % NR_HW_IRQS;
+
Quoted 9 lines

drivers/pci/host/pci-xgene-msi.c line 120

Quoted 3 lines
drivers/pci/host/pci-xgene-msi.c new file +3 -0
+       msg->address_hi = upper_32_bits(msi->msi_addr);
+       msg->address_lo = lower_32_bits(msi->msi_addr) +
+                         (((8 * group) + reg_set) << 16);

It would be safer to compute the offset from msi_addr, and apply {upper,lower}_32_bits() to the resulting value. Otherwise, you end-up with the wrong upper bits if you cross a 4GB boundary. I know this is not the case here, but it doesn't hurt to be correct.

Yes, I changed this in v8 patch.

Quoted 181 lines
Quoted 181 lines
drivers/pci/host/pci-xgene-msi.c new file +181 -0
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+                                       sizeof(struct xgene_msi_group),
+                                       GFP_KERNEL);
+       if (!xgene_msi->msi_groups)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi_group *msi_groups;
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_reg, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+
+       chained_irq_enter(chip, desc);
+
+       msi_groups = irq_desc_get_handler_data(desc);
+       xgene_msi = msi_groups->msi;
+       msi_grp = msi_groups->msi_grp;
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = readl_relaxed(xgene_msi->msi_regs +
+                                  MSI_INT0 + (msi_grp << 16));
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_reg = (msi_grp << 19) + (msir_index << 16);
+               msir_val = readl_relaxed(xgene_msi->msi_regs +
+                                        MSI_IR0 + msir_reg);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0
+                        */
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       WARN_ON(!virq);
+                       if (virq != 0)
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 306

Quoted 5 lines
drivers/pci/host/pci-xgene-msi.c new file +5 -0
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+       }

I wonder if you wouldn't be better off resampling MSIINTn here, just in case something else has been made pending in the meantime.

I added code to read MSI_INTn of the same group again when grp_select becomes 0 (when we handle all the interrupts).

Quoted 41 lines
Quoted 28 lines
drivers/pci/host/pci-xgene-msi.c new file +27 -0
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_groups[i].gic_irq;
+               if (virq != 0) {
+                       irq_set_chained_handler(virq, NULL);
+                       irq_set_handler_data(virq, NULL);
+               }
+       }
+       kfree(msi->msi_groups);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)

drivers/pci/host/pci-xgene-msi.c line 343

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       cpumask_var_t mask;
+       int i;
+       int err;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;

Oh please...

Quoted 3 lines
        for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {

does the same thing.

Thanks, I changed it.

Quoted 26 lines
Quoted 26 lines
drivers/pci/host/pci-xgene-msi.c new file +25 -0
+
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq,
+                                       xgene_msi_isr);
+               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+               if (err) {
+                       pr_err("failed to register GIC IRQ handler\n");
+                       return -EINVAL;
+               }
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi_group->gic_irq, mask);
+                       if (err) {
+                               irq_set_chained_handler(msi_group->gic_irq,
+                                                       NULL);
+                               irq_set_handler_data(msi_group->gic_irq, NULL);
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 374

Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +6 -0
+                               free_cpumask_var(mask);
+                               pr_err("failed to set affinity for GIC IRQ");
+                               return -EINVAL;
+                       }
+                       free_cpumask_var(mask);
+               }

And what happens if alloc_cpumask_var fails?

I add error handling code for this, if this happens, chained handler will be freed and the function returns with error.

Quoted 113 lines
Quoted 6 lines
drivers/pci/host/pci-xgene-msi.c new file +4 -0
+       }
+
+       return 0;
+}

drivers/pci/host/pci-xgene-msi.c line 388

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       int i;
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               if (i % msi->num_cpus != cpu)
+                       continue;

Same remark as above.

Quoted 94 lines
drivers/pci/host/pci-xgene-msi.c new file +94 -0
+
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq, NULL);
+               irq_set_handler_data(msi_group->gic_irq, NULL);
+       }
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
+       int virt_msir;
+       u32 msi_reg, msi_val, msi_idx;
+
+       xgene_msi = &xgene_msi_ctrl;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+       }
+
+       /*
+        * MSInIRx registers are read-to-clear, before registering interrupt
+        * handlers, reading all of them to clear all spurious interrupts
+        * that may occur before the driver is probed.
+        */
Quoted 8 lines

drivers/pci/host/pci-xgene-msi.c line 487

Quoted 5 lines
drivers/pci/host/pci-xgene-msi.c new file +5 -0
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++) {
+                       msi_reg = (irq_index << 19) + (msi_idx << 16);
+                       msi_val = readl(xgene_msi->msi_regs +
+                                        MSI_IR0 + msi_reg);

Feels like the above two line could be turned into a small helper (you have the same in your handling code).

I created 2 helpers to read MSInIRx and MSIINTn registers.

Quoted 112 lines
Quoted 104 lines
drivers/pci/host/pci-xgene-msi.c new file +63 -0
+               }
+               /* Read MSIINTn to confirm */
+               msi_val = readl(xgene_msi->msi_regs +
+                               MSI_INT0 + (irq_index << 16));
+               if (msi_val) {
+                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+                       rc = -EINVAL;
+                       goto error;
+               }
+       }
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               if (xgene_msi_hwirq_alloc(cpu)) {
+                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+                       cpu_notifier_register_done();
+                       goto error;
+               }
+
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       xgene_msi->mchip.of_node = pdev->dev.of_node;
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error_notifier;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+
+error_notifier:
+       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

This is finally starting to look better (that, or my pink-tainted holiday sunglasses are keeping me in a good mood). Please fix the few nits above, and hopefully this can make it into 4.2 (unless someone else spots something nasty here).

Quoted 5 lines

Thanks,

        M.

--
Jazz is not dead. It just smells funny.

Regards,
Duc Dang.

Reviewed-by on v8 2/4
#53 lore raw

Re: [PATCH v8 2/4] PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang
Quoted 193 lines · On Fri, 22 May 2015 19:41:10 +0100 Duc Dang <[email protected]> wrote:

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 573 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 605 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 7b892a9..0932118 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+       select PCI_XGENE_MSI if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       help
+         Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+         This MSI driver will provide MSI support for 5 PCIe ports of
+         APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index e61d91c..f39bde3 100644
@@ -11,5 +11,6 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
drivers/pci/host/pci-xgene-msi.c new file +127 -0
new file mode 100644
index 0000000..0c6823d
@@ -0,0 +1,573 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <[email protected]>
+ *        Duc Dang <[email protected]>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+       struct xgene_msi                *msi;
+       int                             gic_irq;
+       u32                             msi_grp;
+};
+
+struct xgene_msi {
+       struct device_node              *node;
+       struct msi_controller           mchip;
+       struct irq_domain               *domain;
+       u64                             msi_addr;
+       void __iomem                    *msi_regs;
+       unsigned long                   *bitmap;
+       struct mutex                    bitmap_lock;
+       struct xgene_msi_group          *msi_groups;
+       int                             num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The registers layout is like following:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ * MSIINT0                     base_addr + 0x800000
+ * MSIINT1                     base_addr + 0x810000
+ * ...                         ...
+ * MSIINTF                     base_addr + 0x8F0000
+ *
+ * Each index register support 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static inline u32 xgene_msi_ir_read(struct xgene_msi *msi,
+                                   u32 msi_grp, u32 msir_idx)
+{
+       return readl_relaxed(msi->msi_regs + MSI_IR0 +
+                             (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static inline u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/* With 2048 MSI vectors supported, the MSI message can be construct using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32

drivers/pci/host/pci-xgene-msi.c line 137

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = data->hwirq / (NR_HW_IRQS * IRQS_PER_IDX);

Create a hwirq_to_reg_set helper.

drivers/pci/host/pci-xgene-msi.c line 138

Quoted 1 line
drivers/pci/host/pci-xgene-msi.c new file +1 -0
+       u32 group = data->hwirq % NR_HW_IRQS;

Create a hwirq_to_group helper.

drivers/pci/host/pci-xgene-msi.c line 143

Quoted 5 lines
drivers/pci/host/pci-xgene-msi.c new file +5 -0
+
+       msg->address_hi = upper_32_bits(msi->msi_addr +
+                                       (((8 * group) + reg_set) << 16));
+       msg->address_lo = lower_32_bits(msi->msi_addr +
+                                       (((8 * group) + reg_set) << 16));

Rule of thumb: if you write something complicated twice, you've written one time too many:

u64 target_addr = msi->msi_addr + ((8 * group) + reg_set) << 16));

msg->address_hi = upper_32_bits(target_addr);
msg->address_lo = lower_32_bits(target_addr);

It doesn't hurt to write something readable.

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+       msg->data = (data->hwirq / NR_HW_IRQS) % IRQS_PER_IDX;
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.
+ * To maintain the expected behaviour of .set_affinity for each MSI
+ * interrupt, the 16 MSI GIC IRQs are statically allocated to 8 X-Gene
+ * v1 cores (2 GIC IRQs for each core). The MSI vector is moved fom 1
+ * MSI GIC IRQ to another MSI GIC IRQ to steer its MSI interrupt to
+ * correct X-Gene v1 core. As a consequence, the total MSI vectors that

drivers/pci/host/pci-xgene-msi.c line 163

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+ * X-Gene v1 supports will be reduced to 256 (2048/8) vectors.
+ */
+static int xgene_msi_set_affinity(struct irq_data *irq_data,
+                                 const struct cpumask *mask, bool force)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(irq_data);
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       curr_cpu = (irq_data->hwirq % NR_HW_IRQS) % msi->num_cpus;

Create a hwirq_to_cpu helper.

Quoted 43 lines
drivers/pci/host/pci-xgene-msi.c new file +43 -0
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irq_data->hwirq = irq_data->hwirq + (target_cpu - curr_cpu);
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+

drivers/pci/host/pci-xgene-msi.c line 216

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = d->hwirq - (d->hwirq % msi->num_cpus);

Create hwirq_to_canonical_hwirq helper (hwirq - hwirq_to_cpu(hwirq)).

Quoted 92 lines
drivers/pci/host/pci-xgene-msi.c new file +92 -0
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+                                       sizeof(struct xgene_msi_group),
+                                       GFP_KERNEL);
+       if (!xgene_msi->msi_groups)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi_group *msi_groups;
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+
+       chained_irq_enter(chip, desc);
+
+       msi_groups = irq_desc_get_handler_data(desc);
+       xgene_msi = msi_groups->msi;
+       msi_grp = msi_groups->msi_grp;
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg function)
+                */
+               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in

drivers/pci/host/pci-xgene-msi.c line 318

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0
+                        */
+                       hw_irq = hw_irq - (hw_irq % xgene_msi->num_cpus);

hw_irq = hwirq_to_canonical_hwirq(hw_irq);

Quoted 74 lines
drivers/pci/host/pci-xgene-msi.c new file +74 -0
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       WARN_ON(!virq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+
+               if (!grp_select) {
+                       /*
+                        * We handled all interrupts happened in this group,
+                        * resample this group MSI_INTx register in case
+                        * something else has been made pending in the meantime
+                        */
+                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_groups[i].gic_irq;
+               if (virq != 0) {
+                       irq_set_chained_handler(virq, NULL);
+                       irq_set_handler_data(virq, NULL);
+               }
+       }
+       kfree(msi->msi_groups);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       cpumask_var_t mask;
+       int i;
+       int err;
+
+       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq,
+                                       xgene_msi_isr);
+               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+               if (err) {
+                       pr_err("failed to register GIC IRQ handler\n");
+                       return -EINVAL;
+               }
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi_group->gic_irq, mask);
+                       if (err)
+                               pr_err("failed to set affinity for GIC IRQ");

drivers/pci/host/pci-xgene-msi.c line 402

Quoted 10 lines
drivers/pci/host/pci-xgene-msi.c new file +10 -0
+                       free_cpumask_var(mask);
+               } else {
+                       pr_err("failed to alloc CPU mask for affinity\n");
+                       err = -EINVAL;
+               }
+
+               if (err) {
+                       irq_set_chained_handler(msi_group->gic_irq, NULL);
+                       irq_set_handler_data(msi_group->gic_irq, NULL);
+                       return -EINVAL;
			return err;
Quoted 211 lines
drivers/pci/host/pci-xgene-msi.c new file +171 -0
+               }
+       }
+
+       return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       int i;
+
+       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq, NULL);
+               irq_set_handler_data(msi_group->gic_irq, NULL);
+       }
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
+       int virt_msir;
+       u32 msi_val, msi_idx;
+
+       xgene_msi = &xgene_msi_ctrl;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+       }
+
+       /*
+        * MSInIRx registers are read-to-clear, before registering interrupt
+        * handlers, reading all of them to clear all spurious interrupts
+        * that may occur before the driver is probed.
+        */
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+                                                   msi_idx);
+               /* Read MSIINTn to confirm */
+               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+               if (msi_val) {
+                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+                       rc = -EINVAL;
+                       goto error;
+               }
+       }
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               if (xgene_msi_hwirq_alloc(cpu)) {
+                       dev_err(&pdev->dev, "failed to regiser MSI handlers\n");
+                       cpu_notifier_register_done();
+                       goto error;
+               }
+
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       xgene_msi->mchip.of_node = pdev->dev.of_node;
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error_notifier;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+
+error_notifier:
+       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
+
drivers/pci/host/pci-xgene.c +21 -1
index 22751ed..3e6faa1 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (bus->msi)
+               bus->msi->dev = &bus->dev;
+       else
+               return -ENODEV;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
--
Quoted 2 lines

1.9.1

Once you've fixed the couple of nits above, you can add my

You should also CC the irqchip maintainers (Thomas and Jason), as I'd very much like to have their input on this as well. A few "Tested-by" wouldn't hurt either.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny.
author
#54 lore raw

v9 cover PCI: X-Gene: Add APM X-Gene v1 MSI/MSIX termination driver

This patch set adds MSI/MSIX termination driver support for APM X-Gene v1 SoC. APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports. As the version 5 of this patch, the total MSI vectors this driver supports is reduced to 256 to maintain the correct set_affinity behavior for each MSI.

v9 changes:
1. Add more helpers for manipulating MSI messages
2. Improve code readability
3. Make PCI_XGENE_MSI selectable

v8 changes:
1. Add helper to read MSI registers
2. Resample interrupt status of the same group after handling interrupts in that group
3. Error handling in case fail to allocate CPU mask for affinity setting

v7 changes:
1. Add more error handling cases
2. Clear spurious interrupts that may happen during driver probe
3. Not using free_irq for chained irqs
4. Improve GIC IRQ number look up in chained handler

v6 changes:
1. Correctly allocate MSI with bitmap_find_next_zero_area
2. Add notifier for CPU hotplug case
3. Driver clean up
4. Add more detailed information into device tree binding document and move the device tree binding patch before the driver/dts patch

v5 changes:
1. Implement set_affinity for each MSI by statically allocating 2 MSI GIC IRQs for each X-Gene CPU core and moving MSI vectors around these GIC IRQs to steer them to target CPU core. As a consequence, the total MSI vectors that X-Gene v1 supports is reduced to 256.

v4 changes:
1. Remove affinity setting for each MSI
2. Add description about register layout, MSI termination address and data
3. Correct total number of MSI vectors to 2048
4. Clean up error messages
5. Remove unused module code

v3 changes:
1. Implement MSI support using PCI MSI IRQ domain
2. Only use msi_controller to store IRQ domain

v2 changes:
1. Use msi_controller structure
2. Remove arch hooks arch_teardown_msi_irqs and arch_setup_msi_irqs

 .../devicetree/bindings/pci/xgene-pci-msi.txt      |  68 +++
 MAINTAINERS                                        |   8 +
 arch/arm64/boot/dts/apm/apm-storm.dtsi             |  27 +
 drivers/pci/host/Kconfig                           |  10 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-xgene-msi.c                   | 595 +++++++++++++++++++++
 drivers/pci/host/pci-xgene.c                       |  21 +
 7 files changed, 730 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
Signature · 1 line
1.9.1
author
#55 lore raw

PATCH v9 1/4 documentation: dts: Add the device tree binding for APM X-Gene v1 PCIe MSI device tree node

X-Gene v1 PCIe MSI controller block provides PCIE MSI functionality for 5 X-Gene v1 PCIE ports

The driver for this binding is under 'drivers/pci/host/pci-xgene-msi.c'

---
 .../devicetree/bindings/pci/xgene-pci-msi.txt      | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt new file +68 -0
new file mode 100644
index 0000000..36d881c
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+	      X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+	      interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+	+ MSI node:
+	msi at 79000000 {
+		compatible = "apm,xgene1-msi";
+		msi-controller;
+		reg = <0x00 0x79000000 0x0 0x900000>;
+		interrupts = 	<0x0 0x10 0x4>
+				<0x0 0x11 0x4>
+				<0x0 0x12 0x4>
+				<0x0 0x13 0x4>
+				<0x0 0x14 0x4>
+				<0x0 0x15 0x4>
+				<0x0 0x16 0x4>
+				<0x0 0x17 0x4>
+				<0x0 0x18 0x4>
+				<0x0 0x19 0x4>
+				<0x0 0x1a 0x4>
+				<0x0 0x1b 0x4>
+				<0x0 0x1c 0x4>
+				<0x0 0x1d 0x4>
+				<0x0 0x1e 0x4>
+				<0x0 0x1f 0x4>;
+	};
+
+	+ PCIe controller node with msi-parent property pointing to MSI node:
+	pcie0: pcie at 1f2b0000 {
+		status = "disabled";
+		device_type = "pci";
+		compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+		#interrupt-cells = <1>;
+		#size-cells = <2>;
+		#address-cells = <3>;
+		reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+			0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+		reg-names = "csr", "cfg";
+		ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+			  0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+		dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+			      0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+		interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+		interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+				 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+				 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+				 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+		dma-coherent;
+		clocks = <&pcie0clk 0>;
+		msi-parent= <&msi>;
+	};
Signature · 1 line
1.9.1
author
#56 lore raw

PATCH v9 2/4 PCI: X-Gene: Add the APM X-Gene v1 PCIe MSI/MSIX termination driver

In reply to Duc Dang

APM X-Gene v1 SoC supports its own implementation of MSI, which is not compliant to GIC V2M specification for MSI Termination.

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports. This MSI block supports 2048 MSI termination ports coalesced into 16 physical HW IRQ lines and shared across all 5 PCIe ports.

As there are only 16 HW IRQs to serve 2048 MSI vectors, to support set_affinity correctly for each MSI vectors, the 16 HW IRQs are statically allocated to 8 X-Gene v1 cores (2 HW IRQs for each cores). To steer MSI interrupt to target CPU, MSI vector is moved around these HW IRQs lines. With this approach, the total MSI vectors this driver supports is reduced to 256.

---
 drivers/pci/host/Kconfig         |  10 +
 drivers/pci/host/Makefile        |   1 +
 drivers/pci/host/pci-xgene-msi.c | 595 +++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pci-xgene.c     |  21 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/pci/host/pci-xgene-msi.c
drivers/pci/host/Kconfig +10 -0
index 1dfb567..a93fda8 100644
@@ -89,11 +89,21 @@ config PCI_XGENE
 	depends on ARCH_XGENE
 	depends on OF
 	select PCIEPORTBUS
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 	help
 	  Say Y here if you want internal PCI support on APM X-Gene SoC.
 	  There are 5 internal PCIe ports available. Each port is GEN3 capable
 	  and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+	bool "X-Gene v1 PCIe MSI feature"
+	depends on PCI_XGENE && PCI_MSI
+	default y
+	help
+	  Say Y here if you want PCIE MSI support for APM X-Gene v1 SoC.
+	  This MSI driver will provide MSI support for 5 PCIe ports of
+	  APM X-Gene v1 SoC
+
 config PCI_LAYERSCAPE
 	bool "Freescale Layerscape PCIe controller"
 	depends on OF && ARM
drivers/pci/host/Makefile +1 -0
index f733b4e..1957431 100644
@@ -11,6 +11,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
drivers/pci/host/pci-xgene-msi.c new file +595 -0

595 lines not on this page · raw message

drivers/pci/host/pci-xgene.c +21 -0
index ee082c0..a9b4f16 100644
@@ -468,6 +468,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 	return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+	struct device_node *msi_node;
+
+	msi_node = of_parse_phandle(bus->dev.of_node,
+					"msi-parent", 0);
+	if (!msi_node)
+		return -ENODEV;
+
+	bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+	if (bus->msi)
+		bus->msi->dev = &bus->dev;
+	else
+		return -ENODEV;
+	return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
 	struct device_node *dn = pdev->dev.of_node;
@@ -504,6 +521,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 	if (!bus)
 		return -ENOMEM;
 
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		if (xgene_pcie_msi_enable(bus))
+			dev_info(port->dev, "failed to enable MSI\n");
+
 	pci_scan_child_bus(bus);
 	pci_assign_unassigned_bus_resources(bus);
 	pci_bus_add_devices(bus);
Signature · 1 line
1.9.1
author
#66 lore raw

PATCH v9 3/4 arm64: dts: Add the device tree entry for the APM X-Gene PCIe MSI node

In reply to Duc Dang

There is single MSI block in X-Gene v1 SOC which serves all 5 PCIe ports.

---
 arch/arm64/boot/dts/apm/apm-storm.dtsi | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
arch/arm64/boot/dts/apm/apm-storm.dtsi +27 -0
index c8d3e0e..d8f3a1c 100644
@@ -374,6 +374,28 @@
 			};
 		};
 
+		msi: msi at 79000000 {
+			compatible = "apm,xgene1-msi";
+			msi-controller;
+			reg = <0x00 0x79000000 0x0 0x900000>;
+			interrupts = <  0x0 0x10 0x4
+					0x0 0x11 0x4
+					0x0 0x12 0x4
+					0x0 0x13 0x4
+					0x0 0x14 0x4
+					0x0 0x15 0x4
+					0x0 0x16 0x4
+					0x0 0x17 0x4
+					0x0 0x18 0x4
+					0x0 0x19 0x4
+					0x0 0x1a 0x4
+					0x0 0x1b 0x4
+					0x0 0x1c 0x4
+					0x0 0x1d 0x4
+					0x0 0x1e 0x4
+					0x0 0x1f 0x4>;
+		};
+
 		pcie0: pcie at 1f2b0000 {
 			status = "disabled";
 			device_type = "pci";
@@ -395,6 +417,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
 			dma-coherent;
 			clocks = <&pcie0clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie1: pcie at 1f2c0000 {
@@ -418,6 +441,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
 			dma-coherent;
 			clocks = <&pcie1clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie2: pcie at 1f2d0000 {
@@ -441,6 +465,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
 			dma-coherent;
 			clocks = <&pcie2clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie3: pcie at 1f500000 {
@@ -464,6 +489,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
 			dma-coherent;
 			clocks = <&pcie3clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		pcie4: pcie at 1f510000 {
@@ -487,6 +513,7 @@
 					 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
 			dma-coherent;
 			clocks = <&pcie4clk 0>;
+			msi-parent = <&msi>;
 		};
 
 		serial0: serial at 1c020000 {
Signature · 1 line
1.9.1
author
#67 lore raw

PATCH v9 4/4 PCI: X-Gene: Add the MAINTAINERS entry for APM X-Gene v1 PCIe MSI driver

In reply to Duc Dang

This patch adds information of maintainers for APM X-Gene v1 PCIe MSI/MSIX termination driver

---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
index 781e099..59aa7b3 100644
@@ -7564,6 +7564,14 @@ L:	linux-pci at vger.kernel.org
 S:	Orphan
 F:	drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:	Duc Dang <[email protected]>
+L:	linux-pci at vger.kernel.org
+L:	linux-arm-kernel at lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:	drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:	Linux PCMCIA Team
 L:	linux-pcmcia at lists.infradead.org
Signature · 1 line
1.9.1