Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+220 −0
Original line number Original line Diff line number Diff line
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>;
	};
};
+1 −3
Original line number Original line Diff line number Diff line
@@ -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)
{
{
	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,
	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);
+15 −3
Original line number Original line Diff line number Diff line
@@ -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;


+175 −10
Original line number Original line Diff line number Diff line
@@ -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));
}
}
+49 −0
Original line number Original line Diff line number Diff line
@@ -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


	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 */
Loading