xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/timer/nouveau_nvkm_subdev_timer_base.c (revision 798b8d11ecd8257a8e35c3396210f98abf3d9ade)
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