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