Annotation of tme/ic/stp22xx/stp222x-timer.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.