nv04.c 7.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2012 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Ben Skeggs
 */
24
#include "nv04.h"
25 26

static u64
B
Ben Skeggs 已提交
27
nv04_timer_read(struct nvkm_timer *tmr)
28
{
29
	struct nvkm_device *device = tmr->subdev.device;
30 31 32
	u32 hi, lo;

	do {
33 34 35
		hi = nvkm_rd32(device, NV04_PTIMER_TIME_1);
		lo = nvkm_rd32(device, NV04_PTIMER_TIME_0);
	} while (hi != nvkm_rd32(device, NV04_PTIMER_TIME_1));
36 37 38 39 40

	return ((u64)hi << 32 | lo);
}

static void
B
Ben Skeggs 已提交
41
nv04_timer_alarm_trigger(struct nvkm_timer *obj)
42
{
B
Ben Skeggs 已提交
43
	struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base);
44
	struct nvkm_device *device = tmr->base.subdev.device;
45
	struct nvkm_alarm *alarm, *atemp;
46 47 48 49
	unsigned long flags;
	LIST_HEAD(exec);

	/* move any due alarms off the pending list */
B
Ben Skeggs 已提交
50 51 52
	spin_lock_irqsave(&tmr->lock, flags);
	list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
		if (alarm->timestamp <= tmr->base.read(&tmr->base))
53 54 55 56
			list_move_tail(&alarm->head, &exec);
	}

	/* reschedule interrupt for next alarm time */
B
Ben Skeggs 已提交
57 58
	if (!list_empty(&tmr->alarms)) {
		alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
59 60
		nvkm_wr32(device, NV04_PTIMER_ALARM_0, alarm->timestamp);
		nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000001);
61
	} else {
62
		nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000);
63
	}
B
Ben Skeggs 已提交
64
	spin_unlock_irqrestore(&tmr->lock, flags);
65 66 67

	/* execute any pending alarm handlers */
	list_for_each_entry_safe(alarm, atemp, &exec, head) {
68
		list_del_init(&alarm->head);
69 70 71 72 73
		alarm->func(alarm);
	}
}

static void
B
Ben Skeggs 已提交
74
nv04_timer_alarm(struct nvkm_timer *obj, u64 time, struct nvkm_alarm *alarm)
75
{
B
Ben Skeggs 已提交
76
	struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base);
77
	struct nvkm_alarm *list;
78 79
	unsigned long flags;

B
Ben Skeggs 已提交
80
	alarm->timestamp = tmr->base.read(&tmr->base) + time;
81 82

	/* append new alarm to list, in soonest-alarm-first order */
B
Ben Skeggs 已提交
83
	spin_lock_irqsave(&tmr->lock, flags);
84 85 86 87
	if (!time) {
		if (!list_empty(&alarm->head))
			list_del(&alarm->head);
	} else {
B
Ben Skeggs 已提交
88
		list_for_each_entry(list, &tmr->alarms, head) {
89 90 91 92
			if (list->timestamp > alarm->timestamp)
				break;
		}
		list_add_tail(&alarm->head, &list->head);
93
	}
B
Ben Skeggs 已提交
94
	spin_unlock_irqrestore(&tmr->lock, flags);
95

96
	/* process pending alarms */
B
Ben Skeggs 已提交
97
	nv04_timer_alarm_trigger(&tmr->base);
98 99
}

100
static void
B
Ben Skeggs 已提交
101
nv04_timer_alarm_cancel(struct nvkm_timer *obj, struct nvkm_alarm *alarm)
102
{
B
Ben Skeggs 已提交
103
	struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base);
104
	unsigned long flags;
B
Ben Skeggs 已提交
105
	spin_lock_irqsave(&tmr->lock, flags);
106
	list_del_init(&alarm->head);
B
Ben Skeggs 已提交
107
	spin_unlock_irqrestore(&tmr->lock, flags);
108 109
}

110
static void
111
nv04_timer_intr(struct nvkm_subdev *subdev)
112
{
B
Ben Skeggs 已提交
113
	struct nv04_timer *tmr = (void *)subdev;
114 115
	struct nvkm_device *device = tmr->base.subdev.device;
	u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0);
116 117

	if (stat & 0x00000001) {
B
Ben Skeggs 已提交
118
		nv04_timer_alarm_trigger(&tmr->base);
119
		nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001);
120 121 122 123
		stat &= ~0x00000001;
	}

	if (stat) {
124
		nvkm_error(subdev, "intr %08x\n", stat);
125
		nvkm_wr32(device, NV04_PTIMER_INTR_0, stat);
126 127 128
	}
}

129
int
130
nv04_timer_fini(struct nvkm_object *object, bool suspend)
131
{
B
Ben Skeggs 已提交
132
	struct nv04_timer *tmr = (void *)object;
133
	struct nvkm_device *device = tmr->base.subdev.device;
134
	if (suspend)
B
Ben Skeggs 已提交
135
		tmr->suspend_time = nv04_timer_read(&tmr->base);
136
	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000);
