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

Commit 3d116a66 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq updates from Thomas Gleixner:
 "The irq department provides:

   - Support for MSI to wire bridges and a first user of it

   - More ACPI support for ARM/GIC

   - A new TS-4800 interrupt controller driver

   - RCU based free of interrupt descriptors to support the upcoming
     Intel VMD technology without introducing a locking nightmare

   - The usual pile of fixes and updates to drivers and core code"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (41 commits)
  irqchip/omap-intc: Add support for spurious irq handling
  irqchip/zevio: Use irq_data_get_chip_type() helper
  irqchip/omap-intc: Remove duplicate setup for IRQ chip type handler
  irqchip/ts4800: Add TS-4800 interrupt controller
  irqchip/ts4800: Add documentation for TS-4800 interrupt controller
  irq/platform-MSI: Increase the maximum MSIs the MSI framework can support
  irqchip/gicv2m: Miscellaneous fixes for v2m resources and SPI ranges
  irqchip/bcm2836: Make code more readable
  irqchip/bcm2836: Tolerate IRQs while no flag is set in ISR
  irqchip/bcm2836: Add SMP support for the 2836
  irqchip/bcm2836: Fix initialization of the LOCAL_IRQ_CNT timers
  irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support
  irqchip/gic-v2m: Refactor to prepare for ACPI support
  irqdomain: Introduce is_fwnode_irqchip helper
  acpi: pci: Setup MSI domain for ACPI based pci devices
  genirq/msi: Export functions to allow MSI domains in modules
  irqchip/mbigen: Implement the mbigen irq chip operation functions
  irqchip/mbigen: Create irq domain for each mbigen device
  irqchip/mgigen: Add platform device driver for mbigen device
  dt-bindings: Documents the mbigen bindings
  ...
parents b4cee21e d3b421cd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ Allwinner Sunxi NMI Controller
Required properties:

- compatible : should be "allwinner,sun7i-a20-sc-nmi" or
  "allwinner,sun6i-a31-sc-nmi"
  "allwinner,sun6i-a31-sc-nmi" or "allwinner,sun9i-a80-nmi"
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ Main node required properties:
	"arm,cortex-a9-gic"
	"arm,gic-400"
	"arm,pl390"
	"arm,tc11mp-gic"
	"brcm,brahma-b15-gic"
	"qcom,msm-8660-qgic"
	"qcom,msm-qgic2"
+74 −0
Original line number Diff line number Diff line
Hisilicon mbigen device tree bindings.
=======================================

Mbigen means: message based interrupt generator.

MBI is kind of msi interrupt only used on Non-PCI devices.

To reduce the wired interrupt number connected to GIC,
Hisilicon designed mbigen to collect and generate interrupt.


Non-pci devices can connect to mbigen and generate the
interrupt by writing ITS register.

The mbigen chip and devices connect to mbigen have the following properties:

Mbigen main node required properties:
-------------------------------------------
- compatible: Should be "hisilicon,mbigen-v2"

- reg: Specifies the base physical address and size of the Mbigen
  registers.

- interrupt controller: Identifies the node as an interrupt controller

- msi-parent: Specifies the MSI controller this mbigen use.
  For more detail information,please refer to the generic msi-parent binding in
  Documentation/devicetree/bindings/interrupt-controller/msi.txt.

- num-pins: the total number of pins implemented in this Mbigen
  instance.

- #interrupt-cells : Specifies the number of cells needed to encode an
  interrupt source. The value must be 2.

  The 1st cell is hardware pin number of the interrupt.This number is local to
  each mbigen chip and in the range from 0 to the maximum interrupts number
  of the mbigen.

  The 2nd cell is the interrupt trigger type.
	The value of this cell should be:
	1: rising edge triggered
	or
	4: high level triggered

Examples:

	mbigen_device_gmac:intc {
			compatible = "hisilicon,mbigen-v2";
			reg = <0x0 0xc0080000 0x0 0x10000>;
			interrupt-controller;
			msi-parent = <&its_dsa 0x40b1c>;
			num-pins = <9>;
			#interrupt-cells = <2>;
	};

Devices connect to mbigen required properties:
----------------------------------------------------
-interrupt-parent: Specifies the mbigen device node which device connected.

-interrupts:Specifies the interrupt source.
 For the specific information of each cell in this property,please refer to
 the "interrupt-cells" description mentioned above.

Examples:
	gmac0: ethernet@c2080000 {
		#address-cells = <1>;
		#size-cells = <0>;
		reg = <0 0xc2080000 0 0x20000>,
		      <0 0xc0000000 0 0x1000>;
		interrupt-parent  = <&mbigen_device_gmac>;
		interrupts =	<656 1>,
				<657 1>;
	};
+16 −0
Original line number Diff line number Diff line
TS-4800 FPGA interrupt controller

TS-4800 FPGA has an internal interrupt controller. When one of the
interrupts is triggered, the SoC is notified, usually using a GPIO as
parent interrupt source.

