1 /* $OpenBSD: agtimer.c,v 1.10 2020/07/12 20:36:37 naddy Exp $ */ 2 /* 3 * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/queue.h> 22 #include <sys/malloc.h> 23 #include <sys/device.h> 24 #include <sys/kernel.h> 25 #include <sys/timetc.h> 26 #include <sys/evcount.h> 27 28 #include <machine/intr.h> 29 #include <machine/bus.h> 30 #include <machine/fdt.h> 31 32 #include <arm/cpufunc.h> 33 #include <arm/cortex/cortex.h> 34 35 #include <dev/ofw/fdt.h> 36 #include <dev/ofw/openfirm.h> 37 38 /* registers */ 39 #define GTIMER_CNTP_CTL_ENABLE (1 << 0) 40 #define GTIMER_CNTP_CTL_IMASK (1 << 1) 41 #define GTIMER_CNTP_CTL_ISTATUS (1 << 2) 42 43 #define TIMER_FREQUENCY 24 * 1000 * 1000 /* ARM core clock */ 44 int32_t agtimer_frequency = TIMER_FREQUENCY; 45 46 u_int agtimer_get_timecount(struct timecounter *); 47 48 static struct timecounter agtimer_timecounter = { 49 agtimer_get_timecount, NULL, 0xffffffff, 0, "agtimer", 0, NULL 50 }; 51 52 struct agtimer_pcpu_softc { 53 uint64_t pc_nexttickevent; 54 uint64_t pc_nextstatevent; 55 u_int32_t pc_ticks_err_sum; 56 }; 57 58 struct agtimer_softc { 59 struct device sc_dev; 60 int sc_node; 61 62 struct agtimer_pcpu_softc sc_pstat[MAXCPUS]; 63 64 u_int32_t sc_ticks_err_cnt; 65 u_int32_t sc_ticks_per_second; 66 u_int32_t sc_ticks_per_intr; 67 u_int32_t sc_statvar; 68 u_int32_t sc_statmin; 69 70 #ifdef AMPTIMER_DEBUG 71 struct evcount sc_clk_count; 72 struct evcount sc_stat_count; 73 #endif 74 }; 75 76 int agtimer_match(struct device *, void *, void *); 77 void agtimer_attach(struct device *, struct device *, void *); 78 uint64_t agtimer_readcnt64(void); 79 int agtimer_intr(void *); 80 void agtimer_cpu_initclocks(void); 81 void agtimer_delay(u_int); 82 void agtimer_setstatclockrate(int stathz); 83 void agtimer_set_clockrate(int32_t new_frequency); 84 void agtimer_startclock(void); 85 86 struct cfattach agtimer_ca = { 87 sizeof (struct agtimer_softc), agtimer_match, agtimer_attach 88 }; 89 90 struct cfdriver agtimer_cd = { 91 NULL, "agtimer", DV_DULL 92 }; 93 94 uint64_t 95 agtimer_readcnt64(void) 96 { 97 uint64_t val; 98 99 __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 100 101 return (val); 102 } 103 104 static inline int 105 agtimer_get_ctrl(void) 106 { 107 uint32_t val; 108 109 __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 110 111 return (val); 112 } 113 114 static inline int 115 agtimer_set_ctrl(uint32_t val) 116 { 117 __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 118 [val] "r" (val)); 119 120 cpu_drain_writebuf(); 121 //isb(); 122 123 return (0); 124 } 125 126 static inline int 127 agtimer_set_tval(uint32_t val) 128 { 129 __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 130 [val] "r" (val)); 131 cpu_drain_writebuf(); 132 //isb(); 133 134 return (0); 135 } 136 137 int 138 agtimer_match(struct device *parent, void *cfdata, void *aux) 139 { 140 struct fdt_attach_args *faa = (struct fdt_attach_args *)aux; 141 142 return OF_is_compatible(faa->fa_node, "arm,armv7-timer"); 143 } 144 145 void 146 agtimer_attach(struct device *parent, struct device *self, void *aux) 147 { 148 struct agtimer_softc *sc = (struct agtimer_softc *)self; 149 struct fdt_attach_args *faa = aux; 150 151 sc->sc_node = faa->fa_node; 152 153 agtimer_frequency = 154 OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency); 155 sc->sc_ticks_per_second = agtimer_frequency; 156 157 printf(": tick rate %d KHz\n", sc->sc_ticks_per_second /1000); 158 159 /* XXX: disable user access */ 160 161 #ifdef AMPTIMER_DEBUG 162 evcount_attach(&sc->sc_clk_count, "clock", NULL); 163 evcount_attach(&sc->sc_stat_count, "stat", NULL); 164 #endif 165 166 /* 167 * private timer and interrupts not enabled until 168 * timer configures 169 */ 170 171 arm_clock_register(agtimer_cpu_initclocks, agtimer_delay, 172 agtimer_setstatclockrate, agtimer_startclock); 173 174 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 175 agtimer_timecounter.tc_priv = sc; 176 177 tc_init(&agtimer_timecounter); 178 } 179 180 u_int 181 agtimer_get_timecount(struct timecounter *tc) 182 { 183 return agtimer_readcnt64(); 184 } 185 186 int 187 agtimer_intr(void *frame) 188 { 189 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 190 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 191 uint64_t now; 192 uint64_t nextevent; 193 uint32_t r; 194 #if defined(USE_GTIMER_CMP) 195 int skip = 1; 196 #else 197 int64_t delay; 198 #endif 199 int rc = 0; 200 201 /* 202 * DSR - I know that the tick timer is 64 bits, but the following 203 * code deals with rollover, so there is no point in dealing 204 * with the 64 bit math, just let the 32 bit rollover 205 * do the right thing 206 */ 207 208 now = agtimer_readcnt64(); 209 210 while (pc->pc_nexttickevent <= now) { 211 pc->pc_nexttickevent += sc->sc_ticks_per_intr; 212 pc->pc_ticks_err_sum += sc->sc_ticks_err_cnt; 213 214 /* looping a few times is faster than divide */ 215 while (pc->pc_ticks_err_sum > hz) { 216 pc->pc_nexttickevent += 1; 217 pc->pc_ticks_err_sum -= hz; 218 } 219 220 #ifdef AMPTIMER_DEBUG 221 sc->sc_clk_count.ec_count++; 222 #endif 223 rc = 1; 224 hardclock(frame); 225 } 226 while (pc->pc_nextstatevent <= now) { 227 do { 228 r = random() & (sc->sc_statvar -1); 229 } while (r == 0); /* random == 0 not allowed */ 230 pc->pc_nextstatevent += sc->sc_statmin + r; 231 232 /* XXX - correct nextstatevent? */ 233 #ifdef AMPTIMER_DEBUG 234 sc->sc_stat_count.ec_count++; 235 #endif 236 rc = 1; 237 statclock(frame); 238 } 239 240 if (pc->pc_nexttickevent < pc->pc_nextstatevent) 241 nextevent = pc->pc_nexttickevent; 242 else 243 nextevent = pc->pc_nextstatevent; 244 245 delay = nextevent - now; 246 if (delay < 0) 247 delay = 1; 248 249 agtimer_set_tval(delay); 250 251 return (rc); 252 } 253 254 void 255 agtimer_set_clockrate(int32_t new_frequency) 256 { 257 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 258 259 agtimer_frequency = new_frequency; 260 261 if (sc == NULL) 262 return; 263 264 sc->sc_ticks_per_second = agtimer_frequency; 265 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 266 printf("agtimer0: adjusting clock: new tick rate %d KHz\n", 267 sc->sc_ticks_per_second /1000); 268 } 269 270 void 271 agtimer_cpu_initclocks() 272 { 273 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 274 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 275 uint32_t reg; 276 uint64_t next; 277 278 stathz = hz; 279 profhz = hz * 10; 280 281 if (sc->sc_ticks_per_second != agtimer_frequency) { 282 agtimer_set_clockrate(agtimer_frequency); 283 } 284 285 agtimer_setstatclockrate(stathz); 286 287 sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz; 288 sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz; 289 pc->pc_ticks_err_sum = 0; 290 291 /* Setup secure and non-secure timer IRQs. */ 292 arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK, 293 agtimer_intr, NULL, "tick"); 294 arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK, 295 agtimer_intr, NULL, "tick"); 296 297 next = agtimer_readcnt64() + sc->sc_ticks_per_intr; 298 pc->pc_nexttickevent = pc->pc_nextstatevent = next; 299 300 reg = agtimer_get_ctrl(); 301 reg &= ~GTIMER_CNTP_CTL_IMASK; 302 reg |= GTIMER_CNTP_CTL_ENABLE; 303 agtimer_set_tval(sc->sc_ticks_per_second); 304 agtimer_set_ctrl(reg); 305 } 306 307 void 308 agtimer_delay(u_int usecs) 309 { 310 u_int32_t clock, oclock, delta, delaycnt; 311 volatile int j; 312 int csec, usec; 313 314 if (usecs > (0x80000000 / agtimer_frequency)) { 315 csec = usecs / 10000; 316 usec = usecs % 10000; 317 318 delaycnt = (agtimer_frequency / 100) * csec + 319 (agtimer_frequency / 100) * usec / 10000; 320 } else { 321 delaycnt = agtimer_frequency * usecs / 1000000; 322 } 323 if (delaycnt <= 1) 324 for (j = 100; j > 0; j--) 325 ; 326 327 oclock = agtimer_readcnt64(); 328 while (1) { 329 for (j = 100; j > 0; j--) 330 ; 331 clock = agtimer_readcnt64(); 332 delta = clock - oclock; 333 if (delta > delaycnt) 334 break; 335 } 336 } 337 338 void 339 agtimer_setstatclockrate(int newhz) 340 { 341 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 342 int minint, statint; 343 int s; 344 345 s = splclock(); 346 347 statint = sc->sc_ticks_per_second / newhz; 348 /* calculate largest 2^n which is smaller that just over half statint */ 349 sc->sc_statvar = 0x40000000; /* really big power of two */ 350 minint = statint / 2 + 100; 351 while (sc->sc_statvar > minint) 352 sc->sc_statvar >>= 1; 353 354 sc->sc_statmin = statint - (sc->sc_statvar >> 1); 355 356 splx(s); 357 358 /* 359 * XXX this allows the next stat timer to occur then it switches 360 * to the new frequency. Rather than switching instantly. 361 */ 362 } 363 364 void 365 agtimer_startclock(void) 366 { 367 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 368 struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())]; 369 uint64_t nextevent; 370 uint32_t reg; 371 372 nextevent = agtimer_readcnt64() + sc->sc_ticks_per_intr; 373 pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent; 374 375 reg = agtimer_get_ctrl(); 376 reg &= ~GTIMER_CNTP_CTL_IMASK; 377 reg |= GTIMER_CNTP_CTL_ENABLE; 378 agtimer_set_tval(sc->sc_ticks_per_second); 379 agtimer_set_ctrl(reg); 380 } 381 382 void 383 agtimer_init(void) 384 { 385 uint32_t id_pfr1, cntfrq = 0; 386 387 /* Check for Generic Timer support. */ 388 __asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1)); 389 if ((id_pfr1 & 0x000f0000) == 0x00010000) 390 __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq)); 391 392 if (cntfrq != 0) { 393 agtimer_frequency = cntfrq; 394 arm_clock_register(NULL, agtimer_delay, NULL, NULL); 395 } 396 } 397