diff options
author | Simon Glass <sjg@chromium.org> | 2020-02-06 09:55:00 -0700 |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2020-02-07 22:46:32 +0800 |
commit | 025543554c36615a66d66c154f3f763ac788ee15 (patch) | |
tree | c792b8de3065a268ee654dc47259c9ef82d08914 /drivers/misc | |
parent | d9a5fad80857005703d46b6ea27217baa17eb142 (diff) | |
download | u-boot-025543554c36615a66d66c154f3f763ac788ee15.tar.gz u-boot-025543554c36615a66d66c154f3f763ac788ee15.tar.bz2 u-boot-025543554c36615a66d66c154f3f763ac788ee15.zip |
dm: irq: Add support for requesting interrupts
At present driver model supports the IRQ uclass but there is no way to
request a particular interrupt for a driver.
Add a mechanism, similar to clock and reset, to read the interrupts
required by a device from the device tree and to request those interrupts.
U-Boot itself does not have interrupt-driven handlers, so just provide a
means to read and clear an interrupt. This can be useful to handle
peripherals which must use an interrupt to determine when data is
available, for example.
Bring over the basic binding file as well, from Linux v5.4. Note that the
older binding is not supported in U-Boot; the newer 'special form' must be
used.
Add a simple test of the new functionality.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/irq-uclass.c | 116 | ||||
-rw-r--r-- | drivers/misc/irq_sandbox.c | 41 |
2 files changed, 157 insertions, 0 deletions
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), }; |