diff mbox

[1/2] pci/of: to support explicitly declare interrupt pins unused

Message ID 1456401208-10136-1-git-send-email-thunder.leizhen@huawei.com
State New
Headers show

Commit Message

Leizhen (ThunderTown) Feb. 25, 2016, 11:53 a.m. UTC
Interrupt Pin register is read-only and optional. Some pci devices may use
msi/msix but leave the value of Interrupt Pin non-zero. In this case, the
driver will print information as below:
pci 0000:40:00.0: of_irq_parse_pci() failed with rc=-22

It's easily lead to misinterpret.

Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>

---
 drivers/of/of_pci_irq.c | 43 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 40 insertions(+), 3 deletions(-)

--
2.5.0


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

Comments

Mark Rutland Feb. 25, 2016, 12:20 p.m. UTC | #1
Hi,

In future, please send the binding document first in a series, per point
3 of Documentation/devicetree/bindings/submitting-patches.txt. It makes
review easier/faster.

On Thu, Feb 25, 2016 at 07:53:28PM +0800, Zhen Lei wrote:
> Interrupt Pin register is read-only and optional. Some pci devices may use

> msi/msix but leave the value of Interrupt Pin non-zero.


Is that permitted by the spec? Surely 'optional' means it must be zero
if not implemented?

> In this case, the driver will print information as below: pci

> 0000:40:00.0: of_irq_parse_pci() failed with rc=-22

> 

> It's easily lead to misinterpret.


If this is limited to a subset of devices which we know are broken in
this regard, can we not handle these cases explicitly?

> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>

> ---

>  Documentation/devicetree/bindings/pci/host-generic-pci.txt | 2 ++

>  1 file changed, 2 insertions(+)

> 

> diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.txt b/Documentation/devicetree/bindings/pci/host-generic-pci.txt

> index 3f1d3fc..0f10978 100644

> --- a/Documentation/devicetree/bindings/pci/host-generic-pci.txt

> +++ b/Documentation/devicetree/bindings/pci/host-generic-pci.txt

> @@ -70,6 +70,8 @@ Practice: Interrupt Mapping' and requires the following properties:

> 

>  - interrupt-map-mask : <see aforementioned specification>

> 

> +- interrupt-skip-mask: Explicitly declare which pci devices only use msi/msix

> +but leave the value of Interrupt Pin non-zero.


