From 8db02d8b4089fa8098a170738e8ae7939aa8ae7a Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 8 Oct 2015 15:10:48 -0700 Subject: of/irq: Add new function of_msi_map_rid() The device tree property "msi-map" specifies how to create the PCI requester id used in some MSI controllers. Add a new function of_msi_map_rid() that finds the msi-map property and applies its translation to a given requester id. Reviewed-by: Marc Zyngier Acked-by: Rob Herring Signed-off-by: David Daney Signed-off-by: Marc Zyngier --- include/linux/of_irq.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 4bcbd586a672..8cd9334e5731 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -75,6 +75,7 @@ static inline int of_irq_to_resource_table(struct device_node *dev, extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); extern struct device_node *of_irq_find_parent(struct device_node *child); extern void of_msi_configure(struct device *dev, struct device_node *np); +u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in); #else /* !CONFIG_OF */ static inline unsigned int irq_of_parse_and_map(struct device_node *dev, @@ -87,6 +88,12 @@ static inline void *of_irq_find_parent(struct device_node *child) { return NULL; } + +static inline u32 of_msi_map_rid(struct device *dev, + struct device_node *msi_np, u32 rid_in) +{ + return rid_in; +} #endif /* !CONFIG_OF */ #endif /* __OF_IRQ_H */ -- cgit v1.2.3-71-gd317 From b6eec9b717d4dcb39ef024b8a3b619a32468b01e Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 8 Oct 2015 15:10:49 -0700 Subject: PCI/MSI: Add helper function pci_msi_domain_get_msi_rid(). Add pci_msi_domain_get_msi_rid() to return the MSI requester id (RID). Initially needed by gic-v3 based systems. It will be used by follow on patch to drivers/irqchip/irq-gic-v3-its-pci-msi.c Initially supports mapping the RID via OF device tree. In the future, this could be extended to use ACPI _IORT tables as well. Reviewed-by: Marc Zyngier Acked-by: Bjorn Helgaas Signed-off-by: David Daney Signed-off-by: Marc Zyngier --- drivers/pci/msi.c | 32 ++++++++++++++++++++++++++++++++ include/linux/msi.h | 1 + 2 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ddd59fe786f8..5ab5c4f8dcb0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "pci.h" @@ -1327,4 +1328,35 @@ struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnod return domain; } + +static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) +{ + u32 *pa = data; + + *pa = alias; + return 0; +} +/** + * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID) + * @domain: The interrupt domain + * @pdev: The PCI device. + * + * The RID for a device is formed from the alias, with a firmware + * supplied mapping applied + * + * Returns: The RID. + */ +u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) +{ + struct device_node *of_node; + u32 rid = 0; + + pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); + + of_node = irq_domain_get_of_node(domain); + if (of_node) + rid = of_msi_map_rid(&pdev->dev, of_node, rid); + + return rid; +} #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 32a24b9a9556..87723751054d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -294,6 +294,7 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, struct msi_desc *desc); int pci_msi_domain_check_cap(struct irq_domain *domain, struct msi_domain_info *info, struct device *dev); +u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev); #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ #endif /* LINUX_MSI_H */ -- cgit v1.2.3-71-gd317 From 48ae34fb39b0c0cfc76275e844fba5b0b04fa49e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 18 Sep 2015 14:07:40 +0100 Subject: of/irq: Add support code for multi-parent version of "msi-parent" Since 126b16e2ad98 ("Docs: dt: add generic MSI bindings"), the definition of "msi-parent" has evolved, while maintaining some degree of compatibility. It can now express multiple MSI controllers as parents, as well as some sideband data being communicated to the controller. This patch adds the parsing of the property, iterating over the multiple parents until a suitable irqdomain is found. It can also fallback to the original parsing if the old binding is detected. This support code gets used in the subsequent patches. Suggested-by: Robin Murphy Acked-by: Rob Herring Signed-off-by: Marc Zyngier --- drivers/of/irq.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_irq.h | 9 ++++++++ 2 files changed, 68 insertions(+) (limited to 'include/linux') diff --git a/drivers/of/irq.c b/drivers/of/irq.c index c90bd4ec3183..62cfdc2c86ac 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -682,3 +682,62 @@ u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in) return rid_out; } + +static struct irq_domain *__of_get_msi_domain(struct device_node *np, + enum irq_domain_bus_token token) +{ + struct irq_domain *d; + + d = irq_find_matching_host(np, token); + if (!d) + d = irq_find_host(np); + + return d; +} + +/** + * of_msi_get_domain - Use msi-parent to find the relevant MSI domain + * @dev: device for which the domain is requested + * @np: device node for @dev + * @token: bus type for this domain + * + * Parse the msi-parent property (both the simple and the complex + * versions), and returns the corresponding MSI domain. + * + * Returns: the MSI domain for this device (or NULL on failure). + */ +struct irq_domain *of_msi_get_domain(struct device *dev, + struct device_node *np, + enum irq_domain_bus_token token) +{ + struct device_node *msi_np; + struct irq_domain *d; + + /* Check for a single msi-parent property */ + msi_np = of_parse_phandle(np, "msi-parent", 0); + if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) { + d = __of_get_msi_domain(msi_np, token); + if (!d) + of_node_put(msi_np); + return d; + } + + if (token == DOMAIN_BUS_PLATFORM_MSI) { + /* Check for the complex msi-parent version */ + struct of_phandle_args args; + int index = 0; + + while (!of_parse_phandle_with_args(np, "msi-parent", + "#msi-cells", + index, &args)) { + d = __of_get_msi_domain(args.np, token); + if (d) + return d; + + of_node_put(args.np); + index++; + } + } + + return NULL; +} diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 8cd9334e5731..62ae6edecfd5 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -46,6 +46,9 @@ extern int of_irq_get(struct device_node *dev, int index); extern int of_irq_get_byname(struct device_node *dev, const char *name); extern int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs); +extern struct irq_domain *of_msi_get_domain(struct device *dev, + struct device_node *np, + enum irq_domain_bus_token token); #else static inline int of_irq_count(struct device_node *dev) { @@ -64,6 +67,12 @@ static inline int of_irq_to_resource_table(struct device_node *dev, { return 0; } +static inline struct irq_domain *of_msi_get_domain(struct device *dev, + struct device_node *np, + enum irq_domain_bus_token token) +{ + return NULL; +} #endif #if defined(CONFIG_OF) -- cgit v1.2.3-71-gd317 From 82b9b4243c6d99d9e38087fa89183aa7479185e9 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 2 Oct 2015 14:38:55 +0100 Subject: of/irq: Use the msi-map property to provide device-specific MSI domain While msi-parent is used to point to the MSI controller that works for all the devices behind a root complex, it doesn't allow configurations where each individual device can be routed to a separate MSI controller. The msi-map property provides this flexibility (and much more), so let's add a utility function that parses it, and return the corresponding MSI domain. Acked-by: Rob Herring Signed-off-by: Marc Zyngier --- drivers/of/irq.c | 18 ++++++++++++++++++ include/linux/of_irq.h | 7 +++++++ 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/of/irq.c b/drivers/of/irq.c index ed64d98807f9..0baf626da56a 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -688,6 +688,24 @@ static struct irq_domain *__of_get_msi_domain(struct device_node *np, return d; } +/** + * of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain + * @dev: device for which the mapping is to be done. + * @rid: Requester ID for the device. + * + * Walk up the device hierarchy looking for devices with a "msi-map" + * property. + * + * Returns: the MSI domain for this device (or NULL on failure) + */ +struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 rid) +{ + struct device_node *np = NULL; + + __of_msi_map_rid(dev, &np, rid); + return __of_get_msi_domain(np, DOMAIN_BUS_PCI_MSI); +} + /** * of_msi_get_domain - Use msi-parent to find the relevant MSI domain * @dev: device for which the domain is requested diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 62ae6edecfd5..65d969246a4d 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -49,6 +49,8 @@ extern int of_irq_to_resource_table(struct device_node *dev, extern struct irq_domain *of_msi_get_domain(struct device *dev, struct device_node *np, enum irq_domain_bus_token token); +extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev, + u32 rid); #else static inline int of_irq_count(struct device_node *dev) { @@ -73,6 +75,11 @@ static inline struct irq_domain *of_msi_get_domain(struct device *dev, { return NULL; } +static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev, + u32 rid) +{ + return NULL; +} #endif #if defined(CONFIG_OF) -- cgit v1.2.3-71-gd317 From 54fa97eeb9e22b47d68b67ee00987afa7fbc2178 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 2 Oct 2015 14:43:06 +0100 Subject: PCI/MSI: Allow the MSI domain to be device-specific So far, we've always considered that for a given PCI device, its MSI controller was either set by the architecture-specific pcibios hook, or simply inherited from the host bridge. This doesn't cover things like firmware-defined topologies like msi-map (DT) or IORT (ACPI), which can provide information about which MSI controller to use on a per-device basis. This patch adds the necessary hook into the MSI code to allow this feature, and provides the msi-map functionnality as a first implementation. Acked-by: Rob Herring Acked-by: Bjorn Helgaas Signed-off-by: Marc Zyngier --- drivers/pci/msi.c | 17 +++++++++++++++++ drivers/pci/probe.c | 8 ++++++++ include/linux/msi.h | 6 ++++++ 3 files changed, 31 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 5ab5c4f8dcb0..4cd6f3abcecf 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1359,4 +1359,21 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) return rid; } + +/** + * pci_msi_get_device_domain - Get the MSI domain for a given PCI device + * @pdev: The PCI device + * + * Use the firmware data to find a device-specific MSI domain + * (i.e. not one that is ste as a default). + * + * Returns: The coresponding MSI domain or NULL if none has been found. + */ +struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) +{ + u32 rid = 0; + + pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); + return of_msi_map_get_device_domain(&pdev->dev, rid); +} #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7c333f8c2327..f14a970b61fa 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1639,6 +1639,14 @@ static struct irq_domain *pci_dev_msi_domain(struct pci_dev *dev) if (d) return d; + /* + * Let's see if we have a firmware interface able to provide + * the domain. + */ + d = pci_msi_get_device_domain(dev); + if (d) + return d; + return NULL; } diff --git a/include/linux/msi.h b/include/linux/msi.h index 87723751054d..0b4460374020 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -295,6 +295,12 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, int pci_msi_domain_check_cap(struct irq_domain *domain, struct msi_domain_info *info, struct device *dev); u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev); +struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev); +#else +static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) +{ + return NULL; +} #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */ #endif /* LINUX_MSI_H */ -- cgit v1.2.3-71-gd317