These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / nouveau / nvkm / subdev / timer / base.c
1 /*
2  * Copyright 2012 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Ben Skeggs
23  */
24 #include "priv.h"
25
26 u64
27 nvkm_timer_read(struct nvkm_timer *tmr)
28 {
29         return tmr->func->read(tmr);
30 }
31
32 void
33 nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
34 {
35         struct nvkm_alarm *alarm, *atemp;
36         unsigned long flags;
37         LIST_HEAD(exec);
38
39         /* move any due alarms off the pending list */
40         spin_lock_irqsave(&tmr->lock, flags);
41         list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
42                 if (alarm->timestamp <= nvkm_timer_read(tmr))
43                         list_move_tail(&alarm->head, &exec);
44         }
45
46         /* reschedule interrupt for next alarm time */
47         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);
52         }
53         spin_unlock_irqrestore(&tmr->lock, flags);
54
55         /* execute any pending alarm handlers */
56         list_for_each_entry_safe(alarm, atemp, &exec, head) {
57                 list_del_init(&alarm->head);
58                 alarm->func(alarm);
59         }
60 }
61
62 void
63 nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
64 {
65         struct nvkm_alarm *list;
66         unsigned long flags;
67
68         alarm->timestamp = nvkm_timer_read(tmr) + nsec;
69
70         /* append new alarm to list, in soonest-alarm-first order */
71         spin_lock_irqsave(&tmr->lock, flags);
72         if (!nsec) {
73                 if (!list_empty(&alarm->head))
74                         list_del(&alarm->head);
75         } else {
76                 list_for_each_entry(list, &tmr->alarms, head) {
77                         if (list->timestamp > alarm->timestamp)
78                                 break;
79                 }
80                 list_add_tail(&alarm->head, &list->head);
81         }
82         spin_unlock_irqrestore(&tmr->lock, flags);
83
84         /* process pending alarms */
85         nvkm_timer_alarm_trigger(tmr);
86 }
87
88 void
89 nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm)
90 {
91         unsigned long flags;
92         spin_lock_irqsave(&tmr->lock, flags);
93         list_del_init(&alarm->head);
94         spin_unlock_irqrestore(&tmr->lock, flags);
95 }
96
97 static void
98 nvkm_timer_intr(struct nvkm_subdev *subdev)
99 {
100         struct nvkm_timer *tmr = nvkm_timer(subdev);
101         tmr->func->intr(tmr);
102 }
103
104 static int
105 nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
106 {
107         struct nvkm_timer *tmr = nvkm_timer(subdev);
108         tmr->func->alarm_fini(tmr);
109         return 0;
110 }
111
112 static int
113 nvkm_timer_init(struct nvkm_subdev *subdev)
114 {
115         struct nvkm_timer *tmr = nvkm_timer(subdev);
116         if (tmr->func->init)
117                 tmr->func->init(tmr);
118         tmr->func->time(tmr, ktime_to_ns(ktime_get()));
119         nvkm_timer_alarm_trigger(tmr);
120         return 0;
121 }
122
123 static void *
124 nvkm_timer_dtor(struct nvkm_subdev *subdev)
125 {
126         return nvkm_timer(subdev);
127 }
128
129 static const struct nvkm_subdev_func
130 nvkm_timer = {
131         .dtor = nvkm_timer_dtor,
132         .init = nvkm_timer_init,
133         .fini = nvkm_timer_fini,
134         .intr = nvkm_timer_intr,
135 };
136
137 int
138 nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
139                 int index, struct nvkm_timer **ptmr)
140 {
141         struct nvkm_timer *tmr;
142
143         if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
144                 return -ENOMEM;
145
146         nvkm_subdev_ctor(&nvkm_timer, device, index, 0, &tmr->subdev);
147         tmr->func = func;
148         INIT_LIST_HEAD(&tmr->alarms);
149         spin_lock_init(&tmr->lock);
150         return 0;
151 }