]>
Commit | Line | Data |
---|---|---|
5a5c7432 BS |
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 | */ | |
31649ecf BS |
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 | ||
1b0f8438 | 39 | /* Process pending alarms. */ |
31649ecf BS |
40 | spin_lock_irqsave(&tmr->lock, flags); |
41 | list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { | |
1b0f8438 BS |
42 | /* Have we hit the earliest alarm that hasn't gone off? */ |
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 | */ | |
b4e382ca BS |
53 | list_del_init(&alarm->head); |
54 | list_add(&alarm->exec, &exec); | |
31649ecf BS |
55 | } |
56 | ||
1b0f8438 BS |
57 | /* Shut down interrupt if no more pending alarms. */ |
58 | if (list_empty(&tmr->alarms)) | |
31649ecf | 59 | tmr->func->alarm_fini(tmr); |
31649ecf BS |
60 | spin_unlock_irqrestore(&tmr->lock, flags); |
61 | ||
1b0f8438 | 62 | /* Execute completed callbacks. */ |
b4e382ca BS |
63 | list_for_each_entry_safe(alarm, atemp, &exec, exec) { |
64 | list_del(&alarm->exec); | |
31649ecf BS |
65 | alarm->func(alarm); |
66 | } | |
67 | } | |
5a5c7432 | 68 | |
5a5c7432 | 69 | void |
31649ecf | 70 | nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) |
5a5c7432 | 71 | { |
31649ecf BS |
72 | struct nvkm_alarm *list; |
73 | unsigned long flags; | |
74 | ||
9fc64667 BS |
75 | /* Remove alarm from pending list. |
76 | * | |
77 | * This both protects against the corruption of the list, | |
78 | * and implements alarm rescheduling/cancellation. | |
79 | */ | |
31649ecf | 80 | spin_lock_irqsave(&tmr->lock, flags); |
9fc64667 BS |
81 | list_del_init(&alarm->head); |
82 | ||
83 | if (nsec) { | |
84 | /* Insert into pending list, ordered earliest to latest. */ | |
85 | alarm->timestamp = nvkm_timer_read(tmr) + nsec; | |
31649ecf BS |
86 | list_for_each_entry(list, &tmr->alarms, head) { |
87 | if (list->timestamp > alarm->timestamp) | |
88 | break; | |
89 | } | |
330bdf62 | 90 | |
31649ecf | 91 | list_add_tail(&alarm->head, &list->head); |
330bdf62 BS |
92 | |
93 | /* Update HW if this is now the earliest alarm. */ | |
94 | list = list_first_entry(&tmr->alarms, typeof(*list), head); | |
95 | if (list == alarm) { | |
96 | tmr->func->alarm_init(tmr, alarm->timestamp); | |
97 | /* This shouldn't happen if callers aren't stupid. | |
98 | * | |
99 | * Worst case scenario is that it'll take roughly | |
100 | * 4 seconds for the next alarm to trigger. | |
101 | */ | |
102 | WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr)); | |
103 | } | |
31649ecf BS |
104 | } |
105 | spin_unlock_irqrestore(&tmr->lock, flags); | |
5a5c7432 | 106 | } |
b925a75d MP |
107 | |
108 | void | |
31649ecf BS |
109 | nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm) |
110 | { | |
111 | unsigned long flags; | |
112 | spin_lock_irqsave(&tmr->lock, flags); | |
113 | list_del_init(&alarm->head); | |
114 | spin_unlock_irqrestore(&tmr->lock, flags); | |
115 | } | |
116 | ||
117 | static void | |
118 | nvkm_timer_intr(struct nvkm_subdev *subdev) | |
b925a75d | 119 | { |
31649ecf BS |
120 | struct nvkm_timer *tmr = nvkm_timer(subdev); |
121 | tmr->func->intr(tmr); | |
122 | } | |
123 | ||
124 | static int | |
125 | nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend) | |
126 | { | |
127 | struct nvkm_timer *tmr = nvkm_timer(subdev); | |
128 | tmr->func->alarm_fini(tmr); | |
129 | return 0; | |
130 | } | |
131 | ||
132 | static int | |
133 | nvkm_timer_init(struct nvkm_subdev *subdev) | |
134 | { | |
135 | struct nvkm_timer *tmr = nvkm_timer(subdev); | |
136 | if (tmr->func->init) | |
137 | tmr->func->init(tmr); | |
138 | tmr->func->time(tmr, ktime_to_ns(ktime_get())); | |
139 | nvkm_timer_alarm_trigger(tmr); | |
140 | return 0; | |
141 | } | |
142 | ||
143 | static void * | |
144 | nvkm_timer_dtor(struct nvkm_subdev *subdev) | |
145 | { | |
146 | return nvkm_timer(subdev); | |
147 | } | |
148 | ||
149 | static const struct nvkm_subdev_func | |
150 | nvkm_timer = { | |
151 | .dtor = nvkm_timer_dtor, | |
152 | .init = nvkm_timer_init, | |
153 | .fini = nvkm_timer_fini, | |
154 | .intr = nvkm_timer_intr, | |
155 | }; | |
156 | ||
157 | int | |
158 | nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device, | |
159 | int index, struct nvkm_timer **ptmr) | |
160 | { | |
161 | struct nvkm_timer *tmr; | |
162 | ||
163 | if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL))) | |
164 | return -ENOMEM; | |
165 | ||
56d06fa2 | 166 | nvkm_subdev_ctor(&nvkm_timer, device, index, &tmr->subdev); |
31649ecf BS |
167 | tmr->func = func; |
168 | INIT_LIST_HEAD(&tmr->alarms); | |
169 | spin_lock_init(&tmr->lock); | |
170 | return 0; | |
b925a75d | 171 | } |