1 /* $NetBSD: nouveau_nvkm_subdev_timer_base.c,v 1.6 2021/12/19 11:34:46 riastradh Exp $ */
2
3 /*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_timer_base.c,v 1.6 2021/12/19 11:34:46 riastradh Exp $");
28
29 #include "priv.h"
30
31 #include <linux/nbsd-namespace.h>
32 s64
nvkm_timer_wait_test(struct nvkm_timer_wait * wait)33 nvkm_timer_wait_test(struct nvkm_timer_wait *wait)
34 {
35 struct nvkm_subdev *subdev = &wait->tmr->subdev;
36 u64 time = nvkm_timer_read(wait->tmr);
37
38 if (wait->reads == 0) {
39 wait->time0 = time;
40 wait->time1 = time;
41 }
42
43 if (wait->time1 == time) {
44 if (wait->reads++ == 16) {
45 nvkm_fatal(subdev, "stalled at %016"PRIx64"\n", time);
46 return -ETIMEDOUT;
47 }
48 } else {
49 wait->time1 = time;
50 wait->reads = 1;
51 }
52
53 if (wait->time1 - wait->time0 > wait->limit)
54 return -ETIMEDOUT;
55
56 return wait->time1 - wait->time0;
57 }
58
59 void
nvkm_timer_wait_init(struct nvkm_device * device,u64 nsec,struct nvkm_timer_wait * wait)60 nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
61 struct nvkm_timer_wait *wait)
62 {
63 wait->tmr = device->timer;
64 wait->limit = nsec;
65 wait->reads = 0;
66 }
67
68 u64
nvkm_timer_read(struct nvkm_timer * tmr)69 nvkm_timer_read(struct nvkm_timer *tmr)
70 {
71 return tmr->func->read(tmr);
72 }
73
74 void
nvkm_timer_alarm_trigger(struct nvkm_timer * tmr)75 nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
76 {
77 struct nvkm_alarm *alarm, *atemp;
78 unsigned long flags;
79 LIST_HEAD(exec);
80
81 /* Process pending alarms. */
82 spin_lock_irqsave(&tmr->lock, flags);
83 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
84 /* Have we hit the earliest alarm that hasn't gone off? */
85 if (alarm->timestamp > nvkm_timer_read(tmr)) {
86 /* Schedule it. If we didn't race, we're done. */
87 tmr->func->alarm_init(tmr, alarm->timestamp);
88 if (alarm->timestamp > nvkm_timer_read(tmr))
89 break;
90 }
91
92 /* Move to completed list. We'll drop the lock before
93 * executing the callback so it can reschedule itself.
94 */
95 list_del_init(&alarm->head);
96 list_add(&alarm->exec, &exec);
97 }
98
99 /* Shut down interrupt if no more pending alarms. */
100 if (list_empty(&tmr->alarms))
101 tmr->func->alarm_fini(tmr);
102 spin_unlock_irqrestore(&tmr->lock, flags);
103
104 /* Execute completed callbacks. */
105 list_for_each_entry_safe(alarm, atemp, &exec, exec) {
106 list_del(&alarm->exec);
107 alarm->func(alarm);
108 }
109 }
110
111 void
nvkm_timer_alarm(struct nvkm_timer * tmr,u32 nsec,struct nvkm_alarm * alarm)112 nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
113 {
114 struct nvkm_alarm *list;
115 unsigned long flags;
116
117 /* Remove alarm from pending list.
118 *
119 * This both protects against the corruption of the list,
120 * and implements alarm rescheduling/cancellation.
121 */
122 spin_lock_irqsave(&tmr->lock, flags);
123 list_del_init(&alarm->head);
124
125 if (nsec) {
126 /* Insert into pending list, ordered earliest to latest. */
127 alarm->timestamp = nvkm_timer_read(tmr) + nsec;
128 list_for_each_entry(list, &tmr->alarms, head) {
129 if (list->timestamp > alarm->timestamp)
130 break;
131 }
132
133 list_add_tail(&alarm->head, &list->head);
134
135 /* Update HW if this is now the earliest alarm. */
136 list = list_first_entry(&tmr->alarms, typeof(*list), head);
137 if (list == alarm) {
138 tmr->func->alarm_init(tmr, alarm->timestamp);
139 /* This shouldn't happen if callers aren't stupid.
140 *
141 * Worst case scenario is that it'll take roughly
142 * 4 seconds for the next alarm to trigger.
143 */
144 WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
145 }
146 }
147 spin_unlock_irqrestore(&tmr->lock, flags);
148 }
149
150 static void
nvkm_timer_intr(struct nvkm_subdev * subdev)151 nvkm_timer_intr(struct nvkm_subdev *subdev)
152 {
153 struct nvkm_timer *tmr = nvkm_timer(subdev);
154 tmr->func->intr(tmr);
155 }
156
157 static int
nvkm_timer_fini(struct nvkm_subdev * subdev,bool suspend)158 nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
159 {
160 struct nvkm_timer *tmr = nvkm_timer(subdev);
161 tmr->func->alarm_fini(tmr);
162 return 0;
163 }
164
165 static int
nvkm_timer_init(struct nvkm_subdev * subdev)166 nvkm_timer_init(struct nvkm_subdev *subdev)
167 {
168 struct nvkm_timer *tmr = nvkm_timer(subdev);
169 if (tmr->func->init)
170 tmr->func->init(tmr);
171 tmr->func->time(tmr, ktime_to_ns(ktime_get()));
172 nvkm_timer_alarm_trigger(tmr);
173 return 0;
174 }
175
176 static void *
nvkm_timer_dtor(struct nvkm_subdev * subdev)177 nvkm_timer_dtor(struct nvkm_subdev *subdev)
178 {
179 spin_lock_destroy(&nvkm_timer(subdev)->lock);
180 return nvkm_timer(subdev);
181 }
182
183 static const struct nvkm_subdev_func
184 nvkm_timer = {
185 .dtor = nvkm_timer_dtor,
186 .init = nvkm_timer_init,
187 .fini = nvkm_timer_fini,
188 .intr = nvkm_timer_intr,
189 };
190
191 int
nvkm_timer_new_(const struct nvkm_timer_func * func,struct nvkm_device * device,int index,struct nvkm_timer ** ptmr)192 nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
193 int index, struct nvkm_timer **ptmr)
194 {
195 struct nvkm_timer *tmr;
196
197 if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
198 return -ENOMEM;
199
200 nvkm_subdev_ctor(&nvkm_timer, device, index, &tmr->subdev);
201 tmr->func = func;
202 INIT_LIST_HEAD(&tmr->alarms);
203 spin_lock_init(&tmr->lock);
204 return 0;
205 }
206