19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27ede0b0bSTerje Bergstrom /*
37ede0b0bSTerje Bergstrom * Tegra host1x Interrupt Management
47ede0b0bSTerje Bergstrom *
5*625d4ffbSMikko Perttunen * Copyright (c) 2010-2021, NVIDIA Corporation.
67ede0b0bSTerje Bergstrom */
77ede0b0bSTerje Bergstrom
87ede0b0bSTerje Bergstrom #include <linux/clk.h>
97ede0b0bSTerje Bergstrom
107ede0b0bSTerje Bergstrom #include "dev.h"
11687db220SMikko Perttunen #include "fence.h"
127ede0b0bSTerje Bergstrom #include "intr.h"
137ede0b0bSTerje Bergstrom
host1x_intr_add_fence_to_list(struct host1x_fence_list * list,struct host1x_syncpt_fence * fence)14*625d4ffbSMikko Perttunen static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
15*625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence)
167ede0b0bSTerje Bergstrom {
17*625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence_in_list;
18*625d4ffbSMikko Perttunen
19*625d4ffbSMikko Perttunen list_for_each_entry_reverse(fence_in_list, &list->list, list) {
20*625d4ffbSMikko Perttunen if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
21*625d4ffbSMikko Perttunen /* Fence in list is before us, we can insert here */
22*625d4ffbSMikko Perttunen list_add(&fence->list, &fence_in_list->list);
23*625d4ffbSMikko Perttunen return;
24*625d4ffbSMikko Perttunen }
257ede0b0bSTerje Bergstrom }
267ede0b0bSTerje Bergstrom
27*625d4ffbSMikko Perttunen /* Add as first in list */
28*625d4ffbSMikko Perttunen list_add(&fence->list, &list->list);
29*625d4ffbSMikko Perttunen }
307ede0b0bSTerje Bergstrom
host1x_intr_update_hw_state(struct host1x * host,struct host1x_syncpt * sp)31*625d4ffbSMikko Perttunen static void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp)
32*625d4ffbSMikko Perttunen {
33*625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence;
34*625d4ffbSMikko Perttunen
35*625d4ffbSMikko Perttunen if (!list_empty(&sp->fences.list)) {
36*625d4ffbSMikko Perttunen fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list);
37*625d4ffbSMikko Perttunen
38*625d4ffbSMikko Perttunen host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold);
39*625d4ffbSMikko Perttunen host1x_hw_intr_enable_syncpt_intr(host, sp->id);
40*625d4ffbSMikko Perttunen } else {
41*625d4ffbSMikko Perttunen host1x_hw_intr_disable_syncpt_intr(host, sp->id);
42*625d4ffbSMikko Perttunen }
43*625d4ffbSMikko Perttunen }
44*625d4ffbSMikko Perttunen
host1x_intr_add_fence_locked(struct host1x * host,struct host1x_syncpt_fence * fence)45*625d4ffbSMikko Perttunen void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence)
46*625d4ffbSMikko Perttunen {
47*625d4ffbSMikko Perttunen struct host1x_fence_list *fence_list = &fence->sp->fences;
48*625d4ffbSMikko Perttunen
49*625d4ffbSMikko Perttunen INIT_LIST_HEAD(&fence->list);
50*625d4ffbSMikko Perttunen
51*625d4ffbSMikko Perttunen host1x_intr_add_fence_to_list(fence_list, fence);
52*625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, fence->sp);
53*625d4ffbSMikko Perttunen }
54*625d4ffbSMikko Perttunen
host1x_intr_remove_fence(struct host1x * host,struct host1x_syncpt_fence * fence)55*625d4ffbSMikko Perttunen bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence)
56*625d4ffbSMikko Perttunen {
57*625d4ffbSMikko Perttunen struct host1x_fence_list *fence_list = &fence->sp->fences;
58*625d4ffbSMikko Perttunen unsigned long irqflags;
59*625d4ffbSMikko Perttunen
60*625d4ffbSMikko Perttunen spin_lock_irqsave(&fence_list->lock, irqflags);
61*625d4ffbSMikko Perttunen
62*625d4ffbSMikko Perttunen if (list_empty(&fence->list)) {
63*625d4ffbSMikko Perttunen spin_unlock_irqrestore(&fence_list->lock, irqflags);
647ede0b0bSTerje Bergstrom return false;
657ede0b0bSTerje Bergstrom }
667ede0b0bSTerje Bergstrom
67*625d4ffbSMikko Perttunen list_del_init(&fence->list);
68*625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, fence->sp);
69*625d4ffbSMikko Perttunen
70*625d4ffbSMikko Perttunen spin_unlock_irqrestore(&fence_list->lock, irqflags);
71*625d4ffbSMikko Perttunen
727ede0b0bSTerje Bergstrom return true;
737ede0b0bSTerje Bergstrom }
747ede0b0bSTerje Bergstrom
host1x_intr_handle_interrupt(struct host1x * host,unsigned int id)75*625d4ffbSMikko Perttunen void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
767ede0b0bSTerje Bergstrom {
77*625d4ffbSMikko Perttunen struct host1x_syncpt *sp = &host->syncpt[id];
78*625d4ffbSMikko Perttunen struct host1x_syncpt_fence *fence, *tmp;
79*625d4ffbSMikko Perttunen unsigned int value;
807ede0b0bSTerje Bergstrom
81*625d4ffbSMikko Perttunen value = host1x_syncpt_load(sp);
82*625d4ffbSMikko Perttunen
83*625d4ffbSMikko Perttunen spin_lock(&sp->fences.lock);
84*625d4ffbSMikko Perttunen
85*625d4ffbSMikko Perttunen list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
86*625d4ffbSMikko Perttunen if (((value - fence->threshold) & 0x80000000U) != 0U) {
87*625d4ffbSMikko Perttunen /* Fence is not yet expired, we are done */
887ede0b0bSTerje Bergstrom break;
896579324aSTerje Bergstrom }
906579324aSTerje Bergstrom
91*625d4ffbSMikko Perttunen list_del_init(&fence->list);
92*625d4ffbSMikko Perttunen host1x_fence_signal(fence);
937ede0b0bSTerje Bergstrom }
947ede0b0bSTerje Bergstrom
95*625d4ffbSMikko Perttunen /* Re-enable interrupt if necessary */
96*625d4ffbSMikko Perttunen host1x_intr_update_hw_state(host, sp);
977ede0b0bSTerje Bergstrom
98*625d4ffbSMikko Perttunen spin_unlock(&sp->fences.lock);
997ede0b0bSTerje Bergstrom }
1007ede0b0bSTerje Bergstrom
host1x_intr_init(struct host1x * host)101*625d4ffbSMikko Perttunen int host1x_intr_init(struct host1x *host)
1027ede0b0bSTerje Bergstrom {
1037ede0b0bSTerje Bergstrom unsigned int id;
1047ede0b0bSTerje Bergstrom
1057ede0b0bSTerje Bergstrom mutex_init(&host->intr_mutex);
1067ede0b0bSTerje Bergstrom
107*625d4ffbSMikko Perttunen for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) {
108*625d4ffbSMikko Perttunen struct host1x_syncpt *syncpt = &host->syncpt[id];
1097ede0b0bSTerje Bergstrom
110*625d4ffbSMikko Perttunen spin_lock_init(&syncpt->fences.lock);
111*625d4ffbSMikko Perttunen INIT_LIST_HEAD(&syncpt->fences.list);
1127ede0b0bSTerje Bergstrom }
1137ede0b0bSTerje Bergstrom
1147ede0b0bSTerje Bergstrom return 0;
1157ede0b0bSTerje Bergstrom }
1167ede0b0bSTerje Bergstrom
host1x_intr_deinit(struct host1x * host)1177ede0b0bSTerje Bergstrom void host1x_intr_deinit(struct host1x *host)
1187ede0b0bSTerje Bergstrom {
1197ede0b0bSTerje Bergstrom }
1207ede0b0bSTerje Bergstrom
host1x_intr_start(struct host1x * host)1217ede0b0bSTerje Bergstrom void host1x_intr_start(struct host1x *host)
1227ede0b0bSTerje Bergstrom {
1237ede0b0bSTerje Bergstrom u32 hz = clk_get_rate(host->clk);
1247ede0b0bSTerje Bergstrom int err;
1257ede0b0bSTerje Bergstrom
1267ede0b0bSTerje Bergstrom mutex_lock(&host->intr_mutex);
127*625d4ffbSMikko Perttunen err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000));
1287ede0b0bSTerje Bergstrom if (err) {
1297ede0b0bSTerje Bergstrom mutex_unlock(&host->intr_mutex);
1307ede0b0bSTerje Bergstrom return;
1317ede0b0bSTerje Bergstrom }
1327ede0b0bSTerje Bergstrom mutex_unlock(&host->intr_mutex);
1337ede0b0bSTerje Bergstrom }
1347ede0b0bSTerje Bergstrom
host1x_intr_stop(struct host1x * host)1357ede0b0bSTerje Bergstrom void host1x_intr_stop(struct host1x *host)
1367ede0b0bSTerje Bergstrom {
1377ede0b0bSTerje Bergstrom host1x_hw_intr_disable_all_syncpt_intrs(host);
1387ede0b0bSTerje Bergstrom }
139