Files
android_kernel_samsung_sm8750/arch/arm64/gunyah/irq.c
2025-08-11 14:29:00 +02:00

134 lines
2.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*
*/
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <linux/gunyah/gh_rm_drv.h>
#include <asm/gunyah.h>
#define GIC_V3_SPI_MAX 1019
#define GH_RM_NO_IRQ_ALLOC -1
#define IRQ_OFFSET 32
static DEFINE_IDR(gh_rm_free_virq_idr);
/**
* gh_get_irq: Get a Linux IRQ from a Gunyah-compatible vIRQ
* @virq: Gunyah-compatible vIRQ
* @type: IRQ trigger type (IRQ_TYPE_EDGE_RISING)
* @fw_handle: fw node handle
*
* Returns the mapped Linux IRQ# at Gunyah's IRQ domain (i.e. GIC SPI)
*/
int gh_get_irq(u32 virq, u32 type, struct fwnode_handle *fw_handle)
{
struct irq_fwspec fwspec = {};
int ret;
ret = arch_gunyah_fill_irq_fwspec_params(virq, &fwspec);
if (ret) {
pr_err("Failed to translate interrupt: type: %d virq: %d: ret: %d\n",
type, virq, ret);
return ret;
}
fwspec.fwnode = fw_handle;
fwspec.param[2] = type;
return irq_create_fwspec_mapping(&fwspec);
}
EXPORT_SYMBOL_GPL(gh_get_irq);
/**
* gh_get_virq: Allocate a new IRQ if RM-VM hasn't already done already
* @base_virq: The base virtual IRQ number.
* @virq: The virtual IRQ number.
*
* Returns Gunyah compatible vIRQ to bind to.
*/
int gh_get_virq(int base_virq, int virq)
{
int ret;
/* Get the next free vIRQ.
* Subtract IRQ_OFFSET from the base virq to get the base SPI.
*
* Assoiate the address of the idr variable itself as a lookup
* ptr. This will help us to free the virq later.
*/
ret = virq = idr_alloc(&gh_rm_free_virq_idr,
&gh_rm_free_virq_idr,
base_virq - IRQ_OFFSET,
GIC_V3_SPI_MAX, GFP_KERNEL);
if (ret < 0)
return ret;
/* Add IRQ_OFFSET offset to make interrupt as hwirq */
virq += IRQ_OFFSET;
return virq;
}
EXPORT_SYMBOL_GPL(gh_get_virq);
/**
* gh_put_virq: Deallocates a vIRQ.
* @irq: The IRQ number.
*
* Returns 0 on success and EINVAL if no IRQ was found.
*/
int gh_put_virq(int virq)
{
void *idr_ptr;
int virq_num;
virq_num = virq - IRQ_OFFSET;
/* If the idr_find() returns a valid ptr, it means that the
* virq was allocated by the kernel itself and not by hyp.
* Release the IRQ and free the allocation if that's true.
*/
idr_ptr = idr_find(&gh_rm_free_virq_idr, virq_num);
if (idr_ptr) {
idr_remove(&gh_rm_free_virq_idr, virq_num);
return 0;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(gh_put_virq);
/**
* gh_put_irq: Deallocate an Linux IRQ.
* @irq: The IRQ number.
*
* Returns 0 on success and EINVAL if no IRQ was found.
*/
int gh_put_irq(int irq)
{
struct irq_data *irq_data;
unsigned long virq;
if (irq <= 0)
return -EINVAL;
irq_data = irq_get_irq_data(irq);
if (!irq_data)
return -EINVAL;
virq = irq_data->hwirq;
irq_dispose_mapping(irq);
return gh_put_virq(virq);
}
EXPORT_SYMBOL_GPL(gh_put_irq);