/* * * (C) COPYRIGHT 2014-2015 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. * */ #include #include #include #include #if !defined(CONFIG_MALI_NO_MALI) /* GPU IRQ Tags */ #define JOB_IRQ_TAG 0 #define MMU_IRQ_TAG 1 #define GPU_IRQ_TAG 2 static void *kbase_tag(void *ptr, u32 tag) { return (void *)(((uintptr_t) ptr) | tag); } static void *kbase_untag(void *ptr) { return (void *)(((uintptr_t) ptr) & ~3); } static irqreturn_t kbase_job_irq_handler(int irq, void *data) { unsigned long flags; struct kbase_device *kbdev = kbase_untag(data); u32 val; spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags); if (!kbdev->pm.backend.gpu_powered) { /* GPU is turned off - IRQ is not for us */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); return IRQ_NONE; } val = kbase_reg_read(kbdev, JOB_CONTROL_REG(JOB_IRQ_STATUS), NULL); #ifdef CONFIG_MALI_DEBUG if (!kbdev->pm.backend.driver_ready_for_irqs) dev_warn(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n", __func__, irq, val); #endif /* CONFIG_MALI_DEBUG */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); if (!val) return IRQ_NONE; dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val); kbase_job_done(kbdev, val); return IRQ_HANDLED; } KBASE_EXPORT_TEST_API(kbase_job_irq_handler); static irqreturn_t kbase_mmu_irq_handler(int irq, void *data) { unsigned long flags; struct kbase_device *kbdev = kbase_untag(data); u32 val; spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags); if (!kbdev->pm.backend.gpu_powered) { /* GPU is turned off - IRQ is not for us */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); return IRQ_NONE; } atomic_inc(&kbdev->faults_pending); val = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_STATUS), NULL); #ifdef CONFIG_MALI_DEBUG if (!kbdev->pm.backend.driver_ready_for_irqs) dev_warn(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n", __func__, irq, val); #endif /* CONFIG_MALI_DEBUG */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); if (!val) { atomic_dec(&kbdev->faults_pending); return IRQ_NONE; } dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val); kbase_mmu_interrupt(kbdev, val); atomic_dec(&kbdev->faults_pending); return IRQ_HANDLED; } static irqreturn_t kbase_gpu_irq_handler(int irq, void *data) { unsigned long flags; struct kbase_device *kbdev = kbase_untag(data); u32 val; spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags); if (!kbdev->pm.backend.gpu_powered) { /* GPU is turned off - IRQ is not for us */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); return IRQ_NONE; } val = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_STATUS), NULL); #ifdef CONFIG_MALI_DEBUG if (!kbdev->pm.backend.driver_ready_for_irqs) dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n", __func__, irq, val); #endif /* CONFIG_MALI_DEBUG */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); if (!val) return IRQ_NONE; dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val); kbase_gpu_interrupt(kbdev, val); return IRQ_HANDLED; } static irq_handler_t kbase_handler_table[] = { [JOB_IRQ_TAG] = kbase_job_irq_handler, [MMU_IRQ_TAG] = kbase_mmu_irq_handler, [GPU_IRQ_TAG] = kbase_gpu_irq_handler, }; #ifdef CONFIG_MALI_DEBUG #define JOB_IRQ_HANDLER JOB_IRQ_TAG #define MMU_IRQ_HANDLER MMU_IRQ_TAG #define GPU_IRQ_HANDLER GPU_IRQ_TAG /** * kbase_set_custom_irq_handler - Set a custom IRQ handler * @kbdev: Device for which the handler is to be registered * @custom_handler: Handler to be registered * @irq_type: Interrupt type * * Registers given interrupt handler for requested interrupt type * In the case where irq handler is not specified, the default handler shall be * registered * * Return: 0 case success, error code otherwise */ int kbase_set_custom_irq_handler(struct kbase_device *kbdev, irq_handler_t custom_handler, int irq_type) { int result = 0; irq_handler_t requested_irq_handler = NULL; KBASE_DEBUG_ASSERT((JOB_IRQ_HANDLER <= irq_type) && (GPU_IRQ_HANDLER >= irq_type)); /* Release previous handler */ if (kbdev->irqs[irq_type].irq) free_irq(kbdev->irqs[irq_type].irq, kbase_tag(kbdev, irq_type)); requested_irq_handler = (NULL != custom_handler) ? custom_handler : kbase_handler_table[irq_type]; if (0 != request_irq(kbdev->irqs[irq_type].irq, requested_irq_handler, kbdev->irqs[irq_type].flags | IRQF_SHARED, dev_name(kbdev->dev), kbase_tag(kbdev, irq_type))) { result = -EINVAL; dev_err(kbdev->dev, "Can't request interrupt %d (index %d)\n", kbdev->irqs[irq_type].irq, irq_type); #ifdef CONFIG_SPARSE_IRQ dev_err(kbdev->dev, "You have CONFIG_SPARSE_IRQ support enabled - is the interrupt number correct for this configuration?\n"); #endif /* CONFIG_SPARSE_IRQ */ } return result; } KBASE_EXPORT_TEST_API(kbase_set_custom_irq_handler); /* test correct interrupt assigment and reception by cpu */ struct kbasep_irq_test { struct hrtimer timer; wait_queue_head_t wait; int triggered; u32 timeout; }; static struct kbasep_irq_test kbasep_irq_test_data; #define IRQ_TEST_TIMEOUT 500 static irqreturn_t kbase_job_irq_test_handler(int irq, void *data) { unsigned long flags; struct kbase_device *kbdev = kbase_untag(data); u32 val; spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags); if (!kbdev->pm.backend.gpu_powered) { /* GPU is turned off - IRQ is not for us */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); return IRQ_NONE; } val = kbase_reg_read(kbdev, JOB_CONTROL_REG(JOB_IRQ_STATUS), NULL); spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); if (!val) return IRQ_NONE; dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val); kbasep_irq_test_data.triggered = 1; wake_up(&kbasep_irq_test_data.wait); kbase_reg_write(kbdev, JOB_CONTROL_REG(JOB_IRQ_CLEAR), val, NULL); return IRQ_HANDLED; } static irqreturn_t kbase_mmu_irq_test_handler(int irq, void *data) { unsigned long flags; struct kbase_device *kbdev = kbase_untag(data); u32 val; spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags); if (!kbdev->pm.backend.gpu_powered) { /* GPU is turned off - IRQ is not for us */ spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); return IRQ_NONE; } val = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_STATUS), NULL); spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags); if (!val) return IRQ_NONE; dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val); kbasep_irq_test_data.triggered = 1; wake_up(&kbasep_irq_test_data.wait); kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_CLEAR), val, NULL); return IRQ_HANDLED; } static enum hrtimer_restart kbasep_test_interrupt_timeout(struct hrtimer *timer) { struct kbasep_irq_test *test_data = container_of(timer, struct kbasep_irq_test, timer); test_data->timeout = 1; test_data->triggered = 1; wake_up(&test_data->wait); return HRTIMER_NORESTART; } static int kbasep_common_test_interrupt( struct kbase_device * const kbdev, u32 tag) { int err = 0; irq_handler_t test_handler; u32 old_mask_val; u16 mask_offset; u16 rawstat_offset; switch (tag) { case JOB_IRQ_TAG: test_handler = kbase_job_irq_test_handler; rawstat_offset = JOB_CONTROL_REG(JOB_IRQ_RAWSTAT); mask_offset = JOB_CONTROL_REG(JOB_IRQ_MASK); break; case MMU_IRQ_TAG: test_handler = kbase_mmu_irq_test_handler; rawstat_offset = MMU_REG(MMU_IRQ_RAWSTAT); mask_offset = MMU_REG(MMU_IRQ_MASK); break; case GPU_IRQ_TAG: /* already tested by pm_driver - bail out */ default: return 0; } /* store old mask */ old_mask_val = kbase_reg_read(kbdev, mask_offset, NULL); /* mask interrupts */ kbase_reg_write(kbdev, mask_offset, 0x0, NULL); if (kbdev->irqs[tag].irq) { /* release original handler and install test handler */ if (kbase_set_custom_irq_handler(kbdev, test_handler, tag) != 0) { err = -EINVAL; } else { kbasep_irq_test_data.timeout = 0; hrtimer_init(&kbasep_irq_test_data.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); kbasep_irq_test_data.timer.function = kbasep_test_interrupt_timeout; /* trigger interrupt */ kbase_reg_write(kbdev, mask_offset, 0x1, NULL); kbase_reg_write(kbdev, rawstat_offset, 0x1, NULL); hrtimer_start(&kbasep_irq_test_data.timer, HR_TIMER_DELAY_MSEC(IRQ_TEST_TIMEOUT), HRTIMER_MODE_REL); wait_event(kbasep_irq_test_data.wait, kbasep_irq_test_data.triggered != 0); if (kbasep_irq_test_data.timeout != 0) { dev_err(kbdev->dev, "Interrupt %d (index %d) didn't reach CPU.\n", kbdev->irqs[tag].irq, tag); err = -EINVAL; } else { dev_dbg(kbdev->dev, "Interrupt %d (index %d) reached CPU.\n", kbdev->irqs[tag].irq, tag); } hrtimer_cancel(&kbasep_irq_test_data.timer); kbasep_irq_test_data.triggered = 0; /* mask interrupts */ kbase_reg_write(kbdev, mask_offset, 0x0, NULL); /* release test handler */ free_irq(kbdev->irqs[tag].irq, kbase_tag(kbdev, tag)); } /* restore original interrupt */ if (request_irq(kbdev->irqs[tag].irq, kbase_handler_table[tag], kbdev->irqs[tag].flags | IRQF_SHARED, dev_name(kbdev->dev), kbase_tag(kbdev, tag))) { dev_err(kbdev->dev, "Can't restore original interrupt %d (index %d)\n", kbdev->irqs[tag].irq, tag); err = -EINVAL; } } /* restore old mask */ kbase_reg_write(kbdev, mask_offset, old_mask_val, NULL); return err; } int kbasep_common_test_interrupt_handlers( struct kbase_device * const kbdev) { int err; init_waitqueue_head(&kbasep_irq_test_data.wait); kbasep_irq_test_data.triggered = 0; /* A suspend won't happen during startup/insmod */ kbase_pm_context_active(kbdev); err = kbasep_common_test_interrupt(kbdev, JOB_IRQ_TAG); if (err) { dev_err(kbdev->dev, "Interrupt JOB_IRQ didn't reach CPU. Check interrupt assignments.\n"); goto out; } err = kbasep_common_test_interrupt(kbdev, MMU_IRQ_TAG); if (err) { dev_err(kbdev->dev, "Interrupt MMU_IRQ didn't reach CPU. Check interrupt assignments.\n"); goto out; } dev_dbg(kbdev->dev, "Interrupts are correctly assigned.\n"); out: kbase_pm_context_idle(kbdev); return err; } #endif /* CONFIG_MALI_DEBUG */ int kbase_install_interrupts(struct kbase_device *kbdev) { u32 nr = ARRAY_SIZE(kbase_handler_table); int err; u32 i; for (i = 0; i < nr; i++) { err = request_irq(kbdev->irqs[i].irq, kbase_handler_table[i], kbdev->irqs[i].flags | IRQF_SHARED, dev_name(kbdev->dev), kbase_tag(kbdev, i)); if (err) { dev_err(kbdev->dev, "Can't request interrupt %d (index %d)\n", kbdev->irqs[i].irq, i); #ifdef CONFIG_SPARSE_IRQ dev_err(kbdev->dev, "You have CONFIG_SPARSE_IRQ support enabled - is the interrupt number correct for this configuration?\n"); #endif /* CONFIG_SPARSE_IRQ */ goto release; } } return 0; release: while (i-- > 0) free_irq(kbdev->irqs[i].irq, kbase_tag(kbdev, i)); return err; } void kbase_release_interrupts(struct kbase_device *kbdev) { u32 nr = ARRAY_SIZE(kbase_handler_table); u32 i; for (i = 0; i < nr; i++) { if (kbdev->irqs[i].irq) free_irq(kbdev->irqs[i].irq, kbase_tag(kbdev, i)); } } void kbase_synchronize_irqs(struct kbase_device *kbdev) { u32 nr = ARRAY_SIZE(kbase_handler_table); u32 i; for (i = 0; i < nr; i++) { if (kbdev->irqs[i].irq) synchronize_irq(kbdev->irqs[i].irq); } } #endif /* !defined(CONFIG_MALI_NO_MALI) */