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