Annotation of qemu/hw/sh_timer.c, revision 1.1.1.2

1.1       root        1: /*
                      2:  * SuperH Timer modules.
                      3:  *
                      4:  * Copyright (c) 2007 Magnus Damm
                      5:  * Based on arm_timer.c by Paul Brook
                      6:  * Copyright (c) 2005-2006 CodeSourcery.
                      7:  *
                      8:  * This code is licenced under the GPL.
                      9:  */
                     10: 
                     11: #include "hw.h"
                     12: #include "sh.h"
                     13: #include "qemu-timer.h"
                     14: 
                     15: //#define DEBUG_TIMER
                     16: 
                     17: #define TIMER_TCR_TPSC          (7 << 0)
                     18: #define TIMER_TCR_CKEG          (3 << 3)
                     19: #define TIMER_TCR_UNIE          (1 << 5)
                     20: #define TIMER_TCR_ICPE          (3 << 6)
                     21: #define TIMER_TCR_UNF           (1 << 8)
                     22: #define TIMER_TCR_ICPF          (1 << 9)
                     23: #define TIMER_TCR_RESERVED      (0x3f << 10)
                     24: 
                     25: #define TIMER_FEAT_CAPT   (1 << 0)
                     26: #define TIMER_FEAT_EXTCLK (1 << 1)
                     27: 
1.1.1.2 ! root       28: #define OFFSET_TCOR   0
        !            29: #define OFFSET_TCNT   1
        !            30: #define OFFSET_TCR    2
        !            31: #define OFFSET_TCPR   3
        !            32: 
