xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
2431649ecfSBen Skeggs #include "priv.h"
2531649ecfSBen Skeggs #include "regsnv04.h"
26c39f472eSBen Skeggs 
2731649ecfSBen Skeggs void
nv04_timer_time(struct nvkm_timer * tmr,u64 time)2831649ecfSBen Skeggs nv04_timer_time(struct nvkm_timer *tmr, u64 time)
2931649ecfSBen Skeggs {
3031649ecfSBen Skeggs 	struct nvkm_subdev *subdev = &tmr->subdev;
3131649ecfSBen Skeggs 	struct nvkm_device *device = subdev->device;
3231649ecfSBen Skeggs 	u32 hi = upper_32_bits(time);
3331649ecfSBen Skeggs 	u32 lo = lower_32_bits(time);
3431649ecfSBen Skeggs 
3531649ecfSBen Skeggs 	nvkm_debug(subdev, "time low        : %08x\n", lo);
3631649ecfSBen Skeggs 	nvkm_debug(subdev, "time high       : %08x\n", hi);
3731649ecfSBen Skeggs 
3831649ecfSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_TIME_1, hi);
3931649ecfSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_TIME_0, lo);
4031649ecfSBen Skeggs }
4131649ecfSBen Skeggs 
4231649ecfSBen Skeggs u64
nv04_timer_read(struct nvkm_timer * tmr)43cb8bb9ceSBen Skeggs nv04_timer_read(struct nvkm_timer *tmr)
44c39f472eSBen Skeggs {
45c44c049fSBen Skeggs 	struct nvkm_device *device = tmr->subdev.device;
46c39f472eSBen Skeggs 	u32 hi, lo;
47c39f472eSBen Skeggs 
48c39f472eSBen Skeggs 	do {
49c44c049fSBen Skeggs 		hi = nvkm_rd32(device, NV04_PTIMER_TIME_1);
50c44c049fSBen Skeggs 		lo = nvkm_rd32(device, NV04_PTIMER_TIME_0);
51c44c049fSBen Skeggs 	} while (hi != nvkm_rd32(device, NV04_PTIMER_TIME_1));
52c39f472eSBen Skeggs 
53c39f472eSBen Skeggs 	return ((u64)hi << 32 | lo);
54c39f472eSBen Skeggs }
55c39f472eSBen Skeggs 
5631649ecfSBen Skeggs void
nv04_timer_alarm_fini(struct nvkm_timer * tmr)5731649ecfSBen Skeggs nv04_timer_alarm_fini(struct nvkm_timer *tmr)
58c39f472eSBen Skeggs {
5931649ecfSBen Skeggs 	struct nvkm_device *device = tmr->subdev.device;
60c44c049fSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000);
61c39f472eSBen Skeggs }
62c39f472eSBen Skeggs 
6331649ecfSBen Skeggs void
nv04_timer_alarm_init(struct nvkm_timer * tmr,u32 time)6431649ecfSBen Skeggs nv04_timer_alarm_init(struct nvkm_timer *tmr, u32 time)
65c39f472eSBen Skeggs {
6631649ecfSBen Skeggs 	struct nvkm_device *device = tmr->subdev.device;
6731649ecfSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_ALARM_0, time);
6831649ecfSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000001);
69c39f472eSBen Skeggs }
70c39f472eSBen Skeggs 
7131649ecfSBen Skeggs void
nv04_timer_intr(struct nvkm_timer * tmr)7231649ecfSBen Skeggs nv04_timer_intr(struct nvkm_timer *tmr)
73c39f472eSBen Skeggs {
7431649ecfSBen Skeggs 	struct nvkm_subdev *subdev = &tmr->subdev;
7531649ecfSBen Skeggs 	struct nvkm_device *device = subdev->device;
76c44c049fSBen Skeggs 	u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0);
77c39f472eSBen Skeggs 
78c39f472eSBen Skeggs 	if (stat & 0x00000001) {
79c44c049fSBen Skeggs 		nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001);
803733bd8bSBen Skeggs 		nvkm_timer_alarm_trigger(tmr);
81c39f472eSBen Skeggs 		stat &= ~0x00000001;
82c39f472eSBen Skeggs 	}
83c39f472eSBen Skeggs 
84c39f472eSBen Skeggs 	if (stat) {
859d7b9d9fSBen Skeggs 		nvkm_error(subdev, "intr %08x\n", stat);
86c44c049fSBen Skeggs 		nvkm_wr32(device, NV04_PTIMER_INTR_0, stat);
87c39f472eSBen Skeggs 	}
88c39f472eSBen Skeggs }
89c39f472eSBen Skeggs 
9031649ecfSBen Skeggs static void
nv04_timer_init(struct nvkm_timer * tmr)9131649ecfSBen Skeggs nv04_timer_init(struct nvkm_timer *tmr)
92c39f472eSBen Skeggs {
9331649ecfSBen Skeggs 	struct nvkm_subdev *subdev = &tmr->subdev;
949d7b9d9fSBen Skeggs 	struct nvkm_device *device = subdev->device;
9531649ecfSBen Skeggs 	u32 f = 0; /*XXX: nvclk */
9631649ecfSBen Skeggs 	u32 n, d;
97c39f472eSBen Skeggs 
98c39f472eSBen Skeggs 	/* aim for 31.25MHz, which gives us nanosecond timestamps */
99c39f472eSBen Skeggs 	d = 1000000 / 32;
100c39f472eSBen Skeggs 	n = f;
101c39f472eSBen Skeggs 
10231649ecfSBen Skeggs 	if (!f) {
10331649ecfSBen Skeggs 		n = nvkm_rd32(device, NV04_PTIMER_NUMERATOR);
10431649ecfSBen Skeggs 		d = nvkm_rd32(device, NV04_PTIMER_DENOMINATOR);
10531649ecfSBen Skeggs 		if (!n || !d) {
10631649ecfSBen Skeggs 			n = 1;
10731649ecfSBen Skeggs 			d = 1;
108c39f472eSBen Skeggs 		}
1099d7b9d9fSBen Skeggs 		nvkm_warn(subdev, "unknown input clock freq\n");
110c39f472eSBen Skeggs 	}
111c39f472eSBen Skeggs 
112c39f472eSBen Skeggs 	/* reduce ratio to acceptable values */
113c39f472eSBen Skeggs 	while (((n % 5) == 0) && ((d % 5) == 0)) {
114c39f472eSBen Skeggs 		n /= 5;
115c39f472eSBen Skeggs 		d /= 5;
116c39f472eSBen Skeggs 	}
117c39f472eSBen Skeggs 
118c39f472eSBen Skeggs 	while (((n % 2) == 0) && ((d % 2) == 0)) {
119c39f472eSBen Skeggs 		n /= 2;
120c39f472eSBen Skeggs 		d /= 2;
121c39f472eSBen Skeggs 	}
122c39f472eSBen Skeggs 
123c39f472eSBen Skeggs 	while (n > 0xffff || d > 0xffff) {
124c39f472eSBen Skeggs 		n >>= 1;
125c39f472eSBen Skeggs 		d >>= 1;
126c39f472eSBen Skeggs 	}
127c39f472eSBen Skeggs 
1289d7b9d9fSBen Skeggs 	nvkm_debug(subdev, "input frequency : %dHz\n", f);
1299d7b9d9fSBen Skeggs 	nvkm_debug(subdev, "numerator       : %08x\n", n);
1309d7b9d9fSBen Skeggs 	nvkm_debug(subdev, "denominator     : %08x\n", d);
13131649ecfSBen Skeggs 	nvkm_debug(subdev, "timer frequency : %dHz\n", f * d / n);
132c39f472eSBen Skeggs 
133c44c049fSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n);
134c44c049fSBen Skeggs 	nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d);
135c39f472eSBen Skeggs }
136c39f472eSBen Skeggs 
13731649ecfSBen Skeggs static const struct nvkm_timer_func
13831649ecfSBen Skeggs nv04_timer = {
13931649ecfSBen Skeggs 	.init = nv04_timer_init,
14031649ecfSBen Skeggs 	.intr = nv04_timer_intr,
14131649ecfSBen Skeggs 	.read = nv04_timer_read,
14231649ecfSBen Skeggs 	.time = nv04_timer_time,
14331649ecfSBen Skeggs 	.alarm_init = nv04_timer_alarm_init,
14431649ecfSBen Skeggs 	.alarm_fini = nv04_timer_alarm_fini,
14531649ecfSBen Skeggs };
146c39f472eSBen Skeggs 
147c39f472eSBen Skeggs int
nv04_timer_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_timer ** ptmr)148*9aad54d5SBen Skeggs nv04_timer_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
149*9aad54d5SBen Skeggs 	       struct nvkm_timer **ptmr)
150c39f472eSBen Skeggs {
151*9aad54d5SBen Skeggs 	return nvkm_timer_new_(&nv04_timer, device, type, inst, ptmr);
152c39f472eSBen Skeggs }
153