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 struct nvkm_device *device = tmr->subdev.device; 30 u32 hi, lo; 31 32 do { 33 hi = nvkm_rd32(device, NV04_PTIMER_TIME_1); 34 lo = nvkm_rd32(device, NV04_PTIMER_TIME_0); 35 } while (hi != nvkm_rd32(device, NV04_PTIMER_TIME_1)); 36 37 return ((u64)hi << 32 | lo); 38 } 39 40 static void 41 nv04_timer_alarm_trigger(struct nvkm_timer *obj) 42 { 43 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 44 struct nvkm_device *device = tmr->base.subdev.device; 45 struct nvkm_alarm *alarm, *atemp; 46 unsigned long flags; 47 LIST_HEAD(exec); 48 49 /* move any due alarms off the pending list */ 50 spin_lock_irqsave(&tmr->lock, flags); 51 list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { 52 if (alarm->timestamp <= tmr->base.read(&tmr->base)) 53 list_move_tail(&alarm->head, &exec); 54 } 55 56 /* reschedule interrupt for next alarm time */ 57 if (!list_empty(&tmr->alarms)) { 58 alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head); 59 nvkm_wr32(device, NV04_PTIMER_ALARM_0, alarm->timestamp); 60 nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000001); 61 } else { 62 nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000); 63 } 64 spin_unlock_irqrestore(&tmr->lock, flags); 65 66 /* execute any pending alarm handlers */ 67 list_for_each_entry_safe(alarm, atemp, &exec, head) { 68 list_del_init(&alarm->head); 69 alarm->func(alarm); 70 } 71 } 72 73 static void 74 nv04_timer_alarm(struct nvkm_timer *obj, u64 time, struct nvkm_alarm *alarm) 75 { 76 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 77 struct nvkm_alarm *list; 78 unsigned long flags; 79 80 alarm->timestamp = tmr->base.read(&tmr->base) + time; 81 82 /* append new alarm to list, in soonest-alarm-first order */ 83 spin_lock_irqsave(&tmr->lock, flags); 84 if (!time) { 85 if (!list_empty(&alarm->head)) 86 list_del(&alarm->head); 87 } else { 88 list_for_each_entry(list, &tmr->alarms, head) { 89 if (list->timestamp > alarm->timestamp) 90 break; 91 } 92 list_add_tail(&alarm->head, &list->head); 93 } 94 spin_unlock_irqrestore(&tmr->lock, flags); 95 96 /* process pending alarms */ 97 nv04_timer_alarm_trigger(&tmr->base); 98 } 99 100 static void 101 nv04_timer_alarm_cancel(struct nvkm_timer *obj, struct nvkm_alarm *alarm) 102 { 103 struct nv04_timer *tmr = container_of(obj, typeof(*tmr), base); 104 unsigned long flags; 105 spin_lock_irqsave(&tmr->lock, flags); 106 list_del_init(&alarm->head); 107 spin_unlock_irqrestore(&tmr->lock, flags); 108 } 109 110 static void 111 nv04_timer_intr(struct nvkm_subdev *subdev) 112 { 113 struct nv04_timer *tmr = (void *)subdev; 114 struct nvkm_device *device = tmr->base.subdev.device; 115 u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0); 116 117 if (stat & 0x00000001) { 118 nv04_timer_alarm_trigger(&tmr->base); 119 nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001); 120 stat &= ~0x00000001; 121 } 122 123 if (stat) { 124 nv_error(tmr, "unknown stat 0x%08x\n", stat); 125 nvkm_wr32(device, NV04_PTIMER_INTR_0, stat); 126 } 127 } 128 129 int 130 nv04_timer_fini(struct nvkm_object *object, bool suspend) 131 { 132 struct nv04_timer *tmr = (void *)object; 133 struct nvkm_device *device = tmr->base.subdev.device; 134 if (suspend) 135 tmr->suspend_time = nv04_timer_read(&tmr->base); 136 nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000); 137 return nvkm_timer_fini(&tmr->base, suspend); 138 } 139 140 static int 141 nv04_timer_init(struct nvkm_object *object) 142 { 143 struct nv04_timer *tmr = (void *)object; 144 struct nvkm_device *device = tmr->base.subdev.device; 145 u32 m = 1, f, n, d, lo, hi; 146 int ret; 147 148 ret = nvkm_timer_init(&tmr->base); 149 if (ret) 150 return ret; 151 152 /* aim for 31.25MHz, which gives us nanosecond timestamps */ 153 d = 1000000 / 32; 154 155 /* determine base clock for timer source */ 156 #if 0 /*XXX*/ 157 if (device->chipset < 0x40) { 158 n = nvkm_hw_get_clock(device, PLL_CORE); 159 } else 160 #endif 161 if (device->chipset <= 0x40) { 162 /*XXX: figure this out */ 163 f = -1; 164 n = 0; 165 } else { 166 f = device->crystal; 167 n = f; 168 while (n < (d * 2)) { 169 n += (n / m); 170 m++; 171 } 172 173 nvkm_wr32(device, 0x009220, m - 1); 174 } 175 176 if (!n) { 177 nv_warn(tmr, "unknown input clock freq\n"); 178 if (!nvkm_rd32(device, NV04_PTIMER_NUMERATOR) || 179 !nvkm_rd32(device, NV04_PTIMER_DENOMINATOR)) { 180 nvkm_wr32(device, NV04_PTIMER_NUMERATOR, 1); 181 nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, 1); 182 } 183 return 0; 184 } 185 186 /* reduce ratio to acceptable values */ 187 while (((n % 5) == 0) && ((d % 5) == 0)) { 188 n /= 5; 189 d /= 5; 190 } 191 192 while (((n % 2) == 0) && ((d % 2) == 0)) { 193 n /= 2; 194 d /= 2; 195 } 196 197 while (n > 0xffff || d > 0xffff) { 198 n >>= 1; 199 d >>= 1; 200 } 201 202 /* restore the time before suspend */ 203 lo = tmr->suspend_time; 204 hi = (tmr->suspend_time >> 32); 205 206 nv_debug(tmr, "input frequency : %dHz\n", f); 207 nv_debug(tmr, "input multiplier: %d\n", m); 208 nv_debug(tmr, "numerator : 0x%08x\n", n); 209 nv_debug(tmr, "denominator : 0x%08x\n", d); 210 nv_debug(tmr, "timer frequency : %dHz\n", (f * m) * d / n); 211 nv_debug(tmr, "time low : 0x%08x\n", lo); 212 nv_debug(tmr, "time high : 0x%08x\n", hi); 213 214 nvkm_wr32(device, NV04_PTIMER_NUMERATOR, n); 215 nvkm_wr32(device, NV04_PTIMER_DENOMINATOR, d); 216 nvkm_wr32(device, NV04_PTIMER_INTR_0, 0xffffffff); 217 nvkm_wr32(device, NV04_PTIMER_INTR_EN_0, 0x00000000); 218 nvkm_wr32(device, NV04_PTIMER_TIME_1, hi); 219 nvkm_wr32(device, NV04_PTIMER_TIME_0, lo); 220 return 0; 221 } 222 223 void 224 nv04_timer_dtor(struct nvkm_object *object) 225 { 226 struct nv04_timer *tmr = (void *)object; 227 return nvkm_timer_destroy(&tmr->base); 228 } 229 230 int 231 nv04_timer_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 232 struct nvkm_oclass *oclass, void *data, u32 size, 233 struct nvkm_object **pobject) 234 { 235 struct nv04_timer *tmr; 236 int ret; 237 238 ret = nvkm_timer_create(parent, engine, oclass, &tmr); 239 *pobject = nv_object(tmr); 240 if (ret) 241 return ret; 242 243 tmr->base.subdev.intr = nv04_timer_intr; 244 tmr->base.read = nv04_timer_read; 245 tmr->base.alarm = nv04_timer_alarm; 246 tmr->base.alarm_cancel = nv04_timer_alarm_cancel; 247 tmr->suspend_time = 0; 248 249 INIT_LIST_HEAD(&tmr->alarms); 250 spin_lock_init(&tmr->lock); 251 return 0; 252 } 253 254 struct nvkm_oclass 255 nv04_timer_oclass = { 256 .handle = NV_SUBDEV(TIMER, 0x04), 257 .ofuncs = &(struct nvkm_ofuncs) { 258 .ctor = nv04_timer_ctor, 259 .dtor = nv04_timer_dtor, 260 .init = nv04_timer_init, 261 .fini = nv04_timer_fini, 262 } 263 }; 264