2 * Copyright 2012 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include <core/device.h>
29 nv04_timer_read(struct nvkm_timer
*ptimer
)
31 struct nv04_timer_priv
*priv
= (void *)ptimer
;
35 hi
= nv_rd32(priv
, NV04_PTIMER_TIME_1
);
36 lo
= nv_rd32(priv
, NV04_PTIMER_TIME_0
);
37 } while (hi
!= nv_rd32(priv
, NV04_PTIMER_TIME_1
));
39 return ((u64
)hi
<< 32 | lo
);
43 nv04_timer_alarm_trigger(struct nvkm_timer
*ptimer
)
45 struct nv04_timer_priv
*priv
= (void *)ptimer
;
46 struct nvkm_alarm
*alarm
, *atemp
;
50 /* move any due alarms off the pending list */
51 spin_lock_irqsave(&priv
->lock
, flags
);
52 list_for_each_entry_safe(alarm
, atemp
, &priv
->alarms
, head
) {
53 if (alarm
->timestamp
<= ptimer
->read(ptimer
))
54 list_move_tail(&alarm
->head
, &exec
);
57 /* reschedule interrupt for next alarm time */
58 if (!list_empty(&priv
->alarms
)) {
59 alarm
= list_first_entry(&priv
->alarms
, typeof(*alarm
), head
);
60 nv_wr32(priv
, NV04_PTIMER_ALARM_0
, alarm
->timestamp
);
61 nv_wr32(priv
, NV04_PTIMER_INTR_EN_0
, 0x00000001);
63 nv_wr32(priv
, NV04_PTIMER_INTR_EN_0
, 0x00000000);
65 spin_unlock_irqrestore(&priv
->lock
, flags
);
67 /* execute any pending alarm handlers */
68 list_for_each_entry_safe(alarm
, atemp
, &exec
, head
) {
69 list_del_init(&alarm
->head
);
75 nv04_timer_alarm(struct nvkm_timer
*ptimer
, u64 time
, struct nvkm_alarm
*alarm
)
77 struct nv04_timer_priv
*priv
= (void *)ptimer
;
78 struct nvkm_alarm
*list
;
81 alarm
->timestamp
= ptimer
->read(ptimer
) + time
;
83 /* append new alarm to list, in soonest-alarm-first order */
84 spin_lock_irqsave(&priv
->lock
, flags
);
86 if (!list_empty(&alarm
->head
))
87 list_del(&alarm
->head
);
89 list_for_each_entry(list
, &priv
->alarms
, head
) {
90 if (list
->timestamp
> alarm
->timestamp
)
93 list_add_tail(&alarm
->head
, &list
->head
);
95 spin_unlock_irqrestore(&priv
->lock
, flags
);
97 /* process pending alarms */
98 nv04_timer_alarm_trigger(ptimer
);
102 nv04_timer_alarm_cancel(struct nvkm_timer
*ptimer
, struct nvkm_alarm
*alarm
)
104 struct nv04_timer_priv
*priv
= (void *)ptimer
;
106 spin_lock_irqsave(&priv
->lock
, flags
);
107 list_del_init(&alarm
->head
);
108 spin_unlock_irqrestore(&priv
->lock
, flags
);
112 nv04_timer_intr(struct nvkm_subdev
*subdev
)
114 struct nv04_timer_priv
*priv
= (void *)subdev
;
115 u32 stat
= nv_rd32(priv
, NV04_PTIMER_INTR_0
);
117 if (stat
& 0x00000001) {
118 nv04_timer_alarm_trigger(&priv
->base
);
119 nv_wr32(priv
, NV04_PTIMER_INTR_0
, 0x00000001);
124 nv_error(priv
, "unknown stat 0x%08x\n", stat
);
125 nv_wr32(priv
, NV04_PTIMER_INTR_0
, stat
);
130 nv04_timer_fini(struct nvkm_object
*object
, bool suspend
)
132 struct nv04_timer_priv
*priv
= (void *)object
;
134 priv
->suspend_time
= nv04_timer_read(&priv
->base
);
135 nv_wr32(priv
, NV04_PTIMER_INTR_EN_0
, 0x00000000);
136 return nvkm_timer_fini(&priv
->base
, suspend
);
140 nv04_timer_init(struct nvkm_object
*object
)
142 struct nvkm_device
*device
= nv_device(object
);
143 struct nv04_timer_priv
*priv
= (void *)object
;
144 u32 m
= 1, f
, n
, d
, lo
, hi
;
147 ret
= nvkm_timer_init(&priv
->base
);
151 /* aim for 31.25MHz, which gives us nanosecond timestamps */
154 /* determine base clock for timer source */
156 if (device
->chipset
< 0x40) {
157 n
= nvkm_hw_get_clock(device
, PLL_CORE
);
160 if (device
->chipset
<= 0x40) {
161 /*XXX: figure this out */
167 while (n
< (d
* 2)) {
172 nv_wr32(priv
, 0x009220, m
- 1);
176 nv_warn(priv
, "unknown input clock freq\n");
177 if (!nv_rd32(priv
, NV04_PTIMER_NUMERATOR
) ||
178 !nv_rd32(priv
, NV04_PTIMER_DENOMINATOR
)) {
179 nv_wr32(priv
, NV04_PTIMER_NUMERATOR
, 1);
180 nv_wr32(priv
, NV04_PTIMER_DENOMINATOR
, 1);
185 /* reduce ratio to acceptable values */
186 while (((n
% 5) == 0) && ((d
% 5) == 0)) {
191 while (((n
% 2) == 0) && ((d
% 2) == 0)) {
196 while (n
> 0xffff || d
> 0xffff) {
201 /* restore the time before suspend */
202 lo
= priv
->suspend_time
;
203 hi
= (priv
->suspend_time
>> 32);
205 nv_debug(priv
, "input frequency : %dHz\n", f
);
206 nv_debug(priv
, "input multiplier: %d\n", m
);
207 nv_debug(priv
, "numerator : 0x%08x\n", n
);
208 nv_debug(priv
, "denominator : 0x%08x\n", d
);
209 nv_debug(priv
, "timer frequency : %dHz\n", (f
* m
) * d
/ n
);
210 nv_debug(priv
, "time low : 0x%08x\n", lo
);
211 nv_debug(priv
, "time high : 0x%08x\n", hi
);
213 nv_wr32(priv
, NV04_PTIMER_NUMERATOR
, n
);
214 nv_wr32(priv
, NV04_PTIMER_DENOMINATOR
, d
);
215 nv_wr32(priv
, NV04_PTIMER_INTR_0
, 0xffffffff);
216 nv_wr32(priv
, NV04_PTIMER_INTR_EN_0
, 0x00000000);
217 nv_wr32(priv
, NV04_PTIMER_TIME_1
, hi
);
218 nv_wr32(priv
, NV04_PTIMER_TIME_0
, lo
);
223 nv04_timer_dtor(struct nvkm_object
*object
)
225 struct nv04_timer_priv
*priv
= (void *)object
;
226 return nvkm_timer_destroy(&priv
->base
);
230 nv04_timer_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
231 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
232 struct nvkm_object
**pobject
)
234 struct nv04_timer_priv
*priv
;
237 ret
= nvkm_timer_create(parent
, engine
, oclass
, &priv
);
238 *pobject
= nv_object(priv
);
242 priv
->base
.base
.intr
= nv04_timer_intr
;
243 priv
->base
.read
= nv04_timer_read
;
244 priv
->base
.alarm
= nv04_timer_alarm
;
245 priv
->base
.alarm_cancel
= nv04_timer_alarm_cancel
;
246 priv
->suspend_time
= 0;
248 INIT_LIST_HEAD(&priv
->alarms
);
249 spin_lock_init(&priv
->lock
);
254 nv04_timer_oclass
= {
255 .handle
= NV_SUBDEV(TIMER
, 0x04),
256 .ofuncs
= &(struct nvkm_ofuncs
) {
257 .ctor
= nv04_timer_ctor
,
258 .dtor
= nv04_timer_dtor
,
259 .init
= nv04_timer_init
,
260 .fini
= nv04_timer_fini
,