diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e529c54d8d..c228447431 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -93,6 +93,7 @@
 			<&gpio_b 9 0xc 3 2 1>;
 		int-value = <1234>;
 		uint-value = <(-1234)>;
+		interrupts-extended = <&irq 3 0>;
 	};
 
 	junk {
@@ -357,8 +358,10 @@
 		vss-microvolts = <0>;
 	};
 
-	irq {
+	irq: irq {
 		compatible = "sandbox,irq";
+		interrupt-controller;
+		#interrupt-cells = <2>;
 	};
 
 	lcd {
diff --git a/doc/device-tree-bindings/interrupt-controller/interrupts.txt b/doc/device-tree-bindings/interrupt-controller/interrupts.txt
new file mode 100644
index 0000000000..38a399a6b1
--- /dev/null
+++ b/doc/device-tree-bindings/interrupt-controller/interrupts.txt
@@ -0,0 +1,131 @@
+Specifying interrupt information for devices
+============================================
+
+1) Interrupt client nodes
+-------------------------
+
+Nodes that describe devices which generate interrupts must contain an
+"interrupts" property, an "interrupts-extended" property, or both. If both are
+present, the latter should take precedence; the former may be provided simply
+for compatibility with software that does not recognize the latter. These
+properties contain a list of interrupt specifiers, one per output interrupt. The
+format of the interrupt specifier is determined by the interrupt controller to
+which the interrupts are routed; see section 2 below for details.
+
+  Example:
+	interrupt-parent = <&intc1>;
+	interrupts = <5 0>, <6 0>;
+
+The "interrupt-parent" property is used to specify the controller to which
+interrupts are routed and contains a single phandle referring to the interrupt
+controller node. This property is inherited, so it may be specified in an
+interrupt client node or in any of its parent nodes. Interrupts listed in the
+"interrupts" property are always in reference to the node's interrupt parent.
+
+The "interrupts-extended" property is a special form; useful when a node needs
+to reference multiple interrupt parents or a different interrupt parent than
+the inherited one. Each entry in this property contains both the parent phandle
+and the interrupt specifier.
+
+  Example:
+	interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
+
+(NOTE: only this 'special form' is supported in U-Boot)
+
+
+2) Interrupt controller nodes
+-----------------------------
+
+A device is marked as an interrupt controller with the "interrupt-controller"
+property. This is a empty, boolean property. An additional "#interrupt-cells"
+property defines the number of cells needed to specify a single interrupt.
+
+It is the responsibility of the interrupt controller's binding to define the
+length and format of the interrupt specifier. The following two variants are
+commonly used:
+
+  a) one cell
+  -----------
+  The #interrupt-cells property is set to 1 and the single cell defines the
+  index of the interrupt within the controller.
+
+  Example:
+
+	vic: intc at 10140000 {
+		compatible = "arm,versatile-vic";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		reg = <0x10140000 0x1000>;
+	};
+
+	sic: intc at 10003000 {
+		compatible = "arm,versatile-sic";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		reg = <0x10003000 0x1000>;
+		interrupt-parent = <&vic>;
+		interrupts = <31>; /* Cascaded to vic */
+	};
+
+  b) two cells
+  ------------
+  The #interrupt-cells property is set to 2 and the first cell defines the
+  index of the interrupt within the controller, while the second cell is used
+  to specify any of the following flags:
+    - bits[3:0] trigger type and level flags
+        1 = low-to-high edge triggered
+        2 = high-to-low edge triggered
+        4 = active high level-sensitive
+        8 = active low level-sensitive
+
+  Example:
+
+	i2c at 7000c000 {
+		gpioext: gpio-adnp at 41 {
+			compatible = "ad,gpio-adnp";
+			reg = <0x41>;
+
+			interrupt-parent = <&gpio>;
+			interrupts = <160 1>;
+
+			gpio-controller;
+			#gpio-cells = <1>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			nr-gpios = <64>;
+		};
+
+		sx8634 at 2b {
+			compatible = "smtc,sx8634";
+			reg = <0x2b>;
+
+			interrupt-parent = <&gpioext>;
+			interrupts = <3 0x8>;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			threshold = <0x40>;
+			sensitivity = <7>;
+		};
+	};
+
+
+Example of special form (supported by U-Boot):
+
+	acpi_gpe: general-purpose-events {
+		reg = <IOMAP_ACPI_BASE IOMAP_ACPI_SIZE>;
+		compatible = "intel,acpi-gpe";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	tpm at 50 {
+		reg = <0x50>;
+		compatible = "google,cr50";
+		u-boot,i2c-offset-len = <0>;
+		ready-gpio = <&gpio_n 28 GPIO_ACTIVE_LOW>;
+		interrupts-extended = <&acpi_gpe 0x3c 0>;
+	};
diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c
index c52c813ff3..61aa10e465 100644
--- a/drivers/misc/irq-uclass.c
+++ b/drivers/misc/irq-uclass.c
@@ -4,8 +4,11 @@
  * Written by Simon Glass <sjg at chromium.org>
  */
 
+#define LOG_CATEGORY UCLASS_IRQ
+
 #include <common.h>
 #include <dm.h>
+#include <dt-structs.h>
 #include <irq.h>
 #include <dm/device-internal.h>
 
@@ -49,6 +52,119 @@ int irq_restore_polarities(struct udevice *dev)
 	return ops->restore_polarities(dev);
 }
 
