aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c59
1 files changed, 39 insertions, 20 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
index 07dc82bfe346..f2a86eae0a0d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
@@ -36,23 +36,29 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
36 unsigned long flags; 36 unsigned long flags;
37 LIST_HEAD(exec); 37 LIST_HEAD(exec);
38 38
39 /* move any due alarms off the pending list */ 39 /* Process pending alarms. */
40 spin_lock_irqsave(&tmr->lock, flags); 40 spin_lock_irqsave(&tmr->lock, flags);
41 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { 41 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
42 if (alarm->timestamp <= nvkm_timer_read(tmr)) 42 /* Have we hit the earliest alarm that hasn't gone off? */
43 list_move_tail(&alarm->head, &exec); 43 if (alarm->timestamp > nvkm_timer_read(tmr)) {
44 /* Schedule it. If we didn't race, we're done. */
45 tmr->func->alarm_init(tmr, alarm->timestamp);
46 if (alarm->timestamp > nvkm_timer_read(tmr))
47 break;
48 }
49
50 /* Move to completed list. We'll drop the lock before
51 * executing the callback so it can reschedule itself.
52 */
53 list_move_tail(&alarm->head, &exec);
44 } 54 }
45 55
46 /* reschedule interrupt for next alarm time */ 56 /* Shut down interrupt if no more pending alarms. */
47 if (!list_empty(&tmr->alarms)) { 57 if (list_empty(&tmr->alarms))
48 alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
49 tmr->func->alarm_init(tmr, alarm->timestamp);
50 } else {
51 tmr->func->alarm_fini(tmr); 58 tmr->func->alarm_fini(tmr);
52 }
53 spin_unlock_irqrestore(&tmr->lock, flags); 59 spin_unlock_irqrestore(&tmr->lock, flags);
54 60
55 /* execute any pending alarm handlers */ 61 /* Execute completed callbacks. */
56 list_for_each_entry_safe(alarm, atemp, &exec, head) { 62 list_for_each_entry_safe(alarm, atemp, &exec, head) {
57 list_del_init(&alarm->head); 63 list_del_init(&alarm->head);
58 alarm->func(alarm); 64 alarm->func(alarm);
@@ -65,24 +71,37 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
65 struct nvkm_alarm *list; 71 struct nvkm_alarm *list;
66 unsigned long flags; 72 unsigned long flags;
67 73
68 alarm->timestamp = nvkm_timer_read(tmr) + nsec; 74 /* Remove alarm from pending list.
69 75 *
70 /* append new alarm to list, in soonest-alarm-first order */ 76 * This both protects against the corruption of the list,
77 * and implements alarm rescheduling/cancellation.
78 */
71 spin_lock_irqsave(&tmr->lock, flags); 79 spin_lock_irqsave(&tmr->lock, flags);
72 if (!nsec) { 80 list_del_init(&alarm->head);
73 if (!list_empty(&alarm->head)) 81
74 list_del(&alarm->head); 82 if (nsec) {
75 } else { 83 /* Insert into pending list, ordered earliest to latest. */
84 alarm->timestamp = nvkm_timer_read(tmr) + nsec;
76 list_for_each_entry(list, &tmr->alarms, head) { 85 list_for_each_entry(list, &tmr->alarms, head) {
77 if (list->timestamp > alarm->timestamp) 86 if (list->timestamp > alarm->timestamp)
78 break; 87 break;
79 } 88 }
89
80 list_add_tail(&alarm->head, &list->head); 90 list_add_tail(&alarm->head, &list->head);
91
92 /* Update HW if this is now the earliest alarm. */
93 list = list_first_entry(&tmr->alarms, typeof(*list), head);
94 if (list == alarm) {
95 tmr->func->alarm_init(tmr, alarm->timestamp);
96 /* This shouldn't happen if callers aren't stupid.
97 *
98 * Worst case scenario is that it'll take roughly
99 * 4 seconds for the next alarm to trigger.
100 */
101 WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
102 }
81 } 103 }
82 spin_unlock_irqrestore(&tmr->lock, flags); 104 spin_unlock_irqrestore(&tmr->lock, flags);
83
84 /* process pending alarms */
85 nvkm_timer_alarm_trigger(tmr);
86} 105}
87 106
88void 107void