xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/timer/nouveau_nvkm_subdev_timer_nv04.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: nouveau_nvkm_subdev_timer_nv04.c,v 1.3 2021/12/18 23:45:42 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2012 Red Hat Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors: Ben Skeggs
25  */
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_timer_nv04.c,v 1.3 2021/12/18 23:45:42 riastradh Exp $");
28 
29 #include "priv.h"
30 #include "regsnv04.h"
31 
32 void
nv04_timer_time(struct nvkm_timer * tmr,u64 time)33 nv04_timer_time(struct nvkm_timer *tmr, u64 time)
34 {
35 	struct nvkm_subdev *subdev = &tmr->subdev;
36 	struct nvkm_device *device = subdev->device;
37 	u32 hi = upper_32_bits(time);
38 	u32 lo = lower_32_bits(time);
39 
40 	nvkm_debug(subdev, "time low        : %08x\n", lo);
41 	nvkm_debug(subdev, "time high       : %08x\n", hi);
42 
43 	nvkm_wr32(device, NV04_PTIMER_TIME_1, hi);
44 	nvkm_wr32(device, NV04_PTIMER_TIME_0, lo);
45 }
46 
47 u64
nv04_timer_read(struct nvkm_timer * tmr)48 nv04_timer_read(struct nvkm_timer *tmr)
49 {
50 	struct nvkm_device *device = tmr->subdev.device;
51 	u32 hi, lo;
52 
53 	do {
54 		hi = nvkm_rd32(device, NV04_PTIMER_TIME_1);
55 		lo = nvkm_rd32(device, NV04_PTIMER_TIME_0);
56 	} while (hi != nvkm_rd32(device, NV04_PTIMER_TIME_1));
57 
58 	return ((u64)hi << 32 | lo);
59 }
60 
61 void
nv04_timer_alarm_fini(struct nvkm_timer * tmr)62 nv04_timer_alarm_fini(struct nvkm_timer *tmr)
63 {
64 	struct nvkm_device *device = tmr->subdev.device;
65 	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000);
66 }
67 
68 void
nv04_timer_alarm_init(struct nvkm_timer * tmr,u32 time)69 nv04_timer_alarm_init(struct nvkm_timer *tmr, u32 time)
70 {
71 	struct nvkm_device *device = tmr->subdev.device;
72 	nvkm_wr32(device, NV04_PTIMER_ALARM_0, time);
73 	nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000001);
74 }
75 
76 void
nv04_timer_intr(struct nvkm_timer * tmr)77 nv04_timer_intr(struct nvkm_timer *tmr)
78 {
79 	struct nvkm_subdev *subdev = &tmr->subdev;
80 	struct nvkm_device *device = subdev->device;
81 	u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0);
82 
83 	if (stat & 0x00000001) {
84 		nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001);
85 		nvkm_timer_alarm_trigger(tmr);
86 		stat &= ~0x00000001;
87 	}
88 
89 	if (stat) {
90 		nvkm_error(subdev, "intr %08x\n", stat);
91 		nvkm_wr32(device, NV04_PTIMER_INTR_0, stat);
92 	}
93 }
94 
95 static void
nv04_timer_init(struct nvkm_timer * tmr)96 nv04_timer_init(struct nvkm_timer *tmr)
97 {
98 	struct nvkm_subdev *subdev = &tmr->subdev;
99 	struct nvkm_device *device = subdev->device;
100 	u32 f = 0; /*XXX: nvclk */
101 	u32 n, d;
102 
103 	/* aim for 31.25MHz, which gives us nanosecond timestamps */
104 	d = 1000000 / 32;
105 	n = f;
106 
107 	if (!f) {
108 		n = nvkm_rd32(device, NV04_PTIMER_NUMERATOR);
109 		d = nvkm_rd32(device, NV04_PTIMER_DENOMINATOR);
110 		if (!n || !d) {
111 			n = 1;
112 			d = 1;
113 		}
114 		nvkm_warn(subdev, "unknown input clock freq\n");
115 	}
116 
117 	/* reduce ratio to acceptable values */
118 	while (((n % 5) == 0) && ((d % 5) == 0)) {
119 		n /= 5;
120 		d /= 5;
121 	}
122 
123 	while (((n % 2) == 0) && ((d % 2) == 0)) {
124 		n /= 2;
125 		d /= 2;
126 	}
127 
128 	while (n > 0xffff || d > 0xffff) {
129 		n >>= 1;
130 		d >>= 1;
131 	}
132 
133 	nvkm_debug(subdev, "input frequency : %dHz\n", f);
134 	nvkm_debug(subdev, "numerator       : %08x\n", n);
135 	nvkm_debug(subdev, "denominator     : %08x\n", d);
136 	nvkm_debug(subdev, "timer frequency : %dHz\n", f * d / n);
137 
138 	nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n);
139 	nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d);
140 }
141 
142 static const struct nvkm_timer_func
143 nv04_timer = {
144 	.init = nv04_timer_init,
145 	.intr = nv04_timer_intr,
146 	.read = nv04_timer_read,
147 	.time = nv04_timer_time,
148 	.alarm_init = nv04_timer_alarm_init,
149 	.alarm_fini = nv04_timer_alarm_fini,
150 };
151 
152 int
nv04_timer_new(struct nvkm_device * device,int index,struct nvkm_timer ** ptmr)153 nv04_timer_new(struct nvkm_device *device, int index, struct nvkm_timer **ptmr)
154 {
155 	return nvkm_timer_new_(&nv04_timer, device, index, ptmr);
156 }
157