1.1       root       33: typedef struct {
                     34:     ptimer_state *timer;
                     35:     uint32_t tcnt;
                     36:     uint32_t tcor;
                     37:     uint32_t tcr;
                     38:     uint32_t tcpr;
                     39:     int freq;
                     40:     int int_level;
                     41:     int old_level;
                     42:     int feat;
                     43:     int enabled;
1.1.1.2 ! root       44:     qemu_irq irq;
1.1       root       45: } sh_timer_state;
                     46: 
                     47: /* Check all active timers, and schedule the next timer interrupt. */
                     48: 
                     49: static void sh_timer_update(sh_timer_state *s)
                     50: {
                     51:     int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
                     52: 
                     53:     if (new_level != s->old_level)
1.1.1.2 ! root       54:       qemu_set_irq (s->irq, new_level);
1.1       root       55: 
                     56:     s->old_level = s->int_level;
                     57:     s->int_level = new_level;
                     58: }
                     59: 
                     60: static uint32_t sh_timer_read(void *opaque, target_phys_addr_t offset)
                     61: {
                     62:     sh_timer_state *s = (sh_timer_state *)opaque;
                     63: 
                     64:     switch (offset >> 2) {
1.1.1.2 ! root       65:     case OFFSET_TCOR:
1.1       root       66:         return s->tcor;
1.1.1.2 ! root       67:     case OFFSET_TCNT:
1.1       root       68:         return ptimer_get_count(s->timer);
1.1.1.2 ! root       69:     case OFFSET_TCR:
1.1       root       70:         return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
1.1.1.2 ! root       71:     case OFFSET_TCPR:
1.1       root       72:         if (s->feat & TIMER_FEAT_CAPT)
                     73:             return s->tcpr;
                     74:     default:
                     75:         cpu_abort (cpu_single_env, "sh_timer_read: Bad offset %x\n",
                     76:                    (int)offset);
                     77:         return 0;
                     78:     }
                     79: }
                     80: 
                     81: static void sh_timer_write(void *opaque, target_phys_addr_t offset,
                     82:                             uint32_t value)
                     83: {
                     84:     sh_timer_state *s = (sh_timer_state *)opaque;
                     85:     int freq;
                     86: 
                     87:     switch (offset >> 2) {
1.1.1.2 ! root       88:     case OFFSET_TCOR:
1.1       root       89:         s->tcor = value;
                     90:         ptimer_set_limit(s->timer, s->tcor, 0);
                     91:         break;
1.1.1.2 ! root       92:     case OFFSET_TCNT:
1.1       root       93:         s->tcnt = value;
                     94:         ptimer_set_count(s->timer, s->tcnt);
                     95:         break;
1.1.1.2 ! root       96:     case OFFSET_TCR:
1.1       root       97:         if (s->enabled) {
                     98:             /* Pause the timer if it is running.  This may cause some
                     99:                inaccuracy dure to rounding, but avoids a whole lot of other
                    100:                messyness.  */
                    101:             ptimer_stop(s->timer);
                    102:         }
                    103:         freq = s->freq;
                    104:         /* ??? Need to recalculate expiry time after changing divisor.  */
                    105:         switch (value & TIMER_TCR_TPSC) {
                    106:         case 0: freq >>= 2; break;
                    107:         case 1: freq >>= 4; break;
                    108:         case 2: freq >>= 6; break;
                    109:         case 3: freq >>= 8; break;
                    110:         case 4: freq >>= 10; break;
                    111:        case 6:
                    112:        case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
                    113:        default: cpu_abort (cpu_single_env,
                    114:                           "sh_timer_write: Reserved TPSC value\n"); break;
                    115:         }
                    116:         switch ((value & TIMER_TCR_CKEG) >> 3) {
                    117:        case 0: break;
                    118:         case 1:
                    119:         case 2:
                    120:         case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
                    121:        default: cpu_abort (cpu_single_env,
                    122:                           "sh_timer_write: Reserved CKEG value\n"); break;
                    123:         }
                    124:         switch ((value & TIMER_TCR_ICPE) >> 6) {
                    125:        case 0: break;
                    126:         case 2:
                    127:         case 3: if (s->feat & TIMER_FEAT_CAPT) break;
                    128:        default: cpu_abort (cpu_single_env,
                    129:                           "sh_timer_write: Reserved ICPE value\n"); break;
                    130:         }
                    131:        if ((value & TIMER_TCR_UNF) == 0)
                    132:             s->int_level = 0;
                    133: 
                    134:        value &= ~TIMER_TCR_UNF;
                    135: 
                    136:        if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
                    137:             cpu_abort (cpu_single_env,
                    138:                       "sh_timer_write: Reserved ICPF value\n");
                    139: 
                    140:        value &= ~TIMER_TCR_ICPF; /* capture not supported */
                    141: 
                    142:        if (value & TIMER_TCR_RESERVED)
                    143:             cpu_abort (cpu_single_env,
                    144:                       "sh_timer_write: Reserved TCR bits set\n");
                    145:         s->tcr = value;
                    146:         ptimer_set_limit(s->timer, s->tcor, 0);
                    147:         ptimer_set_freq(s->timer, freq);
                    148:         if (s->enabled) {
                    149:             /* Restart the timer if still enabled.  */
                    150:             ptimer_run(s->timer, 0);
                    151:         }
                    152:         break;
1.1.1.2 ! root      153:     case OFFSET_TCPR:
1.1       root      154:         if (s->feat & TIMER_FEAT_CAPT) {
                    155:             s->tcpr = value;
                    156:            break;
                    157:        }
                    158:     default:
                    159:         cpu_abort (cpu_single_env, "sh_timer_write: Bad offset %x\n",
                    160:                    (int)offset);
                    161:     }
                    162:     sh_timer_update(s);
                    163: }
                    164: 
                    165: static void sh_timer_start_stop(void *opaque, int enable)
                    166: {
                    167:     sh_timer_state *s = (sh_timer_state *)opaque;
                    168: 
                    169: #ifdef DEBUG_TIMER
                    170:     printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
                    171: #endif
                    172: 
                    173:     if (s->enabled && !enable) {
                    174:         ptimer_stop(s->timer);
                    175:     }
                    176:     if (!s->enabled && enable) {
                    177:         ptimer_run(s->timer, 0);
                    178:     }
                    179:     s->enabled = !!enable;
                    180: 
                    181: #ifdef DEBUG_TIMER
                    182:     printf("sh_timer_start_stop done %d\n", s->enabled);
                    183: #endif
                    184: }
                    185: 
                    186: static void sh_timer_tick(void *opaque)
                    187: {
                    188:     sh_timer_state *s = (sh_timer_state *)opaque;
                    189:     s->int_level = s->enabled;
                    190:     sh_timer_update(s);
                    191: }
                    192: 
