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