generic_timer.c revision 262534
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: head/sys/arm/arm/generic_timer.c 262534 2014-02-26 22:06:10Z 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 76#define GT_CNTPSIRQ 29 77 78struct arm_tmr_softc { 79 struct resource *irq_res; 80 uint32_t clkfreq; 81 struct eventtimer et; 82}; 83 84static struct arm_tmr_softc *arm_tmr_sc = NULL; 85 86static timecounter_get_t arm_tmr_get_timecount; 87 88static struct timecounter arm_tmr_timecount = { 89 .tc_name = "ARM MPCore Timecounter", 90 .tc_get_timecount = arm_tmr_get_timecount, 91 .tc_poll_pps = NULL, 92 .tc_counter_mask = ~0u, 93 .tc_frequency = 0, 94 .tc_quality = 1000, 95}; 96 97static inline int 98get_freq(void) 99{ 100 uint32_t val; 101 102 __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); 103 104 return (val); 105} 106 107static inline int 108set_freq(uint32_t val) 109{ 110 111 __asm volatile("mcr p15, 0, %[val], c14, c0, 0" : : 112 [val] "r" (val)); 113 isb(); 114 115 return (val); 116} 117 118 119static inline long 120get_cntpct(void) 121{ 122 uint64_t val; 123 124 __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 125 126 return (val); 127} 128 129static inline int 130set_ctrl(uint32_t val) 131{ 132 133 __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 134 [val] "r" (val)); 135 isb(); 136 137 return (0); 138} 139 140static inline int 141set_tval(uint32_t val) 142{ 143 144 __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 145 [val] "r" (val)); 146 isb(); 147 148 return (0); 149} 150 151static inline int 152get_ctrl(void) 153{ 154 uint32_t val; 155 156 __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 157 158 return (val); 159} 160 161static inline int 162get_tval(void) 163{ 164 uint32_t val; 165 166 __asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); 167 168 return (val); 169} 170 171static inline void 172disable_user_access(void) 173{ 174 uint32_t cntkctl; 175 176 __asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); 177 cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN | 178 GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN); 179 __asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); 180 isb(); 181} 182 183static unsigned 184arm_tmr_get_timecount(struct timecounter *tc) 185{ 186 187 return (get_cntpct()); 188} 189 190static int 191arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 192{ 193 struct arm_tmr_softc *sc; 194 int counts, ctrl; 195 196 sc = (struct arm_tmr_softc *)et->et_priv; 197 198 if (first != 0) { 199 counts = ((uint32_t)et->et_frequency * first) >> 32; 200 ctrl = get_ctrl(); 201 ctrl &= ~GT_CTRL_INT_MASK; 202 ctrl |= GT_CTRL_ENABLE; 203 set_tval(counts); 204 set_ctrl(ctrl); 205 return (0); 206 } 207 208 return (EINVAL); 209 210} 211 212static int 213arm_tmr_stop(struct eventtimer *et) 214{ 215 int ctrl; 216 217 ctrl = get_ctrl(); 218 ctrl &= GT_CTRL_ENABLE; 219 set_ctrl(ctrl); 220 221 return (0); 222} 223 224static int 225arm_tmr_intr(void *arg) 226{ 227 struct arm_tmr_softc *sc; 228 int ctrl; 229 230 sc = (struct arm_tmr_softc *)arg; 231 ctrl = get_ctrl(); 232 if (ctrl & GT_CTRL_INT_STAT) { 233 ctrl |= GT_CTRL_INT_MASK; 234 set_ctrl(ctrl); 235 } 236 237 if (sc->et.et_active) 238 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 239 240 return (FILTER_HANDLED); 241} 242 243static int 244arm_tmr_probe(device_t dev) 245{ 246 247 if (!ofw_bus_status_okay(dev)) 248 return (ENXIO); 249 250 if (!ofw_bus_is_compatible(dev, "arm,armv7-timer")) 251 return (ENXIO); 252 253 device_set_desc(dev, "ARMv7 Generic Timer"); 254 return (BUS_PROBE_DEFAULT); 255} 256 257 258static int 259arm_tmr_attach(device_t dev) 260{ 261 struct arm_tmr_softc *sc; 262 phandle_t node; 263 pcell_t clock; 264 void *ihl; 265 int rid; 266 int error; 267 268 sc = device_get_softc(dev); 269 if (arm_tmr_sc) 270 return (ENXIO); 271 272 /* Get the base clock frequency */ 273 node = ofw_bus_get_node(dev); 274 error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock)); 275 if (error <= 0) { 276 device_printf(dev, "missing clock-frequency " 277 "attribute in FDT\n"); 278 return (ENXIO); 279 } 280 sc->clkfreq = fdt32_to_cpu(clock); 281 282 rid = 0; 283 sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 284 GT_CNTPSIRQ, GT_CNTPSIRQ, 285 1, RF_SHAREABLE | RF_ACTIVE); 286 287 arm_tmr_sc = sc; 288 289 /* Setup and enable the timer */ 290 if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK, arm_tmr_intr, 291 NULL, sc, &ihl) != 0) { 292 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 293 device_printf(dev, "Unable to setup the CLK irq handler.\n"); 294 return (ENXIO); 295 } 296 297 set_freq(sc->clkfreq); 298 disable_user_access(); 299 300 arm_tmr_timecount.tc_frequency = sc->clkfreq; 301 tc_init(&arm_tmr_timecount); 302 303 sc->et.et_name = "ARM MPCore Eventtimer"; 304 sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 305 sc->et.et_quality = 1000; 306 307 sc->et.et_frequency = sc->clkfreq; 308 sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency; 309 sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 310 sc->et.et_start = arm_tmr_start; 311 sc->et.et_stop = arm_tmr_stop; 312 sc->et.et_priv = sc; 313 et_register(&sc->et); 314 315 return (0); 316} 317 318static device_method_t arm_tmr_methods[] = { 319 DEVMETHOD(device_probe, arm_tmr_probe), 320 DEVMETHOD(device_attach, arm_tmr_attach), 321 { 0, 0 } 322}; 323 324static driver_t arm_tmr_driver = { 325 "generic_timer", 326 arm_tmr_methods, 327 sizeof(struct arm_tmr_softc), 328}; 329 330static devclass_t arm_tmr_devclass; 331 332DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0); 333 334void 335DELAY(int usec) 336{ 337 int32_t counts, counts_per_usec; 338 uint32_t first, last; 339 340 /* 341 * Check the timers are setup, if not just 342 * use a for loop for the meantime 343 */ 344 if (arm_tmr_sc == NULL) { 345 for (; usec > 0; usec--) 346 for (counts = 200; counts > 0; counts--) 347 /* 348 * Prevent gcc from optimizing 349 * out the loop 350 */ 351 cpufunc_nullop(); 352 return; 353 } 354 355 /* Get the number of times to count */ 356 counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); 357 358 /* 359 * Clamp the timeout at a maximum value (about 32 seconds with 360 * a 66MHz clock). *Nobody* should be delay()ing for anywhere 361 * near that length of time and if they are, they should be hung 362 * out to dry. 363 */ 364 if (usec >= (0x80000000U / counts_per_usec)) 365 counts = (0x80000000U / counts_per_usec) - 1; 366 else 367 counts = usec * counts_per_usec; 368 369 first = get_cntpct(); 370 371 while (counts > 0) { 372 last = get_cntpct(); 373 counts -= (int32_t)(last - first); 374 first = last; 375 } 376} 377