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"
25c39f472eSBen Skeggs
26e4f90a35SBen Skeggs s64
nvkm_timer_wait_test(struct nvkm_timer_wait * wait)27e4f90a35SBen Skeggs nvkm_timer_wait_test(struct nvkm_timer_wait *wait)
28e4f90a35SBen Skeggs {
29e4f90a35SBen Skeggs struct nvkm_subdev *subdev = &wait->tmr->subdev;
30e4f90a35SBen Skeggs u64 time = nvkm_timer_read(wait->tmr);
31e4f90a35SBen Skeggs
32e4f90a35SBen Skeggs if (wait->reads == 0) {
33e4f90a35SBen Skeggs wait->time0 = time;
34e4f90a35SBen Skeggs wait->time1 = time;
35e4f90a35SBen Skeggs }
36e4f90a35SBen Skeggs
37e4f90a35SBen Skeggs if (wait->time1 == time) {
38e4f90a35SBen Skeggs if (wait->reads++ == 16) {
39e4f90a35SBen Skeggs nvkm_fatal(subdev, "stalled at %016llx\n", time);
40e4f90a35SBen Skeggs return -ETIMEDOUT;
41e4f90a35SBen Skeggs }
42e4f90a35SBen Skeggs } else {
43e4f90a35SBen Skeggs wait->time1 = time;
44e4f90a35SBen Skeggs wait->reads = 1;
45e4f90a35SBen Skeggs }
46e4f90a35SBen Skeggs
47e4f90a35SBen Skeggs if (wait->time1 - wait->time0 > wait->limit)
48e4f90a35SBen Skeggs return -ETIMEDOUT;
49e4f90a35SBen Skeggs
50e4f90a35SBen Skeggs return wait->time1 - wait->time0;
51e4f90a35SBen Skeggs }
52e4f90a35SBen Skeggs
53e4f90a35SBen Skeggs void
nvkm_timer_wait_init(struct nvkm_device * device,u64 nsec,struct nvkm_timer_wait * wait)54e4f90a35SBen Skeggs nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
55e4f90a35SBen Skeggs struct nvkm_timer_wait *wait)
56e4f90a35SBen Skeggs {
57e4f90a35SBen Skeggs wait->tmr = device->timer;
58e4f90a35SBen Skeggs wait->limit = nsec;
59e4f90a35SBen Skeggs wait->reads = 0;
60e4f90a35SBen Skeggs }
61e4f90a35SBen Skeggs
6231649ecfSBen Skeggs u64
nvkm_timer_read(struct nvkm_timer * tmr)6331649ecfSBen Skeggs nvkm_timer_read(struct nvkm_timer *tmr)
64c39f472eSBen Skeggs {
6531649ecfSBen Skeggs return tmr->func->read(tmr);
66c39f472eSBen Skeggs }
67c39f472eSBen Skeggs
68c39f472eSBen Skeggs void
nvkm_timer_alarm_trigger(struct nvkm_timer * tmr)6931649ecfSBen Skeggs nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
70c39f472eSBen Skeggs {
7131649ecfSBen Skeggs struct nvkm_alarm *alarm, *atemp;
7231649ecfSBen Skeggs unsigned long flags;
7331649ecfSBen Skeggs LIST_HEAD(exec);
7431649ecfSBen Skeggs
751b0f8438SBen Skeggs /* Process pending alarms. */
7631649ecfSBen Skeggs spin_lock_irqsave(&tmr->lock, flags);
7731649ecfSBen Skeggs list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
781b0f8438SBen Skeggs /* Have we hit the earliest alarm that hasn't gone off? */
791b0f8438SBen Skeggs if (alarm->timestamp > nvkm_timer_read(tmr)) {
801b0f8438SBen Skeggs /* Schedule it. If we didn't race, we're done. */
811b0f8438SBen Skeggs tmr->func->alarm_init(tmr, alarm->timestamp);
821b0f8438SBen Skeggs if (alarm->timestamp > nvkm_timer_read(tmr))
831b0f8438SBen Skeggs break;
841b0f8438SBen Skeggs }
851b0f8438SBen Skeggs
861b0f8438SBen Skeggs /* Move to completed list. We'll drop the lock before
871b0f8438SBen Skeggs * executing the callback so it can reschedule itself.
881b0f8438SBen Skeggs */
89b4e382caSBen Skeggs list_del_init(&alarm->head);
90b4e382caSBen Skeggs list_add(&alarm->exec, &exec);
9131649ecfSBen Skeggs }
9231649ecfSBen Skeggs
931b0f8438SBen Skeggs /* Shut down interrupt if no more pending alarms. */
941b0f8438SBen Skeggs if (list_empty(&tmr->alarms))
9531649ecfSBen Skeggs tmr->func->alarm_fini(tmr);
9631649ecfSBen Skeggs spin_unlock_irqrestore(&tmr->lock, flags);
9731649ecfSBen Skeggs
981b0f8438SBen Skeggs /* Execute completed callbacks. */
99b4e382caSBen Skeggs list_for_each_entry_safe(alarm, atemp, &exec, exec) {
100b4e382caSBen Skeggs list_del(&alarm->exec);
10131649ecfSBen Skeggs alarm->func(alarm);
10231649ecfSBen Skeggs }
10331649ecfSBen Skeggs }
10431649ecfSBen Skeggs
10531649ecfSBen Skeggs void
nvkm_timer_alarm(struct nvkm_timer * tmr,u32 nsec,struct nvkm_alarm * alarm)10631649ecfSBen Skeggs nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
10731649ecfSBen Skeggs {
10831649ecfSBen Skeggs struct nvkm_alarm *list;
10931649ecfSBen Skeggs unsigned long flags;
11031649ecfSBen Skeggs
1119fc64667SBen Skeggs /* Remove alarm from pending list.
1129fc64667SBen Skeggs *
1139fc64667SBen Skeggs * This both protects against the corruption of the list,
1149fc64667SBen Skeggs * and implements alarm rescheduling/cancellation.
1159fc64667SBen Skeggs */
11631649ecfSBen Skeggs spin_lock_irqsave(&tmr->lock, flags);
1179fc64667SBen Skeggs list_del_init(&alarm->head);
1189fc64667SBen Skeggs
1199fc64667SBen Skeggs if (nsec) {
1209fc64667SBen Skeggs /* Insert into pending list, ordered earliest to latest. */
1219fc64667SBen Skeggs alarm->timestamp = nvkm_timer_read(tmr) + nsec;
12231649ecfSBen Skeggs list_for_each_entry(list, &tmr->alarms, head) {
12331649ecfSBen Skeggs if (list->timestamp > alarm->timestamp)
12431649ecfSBen Skeggs break;
12531649ecfSBen Skeggs }
126330bdf62SBen Skeggs
12731649ecfSBen Skeggs list_add_tail(&alarm->head, &list->head);
128330bdf62SBen Skeggs
129330bdf62SBen Skeggs /* Update HW if this is now the earliest alarm. */
130330bdf62SBen Skeggs list = list_first_entry(&tmr->alarms, typeof(*list), head);
131330bdf62SBen Skeggs if (list == alarm) {
132330bdf62SBen Skeggs tmr->func->alarm_init(tmr, alarm->timestamp);
133330bdf62SBen Skeggs /* This shouldn't happen if callers aren't stupid.
134330bdf62SBen Skeggs *
135330bdf62SBen Skeggs * Worst case scenario is that it'll take roughly
136330bdf62SBen Skeggs * 4 seconds for the next alarm to trigger.
137330bdf62SBen Skeggs */
138330bdf62SBen Skeggs WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
139330bdf62SBen Skeggs }
14031649ecfSBen Skeggs }
14131649ecfSBen Skeggs spin_unlock_irqrestore(&tmr->lock, flags);
14231649ecfSBen Skeggs }
14331649ecfSBen Skeggs
14431649ecfSBen Skeggs static void
nvkm_timer_intr(struct nvkm_subdev * subdev)14531649ecfSBen Skeggs nvkm_timer_intr(struct nvkm_subdev *subdev)
14631649ecfSBen Skeggs {
14731649ecfSBen Skeggs struct nvkm_timer *tmr = nvkm_timer(subdev);
14831649ecfSBen Skeggs tmr->func->intr(tmr);
14931649ecfSBen Skeggs }
15031649ecfSBen Skeggs
15131649ecfSBen Skeggs static int
nvkm_timer_fini(struct nvkm_subdev * subdev,bool suspend)15231649ecfSBen Skeggs nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
15331649ecfSBen Skeggs {
15431649ecfSBen Skeggs struct nvkm_timer *tmr = nvkm_timer(subdev);
15531649ecfSBen Skeggs tmr->func->alarm_fini(tmr);
15631649ecfSBen Skeggs return 0;
15731649ecfSBen Skeggs }
15831649ecfSBen Skeggs
15931649ecfSBen Skeggs static int
nvkm_timer_init(struct nvkm_subdev * subdev)16031649ecfSBen Skeggs nvkm_timer_init(struct nvkm_subdev *subdev)
16131649ecfSBen Skeggs {
16231649ecfSBen Skeggs struct nvkm_timer *tmr = nvkm_timer(subdev);
16331649ecfSBen Skeggs if (tmr->func->init)
16431649ecfSBen Skeggs tmr->func->init(tmr);
16531649ecfSBen Skeggs tmr->func->time(tmr, ktime_to_ns(ktime_get()));
16631649ecfSBen Skeggs nvkm_timer_alarm_trigger(tmr);
16731649ecfSBen Skeggs return 0;
16831649ecfSBen Skeggs }
16931649ecfSBen Skeggs
17031649ecfSBen Skeggs static void *
nvkm_timer_dtor(struct nvkm_subdev * subdev)17131649ecfSBen Skeggs nvkm_timer_dtor(struct nvkm_subdev *subdev)
17231649ecfSBen Skeggs {
17331649ecfSBen Skeggs return nvkm_timer(subdev);
17431649ecfSBen Skeggs }
17531649ecfSBen Skeggs
17631649ecfSBen Skeggs static const struct nvkm_subdev_func
17731649ecfSBen Skeggs nvkm_timer = {
17831649ecfSBen Skeggs .dtor = nvkm_timer_dtor,
17931649ecfSBen Skeggs .init = nvkm_timer_init,
18031649ecfSBen Skeggs .fini = nvkm_timer_fini,
18131649ecfSBen Skeggs .intr = nvkm_timer_intr,
18231649ecfSBen Skeggs };
18331649ecfSBen Skeggs
18431649ecfSBen Skeggs int
nvkm_timer_new_(const struct nvkm_timer_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_timer ** ptmr)18531649ecfSBen Skeggs nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
186*9aad54d5SBen Skeggs enum nvkm_subdev_type type, int inst, struct nvkm_timer **ptmr)
18731649ecfSBen Skeggs {
18831649ecfSBen Skeggs struct nvkm_timer *tmr;
18931649ecfSBen Skeggs
19031649ecfSBen Skeggs if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
19131649ecfSBen Skeggs return -ENOMEM;
19231649ecfSBen Skeggs
193*9aad54d5SBen Skeggs nvkm_subdev_ctor(&nvkm_timer, device, type, inst, &tmr->subdev);
19431649ecfSBen Skeggs tmr->func = func;
19531649ecfSBen Skeggs INIT_LIST_HEAD(&tmr->alarms);
19631649ecfSBen Skeggs spin_lock_init(&tmr->lock);
19731649ecfSBen Skeggs return 0;
198c39f472eSBen Skeggs }
199