B
Ben Skeggs 已提交
137
	return nvkm_timer_fini(&tmr->base, suspend);
138 139 140
}

static int
141
nv04_timer_init(struct nvkm_object *object)
142
{
B
Ben Skeggs 已提交
143
	struct nv04_timer *tmr = (void *)object;
144 145
	struct nvkm_subdev *subdev = &tmr->base.subdev;
	struct nvkm_device *device = subdev->device;
146
	u32 m = 1, f, n, d, lo, hi;
147 148
	int ret;

B
Ben Skeggs 已提交
149
	ret = nvkm_timer_init(&tmr->base);
150 151
	if (ret)
		return ret;
152

153
	/* aim for 31.25MHz, which gives us nanosecond timestamps */
154
	d = 1000000 / 32;
155 156

	/* determine base clock for timer source */
157 158
#if 0 /*XXX*/
	if (device->chipset < 0x40) {
159
		n = nvkm_hw_get_clock(device, PLL_CORE);
160
	} else
161 162
#endif
	if (device->chipset <= 0x40) {
163
		/*XXX: figure this out */
164
		f = -1;
165 166
		n = 0;
	} else {
167 168
		f = device->crystal;
		n = f;
169 170 171 172 173
		while (n < (d * 2)) {
			n += (n / m);
			m++;
		}

174
		nvkm_wr32(device, 0x009220, m - 1);
175 176
	}

177
	if (!n) {
178
		nvkm_warn(subdev, "unknown input clock freq\n");
179 180 181 182
		if (!nvkm_rd32(device, NV04_PTIMER_NUMERATOR) ||
		    !nvkm_rd32(device, NV04_PTIMER_DENOMINATOR)) {
			nvkm_wr32(device, NV04_PTIMER_NUMERATOR, 1);
			nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, 1);
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
		}
		return 0;
	}

	/* reduce ratio to acceptable values */
	while (((n % 5) == 0) && ((d % 5) == 0)) {
		n /= 5;
		d /= 5;
	}

	while (((n % 2) == 0) && ((d % 2) == 0)) {
		n /= 2;
		d /= 2;
	}

	while (n > 0xffff || d > 0xffff) {
		n >>= 1;
		d >>= 1;
	}

203
	/* restore the time before suspend */
B
Ben Skeggs 已提交
204 205 206
	lo = tmr->suspend_time;
	hi = (tmr->suspend_time >> 32);

207 208 209 210 211 212 213
	nvkm_debug(subdev, "input frequency : %dHz\n", f);
	nvkm_debug(subdev, "input multiplier: %d\n", m);
	nvkm_debug(subdev, "numerator       : %08x\n", n);
	nvkm_debug(subdev, "denominator     : %08x\n", d);
	nvkm_debug(subdev, "timer frequency : %dHz\n", (f * m) * d / n);
	nvkm_debug(subdev, "time low        : %08x\n", lo);
	nvkm_debug(subdev, "time high       : %08x\n", hi);
B
Ben Skeggs 已提交
214

215 216 217 218 219 220
	nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n);
	nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d);
	nvkm_wr32(device, NV04_PTIMER_INTR_0, 0xffffffff);
	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000);
	nvkm_wr32(device, NV04_PTIMER_TIME_1, hi);
	nvkm_wr32(device, NV04_PTIMER_TIME_0, lo);
221 222 223
	return 0;
}

224
void
225
nv04_timer_dtor(struct nvkm_object *object)
226
{
B
Ben Skeggs 已提交
227 228
	struct nv04_timer *tmr = (void *)object;
	return nvkm_timer_destroy(&tmr->base);
229 230 231
}

int
232 233 234
nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
		struct nvkm_oclass *oclass, void *data, u32 size,
		struct nvkm_object **pobject)
235
{
B
Ben Skeggs 已提交
236
	struct nv04_timer *tmr;
237 238
	int ret;

B
Ben Skeggs 已提交
239 240
	ret = nvkm_timer_create(parent, engine, oclass, &tmr);
	*pobject = nv_object(tmr);
241 242 243
	if (ret)
		return ret;

B
Ben Skeggs 已提交
244 245 246 247 248
	tmr->base.subdev.intr = nv04_timer_intr;
	tmr->base.read = nv04_timer_read;
	tmr->base.alarm = nv04_timer_alarm;
	tmr->base.alarm_cancel = nv04_timer_alarm_cancel;
	tmr->suspend_time = 0;
249

B
Ben Skeggs 已提交
250 251
	INIT_LIST_HEAD(&tmr->alarms);
	spin_lock_init(&tmr->lock);
252
	return 0;
253 254
}

255
struct nvkm_oclass
256 257
nv04_timer_oclass = {
	.handle = NV_SUBDEV(TIMER, 0x04),
258
	.ofuncs = &(struct nvkm_ofuncs) {
259 260 261 262 263 264
		.ctor = nv04_timer_ctor,
		.dtor = nv04_timer_dtor,
		.init = nv04_timer_init,
		.fini = nv04_timer_fini,
	}
};