Required properties:
- compatible: should be "technologic,ts4800-irqc"
- interrupt-controller: identifies the node as an interrupt controller
- reg: physical base address of the controller and length of memory mapped
  region
- #interrupt-cells: specifies the number of cells needed to encode an interrupt
  source, should be 1.
- interrupt-parent: phandle to the parent interrupt controller this one is
  cascaded from
- interrupts: specifies the interrupt line in the interrupt-parent controller
+201 −55
Original line number Diff line number Diff line
@@ -24,13 +24,17 @@
#include <linux/msi.h>
#include <linux/slab.h>

#define DEV_ID_SHIFT	24
#define DEV_ID_SHIFT	21
#define MAX_DEV_MSIS	(1 << (32 - DEV_ID_SHIFT))

/*
 * Internal data structure containing a (made up, but unique) devid
 * and the callback to write the MSI message.
 */
struct platform_msi_priv_data {
	struct device		*dev;
	void 			*host_data;
	msi_alloc_info_t	arg;
	irq_write_msi_msg_t	write_msg;
	int			devid;
};
@@ -110,39 +114,49 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info)
		chip->irq_write_msi_msg = platform_msi_write_msg;
}

static void platform_msi_free_descs(struct device *dev)
static void platform_msi_free_descs(struct device *dev, int base, int nvec)
{
	struct msi_desc *desc, *tmp;

	list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
		if (desc->platform.msi_index >= base &&
		    desc->platform.msi_index < (base + nvec)) {
			list_del(&desc->list);
			free_msi_entry(desc);
		}
	}
}

static int platform_msi_alloc_descs(struct device *dev, int nvec,
static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
					     int nvec,
					     struct platform_msi_priv_data *data)

{
	int i;

