Annotation of qemu/hw/slavio_timer.c, revision 1.1.1.3

1.1       root        1: /*
                      2:  * QEMU Sparc SLAVIO timer controller emulation
                      3:  *
                      4:  * Copyright (c) 2003-2005 Fabrice Bellard
                      5:  * 
                      6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
                      7:  * of this software and associated documentation files (the "Software"), to deal
                      8:  * in the Software without restriction, including without limitation the rights
                      9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     10:  * copies of the Software, and to permit persons to whom the Software is
                     11:  * furnished to do so, subject to the following conditions:
                     12:  *
                     13:  * The above copyright notice and this permission notice shall be included in
                     14:  * all copies or substantial portions of the Software.
                     15:  *
                     16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
                     19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
                     22:  * THE SOFTWARE.
                     23:  */
                     24: #include "vl.h"
                     25: 
                     26: //#define DEBUG_TIMER
                     27: 
                     28: #ifdef DEBUG_TIMER
                     29: #define DPRINTF(fmt, args...) \
                     30: do { printf("TIMER: " fmt , ##args); } while (0)
                     31: #else
                     32: #define DPRINTF(fmt, args...)
                     33: #endif
                     34: 
                     35: /*
                     36:  * Registers of hardware timer in sun4m.
                     37:  *
                     38:  * This is the timer/counter part of chip STP2001 (Slave I/O), also
                     39:  * produced as NCR89C105. See
                     40:  * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
                     41:  * 
                     42:  * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
                     43:  * are zero. Bit 31 is 1 when count has been reached.
                     44:  *
1.1.1.2   root       45:  * Per-CPU timers interrupt local CPU, system timer uses normal
                     46:  * interrupt routing.
                     47:  *
1.1       root       48:  */
                     49: 
                     50: typedef struct SLAVIO_TIMERState {
                     51:     uint32_t limit, count, counthigh;
                     52:     int64_t count_load_time;
                     53:     int64_t expire_time;
                     54:     int64_t stop_time, tick_offset;
                     55:     QEMUTimer *irq_timer;
                     56:     int irq;
                     57:     int reached, stopped;
                     58:     int mode; // 0 = processor, 1 = user, 2 = system
1.1.1.2   root       59:     unsigned int cpu;
1.1       root       60: } SLAVIO_TIMERState;
                     61: 
                     62: #define TIMER_MAXADDR 0x1f
                     63: #define CNT_FREQ 2000000
                     64: 
                     65: // Update count, set irq, update expire_time
                     66: static void slavio_timer_get_out(SLAVIO_TIMERState *s)
                     67: {
                     68:     int out;
                     69:     int64_t diff, ticks, count;
                     70:     uint32_t limit;
                     71: 
                     72:     // There are three clock tick units: CPU ticks, register units
                     73:     // (nanoseconds), and counter ticks (500 ns).
                     74:     if (s->mode == 1 && s->stopped)
                     75:        ticks = s->stop_time;
                     76:     else
                     77:        ticks = qemu_get_clock(vm_clock) - s->tick_offset;
                     78: 
1.1.1.2   root       79:     out = (ticks > s->expire_time);
1.1       root       80:     if (out)
                     81:        s->reached = 0x80000000;
                     82:     if (!s->limit)
                     83:        limit = 0x7fffffff;
                     84:     else
                     85:        limit = s->limit;
                     86: 
                     87:     // Convert register units to counter ticks
                     88:     limit = limit >> 9;
                     89: 
                     90:     // Convert cpu ticks to counter ticks
                     91:     diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec);
                     92: 
                     93:     // Calculate what the counter should be, convert to register
                     94:     // units
                     95:     count = diff % limit;
                     96:     s->count = count << 9;
                     97:     s->counthigh = count >> 22;
                     98: 
                     99:     // Expire time: CPU ticks left to next interrupt
                    100:     // Convert remaining counter ticks to CPU ticks
                    101:     s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ);
                    102: 
1.1.1.3 ! root      103:     DPRINTF("irq %d limit %d reached %d d %" PRId64 " count %d s->c %x diff %" PRId64 " stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode);
1.1       root      104: 
                    105:     if (s->mode != 1)
1.1.1.2   root      106:        pic_set_irq_cpu(s->irq, out, s->cpu);
1.1       root      107: }
                    108: 
                    109: // timer callback
                    110: static void slavio_timer_irq(void *opaque)
                    111: {
                    112:     SLAVIO_TIMERState *s = opaque;
                    113: 
                    114:     if (!s->irq_timer)
                    115:         return;
                    116:     slavio_timer_get_out(s);
                    117:     if (s->mode != 1)
                    118:        qemu_mod_timer(s->irq_timer, s->expire_time);
                    119: }
                    120: 
                    121: static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
                    122: {
                    123:     SLAVIO_TIMERState *s = opaque;
                    124:     uint32_t saddr;
                    125: 
                    126:     saddr = (addr & TIMER_MAXADDR) >> 2;
                    127:     switch (saddr) {
                    128:     case 0:
                    129:        // read limit (system counter mode) or read most signifying
                    130:        // part of counter (user mode)
                    131:        if (s->mode != 1) {
                    132:            // clear irq
1.1.1.2   root      133:            pic_set_irq_cpu(s->irq, 0, s->cpu);
1.1       root      134:            s->count_load_time = qemu_get_clock(vm_clock);
                    135:            s->reached = 0;
                    136:            return s->limit;
                    137:        }
                    138:        else {
                    139:            slavio_timer_get_out(s);
                    140:            return s->counthigh & 0x7fffffff;
                    141:        }
                    142:     case 1:
                    143:        // read counter and reached bit (system mode) or read lsbits
                    144:        // of counter (user mode)
                    145:        slavio_timer_get_out(s);
                    146:        if (s->mode != 1)
                    147:            return (s->count & 0x7fffffff) | s->reached;
                    148:        else
                    149:            return s->count;
                    150:     case 3:
                    151:        // read start/stop status
                    152:        return s->stopped;
                    153:     case 4:
                    154:        // read user/system mode
                    155:        return s->mode & 1;
                    156:     default:
                    157:        return 0;
                    158:     }
                    159: }
                    160: 
                    161: static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
                    162: {
                    163:     SLAVIO_TIMERState *s = opaque;
                    164:     uint32_t saddr;
                    165: 
                    166:     saddr = (addr & TIMER_MAXADDR) >> 2;
                    167:     switch (saddr) {
                    168:     case 0:
                    169:        // set limit, reset counter
                    170:        s->count_load_time = qemu_get_clock(vm_clock);
                    171:        // fall through
                    172:     case 2:
                    173:        // set limit without resetting counter
                    174:        if (!val)
                    175:            s->limit = 0x7fffffff;
                    176:        else
                    177:            s->limit = val & 0x7fffffff;
                    178:        slavio_timer_irq(s);
                    179:        break;
                    180:     case 3:
                    181:        // start/stop user counter
                    182:        if (s->mode == 1) {
                    183:            if (val & 1) {
                    184:                s->stop_time = qemu_get_clock(vm_clock);
                    185:                s->stopped = 1;
                    186:            }
                    187:            else {
                    188:                if (s->stopped)
                    189:                    s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time;
                    190:                s->stopped = 0;
                    191:            }
                    192:        }
                    193:        break;
                    194:     case 4:
                    195:        // bit 0: user (1) or system (0) counter mode
                    196:        if (s->mode == 0 || s->mode == 1)
                    197:            s->mode = val & 1;
                    198:        break;
                    199:     default:
                    200:        break;
                    201:     }
                    202: }
                    203: 
                    204: static CPUReadMemoryFunc *slavio_timer_mem_read[3] = {
                    205:     slavio_timer_mem_readl,
                    206:     slavio_timer_mem_readl,
                    207:     slavio_timer_mem_readl,
                    208: };
                    209: 
                    210: static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = {
                    211:     slavio_timer_mem_writel,
                    212:     slavio_timer_mem_writel,
                    213:     slavio_timer_mem_writel,
                    214: };
                    215: 
                    216: static void slavio_timer_save(QEMUFile *f, void *opaque)
                    217: {
                    218:     SLAVIO_TIMERState *s = opaque;
                    219: 
                    220:     qemu_put_be32s(f, &s->limit);
                    221:     qemu_put_be32s(f, &s->count);
                    222:     qemu_put_be32s(f, &s->counthigh);
                    223:     qemu_put_be64s(f, &s->count_load_time);
                    224:     qemu_put_be64s(f, &s->expire_time);
                    225:     qemu_put_be64s(f, &s->stop_time);
                    226:     qemu_put_be64s(f, &s->tick_offset);
                    227:     qemu_put_be32s(f, &s->irq);
                    228:     qemu_put_be32s(f, &s->reached);
                    229:     qemu_put_be32s(f, &s->stopped);
                    230:     qemu_put_be32s(f, &s->mode);
                    231: }
                    232: 
                    233: static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id)
                    234: {
                    235:     SLAVIO_TIMERState *s = opaque;
                    236:     
                    237:     if (version_id != 1)
                    238:         return -EINVAL;
                    239: 
                    240:     qemu_get_be32s(f, &s->limit);
                    241:     qemu_get_be32s(f, &s->count);
                    242:     qemu_get_be32s(f, &s->counthigh);
                    243:     qemu_get_be64s(f, &s->count_load_time);
                    244:     qemu_get_be64s(f, &s->expire_time);
                    245:     qemu_get_be64s(f, &s->stop_time);
                    246:     qemu_get_be64s(f, &s->tick_offset);
                    247:     qemu_get_be32s(f, &s->irq);
                    248:     qemu_get_be32s(f, &s->reached);
                    249:     qemu_get_be32s(f, &s->stopped);
                    250:     qemu_get_be32s(f, &s->mode);
                    251:     return 0;
                    252: }
                    253: 
                    254: static void slavio_timer_reset(void *opaque)
                    255: {
                    256:     SLAVIO_TIMERState *s = opaque;
                    257: 
                    258:     s->limit = 0;
                    259:     s->count = 0;
                    260:     s->count_load_time = qemu_get_clock(vm_clock);;
                    261:     s->stop_time = s->count_load_time;
                    262:     s->tick_offset = 0;
                    263:     s->reached = 0;
                    264:     s->mode &= 2;
                    265:     s->stopped = 1;
                    266:     slavio_timer_get_out(s);
                    267: }
                    268: 
1.1.1.2   root      269: void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu)
1.1       root      270: {
                    271:     int slavio_timer_io_memory;
                    272:     SLAVIO_TIMERState *s;
                    273: 
                    274:     s = qemu_mallocz(sizeof(SLAVIO_TIMERState));
                    275:     if (!s)
                    276:         return;
                    277:     s->irq = irq;
                    278:     s->mode = mode;
1.1.1.2   root      279:     s->cpu = cpu;
1.1       root      280:     s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s);
                    281: 
                    282:     slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read,
                    283:                                                    slavio_timer_mem_write, s);
                    284:     cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory);
                    285:     register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s);
                    286:     qemu_register_reset(slavio_timer_reset, s);
                    287:     slavio_timer_reset(s);
                    288: }

unix.superglobalmegacorp.com