Unlike the rest of the interrupt mapping properties, this is not
described in  `Open Firmware Recommended Practice: Interrupt Mapping'.

This needs a far more complete description.

This also doesn't strike me as th right approach. The interrupt-map-mask
property describe as relationship between the host-controller-provided
interrupt lines and endpoints, while this seems to be a bug completely
contained within an endpoint.

Thanks,
Mark.

> 

>  Example:

> 

> --

> 2.5.0

> 

> 

> --

> To unsubscribe from this list: send the line "unsubscribe devicetree" in

> the body of a message to majordomo@vger.kernel.org

> More majordomo info at  http://vger.kernel.org/majordomo-info.html

> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Rutland Feb. 26, 2016, 11:46 a.m. UTC | #2
On Fri, Feb 26, 2016 at 03:19:55PM +0800, Leizhen (ThunderTown) wrote:
> 

> On 2016/2/25 20:20, Mark Rutland wrote:

> > Hi,

> > 

> > In future, please send the binding document first in a series, per point

> > 3 of Documentation/devicetree/bindings/submitting-patches.txt. It makes

> > review easier/faster.

> Thank you for your reminding.

> 

> > On Thu, Feb 25, 2016 at 07:53:28PM +0800, Zhen Lei wrote:

> >> Interrupt Pin register is read-only and optional. Some pci devices may use

> >> msi/msix but leave the value of Interrupt Pin non-zero.

> > 

> > Is that permitted by the spec? Surely 'optional' means it must be zero

> > if not implemented?

> 

> In <PCI Local Bus Specification Revision 3.0>:

> Devices (or device functions) that do not use an interrupt pin must put a 0 in this register. This register is read-only.

> 

> So, do you think this is a hardware bug?


Per the above, that does appear to be the case.

> But these pci-devices are not produced by our company.

> 

> In function init_service_irqs, it try msix first, then msi, Interrupt

> PIN is the last attemption. But of_irq_parse_pci() happened before

> this.


I assume that for devices with 0 in this register we do not produce a
warning. So where do we check the interrupt pin register, and when does
this happen relative to of_irq_parse_pci such that we do not produce
that warning?

I als assume that all instances of these particular devices broken in
this regard? If so, I think we need to identify them by Device ID and
Vendor ID, and treat them as if the interrupt pin register read as
zero, in the place we normally check the interrupt pin register.

Note that this is completely independent of the RID/BDF, so the
interrupt-*-mask approach is insufficient.

> In fact, there also a familiar problem exist. As below:

> pci 0000:42:00.0: BAR 7: no space for [io  size 0x1000]

> pci 0000:42:00.0: BAR 7: failed to assign [io  size 0x1000]

> 

> There no "io space" on arm64, maybe only exist on X86. And the Memory Space Indicator also read-only in BAR register.


I'm not entirely sure, but I thought we handled the PCI I/O space as an
MMIO region on ARM64. Do you have many devices/functions attached? It
may be that our VA carveout of 16M is too small.

This is probably worth a separate thread.

> >> In this case, the driver will print information as below: pci

> >> 0000:40:00.0: of_irq_parse_pci() failed with rc=-22

> >>

> >> It's easily lead to misinterpret.

> > 

> > If this is limited to a subset of devices which we know are broken in

> > this regard, can we not handle these cases explicitly?

> Actually, we have another way to block this warning. Use "interrupt-map" to map it to a pesudo IRQ. But I think it will also be misunderstanded.


This is very fragile, as it depends on the RIDs/addresses assigned by
the host controller. If devices are plugged into different slots then
that could change, you get the warning, and other devices may be
prevented from using wired interrupts.

As I mentioned above, I think we need to identify the buggy devices by
ID, rather than by topology.

Thanks,
Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 2306313..a6d51e0 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -4,6 +4,38 @@ 
 #include <linux/export.h>

 /**
+ * of_irq_skip_pci - Skip the interrupt pins for a PCI device
+ * @pdev: the device whose interrupt pins may be skipped
+ * @dn: device node of pdev or pdev's ancestral bus
+ *
+ * Interrupt Pin register is read-only and optional. Some pci devices may use
+ * msi/msix but leave the value of Interrupt Pin non-zero. This function give
+ * an opportunity to suppress the warning about of_irq_parse_pci() failed.
+ */
+static int of_irq_skip_pci(const struct pci_dev *pdev,
+			   const struct device_node *dn)
+{
+	const __be32 *skip_mask, *end;
+	u32 addr, mask;
+	int len;
+
+	skip_mask = of_get_property(dn, "interrupt-skip-mask", &len);
+	if (!skip_mask)
+		return 0;
+
+	end = skip_mask + (len / sizeof(__be32));
+	addr = (pdev->bus->number << 16) | (pdev->devfn << 8);
+
+	while (skip_mask < end) {
+		mask = be32_to_cpu(*(skip_mask++));
+		if ((addr & ~mask) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
  * of_irq_parse_pci - Resolve the interrupt for a PCI device
  * @pdev:       the device whose interrupt is to be resolved
  * @out_irq:    structure of_irq filled by this function
@@ -30,6 +62,9 @@  int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
 		rc = of_irq_parse_one(dn, 0, out_irq);
 		if (!rc)
 			return rc;
+
+		if (of_irq_skip_pci(pdev, dn))
+			return -ENODEV;
 	}

 	/* Ok, we don't, time to have fun. Let's start by building up an
@@ -89,9 +124,11 @@  int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
 	laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
 	laddr[1] = laddr[2] = cpu_to_be32(0);
 	rc = of_irq_parse_raw(laddr, out_irq);
-	if (rc)
-		goto err;
-	return 0;
+	if (!rc)
+		return 0;
+
+	if (of_irq_skip_pci(pdev, ppnode))
+		return -ENODEV;
 err:
 	dev_err(&pdev->dev, "of_irq_parse_pci() failed with rc=%d\n", rc);
 	return rc;