1.1.1.2 ! root      193: static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
1.1       root      194: {
                    195:     sh_timer_state *s;
                    196:     QEMUBH *bh;
                    197: 
                    198:     s = (sh_timer_state *)qemu_mallocz(sizeof(sh_timer_state));
                    199:     s->freq = freq;
                    200:     s->feat = feat;
                    201:     s->tcor = 0xffffffff;
                    202:     s->tcnt = 0xffffffff;
                    203:     s->tcpr = 0xdeadbeef;
1.1.1.2 ! root      204:     s->tcr = 0;
1.1       root      205:     s->enabled = 0;
                    206:     s->irq = irq;
                    207: 
                    208:     bh = qemu_bh_new(sh_timer_tick, s);
                    209:     s->timer = ptimer_init(bh);
1.1.1.2 ! root      210: 
        !           211:     sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
        !           212:     sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
        !           213:     sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
        !           214:     sh_timer_write(s, OFFSET_TCR  >> 2, s->tcpr);
1.1       root      215:     /* ??? Save/restore.  */
                    216:     return s;
                    217: }
                    218: 
                    219: typedef struct {
                    220:     void *timer[3];
                    221:     int level[3];
                    222:     uint32_t tocr;
                    223:     uint32_t tstr;
                    224:     int feat;
                    225: } tmu012_state;
                    226: 
                    227: static uint32_t tmu012_read(void *opaque, target_phys_addr_t offset)
                    228: {
                    229:     tmu012_state *s = (tmu012_state *)opaque;
                    230: 
                    231: #ifdef DEBUG_TIMER
                    232:     printf("tmu012_read 0x%lx\n", (unsigned long) offset);
                    233: #endif
                    234: 
                    235:     if (offset >= 0x20) {
                    236:         if (!(s->feat & TMU012_FEAT_3CHAN))
                    237:            cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n",
                    238:                       (int)offset);
                    239:         return sh_timer_read(s->timer[2], offset - 0x20);
                    240:     }
                    241: 
                    242:     if (offset >= 0x14)
                    243:         return sh_timer_read(s->timer[1], offset - 0x14);
                    244: 
                    245:     if (offset >= 0x08)
                    246:         return sh_timer_read(s->timer[0], offset - 0x08);
                    247: 
                    248:     if (offset == 4)
                    249:         return s->tstr;
                    250: 
                    251:     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
                    252:         return s->tocr;
                    253: 
                    254:     cpu_abort (cpu_single_env, "tmu012_write: Bad offset %x\n",
                    255:               (int)offset);
                    256:     return 0;
                    257: }
                    258: 
                    259: static void tmu012_write(void *opaque, target_phys_addr_t offset,
                    260:                         uint32_t value)
                    261: {
                    262:     tmu012_state *s = (tmu012_state *)opaque;
                    263: 
                    264: #ifdef DEBUG_TIMER
                    265:     printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
                    266: #endif
                    267: 
                    268:     if (offset >= 0x20) {
                    269:         if (!(s->feat & TMU012_FEAT_3CHAN))
                    270:            cpu_abort (cpu_single_env, "tmu012_write: Bad channel offset %x\n",
                    271:                       (int)offset);
                    272:         sh_timer_write(s->timer[2], offset - 0x20, value);
                    273:        return;
                    274:     }
                    275: 
                    276:     if (offset >= 0x14) {
                    277:         sh_timer_write(s->timer[1], offset - 0x14, value);
                    278:        return;
                    279:     }
                    280: 
                    281:     if (offset >= 0x08) {
                    282:         sh_timer_write(s->timer[0], offset - 0x08, value);
                    283:        return;
                    284:     }
                    285: 
                    286:     if (offset == 4) {
                    287:         sh_timer_start_stop(s->timer[0], value & (1 << 0));
                    288:         sh_timer_start_stop(s->timer[1], value & (1 << 1));
                    289:         if (s->feat & TMU012_FEAT_3CHAN)
                    290:             sh_timer_start_stop(s->timer[2], value & (1 << 2));
                    291:        else
                    292:             if (value & (1 << 2))
                    293:                 cpu_abort (cpu_single_env, "tmu012_write: Bad channel\n");
                    294: 
                    295:        s->tstr = value;
                    296:        return;
                    297:     }
                    298: 
                    299:     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
                    300:         s->tocr = value & (1 << 0);
                    301:     }
                    302: }
                    303: 
                    304: static CPUReadMemoryFunc *tmu012_readfn[] = {
                    305:     tmu012_read,
                    306:     tmu012_read,
                    307:     tmu012_read
                    308: };
                    309: 
                    310: static CPUWriteMemoryFunc *tmu012_writefn[] = {
                    311:     tmu012_write,
                    312:     tmu012_write,
                    313:     tmu012_write
                    314: };
                    315: 
                    316: void tmu012_init(target_phys_addr_t base, int feat, uint32_t freq,
1.1.1.2 ! root      317:                 qemu_irq ch0_irq, qemu_irq ch1_irq,
        !           318:                 qemu_irq ch2_irq0, qemu_irq ch2_irq1)
1.1       root      319: {
                    320:     int iomemtype;
                    321:     tmu012_state *s;
                    322:     int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
                    323: 
                    324:     s = (tmu012_state *)qemu_mallocz(sizeof(tmu012_state));
                    325:     s->feat = feat;
                    326:     s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
                    327:     s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
                    328:     if (feat & TMU012_FEAT_3CHAN)
                    329:         s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
                    330:                                    ch2_irq0); /* ch2_irq1 not supported */
                    331:     iomemtype = cpu_register_io_memory(0, tmu012_readfn,
                    332:                                        tmu012_writefn, s);
1.1.1.2 ! root      333:     cpu_register_physical_memory(P4ADDR(base), 0x00001000, iomemtype);
        !           334:     cpu_register_physical_memory(A7ADDR(base), 0x00001000, iomemtype);
1.1       root      335:     /* ??? Save/restore.  */
                    336: }

unix.superglobalmegacorp.com