sdk-hwV1.3/lichee/linux-4.9/drivers/aipu/src/aipu_irq.c

102 lines
2.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018-2021 Arm Technology (China) Co., Ltd. All rights reserved. */
/**
* @file aipu_irq.c
* Implementation of the interrupt request and handlers' abstraction
*/
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include "aipu_irq.h"
#include "aipu_core.h"
static irqreturn_t aipu_irq_handler_upper_half(int irq, void *dev_id)
{
struct aipu_core *core = (struct aipu_core *)(((struct device *)dev_id)->driver_data);
return core->ops->upper_half(core);
}
static void aipu_irq_handler_bottom_half(struct work_struct *work)
{
struct aipu_irq_object *irq_obj = NULL;
struct aipu_core *core = NULL;
if (work) {
irq_obj = container_of(work, struct aipu_irq_object, work);
core = irq_obj->core;
core->ops->bottom_half(core);
}
}
struct aipu_irq_object *aipu_create_irq_object(u32 irqnum, void *core, char *description)
{
int ret = 0;
struct aipu_irq_object *irq_obj = NULL;
if ((!core) || (!description))
return NULL;
irq_obj = kzalloc(sizeof(*irq_obj), GFP_KERNEL);
if (!irq_obj)
goto finish;
irq_obj->aipu_wq = NULL;
irq_obj->irqnum = 0;
irq_obj->dev = ((struct aipu_core *)core)->dev;
irq_obj->aipu_wq = create_singlethread_workqueue(description);
if (!irq_obj->aipu_wq)
goto err_handle;
INIT_WORK(&irq_obj->work, aipu_irq_handler_bottom_half);
ret = request_irq(irqnum, aipu_irq_handler_upper_half, IRQF_SHARED | IRQF_TRIGGER_HIGH,
description, irq_obj->dev);
if (ret) {
dev_err(irq_obj->dev, "request IRQ (num %u) failed! (errno = %d)", irqnum, ret);
goto err_handle;
}
irq_obj->irqnum = irqnum;
irq_obj->core = core;
/* success */
goto finish;
err_handle:
aipu_destroy_irq_object(irq_obj);
irq_obj = NULL;
finish:
return irq_obj;
}
void aipu_irq_schedulework(struct aipu_irq_object *irq_obj)
{
if (irq_obj)
queue_work(irq_obj->aipu_wq, &irq_obj->work);
}
void aipu_irq_flush_workqueue(struct aipu_irq_object *irq_obj)
{
/* only one workqueue currently */
flush_workqueue(irq_obj->aipu_wq);
}
void aipu_destroy_irq_object(struct aipu_irq_object *irq_obj)
{
if (irq_obj) {
if (irq_obj->aipu_wq) {
flush_workqueue(irq_obj->aipu_wq);
destroy_workqueue(irq_obj->aipu_wq);
irq_obj->aipu_wq = NULL;
}
if (irq_obj->irqnum)
free_irq(irq_obj->irqnum, irq_obj->dev);
kfree(irq_obj);
flush_scheduled_work();
}
}