|
|
1.1 root 1: /* $Id: sun4-timer.c,v 1.2 2006/11/16 02:42:16 fredette Exp $ */
2:
3: /* machine/sun4/sun4-timer.c - implementation of Sun 4 timer emulation: */
4:
5: /*
6: * Copyright (c) 2006 Matt Fredette
7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Matt Fredette.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33: * POSSIBILITY OF SUCH DAMAGE.
34: */
35:
36: #include <tme/common.h>
37: _TME_RCSID("$Id: sun4-timer.c,v 1.2 2006/11/16 02:42:16 fredette Exp $");
38:
39: /* includes: */
40: #include <tme/generic/bus-device.h>
41: #include "sun4-impl.h"
42:
43: /* macros: */
44:
45: /* real sun4/4c timer bits: */
46: #define TME_SUN4_32_TIMER_LIMIT TME_BIT(31)
47: #define TME_SUN44C_TIMER_MASK (0x7ffffc00)
48: #define TME_SUN4M_TIMER_MASK (0x7ffffe00)
49:
50: /* define this to track interrupt rates, reporting once every N
51: seconds: */
52: #if 1
53: #define TME_SUN4_TIMER_TRACK_INT_RATE (10)
54: #endif
55:
56: /* this makes timer callouts. it must be called with the mutex held: */
57: static void
58: _tme_sun4_timer_callout(struct tme_sun4 *sun4)
59: {
60: struct tme_bus_connection *conn_bus;
61: struct tme_sun4_timer *timer;
62: unsigned int int_asserted;
63: int again;
64: int rc;
65:
66: /* if this function is already running in another thread, return
67: now. the other thread will do our work: */
68: if (sun4->tme_sun4_timer_callouts_running) {
69: return;
70: }
71:
72: /* callouts are now running: */
73: sun4->tme_sun4_timer_callouts_running = TRUE;
74:
75: /* get our bus connection: */
76: conn_bus = sun4->tme_sun4_buses[TME_SUN4_32_CONN_REG_TIMER];
77:
78: /* loop forever: */
79: for (again = TRUE; again;) {
80: again = FALSE;
81:
82: /* check all of the timers for changes: */
83: timer = &sun4->tme_sun4_timers[0];
84: do {
85:
86: /* if this timer needs an interrupt callout: */
87: int_asserted = (timer->tme_sun4_timer_counter & TME_SUN4_32_TIMER_LIMIT) != 0;
88: if (!int_asserted
89: != !timer->tme_sun4_timer_int_asserted) {
90:
91: /* unlock our mutex: */
92: tme_mutex_unlock(&sun4->tme_sun4_mutex);
93:
94: /* call out the bus interrupt signal edge: */
95: rc = (*conn_bus->tme_bus_signal)
96: (conn_bus,
97: ((timer == &sun4->tme_sun4_timers[0]
98: ? TME_BUS_SIGNAL_INT(10)
99: : TME_BUS_SIGNAL_INT(14))
100: | (int_asserted
101: ? TME_BUS_SIGNAL_LEVEL_ASSERTED
102: : TME_BUS_SIGNAL_LEVEL_NEGATED)));
103:
104: /* lock our mutex: */
105: tme_mutex_lock(&sun4->tme_sun4_mutex);
106:
107: /* if this callout was successful, note the new state of the
108: interrupt signal: */
109: if (rc == TME_OK) {
110: timer->tme_sun4_timer_int_asserted = int_asserted;
111: again = TRUE;
112: }
113:
114: /* otherwise, abort: */
115: else {
116: abort();
117: }
118: }
119: } while (++timer != (&sun4->tme_sun4_timers[0] + TME_ARRAY_ELS(sun4->tme_sun4_timers)));
120:
121: }
122:
123: /* there are no more callouts to make: */
124: sun4->tme_sun4_timer_callouts_running = FALSE;
125: }
126:
127: /* this can be called to force an immediate timer interrupt: */
128: void
129: _tme_sun4_timer_int_force(struct tme_sun4 *sun4,
130: struct tme_sun4_timer *timer)
131: {
132:
133: /* lock our mutex: */
134: tme_mutex_lock(&sun4->tme_sun4_mutex);
135:
136: /* force an immediate timer interrupt: */
137: timer->tme_sun4_timer_counter = TME_SUN4_32_TIMER_LIMIT;
138: timer->tme_sun4_timer_limit |= TME_SUN4_32_TIMER_LIMIT;
139:
140: /* call out any interrupts: */
141: _tme_sun4_timer_callout(sun4);
142:
143: /* unlock our mutex: */
144: tme_mutex_unlock(&sun4->tme_sun4_mutex);
145: }
146:
147: /* the sun4 timer update function. it must be called with the mutex
148: locked: */
149: static void
150: _tme_sun4_timer_update(struct tme_sun4_timer *timer, struct timeval *now, struct timeval *sleep)
151: {
152:
153: /* get the current time: */
154: gettimeofday(now, NULL);
155:
156: #ifdef TME_SUN4_TIMER_TRACK_INT_RATE
157:
158: /* if the sample time has finished: */
159: if (timer->tme_sun4_timer_track_sample.tv_sec < now->tv_sec
160: || (timer->tme_sun4_timer_track_sample.tv_sec == now->tv_sec
161: && timer->tme_sun4_timer_track_sample.tv_usec <= now->tv_usec)) {
162:
163: /* if the timer has made any interrupts during the sample time: */
164: if (timer->tme_sun4_timer_track_ints > 0) {
165:
166: /* log the interrupt rate: */
167: tme_log(TME_SUN4_LOG_HANDLE(timer->tme_sun4_timer_sun4),
168: 0, TME_OK,
169: (TME_SUN4_LOG_HANDLE(timer->tme_sun4_timer_sun4),
170: "level %d timer interrupt rate: %ld/sec",
171: (timer == &timer->tme_sun4_timer_sun4->tme_sun4_timers[0]
172: ? 10
173: : 14),
174: (timer->tme_sun4_timer_track_ints
175: / (now->tv_sec
176: - (timer->tme_sun4_timer_track_sample.tv_sec
177: - TME_SUN4_TIMER_TRACK_INT_RATE)))));
178: }
179:
180: /* reset the sampling: */
181: timer->tme_sun4_timer_track_ints = 0;
182: timer->tme_sun4_timer_track_sample = *now;
183: timer->tme_sun4_timer_track_sample.tv_sec += TME_SUN4_TIMER_TRACK_INT_RATE;
184: }
185:
186: #endif /* TME_SUN4_TIMER_TRACK_INT_RATE */
187:
188: /* if this timer has not reached its next limit: */
189: if (__tme_predict_false(timer->tme_sun4_timer_limit_next.tv_sec > now->tv_sec
190: || (timer->tme_sun4_timer_limit_next.tv_sec == now->tv_sec
191: && timer->tme_sun4_timer_limit_next.tv_usec > now->tv_usec))) {
192:
193: /* sleep until this timer reaches its next limit: */
194: sleep->tv_sec = timer->tme_sun4_timer_limit_next.tv_sec - now->tv_sec;
195: sleep->tv_usec = timer->tme_sun4_timer_limit_next.tv_usec - now->tv_usec;
196: if (timer->tme_sun4_timer_limit_next.tv_usec < now->tv_usec) {
197: sleep->tv_sec--;
198: sleep->tv_usec += 1000000;
199: }
200: return;
201: }
202:
203: /* set this timer's next limit time: */
204: do {
205: timer->tme_sun4_timer_limit_next.tv_sec += timer->tme_sun4_timer_period.tv_sec;
206: timer->tme_sun4_timer_limit_next.tv_usec += timer->tme_sun4_timer_period.tv_usec;
207: if (__tme_predict_false(timer->tme_sun4_timer_limit_next.tv_usec >= 1000000)) {
208: timer->tme_sun4_timer_limit_next.tv_usec -= 1000000;
209: timer->tme_sun4_timer_limit_next.tv_sec += 1;
210: }
211: } while (timer->tme_sun4_timer_limit_next.tv_sec < now->tv_sec
212: || (timer->tme_sun4_timer_limit_next.tv_sec == now->tv_sec
213: && timer->tme_sun4_timer_limit_next.tv_usec <= now->tv_usec));
214:
215: /* mark this timer as having reached its limit: */
216: #ifdef TME_SUN4_TIMER_TRACK_INT_RATE
217: if (!(timer->tme_sun4_timer_counter
218: & TME_SUN4_32_TIMER_LIMIT)) {
219: timer->tme_sun4_timer_track_ints++;
220: }
221: #endif /* TME_SUN4_TIMER_TRACK_INT_RATE */
222: timer->tme_sun4_timer_counter = TME_SUN4_32_TIMER_LIMIT;
223: timer->tme_sun4_timer_limit |= TME_SUN4_32_TIMER_LIMIT;
224:
225: /* sleep for the normal period: */
226: *sleep = timer->tme_sun4_timer_period;
227: }
228:
229: /* this resets a timer: */
230: static void
231: _tme_sun4_timer_reset(struct tme_sun4_timer *timer)
232: {
233: tme_uint32_t counter_one;
234: tme_uint32_t ticks;
235: tme_uint32_t ticks_max;
236: tme_uint32_t usecs;
237:
238: /* to keep things simpler, we always use the sun4m 500ns tick: */
239: counter_one = TME_SUN4_IS_SUN44C(timer->tme_sun4_timer_sun4) ? 2 : 1;
240: ticks_max = (TME_SUN4M_TIMER_MASK / _TME_FIELD_MASK_FACTOR(TME_SUN4M_TIMER_MASK)) + 1;
241:
242: /* get this timer's period, in 500ns ticks. NB that we account for
243: the fact that timers count from [1..limit), and not [0..limit): */
244: /* XXX FIXME - we assume that the limit value of one gives the
245: longest possible period. is this right? */
246: ticks = TME_FIELD_MASK_EXTRACTU(timer->tme_sun4_timer_limit, TME_SUN4M_TIMER_MASK);
247: ticks = (ticks - counter_one) & (ticks_max - counter_one);
248: if (__tme_predict_false(ticks == 0)) {
249: ticks = ticks_max;
250: }
251:
252: /* convert the timer's period from 500ns ticks to a struct timeval
253: and save it: */
254: usecs = ticks / 2;
255: timer->tme_sun4_timer_period.tv_sec = 0;
256: if (__tme_predict_false(usecs >= 1000000)) {
257: timer->tme_sun4_timer_period.tv_sec = usecs / 1000000;
258: usecs %= 1000000;
259: }
260: timer->tme_sun4_timer_period.tv_usec = usecs;
261:
262: /* set the next limit time for this timer: */
263: gettimeofday(&timer->tme_sun4_timer_limit_next, NULL);
264: timer->tme_sun4_timer_limit_next.tv_sec += timer->tme_sun4_timer_period.tv_sec;
265: timer->tme_sun4_timer_limit_next.tv_usec += timer->tme_sun4_timer_period.tv_usec;
266: if (timer->tme_sun4_timer_limit_next.tv_usec >= 1000000) {
267: timer->tme_sun4_timer_limit_next.tv_usec -= 1000000;
268: timer->tme_sun4_timer_limit_next.tv_sec += 1;
269: }
270: }
271:
272: /* the sun4 timer thread: */
273: static void
274: _tme_sun4_timer_th(struct tme_sun4_timer *timer)
275: {
276: struct tme_sun4 *sun4;
277: struct timeval now;
278: struct timeval sleep;
279:
280: /* recover our sun4: */
281: sun4 = timer->tme_sun4_timer_sun4;
282:
283: /* lock our mutex: */
284: tme_mutex_lock(&sun4->tme_sun4_mutex);
285:
286: /* loop forever: */
287: for (;;) {
288:
289: /* update this timer: */
290: _tme_sun4_timer_update(timer, &now, &sleep);
291:
292: /* call out any interrupts: */
293: _tme_sun4_timer_callout(sun4);
294:
295: /* sleep, but wake up if our timer configuration changes: */
296: tme_cond_sleep_yield(&timer->tme_sun4_timer_cond,
297: &sun4->tme_sun4_mutex,
298: &sleep);
299: }
300: /* NOTREACHED */
301: }
302:
303: /* the sun4 timer control bus cycle handler: */
304: int
305: _tme_sun4_timer_cycle_control(void *_sun4, struct tme_bus_cycle *cycle_init)
306: {
307: struct tme_sun4 *sun4;
308: unsigned int timer_i;
309: struct tme_sun4_timer *timer;
310: tme_uint32_t reg;
311: tme_uint32_t value32;
312: struct tme_bus_cycle cycle_resp;
313: struct timeval now;
314: struct timeval last_reset;
315: tme_uint32_t counter_one;
316: tme_uint32_t usecs;
317: tme_uint32_t ticks;
318:
319: /* recover our sun4: */
320: sun4 = (struct tme_sun4 *) _sun4;
321:
322: /* this must be a full 32-bit register access: */
323: if ((cycle_init->tme_bus_cycle_address % sizeof(tme_uint32_t)) != 0
324: || cycle_init->tme_bus_cycle_size != sizeof(tme_uint32_t)) {
325: abort();
326: }
327:
328: /* get the timer and register accessed: */
329: if (TME_SUN4_IS_SUN44C(sun4)) {
330: timer_i = cycle_init->tme_bus_cycle_address / TME_SUN44C_TIMER_SIZ_REG;
331: reg = cycle_init->tme_bus_cycle_address & TME_SUN4_32_TIMER_SIZ_COUNTER;
332: }
333: else {
334: abort();
335: }
336: timer = &sun4->tme_sun4_timers[timer_i];
337:
338: /* lock our mutex: */
339: tme_mutex_lock(&sun4->tme_sun4_mutex);
340:
341: /* if this is a read: */
342: if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
343:
344: /* dispatch on the register: */
345: switch (reg) {
346: default: assert(FALSE);
347: case TME_SUN4_32_TIMER_REG_COUNTER:
348:
349: /* update the timers: */
350: _tme_sun4_timer_update(timer, &now, &last_reset);
351:
352: /* get the time of the last reset: */
353: last_reset = timer->tme_sun4_timer_limit_next;
354: if (last_reset.tv_usec < timer->tme_sun4_timer_period.tv_usec) {
355: last_reset.tv_sec -= 1;
356: last_reset.tv_usec += 1000000;
357: }
358: last_reset.tv_sec -= timer->tme_sun4_timer_period.tv_sec;
359: last_reset.tv_usec -= timer->tme_sun4_timer_period.tv_usec;
360:
361: /* get the number of microseconds since the last reset: */
362: usecs = now.tv_sec - last_reset.tv_sec;
363: usecs *= 1000000;
364: usecs
365: += (((tme_int32_t) now.tv_usec)
366: - ((tme_int32_t) last_reset.tv_usec));
367:
368: /* to keep things simpler, we always use the sun4m 500ns tick: */
369: counter_one = TME_SUN4_IS_SUN44C(sun4) ? 2 : 1;
370:
371: /* convert the number of microseconds until this timer resets again,
372: to the 500ns tick counter value for the timer. NB that we
373: account for the fact that timers count from [1..limit), and not
374: [0..limit): */
375: ticks = (usecs * 2) + counter_one;
376: TME_FIELD_MASK_DEPOSITU(timer->tme_sun4_timer_counter,
377: TME_SUN4M_TIMER_MASK,
378: ticks);
379:
380: /* read this timer's counter register: */
381: value32 = timer->tme_sun4_timer_counter;
382: break;
383:
384: case TME_SUN4_32_TIMER_REG_LIMIT:
385:
386: /* read this timer's limit register: */
387: value32 = timer->tme_sun4_timer_limit;
388:
389: /* a read of the limit register is used to acknowledge an
390: interrupt, which probably means clearing the limit bit on the
391: counter register: */
392: timer->tme_sun4_timer_counter = 0;
393: timer->tme_sun4_timer_limit &= ~TME_SUN4_32_TIMER_LIMIT;
394: break;
395: }
396:
397: tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK,
398: (TME_SUN4_LOG_HANDLE(sun4),
399: _("timer #%d %s -> 0x%08x"),
400: timer_i,
401: (reg == TME_SUN4_32_TIMER_REG_COUNTER
402: ? "counter"
403: : "limit"),
404: value32));
405:
406: /* byteswap the register value: */
407: value32 = tme_htobe_u32(value32);
408: }
409:
410: /* run the bus cycle: */
411: cycle_resp.tme_bus_cycle_buffer = (tme_uint8_t *) &value32;
412: cycle_resp.tme_bus_cycle_buffer_increment = 1;
413: cycle_resp.tme_bus_cycle_lane_routing = cycle_init->tme_bus_cycle_lane_routing;
414: cycle_resp.tme_bus_cycle_address = 0;
415: cycle_resp.tme_bus_cycle_type = (cycle_init->tme_bus_cycle_type
416: ^ (TME_BUS_CYCLE_WRITE
417: | TME_BUS_CYCLE_READ));
418: cycle_resp.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS32_LOG2);
419: tme_bus_cycle_xfer(cycle_init, &cycle_resp);
420:
421: /* if this is a write: */
422: if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
423:
424: /* byteswap the register value: */
425: value32 = tme_htobe_u32(value32);
426:
427: tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK,
428: (TME_SUN4_LOG_HANDLE(sun4),
429: _("timer #%d %s <- 0x%08x"),
430: timer_i,
431: (reg == TME_SUN4_32_TIMER_REG_COUNTER
432: ? "counter"
433: : "limit"),
434: value32));
435:
436: /* dispatch on the register: */
437: switch (reg) {
438: default: assert(FALSE);
439: case TME_SUN4_32_TIMER_REG_COUNTER:
440: abort();
441:
442: case TME_SUN4_32_TIMER_REG_LIMIT:
443:
444: /* write the timer's limit register: */
445: timer->tme_sun4_timer_limit = value32;
446:
447: /* reset this timer: */
448: _tme_sun4_timer_reset(timer);
449:
450: /* wake up the thread for this timer: */
451: tme_cond_notify(&timer->tme_sun4_timer_cond, FALSE);
452: break;
453: }
454: }
455:
456: /* make any callouts: */
457: _tme_sun4_timer_callout(sun4);
458:
459: /* unlock the mutex: */
460: tme_mutex_unlock(&sun4->tme_sun4_mutex);
461:
462: /* no faults: */
463: return (TME_OK);
464: }
465:
466: /* this creates the sun4 timers: */
467: void
468: _tme_sun4_timer_new(struct tme_sun4 *sun4)
469: {
470: struct tme_sun4_timer *timer;
471:
472: /* loop over the timers: */
473: timer = &sun4->tme_sun4_timers[0];
474: do {
475:
476: /* initialize and reset the timer: */
477: timer->tme_sun4_timer_sun4 = sun4;
478: tme_cond_init(&timer->tme_sun4_timer_cond);
479: _tme_sun4_timer_reset(timer);
480:
481: /* start the thread for this timer: */
482: tme_thread_create((tme_thread_t) _tme_sun4_timer_th, timer);
483:
484: } while (++timer != (&sun4->tme_sun4_timers[0] + TME_ARRAY_ELS(sun4->tme_sun4_timers)));
485: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.