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, &tmr->subdev);
147 	tmr->func = func;
148 	INIT_LIST_HEAD(&tmr->alarms);
149 	spin_lock_init(&tmr->lock);
150 	return 0;
151 }
152