From f7c7a200276ab65d43b123b6877570570d0af08f Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Fri, 15 May 2020 14:47:19 -0700 Subject: [PATCH] haven: irq: Support lending from other domains Client drivers may lend IRQs with knowledge of only their GPIO interrupt number, which would not directly have an underlying GIC hwirq. Thus, tweak RM's understanding of IRQs to be aware of IRQ domains. Now, the irq backed by GPIO will be translated to GIC domain. Change-Id: I191d9e072b14f4501f5bbeb5d5263f50e0eef8c1 Signed-off-by: Elliot Berman --- drivers/virt/haven/hh_irq_lend.c | 19 ++++----- drivers/virt/haven/hh_rm_core.c | 54 ++++++++++++++++++++++---- drivers/virt/haven/hh_rm_drv_private.h | 1 - drivers/virt/haven/hh_rm_iface.c | 1 - include/linux/haven/hh_irq_lend.h | 2 +- include/linux/haven/hh_rm_drv.h | 14 +++++++ 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/drivers/virt/haven/hh_irq_lend.c b/drivers/virt/haven/hh_irq_lend.c index 9fbbb8c205dd..76fd1f7fdc58 100644 --- a/drivers/virt/haven/hh_irq_lend.c +++ b/drivers/virt/haven/hh_irq_lend.c @@ -141,17 +141,14 @@ int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, int ret, virq; unsigned long flags; struct hh_irq_entry *entry; - struct irq_data *irq_data; if (label >= HH_IRQ_LABEL_MAX || !on_release) return -EINVAL; entry = &hh_irq_entries[label]; - irq_data = irq_get_irq_data(irq); - if (!irq_data) + if (hh_rm_irq_to_virq(irq, &virq)) return -EINVAL; - virq = irq_data->hwirq; spin_lock_irqsave(&hh_irq_lend_lock, flags); if (entry->state != HH_IRQ_STATE_NONE) { @@ -245,17 +242,18 @@ EXPORT_SYMBOL(hh_irq_wait_for_lend); * hh_irq_accept: Register to receive interrupts with a lent vIRQ * @label: vIRQ high-level label * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1 + * @type: IRQ flags to use when allowing RM to choose the IRQ. If irq parameter + * is specified, then type is unused. * * Returns the Linux IRQ# that vIRQ was registered to on success. * Returns <0 on error * This function is not thread-safe w.r.t. IRQ lend state. Do not race with * with hh_irq_release or another hh_irq_accept with same label. */ -int hh_irq_accept(enum hh_irq_label label, int irq) +int hh_irq_accept(enum hh_irq_label label, int irq, int type) { struct hh_irq_entry *entry; - const struct irq_data *irq_data; - int virq; + u32 virq; if (label >= HH_IRQ_LABEL_MAX) return -EINVAL; @@ -266,10 +264,8 @@ int hh_irq_accept(enum hh_irq_label label, int irq) return -EINVAL; if (irq != -1) { - irq_data = irq_get_irq_data(irq); - if (!irq_data) + if (hh_rm_irq_to_virq(irq, &virq)) return -EINVAL; - virq = irq_data->hwirq; } else virq = -1; @@ -278,8 +274,7 @@ int hh_irq_accept(enum hh_irq_label label, int irq) return virq; if (irq == -1) - irq = hh_rm_virq_to_linux_irq(virq - 32, GIC_SPI, - IRQ_TYPE_LEVEL_HIGH); + irq = hh_rm_virq_to_irq(virq, type); return irq; } diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index 330acc9c5a85..f1c0ae48dd0c 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -60,6 +60,7 @@ static DEFINE_MUTEX(hh_rm_send_lock); static DEFINE_IDA(hh_rm_free_virq_ida); static struct device_node *hh_rm_intc; +static struct irq_domain *hh_rm_irq_domain; static u32 hh_rm_base_virq; SRCU_NOTIFIER_HEAD_STATIC(hh_rm_notifier); @@ -624,19 +625,54 @@ out: return ret; } -int hh_rm_virq_to_linux_irq(u32 virq, u32 type, u32 trigger) +/** + * hh_rm_virq_to_irq: Get a Linux IRQ from a Haven-compatible vIRQ + * @virq: Haven-compatible vIRQ + * @type: IRQ trigger type (IRQ_TYPE_EDGE_RISING) + * + * Returns the mapped Linux IRQ# at Haven's IRQ domain (i.e. GIC SPI) + */ +int hh_rm_virq_to_irq(u32 virq, u32 type) { struct irq_fwspec fwspec = {}; + if (virq < 32 || virq >= GIC_V3_SPI_MAX) { + pr_warn("%s: expecting an SPI from RM, but got GIC IRQ %d\n", + __func__, virq); + } + fwspec.fwnode = of_node_to_fwnode(hh_rm_intc); fwspec.param_count = 3; - fwspec.param[0] = type; - fwspec.param[1] = virq; - fwspec.param[2] = trigger; + fwspec.param[0] = GIC_SPI; + fwspec.param[1] = virq - 32; + fwspec.param[2] = type; return irq_create_fwspec_mapping(&fwspec); } -EXPORT_SYMBOL(hh_rm_virq_to_linux_irq); +EXPORT_SYMBOL(hh_rm_virq_to_irq); + +/** + * hh_rm_irq_to_virq: Get a Haven-compatible vIRQ from a Linux IRQ + * @irq: Linux-assigned IRQ# + * @virq: out value where Haven-compatible vIRQ is stored + * + * Returns 0 upon success, -EINVAL if the Linux IRQ could not be mapped to + * a Haven vIRQ (i.e., the IRQ does not correspond to any GIC-level IRQ) + */ +int hh_rm_irq_to_virq(int irq, u32 *virq) +{ + struct irq_data *irq_data; + + irq_data = irq_domain_get_irq_data(hh_rm_irq_domain, irq); + if (!irq_data) + return -EINVAL; + + if (virq) + *virq = irq_data->hwirq; + + return 0; +} +EXPORT_SYMBOL(hh_rm_irq_to_virq); static int hh_rm_get_irq(struct hh_vm_get_hyp_res_resp_entry *res_entry) { @@ -673,8 +709,7 @@ static int hh_rm_get_irq(struct hh_vm_get_hyp_res_resp_entry *res_entry) return -EINVAL; } - return hh_rm_virq_to_linux_irq(virq - 32, GIC_SPI, - IRQ_TYPE_EDGE_RISING); + return hh_rm_virq_to_irq(virq, IRQ_TYPE_EDGE_RISING); err: ida_free(&hh_rm_free_virq_ida, virq - 32); @@ -838,6 +873,11 @@ static int hh_rm_drv_probe(struct platform_device *pdev) dev_err(dev, "Failed to get the IRQ parent node\n"); return -ENXIO; } + hh_rm_irq_domain = irq_find_host(hh_rm_intc); + if (!hh_rm_irq_domain) { + dev_err(dev, "Failed to get IRQ domain associated with RM\n"); + return -ENXIO; + } hh_rm_msgq_desc = hh_msgq_register(HH_MSGQ_LABEL_RM); if (IS_ERR_OR_NULL(hh_rm_msgq_desc)) diff --git a/drivers/virt/haven/hh_rm_drv_private.h b/drivers/virt/haven/hh_rm_drv_private.h index 243879f4f905..36f990b24189 100644 --- a/drivers/virt/haven/hh_rm_drv_private.h +++ b/drivers/virt/haven/hh_rm_drv_private.h @@ -281,7 +281,6 @@ struct hh_mem_notify_req_payload { /* End Message ID headers */ /* Common function declerations */ -int hh_rm_virq_to_linux_irq(u32 virq, u32 type, u32 trigger); int hh_update_vm_prop_table(enum hh_vm_names vm_name, struct hh_vm_property *vm_prop); void *hh_rm_call(hh_rm_msgid_t message_id, diff --git a/drivers/virt/haven/hh_rm_iface.c b/drivers/virt/haven/hh_rm_iface.c index 896ba49fc195..8d7446836cd9 100644 --- a/drivers/virt/haven/hh_rm_iface.c +++ b/drivers/virt/haven/hh_rm_iface.c @@ -373,7 +373,6 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) return ret; } -EXPORT_SYMBOL(hh_rm_vm_irq_release); /** * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has diff --git a/include/linux/haven/hh_irq_lend.h b/include/linux/haven/hh_irq_lend.h index 2815e6c97be2..f2737395b28d 100644 --- a/include/linux/haven/hh_irq_lend.h +++ b/include/linux/haven/hh_irq_lend.h @@ -26,7 +26,7 @@ int hh_irq_reclaim(enum hh_irq_label label); int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, hh_irq_handle_fn on_lend, void *data); -int hh_irq_accept(enum hh_irq_label label, int hw_irq); +int hh_irq_accept(enum hh_irq_label label, int irq, int type); int hh_irq_release(enum hh_irq_label label); #endif diff --git a/include/linux/haven/hh_rm_drv.h b/include/linux/haven/hh_rm_drv.h index 9dad98a13fcb..dc11541af543 100644 --- a/include/linux/haven/hh_rm_drv.h +++ b/include/linux/haven/hh_rm_drv.h @@ -183,6 +183,9 @@ int hh_rm_register_notifier(struct notifier_block *nb); int hh_rm_unregister_notifier(struct notifier_block *nb); /* Client APIs for IRQ management */ +int hh_rm_virq_to_irq(u32 virq, u32 type); +int hh_rm_irq_to_virq(int irq, u32 *virq); + int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq); int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, hh_virq_handle_t *virq_handle); @@ -227,6 +230,7 @@ int hh_rm_mem_lend(u8 mem_type, u8 flags, hh_label_t label, int hh_rm_mem_notify(hh_memparcel_handle_t handle, u8 flags, hh_label_t mem_info_tag, struct hh_notify_vmid_desc *vmid_desc); + #else /* RM client register notifications APIs */ static inline int hh_rm_register_notifier(struct notifier_block *nb) @@ -240,6 +244,16 @@ static inline int hh_rm_unregister_notifier(struct notifier_block *nb) } /* Client APIs for IRQ management */ +static inline int hh_rm_virq_to_irq(u32 virq) +{ + return -EINVAL; +} + +static inline int hh_rm_irq_to_virq(int irq, u32 *virq) +{ + return -EINVAL; +} + static inline int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) { return -EINVAL;