	for (i = 0; i < nvec; i++) {
	struct msi_desc *desc;
	int i, base = 0;

	if (!list_empty(dev_to_msi_list(dev))) {
		desc = list_last_entry(dev_to_msi_list(dev),
				       struct msi_desc, list);
		base = desc->platform.msi_index + 1;
	}

	for (i = 0; i < nvec; i++) {
		desc = alloc_msi_entry(dev);
		if (!desc)
			break;

		desc->platform.msi_priv_data = data;
		desc->platform.msi_index = i;
		desc->platform.msi_index = base + i;
		desc->nvec_used = 1;
		desc->irq = virq ? virq + i : 0;

		list_add_tail(&desc->list, dev_to_msi_list(dev));
	}

	if (i != nvec) {
		/* Clean up the mess */
		platform_msi_free_descs(dev);
		platform_msi_free_descs(dev, base, nvec);

		return -ENOMEM;
	}
@@ -150,6 +164,13 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
	return 0;
}

static int platform_msi_alloc_descs(struct device *dev, int nvec,
				    struct platform_msi_priv_data *data)

{
	return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
}

/**
 * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
 * @fwnode:		Optional fwnode of the interrupt controller
@@ -180,56 +201,75 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
	return domain;
}

/**
 * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
 * @dev:		The device for which to allocate interrupts
 * @nvec:		The number of interrupts to allocate
 * @write_msi_msg:	Callback to write an interrupt message for @dev
 *
 * Returns:
 * Zero for success, or an error code in case of failure
 */
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
static struct platform_msi_priv_data *
platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
			     irq_write_msi_msg_t write_msi_msg)
{
	struct platform_msi_priv_data *priv_data;
	int err;

	struct platform_msi_priv_data *datap;
	/*
	 * Limit the number of interrupts to 256 per device. Should we
	 * need to bump this up, DEV_ID_SHIFT should be adjusted
	 * accordingly (which would impact the max number of MSI
	 * capable devices).
	 */
	if (!dev->msi_domain || !write_msi_msg || !nvec ||
	    nvec > (1 << (32 - DEV_ID_SHIFT)))
		return -EINVAL;
	if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
		return ERR_PTR(-EINVAL);

	if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
		dev_err(dev, "Incompatible msi_domain, giving up\n");
		return -EINVAL;
		return ERR_PTR(-EINVAL);
	}

	/* Already had a helping of MSI? Greed... */
	if (!list_empty(dev_to_msi_list(dev)))
		return -EBUSY;
		return ERR_PTR(-EBUSY);

	priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
	if (!priv_data)
		return -ENOMEM;
	datap = kzalloc(sizeof(*datap), GFP_KERNEL);
	if (!datap)
		return ERR_PTR(-ENOMEM);

	priv_data->devid = ida_simple_get(&platform_msi_devid_ida,
	datap->devid = ida_simple_get(&platform_msi_devid_ida,
				      0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
	if (priv_data->devid < 0) {
		err = priv_data->devid;
		goto out_free_data;
	if (datap->devid < 0) {
		int err = datap->devid;
		kfree(datap);
		return ERR_PTR(err);
	}

	priv_data->write_msg = write_msi_msg;
	datap->write_msg = write_msi_msg;
	datap->dev = dev;

	return datap;
}

static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
{
	ida_simple_remove(&platform_msi_devid_ida, data->devid);
	kfree(data);
}

/**
 * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
 * @dev:		The device for which to allocate interrupts
 * @nvec:		The number of interrupts to allocate
 * @write_msi_msg:	Callback to write an interrupt message for @dev
 *
 * Returns:
 * Zero for success, or an error code in case of failure
 */
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
				   irq_write_msi_msg_t write_msi_msg)
{
	struct platform_msi_priv_data *priv_data;
	int err;

	priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
	if (IS_ERR(priv_data))
		return PTR_ERR(priv_data);

	err = platform_msi_alloc_descs(dev, nvec, priv_data);
	if (err)
		goto out_free_id;
		goto out_free_priv_data;

	err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
	if (err)
@@ -238,11 +278,9 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
	return 0;

out_free_desc:
	platform_msi_free_descs(dev);
out_free_id:
	ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
out_free_data:
	kfree(priv_data);
	platform_msi_free_descs(dev, 0, nvec);
out_free_priv_data:
	platform_msi_free_priv_data(priv_data);

	return err;
}
@@ -253,18 +291,126 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
 */
void platform_msi_domain_free_irqs(struct device *dev)
{
	if (!list_empty(dev_to_msi_list(dev))) {
		struct msi_desc *desc;

		desc = first_msi_entry(dev);
	if (desc) {
		platform_msi_free_priv_data(desc->platform.msi_priv_data);
	}

	msi_domain_free_irqs(dev->msi_domain, dev);
	platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
}

/**
 * platform_msi_get_host_data - Query the private data associated with
 *                              a platform-msi domain
 * @domain:	The platform-msi domain
 *
 * Returns the private data provided when calling
 * platform_msi_create_device_domain.
 */
void *platform_msi_get_host_data(struct irq_domain *domain)
{
	struct platform_msi_priv_data *data = domain->host_data;
	return data->host_data;
}

/**
 * platform_msi_create_device_domain - Create a platform-msi domain
 *
 * @dev:		The device generating the MSIs
 * @nvec:		The number of MSIs that need to be allocated
 * @write_msi_msg:	Callback to write an interrupt message for @dev
 * @ops:		The hierarchy domain operations to use
 * @host_data:		Private data associated to this domain
 *
 * Returns an irqdomain for @nvec interrupts
 */
struct irq_domain *
platform_msi_create_device_domain(struct device *dev,
				  unsigned int nvec,
				  irq_write_msi_msg_t write_msi_msg,
				  const struct irq_domain_ops *ops,
				  void *host_data)
{
	struct platform_msi_priv_data *data;
	struct irq_domain *domain;
	int err;

		data = desc->platform.msi_priv_data;
	data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
	if (IS_ERR(data))
		return NULL;

		ida_simple_remove(&platform_msi_devid_ida, data->devid);
		kfree(data);
	data->host_data = host_data;
	domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
					     of_node_to_fwnode(dev->of_node),
					     ops, data);
	if (!domain)
		goto free_priv;

	err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
	if (err)
		goto free_domain;

	return domain;

free_domain:
	irq_domain_remove(domain);
free_priv:
	platform_msi_free_priv_data(data);
	return NULL;
}

	msi_domain_free_irqs(dev->msi_domain, dev);
	platform_msi_free_descs(dev);
/**
 * platform_msi_domain_free - Free interrupts associated with a platform-msi
 *                            domain
 *
 * @domain:	The platform-msi domain
 * @virq:	The base irq from which to perform the free operation
 * @nvec:	How many interrupts to free from @virq
 */
void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
			      unsigned int nvec)
{
	struct platform_msi_priv_data *data = domain->host_data;
	struct msi_desc *desc;
	for_each_msi_entry(desc, data->dev) {
		if (WARN_ON(!desc->irq || desc->nvec_used != 1))
			return;
		if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
			continue;

		irq_domain_free_irqs_common(domain, desc->irq, 1);
	}
}

/**
 * platform_msi_domain_alloc - Allocate interrupts associated with
 *			       a platform-msi domain
 *
 * @domain:	The platform-msi domain
 * @virq:	The base irq from which to perform the allocate operation
 * @nvec:	How many interrupts to free from @virq
 *
 * Return 0 on success, or an error code on failure. Must be called
 * with irq_domain_mutex held (which can only be done as part of a
 * top-level interrupt allocation).
 */
int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
			      unsigned int nr_irqs)
{
	struct platform_msi_priv_data *data = domain->host_data;
	int err;

	err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
	if (err)
		return err;

	err = msi_domain_populate_irqs(domain->parent, data->dev,
				       virq, nr_irqs, &data->arg);
	if (err)
		platform_msi_domain_free(domain, virq, nr_irqs);

	return err;
}
Loading