+int irq_read_and_clear(struct irq *irq)
+{
+	const struct irq_ops *ops = irq_get_ops(irq->dev);
+
+	if (!ops->read_and_clear)
+		return -ENOSYS;
+
+	return ops->read_and_clear(irq);
+}
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+int irq_get_by_index_platdata(struct udevice *dev, int index,
+			      struct phandle_1_arg *cells, struct irq *irq)
+{
+	int ret;
+
+	if (index != 0)
+		return -ENOSYS;
+	ret = uclass_get_device(UCLASS_IRQ, 0, &irq->dev);
+	if (ret)
+		return ret;
+	irq->id = cells[0].arg[0];
+
+	return 0;
+}
+#else
+static int irq_of_xlate_default(struct irq *irq,
+				struct ofnode_phandle_args *args)
+{
+	log_debug("(irq=%p)\n", irq);
+
+	if (args->args_count > 1) {
+		log_debug("Invaild args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	if (args->args_count)
+		irq->id = args->args[0];
+	else
+		irq->id = 0;
+
+	return 0;
+}
+
+static int irq_get_by_index_tail(int ret, ofnode node,
+				 struct ofnode_phandle_args *args,
+				 const char *list_name, int index,
+				 struct irq *irq)
+{
+	struct udevice *dev_irq;
+	const struct irq_ops *ops;
+
+	assert(irq);
+	irq->dev = NULL;
+	if (ret)
+		goto err;
+
+	ret = uclass_get_device_by_ofnode(UCLASS_IRQ, args->node, &dev_irq);
+	if (ret) {
+		log_debug("uclass_get_device_by_ofnode failed: err=%d\n", ret);
+		return ret;
+	}
+
+	irq->dev = dev_irq;
+
+	ops = irq_get_ops(dev_irq);
+
+	if (ops->of_xlate)
+		ret = ops->of_xlate(irq, args);
+	else
+		ret = irq_of_xlate_default(irq, args);
+	if (ret) {
+		log_debug("of_xlate() failed: %d\n", ret);
+		return ret;
+	}
+
+	return irq_request(dev_irq, irq);
+err:
+	log_debug("Node '%s', property '%s', failed to request IRQ index %d: %d\n",
+		  ofnode_get_name(node), list_name, index, ret);
+	return ret;
+}
+
+int irq_get_by_index(struct udevice *dev, int index, struct irq *irq)
+{
+	struct ofnode_phandle_args args;
+	int ret;
+
+	ret = dev_read_phandle_with_args(dev, "interrupts-extended",
+					 "#interrupt-cells", 0, index, &args);
+
+	return irq_get_by_index_tail(ret, dev_ofnode(dev), &args,
+				     "interrupts-extended", index > 0, irq);
+}
+#endif /* OF_PLATDATA */
+
+int irq_request(struct udevice *dev, struct irq *irq)
+{
+	const struct irq_ops *ops;
+
+	log_debug("(dev=%p, irq=%p)\n", dev, irq);
+	if (!irq)
+		return 0;
+	ops = irq_get_ops(dev);
+
+	irq->dev = dev;
+
+	if (!ops->request)
+		return 0;
+
+	return ops->request(irq);
+}
+
 int irq_first_device_type(enum irq_dev_t type, struct udevice **devp)
 {
 	int ret;
diff --git a/drivers/misc/irq_sandbox.c b/drivers/misc/irq_sandbox.c
index 011022ac62..54bc47c8d8 100644
--- a/drivers/misc/irq_sandbox.c
+++ b/drivers/misc/irq_sandbox.c
@@ -8,6 +8,18 @@
 #include <common.h>
 #include <dm.h>
 #include <irq.h>
+#include <asm/test.h>
+
+/**
+ * struct sandbox_irq_priv - private data for this driver
+ *
+ * @count: Counts the number calls to the read_and_clear() method
+ * @pending: true if an interrupt is pending, else false
+ */
+struct sandbox_irq_priv {
+	int count;
+	bool pending;
+};
 
 static int sandbox_set_polarity(struct udevice *dev, uint irq, bool active_low)
 {
@@ -35,11 +47,39 @@ static int sandbox_restore_polarities(struct udevice *dev)
 	return 0;
 }
 
+static int sandbox_irq_read_and_clear(struct irq *irq)
+{
+	struct sandbox_irq_priv *priv = dev_get_priv(irq->dev);
+
+	if (irq->id != SANDBOX_IRQN_PEND)
+		return -EINVAL;
+	priv->count++;
+	if (priv->pending) {
+		priv->pending = false;
+		return 1;
+	}
+
+	if (!(priv->count % 3))
+		priv->pending = true;
+
+	return 0;
+}
+
+static int sandbox_irq_of_xlate(struct irq *irq,
+				struct ofnode_phandle_args *args)
+{
+	irq->id = args->args[0];
+
+	return 0;
+}
+
 static const struct irq_ops sandbox_irq_ops = {
 	.route_pmc_gpio_gpe	= sandbox_route_pmc_gpio_gpe,
 	.set_polarity		= sandbox_set_polarity,
 	.snapshot_polarities	= sandbox_snapshot_polarities,
 	.restore_polarities	= sandbox_restore_polarities,
+	.read_and_clear		= sandbox_irq_read_and_clear,
+	.of_xlate		= sandbox_irq_of_xlate,
 };
 
 static const struct udevice_id sandbox_irq_ids[] = {
@@ -52,4 +92,5 @@ U_BOOT_DRIVER(sandbox_irq_drv) = {
 	.id		= UCLASS_IRQ,
 	.of_match	= sandbox_irq_ids,
 	.ops		= &sandbox_irq_ops,
+	.priv_auto_alloc_size	= sizeof(struct sandbox_irq_priv),
 };
diff --git a/include/irq.h b/include/irq.h
index 8b4e2ecfc0..b71afe9bee 100644
--- a/include/irq.h
+++ b/include/irq.h
@@ -19,8 +19,21 @@ enum irq_dev_t {
 	SANDBOX_IRQT_BASE,	/* Sandbox testing */
 };
 
+/**
+ * struct irq - A single irq line handled by an interrupt controller
+ *
+ * @dev: IRQ device that handles this irq
+ * @id: ID to identify this irq with the device
+ */
+struct irq {
+	struct udevice *dev;
+	ulong id;
+};
+
 /**
  * struct irq_ops - Operations for the IRQ
+ *
+ * Each IRQ device can handle mulitple IRQ lines
  */
 struct irq_ops {
 	/**
@@ -57,6 +70,55 @@ struct irq_ops {
 	 * @return 0
 	 */
 	int (*restore_polarities)(struct udevice *dev);
+
+	/**
+	 * read_and_clear() - get the value of an interrupt and clear it
+	 *
+	 * Clears the interrupt if pending
+	 *
+	 * @irq: IRQ line
+	 * @return 0 if interrupt is not pending, 1 if it was (and so has been
+	 *	cleared), -ve on error
+	 */
+	int (*read_and_clear)(struct irq *irq);
+	/**
+	 * of_xlate - Translate a client's device-tree (OF) irq specifier.
+	 *
+	 * The irq core calls this function as the first step in implementing
+	 * a client's irq_get_by_*() call.
+	 *
+	 * If this function pointer is set to NULL, the irq core will use a
+	 * default implementation, which assumes #interrupt-cells = <1>, and
+	 * that the DT cell contains a simple integer irq ID.
+	 *
+	 * @irq:	The irq struct to hold the translation result.
+	 * @args:	The irq specifier values from device tree.
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*of_xlate)(struct irq *irq, struct ofnode_phandle_args *args);
+	/**
+	 * request - Request a translated irq.
+	 *
+	 * The irq core calls this function as the second step in
+	 * implementing a client's irq_get_by_*() call, following a successful
+	 * xxx_xlate() call, or as the only step in implementing a client's
+	 * irq_request() call.
+	 *
+	 * @irq:	The irq struct to request; this has been fille in by
+	 *		a previoux xxx_xlate() function call, or by the caller
+	 *		of irq_request().
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*request)(struct irq *irq);
+	/**
+	 * free - Free a previously requested irq.
+	 *
+	 * This is the implementation of the client irq_free() API.
+	 *
+	 * @irq:	The irq to free.
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*free)(struct irq *irq);
 };
 
 #define irq_get_ops(dev)	((struct irq_ops *)(dev)->driver->ops)
@@ -96,6 +158,59 @@ int irq_snapshot_polarities(struct udevice *dev);
  */
 int irq_restore_polarities(struct udevice *dev);
 
+/**
+ * read_and_clear() - get the value of an interrupt and clear it
+ *
+ * Clears the interrupt if pending
+ *
+ * @dev: IRQ device
+ * @return 0 if interrupt is not pending, 1 if it was (and so has been
+ *	cleared), -ve on error
+ */
+int irq_read_and_clear(struct irq *irq);
+
+/**
+ * irq_get_by_index - Get/request an irq by integer index.
+ *
+ * This looks up and requests an irq. The index is relative to the client
+ * device; each device is assumed to have n irqs associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device irq indices to provider irqs may be via device-tree
+ * properties, board-provided mapping tables, or some other mechanism.
+ *
+ * @dev:	The client device.
+ * @index:	The index of the irq to request, within the client's list of
+ *		irqs.
+ * @irq:	A pointer to a irq struct to initialise.
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_get_by_index(struct udevice *dev, int index, struct irq *irq);
+
+/**
+ * irq_request - Request a irq by provider-specific ID.
+ *
+ * This requests a irq using a provider-specific ID. Generally, this function
+ * should not be used, since irq_get_by_index/name() provide an interface that
+ * better separates clients from intimate knowledge of irq providers.
+ * However, this function may be useful in core SoC-specific code.
+ *
+ * @dev:	The irq provider device.
+ * @irq:	A pointer to a irq struct to initialise. The caller must
+ *		have already initialised any field in this struct which the
+ *		irq provider uses to identify the irq.
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_request(struct udevice *dev, struct irq *irq);
+
+/**
+ * irq_free - Free a previously requested irq.
+ *
+ * @irq:	A irq struct that was previously successfully requested by
+ *		irq_request/get_by_*().
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_free(struct irq *irq);
+
 /**
  * irq_first_device_type() - Get a particular interrupt controller
  *
diff --git a/test/dm/irq.c b/test/dm/irq.c
index 0f23f108a7..192d80d7e1 100644
--- a/test/dm/irq.c
+++ b/test/dm/irq.c
@@ -44,3 +44,34 @@ static int dm_test_irq_type(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_irq_type, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
 
+/* Test of irq_read_and_clear() */
+static int dm_test_read_and_clear(struct unit_test_state *uts)
+{
+	struct irq irq;
+
+	ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &irq.dev));
+	irq.id = SANDBOX_IRQN_PEND;
+	ut_asserteq(0, irq_read_and_clear(&irq));
+	ut_asserteq(0, irq_read_and_clear(&irq));
+	ut_asserteq(0, irq_read_and_clear(&irq));
+	ut_asserteq(1, irq_read_and_clear(&irq));
+	ut_asserteq(0, irq_read_and_clear(&irq));
+
+	return 0;
+}
+DM_TEST(dm_test_read_and_clear, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of irq_request() */
+static int dm_test_request(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct irq irq;
+
+	ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+	ut_asserteq_str("a-test", dev->name);
+	ut_assertok(irq_get_by_index(dev, 0, &irq));
+	ut_asserteq(3, irq.id);
+
+	return 0;
+}
+DM_TEST(dm_test_request, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
