1 /* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24 #include "nv04.h" 25 26 static u64 27 nv04_timer_read(struct nvkm_timer *tmr) 28 { 29 u32 hi, lo; 30 31 do { 32 hi = nv_rd32(tmr, NV04_PTIMER_TIME_1); 33 lo = nv_rd32(tmr, NV04_PTIMER_TIME_0); 34 } while (hi != nv_rd32(tmr, NV04_PTIMER_TIME_1)); 35 36 return ((u64)hi << 32 | lo); 37 } 38 39 static void 40 nv04_timer_alarm_trigger(struct nvkm_timer *obj) 41 { 42 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 43 struct nvkm_alarm *alarm, *atemp; 44 unsigned long flags; 45 LIST_HEAD(exec); 46 47 /* move any due alarms off the pending list */ 48 spin_lock_irqsave(&tmr->lock, flags); 49 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { 50 if (alarm->timestamp <= tmr->base.read(&tmr->base)) 51 list_move_tail(&alarm->head, &exec); 52 } 53 54 /* reschedule interrupt for next alarm time */ 55 if (!list_empty(&tmr->alarms)) { 56 alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head); 57 nv_wr32(tmr, NV04_PTIMER_ALARM_0, alarm->timestamp); 58 nv_wr32(tmr, NV04_PTIMER_INTR_EN_0, 0x00000001); 59 } else { 60 nv_wr32(tmr, NV04_PTIMER_INTR_EN_0, 0x00000000); 61 } 62 spin_unlock_irqrestore(&tmr->lock, flags); 63 64 /* execute any pending alarm handlers */ 65 list_for_each_entry_safe(alarm, atemp, &exec, head) { 66 list_del_init(&alarm->head); 67 alarm->func(alarm); 68 } 69 } 70 71 static void 72 nv04_timer_alarm(struct nvkm_timer *obj, u64 time, struct nvkm_alarm *alarm) 73 { 74 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 75 struct nvkm_alarm *list; 76 unsigned long flags; 77 78 alarm->timestamp = tmr->base.read(&tmr->base) + time; 79 80 /* append new alarm to list, in soonest-alarm-first order */ 81 spin_lock_irqsave(&tmr->lock, flags); 82 if (!time) { 83 if (!list_empty(&alarm->head)) 84 list_del(&alarm->head); 85 } else { 86 list_for_each_entry(list, &tmr->alarms, head) { 87 if (list->timestamp > alarm->timestamp) 88 break; 89 } 90 list_add_tail(&alarm->head, &list->head); 91 } 92 spin_unlock_irqrestore(&tmr->lock, flags); 93 94 /* process pending alarms */ 95 nv04_timer_alarm_trigger(&tmr->base); 96 } 97 98 static void 99 nv04_timer_alarm_cancel(struct nvkm_timer *obj, struct nvkm_alarm *alarm) 100 { 101 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 102 unsigned long flags; 103 spin_lock_irqsave(&tmr->lock, flags); 104 list_del_init(&alarm->head); 105 spin_unlock_irqrestore(&tmr->lock, flags); 106 } 107 108 static void 109 nv04_timer_intr(struct nvkm_subdev *subdev) 110 { 111 struct nv04_timer *tmr = (void *)subdev; 112 u32 stat = nv_rd32(tmr, NV04_PTIMER_INTR_0); 113 114 if (stat & 0x00000001) { 115 nv04_timer_alarm_trigger(&tmr->base); 116 nv_wr32(tmr, NV04_PTIMER_INTR_0, 0x00000001); 117 stat &= ~0x00000001; 118 } 119 120 if (stat) { 121 nv_error(tmr, "unknown stat 0x%08x\n", stat); 122 nv_wr32(tmr, NV04_PTIMER_INTR_0, stat); 123 } 124 } 125 126 int 127 nv04_timer_fini(struct nvkm_object *object, bool suspend) 128 { 129 struct nv04_timer *tmr = (void *)object; 130 if (suspend) 131 tmr->suspend_time = nv04_timer_read(&tmr->base); 132 nv_wr32(tmr, NV04_PTIMER_INTR_EN_0, 0x00000000); 133 return nvkm_timer_fini(&tmr->base, suspend); 134 } 135 136 static int 137 nv04_timer_init(struct nvkm_object *object) 138 { 139 struct nvkm_device *device = nv_device(object); 140 struct nv04_timer *tmr = (void *)object; 141 u32 m = 1, f, n, d, lo, hi; 142 int ret; 143 144 ret = nvkm_timer_init(&tmr->base); 145 if (ret) 146 return ret; 147 148 /* aim for 31.25MHz, which gives us nanosecond timestamps */ 149 d = 1000000 / 32; 150 151 /* determine base clock for timer source */ 152 #if 0 /*XXX*/ 153 if (device->chipset < 0x40) { 154 n = nvkm_hw_get_clock(device, PLL_CORE); 155 } else 156 #endif 157 if (device->chipset <= 0x40) { 158 /*XXX: figure this out */ 159 f = -1; 160 n = 0; 161 } else { 162 f = device->crystal; 163 n = f; 164 while (n < (d * 2)) { 165 n += (n / m); 166 m++; 167 } 168 169 nv_wr32(tmr, 0x009220, m - 1); 170 } 171 172 if (!n) { 173 nv_warn(tmr, "unknown input clock freq\n"); 174 if (!nv_rd32(tmr, NV04_PTIMER_NUMERATOR) || 175 !nv_rd32(tmr, NV04_PTIMER_DENOMINATOR)) { 176 nv_wr32(tmr, NV04_PTIMER_NUMERATOR, 1); 177 nv_wr32(tmr, NV04_PTIMER_DENOMINATOR, 1); 178 } 179 return 0; 180 } 181 182 /* reduce ratio to acceptable values */ 183 while (((n % 5) == 0) && ((d % 5) == 0)) { 184 n /= 5; 185 d /= 5; 186 } 187 188 while (((n % 2) == 0) && ((d % 2) == 0)) { 189 n /= 2; 190 d /= 2; 191 } 192 193 while (n > 0xffff || d > 0xffff) { 194 n >>= 1; 195 d >>= 1; 196 } 197 198 /* restore the time before suspend */ 199 lo = tmr->suspend_time; 200 hi = (tmr->suspend_time >> 32); 201 202 nv_debug(tmr, "input frequency : %dHz\n", f); 203 nv_debug(tmr, "input multiplier: %d\n", m); 204 nv_debug(tmr, "numerator : 0x%08x\n", n); 205 nv_debug(tmr, "denominator : 0x%08x\n", d); 206 nv_debug(tmr, "timer frequency : %dHz\n", (f * m) * d / n); 207 nv_debug(tmr, "time low : 0x%08x\n", lo); 208 nv_debug(tmr, "time high : 0x%08x\n", hi); 209 210 nv_wr32(tmr, NV04_PTIMER_NUMERATOR, n); 211 nv_wr32(tmr, NV04_PTIMER_DENOMINATOR, d); 212 nv_wr32(tmr, NV04_PTIMER_INTR_0, 0xffffffff); 213 nv_wr32(tmr, NV04_PTIMER_INTR_EN_0, 0x00000000); 214 nv_wr32(tmr, NV04_PTIMER_TIME_1, hi); 215 nv_wr32(tmr, NV04_PTIMER_TIME_0, lo); 216 return 0; 217 } 218 219 void 220 nv04_timer_dtor(struct nvkm_object *object) 221 { 222 struct nv04_timer *tmr = (void *)object; 223 return nvkm_timer_destroy(&tmr->base); 224 } 225 226 int 227 nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 228 struct nvkm_oclass *oclass, void *data, u32 size, 229 struct nvkm_object **pobject) 230 { 231 struct nv04_timer *tmr; 232 int ret; 233 234 ret = nvkm_timer_create(parent, engine, oclass, &tmr); 235 *pobject = nv_object(tmr); 236 if (ret) 237 return ret; 238 239 tmr->base.subdev.intr = nv04_timer_intr; 240 tmr->base.read = nv04_timer_read; 241 tmr->base.alarm = nv04_timer_alarm; 242 tmr->base.alarm_cancel = nv04_timer_alarm_cancel; 243 tmr->suspend_time = 0; 244 245 INIT_LIST_HEAD(&tmr->alarms); 246 spin_lock_init(&tmr->lock); 247 return 0; 248 } 249 250 struct nvkm_oclass 251 nv04_timer_oclass = { 252 .handle = NV_SUBDEV(TIMER, 0x04), 253 .ofuncs = &(struct nvkm_ofuncs) { 254 .ctor = nv04_timer_ctor, 255 .dtor = nv04_timer_dtor, 256 .init = nv04_timer_init, 257 .fini = nv04_timer_fini, 258 } 259 }; 260