Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2012 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs
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     /* Process pending alarms. */
0076     spin_lock_irqsave(&tmr->lock, flags);
0077     list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
0078         /* Have we hit the earliest alarm that hasn't gone off? */
0079         if (alarm->timestamp > nvkm_timer_read(tmr)) {
0080             /* Schedule it.  If we didn't race, we're done. */
0081             tmr->func->alarm_init(tmr, alarm->timestamp);
0082             if (alarm->timestamp > nvkm_timer_read(tmr))
0083                 break;
0084         }
0085 
0086         /* Move to completed list.  We'll drop the lock before
0087          * executing the callback so it can reschedule itself.
0088          */
0089         list_del_init(&alarm->head);
0090         list_add(&alarm->exec, &exec);
0091     }
0092 
0093     /* Shut down interrupt if no more pending alarms. */
0094     if (list_empty(&tmr->alarms))
0095         tmr->func->alarm_fini(tmr);
0096     spin_unlock_irqrestore(&tmr->lock, flags);
0097 
0098     /* Execute completed callbacks. */
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     /* Remove alarm from pending list.
0112      *
0113      * This both protects against the corruption of the list,
0114      * and implements alarm rescheduling/cancellation.
0115      */
0116     spin_lock_irqsave(&tmr->lock, flags);
0117     list_del_init(&alarm->head);
0118 
0119     if (nsec) {
0120         /* Insert into pending list, ordered earliest to latest. */
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         /* Update HW if this is now the earliest alarm. */
0130         list = list_first_entry(&tmr->alarms, typeof(*list), head);
0131         if (list == alarm) {
0132             tmr->func->alarm_init(tmr, alarm->timestamp);
0133             /* This shouldn't happen if callers aren't stupid.
0134              *
0135              * Worst case scenario is that it'll take roughly
0136              * 4 seconds for the next alarm to trigger.
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 }