projects/linux·blob master · refs

160 lines · 3.9 KiB

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