generic_timer.c revision 270075
1/*- 2 * Copyright (c) 2011 The FreeBSD Foundation 3 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 4 * All rights reserved. 5 * 6 * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the company nor the name of the author may be used to 17 * endorse or promote products derived from this software without specific 18 * prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33/** 34 * Cortex-A15 (and probably A7) Generic Timer 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: stable/10/sys/arm/arm/generic_timer.c 270075 2014-08-17 01:28:03Z ian $"); 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/bus.h> 43#include <sys/kernel.h> 44#include <sys/module.h> 45#include <sys/malloc.h> 46#include <sys/rman.h> 47#include <sys/timeet.h> 48#include <sys/timetc.h> 49#include <sys/watchdog.h> 50#include <machine/bus.h> 51#include <machine/cpu.h> 52#include <machine/intr.h> 53 54#include <dev/fdt/fdt_common.h> 55#include <dev/ofw/openfirm.h> 56#include <dev/ofw/ofw_bus.h> 57#include <dev/ofw/ofw_bus_subr.h> 58 59#include <machine/bus.h> 60#include <machine/fdt.h> 61 62#define GT_CTRL_ENABLE (1 << 0) 63#define GT_CTRL_INT_MASK (1 << 1) 64#define GT_CTRL_INT_STAT (1 << 2) 65#define GT_REG_CTRL 0 66#define GT_REG_TVAL 1 67 68#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ 69#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */ 70#define GT_CNTKCTL_EVNTI (1 << 4) /* Virtual counter event bits */ 71#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */ 72#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */ 73#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */ 74#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */ 75 76struct arm_tmr_softc { 77 struct resource *res[4]; 78 void *ihl[4]; 79 uint32_t clkfreq; 80 struct eventtimer et; 81}; 82 83static struct arm_tmr_softc *arm_tmr_sc = NULL; 84 85static struct resource_spec timer_spec[] = { 86 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Secure */ 87 { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Non-secure */ 88 { SYS_RES_IRQ, 2, RF_ACTIVE }, /* Virt */ 89 { SYS_RES_IRQ, 3, RF_ACTIVE }, /* Hyp */ 90 { -1, 0 } 91}; 92 93static timecounter_get_t arm_tmr_get_timecount; 94 95static struct timecounter arm_tmr_timecount = { 96 .tc_name = "ARM MPCore Timecounter", 97 .tc_get_timecount = arm_tmr_get_timecount, 98 .tc_poll_pps = NULL, 99 .tc_counter_mask = ~0u, 100 .tc_frequency = 0, 101 .tc_quality = 1000, 102}; 103 104static inline int 105get_freq(void) 106{ 107 uint32_t val; 108 109 __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); 110 111 return (val); 112} 113 114static inline int 115set_freq(uint32_t val) 116{ 117 118 __asm volatile("mcr p15, 0, %[val], c14, c0, 0" : : 119 [val] "r" (val)); 120 isb(); 121 122 return (val); 123} 124 125 126static inline long 127get_cntpct(void) 128{ 129 uint64_t val; 130 131 __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 132 133 return (val); 134} 135 136static inline int 137set_ctrl(uint32_t val) 138{ 139 140 __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 141 [val] "r" (val)); 142 isb(); 143 144 return (0); 145} 146 147static inline int 148set_tval(uint32_t val) 149{ 150 151 __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 152 [val] "r" (val)); 153 isb(); 154 155 return (0); 156} 157 158static inline int 159get_ctrl(void) 160{ 161 uint32_t val; 162 163 __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 164 165 return (val); 166} 167 168static inline int 169get_tval(void) 170{ 171 uint32_t val; 172 173 __asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); 174 175 return (val); 176} 177 178static inline void 179disable_user_access(void) 180{ 181 uint32_t cntkctl; 182 183 __asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); 184 cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN | 185 GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN); 186 __asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); 187 isb(); 188} 189 190static unsigned 191arm_tmr_get_timecount(struct timecounter *tc) 192{ 193 194 return (get_cntpct()); 195} 196 197static int 198arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 199{ 200 struct arm_tmr_softc *sc; 201 int counts, ctrl; 202 203 sc = (struct arm_tmr_softc *)et->et_priv; 204 205 if (first != 0) { 206 counts = ((uint32_t)et->et_frequency * first) >> 32; 207 ctrl = get_ctrl(); 208 ctrl &= ~GT_CTRL_INT_MASK; 209 ctrl |= GT_CTRL_ENABLE; 210 set_tval(counts); 211 set_ctrl(ctrl); 212 return (0); 213 } 214 215 return (EINVAL); 216 217} 218 219static int 220arm_tmr_stop(struct eventtimer *et) 221{ 222 int ctrl; 223 224 ctrl = get_ctrl(); 225 ctrl &= GT_CTRL_ENABLE; 226 set_ctrl(ctrl); 227 228 return (0); 229} 230 231static int 232arm_tmr_intr(void *arg) 233{ 234 struct arm_tmr_softc *sc; 235 int ctrl; 236 237 sc = (struct arm_tmr_softc *)arg; 238 ctrl = get_ctrl(); 239 if (ctrl & GT_CTRL_INT_STAT) { 240 ctrl |= GT_CTRL_INT_MASK; 241 set_ctrl(ctrl); 242 } 243 244 if (sc->et.et_active) 245 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 246 247 return (FILTER_HANDLED); 248} 249 250static int 251arm_tmr_probe(device_t dev) 252{ 253 254 if (!ofw_bus_status_okay(dev)) 255 return (ENXIO); 256 257 if (!ofw_bus_is_compatible(dev, "arm,armv7-timer")) 258 return (ENXIO); 259 260 device_set_desc(dev, "ARMv7 Generic Timer"); 261 return (BUS_PROBE_DEFAULT); 262} 263 264 265static int 266arm_tmr_attach(device_t dev) 267{ 268 struct arm_tmr_softc *sc; 269 phandle_t node; 270 pcell_t clock; 271 int error; 272 int i; 273 274 sc = device_get_softc(dev); 275 if (arm_tmr_sc) 276 return (ENXIO); 277 278 /* Get the base clock frequency */ 279 node = ofw_bus_get_node(dev); 280 error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock)); 281 if (error > 0) { 282 sc->clkfreq = fdt32_to_cpu(clock); 283 } 284 285 if (sc->clkfreq == 0) { 286 /* Try to get clock frequency from timer */ 287 sc->clkfreq = get_freq(); 288 } 289 290 if (sc->clkfreq == 0) { 291 device_printf(dev, "No clock frequency specified\n"); 292 return (ENXIO); 293 } 294 295 if (bus_alloc_resources(dev, timer_spec, sc->res)) { 296 device_printf(dev, "could not allocate resources\n"); 297 return (ENXIO); 298 }; 299 300 arm_tmr_sc = sc; 301 302 /* Setup secure and non-secure IRQs handler */ 303 for (i = 0; i < 2; i++) { 304 error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, 305 arm_tmr_intr, NULL, sc, &sc->ihl[i]); 306 if (error) { 307 device_printf(dev, "Unable to alloc int resource.\n"); 308 return (ENXIO); 309 } 310 } 311 312 disable_user_access(); 313 314 arm_tmr_timecount.tc_frequency = sc->clkfreq; 315 tc_init(&arm_tmr_timecount); 316 317 sc->et.et_name = "ARM MPCore Eventtimer"; 318 sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 319 sc->et.et_quality = 1000; 320 321 sc->et.et_frequency = sc->clkfreq; 322 sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency; 323 sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 324 sc->et.et_start = arm_tmr_start; 325 sc->et.et_stop = arm_tmr_stop; 326 sc->et.et_priv = sc; 327 et_register(&sc->et); 328 329 return (0); 330} 331 332static device_method_t arm_tmr_methods[] = { 333 DEVMETHOD(device_probe, arm_tmr_probe), 334 DEVMETHOD(device_attach, arm_tmr_attach), 335 { 0, 0 } 336}; 337 338static driver_t arm_tmr_driver = { 339 "generic_timer", 340 arm_tmr_methods, 341 sizeof(struct arm_tmr_softc), 342}; 343 344static devclass_t arm_tmr_devclass; 345 346EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0, 347 BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 348 349void 350DELAY(int usec) 351{ 352 int32_t counts, counts_per_usec; 353 uint32_t first, last; 354 355 /* 356 * Check the timers are setup, if not just 357 * use a for loop for the meantime 358 */ 359 if (arm_tmr_sc == NULL) { 360 for (; usec > 0; usec--) 361 for (counts = 200; counts > 0; counts--) 362 /* 363 * Prevent gcc from optimizing 364 * out the loop 365 */ 366 cpufunc_nullop(); 367 return; 368 } 369 370 /* Get the number of times to count */ 371 counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); 372 373 /* 374 * Clamp the timeout at a maximum value (about 32 seconds with 375 * a 66MHz clock). *Nobody* should be delay()ing for anywhere 376 * near that length of time and if they are, they should be hung 377 * out to dry. 378 */ 379 if (usec >= (0x80000000U / counts_per_usec)) 380 counts = (0x80000000U / counts_per_usec) - 1; 381 else 382 counts = usec * counts_per_usec; 383 384 first = get_cntpct(); 385 386 while (counts > 0) { 387 last = get_cntpct(); 388 counts -= (int32_t)(last - first); 389 first = last; 390 } 391} 392