xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
2431649ecfSBen Skeggs #include "priv.h"
25c39f472eSBen Skeggs 
26e4f90a35SBen Skeggs s64
nvkm_timer_wait_test(struct nvkm_timer_wait * wait)27e4f90a35SBen Skeggs nvkm_timer_wait_test(struct nvkm_timer_wait *wait)
28e4f90a35SBen Skeggs {
29e4f90a35SBen Skeggs 	struct nvkm_subdev *subdev = &wait->tmr->subdev;
30e4f90a35SBen Skeggs 	u64 time = nvkm_timer_read(wait->tmr);
31e4f90a35SBen Skeggs 
32e4f90a35SBen Skeggs 	if (wait->reads == 0) {
33e4f90a35SBen Skeggs 		wait->time0 = time;
34e4f90a35SBen Skeggs 		wait->time1 = time;
35e4f90a35SBen Skeggs 	}
36e4f90a35SBen Skeggs 
37e4f90a35SBen Skeggs 	if (wait->time1 == time) {
38e4f90a35SBen Skeggs 		if (wait->reads++ == 16) {
39e4f90a35SBen Skeggs 			nvkm_fatal(subdev, "stalled at %016llx\n", time);
40e4f90a35SBen Skeggs 			return -ETIMEDOUT;
41e4f90a35SBen Skeggs 		}
42e4f90a35SBen Skeggs 	} else {
43e4f90a35SBen Skeggs 		wait->time1 = time;
44e4f90a35SBen Skeggs 		wait->reads = 1;
45e4f90a35SBen Skeggs 	}
46e4f90a35SBen Skeggs 
47e4f90a35SBen Skeggs 	if (wait->time1 - wait->time0 > wait->limit)
48e4f90a35SBen Skeggs 		return -ETIMEDOUT;
49e4f90a35SBen Skeggs 
50e4f90a35SBen Skeggs 	return wait->time1 - wait->time0;
51e4f90a35SBen Skeggs }
52e4f90a35SBen Skeggs 
53e4f90a35SBen Skeggs void
nvkm_timer_wait_init(struct nvkm_device * device,u64 nsec,struct nvkm_timer_wait * wait)54e4f90a35SBen Skeggs nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
55e4f90a35SBen Skeggs 		     struct nvkm_timer_wait *wait)
56e4f90a35SBen Skeggs {
57e4f90a35SBen Skeggs 	wait->tmr = device->timer;
58e4f90a35SBen Skeggs 	wait->limit = nsec;
59e4f90a35SBen Skeggs 	wait->reads = 0;
60e4f90a35SBen Skeggs }
61e4f90a35SBen Skeggs 
6231649ecfSBen Skeggs u64
nvkm_timer_read(struct nvkm_timer * tmr)6331649ecfSBen Skeggs nvkm_timer_read(struct nvkm_timer *tmr)
64c39f472eSBen Skeggs {
6531649ecfSBen Skeggs 	return tmr->func->read(tmr);
66c39f472eSBen Skeggs }
67c39f472eSBen Skeggs 
68c39f472eSBen Skeggs void
nvkm_timer_alarm_trigger(struct nvkm_timer * tmr)6931649ecfSBen Skeggs nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
70c39f472eSBen Skeggs {
7131649ecfSBen Skeggs 	struct nvkm_alarm *alarm, *atemp;
7231649ecfSBen Skeggs 	unsigned long flags;
7331649ecfSBen Skeggs 	LIST_HEAD(exec);
7431649ecfSBen Skeggs 
751b0f8438SBen Skeggs 	/* Process pending alarms. */
7631649ecfSBen Skeggs 	spin_lock_irqsave(&tmr->lock, flags);
7731649ecfSBen Skeggs 	list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
781b0f8438SBen Skeggs 		/* Have we hit the earliest alarm that hasn't gone off? */
791b0f8438SBen Skeggs 		if (alarm->timestamp > nvkm_timer_read(tmr)) {
801b0f8438SBen Skeggs 			/* Schedule it.  If we didn't race, we're done. */
811b0f8438SBen Skeggs 			tmr->func->alarm_init(tmr, alarm->timestamp);
821b0f8438SBen Skeggs 			if (alarm->timestamp > nvkm_timer_read(tmr))
831b0f8438SBen Skeggs 				break;
841b0f8438SBen Skeggs 		}
851b0f8438SBen Skeggs 
861b0f8438SBen Skeggs 		/* Move to completed list.  We'll drop the lock before
871b0f8438SBen Skeggs 		 * executing the callback so it can reschedule itself.
881b0f8438SBen Skeggs 		 */
89b4e382caSBen Skeggs 		list_del_init(&alarm->head);
90b4e382caSBen Skeggs 		list_add(&alarm->exec, &exec);
9131649ecfSBen Skeggs 	}
9231649ecfSBen Skeggs 
931b0f8438SBen Skeggs 	/* Shut down interrupt if no more pending alarms. */
941b0f8438SBen Skeggs 	if (list_empty(&tmr->alarms))
9531649ecfSBen Skeggs 		tmr->func->alarm_fini(tmr);
9631649ecfSBen Skeggs 	spin_unlock_irqrestore(&tmr->lock, flags);
9731649ecfSBen Skeggs 
981b0f8438SBen Skeggs 	/* Execute completed callbacks. */
99b4e382caSBen Skeggs 	list_for_each_entry_safe(alarm, atemp, &exec, exec) {
100b4e382caSBen Skeggs 		list_del(&alarm->exec);
10131649ecfSBen Skeggs 		alarm->func(alarm);
10231649ecfSBen Skeggs 	}
10331649ecfSBen Skeggs }
10431649ecfSBen Skeggs 
10531649ecfSBen Skeggs void
nvkm_timer_alarm(struct nvkm_timer * tmr,u32 nsec,struct nvkm_alarm * alarm)10631649ecfSBen Skeggs nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
10731649ecfSBen Skeggs {
10831649ecfSBen Skeggs 	struct nvkm_alarm *list;
10931649ecfSBen Skeggs 	unsigned long flags;
11031649ecfSBen Skeggs 
1119fc64667SBen Skeggs 	/* Remove alarm from pending list.
1129fc64667SBen Skeggs 	 *
1139fc64667SBen Skeggs 	 * This both protects against the corruption of the list,
1149fc64667SBen Skeggs 	 * and implements alarm rescheduling/cancellation.
1159fc64667SBen Skeggs 	 */
11631649ecfSBen Skeggs 	spin_lock_irqsave(&tmr->lock, flags);
1179fc64667SBen Skeggs 	list_del_init(&alarm->head);
1189fc64667SBen Skeggs 
1199fc64667SBen Skeggs 	if (nsec) {
1209fc64667SBen Skeggs 		/* Insert into pending list, ordered earliest to latest. */
1219fc64667SBen Skeggs 		alarm->timestamp = nvkm_timer_read(tmr) + nsec;
12231649ecfSBen Skeggs 		list_for_each_entry(list, &tmr->alarms, head) {
12331649ecfSBen Skeggs 			if (list->timestamp > alarm->timestamp)
12431649ecfSBen Skeggs 				break;
12531649ecfSBen Skeggs 		}
126330bdf62SBen Skeggs 
12731649ecfSBen Skeggs 		list_add_tail(&alarm->head, &list->head);
128330bdf62SBen Skeggs 
129330bdf62SBen Skeggs 		/* Update HW if this is now the earliest alarm. */
130330bdf62SBen Skeggs 		list = list_first_entry(&tmr->alarms, typeof(*list), head);
131330bdf62SBen Skeggs 		if (list == alarm) {
132330bdf62SBen Skeggs 			tmr->func->alarm_init(tmr, alarm->timestamp);
133330bdf62SBen Skeggs 			/* This shouldn't happen if callers aren't stupid.
134330bdf62SBen Skeggs 			 *
135330bdf62SBen Skeggs 			 * Worst case scenario is that it'll take roughly
136330bdf62SBen Skeggs 			 * 4 seconds for the next alarm to trigger.
137330bdf62SBen Skeggs 			 */
138330bdf62SBen Skeggs 			WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
139330bdf62SBen Skeggs 		}
14031649ecfSBen Skeggs 	}
14131649ecfSBen Skeggs 	spin_unlock_irqrestore(&tmr->lock, flags);
14231649ecfSBen Skeggs }
14331649ecfSBen Skeggs 
14431649ecfSBen Skeggs static void
nvkm_timer_intr(struct nvkm_subdev * subdev)14531649ecfSBen Skeggs nvkm_timer_intr(struct nvkm_subdev *subdev)
14631649ecfSBen Skeggs {
14731649ecfSBen Skeggs 	struct nvkm_timer *tmr = nvkm_timer(subdev);
14831649ecfSBen Skeggs 	tmr->func->intr(tmr);
14931649ecfSBen Skeggs }
15031649ecfSBen Skeggs 
15131649ecfSBen Skeggs static int
nvkm_timer_fini(struct nvkm_subdev * subdev,bool suspend)15231649ecfSBen Skeggs nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
15331649ecfSBen Skeggs {
15431649ecfSBen Skeggs 	struct nvkm_timer *tmr = nvkm_timer(subdev);
15531649ecfSBen Skeggs 	tmr->func->alarm_fini(tmr);
15631649ecfSBen Skeggs 	return 0;
15731649ecfSBen Skeggs }
15831649ecfSBen Skeggs 
15931649ecfSBen Skeggs static int
nvkm_timer_init(struct nvkm_subdev * subdev)16031649ecfSBen Skeggs nvkm_timer_init(struct nvkm_subdev *subdev)
16131649ecfSBen Skeggs {
16231649ecfSBen Skeggs 	struct nvkm_timer *tmr = nvkm_timer(subdev);
16331649ecfSBen Skeggs 	if (tmr->func->init)
16431649ecfSBen Skeggs 		tmr->func->init(tmr);
16531649ecfSBen Skeggs 	tmr->func->time(tmr, ktime_to_ns(ktime_get()));
16631649ecfSBen Skeggs 	nvkm_timer_alarm_trigger(tmr);
16731649ecfSBen Skeggs 	return 0;
16831649ecfSBen Skeggs }
16931649ecfSBen Skeggs 
17031649ecfSBen Skeggs static void *
nvkm_timer_dtor(struct nvkm_subdev * subdev)17131649ecfSBen Skeggs nvkm_timer_dtor(struct nvkm_subdev *subdev)
17231649ecfSBen Skeggs {
17331649ecfSBen Skeggs 	return nvkm_timer(subdev);
17431649ecfSBen Skeggs }
17531649ecfSBen Skeggs 
17631649ecfSBen Skeggs static const struct nvkm_subdev_func
17731649ecfSBen Skeggs nvkm_timer = {
17831649ecfSBen Skeggs 	.dtor = nvkm_timer_dtor,
17931649ecfSBen Skeggs 	.init = nvkm_timer_init,
18031649ecfSBen Skeggs 	.fini = nvkm_timer_fini,
18131649ecfSBen Skeggs 	.intr = nvkm_timer_intr,
18231649ecfSBen Skeggs };
18331649ecfSBen Skeggs 
18431649ecfSBen Skeggs int
nvkm_timer_new_(const struct nvkm_timer_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_timer ** ptmr)18531649ecfSBen Skeggs nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
186*9aad54d5SBen Skeggs 		enum nvkm_subdev_type type, int inst, struct nvkm_timer **ptmr)
18731649ecfSBen Skeggs {
18831649ecfSBen Skeggs 	struct nvkm_timer *tmr;
18931649ecfSBen Skeggs 
19031649ecfSBen Skeggs 	if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
19131649ecfSBen Skeggs 		return -ENOMEM;
19231649ecfSBen Skeggs 
193*9aad54d5SBen Skeggs 	nvkm_subdev_ctor(&nvkm_timer, device, type, inst, &tmr->subdev);
19431649ecfSBen Skeggs 	tmr->func = func;
19531649ecfSBen Skeggs 	INIT_LIST_HEAD(&tmr->alarms);
19631649ecfSBen Skeggs 	spin_lock_init(&tmr->lock);
19731649ecfSBen Skeggs 	return 0;
198c39f472eSBen Skeggs }
199