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