drm/nouveau/timer: restore the time on resume
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / gpu / drm / nouveau / core / subdev / timer / nv04.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
25 #include <subdev/timer.h>
26
27 #define NV04_PTIMER_INTR_0      0x009100
28 #define NV04_PTIMER_INTR_EN_0   0x009140
29 #define NV04_PTIMER_NUMERATOR   0x009200
30 #define NV04_PTIMER_DENOMINATOR 0x009210
31 #define NV04_PTIMER_TIME_0      0x009400
32 #define NV04_PTIMER_TIME_1      0x009410
33 #define NV04_PTIMER_ALARM_0     0x009420
34
35 struct nv04_timer_priv {
36         struct nouveau_timer base;
37         struct list_head alarms;
38         spinlock_t lock;
39         u64 suspend_time;
40 };
41
42 static u64
43 nv04_timer_read(struct nouveau_timer *ptimer)
44 {
45         struct nv04_timer_priv *priv = (void *)ptimer;
46         u32 hi, lo;
47
48         do {
49                 hi = nv_rd32(priv, NV04_PTIMER_TIME_1);
50                 lo = nv_rd32(priv, NV04_PTIMER_TIME_0);
51         } while (hi != nv_rd32(priv, NV04_PTIMER_TIME_1));
52
53         return ((u64)hi << 32 | lo);
54 }
55
56 static void
57 nv04_timer_alarm_trigger(struct nouveau_timer *ptimer)
58 {
59         struct nv04_timer_priv *priv = (void *)ptimer;
60         struct nouveau_alarm *alarm, *atemp;
61         unsigned long flags;
62         LIST_HEAD(exec);
63
64         /* move any due alarms off the pending list */
65         spin_lock_irqsave(&priv->lock, flags);
66         list_for_each_entry_safe(alarm, atemp, &priv->alarms, head) {
67                 if (alarm->timestamp <= ptimer->read(ptimer))
68                         list_move_tail(&alarm->head, &exec);
69         }
70
71         /* reschedule interrupt for next alarm time */
72         if (!list_empty(&priv->alarms)) {
73                 alarm = list_first_entry(&priv->alarms, typeof(*alarm), head);
74                 nv_wr32(priv, NV04_PTIMER_ALARM_0, alarm->timestamp);
75                 nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000001);
76         } else {
77                 nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
78         }
79         spin_unlock_irqrestore(&priv->lock, flags);
80
81         /* execute any pending alarm handlers */
82         list_for_each_entry_safe(alarm, atemp, &exec, head) {
83                 list_del_init(&alarm->head);
84                 alarm->func(alarm);
85         }
86 }
87
88 static void
89 nv04_timer_alarm(struct nouveau_timer *ptimer, u64 time,
90                  struct nouveau_alarm *alarm)
91 {
92         struct nv04_timer_priv *priv = (void *)ptimer;
93         struct nouveau_alarm *list;
94         unsigned long flags;
95
96         alarm->timestamp = ptimer->read(ptimer) + time;
97
98         /* append new alarm to list, in soonest-alarm-first order */
99         spin_lock_irqsave(&priv->lock, flags);
100         if (!time) {
101                 if (!list_empty(&alarm->head))
102                         list_del(&alarm->head);
103         } else {
104                 list_for_each_entry(list, &priv->alarms, head) {
105                         if (list->timestamp > alarm->timestamp)
106                                 break;
107                 }
108                 list_add_tail(&alarm->head, &list->head);
109         }
110         spin_unlock_irqrestore(&priv->lock, flags);
111
112         /* process pending alarms */
113         nv04_timer_alarm_trigger(ptimer);
114 }
115
116 static void
117 nv04_timer_intr(struct nouveau_subdev *subdev)
118 {
119         struct nv04_timer_priv *priv = (void *)subdev;
120         u32 stat = nv_rd32(priv, NV04_PTIMER_INTR_0);
121
122         if (stat & 0x00000001) {
123                 nv04_timer_alarm_trigger(&priv->base);
124                 nv_wr32(priv, NV04_PTIMER_INTR_0, 0x00000001);
125                 stat &= ~0x00000001;
126         }
127
128         if (stat) {
129                 nv_error(priv, "unknown stat 0x%08x\n", stat);
130                 nv_wr32(priv, NV04_PTIMER_INTR_0, stat);
131         }
132 }
133
134 static int
135 nv04_timer_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
136                 struct nouveau_oclass *oclass, void *data, u32 size,
137                 struct nouveau_object **pobject)
138 {
139         struct nv04_timer_priv *priv;
140         int ret;
141
142         ret = nouveau_timer_create(parent, engine, oclass, &priv);
143         *pobject = nv_object(priv);
144         if (ret)
145                 return ret;
146
147         priv->base.base.intr = nv04_timer_intr;
148         priv->base.read = nv04_timer_read;
149         priv->base.alarm = nv04_timer_alarm;
150         priv->suspend_time = 0;
151
152         INIT_LIST_HEAD(&priv->alarms);
153         spin_lock_init(&priv->lock);
154         return 0;
155 }
156
157 static void
158 nv04_timer_dtor(struct nouveau_object *object)
159 {
160         struct nv04_timer_priv *priv = (void *)object;
161         return nouveau_timer_destroy(&priv->base);
162 }
163
164 static int
165 nv04_timer_init(struct nouveau_object *object)
166 {
167         struct nouveau_device *device = nv_device(object);
168         struct nv04_timer_priv *priv = (void *)object;
169         u32 m = 1, f, n, d, lo, hi;
170         int ret;
171
172         ret = nouveau_timer_init(&priv->base);
173         if (ret)
174                 return ret;
175
176         /* aim for 31.25MHz, which gives us nanosecond timestamps */
177         d = 1000000 / 32;
178
179         /* determine base clock for timer source */
180 #if 0 /*XXX*/
181         if (device->chipset < 0x40) {
182                 n = nouveau_hw_get_clock(device, PLL_CORE);
183         } else
184 #endif
185         if (device->chipset <= 0x40) {
186                 /*XXX: figure this out */
187                 f = -1;
188                 n = 0;
189         } else {
190                 f = device->crystal;
191                 n = f;
192                 while (n < (d * 2)) {
193                         n += (n / m);
194                         m++;
195                 }
196
197                 nv_wr32(priv, 0x009220, m - 1);
198         }
199
200         if (!n) {
201                 nv_warn(priv, "unknown input clock freq\n");
202                 if (!nv_rd32(priv, NV04_PTIMER_NUMERATOR) ||
203                     !nv_rd32(priv, NV04_PTIMER_DENOMINATOR)) {
204                         nv_wr32(priv, NV04_PTIMER_NUMERATOR, 1);
205                         nv_wr32(priv, NV04_PTIMER_DENOMINATOR, 1);
206                 }
207                 return 0;
208         }
209
210         /* reduce ratio to acceptable values */
211         while (((n % 5) == 0) && ((d % 5) == 0)) {
212                 n /= 5;
213                 d /= 5;
214         }
215
216         while (((n % 2) == 0) && ((d % 2) == 0)) {
217                 n /= 2;
218                 d /= 2;
219         }
220
221         while (n > 0xffff || d > 0xffff) {
222                 n >>= 1;
223                 d >>= 1;
224         }
225
226         /* restore the time before suspend */
227         lo = priv->suspend_time;
228         hi = (priv->suspend_time >> 32);
229
230         nv_debug(priv, "input frequency : %dHz\n", f);
231         nv_debug(priv, "input multiplier: %d\n", m);
232         nv_debug(priv, "numerator       : 0x%08x\n", n);
233         nv_debug(priv, "denominator     : 0x%08x\n", d);
234         nv_debug(priv, "timer frequency : %dHz\n", (f * m) * d / n);
235         nv_debug(priv, "time low        : 0x%08x\n", lo);
236         nv_debug(priv, "time high       : 0x%08x\n", hi);
237
238         nv_wr32(priv, NV04_PTIMER_NUMERATOR, n);
239         nv_wr32(priv, NV04_PTIMER_DENOMINATOR, d);
240         nv_wr32(priv, NV04_PTIMER_INTR_0, 0xffffffff);
241         nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
242         nv_wr32(priv, NV04_PTIMER_TIME_1, hi);
243         nv_wr32(priv, NV04_PTIMER_TIME_0, lo);
244
245         return 0;
246 }
247
248 static int
249 nv04_timer_fini(struct nouveau_object *object, bool suspend)
250 {
251         struct nv04_timer_priv *priv = (void *)object;
252         if (suspend)
253                 priv->suspend_time = nv04_timer_read(&priv->base);
254         nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
255         return nouveau_timer_fini(&priv->base, suspend);
256 }
257
258 struct nouveau_oclass
259 nv04_timer_oclass = {
260         .handle = NV_SUBDEV(TIMER, 0x04),
261         .ofuncs = &(struct nouveau_ofuncs) {
262                 .ctor = nv04_timer_ctor,
263                 .dtor = nv04_timer_dtor,
264                 .init = nv04_timer_init,
265                 .fini = nv04_timer_fini,
266         }
267 };