19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27ede0b0bSTerje Bergstrom /* 37ede0b0bSTerje Bergstrom * Tegra host1x Interrupt Management 47ede0b0bSTerje Bergstrom * 5625d4ffbSMikko Perttunen * Copyright (c) 2010-2021, NVIDIA Corporation. 67ede0b0bSTerje Bergstrom */ 77ede0b0bSTerje Bergstrom 87ede0b0bSTerje Bergstrom #include <linux/clk.h> 9*4c27ac45SMikko Perttunen #include <linux/interrupt.h> 107ede0b0bSTerje Bergstrom #include "dev.h" 11687db220SMikko Perttunen #include "fence.h" 127ede0b0bSTerje Bergstrom #include "intr.h" 137ede0b0bSTerje Bergstrom 14625d4ffbSMikko Perttunen static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list, 15625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence) 167ede0b0bSTerje Bergstrom { 17625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence_in_list; 18625d4ffbSMikko Perttunen 19625d4ffbSMikko Perttunen list_for_each_entry_reverse(fence_in_list, &list->list, list) { 20625d4ffbSMikko Perttunen if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { 21625d4ffbSMikko Perttunen /* Fence in list is before us, we can insert here */ 22625d4ffbSMikko Perttunen list_add(&fence->list, &fence_in_list->list); 23625d4ffbSMikko Perttunen return; 24625d4ffbSMikko Perttunen } 257ede0b0bSTerje Bergstrom } 267ede0b0bSTerje Bergstrom 27625d4ffbSMikko Perttunen /* Add as first in list */ 28625d4ffbSMikko Perttunen list_add(&fence->list, &list->list); 29625d4ffbSMikko Perttunen } 307ede0b0bSTerje Bergstrom 31625d4ffbSMikko Perttunen static void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp) 32625d4ffbSMikko Perttunen { 33625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence; 34625d4ffbSMikko Perttunen 35625d4ffbSMikko Perttunen if (!list_empty(&sp->fences.list)) { 36625d4ffbSMikko Perttunen fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list); 37625d4ffbSMikko Perttunen 38625d4ffbSMikko Perttunen host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold); 39625d4ffbSMikko Perttunen host1x_hw_intr_enable_syncpt_intr(host, sp->id); 40625d4ffbSMikko Perttunen } else { 41625d4ffbSMikko Perttunen host1x_hw_intr_disable_syncpt_intr(host, sp->id); 42625d4ffbSMikko Perttunen } 43625d4ffbSMikko Perttunen } 44625d4ffbSMikko Perttunen 45625d4ffbSMikko Perttunen void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence) 46625d4ffbSMikko Perttunen { 47625d4ffbSMikko Perttunen struct host1x_fence_list *fence_list = &fence->sp->fences; 48625d4ffbSMikko Perttunen 49625d4ffbSMikko Perttunen INIT_LIST_HEAD(&fence->list); 50625d4ffbSMikko Perttunen 51625d4ffbSMikko Perttunen host1x_intr_add_fence_to_list(fence_list, fence); 52625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, fence->sp); 53625d4ffbSMikko Perttunen } 54625d4ffbSMikko Perttunen 55625d4ffbSMikko Perttunen bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence) 56625d4ffbSMikko Perttunen { 57625d4ffbSMikko Perttunen struct host1x_fence_list *fence_list = &fence->sp->fences; 58625d4ffbSMikko Perttunen unsigned long irqflags; 59625d4ffbSMikko Perttunen 60625d4ffbSMikko Perttunen spin_lock_irqsave(&fence_list->lock, irqflags); 61625d4ffbSMikko Perttunen 62625d4ffbSMikko Perttunen if (list_empty(&fence->list)) { 63625d4ffbSMikko Perttunen spin_unlock_irqrestore(&fence_list->lock, irqflags); 647ede0b0bSTerje Bergstrom return false; 657ede0b0bSTerje Bergstrom } 667ede0b0bSTerje Bergstrom 67625d4ffbSMikko Perttunen list_del_init(&fence->list); 68625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, fence->sp); 69625d4ffbSMikko Perttunen 70625d4ffbSMikko Perttunen spin_unlock_irqrestore(&fence_list->lock, irqflags); 71625d4ffbSMikko Perttunen 727ede0b0bSTerje Bergstrom return true; 737ede0b0bSTerje Bergstrom } 747ede0b0bSTerje Bergstrom 75625d4ffbSMikko Perttunen void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id) 767ede0b0bSTerje Bergstrom { 77625d4ffbSMikko Perttunen struct host1x_syncpt *sp = &host->syncpt[id]; 78625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence, *tmp; 79625d4ffbSMikko Perttunen unsigned int value; 807ede0b0bSTerje Bergstrom 81625d4ffbSMikko Perttunen value = host1x_syncpt_load(sp); 82625d4ffbSMikko Perttunen 83625d4ffbSMikko Perttunen spin_lock(&sp->fences.lock); 84625d4ffbSMikko Perttunen 85625d4ffbSMikko Perttunen list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) { 86625d4ffbSMikko Perttunen if (((value - fence->threshold) & 0x80000000U) != 0U) { 87625d4ffbSMikko Perttunen /* Fence is not yet expired, we are done */ 887ede0b0bSTerje Bergstrom break; 896579324aSTerje Bergstrom } 906579324aSTerje Bergstrom 91625d4ffbSMikko Perttunen list_del_init(&fence->list); 92625d4ffbSMikko Perttunen host1x_fence_signal(fence); 937ede0b0bSTerje Bergstrom } 947ede0b0bSTerje Bergstrom 95625d4ffbSMikko Perttunen /* Re-enable interrupt if necessary */ 96625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, sp); 977ede0b0bSTerje Bergstrom 98625d4ffbSMikko Perttunen spin_unlock(&sp->fences.lock); 997ede0b0bSTerje Bergstrom } 1007ede0b0bSTerje Bergstrom 101625d4ffbSMikko Perttunen int host1x_intr_init(struct host1x *host) 1027ede0b0bSTerje Bergstrom { 103*4c27ac45SMikko Perttunen struct host1x_intr_irq_data *irq_data; 1047ede0b0bSTerje Bergstrom unsigned int id; 105*4c27ac45SMikko Perttunen int i, err; 1067ede0b0bSTerje Bergstrom 1077ede0b0bSTerje Bergstrom mutex_init(&host->intr_mutex); 1087ede0b0bSTerje Bergstrom 109625d4ffbSMikko Perttunen for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { 110625d4ffbSMikko Perttunen struct host1x_syncpt *syncpt = &host->syncpt[id]; 1117ede0b0bSTerje Bergstrom 112625d4ffbSMikko Perttunen spin_lock_init(&syncpt->fences.lock); 113625d4ffbSMikko Perttunen INIT_LIST_HEAD(&syncpt->fences.list); 1147ede0b0bSTerje Bergstrom } 1157ede0b0bSTerje Bergstrom 116*4c27ac45SMikko Perttunen irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL); 117*4c27ac45SMikko Perttunen if (!irq_data) 118*4c27ac45SMikko Perttunen return -ENOMEM; 119*4c27ac45SMikko Perttunen 120*4c27ac45SMikko Perttunen host1x_hw_intr_disable_all_syncpt_intrs(host); 121*4c27ac45SMikko Perttunen 122*4c27ac45SMikko Perttunen for (i = 0; i < host->num_syncpt_irqs; i++) { 123*4c27ac45SMikko Perttunen irq_data[i].host = host; 124*4c27ac45SMikko Perttunen irq_data[i].offset = i; 125*4c27ac45SMikko Perttunen 126*4c27ac45SMikko Perttunen err = devm_request_irq(host->dev, host->syncpt_irqs[i], 127*4c27ac45SMikko Perttunen host->intr_op->isr, IRQF_SHARED, 128*4c27ac45SMikko Perttunen "host1x_syncpt", &irq_data[i]); 129*4c27ac45SMikko Perttunen if (err < 0) 130*4c27ac45SMikko Perttunen return err; 131*4c27ac45SMikko Perttunen } 132*4c27ac45SMikko Perttunen 1337ede0b0bSTerje Bergstrom return 0; 1347ede0b0bSTerje Bergstrom } 1357ede0b0bSTerje Bergstrom 1367ede0b0bSTerje Bergstrom void host1x_intr_deinit(struct host1x *host) 1377ede0b0bSTerje Bergstrom { 1387ede0b0bSTerje Bergstrom } 1397ede0b0bSTerje Bergstrom 1407ede0b0bSTerje Bergstrom void host1x_intr_start(struct host1x *host) 1417ede0b0bSTerje Bergstrom { 1427ede0b0bSTerje Bergstrom u32 hz = clk_get_rate(host->clk); 1437ede0b0bSTerje Bergstrom int err; 1447ede0b0bSTerje Bergstrom 1457ede0b0bSTerje Bergstrom mutex_lock(&host->intr_mutex); 146625d4ffbSMikko Perttunen err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000)); 1477ede0b0bSTerje Bergstrom if (err) { 1487ede0b0bSTerje Bergstrom mutex_unlock(&host->intr_mutex); 1497ede0b0bSTerje Bergstrom return; 1507ede0b0bSTerje Bergstrom } 1517ede0b0bSTerje Bergstrom mutex_unlock(&host->intr_mutex); 1527ede0b0bSTerje Bergstrom } 1537ede0b0bSTerje Bergstrom 1547ede0b0bSTerje Bergstrom void host1x_intr_stop(struct host1x *host) 1557ede0b0bSTerje Bergstrom { 1567ede0b0bSTerje Bergstrom host1x_hw_intr_disable_all_syncpt_intrs(host); 1577ede0b0bSTerje Bergstrom } 158