generic_timer.c revision 271189
1252372Sray/*- 2252372Sray * Copyright (c) 2011 The FreeBSD Foundation 3252372Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 4252372Sray * All rights reserved. 5252372Sray * 6252372Sray * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com> 7252372Sray * 8252372Sray * Redistribution and use in source and binary forms, with or without 9252372Sray * modification, are permitted provided that the following conditions 10252372Sray * are met: 11252372Sray * 1. Redistributions of source code must retain the above copyright 12252372Sray * notice, this list of conditions and the following disclaimer. 13252372Sray * 2. Redistributions in binary form must reproduce the above copyright 14252372Sray * notice, this list of conditions and the following disclaimer in the 15252372Sray * documentation and/or other materials provided with the distribution. 16252372Sray * 3. The name of the company nor the name of the author may be used to 17252372Sray * endorse or promote products derived from this software without specific 18252372Sray * prior written permission. 19252372Sray * 20252372Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21252372Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22252372Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23252372Sray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24252372Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25252372Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26252372Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27252372Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28252372Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29252372Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30252372Sray * SUCH DAMAGE. 31252372Sray */ 32252372Sray 33252372Sray/** 34252372Sray * Cortex-A15 (and probably A7) Generic Timer 35252372Sray */ 36252372Sray 37252372Sray#include <sys/cdefs.h> 38252372Sray__FBSDID("$FreeBSD: head/sys/arm/arm/generic_timer.c 271189 2014-09-06 13:21:07Z andrew $"); 39252372Sray 40252372Sray#include <sys/param.h> 41252372Sray#include <sys/systm.h> 42252372Sray#include <sys/bus.h> 43252372Sray#include <sys/kernel.h> 44252372Sray#include <sys/module.h> 45252372Sray#include <sys/malloc.h> 46252372Sray#include <sys/rman.h> 47252372Sray#include <sys/timeet.h> 48252372Sray#include <sys/timetc.h> 49252372Sray#include <sys/watchdog.h> 50252372Sray#include <machine/bus.h> 51252372Sray#include <machine/cpu.h> 52252372Sray#include <machine/intr.h> 53252372Sray 54252372Sray#include <dev/fdt/fdt_common.h> 55252372Sray#include <dev/ofw/openfirm.h> 56252372Sray#include <dev/ofw/ofw_bus.h> 57252372Sray#include <dev/ofw/ofw_bus_subr.h> 58252372Sray 59252372Sray#include <machine/bus.h> 60252372Sray#include <machine/fdt.h> 61252372Sray 62252780Sray#define GT_CTRL_ENABLE (1 << 0) 63252780Sray#define GT_CTRL_INT_MASK (1 << 1) 64252780Sray#define GT_CTRL_INT_STAT (1 << 2) 65252780Sray#define GT_REG_CTRL 0 66252780Sray#define GT_REG_TVAL 1 67252372Sray 68252780Sray#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */ 69252780Sray#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */ 70271189Sandrew#define GT_CNTKCTL_EVNTI (0xf << 4) /* Virtual counter event bits */ 71252780Sray#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */ 72252780Sray#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */ 73252780Sray#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */ 74252780Sray#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */ 75252372Sray 76252372Sraystruct arm_tmr_softc { 77264065Sbr struct resource *res[4]; 78264065Sbr void *ihl[4]; 79252372Sray uint32_t clkfreq; 80252372Sray struct eventtimer et; 81271189Sandrew bool physical; 82252372Sray}; 83252372Sray 84252372Sraystatic struct arm_tmr_softc *arm_tmr_sc = NULL; 85252372Sray 86264065Sbrstatic struct resource_spec timer_spec[] = { 87264065Sbr { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Secure */ 88264065Sbr { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Non-secure */ 89264065Sbr { SYS_RES_IRQ, 2, RF_ACTIVE }, /* Virt */ 90264065Sbr { SYS_RES_IRQ, 3, RF_ACTIVE }, /* Hyp */ 91264065Sbr { -1, 0 } 92264065Sbr}; 93264065Sbr 94252372Sraystatic timecounter_get_t arm_tmr_get_timecount; 95252372Sray 96252372Sraystatic struct timecounter arm_tmr_timecount = { 97252372Sray .tc_name = "ARM MPCore Timecounter", 98252372Sray .tc_get_timecount = arm_tmr_get_timecount, 99252372Sray .tc_poll_pps = NULL, 100252372Sray .tc_counter_mask = ~0u, 101252372Sray .tc_frequency = 0, 102252372Sray .tc_quality = 1000, 103252372Sray}; 104252372Sray 105271189Sandrewstatic int 106252372Srayget_freq(void) 107252372Sray{ 108252372Sray uint32_t val; 109252372Sray 110271189Sandrew /* cntfrq */ 111252372Sray __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val)); 112252372Sray 113252372Sray return (val); 114252372Sray} 115252372Sray 116271189Sandrewstatic long 117271189Sandrewget_cntxct(bool physical) 118252372Sray{ 119271189Sandrew uint64_t val; 120252372Sray 121252372Sray isb(); 122271189Sandrew if (physical) 123271189Sandrew /* cntpct */ 124271189Sandrew __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val)); 125271189Sandrew else 126271189Sandrew /* cntvct */ 127271189Sandrew __asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (val)); 128252372Sray 129252372Sray return (val); 130252372Sray} 131252372Sray 132271189Sandrewstatic int 133271189Sandrewset_ctrl(uint32_t val, bool physical) 134252372Sray{ 135252372Sray 136271189Sandrew if (physical) 137271189Sandrew /* cntp_ctl */ 138271189Sandrew __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : : 139271189Sandrew [val] "r" (val)); 140271189Sandrew else 141271189Sandrew /* cntv_ctl */ 142271189Sandrew __asm volatile("mcr p15, 0, %[val], c14, c3, 1" : : 143271189Sandrew [val] "r" (val)); 144252372Sray isb(); 145252372Sray 146252372Sray return (0); 147252372Sray} 148252372Sray 149271189Sandrewstatic int 150271189Sandrewset_tval(uint32_t val, bool physical) 151252372Sray{ 152252372Sray 153271189Sandrew if (physical) 154271189Sandrew /* cntp_tval */ 155271189Sandrew __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : : 156271189Sandrew [val] "r" (val)); 157271189Sandrew else 158271189Sandrew /* cntv_tval */ 159271189Sandrew __asm volatile("mcr p15, 0, %[val], c14, c3, 0" : : 160271189Sandrew [val] "r" (val)); 161252372Sray isb(); 162252372Sray 163252372Sray return (0); 164252372Sray} 165252372Sray 166271189Sandrewstatic int 167271189Sandrewget_ctrl(bool physical) 168252372Sray{ 169252372Sray uint32_t val; 170252372Sray 171271189Sandrew if (physical) 172271189Sandrew /* cntp_ctl */ 173271189Sandrew __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val)); 174271189Sandrew else 175271189Sandrew /* cntv_ctl */ 176271189Sandrew __asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val)); 177252372Sray 178252372Sray return (val); 179252372Sray} 180252372Sray 181271189Sandrewstatic void 182252372Sraydisable_user_access(void) 183252372Sray{ 184252372Sray uint32_t cntkctl; 185252372Sray 186252372Sray __asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); 187252780Sray cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN | 188252780Sray GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN); 189252372Sray __asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); 190252372Sray isb(); 191252372Sray} 192252372Sray 193252372Sraystatic unsigned 194252372Srayarm_tmr_get_timecount(struct timecounter *tc) 195252372Sray{ 196252372Sray 197271189Sandrew return (get_cntxct(arm_tmr_sc->physical)); 198252372Sray} 199252372Sray 200252372Sraystatic int 201252372Srayarm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 202252372Sray{ 203252372Sray struct arm_tmr_softc *sc; 204252372Sray int counts, ctrl; 205252372Sray 206252372Sray sc = (struct arm_tmr_softc *)et->et_priv; 207252372Sray 208252372Sray if (first != 0) { 209252372Sray counts = ((uint32_t)et->et_frequency * first) >> 32; 210271189Sandrew ctrl = get_ctrl(sc->physical); 211252780Sray ctrl &= ~GT_CTRL_INT_MASK; 212252780Sray ctrl |= GT_CTRL_ENABLE; 213271189Sandrew set_tval(counts, sc->physical); 214271189Sandrew set_ctrl(ctrl, sc->physical); 215252372Sray return (0); 216252372Sray } 217252372Sray 218252372Sray return (EINVAL); 219252372Sray 220252372Sray} 221252372Sray 222252372Sraystatic int 223252372Srayarm_tmr_stop(struct eventtimer *et) 224252372Sray{ 225271189Sandrew struct arm_tmr_softc *sc; 226252372Sray int ctrl; 227252372Sray 228271189Sandrew sc = (struct arm_tmr_softc *)et->et_priv; 229271189Sandrew 230271189Sandrew ctrl = get_ctrl(sc->physical); 231252780Sray ctrl &= GT_CTRL_ENABLE; 232271189Sandrew set_ctrl(ctrl, sc->physical); 233252372Sray 234252372Sray return (0); 235252372Sray} 236252372Sray 237252372Sraystatic int 238252372Srayarm_tmr_intr(void *arg) 239252372Sray{ 240252372Sray struct arm_tmr_softc *sc; 241252372Sray int ctrl; 242252372Sray 243252372Sray sc = (struct arm_tmr_softc *)arg; 244271189Sandrew ctrl = get_ctrl(sc->physical); 245252780Sray if (ctrl & GT_CTRL_INT_STAT) { 246252780Sray ctrl |= GT_CTRL_INT_MASK; 247271189Sandrew set_ctrl(ctrl, sc->physical); 248252372Sray } 249252372Sray 250252372Sray if (sc->et.et_active) 251252372Sray sc->et.et_event_cb(&sc->et, sc->et.et_arg); 252252372Sray 253252372Sray return (FILTER_HANDLED); 254252372Sray} 255252372Sray 256252372Sraystatic int 257252372Srayarm_tmr_probe(device_t dev) 258252372Sray{ 259252372Sray 260261410Sian if (!ofw_bus_status_okay(dev)) 261261410Sian return (ENXIO); 262261410Sian 263252372Sray if (!ofw_bus_is_compatible(dev, "arm,armv7-timer")) 264252372Sray return (ENXIO); 265252372Sray 266252372Sray device_set_desc(dev, "ARMv7 Generic Timer"); 267252372Sray return (BUS_PROBE_DEFAULT); 268252372Sray} 269252372Sray 270252372Sray 271252372Sraystatic int 272252372Srayarm_tmr_attach(device_t dev) 273252372Sray{ 274252372Sray struct arm_tmr_softc *sc; 275252372Sray phandle_t node; 276252372Sray pcell_t clock; 277252372Sray int error; 278264065Sbr int i; 279252372Sray 280252372Sray sc = device_get_softc(dev); 281252372Sray if (arm_tmr_sc) 282252372Sray return (ENXIO); 283252372Sray 284252372Sray /* Get the base clock frequency */ 285252372Sray node = ofw_bus_get_node(dev); 286252372Sray error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock)); 287264065Sbr if (error > 0) { 288264065Sbr sc->clkfreq = fdt32_to_cpu(clock); 289264065Sbr } 290264065Sbr 291264065Sbr if (sc->clkfreq == 0) { 292264065Sbr /* Try to get clock frequency from timer */ 293264065Sbr sc->clkfreq = get_freq(); 294264065Sbr } 295264065Sbr 296264065Sbr if (sc->clkfreq == 0) { 297264065Sbr device_printf(dev, "No clock frequency specified\n"); 298252372Sray return (ENXIO); 299252372Sray } 300252372Sray 301264065Sbr if (bus_alloc_resources(dev, timer_spec, sc->res)) { 302264065Sbr device_printf(dev, "could not allocate resources\n"); 303264065Sbr return (ENXIO); 304271189Sandrew } 305252372Sray 306271189Sandrew sc->physical = true; 307271189Sandrew 308252372Sray arm_tmr_sc = sc; 309252372Sray 310271189Sandrew /* Setup secure, non-secure and virtual IRQs handler */ 311271189Sandrew for (i = 0; i < 3; i++) { 312264065Sbr error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, 313264065Sbr arm_tmr_intr, NULL, sc, &sc->ihl[i]); 314264065Sbr if (error) { 315264065Sbr device_printf(dev, "Unable to alloc int resource.\n"); 316264065Sbr return (ENXIO); 317264065Sbr } 318252372Sray } 319252372Sray 320252372Sray disable_user_access(); 321252372Sray 322252427Sray arm_tmr_timecount.tc_frequency = sc->clkfreq; 323252372Sray tc_init(&arm_tmr_timecount); 324252372Sray 325252372Sray sc->et.et_name = "ARM MPCore Eventtimer"; 326252372Sray sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 327252372Sray sc->et.et_quality = 1000; 328252372Sray 329252372Sray sc->et.et_frequency = sc->clkfreq; 330252372Sray sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency; 331252372Sray sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 332252372Sray sc->et.et_start = arm_tmr_start; 333252372Sray sc->et.et_stop = arm_tmr_stop; 334252372Sray sc->et.et_priv = sc; 335252372Sray et_register(&sc->et); 336252372Sray 337252372Sray return (0); 338252372Sray} 339252372Sray 340252372Sraystatic device_method_t arm_tmr_methods[] = { 341252372Sray DEVMETHOD(device_probe, arm_tmr_probe), 342252372Sray DEVMETHOD(device_attach, arm_tmr_attach), 343252372Sray { 0, 0 } 344252372Sray}; 345252372Sray 346252372Sraystatic driver_t arm_tmr_driver = { 347252372Sray "generic_timer", 348252372Sray arm_tmr_methods, 349252372Sray sizeof(struct arm_tmr_softc), 350252372Sray}; 351252372Sray 352252372Sraystatic devclass_t arm_tmr_devclass; 353252372Sray 354269605SianEARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0, 355269605Sian BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 356252372Sray 357252372Srayvoid 358252372SrayDELAY(int usec) 359252372Sray{ 360252372Sray int32_t counts, counts_per_usec; 361252372Sray uint32_t first, last; 362252372Sray 363252372Sray /* 364252372Sray * Check the timers are setup, if not just 365252372Sray * use a for loop for the meantime 366252427Sray */ 367252372Sray if (arm_tmr_sc == NULL) { 368252372Sray for (; usec > 0; usec--) 369252372Sray for (counts = 200; counts > 0; counts--) 370252372Sray /* 371252372Sray * Prevent gcc from optimizing 372252372Sray * out the loop 373252372Sray */ 374252372Sray cpufunc_nullop(); 375252372Sray return; 376252372Sray } 377252372Sray 378252372Sray /* Get the number of times to count */ 379252427Sray counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); 380252372Sray 381252372Sray /* 382252372Sray * Clamp the timeout at a maximum value (about 32 seconds with 383252372Sray * a 66MHz clock). *Nobody* should be delay()ing for anywhere 384252372Sray * near that length of time and if they are, they should be hung 385252372Sray * out to dry. 386252372Sray */ 387252372Sray if (usec >= (0x80000000U / counts_per_usec)) 388252372Sray counts = (0x80000000U / counts_per_usec) - 1; 389252372Sray else 390252372Sray counts = usec * counts_per_usec; 391252372Sray 392271189Sandrew first = get_cntxct(arm_tmr_sc->physical); 393252372Sray 394252372Sray while (counts > 0) { 395271189Sandrew last = get_cntxct(arm_tmr_sc->physical); 396252372Sray counts -= (int32_t)(last - first); 397252372Sray first = last; 398252372Sray } 399252372Sray} 400