Merge "haven: irq: Support lending from other domains"

This commit is contained in:
qctecmdr 2020-06-07 19:33:14 -07:00 • committed by Gerrit - the friendly Code Review server
commit 6d688f0472
6 changed files with 69 additions and 22 deletions

View file

@ -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;
}

View file

@ -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))

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;