Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/host1x/intr.c
26428 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Tegra host1x Interrupt Management
4
*
5
* Copyright (c) 2010-2021, NVIDIA Corporation.
6
*/
7
8
#include <linux/clk.h>
9
#include <linux/interrupt.h>
10
#include "dev.h"
11
#include "fence.h"
12
#include "intr.h"
13
14
static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list,
15
struct host1x_syncpt_fence *fence)
16
{
17
struct host1x_syncpt_fence *fence_in_list;
18
19
list_for_each_entry_reverse(fence_in_list, &list->list, list) {
20
if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) {
21
/* Fence in list is before us, we can insert here */
22
list_add(&fence->list, &fence_in_list->list);
23
return;
24
}
25
}
26
27
/* Add as first in list */
28
list_add(&fence->list, &list->list);
29
}
30
31
static void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp)
32
{
33
struct host1x_syncpt_fence *fence;
34
35
if (!list_empty(&sp->fences.list)) {
36
fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list);
37
38
host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold);
39
host1x_hw_intr_enable_syncpt_intr(host, sp->id);
40
} else {
41
host1x_hw_intr_disable_syncpt_intr(host, sp->id);
42
}
43
}
44
45
void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence)
46
{
47
struct host1x_fence_list *fence_list = &fence->sp->fences;
48
49
INIT_LIST_HEAD(&fence->list);
50
51
host1x_intr_add_fence_to_list(fence_list, fence);
52
host1x_intr_update_hw_state(host, fence->sp);
53
}
54
55
bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence)
56
{
57
struct host1x_fence_list *fence_list = &fence->sp->fences;
58
unsigned long irqflags;
59
60
spin_lock_irqsave(&fence_list->lock, irqflags);
61
62
if (list_empty(&fence->list)) {
63
spin_unlock_irqrestore(&fence_list->lock, irqflags);
64
return false;
65
}
66
67
list_del_init(&fence->list);
68
host1x_intr_update_hw_state(host, fence->sp);
69
70
spin_unlock_irqrestore(&fence_list->lock, irqflags);
71
72
return true;
73
}
74
75
void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
76
{
77
struct host1x_syncpt *sp = &host->syncpt[id];
78
struct host1x_syncpt_fence *fence, *tmp;
79
unsigned int value;
80
81
value = host1x_syncpt_load(sp);
82
83
spin_lock(&sp->fences.lock);
84
85
list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) {
86
if (((value - fence->threshold) & 0x80000000U) != 0U) {
87
/* Fence is not yet expired, we are done */
88
break;
89
}
90
91
list_del_init(&fence->list);
92
host1x_fence_signal(fence);
93
}
94
95
/* Re-enable interrupt if necessary */
96
host1x_intr_update_hw_state(host, sp);
97
98
spin_unlock(&sp->fences.lock);
99
}
100
101
int host1x_intr_init(struct host1x *host)
102
{
103
struct host1x_intr_irq_data *irq_data;
104
unsigned int id;
105
int i, err;
106
107
for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) {
108
struct host1x_syncpt *syncpt = &host->syncpt[id];
109
110
spin_lock_init(&syncpt->fences.lock);
111
INIT_LIST_HEAD(&syncpt->fences.list);
112
}
113
114
irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL);
115
if (!irq_data)
116
return -ENOMEM;
117
118
host1x_hw_intr_disable_all_syncpt_intrs(host);
119
120
for (i = 0; i < host->num_syncpt_irqs; i++) {
121
irq_data[i].host = host;
122
irq_data[i].offset = i;
123
124
err = devm_request_irq(host->dev, host->syncpt_irqs[i],
125
host->intr_op->isr, IRQF_SHARED,
126
"host1x_syncpt", &irq_data[i]);
127
if (err < 0)
128
return err;
129
}
130
131
return 0;
132
}
133
134
void host1x_intr_deinit(struct host1x *host)
135
{
136
}
137
138
void host1x_intr_start(struct host1x *host)
139
{
140
u32 hz = clk_get_rate(host->clk);
141
int err;
142
143
mutex_lock(&host->intr_mutex);
144
err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000));
145
if (err) {
146
mutex_unlock(&host->intr_mutex);
147
return;
148
}
149
mutex_unlock(&host->intr_mutex);
150
}
151
152
void host1x_intr_stop(struct host1x *host)
153
{
154
host1x_hw_intr_disable_all_syncpt_intrs(host);
155
}
156
157