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 "nv04.h"
25
26#include <core/device.h>
27
28static u64
29nv04_timer_read(struct nvkm_timer *ptimer)
30{
31	struct nv04_timer_priv *priv = (void *)ptimer;
32	u32 hi, lo;
33
34	do {
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));
38
39	return ((u64)hi << 32 | lo);
40}
41
42static void
43nv04_timer_alarm_trigger(struct nvkm_timer *ptimer)
44{
45	struct nv04_timer_priv *priv = (void *)ptimer;
46	struct nvkm_alarm *alarm, *atemp;
47	unsigned long flags;
48	LIST_HEAD(exec);
49
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);
55	}
56
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);
62	} else {
63		nv_wr32(priv, NV04_PTIMER_INTR_EN_0, 0x00000000);
64	}
65	spin_unlock_irqrestore(&priv->lock, flags);
66
67	/* execute any pending alarm handlers */
68	list_for_each_entry_safe(alarm, atemp, &exec, head) {
69		list_del_init(&alarm->head);
70		alarm->func(alarm);
71	}
72}
73
74static void
75nv04_timer_alarm(struct nvkm_timer *ptimer, u64 time, struct nvkm_alarm *alarm)
76{
77	struct nv04_timer_priv *priv = (void *)ptimer;
78	struct nvkm_alarm *list;
79	unsigned long flags;
80
81	alarm->timestamp = ptimer->read(ptimer) + time;
82
83	/* append new alarm to list, in soonest-alarm-first order */
84	spin_lock_irqsave(&priv->lock, flags);
85	if (!time) {
86		if (!list_empty(&alarm->head))
87			list_del(&alarm->head);
88	} else {
89		list_for_each_entry(list, &priv->alarms, head) {
90			if (list->timestamp > alarm->timestamp)
91				break;
92		}
93		list_add_tail(&alarm->head, &list->head);
94	}
95	spin_unlock_irqrestore(&priv->lock, flags);
96
97	/* process pending alarms */
98	nv04_timer_alarm_trigger(ptimer);
99}
100
101static void
102nv04_timer_alarm_cancel(struct nvkm_timer *ptimer, struct nvkm_alarm *alarm)
103{
104	struct nv04_timer_priv *priv = (void *)ptimer;
105	unsigned long flags;
106	spin_lock_irqsave(&priv->lock, flags);
107	list_del_init(&alarm->head);
108	spin_unlock_irqrestore(&priv->lock, flags);
109}
110
111static void
112nv04_timer_intr(struct nvkm_subdev *subdev)
113{
114	struct nv04_timer_priv *priv = (void *)subdev;
115	u32 stat = nv_rd32(priv, NV04_PTIMER_INTR_0);
116
117	if (stat & 0x00000001) {
118		nv04_timer_alarm_trigger(&priv->base);
119		nv_wr32(priv, NV04_PTIMER_INTR_0, 0x00000001);
120		stat &= ~0x00000001;
121	}
122
123	if (stat) {
124		nv_error(priv, "unknown stat 0x%08x\n", stat);
125		nv_wr32(priv, NV04_PTIMER_INTR_0, stat);
126	}
127}
128
129int
130nv04_timer_fini(struct nvkm_object *object, bool suspend)
131{
132	struct nv04_timer_priv *priv = (void *)object;
133	if (suspend)
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);
137}
138
139static int
140nv04_timer_init(struct nvkm_object *object)
141{
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;
145	int ret;
146
147	ret = nvkm_timer_init(&priv->base);
148	if (ret)
149		return ret;
150
151	/* aim for 31.25MHz, which gives us nanosecond timestamps */
152	d = 1000000 / 32;
153
154	/* determine base clock for timer source */
155#if 0 /*XXX*/
156	if (device->chipset < 0x40) {
157		n = nvkm_hw_get_clock(device, PLL_CORE);
158	} else
159#endif
160	if (device->chipset <= 0x40) {
161		/*XXX: figure this out */
162		f = -1;
163		n = 0;
164	} else {
165		f = device->crystal;
166		n = f;
167		while (n < (d * 2)) {
168			n += (n / m);
169			m++;
170		}
171
172		nv_wr32(priv, 0x009220, m - 1);
173	}
174
175	if (!n) {
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);
181		}
182		return 0;
183	}
184
185	/* reduce ratio to acceptable values */
186	while (((n % 5) == 0) && ((d % 5) == 0)) {
187		n /= 5;
188		d /= 5;
189	}
190
191	while (((n % 2) == 0) && ((d % 2) == 0)) {
192		n /= 2;
193		d /= 2;
194	}
195
196	while (n > 0xffff || d > 0xffff) {
197		n >>= 1;
198		d >>= 1;
199	}
200
201	/* restore the time before suspend */
202	lo = priv->suspend_time;
203	hi = (priv->suspend_time >> 32);
204
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);
212
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);
219	return 0;
220}
221
222void
223nv04_timer_dtor(struct nvkm_object *object)
224{
225	struct nv04_timer_priv *priv = (void *)object;
226	return nvkm_timer_destroy(&priv->base);
227}
228
229int
230nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
231		struct nvkm_oclass *oclass, void *data, u32 size,
232		struct nvkm_object **pobject)
233{
234	struct nv04_timer_priv *priv;
235	int ret;
236
237	ret = nvkm_timer_create(parent, engine, oclass, &priv);
238	*pobject = nv_object(priv);
239	if (ret)
240		return ret;
241
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;
247
248	INIT_LIST_HEAD(&priv->alarms);
249	spin_lock_init(&priv->lock);
250	return 0;
251}
252
253struct nvkm_oclass
254nv04_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,
261	}
262};
263