Commit 3b93baf5 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'msi-map-4.4' of...

Merge tag 'msi-map-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Support for msi-map, and msi-parent update from Marc Zyngier:

- New map-map property to describe the remapping of requester-ids,
  and the routing of MSIs to controllers
- New hooks to make MSI domains per-device if required
- Extension of msi-parent to provide sideband information
- Extensive documentation for both msi-map and msi-parent
parents a71225e2 54fa97ee
This document describes the generic device tree binding for describing the
relationship between PCI devices and MSI controllers.
Each PCI device under a root complex is uniquely identified by its Requester ID
(AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
Function number.
For the purpose of this document, when treated as a numeric value, a RID is
formatted such that:
* Bits [15:8] are the Bus number.
* Bits [7:3] are the Device number.
* Bits [2:0] are the Function number.
* Any other bits required for padding must be zero.
MSIs may be distinguished in part through the use of sideband data accompanying
writes. In the case of PCI devices, this sideband data may be derived from the
Requester ID. A mechanism is required to associate a device with both the MSI
controllers it can address, and the sideband data that will be associated with
its writes to those controllers.
For generic MSI bindings, see
Documentation/devicetree/bindings/interrupt-controller/msi.txt.
PCI root complex
================
Optional properties
-------------------
- msi-map: Maps a Requester ID to an MSI controller and associated
msi-specifier data. The property is an arbitrary number of tuples of
(rid-base,msi-controller,msi-base,length), where:
* rid-base is a single cell describing the first RID matched by the entry.
* msi-controller is a single phandle to an MSI controller
* msi-base is an msi-specifier describing the msi-specifier produced for the
first RID matched by the entry.
* length is a single cell describing how many consecutive RIDs are matched
following the rid-base.
Any RID r in the interval [rid-base, rid-base + length) is associated with
the listed msi-controller, with the msi-specifier (r - rid-base + msi-base).
- msi-map-mask: A mask to be applied to each Requester ID prior to being mapped
to an msi-specifier per the msi-map property.
- msi-parent: Describes the MSI parent of the root complex itself. Where
the root complex and MSI controller do not pass sideband data with MSI
writes, this property may be used to describe the MSI controller(s)
used by PCI devices under the root complex, if defined as such in the
binding for the root complex.
Example (1)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
msi: msi-controller@a {
reg = <0xa 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
pci: pci@f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the MSI controller is
* the RID, identity-mapped.
*/
msi-map = <0x0 &msi_a 0x0 0x10000>,
};
};
Example (2)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
msi: msi-controller@a {
reg = <0xa 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
pci: pci@f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the MSI controller is
* the RID, masked to only the device and function bits.
*/
msi-map = <0x0 &msi_a 0x0 0x100>,
msi-map-mask = <0xff>
};
};
Example (3)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
msi: msi-controller@a {
reg = <0xa 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
pci: pci@f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the MSI controller is
* the RID, but the high bit of the bus number is
* ignored.
*/
msi-map = <0x0000 &msi 0x0000 0x8000>,
<0x8000 &msi 0x0000 0x8000>;
};
};
Example (4)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
msi: msi-controller@a {
reg = <0xa 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
pci: pci@f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the MSI controller is
* the RID, but the high bit of the bus number is
* negated.
*/
msi-map = <0x0000 &msi 0x8000 0x8000>,
<0x8000 &msi 0x0000 0x8000>;
};
};
Example (5)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
msi_a: msi-controller@a {
reg = <0xa 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
msi_b: msi-controller@b {
reg = <0xb 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
msi_c: msi-controller@c {
reg = <0xc 0x1>;
compatible = "vendor,some-controller";
msi-controller;
#msi-cells = <1>;
};
pci: pci@c {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to MSI controller a is the
* RID, but the high bit of the bus number is negated.
* The sideband data provided to MSI controller b is the
* RID, identity-mapped.
* MSI controller c is not addressable.
*/
msi-map = <0x0000 &msi_a 0x8000 0x08000>,
<0x8000 &msi_a 0x0000 0x08000>,
<0x0000 &msi_b 0x0000 0x10000>;
};
};
...@@ -42,7 +42,6 @@ static struct irq_chip its_msi_irq_chip = { ...@@ -42,7 +42,6 @@ static struct irq_chip its_msi_irq_chip = {
struct its_pci_alias { struct its_pci_alias {
struct pci_dev *pdev; struct pci_dev *pdev;
u32 dev_id;
u32 count; u32 count;
}; };
...@@ -60,7 +59,6 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) ...@@ -60,7 +59,6 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
{ {
struct its_pci_alias *dev_alias = data; struct its_pci_alias *dev_alias = data;
dev_alias->dev_id = alias;
if (pdev != dev_alias->pdev) if (pdev != dev_alias->pdev)
dev_alias->count += its_pci_msi_vec_count(pdev); dev_alias->count += its_pci_msi_vec_count(pdev);
...@@ -86,7 +84,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, ...@@ -86,7 +84,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
/* ITS specific DeviceID, as the core ITS ignores dev. */ /* ITS specific DeviceID, as the core ITS ignores dev. */
info->scratchpad[0].ul = dev_alias.dev_id; info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
return msi_info->ops->msi_prepare(domain->parent, return msi_info->ops->msi_prepare(domain->parent,
dev, dev_alias.count, info); dev, dev_alias.count, info);
......
...@@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, ...@@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
{ {
struct msi_domain_info *msi_info; struct msi_domain_info *msi_info;
u32 dev_id; u32 dev_id;
int ret; int ret, index = 0;
msi_info = msi_get_domain_info(domain->parent); msi_info = msi_get_domain_info(domain->parent);
/* Suck the DeviceID out of the msi-parent property */ /* Suck the DeviceID out of the msi-parent property */
ret = of_property_read_u32_index(dev->of_node, "msi-parent", do {
1, &dev_id); struct of_phandle_args args;
ret = of_parse_phandle_with_args(dev->of_node,
"msi-parent", "#msi-cells",
index, &args);
if (args.np == irq_domain_get_of_node(domain)) {
if (WARN_ON(args.args_count != 1))
return -EINVAL;
dev_id = args.args[0];
break;
}
} while (!ret);
if (ret) if (ret)
return ret; return ret;
......
...@@ -579,22 +579,187 @@ void __init of_irq_init(const struct of_device_id *matches) ...@@ -579,22 +579,187 @@ void __init of_irq_init(const struct of_device_id *matches)
} }
} }
static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
u32 rid_in)
{
struct device *parent_dev;
struct device_node *msi_controller_node;
struct device_node *msi_np = *np;
u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
int msi_map_len;
bool matched;
u32 rid_out = rid_in;
const __be32 *msi_map = NULL;
/*
* Walk up the device parent links looking for one with a
* "msi-map" property.
*/
for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
if (!parent_dev->of_node)
continue;
msi_map = of_get_property(parent_dev->of_node,
"msi-map", &msi_map_len);
if (!msi_map)
continue;
if (msi_map_len % (4 * sizeof(__be32))) {
dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
msi_map_len);
return rid_out;
}
/* We have a good parent_dev and msi_map, let's use them. */
break;
}
if (!msi_map)
return rid_out;
/* The default is to select all bits. */
map_mask = 0xffffffff;
/*
* Can be overridden by "msi-map-mask" property. If
* of_property_read_u32() fails, the default is used.
*/
of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
masked_rid = map_mask & rid_in;
matched = false;
while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
rid_base = be32_to_cpup(msi_map + 0);
phandle = be32_to_cpup(msi_map + 1);
msi_base = be32_to_cpup(msi_map + 2);
rid_len = be32_to_cpup(msi_map + 3);
msi_controller_node = of_find_node_by_phandle(phandle);
matched = (masked_rid >= rid_base &&
masked_rid < rid_base + rid_len);
if (msi_np)
matched &= msi_np == msi_controller_node;
if (matched && !msi_np) {
*np = msi_np = msi_controller_node;
break;
}
of_node_put(msi_controller_node);
msi_map_len -= 4 * sizeof(__be32);
msi_map += 4;
}
if (!matched)
return rid_out;
rid_out = masked_rid + msi_base;
dev_dbg(dev,
"msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
dev_name(parent_dev), map_mask, rid_base, msi_base,
rid_len, rid_in, rid_out);
return rid_out;
}
/** /**
* of_msi_configure - Set the msi_domain field of a device * of_msi_map_rid - Map a MSI requester ID for a device.
* @dev: device structure to associate with an MSI irq domain * @dev: device for which the mapping is to be done.
* @np: device node for that device * @msi_np: device node of the expected msi controller.
* @rid_in: unmapped MSI requester ID for the device.
*
* Walk up the device hierarchy looking for devices with a "msi-map"
* property. If found, apply the mapping to @rid_in.
*
* Returns the mapped MSI requester ID.
*/ */
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)
{
return __of_msi_map_rid(dev, &msi_np, rid_in);
}
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_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
* @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 device_node *msi_np;
struct irq_domain *d; struct irq_domain *d;
/* Check for a single msi-parent property */
msi_np = of_parse_phandle(np, "msi-parent", 0); msi_np = of_parse_phandle(np, "msi-parent", 0);
if (!msi_np) if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) {
return; d = __of_get_msi_domain(msi_np, token);
d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI);
if (!d) if (!d)
d = irq_find_host(msi_np); of_node_put(msi_np);
dev_set_msi_domain(dev, d); 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;
}
/**
* of_msi_configure - Set the msi_domain field of a device
* @dev: device structure to associate with an MSI irq domain
* @np: device node for that device
*/
void of_msi_configure(struct device *dev, struct device_node *np)
{
dev_set_msi_domain(dev,
of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
} }
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/of_irq.h>
#include "pci.h" #include "pci.h"
...@@ -1327,4 +1328,52 @@ struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnod ...@@ -1327,4 +1328,52 @@ struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnod
return domain; 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;
}
/**
* 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 */ #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h> #include <linux/of_pci.h>
#include "pci.h" #include "pci.h"
...@@ -64,27 +65,25 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus) ...@@ -64,27 +65,25 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus) struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus)
{ {
#ifdef CONFIG_IRQ_DOMAIN #ifdef CONFIG_IRQ_DOMAIN
struct device_node *np;
struct irq_domain *d; struct irq_domain *d;
if (!bus->dev.of_node) if (!bus->dev.of_node)
return NULL; return NULL;
/* Start looking for a phandle to an MSI controller. */ /* Start looking for a phandle to an MSI controller. */
np = of_parse_phandle(bus->dev.of_node, "msi-parent", 0); d = of_msi_get_domain(&bus->dev, bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
if (d)
return d;
/* /*
* If we don't have an msi-parent property, look for a domain * If we don't have an msi-parent property, look for a domain
* directly attached to the host bridge. * directly attached to the host bridge.
*/ */
if (!np) d = irq_find_matching_host(bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
np = bus->dev.of_node;
d = irq_find_matching_host(np, DOMAIN_BUS_PCI_MSI);
if (d) if (d)
return d; return d;
return irq_find_host(np); return irq_find_host(bus->dev.of_node);
#else #else
return NULL; return NULL;
#endif #endif
......
...@@ -1622,15 +1622,48 @@ static void pci_init_capabilities(struct pci_dev *dev) ...@@ -1622,15 +1622,48 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_enable_acs(dev); pci_enable_acs(dev);
} }
/*
* This is the equivalent of pci_host_bridge_msi_domain that acts on
* devices. Firmware interfaces that can select the MSI domain on a
* per-device basis should be called from here.
*/
static struct irq_domain *pci_dev_msi_domain(struct pci_dev *dev)
{
struct irq_domain *d;
/*
* If a domain has been set through the pcibios_add_device
* callback, then this is the one (platform code knows best).
*/
d = dev_get_msi_domain(&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;
}
static void pci_set_msi_domain(struct pci_dev *dev) static void pci_set_msi_domain(struct pci_dev *dev)
{ {
struct irq_domain *d;
/* /*
* If no domain has been set through the pcibios_add_device * If the platform or firmware interfaces cannot supply a
* callback, inherit the default from the bus device. * device-specific MSI domain, then inherit the default domain
* from the host bridge itself.
*/ */
if (!dev_get_msi_domain(&dev->dev)) d = pci_dev_msi_domain(dev);
dev_set_msi_domain(&dev->dev, if (!d)
dev_get_msi_domain(&dev->bus->dev)); d = dev_get_msi_domain(&dev->bus->dev);
dev_set_msi_domain(&dev->dev, d);
} }
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
......
...@@ -294,6 +294,13 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev, ...@@ -294,6 +294,13 @@ irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
struct msi_desc *desc); struct msi_desc *desc);
int pci_msi_domain_check_cap(struct irq_domain *domain, int pci_msi_domain_check_cap(struct irq_domain *domain,
struct msi_domain_info *info, struct device *dev); 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 /* CONFIG_PCI_MSI_IRQ_DOMAIN */
#endif /* LINUX_MSI_H */ #endif /* LINUX_MSI_H */
...@@ -46,6 +46,11 @@ extern int of_irq_get(struct device_node *dev, int index); ...@@ -46,6 +46,11 @@ 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_get_byname(struct device_node *dev, const char *name);
extern int of_irq_to_resource_table(struct device_node *dev, extern int of_irq_to_resource_table(struct device_node *dev,
struct resource *res, int nr_irqs); 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);
extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
u32 rid);
#else #else
static inline int of_irq_count(struct device_node *dev) static inline int of_irq_count(struct device_node *dev)
{ {
...@@ -64,6 +69,17 @@ static inline int of_irq_to_resource_table(struct device_node *dev, ...@@ -64,6 +69,17 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
{ {
return 0; 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;
}
static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
u32 rid)
{
return NULL;
}
#endif #endif
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
...@@ -75,6 +91,7 @@ static inline int of_irq_to_resource_table(struct device_node *dev, ...@@ -75,6 +91,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 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 struct device_node *of_irq_find_parent(struct device_node *child);
extern void of_msi_configure(struct device *dev, struct device_node *np); 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 */ #else /* !CONFIG_OF */
static inline unsigned int irq_of_parse_and_map(struct device_node *dev, static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
...@@ -87,6 +104,12 @@ static inline void *of_irq_find_parent(struct device_node *child) ...@@ -87,6 +104,12 @@ static inline void *of_irq_find_parent(struct device_node *child)
{ {
return NULL; 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 /* !CONFIG_OF */
#endif /* __OF_IRQ_H */ #endif /* __OF_IRQ_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment