|
|
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.