Annotation of sys/arch/arm/xscale/i80321_clock.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: i80321_clock.c,v 1.7 2007/05/21 14:54:35 drahn Exp $ */
2:
3: /*
4: * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20: #include <sys/param.h>
21: #include <sys/systm.h>
22: #include <sys/kernel.h>
23: #include <sys/time.h>
24: #include <sys/device.h>
25: #include <sys/timetc.h>
26: #include <dev/clock_subr.h>
27:
28: #include <machine/bus.h>
29: #include <machine/intr.h>
30:
31: #include <arm/cpufunc.h>
32:
33: #include <arm/xscale/i80321reg.h>
34: #include <arm/xscale/i80321var.h>
35:
36: #define TIMER_FREQUENCY 200000000 /* 200MHz */
37:
38: static struct evcount clk_count;
39: static struct evcount stat_count;
40: static int clk_irq = 129; /* XXX */
41: static int stat_irq = 130; /* XXX */
42:
43: uint32_t nextstatevent;
44: uint32_t nexttickevent;
45: uint32_t ticks_per_intr;
46: uint32_t ticks_per_second;
47: uint32_t lastnow;
48: uint32_t statvar, statmin;
49: int i80321_timer_inited;
50: static inline u_int32_t tmr0_read(void);
51: static inline void tmr0_write(u_int32_t val);
52: static inline u_int32_t tcr0_read(void);
53: static inline void tcr0_write(u_int32_t val);
54: static inline u_int32_t trr0_read(void);
55: static inline void trr0_write(u_int32_t val);
56: static inline u_int32_t tmr1_read(void);
57: static inline void tmr1_write(u_int32_t val);
58: static inline u_int32_t tcr1_read(void);
59: static inline void tcr1_write(u_int32_t val);
60: static inline u_int32_t trr1_read(void);
61: static inline void trr1_write(u_int32_t val);
62: static inline u_int32_t tisr_read(void);
63: static inline void tisr_write(u_int32_t val);
64: int i80321_intr(void *frame);
65:
66: u_int tcr1_get_timecount(struct timecounter *tc);
67:
68: static struct timecounter tcr1_timecounter = {
69: tcr1_get_timecount, NULL, 0xffffffff, 0, "tcr1", 0, NULL
70: };
71:
72:
73: /*
74: * TMR0 is used in non-reload mode as it is used for both the clock
75: * timer and sched timer.
76: *
77: * The counters on 80321 are count down interrupt on 0, not match
78: * register based, so it is not possible to find out how much
79: * many interrupts passed while irqs were blocked.
80: * also it is not possible to atomically add to the register
81: * get get it to precisely fire at a non-fixed interval.
82: *
83: * To work around this both timers are used, TMR1 is used as a reference
84: * clock set to auto reload with 0xffffffff, however we just ignore the
85: * interrupt it would generate. NOTE: does this drop one tick
86: * ever wrap? Could the reference timer be used in non-reload mode,
87: * where it would just keep counting, and not stop at 0 ?
88: *
89: * Internally this keeps track of when the next timer should fire
90: * and based on that time and the current value of the reference
91: * clock a number is written into the timer count register to schedule
92: * the next event.
93: */
94:
95:
96: static inline u_int32_t
97: tmr0_read(void)
98: {
99: u_int32_t ret;
100: __asm volatile ("mrc p6, 0, %0, c0, c1, 0" : "=r" (ret));
101: return ret;
102: }
103:
104: static inline void
105: tmr0_write(u_int32_t val)
106: {
107: __asm volatile ("mcr p6, 0, %0, c0, c1, 0" :: "r" (val));
108: }
109:
110: static inline u_int32_t
111: tcr0_read(void)
112: {
113: u_int32_t ret;
114: __asm volatile ("mrc p6, 0, %0, c2, c1, 0" : "=r" (ret));
115: return ret;
116: }
117:
118: static inline void
119: tcr0_write(u_int32_t val)
120: {
121: __asm volatile ("mcr p6, 0, %0, c2, c1, 0" :: "r" (val));
122: }
123:
124: static inline u_int32_t
125: trr0_read(void)
126: {
127: u_int32_t ret;
128: __asm volatile ("mrc p6, 0, %0, c4, c1, 0" : "=r" (ret));
129: return ret;
130: }
131:
132: static inline void
133: trr0_write(u_int32_t val)
134: {
135: __asm volatile ("mcr p6, 0, %0, c4, c1, 0" :: "r" (val));
136: }
137:
138: static inline u_int32_t
139: tmr1_read(void)
140: {
141: u_int32_t ret;
142: __asm volatile ("mrc p6, 0, %0, c1, c1, 0" : "=r" (ret));
143: return ret;
144: }
145:
146: static inline void
147: tmr1_write(u_int32_t val)
148: {
149: __asm volatile ("mcr p6, 0, %0, c1, c1, 0" :: "r" (val));
150: }
151:
152: inline u_int32_t
153: tcr1_read(void)
154: {
155: u_int32_t ret;
156: __asm volatile ("mrc p6, 0, %0, c3, c1, 0" : "=r" (ret));
157: return ret;
158: }
159:
160: static inline void
161: tcr1_write(u_int32_t val)
162: {
163: __asm volatile ("mcr p6, 0, %0, c3, c1, 0" :: "r" (val));
164: }
165:
166: static inline u_int32_t
167: trr1_read(void)
168: {
169: u_int32_t ret;
170: __asm volatile ("mrc p6, 0, %0, c5, c1, 0" : "=r" (ret));
171: return ret;
172: }
173:
174: static inline void
175: trr1_write(u_int32_t val)
176: {
177: __asm volatile ("mcr p6, 0, %0, c5, c1, 0" :: "r" (val));
178: }
179:
180: static inline u_int32_t
181: tisr_read()
182: {
183: u_int32_t ret;
184: __asm volatile ("mrc p6, 0, %0, c6, c1, 0" : "=r" (ret));
185: return ret;
186: }
187:
188: static inline void
189: tisr_write(u_int32_t val)
190: {
191: __asm volatile ("mcr p6, 0, %0, c6, c1, 0" :: "r" (val));
192: }
193:
194: /* counter counts down not up, so reverse the results by subtracting. */
195: u_int
196: tcr1_get_timecount(struct timecounter *tc)
197: {
198: return UINT_MAX - tcr1_read();
199: }
200:
201: /*
202: * timer 1 is running a timebase counter,
203: * ie reload 0xffffffff, reload, interrupt ignored
204: * timer 0 will be programmed with the delay until the next
205: * event. this is not set for reload
206: */
207: int
208: i80321_intr(void *frame)
209: {
210: uint32_t now, r;
211: uint32_t nextevent;
212:
213: tisr_write(TISR_TMR0);
214: now = tcr1_read();
215:
216: #if 0
217: if (lastnow < now) {
218: /* rollover, remove the missing 'tick'; 1-0xffffffff, not 0- */
219: nextstatevent -=1;
220: nexttickevent -=1;
221: }
222: #endif
223: while ((int32_t) (now - nexttickevent) < 0) {
224: nexttickevent -= ticks_per_intr;
225: /* XXX - correct nexttickevent? */
226: clk_count.ec_count++;
227: hardclock(frame);
228: }
229: while ((int32_t) (now - nextstatevent) < 0) {
230: do {
231: r = random() & (statvar -1);
232: } while (r == 0); /* random == 0 not allowed */
233: nextstatevent -= statmin + r;
234: /* XXX - correct nextstatevent? */
235: stat_count.ec_count++;
236: statclock(frame);
237: }
238: if ((now - nexttickevent) < (now - nextstatevent))
239: nextevent = now - nexttickevent;
240: else
241: nextevent = now - nextstatevent;
242: if (nextevent < 10 /* XXX */)
243: nextevent = 10;
244: if (nextevent > ticks_per_intr) {
245: /*
246: * If interrupts are blocked too long, like during
247: * the root prompt or ddb, the timer can roll over,
248: * this will allow the system to continue to run
249: * even if time is lost.
250: */
251: nextevent = ticks_per_intr;
252: nexttickevent = now;
253: nextstatevent = now;
254: }
255:
256:
257: tcr0_write(nextevent);
258: tmr0_write(TMRx_ENABLE|TMRx_PRIV|TMRx_CSEL_CORE);
259:
260: lastnow = now;
261:
262: return 1;
263: }
264:
265: void
266: cpu_initclocks()
267: {
268: uint32_t now;
269:
270: /* would it make sense to have this be 100/1000 to round nicely? */
271: /* 100/1000 or 128/1024 ? */
272: stathz = 100;
273: profhz = 1000;
274:
275: ticks_per_second = 200 * 1000000; /* 200 MHz */
276:
277: setstatclockrate(stathz);
278:
279: ticks_per_intr = ticks_per_second / hz;
280:
281: evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
282: evcount_attach(&stat_count, "stat", (void *)&stat_irq, &evcount_intr);
283:
284: (void) i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK, i80321_intr,
285: NULL, NULL);
286:
287: now = 0xffffffff;
288: nextstatevent = now - ticks_per_intr;
289: nexttickevent = now - ticks_per_intr;
290:
291: tcr1_write(now);
292: trr1_write(now);
293: tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_PRIV|TMRx_CSEL_CORE);
294:
295: tcr0_write(now); /* known big value */
296: tmr0_write(TMRx_ENABLE|TMRx_PRIV|TMRx_CSEL_CORE);
297: tcr0_write(ticks_per_intr);
298:
299: tcr1_timecounter.tc_frequency = ticks_per_second;
300: tc_init(&tcr1_timecounter);
301:
302:
303: i80321_timer_inited = 1;
304: }
305:
306: void
307: delay(u_int usecs)
308: {
309: u_int32_t clock, oclock, delta, delaycnt;
310: volatile int j;
311: int csec, usec;
312:
313: csec = usecs / 10000;
314: usec = usecs % 10000;
315:
316: delaycnt = (TIMER_FREQUENCY / 100) * csec +
317: (TIMER_FREQUENCY / 100) * usec / 10000;
318:
319: if (delaycnt <= 1) /* delay too short spin for a bit */
320: for (j = 100; j > 0; j--)
321: ;
322:
323: if (i80321_timer_inited == 0) {
324: /* clock isn't initialized yet */
325: for (; usecs > 0; usecs--)
326: for (j = 100; j > 0; j--)
327: ;
328: return;
329: }
330:
331: oclock = tcr1_read();
332:
333: while(1) {
334: clock = tcr1_read();
335: /* timer counts down, not up so old - new */
336: delta = oclock - clock;
337: if (delta > delaycnt)
338: break;
339: }
340: }
341:
342: void
343: setstatclockrate(int newhz)
344: {
345: int minint, statint;
346: int s;
347:
348: s = splclock();
349:
350: statint = ticks_per_second / newhz;
351: /* calculate largest 2^n which is smaller that just over half statint */
352: statvar = 0x40000000; /* really big power of two */
353: minint = statint / 2 + 100;
354: while (statvar > minint)
355: statvar >>= 1;
356:
357: statmin = statint - (statvar >> 1);
358:
359: splx(s);
360:
361: /*
362: * XXX this allows the next stat timer to occur then it switches
363: * to the new frequency. Rather than switching instantly.
364: */
365: }
366:
367: void
368: i80321_calibrate_delay(void)
369: {
370:
371: tmr1_write(0); /* stop timer */
372: tisr_write(TISR_TMR1); /* clear interrupt */
373: trr1_write(0xffffffff); /* reload value */
374: tcr1_write(0xffffffff); /* current value */
375:
376: tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_PRIV|TMRx_CSEL_CORE);
377: }
378:
379: todr_chip_handle_t todr_handle;
380:
381: /*
382: * inittodr:
383: *
384: * Initialize time from the time-of-day register.
385: */
386: #define MINYEAR 2003 /* minimum plausible year */
387: void
388: inittodr(time_t base)
389: {
390: time_t deltat;
391: struct timeval rtctime;
392: struct timespec ts;
393: int badbase;
394:
395: if (base < (MINYEAR - 1970) * SECYR) {
396: printf("WARNING: preposterous time in file system\n");
397: /* read the system clock anyway */
398: base = (MINYEAR - 1970) * SECYR;
399: badbase = 1;
400: } else
401: badbase = 0;
402:
403: if (todr_handle == NULL ||
404: todr_gettime(todr_handle, &rtctime) != 0 ||
405: rtctime.tv_sec == 0) {
406: /*
407: * Believe the time in the file system for lack of
408: * anything better, resetting the TODR.
409: */
410: rtctime.tv_sec = base;
411: rtctime.tv_usec = 0;
412: if (todr_handle != NULL && !badbase) {
413: printf("WARNING: preposterous clock chip time\n");
414: resettodr();
415: }
416: goto bad;
417: }
418:
419: ts.tv_sec = rtctime.tv_sec;
420: ts.tv_nsec = rtctime.tv_usec * 1000;
421: tc_setclock(&ts);
422:
423: if (!badbase) {
424: /*
425: * See if we gained/lost two or more days; if
426: * so, assume something is amiss.
427: */
428: deltat = rtctime.tv_sec - base;
429: if (deltat < 0)
430: deltat = -deltat;
431: if (deltat < 2 * SECDAY)
432: return; /* all is well */
433:
434: printf("WARNING: clock %s %ld days\n",
435: rtctime.tv_sec < base ? "lost" : "gained",
436: (long)deltat / SECDAY);
437: }
438: bad:
439: printf("WARNING: CHECK AND RESET THE DATE!\n");
440: }
441:
442: /*
443: * resettodr:
444: *
445: * Reset the time-of-day register with the current time.
446: */
447: void
448: resettodr(void)
449: {
450: struct timeval rtctime;
451:
452: if (time_second == 0)
453: return;
454:
455: microtime(&rtctime);
456:
457: if (todr_handle != NULL &&
458: todr_settime(todr_handle, &rtctime) != 0)
459: printf("resettodr: failed to set time\n");
460: }
461:
CVSweb