| /* |
| * Copyright (C) 2010-2016 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained from Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * @file mali_osk_irq.c |
| * Implementation of the OS abstraction layer for the kernel device driver |
| */ |
| |
| #include <linux/types.h> |
| #include <linux/version.h> |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 29)) |
| #include <mach/cpu.h> |
| #endif |
| #include <linux/slab.h> /* For memory allocation */ |
| #include <linux/interrupt.h> |
| #include <linux/wait.h> |
| #include <linux/sched.h> |
| |
| #include "mali_osk.h" |
| #include "mali_kernel_common.h" |
| |
| typedef struct _mali_osk_irq_t_struct { |
| u32 irqnum; |
| void *data; |
| _mali_osk_irq_uhandler_t uhandler; |
| } mali_osk_irq_object_t; |
| |
| typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *); |
| static irqreturn_t irq_handler_upper_half(int port_name, void *dev_id); /* , struct pt_regs *regs*/ |
| |
| #if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6 |
| u32 get_irqnum(struct _mali_osk_irq_t_struct* irq) |
| { |
| if (irq) |
| return irq->irqnum; |
| else |
| return 0; |
| } |
| #endif |
| |
| #if defined(DEBUG) |
| |
| struct test_interrupt_data { |
| _mali_osk_irq_ack_t ack_func; |
| void *probe_data; |
| mali_bool interrupt_received; |
| wait_queue_head_t wq; |
| }; |
| |
| static irqreturn_t test_interrupt_upper_half(int port_name, void *dev_id) |
| { |
| irqreturn_t ret = IRQ_NONE; |
| struct test_interrupt_data *data = (struct test_interrupt_data *)dev_id; |
| |
| if (_MALI_OSK_ERR_OK == data->ack_func(data->probe_data)) { |
| data->interrupt_received = MALI_TRUE; |
| wake_up(&data->wq); |
| ret = IRQ_HANDLED; |
| } |
| |
| return ret; |
| } |
| |
| static _mali_osk_errcode_t test_interrupt(u32 irqnum, |
| _mali_osk_irq_trigger_t trigger_func, |
| _mali_osk_irq_ack_t ack_func, |
| void *probe_data, |
| const char *description) |
| { |
| unsigned long irq_flags = 0; |
| struct test_interrupt_data data = { |
| .ack_func = ack_func, |
| .probe_data = probe_data, |
| .interrupt_received = MALI_FALSE, |
| }; |
| |
| #if defined(CONFIG_MALI_SHARED_INTERRUPTS) |
| irq_flags |= IRQF_SHARED; |
| #endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */ |
| |
| if (0 != request_irq(irqnum, test_interrupt_upper_half, irq_flags, description, &data)) { |
| MALI_DEBUG_PRINT(2, ("Unable to install test IRQ handler for core '%s'\n", description)); |
| return _MALI_OSK_ERR_FAULT; |
| } |
| |
| init_waitqueue_head(&data.wq); |
| |
| trigger_func(probe_data); |
| wait_event_timeout(data.wq, data.interrupt_received, 100); |
| |
| free_irq(irqnum, &data); |
| |
| if (data.interrupt_received) { |
| MALI_DEBUG_PRINT(3, ("%s: Interrupt test OK\n", description)); |
| return _MALI_OSK_ERR_OK; |
| } else { |
| MALI_PRINT_ERROR(("%s: Failed interrupt test on %u\n", description, irqnum)); |
| return _MALI_OSK_ERR_FAULT; |
| } |
| } |
| |
| #endif /* defined(DEBUG) */ |
| |
| _mali_osk_irq_t *_mali_osk_irq_init(u32 irqnum, _mali_osk_irq_uhandler_t uhandler, void *int_data, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *probe_data, const char *description) |
| { |
| mali_osk_irq_object_t *irq_object; |
| unsigned long irq_flags = 0; |
| |
| #if defined(CONFIG_MALI_SHARED_INTERRUPTS) |
| irq_flags |= IRQF_SHARED; |
| #endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */ |
| |
| irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL); |
| if (NULL == irq_object) { |
| return NULL; |
| } |
| |
| if (-1 == irqnum) { |
| /* Probe for IRQ */ |
| if ((NULL != trigger_func) && (NULL != ack_func)) { |
| unsigned long probe_count = 3; |
| _mali_osk_errcode_t err; |
| int irq; |
| |
| MALI_DEBUG_PRINT(2, ("Probing for irq\n")); |
| |
| do { |
| unsigned long mask; |
| |
| mask = probe_irq_on(); |
| trigger_func(probe_data); |
| |
| _mali_osk_time_ubusydelay(5); |
| |
| irq = probe_irq_off(mask); |
| err = ack_func(probe_data); |
| } while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--); |
| |
| if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1; |
| else irqnum = irq; |
| } else irqnum = -1; /* no probe functions, fault */ |
| |
| if (-1 != irqnum) { |
| /* found an irq */ |
| MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum)); |
| } else { |
| MALI_DEBUG_PRINT(2, ("Probe for irq failed\n")); |
| } |
| } |
| |
| irq_object->irqnum = irqnum; |
| irq_object->uhandler = uhandler; |
| irq_object->data = int_data; |
| |
| if (-1 == irqnum) { |
| MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description)); |
| kfree(irq_object); |
| return NULL; |
| } |
| |
| #if defined(DEBUG) |
| /* Verify that the configured interrupt settings are working */ |
| if (_MALI_OSK_ERR_OK != test_interrupt(irqnum, trigger_func, ack_func, probe_data, description)) { |
| MALI_DEBUG_PRINT(2, ("Test of IRQ(%d) handler for core '%s' failed\n", irqnum, description)); |
| kfree(irq_object); |
| return NULL; |
| } |
| #endif |
| |
| if (0 != request_irq(irqnum, irq_handler_upper_half, irq_flags, description, irq_object)) { |
| MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description)); |
| kfree(irq_object); |
| return NULL; |
| } |
| |
| return irq_object; |
| } |
| |
| void _mali_osk_irq_term(_mali_osk_irq_t *irq) |
| { |
| mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; |
| free_irq(irq_object->irqnum, irq_object); |
| kfree(irq_object); |
| } |
| |
| |
| /** This function is called directly in interrupt context from the OS just after |
| * the CPU get the hw-irq from mali, or other devices on the same IRQ-channel. |
| * It is registered one of these function for each mali core. When an interrupt |
| * arrives this function will be called equal times as registered mali cores. |
| * That means that we only check one mali core in one function call, and the |
| * core we check for each turn is given by the \a dev_id variable. |
| * If we detect an pending interrupt on the given core, we mask the interrupt |
| * out by settging the core's IRQ_MASK register to zero. |
| * Then we schedule the mali_core_irq_handler_bottom_half to run as high priority |
| * work queue job. |
| */ |
| static irqreturn_t irq_handler_upper_half(int port_name, void *dev_id) /* , struct pt_regs *regs*/ |
| { |
| irqreturn_t ret = IRQ_NONE; |
| mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id; |
| |
| if (_MALI_OSK_ERR_OK == irq_object->uhandler(irq_object->data)) { |
| ret = IRQ_HANDLED; |
| } |
| |
| return ret; |
| } |