mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
Support for msi-map, and msi-parent update:
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWIOjtAAoJECPQ0LrRPXpDU9gP/iq0eIWB0t4ZssF/+SUhS3DJ 0UyinQojUhwU4NYEL7hO1F0A2ZEHPmlkx+pT6OaJPydzXSAN8rtHvL/jYkNHEdDZ V6lHFCKDQvlmlO/bUAyqH86cnqzjGXrbah5w+lfKuxmN6yoj8YnKbRLFKK/Kc8XJ iJ4ULspEMJnv+IXq45rwXcO1VYydQopTAmeMin4ebCT8p28dEjkkdpWl31k6jPVZ d400pa2McTJQWb12w+Y/fAsKYN4NrvA+mfh12fyIDWcY0xnQX+abdZ4fnm6Y6hH5 Z0rnfsTfs3oz04szD2hnd+cxkAtVaGA0uxvqQC+0YAtHcOUXBI9/6WidQHU53gph nrXygI7X4msKuJybjNcv+F5wxmVWwrTt4SqJZNYd2FLBQ19gnNMrUPTDc+qe80Ax Z8N3/nYUxbKchObbrYjRE2qTN08RUNTng1jQKN/YiCHKPdSE1RjWc6pYDC1SjaZw 3Y1qS8mNNOocvUYERl7whiBTMc1JWsiAA+yJMzuj1uymb5z33BYshumvNyGI7c4/ YdU3pvwpTKZJQjoLHhH95hxyPdxO2oBC9wj3cjLI1lxMQrSow4ejEJLjLdZsu6mX BnicSIrxWeXNFagK+/zMlh8nK6cLMeQGlypR+s0eFlKvrtdw2BN6WHAjJB6Iw7Sx GiuOIoHIp1bVBFXHJltC =7PT3 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
3b93baf56d
220
Documentation/devicetree/bindings/pci/pci-msi.txt
Normal file
220
Documentation/devicetree/bindings/pci/pci-msi.txt
Normal file
@ -0,0 +1,220 @@
|
||||
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 = {
|
||||
|
||||
struct its_pci_alias {
|
||||
struct pci_dev *pdev;
|
||||
u32 dev_id;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
dev_alias->dev_id = alias;
|
||||
if (pdev != dev_alias->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,
|
||||
pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
|
||||
|
||||
/* 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,
|
||||
dev, dev_alias.count, info);
|
||||
|
@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
{
|
||||
struct msi_domain_info *msi_info;
|
||||
u32 dev_id;
|
||||
int ret;
|
||||
int ret, index = 0;
|
||||
|
||||
msi_info = msi_get_domain_info(domain->parent);
|
||||
|
||||
/* Suck the DeviceID out of the msi-parent property */
|
||||
ret = of_property_read_u32_index(dev->of_node, "msi-parent",
|
||||
1, &dev_id);
|
||||
do {
|
||||
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)
|
||||
return ret;
|
||||
|
||||
|
187
drivers/of/irq.c
187
drivers/of/irq.c
@ -579,6 +579,180 @@ err:
|
||||
}
|
||||
}
|
||||
|
||||
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_map_rid - Map a MSI requester ID for a device.
|
||||
* @dev: device for which the mapping is to be done.
|
||||
* @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.
|
||||
*/
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_msi_configure - Set the msi_domain field of a device
|
||||
* @dev: device structure to associate with an MSI irq domain
|
||||
@ -586,15 +760,6 @@ err:
|
||||
*/
|
||||
void of_msi_configure(struct device *dev, struct device_node *np)
|
||||
{
|
||||
struct device_node *msi_np;
|
||||
struct irq_domain *d;
|
||||
|
||||
msi_np = of_parse_phandle(np, "msi-parent", 0);
|
||||
if (!msi_np)
|
||||
return;
|
||||
|
||||
d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI);
|
||||
if (!d)
|
||||
d = irq_find_host(msi_np);
|
||||
dev_set_msi_domain(dev, d);
|
||||
dev_set_msi_domain(dev,
|
||||
of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include "pci.h"
|
||||
|
||||
@ -1327,4 +1328,52 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include "pci.h"
|
||||
|
||||
@ -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)
|
||||
{
|
||||
#ifdef CONFIG_IRQ_DOMAIN
|
||||
struct device_node *np;
|
||||
struct irq_domain *d;
|
||||
|
||||
if (!bus->dev.of_node)
|
||||
return NULL;
|
||||
|
||||
/* 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
|
||||
* directly attached to the host bridge.
|
||||
*/
|
||||
if (!np)
|
||||
np = bus->dev.of_node;
|
||||
|
||||
d = irq_find_matching_host(np, DOMAIN_BUS_PCI_MSI);
|
||||
d = irq_find_matching_host(bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
|
||||
if (d)
|
||||
return d;
|
||||
|
||||
return irq_find_host(np);
|
||||
return irq_find_host(bus->dev.of_node);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
|
@ -1622,15 +1622,48 @@ static void pci_init_capabilities(struct pci_dev *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)
|
||||
{
|
||||
struct irq_domain *d;
|
||||
|
||||
/*
|
||||
* If no domain has been set through the pcibios_add_device
|
||||
* callback, inherit the default from the bus device.
|
||||
* If the platform or firmware interfaces cannot supply a
|
||||
* device-specific MSI domain, then inherit the default domain
|
||||
* from the host bridge itself.
|
||||
*/
|
||||
if (!dev_get_msi_domain(&dev->dev))
|
||||
dev_set_msi_domain(&dev->dev,
|
||||
dev_get_msi_domain(&dev->bus->dev));
|
||||
d = pci_dev_msi_domain(dev);
|
||||
if (!d)
|
||||
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)
|
||||
|
@ -294,6 +294,13 @@ 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);
|
||||
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 */
|
||||
|
@ -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_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);
|
||||
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)
|
||||
{
|
||||
@ -64,6 +69,17 @@ 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;
|
||||
}
|
||||
static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
|
||||
u32 rid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
@ -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 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 +104,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 */
|
||||
|
Loading…
Reference in New Issue
Block a user