1*77e75c28Sskrll /* $NetBSD: footbridge_clock.c,v 1.27 2021/08/13 11:40:43 skrll Exp $ */
2af8ce959Schris
3af8ce959Schris /*
4af8ce959Schris * Copyright (c) 1997 Mark Brinicombe.
5af8ce959Schris * Copyright (c) 1997 Causality Limited.
6af8ce959Schris * All rights reserved.
7af8ce959Schris *
8af8ce959Schris * Redistribution and use in source and binary forms, with or without
9af8ce959Schris * modification, are permitted provided that the following conditions
10af8ce959Schris * are met:
11af8ce959Schris * 1. Redistributions of source code must retain the above copyright
12af8ce959Schris * notice, this list of conditions and the following disclaimer.
13af8ce959Schris * 2. Redistributions in binary form must reproduce the above copyright
14af8ce959Schris * notice, this list of conditions and the following disclaimer in the
15af8ce959Schris * documentation and/or other materials provided with the distribution.
16af8ce959Schris * 3. All advertising materials mentioning features or use of this software
17af8ce959Schris * must display the following acknowledgement:
18af8ce959Schris * This product includes software developed by Mark Brinicombe
19af8ce959Schris * for the NetBSD Project.
20af8ce959Schris * 4. The name of the company nor the name of the author may be used to
21af8ce959Schris * endorse or promote products derived from this software without specific
22af8ce959Schris * prior written permission.
23af8ce959Schris *
24af8ce959Schris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25af8ce959Schris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26af8ce959Schris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27af8ce959Schris * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28af8ce959Schris * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29af8ce959Schris * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30af8ce959Schris * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31af8ce959Schris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32af8ce959Schris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33af8ce959Schris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34af8ce959Schris * SUCH DAMAGE.
35af8ce959Schris */
36af8ce959Schris
379fd86b68Schris #include <sys/cdefs.h>
38*77e75c28Sskrll __KERNEL_RCSID(0, "$NetBSD: footbridge_clock.c,v 1.27 2021/08/13 11:40:43 skrll Exp $");
399fd86b68Schris
40af8ce959Schris /* Include header files */
41af8ce959Schris
42af8ce959Schris #include <sys/types.h>
43af8ce959Schris #include <sys/param.h>
44af8ce959Schris #include <sys/systm.h>
45af8ce959Schris #include <sys/kernel.h>
46af8ce959Schris #include <sys/time.h>
476cfcc60eSgdamore #include <sys/timetc.h>
48af8ce959Schris #include <sys/device.h>
49af8ce959Schris
50792b7ebdSmatt #include <machine/intr.h>
510c57d872Sthorpej
520c57d872Sthorpej #include <arm/cpufunc.h>
530c57d872Sthorpej
54af8ce959Schris #include <arm/footbridge/dc21285reg.h>
55af8ce959Schris #include <arm/footbridge/footbridgevar.h>
566c4ac1deSchris #include <arm/footbridge/footbridge.h>
57af8ce959Schris
58af8ce959Schris extern struct footbridge_softc *clock_sc;
59af8ce959Schris extern u_int dc21285_fclk;
60af8ce959Schris
61a78ddf8bSgdamore int clockhandler(void *);
62a78ddf8bSgdamore int statclockhandler(void *);
63a78ddf8bSgdamore static int load_timer(int, int);
64e3a3a9f5Schris
65dfcb3e35Schris /*
66dfcb3e35Schris * Statistics clock variance, in usec. Variance must be a
67dfcb3e35Schris * power of two. Since this gives us an even number, not an odd number,
68dfcb3e35Schris * we discard one case and compensate. That is, a variance of 1024 would
69dfcb3e35Schris * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
70dfcb3e35Schris * This is symmetric about the point 512, or statvar/2, and thus averages
71dfcb3e35Schris * to that value (assuming uniform random numbers).
72dfcb3e35Schris */
73dfcb3e35Schris const int statvar = 1024;
74dfcb3e35Schris int statmin; /* minimum stat clock count in ticks */
75dfcb3e35Schris int statcountperusec; /* number of ticks per usec at current stathz */
76dfcb3e35Schris int statprev; /* last value of we set statclock to */
77e3a3a9f5Schris
786cfcc60eSgdamore void footbridge_tc_init(void);
796cfcc60eSgdamore
80af8ce959Schris #if 0
819cbe4c86Sskrll static int clockmatch(device_t parent, cfdata_t cf, void *aux);
829cbe4c86Sskrll static void clockattach(device_t parent, device_t self, void *aux);
83af8ce959Schris
849cbe4c86Sskrll CFATTACH_DECL_NEW(footbridge_clock, sizeof(struct clock_softc),
85c5e91d44Sthorpej clockmatch, clockattach, NULL, NULL);
86af8ce959Schris
87af8ce959Schris /*
889cbe4c86Sskrll * int clockmatch(device_t parent, cfdata_t cf, void *aux);
89af8ce959Schris *
90af8ce959Schris * Just return ok for this if it is device 0
91af8ce959Schris */
92af8ce959Schris
93af8ce959Schris static int
949cbe4c86Sskrll clockmatch(device_t parent, cfdata_t cf, void *aux)
95af8ce959Schris {
96af8ce959Schris union footbridge_attach_args *fba = aux;
97af8ce959Schris
98af8ce959Schris if (strcmp(fba->fba_ca.ca_name, "clk") == 0)
99a78ddf8bSgdamore return 1;
100a78ddf8bSgdamore return 0;
101af8ce959Schris }
102af8ce959Schris
103af8ce959Schris
104af8ce959Schris /*
1059cbe4c86Sskrll * void clockattach(device_t parent, device_t self, void *aux)
106af8ce959Schris *
107af8ce959Schris */
108af8ce959Schris
109af8ce959Schris static void
1109cbe4c86Sskrll clockattach(device_t parent, device_t self, void *aux)
111af8ce959Schris {
1129cbe4c86Sskrll struct clock_softc *sc = device_private(self);
113af8ce959Schris union footbridge_attach_args *fba = aux;
114af8ce959Schris
1159cbe4c86Sskrll sc->sc_dev = self;
116af8ce959Schris sc->sc_iot = fba->fba_ca.ca_iot;
117af8ce959Schris sc->sc_ioh = fba->fba_ca.ca_ioh;
118af8ce959Schris
119af8ce959Schris clock_sc = sc;
120af8ce959Schris
121af8ce959Schris /* Cannot do anything until cpu_initclocks() has been called */
122af8ce959Schris
1239cbe4c86Sskrll aprint_normal("\n");
124af8ce959Schris }
125af8ce959Schris #endif
126af8ce959Schris
127af8ce959Schris /*
128af8ce959Schris * int clockhandler(struct clockframe *frame)
129af8ce959Schris *
130af8ce959Schris * Function called by timer 1 interrupts.
131af8ce959Schris * This just clears the interrupt condition and calls hardclock().
132af8ce959Schris */
133af8ce959Schris
134af8ce959Schris int
clockhandler(void * aframe)135a78ddf8bSgdamore clockhandler(void *aframe)
136af8ce959Schris {
137e3a3a9f5Schris struct clockframe *frame = aframe;
138af8ce959Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
139af8ce959Schris TIMER_1_CLEAR, 0);
140af8ce959Schris hardclock(frame);
141a78ddf8bSgdamore return 0; /* Pass the interrupt on down the chain */
142af8ce959Schris }
143af8ce959Schris
144af8ce959Schris /*
145af8ce959Schris * int statclockhandler(struct clockframe *frame)
146af8ce959Schris *
147af8ce959Schris * Function called by timer 2 interrupts.
148af8ce959Schris * This just clears the interrupt condition and calls statclock().
149af8ce959Schris */
150af8ce959Schris
151af8ce959Schris int
statclockhandler(void * aframe)152a78ddf8bSgdamore statclockhandler(void *aframe)
153af8ce959Schris {
154e3a3a9f5Schris struct clockframe *frame = aframe;
155dfcb3e35Schris int newint, r;
156dfcb3e35Schris int currentclock ;
157dfcb3e35Schris
158dfcb3e35Schris /* start the clock off again */
159af8ce959Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
160af8ce959Schris TIMER_2_CLEAR, 0);
161dfcb3e35Schris
162dfcb3e35Schris do {
163dfcb3e35Schris r = random() & (statvar-1);
164dfcb3e35Schris } while (r == 0);
165dfcb3e35Schris newint = statmin + (r * statcountperusec);
166dfcb3e35Schris
167dfcb3e35Schris /* fetch the current count */
168dfcb3e35Schris currentclock = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
169dfcb3e35Schris TIMER_2_VALUE);
170dfcb3e35Schris
171dfcb3e35Schris /*
172dfcb3e35Schris * work out how much time has run, add another usec for time spent
173dfcb3e35Schris * here
174dfcb3e35Schris */
175dfcb3e35Schris r = ((statprev - currentclock) + statcountperusec);
176dfcb3e35Schris
177dfcb3e35Schris if (r < newint) {
178dfcb3e35Schris newint -= r;
179dfcb3e35Schris r = 0;
180dfcb3e35Schris }
181dfcb3e35Schris else
182dfcb3e35Schris printf("statclockhandler: Statclock overrun\n");
183dfcb3e35Schris
184dfcb3e35Schris
185dfcb3e35Schris /*
186dfcb3e35Schris * update the clock to the new counter, this reloads the existing
187dfcb3e35Schris * timer
188dfcb3e35Schris */
189dfcb3e35Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
190dfcb3e35Schris TIMER_2_LOAD, newint);
191dfcb3e35Schris statprev = newint;
192af8ce959Schris statclock(frame);
193dfcb3e35Schris if (r)
194dfcb3e35Schris /*
195dfcb3e35Schris * We've completely overrun the previous interval,
196dfcb3e35Schris * make sure we report the correct number of ticks.
197dfcb3e35Schris */
198dfcb3e35Schris statclock(frame);
199dfcb3e35Schris
200a78ddf8bSgdamore return 0; /* Pass the interrupt on down the chain */
201af8ce959Schris }
202af8ce959Schris
203af8ce959Schris static int
load_timer(int base,int herz)204a78ddf8bSgdamore load_timer(int base, int herz)
205af8ce959Schris {
206af8ce959Schris unsigned int timer_count;
207af8ce959Schris int control;
208af8ce959Schris
209172a6238She timer_count = dc21285_fclk / herz;
21023bc2503Sthorpej if (timer_count > TIMER_MAX_VAL * 16) {
211af8ce959Schris control = TIMER_FCLK_256;
212af8ce959Schris timer_count >>= 8;
21323bc2503Sthorpej } else if (timer_count > TIMER_MAX_VAL) {
214af8ce959Schris control = TIMER_FCLK_16;
215af8ce959Schris timer_count >>= 4;
216af8ce959Schris } else
217af8ce959Schris control = TIMER_FCLK;
218af8ce959Schris
219af8ce959Schris control |= (TIMER_ENABLE | TIMER_MODE_PERIODIC);
220af8ce959Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
221af8ce959Schris base + TIMER_LOAD, timer_count);
222af8ce959Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
223af8ce959Schris base + TIMER_CONTROL, control);
224af8ce959Schris bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
225af8ce959Schris base + TIMER_CLEAR, 0);
226a78ddf8bSgdamore return timer_count;
227af8ce959Schris }
228af8ce959Schris
229af8ce959Schris /*
230172a6238She * void setstatclockrate(int herz)
231af8ce959Schris *
232af8ce959Schris * Set the stat clock rate. The stat clock uses timer2
233af8ce959Schris */
234af8ce959Schris
235af8ce959Schris void
setstatclockrate(int herz)236a78ddf8bSgdamore setstatclockrate(int herz)
237af8ce959Schris {
238dfcb3e35Schris int statint;
239dfcb3e35Schris int countpersecond;
240dfcb3e35Schris int statvarticks;
241af8ce959Schris
242172a6238She /* statint == num in counter to drop by desired herz */
2434ece245bStsutsui statint = statprev = clock_sc->sc_statclock_count =
244172a6238She load_timer(TIMER_2_BASE, herz);
245dfcb3e35Schris
246dfcb3e35Schris /* Get the total ticks a second */
247172a6238She countpersecond = statint * herz;
248dfcb3e35Schris
249dfcb3e35Schris /* now work out how many ticks per usec */
250dfcb3e35Schris statcountperusec = countpersecond / 1000000;
251dfcb3e35Schris
252dfcb3e35Schris /* calculate a variance range of statvar */
253dfcb3e35Schris statvarticks = statcountperusec * statvar;
254dfcb3e35Schris
255dfcb3e35Schris /* minimum is statint - 50% of variant */
256dfcb3e35Schris statmin = statint - (statvarticks / 2);
257af8ce959Schris }
258af8ce959Schris
259af8ce959Schris /*
260af8ce959Schris * void cpu_initclocks(void)
261af8ce959Schris *
262af8ce959Schris * Initialise the clocks.
263af8ce959Schris *
264af8ce959Schris * Timer 1 is used for the main system clock (hardclock)
265af8ce959Schris * Timer 2 is used for the statistics clock (statclock)
266af8ce959Schris */
267af8ce959Schris
268af8ce959Schris void
cpu_initclocks(void)269a78ddf8bSgdamore cpu_initclocks(void)
270af8ce959Schris {
2714c558675Schris /* stathz and profhz should be set to something, we have the timer */
2724c558675Schris if (stathz == 0)
273dfcb3e35Schris stathz = hz;
2744c558675Schris
2754c558675Schris if (profhz == 0)
2764c558675Schris profhz = stathz * 5;
277af8ce959Schris
278af8ce959Schris /* Report the clock frequencies */
2799cbe4c86Sskrll aprint_debug("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
280af8ce959Schris
281af8ce959Schris /* Setup timer 1 and claim interrupt */
282af8ce959Schris clock_sc->sc_clock_count = load_timer(TIMER_1_BASE, hz);
283af8ce959Schris
284af8ce959Schris /*
285af8ce959Schris * Use ticks per 256us for accuracy since ticks per us is often
286af8ce959Schris * fractional e.g. @ 66MHz
287af8ce959Schris */
288af8ce959Schris clock_sc->sc_clock_ticks_per_256us =
289af8ce959Schris ((((clock_sc->sc_clock_count * hz) / 1000) * 256) / 1000);
29061578bc3Schris clock_sc->sc_clockintr = footbridge_intr_claim(IRQ_TIMER_1, IPL_CLOCK,
291af8ce959Schris "tmr1 hard clk", clockhandler, 0);
292af8ce959Schris
293af8ce959Schris if (clock_sc->sc_clockintr == NULL)
2940f09ed48Sprovos panic("%s: Cannot install timer 1 interrupt handler",
2959cbe4c86Sskrll device_xname(clock_sc->sc_dev));
296af8ce959Schris
297af8ce959Schris /* If stathz is non-zero then setup the stat clock */
298af8ce959Schris if (stathz) {
299af8ce959Schris /* Setup timer 2 and claim interrupt */
300af8ce959Schris setstatclockrate(stathz);
3014b293a84Sad clock_sc->sc_statclockintr = footbridge_intr_claim(IRQ_TIMER_2, IPL_HIGH,
302af8ce959Schris "tmr2 stat clk", statclockhandler, 0);
303af8ce959Schris if (clock_sc->sc_statclockintr == NULL)
3040f09ed48Sprovos panic("%s: Cannot install timer 2 interrupt handler",
3059cbe4c86Sskrll device_xname(clock_sc->sc_dev));
306af8ce959Schris }
3076cfcc60eSgdamore
3086cfcc60eSgdamore footbridge_tc_init();
309af8ce959Schris }
310af8ce959Schris
3116cfcc60eSgdamore static uint32_t
fclk_get_count(struct timecounter * tc)3126cfcc60eSgdamore fclk_get_count(struct timecounter *tc)
3136cfcc60eSgdamore {
3146cfcc60eSgdamore return (TIMER_MAX_VAL -
3156cfcc60eSgdamore bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
3166cfcc60eSgdamore TIMER_3_VALUE));
3176cfcc60eSgdamore }
318af8ce959Schris
319af8ce959Schris void
footbridge_tc_init(void)3206cfcc60eSgdamore footbridge_tc_init(void)
321af8ce959Schris {
3226cfcc60eSgdamore static struct timecounter fb_tc = {
3236cfcc60eSgdamore .tc_get_timecount = fclk_get_count,
3246cfcc60eSgdamore .tc_counter_mask = TIMER_MAX_VAL,
3256cfcc60eSgdamore .tc_name = "dc21285_fclk",
3266cfcc60eSgdamore .tc_quality = 100
3276cfcc60eSgdamore };
3286cfcc60eSgdamore fb_tc.tc_frequency = dc21285_fclk;
3296cfcc60eSgdamore tc_init(&fb_tc);
330af8ce959Schris }
331af8ce959Schris
332af8ce959Schris /*
3336c4ac1deSchris * Use a timer to track microseconds, if the footbridge hasn't been setup we
3346c4ac1deSchris * rely on an estimated loop, however footbridge is attached very early on.
335af8ce959Schris */
336af8ce959Schris
3376c4ac1deSchris static int delay_count_per_usec = 0;
338af8ce959Schris
3396c4ac1deSchris void
calibrate_delay(void)3406c4ac1deSchris calibrate_delay(void)
3416c4ac1deSchris {
3426cfcc60eSgdamore /*
3436cfcc60eSgdamore * For all current footbridge hardware, the fclk runs at a
3446cfcc60eSgdamore * rate that is sufficiently slow enough that we don't need to
3456cfcc60eSgdamore * use a prescaler. A prescaler would be needed if the fclk
3466cfcc60eSgdamore * could wrap within 2 hardclock periods (2 * HZ). With
3476cfcc60eSgdamore * normal values of HZ (100 and higher), this is unlikely to
3486cfcc60eSgdamore * ever happen.
3496cfcc60eSgdamore *
3506cfcc60eSgdamore * We let TIMER 3 just run free, at the freqeuncy supplied by
3516cfcc60eSgdamore * dc21285_fclk.
3526cfcc60eSgdamore */
3536cfcc60eSgdamore bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
3546cfcc60eSgdamore TIMER_3_BASE + TIMER_CONTROL, TIMER_ENABLE);
3556cfcc60eSgdamore delay_count_per_usec = dc21285_fclk / 1000000;
3566cfcc60eSgdamore if (dc21285_fclk % 1000000)
3576cfcc60eSgdamore delay_count_per_usec += 1;
3586c4ac1deSchris }
359af8ce959Schris
360af8ce959Schris void
delay(unsigned n)3616cfcc60eSgdamore delay(unsigned n)
362af8ce959Schris {
3636c4ac1deSchris uint32_t cur, last, delta, usecs;
364af8ce959Schris
3656cfcc60eSgdamore if (n == 0)
3666cfcc60eSgdamore return;
3676c4ac1deSchris
36873ca5359Smatt /*
36973ca5359Smatt * not calibrated the timer yet, so try to live with this horrible
37073ca5359Smatt * loop!
3716cfcc60eSgdamore *
3726cfcc60eSgdamore * Note: a much better solution might be to have the timers
3736cfcc60eSgdamore * get get calibrated out of mach_init. Of course, the
3746cfcc60eSgdamore * clock_sc needs to be set up, so we can read/write the clock
3756cfcc60eSgdamore * registers.
37673ca5359Smatt */
3776cfcc60eSgdamore if (!delay_count_per_usec)
3786c4ac1deSchris {
379d21e4ed1Schris /*
380d21e4ed1Schris * the loop below has a core of 6 instructions
381d21e4ed1Schris * StrongArms top out at 233Mhz, so one instruction takes
382d21e4ed1Schris * 0.004 us, and 6 take 0.025 us, so we need to loop 40
383d21e4ed1Schris * times to make one usec
384d21e4ed1Schris */
385d21e4ed1Schris int delaycount = 40;
3866cfcc60eSgdamore volatile int i;
3876cfcc60eSgdamore
38847c99ba5Smycroft while (n-- > 0) {
389af8ce959Schris for (i = delaycount; --i;);
3906c4ac1deSchris }
3916c4ac1deSchris return;
3926c4ac1deSchris }
393a8d4145fSchris
394625d05a4Schris last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
395625d05a4Schris TIMER_3_VALUE);
3966cfcc60eSgdamore delta = usecs = 0;
3976c4ac1deSchris
3986cfcc60eSgdamore while (n > usecs) {
3996c4ac1deSchris cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
4006c4ac1deSchris TIMER_3_VALUE);
4016c4ac1deSchris if (last < cur)
4026c4ac1deSchris /* timer has wrapped */
4036cfcc60eSgdamore delta += ((TIMER_MAX_VAL - cur) + last);
404af8ce959Schris else
4056c4ac1deSchris delta += (last - cur);
4066c4ac1deSchris
4076c4ac1deSchris last = cur;
4086cfcc60eSgdamore
4096cfcc60eSgdamore while (delta >= delay_count_per_usec) {
4106cfcc60eSgdamore delta -= delay_count_per_usec;
4116cfcc60eSgdamore usecs++;
4126cfcc60eSgdamore }
413af8ce959Schris }
414af8ce959Schris }
415af8ce959Schris
416af8ce959Schris /* End of footbridge_clock.c */
417