0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include "priv.h"
0025
0026 s64
0027 nvkm_timer_wait_test(struct nvkm_timer_wait *wait)
0028 {
0029 struct nvkm_subdev *subdev = &wait->tmr->subdev;
0030 u64 time = nvkm_timer_read(wait->tmr);
0031
0032 if (wait->reads == 0) {
0033 wait->time0 = time;
0034 wait->time1 = time;
0035 }
0036
0037 if (wait->time1 == time) {
0038 if (wait->reads++ == 16) {
0039 nvkm_fatal(subdev, "stalled at %016llx\n", time);
0040 return -ETIMEDOUT;
0041 }
0042 } else {
0043 wait->time1 = time;
0044 wait->reads = 1;
0045 }
0046
0047 if (wait->time1 - wait->time0 > wait->limit)
0048 return -ETIMEDOUT;
0049
0050 return wait->time1 - wait->time0;
0051 }
0052
0053 void
0054 nvkm_timer_wait_init(struct nvkm_device *device, u64 nsec,
0055 struct nvkm_timer_wait *wait)
0056 {
0057 wait->tmr = device->timer;
0058 wait->limit = nsec;
0059 wait->reads = 0;
0060 }
0061
0062 u64
0063 nvkm_timer_read(struct nvkm_timer *tmr)
0064 {
0065 return tmr->func->read(tmr);
0066 }
0067
0068 void
0069 nvkm_timer_alarm_trigger(struct nvkm_timer *tmr)
0070 {
0071 struct nvkm_alarm *alarm, *atemp;
0072 unsigned long flags;
0073 LIST_HEAD(exec);
0074
0075
0076 spin_lock_irqsave(&tmr->lock, flags);
0077 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
0078
0079 if (alarm->timestamp > nvkm_timer_read(tmr)) {
0080
0081 tmr->func->alarm_init(tmr, alarm->timestamp);
0082 if (alarm->timestamp > nvkm_timer_read(tmr))
0083 break;
0084 }
0085
0086
0087
0088
0089 list_del_init(&alarm->head);
0090 list_add(&alarm->exec, &exec);
0091 }
0092
0093
0094 if (list_empty(&tmr->alarms))
0095 tmr->func->alarm_fini(tmr);
0096 spin_unlock_irqrestore(&tmr->lock, flags);
0097
0098
0099 list_for_each_entry_safe(alarm, atemp, &exec, exec) {
0100 list_del(&alarm->exec);
0101 alarm->func(alarm);
0102 }
0103 }
0104
0105 void
0106 nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
0107 {
0108 struct nvkm_alarm *list;
0109 unsigned long flags;
0110
0111
0112
0113
0114
0115
0116 spin_lock_irqsave(&tmr->lock, flags);
0117 list_del_init(&alarm->head);
0118
0119 if (nsec) {
0120
0121 alarm->timestamp = nvkm_timer_read(tmr) + nsec;
0122 list_for_each_entry(list, &tmr->alarms, head) {
0123 if (list->timestamp > alarm->timestamp)
0124 break;
0125 }
0126
0127 list_add_tail(&alarm->head, &list->head);
0128
0129
0130 list = list_first_entry(&tmr->alarms, typeof(*list), head);
0131 if (list == alarm) {
0132 tmr->func->alarm_init(tmr, alarm->timestamp);
0133
0134
0135
0136
0137
0138 WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
0139 }
0140 }
0141 spin_unlock_irqrestore(&tmr->lock, flags);
0142 }
0143
0144 static void
0145 nvkm_timer_intr(struct nvkm_subdev *subdev)
0146 {
0147 struct nvkm_timer *tmr = nvkm_timer(subdev);
0148 tmr->func->intr(tmr);
0149 }
0150
0151 static int
0152 nvkm_timer_fini(struct nvkm_subdev *subdev, bool suspend)
0153 {
0154 struct nvkm_timer *tmr = nvkm_timer(subdev);
0155 tmr->func->alarm_fini(tmr);
0156 return 0;
0157 }
0158
0159 static int
0160 nvkm_timer_init(struct nvkm_subdev *subdev)
0161 {
0162 struct nvkm_timer *tmr = nvkm_timer(subdev);
0163 if (tmr->func->init)
0164 tmr->func->init(tmr);
0165 tmr->func->time(tmr, ktime_to_ns(ktime_get()));
0166 nvkm_timer_alarm_trigger(tmr);
0167 return 0;
0168 }
0169
0170 static void *
0171 nvkm_timer_dtor(struct nvkm_subdev *subdev)
0172 {
0173 return nvkm_timer(subdev);
0174 }
0175
0176 static const struct nvkm_subdev_func
0177 nvkm_timer = {
0178 .dtor = nvkm_timer_dtor,
0179 .init = nvkm_timer_init,
0180 .fini = nvkm_timer_fini,
0181 .intr = nvkm_timer_intr,
0182 };
0183
0184 int
0185 nvkm_timer_new_(const struct nvkm_timer_func *func, struct nvkm_device *device,
0186 enum nvkm_subdev_type type, int inst, struct nvkm_timer **ptmr)
0187 {
0188 struct nvkm_timer *tmr;
0189
0190 if (!(tmr = *ptmr = kzalloc(sizeof(*tmr), GFP_KERNEL)))
0191 return -ENOMEM;
0192
0193 nvkm_subdev_ctor(&nvkm_timer, device, type, inst, &tmr->subdev);
0194 tmr->func = func;
0195 INIT_LIST_HEAD(&tmr->alarms);
0196 spin_lock_init(&tmr->lock);
0197 return 0;
0198 }