summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/sandbox/dts/test.dts5
-rw-r--r--doc/device-tree-bindings/interrupt-controller/interrupts.txt131
-rw-r--r--drivers/misc/irq-uclass.c116
-rw-r--r--drivers/misc/irq_sandbox.c41
-rw-r--r--include/irq.h115
-rw-r--r--test/dm/irq.c32
6 files changed, 439 insertions, 1 deletions
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@10140000 {
+ compatible = "arm,versatile-vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x10140000 0x1000>;
+ };
+
+ sic: intc@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@7000c000 {
+ gpioext: gpio-adnp@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@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@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@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
@@ -20,7 +20,20 @@ enum irq_dev_t {
};
/**
+ * 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)
@@ -97,6 +159,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
*
* On success this returns an activated interrupt device.
diff --git a/test/dm/irq.c b/test/dm/irq.c
index adbcffbe9c..192d80d7e1 100644
--- a/test/dm/irq.c
+++ b/test/dm/irq.c
@@ -43,3 +43,35 @@ static int dm_test_irq_type(struct unit_test_state *uts)
return 0;
}
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);