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