|
|
1.1 root 1: /* $Id: stp222x-timer.c,v 1.3 2010/06/05 14:38:00 fredette Exp $ */
2:
3: /* ic/stp22xx/stp222x-timer.c - emulation of the timers of the UPA to
4: SBus interface controller (STP2220) and the UPA to PCI interface
5: controller (STP2222): */
6:
7: /*
8: * Copyright (c) 2009 Matt Fredette
9: * All rights reserved.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by Matt Fredette.
22: * 4. The name of the author may not be used to endorse or promote products
23: * derived from this software without specific prior written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35: * POSSIBILITY OF SUCH DAMAGE.
36: */
37:
38: #include <tme/common.h>
39: _TME_RCSID("$Id: stp222x-timer.c,v 1.3 2010/06/05 14:38:00 fredette Exp $");
40:
41: /* includes: */
42: #include "stp222x-impl.h"
43:
44: /* macros: */
45:
46: /* the counter register: */
47: #define TME_STP222X_TIMER_COUNT_COUNT ((2 << 28) - (1 << 0))
48:
49: /* the limit register: */
50: #define TME_STP222X_TIMER_LIMIT_INT_EN (((tme_uint32_t) 1) << 31)
51: #define TME_STP222X_TIMER_LIMIT_RELOAD TME_BIT(30)
52: #define TME_STP222X_TIMER_LIMIT_PERIODIC TME_BIT(29)
53: #define TME_STP222X_TIMER_LIMIT_LIMIT TME_STP222X_TIMER_COUNT_COUNT
54:
55: /* define this to track interrupt rates, reporting once every N
56: seconds: */
57: #if 1
58: #define TME_STP222X_TIMER_TRACK_INT_RATE (10)
59: #endif
60:
61: /* this updates a timer: */
62: static void
63: _tme_stp222x_timer_update(struct tme_stp222x_timer *timer,
64: struct timeval *now,
65: struct timeval *sleep)
66: {
67:
68: /* get the current time: */
69: gettimeofday(now, NULL);
70:
71: #ifdef TME_STP222X_TIMER_TRACK_INT_RATE
72:
73: /* if the sample time has finished: */
74: if (timer->tme_stp222x_timer_track_sample.tv_sec < now->tv_sec
75: || (timer->tme_stp222x_timer_track_sample.tv_sec == now->tv_sec
76: && timer->tme_stp222x_timer_track_sample.tv_usec <= now->tv_usec)) {
77:
78: /* if the timer has made any interrupts during the sample time: */
79: if (timer->tme_stp222x_timer_track_ints > 0) {
80:
81: /* log the interrupt rate: */
82: tme_log(TME_STP222X_LOG_HANDLE(timer->tme_stp222x_timer_stp222x),
83: 0, TME_OK,
84: (TME_STP222X_LOG_HANDLE(timer->tme_stp222x_timer_stp222x),
85: "timer %d timer interrupt rate: %ld/sec",
86: (timer == &timer->tme_stp222x_timer_stp222x->tme_stp222x_timers[1]),
87: (timer->tme_stp222x_timer_track_ints
88: / (unsigned long) (now->tv_sec
89: - (timer->tme_stp222x_timer_track_sample.tv_sec
90: - TME_STP222X_TIMER_TRACK_INT_RATE)))));
91: }
92:
93: /* reset the sampling: */
94: timer->tme_stp222x_timer_track_ints = 0;
95: timer->tme_stp222x_timer_track_sample = *now;
96: timer->tme_stp222x_timer_track_sample.tv_sec += TME_STP222X_TIMER_TRACK_INT_RATE;
97: }
98:
99: #endif /* TME_STP222X_TIMER_TRACK_INT_RATE */
100:
101: /* if this timer has reached its next limit: */
102: if (__tme_predict_true(timer->tme_stp222x_timer_limit_next.tv_sec < now->tv_sec
103: || (timer->tme_stp222x_timer_limit_next.tv_sec == now->tv_sec
104: && timer->tme_stp222x_timer_limit_next.tv_usec <= now->tv_usec))) {
105:
106: /* if this timer is not in periodic mode: */
107: if ((timer->tme_stp222x_timer_limit & TME_STP222X_TIMER_LIMIT_PERIODIC) == 0) {
108:
109: /* this timer's period is now the maximum: */
110: timer->tme_stp222x_timer_period.tv_sec = ((TME_STP222X_TIMER_COUNT_COUNT + 1) / 1000000);
111: timer->tme_stp222x_timer_period.tv_usec = ((TME_STP222X_TIMER_COUNT_COUNT + 1) % 1000000);
112: }
113:
114: /* set this timer's next limit time: */
115: do {
116: timer->tme_stp222x_timer_limit_next.tv_sec += timer->tme_stp222x_timer_period.tv_sec;
117: timer->tme_stp222x_timer_limit_next.tv_usec += timer->tme_stp222x_timer_period.tv_usec;
118: if (__tme_predict_false(timer->tme_stp222x_timer_limit_next.tv_usec >= 1000000)) {
119: timer->tme_stp222x_timer_limit_next.tv_usec -= 1000000;
120: timer->tme_stp222x_timer_limit_next.tv_sec += 1;
121: }
122: } while (timer->tme_stp222x_timer_limit_next.tv_sec < now->tv_sec
123: || (timer->tme_stp222x_timer_limit_next.tv_sec == now->tv_sec
124: && timer->tme_stp222x_timer_limit_next.tv_usec <= now->tv_usec));
125:
126: /* if this timer's interrupt is enabled: */
127: if (timer->tme_stp222x_timer_limit & TME_STP222X_TIMER_LIMIT_INT_EN) {
128:
129: /* receive an interrupt: */
130: #ifdef TME_STP222X_TIMER_TRACK_INT_RATE
131: timer->tme_stp222x_timer_track_ints++;
132: #endif /* TME_STP222X_TIMER_TRACK_INT_RATE */
133: tme_stp222x_mdu_receive(timer->tme_stp222x_timer_stp222x,
134: timer->tme_stp222x_timer_idi);
135: }
136: }
137:
138: /* sleep until this timer reaches its next limit: */
139: sleep->tv_sec = timer->tme_stp222x_timer_limit_next.tv_sec - now->tv_sec;
140: sleep->tv_usec = timer->tme_stp222x_timer_limit_next.tv_usec - now->tv_usec;
141: if (timer->tme_stp222x_timer_limit_next.tv_usec < now->tv_usec) {
142: sleep->tv_sec--;
143: sleep->tv_usec += 1000000;
144: }
145: }
146:
147: /* the stp222x timer thread: */
148: static void
149: _tme_stp222x_timer_th(void *_timer)
150: {
151: struct tme_stp222x_timer *timer;
152: struct tme_stp222x *stp222x;
153: struct timeval now;
154: struct timeval sleep;
155:
156: /* recover our data structures: */
157: timer = (struct tme_stp222x_timer *) _timer;
158: stp222x = timer->tme_stp222x_timer_stp222x;
159:
160: /* enter: */
161: tme_stp22xx_enter(&stp222x->tme_stp222x);
162:
163: /* loop forever: */
164: for (;;) {
165:
166: /* update this timer: */
167: _tme_stp222x_timer_update(timer, &now, &sleep);
168:
169: /* sleep, but wake up if our timer configuration changes: */
170: tme_stp22xx_cond_sleep_yield(&stp222x->tme_stp222x,
171: &timer->tme_stp222x_timer_cond,
172: &sleep);
173: }
174: /* NOTREACHED */
175: }
176:
177: /* this resets a timer: */
178: static void
179: _tme_stp222x_timer_reset(struct tme_stp222x_timer *timer,
180: tme_uint32_t count)
181: {
182: tme_uint32_t limit;
183: tme_uint32_t period;
184:
185: /* get the timer's initial period, until the count register next
186: reaches the limit. NB that we assume that if count and limit are
187: already equal now, that does not count as reaching the limit, and
188: that a complete timer period is needed: */
189: limit = TME_FIELD_MASK_EXTRACTU(timer->tme_stp222x_timer_limit, TME_STP222X_TIMER_LIMIT_LIMIT);
190: period = ((limit - (count + 1)) & TME_STP222X_TIMER_COUNT_COUNT) + 1;
191:
192: /* save this timer's initial period: */
193: timer->tme_stp222x_timer_period.tv_sec = 0;
194: if (__tme_predict_false(period >= 1000000)) {
195: timer->tme_stp222x_timer_period.tv_sec = period / 1000000;
196: period %= 1000000;
197: }
198: timer->tme_stp222x_timer_period.tv_usec = period;
199:
200: /* set the next limit time for this timer: */
201: gettimeofday(&timer->tme_stp222x_timer_limit_next, NULL);
202: timer->tme_stp222x_timer_limit_next.tv_sec += timer->tme_stp222x_timer_period.tv_sec;
203: timer->tme_stp222x_timer_limit_next.tv_usec += timer->tme_stp222x_timer_period.tv_usec;
204: if (timer->tme_stp222x_timer_limit_next.tv_usec >= 1000000) {
205: timer->tme_stp222x_timer_limit_next.tv_usec -= 1000000;
206: timer->tme_stp222x_timer_limit_next.tv_sec += 1;
207: }
208: }
209:
210: /* this reads a timer's count register: */
211: static tme_uint32_t
212: _tme_stp222x_timer_count(struct tme_stp222x_timer *timer)
213: {
214: struct timeval now;
215: struct timeval sleep;
216: tme_uint32_t count;
217:
218: /* update the timers: */
219: _tme_stp222x_timer_update(timer, &now, &sleep);
220:
221: /* get the absolute count until the next limit: */
222: count = sleep.tv_sec;
223: count *= 1000000;
224: count += sleep.tv_usec;
225:
226: /* the count register value is that distance to the limit
227: register: */
228: return ((timer->tme_stp222x_timer_limit
229: - count)
230: & TME_STP222X_TIMER_COUNT_COUNT);
231: }
232:
233: /* the timer register handler: */
234: void
235: tme_stp222x_timer_regs(struct tme_stp222x *stp222x,
236: struct tme_stp222x_reg *reg)
237: {
238: unsigned int timer_i;
239: struct tme_stp222x_timer *timer;
240: tme_uint32_t reg_address;
241: tme_uint32_t count;
242: tme_uint32_t limit;
243:
244: /* get the timer and register accessed: */
245: reg_address = reg->tme_stp222x_reg_address;
246: timer_i
247: = ((reg_address
248: / TME_STP222X_TIMER_SIZE)
249: % TME_ARRAY_ELS(stp222x->tme_stp222x_timers));
250: timer = &stp222x->tme_stp222x_timers[timer_i];
251: reg_address %= TME_STP222X_TIMER_SIZE;
252:
253: /* if this is a write: */
254: if (reg->tme_stp222x_reg_write) {
255:
256: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
257: (TME_STP222X_LOG_HANDLE(stp222x),
258: _("timer #%d %s <- 0x%08" TME_PRIx64),
259: timer_i,
260: (reg_address == TME_STP222X_TIMER_COUNT
261: ? "count"
262: : "limit"),
263: reg->tme_stp222x_reg_value));
264:
265: /* dispatch on the register: */
266: switch (reg_address) {
267: case TME_STP222X_TIMER_COUNT:
268:
269: /* get the new count register value: */
270: count
271: = (((tme_uint32_t) reg->tme_stp222x_reg_value)
272: & TME_STP222X_TIMER_COUNT_COUNT);
273: break;
274:
275: case TME_STP222X_TIMER_LIMIT:
276:
277: /* get the new limit register value: */
278: limit
279: = (((tme_uint32_t) reg->tme_stp222x_reg_value)
280: & (TME_STP222X_TIMER_LIMIT_LIMIT
281: | TME_STP222X_TIMER_LIMIT_INT_EN
282: | TME_STP222X_TIMER_LIMIT_RELOAD
283: | TME_STP222X_TIMER_LIMIT_PERIODIC));
284:
285: /* get the new count register value: */
286: count
287: = ((limit & TME_STP222X_TIMER_LIMIT_RELOAD)
288: ? 0
289: : _tme_stp222x_timer_count(timer));
290:
291: /* write the timer's limit register: */
292: timer->tme_stp222x_timer_limit = limit & ~TME_STP222X_TIMER_LIMIT_RELOAD;
293: break;
294:
295: default: return;
296: }
297:
298: /* reset this timer: */
299: _tme_stp222x_timer_reset(timer, count);
300:
301: /* wake up the thread for this timer: */
302: tme_stp22xx_cond_notify(&timer->tme_stp222x_timer_cond);
303: }
304:
305: /* otherwise, this must be a read: */
306: else {
307:
308: /* dispatch on the register: */
309: switch (reg_address) {
310: case TME_STP222X_TIMER_COUNT:
311:
312: /* read this timer's count register: */
313: reg->tme_stp222x_reg_value = _tme_stp222x_timer_count(timer);
314: break;
315:
316: case TME_STP222X_TIMER_LIMIT:
317:
318: /* read this timer's limit register: */
319: reg->tme_stp222x_reg_value = timer->tme_stp222x_timer_limit;
320: break;
321:
322: default: return;
323: }
324:
325: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
326: (TME_STP222X_LOG_HANDLE(stp222x),
327: _("timer #%d %s -> 0x%08" TME_PRIx64),
328: timer_i,
329: (reg_address == TME_STP222X_TIMER_COUNT
330: ? "count"
331: : "limit"),
332: reg->tme_stp222x_reg_value));
333: }
334:
335: /* this register access has been completed: */
336: reg->tme_stp222x_reg_completed = TRUE;
337: }
338:
339: /* this initializes an stp222x timer: */
340: void
341: tme_stp222x_timer_init(struct tme_stp222x *stp222x,
342: struct tme_stp222x_timer *timer)
343: {
344:
345: /* initialize and reset the timer: */
346: timer->tme_stp222x_timer_stp222x = stp222x;
347: tme_stp22xx_cond_init(&timer->tme_stp222x_timer_cond);
348: _tme_stp222x_timer_reset(timer, 0);
349:
350: /* start the thread for this timer: */
351: tme_thread_create(_tme_stp222x_timer_th, timer);
352:
353: /* set the IDI for this timer: */
354: timer->tme_stp222x_timer_idi
355: = (TME_STP222X_IS_2220(stp222x)
356: ? TME_STP2220_IDI_TIMER(timer
357: == &timer->tme_stp222x_timer_stp222x->tme_stp222x_timers[1])
358: : TME_STP2222_IDI_TIMER(timer
359: == &timer->tme_stp222x_timer_stp222x->tme_stp222x_timers[1]));;
360: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.