These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / nouveau / nvkm / subdev / timer / base.c
index d894061..d4dae1f 100644 (file)
  *
  * Authors: Ben Skeggs
  */
-#include <subdev/timer.h>
+#include "priv.h"
 
-bool
-nvkm_timer_wait_eq(void *obj, u64 nsec, u32 addr, u32 mask, u32 data)
+u64
+nvkm_timer_read(struct nvkm_timer *tmr)
 {
-       struct nvkm_timer *ptimer = nvkm_timer(obj);
-       u64 time0;
-
-       time0 = ptimer->read(ptimer);
-       do {
-               if (nv_iclass(obj, NV_SUBDEV_CLASS)) {
-                       if ((nv_rd32(obj, addr) & mask) == data)
-                               return true;
-               } else {
-                       if ((nv_ro32(obj, addr) & mask) == data)
-                               return true;
-               }
-       } while (ptimer->read(ptimer) - time0 < nsec);
+       return tmr->func->read(tmr);
+}
+
+void
+nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
+{
+       struct nvkm_alarm *alarm, *atemp;
+       unsigned long flags;
+       LIST_HEAD(exec);
+
+       /* move any due alarms off the pending list */
+       spin_lock_irqsave(&tmr->lock, flags);
+       list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
+               if (alarm->timestamp <= nvkm_timer_read(tmr))
+                       list_move_tail(&alarm->head, &exec);
+       }
 
-       return false;
+       /* reschedule interrupt for next alarm time */
+       if (!list_empty(&tmr->alarms)) {
+               alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
+               tmr->func->alarm_init(tmr, alarm->timestamp);
+       } else {
+               tmr->func->alarm_fini(tmr);
+       }
+       spin_unlock_irqrestore(&tmr->lock, flags);
+
+       /* execute any pending alarm handlers */
+       list_for_each_entry_safe(alarm, atemp, &exec, head) {
+               list_del_init(&alarm->head);
+               alarm->func(alarm);
+       }
 }
 
-bool
-nvkm_timer_wait_ne(void *obj, u64 nsec, u32 addr, u32 mask, u32 data)
+void
+nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
 {
-       struct nvkm_timer *ptimer = nvkm_timer(obj);
-       u64 time0;
-
-       time0 = ptimer->read(ptimer);
-       do {
-               if (nv_iclass(obj, NV_SUBDEV_CLASS)) {
-                       if ((nv_rd32(obj, addr) & mask) != data)
-                               return true;
-               } else {
-                       if ((nv_ro32(obj, addr) & mask) != data)
-                               return true;
+       struct nvkm_alarm *list;
+       unsigned long flags;
+
+       alarm->timestamp = nvkm_timer_read(tmr) + nsec;
+
+       /* append new alarm to list, in soonest-alarm-first order */
+       spin_lock_irqsave(&tmr->lock, flags);
+       if (!nsec) {
+               if (!list_empty(&alarm->head))
+                       list_del(&alarm->head);
+       } else {
+               list_for_each_entry(list, &tmr->alarms, head) {
+                       if (list->timestamp > alarm->timestamp)
+                               break;
                }
-       } while (ptimer->read(ptimer) - time0 < nsec);
+               list_add_tail(&alarm->head, &list->head);
+       }
+       spin_unlock_irqrestore(&tmr->lock, flags);
 
-       return false;
+       /* process pending alarms */
+       nvkm_timer_alarm_trigger(tmr);
 }
 
-bool
-nvkm_timer_wait_cb(void *obj, u64 nsec, bool (*func)(void *), void *data)
+void
+nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm)
 {
-       struct nvkm_timer *ptimer = nvkm_timer(obj);
-       u64 time0;
+       unsigned long flags;
+       spin_lock_irqsave(&tmr->lock, flags);
+       list_del_init(&alarm->head);
+       spin_unlock_irqrestore(&tmr->lock, flags);
+}
 
-       time0 = ptimer->read(ptimer);
-       do {
-               if (func(data) == true)
-                       return true;
-       } while (ptimer->read(ptimer) - time0 < nsec);
+static void
+nvkm_timer_intr(struct nvkm_subdev *subdev)
+{
+       struct nvkm_timer *tmr = nvkm_timer(subdev);
+       tmr->func->intr(tmr);
+}
 
-       return false;
+static int
+nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
+{
+       struct nvkm_timer *tmr = nvkm_timer(subdev);
+       tmr->func->alarm_fini(tmr);
+       return 0;
 }
 
-void
-nvkm_timer_alarm(void *obj, u32 nsec, struct nvkm_alarm *alarm)
+static int
+nvkm_timer_init(struct nvkm_subdev *subdev)
 {
-       struct nvkm_timer *ptimer = nvkm_timer(obj);
-       ptimer->alarm(ptimer, nsec, alarm);
+       struct nvkm_timer *tmr = nvkm_timer(subdev);
+       if (tmr->func->init)
+               tmr->func->init(tmr);
+       tmr->func->time(tmr, ktime_to_ns(ktime_get()));
+       nvkm_timer_alarm_trigger(tmr);
+       return 0;
 }
 
-void
-nvkm_timer_alarm_cancel(void *obj, struct nvkm_alarm *alarm)
+static void *
+nvkm_timer_dtor(struct nvkm_subdev *subdev)
 {
-       struct nvkm_timer *ptimer = nvkm_timer(obj);
-       ptimer->alarm_cancel(ptimer, alarm);
+       return nvkm_timer(subdev);
+}
+
+static const struct nvkm_subdev_func
+nvkm_timer = {
+       .dtor = nvkm_timer_dtor,
+       .init = nvkm_timer_init,
+       .fini = nvkm_timer_fini,
+       .intr = nvkm_timer_intr,
+};
+
+int
+nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
+               int index, struct nvkm_timer **ptmr)
+{
+       struct nvkm_timer *tmr;
+
+       if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
+               return -ENOMEM;
+
+       nvkm_subdev_ctor(&nvkm_timer, device, index, 0, &tmr->subdev);
+       tmr->func = func;
+       INIT_LIST_HEAD(&tmr->alarms);
+       spin_lock_init(&tmr->lock);
+       return 0;
 }