1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * Timer routines File: cfe_timer.c 5 * 6 * This module contains routines to keep track of the system time,. 7 * Since we don't have any interrupts in the firmware, even the 8 * timer is polled. The timer must be called often enough 9 * to prevent missing the overflow of the CP0 COUNT 10 * register, approximately 2 billion cycles (half the count) 11 * 12 * Be sure to use the POLL() macro each time you enter a loop 13 * where you are waiting for some I/O event to occur or 14 * are waiting for time to elapse. 15 * 16 * It is *not* a time-of-year clock. The timer is only used 17 * for timing I/O events. 18 * 19 * Internally, time is maintained in units of "CLOCKSPERTICK", 20 * which should be about tenths of seconds. 21 * 22 * Author: Mitch Lichtenberg 23 * 24 ********************************************************************* 25 * 26 * Copyright 2000,2001,2002,2003 27 * Broadcom Corporation. All rights reserved. 28 * 29 * This software is furnished under license and may be used and 30 * copied only in accordance with the following terms and 31 * conditions. Subject to these conditions, you may download, 32 * copy, install, use, modify and distribute modified or unmodified 33 * copies of this software in source and/or binary form. No title 34 * or ownership is transferred hereby. 35 * 36 * 1) Any source code used, modified or distributed must reproduce 37 * and retain this copyright notice and list of conditions 38 * as they appear in the source file. 39 * 40 * 2) No right is granted to use any trade name, trademark, or 41 * logo of Broadcom Corporation. The "Broadcom Corporation" 42 * name may not be used to endorse or promote products derived 43 * from this software without the prior written permission of 44 * Broadcom Corporation. 45 * 46 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 47 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 48 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 49 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 50 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 51 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 52 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 53 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 54 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 55 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 56 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 57 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 58 * THE POSSIBILITY OF SUCH DAMAGE. 59 ********************************************************************* */ 60 61#include "cfe.h" 62 63#ifndef CPUCFG_CYCLESPERCPUTICK 64#define CPUCFG_CYCLESPERCPUTICK 1 /* CPU clock ticks per CP0 COUNT */ 65#endif 66 67/* ********************************************************************* 68 * Externs 69 ********************************************************************* */ 70 71extern uint32_t _getticks(void); /* return value of CP0 COUNT */ 72 73/* ********************************************************************* 74 * Data 75 ********************************************************************* */ 76 77volatile cfe_timer_t cfe_ticks; /* current system time */ 78 79unsigned int cfe_cpu_speed; /* CPU speed in clocks/second */ 80 81/* With current technology, we would like to accomodate 82 sub-microsecond delays, but clocks per nanosecond may be a small 83 number. For more precision, we convert in terms of Kns, where 84 one Kns = 1024 nsec, and scale by shifting. */ 85 86static unsigned int cfe_clocks_per_Kns; 87static unsigned int cfe_clocks_per_usec; 88static unsigned int cfe_clocks_per_tick; 89 90static uint32_t cfe_oldcount; /* For keeping track of ticks */ 91static uint32_t cfe_remticks; 92static int cfe_timer_initflg = 0; 93 94/* 95 * C0_COUNT clocks per tick. Some CPUs tick CP0 every 'n' cycles, 96 * that's what CPUCFG_CYCLESPERCPUTICK is for. 97 */ 98#define CFE_CLOCKSPERUSEC(cpu_speed) \ 99 ((cpu_speed)/1000000/(CPUCFG_CYCLESPERCPUTICK)) 100#define CFE_CLOCKSPERKNS(cpu_speed) \ 101 ((cpu_speed)/976563/(CPUCFG_CYCLESPERCPUTICK)) 102#define CFE_CLOCKSPERTICK(cpu_speed) \ 103 ((cpu_speed)/(CFE_HZ)/(CPUCFG_CYCLESPERCPUTICK)) 104 105 106/* ********************************************************************* 107 * cfe_timer_task() 108 * 109 * This routine is called as part of normal device polling to 110 * update the system time. We read the CP0 COUNT register, 111 * add the delta into our current time, convert to ticks, 112 * and keep track of the COUNT register overflow 113 * 114 * Input parameters: 115 * nothing 116 * 117 * Return value: 118 * nothing 119 ********************************************************************* */ 120 121 122static void cfe_timer_task(void *arg) 123{ 124 uint32_t count; 125 uint32_t deltaticks; 126 uint32_t clockspertick; 127 128 clockspertick = cfe_clocks_per_tick; 129 130 count = _getticks(); 131 deltaticks = (count - cfe_oldcount); 132 cfe_remticks += deltaticks; 133 134 /* 135 * If CP0 COUNT jumped by a large value, use div/mod to update 136 * the clock. Otherwise, 137 * assume it only moved by one tick and use a simple 138 * loop to update it. This loop probably will not 139 * execute more than once. 140 */ 141 142 if (cfe_remticks > (clockspertick << 4)) { 143 cfe_ticks += (cfe_remticks / clockspertick); 144 cfe_remticks %= clockspertick; 145 } 146 else { 147 while (cfe_remticks > clockspertick) { 148 cfe_remticks -= clockspertick; 149 cfe_ticks++; 150 } 151 } 152 153 cfe_oldcount = count; 154} 155 156 157/* ********************************************************************* 158 * cfe_timer_init(cpu_speed) 159 * 160 * Initialize the timer module. 161 * 162 * Input parameters: 163 * CPU clock frequency in Hz (normally cfe_cpu_speed) 164 * 165 * Return value: 166 * nothing 167 ********************************************************************* */ 168 169void cfe_timer_init(unsigned int cpu_speed) 170{ 171 cfe_clocks_per_tick = CFE_CLOCKSPERTICK(cpu_speed); 172 cfe_clocks_per_Kns = CFE_CLOCKSPERKNS(cpu_speed); 173 if (cfe_clocks_per_Kns == 0) 174 cfe_clocks_per_Kns = 1; /* for the simulator */ 175 cfe_clocks_per_usec = CFE_CLOCKSPERUSEC(cpu_speed); 176 if (cfe_clocks_per_usec == 0) 177 cfe_clocks_per_usec = 1; /* for the simulator */ 178 179 cfe_oldcount = _getticks(); /* get current COUNT register */ 180 cfe_ticks = 0; 181 182 if (!cfe_timer_initflg) { 183 cfe_bg_add(cfe_timer_task,NULL); /* add task for background polling */ 184 cfe_timer_initflg = 1; 185 } 186} 187 188 189/* ********************************************************************* 190 * cfe_sleep(ticks) 191 * 192 * Sleep for 'ticks' ticks. Background tasks are processed while 193 * we wait. 194 * 195 * Input parameters: 196 * ticks - number of ticks to sleep (note: *not* clocks!) 197 * 198 * Return value: 199 * nothing 200 ********************************************************************* */ 201 202void cfe_sleep(int ticks) 203{ 204 int64_t timer; 205 206 TIMER_SET(timer,ticks); 207 while (!TIMER_EXPIRED(timer)) { 208 POLL(); 209 } 210} 211 212 213 214/* ********************************************************************* 215 * cfe_usleep(usec) 216 * 217 * Sleep for approximately the specified number of microseconds. 218 * 219 * Input parameters: 220 * usec - number of microseconds to wait 221 * 222 * Return value: 223 * nothing 224 ********************************************************************* */ 225 226void cfe_usleep(int usec) 227{ 228 uint32_t newcount; 229 uint32_t now; 230 231 /* XXX fix the wrap problem */ 232 233 now = _getticks(); 234 newcount = now + usec*cfe_clocks_per_usec; 235 236 if (newcount < now) /* wait for wraparound */ 237 while (_getticks() > now) 238 ; 239 240 241 while (_getticks() < newcount) 242 ; 243} 244 245 246/* ********************************************************************* 247 * cfe_nsleep(nsec) 248 * 249 * Sleep for approximately the specified number of nanoseconds. 250 * 251 * Input parameters: 252 * nsec - number of nanoseconds to wait 253 * 254 * Return value: 255 * nothing 256 ********************************************************************* */ 257 258void cfe_nsleep(int nsec) 259{ 260 uint32_t newcount; 261 uint32_t now; 262 263 /* XXX fix the wrap problem */ 264 265 now = _getticks(); 266 newcount = now + ((nsec*cfe_clocks_per_Kns + 512) >> 10); 267 268 if (newcount < now) /* wait for wraparound */ 269 while (_getticks() > now) 270 ; 271 272 while (_getticks() < newcount) 273 ; 274} 275