new file mode 100644
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _ASM_GUNYAH_H
+#define _ASM_GUNYAH_H
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+static inline int arch_gunyah_fill_irq_fwspec_params(u32 virq,
+ struct irq_fwspec *fwspec)
+{
+ /* Assume that Gunyah gave us an SPI or ESPI; defensively check it */
+ if (WARN(virq < 32, "Unexpected virq: %d\n", virq)) {
+ return -EINVAL;
+ } else if (virq <= 1019) {
+ fwspec->param_count = 3;
+ fwspec->param[0] = 0; /* GIC_SPI */
+ fwspec->param[1] = virq - 32; /* virq 32 -> SPI 0 */
+ fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+ } else if (WARN(virq < 4096, "Unexpected virq: %d\n", virq)) {
+ return -EINVAL;
+ } else if (virq < 5120) {
+ fwspec->param_count = 3;
+ fwspec->param[0] = 2; /* GIC_ESPI */
+ fwspec->param[1] = virq - 4096; /* virq 4096 -> ESPI 0 */
+ fwspec->param[2] = IRQ_TYPE_EDGE_RISING;
+ } else {
+ WARN(1, "Unexpected virq: %d\n", virq);
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
@@ -9,8 +9,10 @@
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <asm/gunyah.h>
#include <linux/gunyah_rsc_mgr.h>
/* clang-format off */
@@ -118,6 +120,7 @@ struct gunyah_rm_message {
* @send_lock: synchronization to allow only one request to be sent at a time
* @send_ready: completed when we know Tx message queue can take more messages
* @nh: notifier chain for clients interested in RM notification messages
+ * @parent_fwnode: Parent IRQ fwnode to translate Gunyah hwirqs to Linux irqs
*/
struct gunyah_rm {
struct device *dev;
@@ -133,6 +136,8 @@ struct gunyah_rm {
struct mutex send_lock;
struct completion send_ready;
struct blocking_notifier_head nh;
+
+ struct fwnode_handle *parent_fwnode;
};
/* Global resource manager instance */
@@ -177,6 +182,53 @@ static inline int gunyah_rm_error_remap(enum gunyah_rm_error rm_error)
}
}
+struct gunyah_resource *
+gunyah_rm_alloc_resource(struct gunyah_rm *rm,
+ struct gunyah_rm_hyp_resource *hyp_resource)
+{
+ struct gunyah_resource *ghrsc;
+ int ret;
+
+ ghrsc = kzalloc(sizeof(*ghrsc), GFP_KERNEL);
+ if (!ghrsc)
+ return NULL;
+
+ ghrsc->type = hyp_resource->type;
+ ghrsc->capid = le64_to_cpu(hyp_resource->cap_id);
+ ghrsc->irq = IRQ_NOTCONNECTED;
+ ghrsc->rm_label = le32_to_cpu(hyp_resource->resource_label);
+ if (hyp_resource->virq) {
+ struct irq_fwspec fwspec;
+
+
+ fwspec.fwnode = rm->parent_fwnode;
+ ret = arch_gunyah_fill_irq_fwspec_params(le32_to_cpu(hyp_resource->virq), &fwspec);
+ if (ret) {
+ dev_err(rm->dev,
+ "Failed to translate interrupt for resource %d label: %d: %d\n",
+ ghrsc->type, ghrsc->rm_label, ret);
+ }
+
+ ret = irq_create_fwspec_mapping(&fwspec);
+ if (ret < 0) {
+ dev_err(rm->dev,
+ "Failed to allocate interrupt for resource %d label: %d: %d\n",
+ ghrsc->type, ghrsc->rm_label, ret);
+ kfree(ghrsc);
+ return NULL;
+ }
+ ghrsc->irq = ret;
+ }
+
+ return ghrsc;
+}
+
+void gunyah_rm_free_resource(struct gunyah_resource *ghrsc)
+{
+ irq_dispose_mapping(ghrsc->irq);
+ kfree(ghrsc);
+}
+
static int gunyah_rm_init_message_payload(struct gunyah_rm_message *message,
const void *msg, size_t hdr_size,
size_t msg_size)
@@ -676,6 +728,7 @@ static int gunyah_rm_probe_rx_msgq(struct gunyah_rm *rm,
static int gunyah_rm_probe(struct platform_device *pdev)
{
+ struct device_node *parent_irq_node;
int ret;
gunyah_rm = devm_kzalloc(&pdev->dev, sizeof(*gunyah_rm), GFP_KERNEL);
@@ -695,6 +748,21 @@ static int gunyah_rm_probe(struct platform_device *pdev)
ret = gunyah_rm_probe_tx_msgq(gunyah_rm, pdev);
if (ret)
return ret;
+
+ parent_irq_node = of_irq_find_parent(pdev->dev.of_node);
+ if (!parent_irq_node) {
+ dev_err(&pdev->dev,
+ "Failed to find interrupt parent of resource manager\n");
+ return -ENODEV;
+ }
+
+ gunyah_rm->parent_fwnode = of_node_to_fwnode(parent_irq_node);
+ if (!gunyah_rm->parent_fwnode) {
+ dev_err(&pdev->dev,
+ "Failed to find interrupt parent domain of resource manager\n");
+ return -ENODEV;
+ }
+
/* assume RM is ready to receive messages from us */
complete(&gunyah_rm->send_ready);
@@ -35,6 +35,8 @@ struct gunyah_resource {
enum gunyah_resource_type type;
u64 capid;
unsigned int irq;
+
+ u32 rm_label;
};
/**
@@ -97,6 +97,10 @@ struct gunyah_rm_hyp_resources {
int gunyah_rm_get_hyp_resources(struct gunyah_rm *rm, u16 vmid,
struct gunyah_rm_hyp_resources **resources);
+struct gunyah_resource *
+gunyah_rm_alloc_resource(struct gunyah_rm *rm,
+ struct gunyah_rm_hyp_resource *hyp_resource);
+void gunyah_rm_free_resource(struct gunyah_resource *ghrsc);
int gunyah_rm_call(struct gunyah_rm *rsc_mgr, u32 message_id,
const void *req_buf, size_t req_buf_size, void **resp_buf,