1239268Sgonzo/*- 2239268Sgonzo * Copyright (c) 2011 The FreeBSD Foundation 3239268Sgonzo * All rights reserved. 4239268Sgonzo * 5239268Sgonzo * Developed by Ben Gray <ben.r.gray@gmail.com> 6239268Sgonzo * 7239268Sgonzo * Redistribution and use in source and binary forms, with or without 8239268Sgonzo * modification, are permitted provided that the following conditions 9239268Sgonzo * are met: 10239268Sgonzo * 1. Redistributions of source code must retain the above copyright 11239268Sgonzo * notice, this list of conditions and the following disclaimer. 12239268Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 13239268Sgonzo * notice, this list of conditions and the following disclaimer in the 14239268Sgonzo * documentation and/or other materials provided with the distribution. 15239268Sgonzo * 3. The name of the company nor the name of the author may be used to 16239268Sgonzo * endorse or promote products derived from this software without specific 17239268Sgonzo * prior written permission. 18239268Sgonzo * 19239268Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20239268Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21239268Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22239268Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23239268Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24239268Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25239268Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26239268Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27239268Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28239268Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29239268Sgonzo * SUCH DAMAGE. 30239268Sgonzo */ 31239268Sgonzo 32239268Sgonzo/** 33266347Sian * The ARM Cortex-A9 core can support a global timer plus a private and 34266347Sian * watchdog timer per core. This driver reserves memory and interrupt 35266347Sian * resources for accessing both timer register sets, these resources are 36266347Sian * stored globally and used to setup the timecount and eventtimer. 37239268Sgonzo * 38266347Sian * The timecount timer uses the global 64-bit counter, whereas the 39266347Sian * per-CPU eventtimer uses the private 32-bit counters. 40239268Sgonzo * 41239268Sgonzo * 42266347Sian * REF: ARM Cortex-A9 MPCore, Technical Reference Manual (rev. r2p2) 43239268Sgonzo */ 44239268Sgonzo 45239268Sgonzo#include <sys/cdefs.h> 46239268Sgonzo__FBSDID("$FreeBSD$"); 47239268Sgonzo 48239268Sgonzo#include <sys/param.h> 49239268Sgonzo#include <sys/systm.h> 50239268Sgonzo#include <sys/bus.h> 51239268Sgonzo#include <sys/kernel.h> 52239268Sgonzo#include <sys/module.h> 53239268Sgonzo#include <sys/malloc.h> 54239268Sgonzo#include <sys/rman.h> 55239268Sgonzo#include <sys/timeet.h> 56239268Sgonzo#include <sys/timetc.h> 57239268Sgonzo#include <sys/watchdog.h> 58239268Sgonzo#include <machine/bus.h> 59239268Sgonzo#include <machine/cpu.h> 60239268Sgonzo#include <machine/intr.h> 61239268Sgonzo 62239268Sgonzo#include <dev/fdt/fdt_common.h> 63239268Sgonzo#include <dev/ofw/openfirm.h> 64239268Sgonzo#include <dev/ofw/ofw_bus.h> 65239268Sgonzo#include <dev/ofw/ofw_bus_subr.h> 66239268Sgonzo 67239268Sgonzo#include <machine/bus.h> 68239268Sgonzo#include <machine/fdt.h> 69239268Sgonzo 70266347Sian#include <arm/arm/mpcore_timervar.h> 71266347Sian 72239268Sgonzo/* Private (per-CPU) timer register map */ 73239268Sgonzo#define PRV_TIMER_LOAD 0x0000 74239268Sgonzo#define PRV_TIMER_COUNT 0x0004 75239268Sgonzo#define PRV_TIMER_CTRL 0x0008 76239268Sgonzo#define PRV_TIMER_INTR 0x000C 77239268Sgonzo 78239268Sgonzo#define PRV_TIMER_CTR_PRESCALER_SHIFT 8 79239268Sgonzo#define PRV_TIMER_CTRL_IRQ_ENABLE (1UL << 2) 80239268Sgonzo#define PRV_TIMER_CTRL_AUTO_RELOAD (1UL << 1) 81239268Sgonzo#define PRV_TIMER_CTRL_TIMER_ENABLE (1UL << 0) 82239268Sgonzo 83239268Sgonzo#define PRV_TIMER_INTR_EVENT (1UL << 0) 84239268Sgonzo 85239268Sgonzo/* Global timer register map */ 86239268Sgonzo#define GBL_TIMER_COUNT_LOW 0x0000 87239268Sgonzo#define GBL_TIMER_COUNT_HIGH 0x0004 88239268Sgonzo#define GBL_TIMER_CTRL 0x0008 89239268Sgonzo#define GBL_TIMER_INTR 0x000C 90239268Sgonzo 91239268Sgonzo#define GBL_TIMER_CTR_PRESCALER_SHIFT 8 92239268Sgonzo#define GBL_TIMER_CTRL_AUTO_INC (1UL << 3) 93239268Sgonzo#define GBL_TIMER_CTRL_IRQ_ENABLE (1UL << 2) 94239268Sgonzo#define GBL_TIMER_CTRL_COMP_ENABLE (1UL << 1) 95239268Sgonzo#define GBL_TIMER_CTRL_TIMER_ENABLE (1UL << 0) 96239268Sgonzo 97239268Sgonzo#define GBL_TIMER_INTR_EVENT (1UL << 0) 98239268Sgonzo 99239268Sgonzostruct arm_tmr_softc { 100239268Sgonzo struct resource * tmr_res[4]; 101239268Sgonzo bus_space_tag_t prv_bst; 102239268Sgonzo bus_space_tag_t gbl_bst; 103239268Sgonzo bus_space_handle_t prv_bsh; 104239268Sgonzo bus_space_handle_t gbl_bsh; 105266347Sian uint64_t clkfreq; 106239268Sgonzo struct eventtimer et; 107239268Sgonzo}; 108239268Sgonzo 109239268Sgonzostatic struct resource_spec arm_tmr_spec[] = { 110239268Sgonzo { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Global registers */ 111239268Sgonzo { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Global timer interrupt (unused) */ 112239268Sgonzo { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Private (per-CPU) registers */ 113239268Sgonzo { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Private timer interrupt */ 114239268Sgonzo { -1, 0 } 115239268Sgonzo}; 116239268Sgonzo 117239268Sgonzostatic struct arm_tmr_softc *arm_tmr_sc = NULL; 118239268Sgonzo 119266347Sianstatic uint64_t platform_arm_tmr_freq = 0; 120253971Scognet 121239268Sgonzo#define tmr_prv_read_4(reg) \ 122239268Sgonzo bus_space_read_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg) 123239268Sgonzo#define tmr_prv_write_4(reg, val) \ 124239268Sgonzo bus_space_write_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg, val) 125239268Sgonzo#define tmr_gbl_read_4(reg) \ 126239268Sgonzo bus_space_read_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg) 127239268Sgonzo#define tmr_gbl_write_4(reg, val) \ 128239268Sgonzo bus_space_write_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg, val) 129239268Sgonzo 130239268Sgonzo 131239268Sgonzostatic timecounter_get_t arm_tmr_get_timecount; 132239268Sgonzo 133239268Sgonzostatic struct timecounter arm_tmr_timecount = { 134266207Sian .tc_name = "MPCore", 135239268Sgonzo .tc_get_timecount = arm_tmr_get_timecount, 136239268Sgonzo .tc_poll_pps = NULL, 137239268Sgonzo .tc_counter_mask = ~0u, 138239268Sgonzo .tc_frequency = 0, 139266207Sian .tc_quality = 800, 140239268Sgonzo}; 141239268Sgonzo 142239268Sgonzo/** 143239268Sgonzo * arm_tmr_get_timecount - reads the timecount (global) timer 144239268Sgonzo * @tc: pointer to arm_tmr_timecount struct 145239268Sgonzo * 146239268Sgonzo * We only read the lower 32-bits, the timecount stuff only uses 32-bits 147239268Sgonzo * so (for now?) ignore the upper 32-bits. 148239268Sgonzo * 149239268Sgonzo * RETURNS 150239268Sgonzo * The lower 32-bits of the counter. 151239268Sgonzo */ 152239268Sgonzostatic unsigned 153239268Sgonzoarm_tmr_get_timecount(struct timecounter *tc) 154239268Sgonzo{ 155239268Sgonzo return (tmr_gbl_read_4(GBL_TIMER_COUNT_LOW)); 156239268Sgonzo} 157239268Sgonzo 158239268Sgonzo/** 159239268Sgonzo * arm_tmr_start - starts the eventtimer (private) timer 160239268Sgonzo * @et: pointer to eventtimer struct 161239268Sgonzo * @first: the number of seconds and fractional sections to trigger in 162239268Sgonzo * @period: the period (in seconds and fractional sections) to set 163239268Sgonzo * 164239268Sgonzo * If the eventtimer is required to be in oneshot mode, period will be 165239268Sgonzo * NULL and first will point to the time to trigger. If in periodic mode 166239268Sgonzo * period will contain the time period and first may optionally contain 167239268Sgonzo * the time for the first period. 168239268Sgonzo * 169239268Sgonzo * RETURNS 170239268Sgonzo * Always returns 0 171239268Sgonzo */ 172239268Sgonzostatic int 173247463Smavarm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 174239268Sgonzo{ 175239268Sgonzo uint32_t load, count; 176239268Sgonzo uint32_t ctrl; 177239268Sgonzo 178266347Sian tmr_prv_write_4(PRV_TIMER_CTRL, 0); 179266347Sian tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); 180266347Sian 181239268Sgonzo ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE; 182239268Sgonzo 183247463Smav if (period != 0) { 184247463Smav load = ((uint32_t)et->et_frequency * period) >> 32; 185239268Sgonzo ctrl |= PRV_TIMER_CTRL_AUTO_RELOAD; 186247463Smav } else 187239268Sgonzo load = 0; 188239268Sgonzo 189247463Smav if (first != 0) 190266347Sian count = (uint32_t)((et->et_frequency * first) >> 32); 191247463Smav else 192239268Sgonzo count = load; 193239268Sgonzo 194239268Sgonzo tmr_prv_write_4(PRV_TIMER_LOAD, load); 195239268Sgonzo tmr_prv_write_4(PRV_TIMER_COUNT, count); 196266347Sian tmr_prv_write_4(PRV_TIMER_CTRL, ctrl); 197239268Sgonzo 198239268Sgonzo return (0); 199239268Sgonzo} 200239268Sgonzo 201239268Sgonzo/** 202239268Sgonzo * arm_tmr_stop - stops the eventtimer (private) timer 203239268Sgonzo * @et: pointer to eventtimer struct 204239268Sgonzo * 205239268Sgonzo * Simply stops the private timer by clearing all bits in the ctrl register. 206239268Sgonzo * 207239268Sgonzo * RETURNS 208239268Sgonzo * Always returns 0 209239268Sgonzo */ 210239268Sgonzostatic int 211239268Sgonzoarm_tmr_stop(struct eventtimer *et) 212239268Sgonzo{ 213239268Sgonzo tmr_prv_write_4(PRV_TIMER_CTRL, 0); 214266347Sian tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); 215239268Sgonzo return (0); 216239268Sgonzo} 217239268Sgonzo 218239268Sgonzo/** 219239268Sgonzo * arm_tmr_intr - ISR for the eventtimer (private) timer 220239268Sgonzo * @arg: pointer to arm_tmr_softc struct 221239268Sgonzo * 222239268Sgonzo * Clears the event register and then calls the eventtimer callback. 223239268Sgonzo * 224239268Sgonzo * RETURNS 225239268Sgonzo * Always returns FILTER_HANDLED 226239268Sgonzo */ 227239268Sgonzostatic int 228239268Sgonzoarm_tmr_intr(void *arg) 229239268Sgonzo{ 230239268Sgonzo struct arm_tmr_softc *sc = (struct arm_tmr_softc *)arg; 231239268Sgonzo 232239268Sgonzo tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); 233239268Sgonzo 234239268Sgonzo if (sc->et.et_active) 235239268Sgonzo sc->et.et_event_cb(&sc->et, sc->et.et_arg); 236239268Sgonzo 237239268Sgonzo return (FILTER_HANDLED); 238239268Sgonzo} 239239268Sgonzo 240239268Sgonzo 241239268Sgonzo 242239268Sgonzo 243239268Sgonzo/** 244239268Sgonzo * arm_tmr_probe - timer probe routine 245239268Sgonzo * @dev: new device 246239268Sgonzo * 247239268Sgonzo * The probe function returns success when probed with the fdt compatible 248239268Sgonzo * string set to "arm,mpcore-timers". 249239268Sgonzo * 250239268Sgonzo * RETURNS 251239268Sgonzo * BUS_PROBE_DEFAULT if the fdt device is compatible, otherwise ENXIO. 252239268Sgonzo */ 253239268Sgonzostatic int 254239268Sgonzoarm_tmr_probe(device_t dev) 255239268Sgonzo{ 256266152Sian 257266152Sian if (!ofw_bus_status_okay(dev)) 258266152Sian return (ENXIO); 259266152Sian 260239268Sgonzo if (!ofw_bus_is_compatible(dev, "arm,mpcore-timers")) 261239268Sgonzo return (ENXIO); 262239268Sgonzo 263266207Sian device_set_desc(dev, "ARM MPCore Timers"); 264239268Sgonzo return (BUS_PROBE_DEFAULT); 265239268Sgonzo} 266239268Sgonzo 267239268Sgonzo/** 268239268Sgonzo * arm_tmr_attach - attaches the timer to the simplebus 269239268Sgonzo * @dev: new device 270239268Sgonzo * 271239268Sgonzo * Reserves memory and interrupt resources, stores the softc structure 272239268Sgonzo * globally and registers both the timecount and eventtimer objects. 273239268Sgonzo * 274239268Sgonzo * RETURNS 275239268Sgonzo * Zero on sucess or ENXIO if an error occuried. 276239268Sgonzo */ 277239268Sgonzostatic int 278239268Sgonzoarm_tmr_attach(device_t dev) 279239268Sgonzo{ 280239268Sgonzo struct arm_tmr_softc *sc = device_get_softc(dev); 281239268Sgonzo phandle_t node; 282239268Sgonzo pcell_t clock; 283239268Sgonzo void *ihl; 284266347Sian boolean_t fixed_freq; 285239268Sgonzo 286239268Sgonzo if (arm_tmr_sc) 287239268Sgonzo return (ENXIO); 288239268Sgonzo 289266347Sian if (platform_arm_tmr_freq == ARM_TMR_FREQUENCY_VARIES) { 290266347Sian fixed_freq = false; 291266347Sian } else { 292266347Sian fixed_freq = true; 293266347Sian if (platform_arm_tmr_freq != 0) { 294266347Sian sc->clkfreq = platform_arm_tmr_freq; 295266347Sian } else { 296266347Sian /* Get the base clock frequency */ 297266347Sian node = ofw_bus_get_node(dev); 298266347Sian if ((OF_getencprop(node, "clock-frequency", &clock, 299266347Sian sizeof(clock))) <= 0) { 300266347Sian device_printf(dev, "missing clock-frequency " 301266347Sian "attribute in FDT\n"); 302266347Sian return (ENXIO); 303266347Sian } 304266352Sian sc->clkfreq = clock; 305253971Scognet } 306239268Sgonzo } 307239268Sgonzo 308239268Sgonzo if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) { 309239268Sgonzo device_printf(dev, "could not allocate resources\n"); 310239268Sgonzo return (ENXIO); 311239268Sgonzo } 312239268Sgonzo 313239268Sgonzo /* Global timer interface */ 314239268Sgonzo sc->gbl_bst = rman_get_bustag(sc->tmr_res[0]); 315239268Sgonzo sc->gbl_bsh = rman_get_bushandle(sc->tmr_res[0]); 316239268Sgonzo 317239268Sgonzo /* Private per-CPU timer interface */ 318239268Sgonzo sc->prv_bst = rman_get_bustag(sc->tmr_res[2]); 319239268Sgonzo sc->prv_bsh = rman_get_bushandle(sc->tmr_res[2]); 320239268Sgonzo 321239268Sgonzo arm_tmr_sc = sc; 322239268Sgonzo 323239268Sgonzo /* Disable both timers to start off */ 324239268Sgonzo tmr_prv_write_4(PRV_TIMER_CTRL, 0x00000000); 325239268Sgonzo tmr_gbl_write_4(GBL_TIMER_CTRL, 0x00000000); 326239268Sgonzo 327239268Sgonzo if (bus_setup_intr(dev, sc->tmr_res[3], INTR_TYPE_CLK, arm_tmr_intr, 328239268Sgonzo NULL, sc, &ihl) != 0) { 329239268Sgonzo bus_release_resources(dev, arm_tmr_spec, sc->tmr_res); 330239268Sgonzo device_printf(dev, "Unable to setup the clock irq handler.\n"); 331239268Sgonzo return (ENXIO); 332239268Sgonzo } 333239268Sgonzo 334266347Sian /* 335266347Sian * If the clock is fixed-frequency, setup and enable the global timer to 336266347Sian * use as the timecounter. If it's variable frequency it won't work as 337266347Sian * a timecounter. We also can't use it for DELAY(), so hopefully the 338266347Sian * platform provides its own implementation. If it doesn't, ours will 339266347Sian * get used, but since the frequency isn't set, it will only use the 340266347Sian * bogus loop counter. 341266347Sian */ 342266347Sian if (fixed_freq) { 343266347Sian tmr_gbl_write_4(GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE); 344266347Sian arm_tmr_timecount.tc_frequency = sc->clkfreq; 345266347Sian tc_init(&arm_tmr_timecount); 346266347Sian } 347266347Sian 348266347Sian /* 349266347Sian * Setup and register the eventtimer. Most event timers set their min 350266347Sian * and max period values to some value calculated from the clock 351266347Sian * frequency. We might not know yet what our runtime clock frequency 352266347Sian * will be, so we just use some safe values. A max of 2 seconds ensures 353266347Sian * that even if our base clock frequency is 2GHz (meaning a 4GHz CPU), 354266347Sian * we won't overflow our 32-bit timer count register. A min of 20 355266347Sian * nanoseconds is pretty much completely arbitrary. 356266347Sian */ 357266207Sian sc->et.et_name = "MPCore"; 358239268Sgonzo sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; 359239268Sgonzo sc->et.et_quality = 1000; 360239268Sgonzo sc->et.et_frequency = sc->clkfreq; 361266347Sian sc->et.et_min_period = 20 * SBT_1NS; 362266347Sian sc->et.et_max_period = 2 * SBT_1S; 363239268Sgonzo sc->et.et_start = arm_tmr_start; 364239268Sgonzo sc->et.et_stop = arm_tmr_stop; 365239268Sgonzo sc->et.et_priv = sc; 366239268Sgonzo et_register(&sc->et); 367239268Sgonzo 368239268Sgonzo return (0); 369239268Sgonzo} 370239268Sgonzo 371239268Sgonzostatic device_method_t arm_tmr_methods[] = { 372239268Sgonzo DEVMETHOD(device_probe, arm_tmr_probe), 373239268Sgonzo DEVMETHOD(device_attach, arm_tmr_attach), 374239268Sgonzo { 0, 0 } 375239268Sgonzo}; 376239268Sgonzo 377239268Sgonzostatic driver_t arm_tmr_driver = { 378239268Sgonzo "mp_tmr", 379239268Sgonzo arm_tmr_methods, 380239268Sgonzo sizeof(struct arm_tmr_softc), 381239268Sgonzo}; 382239268Sgonzo 383239268Sgonzostatic devclass_t arm_tmr_devclass; 384239268Sgonzo 385270075SianEARLY_DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0, 386270075Sian BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 387239268Sgonzo 388266347Sian/* 389266347Sian * Handle a change in clock frequency. The mpcore timer runs at half the CPU 390266347Sian * frequency. When the CPU frequency changes due to power-saving or thermal 391266347Sian * managment, the platform-specific code that causes the frequency change calls 392266347Sian * this routine to inform the clock driver, and we in turn inform the event 393266347Sian * timer system, which actually updates the value in et->frequency for us and 394266347Sian * reschedules the current event(s) in a way that's atomic with respect to 395266347Sian * start/stop/intr code that may be running on various CPUs at the time of the 396266347Sian * call. 397266347Sian * 398266347Sian * This routine can also be called by a platform's early init code. If the 399266347Sian * value passed is ARM_TMR_FREQUENCY_VARIES, that will cause the attach() code 400266347Sian * to register as an eventtimer, but not a timecounter. If the value passed in 401266347Sian * is any other non-zero value it is used as the fixed frequency for the timer. 402266347Sian */ 403266347Sianvoid 404266347Sianarm_tmr_change_frequency(uint64_t newfreq) 405266347Sian{ 406266347Sian 407266347Sian if (arm_tmr_sc == NULL) 408266347Sian platform_arm_tmr_freq = newfreq; 409266347Sian else 410266347Sian et_change_frequency(&arm_tmr_sc->et, newfreq); 411266347Sian} 412266347Sian 413239268Sgonzo/** 414239268Sgonzo * DELAY - Delay for at least usec microseconds. 415239268Sgonzo * @usec: number of microseconds to delay by 416239268Sgonzo * 417239268Sgonzo * This function is called all over the kernel and is suppose to provide a 418239268Sgonzo * consistent delay. This function may also be called before the console 419239268Sgonzo * is setup so no printf's can be called here. 420239268Sgonzo * 421239268Sgonzo * RETURNS: 422239268Sgonzo * nothing 423239268Sgonzo */ 424266207Sianstatic void __used /* Must emit function code for the weak ref below. */ 425266207Sianarm_tmr_DELAY(int usec) 426239268Sgonzo{ 427239268Sgonzo int32_t counts_per_usec; 428239268Sgonzo int32_t counts; 429239268Sgonzo uint32_t first, last; 430239268Sgonzo 431239268Sgonzo /* Check the timers are setup, if not just use a for loop for the meantime */ 432266347Sian if (arm_tmr_sc == NULL || arm_tmr_timecount.tc_frequency == 0) { 433239268Sgonzo for (; usec > 0; usec--) 434239268Sgonzo for (counts = 200; counts > 0; counts--) 435239268Sgonzo cpufunc_nullop(); /* Prevent gcc from optimizing 436239268Sgonzo * out the loop 437239268Sgonzo */ 438239268Sgonzo return; 439239268Sgonzo } 440239268Sgonzo 441239268Sgonzo /* Get the number of times to count */ 442239268Sgonzo counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); 443239268Sgonzo 444239268Sgonzo /* 445239268Sgonzo * Clamp the timeout at a maximum value (about 32 seconds with 446239268Sgonzo * a 66MHz clock). *Nobody* should be delay()ing for anywhere 447239268Sgonzo * near that length of time and if they are, they should be hung 448239268Sgonzo * out to dry. 449239268Sgonzo */ 450239268Sgonzo if (usec >= (0x80000000U / counts_per_usec)) 451239268Sgonzo counts = (0x80000000U / counts_per_usec) - 1; 452239268Sgonzo else 453239268Sgonzo counts = usec * counts_per_usec; 454239268Sgonzo 455239268Sgonzo first = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW); 456239268Sgonzo 457239268Sgonzo while (counts > 0) { 458239268Sgonzo last = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW); 459239268Sgonzo counts -= (int32_t)(last - first); 460239268Sgonzo first = last; 461239268Sgonzo } 462239268Sgonzo} 463266207Sian 464266207Sian/* 465266207Sian * Supply a DELAY() implementation via weak linkage. A platform may want to use 466266207Sian * the mpcore per-cpu eventtimers but provide its own DELAY() routine, 467266207Sian * especially when the core frequency can change on the fly. 468266207Sian */ 469266207Sian__weak_reference(arm_tmr_DELAY, DELAY); 470266207Sian 471