qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

sh_timer.c (10241B)


      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 licensed under the GPL.
      9  */
     10 
     11 #include "qemu/osdep.h"
     12 #include "exec/memory.h"
     13 #include "qemu/log.h"
     14 #include "hw/irq.h"
     15 #include "hw/sh4/sh.h"
     16 #include "hw/timer/tmu012.h"
     17 #include "hw/ptimer.h"
     18 #include "trace.h"
     19 
     20 #define TIMER_TCR_TPSC          (7 << 0)
     21 #define TIMER_TCR_CKEG          (3 << 3)
     22 #define TIMER_TCR_UNIE          (1 << 5)
     23 #define TIMER_TCR_ICPE          (3 << 6)
     24 #define TIMER_TCR_UNF           (1 << 8)
     25 #define TIMER_TCR_ICPF          (1 << 9)
     26 #define TIMER_TCR_RESERVED      (0x3f << 10)
     27 
     28 #define TIMER_FEAT_CAPT   (1 << 0)
     29 #define TIMER_FEAT_EXTCLK (1 << 1)
     30 
     31 #define OFFSET_TCOR   0
     32 #define OFFSET_TCNT   1
     33 #define OFFSET_TCR    2
     34 #define OFFSET_TCPR   3
     35 
     36 typedef struct {
     37     ptimer_state *timer;
     38     uint32_t tcnt;
     39     uint32_t tcor;
     40     uint32_t tcr;
     41     uint32_t tcpr;
     42     int freq;
     43     int int_level;
     44     int old_level;
     45     int feat;
     46     int enabled;
     47     qemu_irq irq;
     48 } SHTimerState;
     49 
     50 /* Check all active timers, and schedule the next timer interrupt. */
     51 
     52 static void sh_timer_update(SHTimerState *s)
     53 {
     54     int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
     55 
     56     if (new_level != s->old_level) {
     57         qemu_set_irq(s->irq, new_level);
     58     }
     59     s->old_level = s->int_level;
     60     s->int_level = new_level;
     61 }
     62 
     63 static uint32_t sh_timer_read(void *opaque, hwaddr offset)
     64 {
     65     SHTimerState *s = opaque;
     66 
     67     switch (offset >> 2) {
     68     case OFFSET_TCOR:
     69         return s->tcor;
     70     case OFFSET_TCNT:
     71         return ptimer_get_count(s->timer);
     72     case OFFSET_TCR:
     73         return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
     74     case OFFSET_TCPR:
     75         if (s->feat & TIMER_FEAT_CAPT) {
     76             return s->tcpr;
     77         }
     78     }
     79     qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
     80                   __func__, offset);
     81     return 0;
     82 }
     83 
     84 static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value)
     85 {
     86     SHTimerState *s = opaque;
     87     int freq;
     88 
     89     switch (offset >> 2) {
     90     case OFFSET_TCOR:
     91         s->tcor = value;
     92         ptimer_transaction_begin(s->timer);
     93         ptimer_set_limit(s->timer, s->tcor, 0);
     94         ptimer_transaction_commit(s->timer);
     95         break;
     96     case OFFSET_TCNT:
     97         s->tcnt = value;
     98         ptimer_transaction_begin(s->timer);
     99         ptimer_set_count(s->timer, s->tcnt);
    100         ptimer_transaction_commit(s->timer);
    101         break;
    102     case OFFSET_TCR:
    103         ptimer_transaction_begin(s->timer);
    104         if (s->enabled) {
    105             /*
    106              * Pause the timer if it is running. This may cause some inaccuracy
    107              * due to rounding, but avoids a whole lot of other messiness
    108              */
    109             ptimer_stop(s->timer);
    110         }
    111         freq = s->freq;
    112         /* ??? Need to recalculate expiry time after changing divisor.  */
    113         switch (value & TIMER_TCR_TPSC) {
    114         case 0:
    115             freq >>= 2;
    116             break;
    117         case 1:
    118             freq >>= 4;
    119             break;
    120         case 2:
    121             freq >>= 6;
    122             break;
    123         case 3:
    124             freq >>= 8;
    125             break;
    126         case 4:
    127             freq >>= 10;
    128             break;
    129         case 6:
    130         case 7:
    131             if (s->feat & TIMER_FEAT_EXTCLK) {
    132                 break;
    133             }
    134             /* fallthrough */
    135         default:
    136             qemu_log_mask(LOG_GUEST_ERROR,
    137                           "%s: Reserved TPSC value\n", __func__);
    138         }
    139         switch ((value & TIMER_TCR_CKEG) >> 3) {
    140         case 0:
    141             break;
    142         case 1:
    143         case 2:
    144         case 3:
    145             if (s->feat & TIMER_FEAT_EXTCLK) {
    146                 break;
    147             }
    148             /* fallthrough */
    149         default:
    150             qemu_log_mask(LOG_GUEST_ERROR,
    151                           "%s: Reserved CKEG value\n", __func__);
    152         }
    153         switch ((value & TIMER_TCR_ICPE) >> 6) {
    154         case 0:
    155             break;
    156         case 2:
    157         case 3:
    158             if (s->feat & TIMER_FEAT_CAPT) {
    159                 break;
    160             }
    161             /* fallthrough */
    162         default:
    163             qemu_log_mask(LOG_GUEST_ERROR,
    164                           "%s: Reserved ICPE value\n", __func__);
    165         }
    166         if ((value & TIMER_TCR_UNF) == 0) {
    167             s->int_level = 0;
    168         }
    169 
    170         value &= ~TIMER_TCR_UNF;
    171 
    172         if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) {
    173             qemu_log_mask(LOG_GUEST_ERROR,
    174                           "%s: Reserved ICPF value\n", __func__);
    175         }
    176 
    177         value &= ~TIMER_TCR_ICPF; /* capture not supported */
    178 
    179         if (value & TIMER_TCR_RESERVED) {
    180             qemu_log_mask(LOG_GUEST_ERROR,
    181                           "%s: Reserved TCR bits set\n", __func__);
    182         }
    183         s->tcr = value;
    184         ptimer_set_limit(s->timer, s->tcor, 0);
    185         ptimer_set_freq(s->timer, freq);
    186         if (s->enabled) {
    187             /* Restart the timer if still enabled.  */
    188             ptimer_run(s->timer, 0);
    189         }
    190         ptimer_transaction_commit(s->timer);
    191         break;
    192     case OFFSET_TCPR:
    193         if (s->feat & TIMER_FEAT_CAPT) {
    194             s->tcpr = value;
    195             break;
    196         }
    197         /* fallthrough */
    198     default:
    199         qemu_log_mask(LOG_GUEST_ERROR,
    200                       "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
    201     }
    202     sh_timer_update(s);
    203 }
    204 
    205 static void sh_timer_start_stop(void *opaque, int enable)
    206 {
    207     SHTimerState *s = opaque;
    208 
    209     trace_sh_timer_start_stop(enable, s->enabled);
    210     ptimer_transaction_begin(s->timer);
    211     if (s->enabled && !enable) {
    212         ptimer_stop(s->timer);
    213     }
    214     if (!s->enabled && enable) {
    215         ptimer_run(s->timer, 0);
    216     }
    217     ptimer_transaction_commit(s->timer);
    218     s->enabled = !!enable;
    219 }
    220 
    221 static void sh_timer_tick(void *opaque)
    222 {
    223     SHTimerState *s = opaque;
    224     s->int_level = s->enabled;
    225     sh_timer_update(s);
    226 }
    227 
    228 static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
    229 {
    230     SHTimerState *s;
    231 
    232     s = g_malloc0(sizeof(*s));
    233     s->freq = freq;
    234     s->feat = feat;
    235     s->tcor = 0xffffffff;
    236     s->tcnt = 0xffffffff;
    237     s->tcpr = 0xdeadbeef;
    238     s->tcr = 0;
    239     s->enabled = 0;
    240     s->irq = irq;
    241 
    242     s->timer = ptimer_init(sh_timer_tick, s, PTIMER_POLICY_LEGACY);
    243 
    244     sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
    245     sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
    246     sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
    247     sh_timer_write(s, OFFSET_TCR  >> 2, s->tcpr);
    248     /* ??? Save/restore.  */
    249     return s;
    250 }
    251 
    252 typedef struct {
    253     MemoryRegion iomem;
    254     MemoryRegion iomem_p4;
    255     MemoryRegion iomem_a7;
    256     void *timer[3];
    257     int level[3];
    258     uint32_t tocr;
    259     uint32_t tstr;
    260     int feat;
    261 } tmu012_state;
    262 
    263 static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size)
    264 {
    265     tmu012_state *s = opaque;
    266 
    267     trace_sh_timer_read(offset);
    268     if (offset >= 0x20) {
    269         if (!(s->feat & TMU012_FEAT_3CHAN)) {
    270             qemu_log_mask(LOG_GUEST_ERROR,
    271                           "%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
    272                           __func__, offset);
    273         }
    274         return sh_timer_read(s->timer[2], offset - 0x20);
    275     }
    276 
    277     if (offset >= 0x14) {
    278         return sh_timer_read(s->timer[1], offset - 0x14);
    279     }
    280     if (offset >= 0x08) {
    281         return sh_timer_read(s->timer[0], offset - 0x08);
    282     }
    283     if (offset == 4) {
    284         return s->tstr;
    285     }
    286     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
    287         return s->tocr;
    288     }
    289 
    290     qemu_log_mask(LOG_GUEST_ERROR,
    291                   "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
    292     return 0;
    293 }
    294 
    295 static void tmu012_write(void *opaque, hwaddr offset,
    296                         uint64_t value, unsigned size)
    297 {
    298     tmu012_state *s = opaque;
    299 
    300     trace_sh_timer_write(offset, value);
    301     if (offset >= 0x20) {
    302         if (!(s->feat & TMU012_FEAT_3CHAN)) {
    303             qemu_log_mask(LOG_GUEST_ERROR,
    304                           "%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
    305                           __func__, offset);
    306         }
    307         sh_timer_write(s->timer[2], offset - 0x20, value);
    308         return;
    309     }
    310 
    311     if (offset >= 0x14) {
    312         sh_timer_write(s->timer[1], offset - 0x14, value);
    313         return;
    314     }
    315 
    316     if (offset >= 0x08) {
    317         sh_timer_write(s->timer[0], offset - 0x08, value);
    318         return;
    319     }
    320 
    321     if (offset == 4) {
    322         sh_timer_start_stop(s->timer[0], value & (1 << 0));
    323         sh_timer_start_stop(s->timer[1], value & (1 << 1));
    324         if (s->feat & TMU012_FEAT_3CHAN) {
    325             sh_timer_start_stop(s->timer[2], value & (1 << 2));
    326         } else {
    327             if (value & (1 << 2)) {
    328                 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad channel\n", __func__);
    329             }
    330         }
    331 
    332         s->tstr = value;
    333         return;
    334     }
    335 
    336     if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
    337         s->tocr = value & (1 << 0);
    338     }
    339 }
    340 
    341 static const MemoryRegionOps tmu012_ops = {
    342     .read = tmu012_read,
    343     .write = tmu012_write,
    344     .endianness = DEVICE_NATIVE_ENDIAN,
    345 };
    346 
    347 void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq,
    348                  qemu_irq ch0_irq, qemu_irq ch1_irq,
    349                  qemu_irq ch2_irq0, qemu_irq ch2_irq1)
    350 {
    351     tmu012_state *s;
    352     int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
    353 
    354     s = g_malloc0(sizeof(*s));
    355     s->feat = feat;
    356     s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
    357     s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
    358     if (feat & TMU012_FEAT_3CHAN) {
    359         s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
    360                                     ch2_irq0); /* ch2_irq1 not supported */
    361     }
    362 
    363     memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, "timer", 0x30);
    364 
    365     memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4",
    366                              &s->iomem, 0, memory_region_size(&s->iomem));
    367     memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
    368 
    369     memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7",
    370                              &s->iomem, 0, memory_region_size(&s->iomem));
    371     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
    372     /* ??? Save/restore.  */
    373 }