1 /* $OpenBSD: agtimer.c,v 1.16 2023/01/17 02:58:22 cheloha 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/clockintr.h> 22 #include <sys/device.h> 23 #include <sys/kernel.h> 24 #include <sys/stdint.h> 25 #include <sys/timetc.h> 26 27 #include <machine/intr.h> 28 #include <machine/bus.h> 29 #include <machine/fdt.h> 30 31 #include <arm/cpufunc.h> 32 33 #include <dev/ofw/fdt.h> 34 #include <dev/ofw/openfirm.h> 35 36 /* registers */ 37 #define GTIMER_CNTP_CTL_ENABLE (1 << 0) 38 #define GTIMER_CNTP_CTL_IMASK (1 << 1) 39 #define GTIMER_CNTP_CTL_ISTATUS (1 << 2) 40 41 #define TIMER_FREQUENCY 24 * 1000 * 1000 /* ARM core clock */ 42 int32_t agtimer_frequency = TIMER_FREQUENCY; 43 44 u_int agtimer_get_timecount(struct timecounter *); 45 46 static struct timecounter agtimer_timecounter = { 47 .tc_get_timecount = agtimer_get_timecount, 48 .tc_poll_pps = NULL, 49 .tc_counter_mask = 0xffffffff, 50 .tc_frequency = 0, 51 .tc_name = "agtimer", 52 .tc_quality = 0, 53 .tc_priv = NULL, 54 }; 55 56 struct agtimer_softc { 57 struct device sc_dev; 58 int sc_node; 59 u_int32_t sc_ticks_per_second; 60 uint64_t sc_nsec_cycle_ratio; 61 uint64_t sc_nsec_max; 62 }; 63 64 int agtimer_match(struct device *, void *, void *); 65 void agtimer_attach(struct device *, struct device *, void *); 66 uint64_t agtimer_readcnt64(void); 67 int agtimer_intr(void *); 68 void agtimer_cpu_initclocks(void); 69 void agtimer_delay(u_int); 70 void agtimer_setstatclockrate(int stathz); 71 void agtimer_set_clockrate(int32_t new_frequency); 72 void agtimer_startclock(void); 73 74 const struct cfattach agtimer_ca = { 75 sizeof (struct agtimer_softc), agtimer_match, agtimer_attach 76 }; 77 78 struct cfdriver agtimer_cd = { 79 NULL, "agtimer", DV_DULL 80 }; 81 82 void agtimer_rearm(void *, uint64_t); 83 void agtimer_trigger(void *); 84 85 struct intrclock agtimer_intrclock = { 86 .ic_rearm = agtimer_rearm, 87 .ic_trigger = agtimer_trigger 88 }; 89 90 uint64_t 91 agtimer_readcnt64(void) 92 { 93 uint64_t val; 94 95 __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 96 97 return (val); 98 } 99 100 static inline int 101 agtimer_get_ctrl(void) 102 { 103 uint32_t val; 104 105 __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 106 107 return (val); 108 } 109 110 static inline int 111 agtimer_set_ctrl(uint32_t val) 112 { 113 __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 114 [val] "r" (val)); 115 116 cpu_drain_writebuf(); 117 //isb(); 118 119 return (0); 120 } 121 122 static inline int 123 agtimer_set_tval(uint32_t val) 124 { 125 __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 126 [val] "r" (val)); 127 cpu_drain_writebuf(); 128 //isb(); 129 130 return (0); 131 } 132 133 int 134 agtimer_match(struct device *parent, void *cfdata, void *aux) 135 { 136 struct fdt_attach_args *faa = (struct fdt_attach_args *)aux; 137 138 return OF_is_compatible(faa->fa_node, "arm,armv7-timer"); 139 } 140 141 void 142 agtimer_attach(struct device *parent, struct device *self, void *aux) 143 { 144 struct agtimer_softc *sc = (struct agtimer_softc *)self; 145 struct fdt_attach_args *faa = aux; 146 147 sc->sc_node = faa->fa_node; 148 149 agtimer_frequency = 150 OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency); 151 sc->sc_ticks_per_second = agtimer_frequency; 152 sc->sc_nsec_cycle_ratio = 153 sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; 154 sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; 155 printf(": %d kHz\n", sc->sc_ticks_per_second / 1000); 156 157 /* XXX: disable user access */ 158 159 /* 160 * private timer and interrupts not enabled until 161 * timer configures 162 */ 163 164 arm_clock_register(agtimer_cpu_initclocks, agtimer_delay, 165 agtimer_setstatclockrate, agtimer_startclock); 166 167 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 168 agtimer_timecounter.tc_priv = sc; 169 tc_init(&agtimer_timecounter); 170 171 agtimer_intrclock.ic_cookie = sc; 172 } 173 174 u_int 175 agtimer_get_timecount(struct timecounter *tc) 176 { 177 return agtimer_readcnt64(); 178 } 179 180 void 181 agtimer_rearm(void *cookie, uint64_t nsecs) 182 { 183 struct agtimer_softc *sc = cookie; 184 uint32_t cycles; 185 186 if (nsecs > sc->sc_nsec_max) 187 nsecs = sc->sc_nsec_max; 188 cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32; 189 if (cycles > INT32_MAX) 190 cycles = INT32_MAX; 191 agtimer_set_tval(cycles); 192 } 193 194 void 195 agtimer_trigger(void *unused) 196 { 197 agtimer_set_tval(0); 198 } 199 200 int 201 agtimer_intr(void *frame) 202 { 203 return clockintr_dispatch(frame); 204 } 205 206 void 207 agtimer_set_clockrate(int32_t new_frequency) 208 { 209 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 210 211 agtimer_frequency = new_frequency; 212 213 if (sc == NULL) 214 return; 215 216 sc->sc_ticks_per_second = agtimer_frequency; 217 sc->sc_nsec_cycle_ratio = 218 sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; 219 sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; 220 221 agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second; 222 223 printf("agtimer0: adjusting clock: new rate %d kHz\n", 224 sc->sc_ticks_per_second / 1000); 225 } 226 227 void 228 agtimer_cpu_initclocks(void) 229 { 230 struct agtimer_softc *sc = agtimer_cd.cd_devs[0]; 231 uint32_t reg; 232 233 stathz = hz; 234 profhz = stathz * 10; 235 clockintr_init(CL_RNDSTAT); 236 237 if (sc->sc_ticks_per_second != agtimer_frequency) { 238 agtimer_set_clockrate(agtimer_frequency); 239 } 240 241 clockintr_cpu_init(&agtimer_intrclock); 242 243 /* Setup secure and non-secure timer IRQs. */ 244 arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK, 245 agtimer_intr, NULL, "tick"); 246 arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK, 247 agtimer_intr, NULL, "tick"); 248 249 reg = agtimer_get_ctrl(); 250 reg &= ~GTIMER_CNTP_CTL_IMASK; 251 reg |= GTIMER_CNTP_CTL_ENABLE; 252 agtimer_set_tval(INT32_MAX); 253 agtimer_set_ctrl(reg); 254 255 clockintr_trigger(); 256 } 257 258 void 259 agtimer_delay(u_int usecs) 260 { 261 u_int32_t clock, oclock, delta, delaycnt; 262 volatile int j; 263 int csec, usec; 264 265 if (usecs > (0x80000000 / agtimer_frequency)) { 266 csec = usecs / 10000; 267 usec = usecs % 10000; 268 269 delaycnt = (agtimer_frequency / 100) * csec + 270 (agtimer_frequency / 100) * usec / 10000; 271 } else { 272 delaycnt = agtimer_frequency * usecs / 1000000; 273 } 274 if (delaycnt <= 1) 275 for (j = 100; j > 0; j--) 276 ; 277 278 oclock = agtimer_readcnt64(); 279 while (1) { 280 for (j = 100; j > 0; j--) 281 ; 282 clock = agtimer_readcnt64(); 283 delta = clock - oclock; 284 if (delta > delaycnt) 285 break; 286 } 287 } 288 289 void 290 agtimer_setstatclockrate(int newhz) 291 { 292 clockintr_setstatclockrate(newhz); 293 } 294 295 void 296 agtimer_startclock(void) 297 { 298 uint32_t reg; 299 300 clockintr_cpu_init(&agtimer_intrclock); 301 302 reg = agtimer_get_ctrl(); 303 reg &= ~GTIMER_CNTP_CTL_IMASK; 304 reg |= GTIMER_CNTP_CTL_ENABLE; 305 agtimer_set_tval(INT32_MAX); 306 agtimer_set_ctrl(reg); 307 308 clockintr_trigger(); 309 } 310 311 void 312 agtimer_init(void) 313 { 314 uint32_t id_pfr1, cntfrq = 0; 315 316 /* Check for Generic Timer support. */ 317 __asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1)); 318 if ((id_pfr1 & 0x000f0000) == 0x00010000) 319 __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq)); 320 321 if (cntfrq != 0) { 322 agtimer_frequency = cntfrq; 323 arm_clock_register(NULL, agtimer_delay, NULL, NULL); 324 } 325 } 326