qemu

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

a9gtimer.c (11635B)


      1 /*
      2  * Global peripheral timer block for ARM A9MP
      3  *
      4  * (C) 2013 Xilinx Inc.
      5  *
      6  * Written by François LEGAL
      7  * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
      8  *
      9  * This program is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU General Public License
     11  * as published by the Free Software Foundation; either version
     12  * 2 of the License, or (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License along
     20  * with this program; if not, see <http://www.gnu.org/licenses/>.
     21  */
     22 
     23 #include "qemu/osdep.h"
     24 #include "hw/hw.h"
     25 #include "hw/irq.h"
     26 #include "hw/qdev-properties.h"
     27 #include "hw/timer/a9gtimer.h"
     28 #include "migration/vmstate.h"
     29 #include "qapi/error.h"
     30 #include "qemu/timer.h"
     31 #include "qemu/bitops.h"
     32 #include "qemu/log.h"
     33 #include "qemu/module.h"
     34 #include "hw/core/cpu.h"
     35 
     36 #ifndef A9_GTIMER_ERR_DEBUG
     37 #define A9_GTIMER_ERR_DEBUG 0
     38 #endif
     39 
     40 #define DB_PRINT_L(level, ...) do { \
     41     if (A9_GTIMER_ERR_DEBUG > (level)) { \
     42         fprintf(stderr,  ": %s: ", __func__); \
     43         fprintf(stderr, ## __VA_ARGS__); \
     44     } \
     45 } while (0)
     46 
     47 #define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__)
     48 
     49 static inline int a9_gtimer_get_current_cpu(A9GTimerState *s)
     50 {
     51     if (current_cpu->cpu_index >= s->num_cpu) {
     52         hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n",
     53                  s->num_cpu, current_cpu->cpu_index);
     54     }
     55     return current_cpu->cpu_index;
     56 }
     57 
     58 static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s)
     59 {
     60     uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT,
     61                                   R_CONTROL_PRESCALER_LEN);
     62 
     63     return (prescale + 1) * 10;
     64 }
     65 
     66 static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s)
     67 {
     68     A9GTimerUpdate ret;
     69 
     70     ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     71     ret.new = s->ref_counter +
     72               (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s);
     73     return ret;
     74 }
     75 
     76 static void a9_gtimer_update(A9GTimerState *s, bool sync)
     77 {
     78 
     79     A9GTimerUpdate update = a9_gtimer_get_update(s);
     80     int i;
     81     int64_t next_cdiff = 0;
     82 
     83     for (i = 0; i < s->num_cpu; ++i) {
     84         A9GTimerPerCPU *gtb = &s->per_cpu[i];
     85         int64_t cdiff = 0;
     86 
     87         if ((s->control & R_CONTROL_TIMER_ENABLE) &&
     88                 (gtb->control & R_CONTROL_COMP_ENABLE)) {
     89             /* R2p0+, where the compare function is >= */
     90             if (gtb->compare < update.new) {
     91                 DB_PRINT("Compare event happened for CPU %d\n", i);
     92                 gtb->status = 1;
     93                 if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) {
     94                     uint64_t inc =
     95                         QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc);
     96                     DB_PRINT("Auto incrementing timer compare by %"
     97                                                         PRId64 "\n", inc);
     98                     gtb->compare += inc;
     99                 }
    100             }
    101             cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1;
    102             if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) {
    103                 next_cdiff = cdiff;
    104             }
    105         }
    106 
    107         qemu_set_irq(gtb->irq,
    108                      gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE));
    109     }
    110 
    111     timer_del(s->timer);
    112     if (next_cdiff) {
    113         DB_PRINT("scheduling qemu_timer to fire again in %"
    114                  PRIx64 " cycles\n", next_cdiff);
    115         timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s));
    116     }
    117 
    118     if (s->control & R_CONTROL_TIMER_ENABLE) {
    119         s->counter = update.new;
    120     }
    121 
    122     if (sync) {
    123         s->cpu_ref_time = update.now;
    124         s->ref_counter = s->counter;
    125     }
    126 }
    127 
    128 static void a9_gtimer_update_no_sync(void *opaque)
    129 {
    130     A9GTimerState *s = A9_GTIMER(opaque);
    131 
    132     a9_gtimer_update(s, false);
    133 }
    134 
    135 static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size)
    136 {
    137     A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
    138     A9GTimerState *s = gtb->parent;
    139     A9GTimerUpdate update;
    140     uint64_t ret = 0;
    141     int shift = 0;
    142 
    143     switch (addr) {
    144     case R_COUNTER_HI:
    145         shift = 32;
    146         /* fallthrough */
    147     case R_COUNTER_LO:
    148         update = a9_gtimer_get_update(s);
    149         ret = extract64(update.new, shift, 32);
    150         break;
    151     case R_CONTROL:
    152         ret = s->control | gtb->control;
    153         break;
    154     case R_INTERRUPT_STATUS:
    155         ret = gtb->status;
    156         break;
    157     case R_COMPARATOR_HI:
    158         shift = 32;
    159         /* fallthrough */
    160     case R_COMPARATOR_LO:
    161         ret = extract64(gtb->compare, shift, 32);
    162         break;
    163     case R_AUTO_INCREMENT:
    164         ret =  gtb->inc;
    165         break;
    166     default:
    167         qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n",
    168                       (unsigned)addr);
    169         return 0;
    170     }
    171 
    172     DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret);
    173     return ret;
    174 }
    175 
    176 static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value,
    177                             unsigned size)
    178 {
    179     A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque;
    180     A9GTimerState *s = gtb->parent;
    181     int shift = 0;
    182 
    183     DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value);
    184 
    185     switch (addr) {
    186     case R_COUNTER_HI:
    187         shift = 32;
    188         /* fallthrough */
    189     case R_COUNTER_LO:
    190         /*
    191          * Keep it simple - ARM docco explicitly says to disable timer before
    192          * modding it, so don't bother trying to do all the difficult on the fly
    193          * timer modifications - (if they even work in real hardware??).
    194          */
    195         if (s->control & R_CONTROL_TIMER_ENABLE) {
    196             qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n");
    197             return;
    198         }
    199         s->counter = deposit64(s->counter, shift, 32, value);
    200         return;
    201     case R_CONTROL:
    202         a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC);
    203         gtb->control = value & R_CONTROL_BANKED;
    204         s->control = value & ~R_CONTROL_BANKED;
    205         break;
    206     case R_INTERRUPT_STATUS:
    207         a9_gtimer_update(s, false);
    208         gtb->status &= ~value;
    209         break;
    210     case R_COMPARATOR_HI:
    211         shift = 32;
    212         /* fallthrough */
    213     case R_COMPARATOR_LO:
    214         a9_gtimer_update(s, false);
    215         gtb->compare = deposit64(gtb->compare, shift, 32, value);
    216         break;
    217     case R_AUTO_INCREMENT:
    218         gtb->inc = value;
    219         return;
    220     default:
    221         return;
    222     }
    223 
    224     a9_gtimer_update(s, false);
    225 }
    226 
    227 /* Wrapper functions to implement the "read global timer for
    228  * the current CPU" memory regions.
    229  */
    230 static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr,
    231                                     unsigned size)
    232 {
    233     A9GTimerState *s = A9_GTIMER(opaque);
    234     int id = a9_gtimer_get_current_cpu(s);
    235 
    236     /* no \n so concatenates with message from read fn */
    237     DB_PRINT("CPU:%d:", id);
    238 
    239     return a9_gtimer_read(&s->per_cpu[id], addr, size);
    240 }
    241 
    242 static void a9_gtimer_this_write(void *opaque, hwaddr addr,
    243                                  uint64_t value, unsigned size)
    244 {
    245     A9GTimerState *s = A9_GTIMER(opaque);
    246     int id = a9_gtimer_get_current_cpu(s);
    247 
    248     /* no \n so concatenates with message from write fn */
    249     DB_PRINT("CPU:%d:", id);
    250 
    251     a9_gtimer_write(&s->per_cpu[id], addr, value, size);
    252 }
    253 
    254 static const MemoryRegionOps a9_gtimer_this_ops = {
    255     .read = a9_gtimer_this_read,
    256     .write = a9_gtimer_this_write,
    257     .valid = {
    258         .min_access_size = 4,
    259         .max_access_size = 4,
    260     },
    261     .endianness = DEVICE_NATIVE_ENDIAN,
    262 };
    263 
    264 static const MemoryRegionOps a9_gtimer_ops = {
    265     .read = a9_gtimer_read,
    266     .write = a9_gtimer_write,
    267     .valid = {
    268         .min_access_size = 4,
    269         .max_access_size = 4,
    270     },
    271     .endianness = DEVICE_NATIVE_ENDIAN,
    272 };
    273 
    274 static void a9_gtimer_reset(DeviceState *dev)
    275 {
    276     A9GTimerState *s = A9_GTIMER(dev);
    277     int i;
    278 
    279     s->counter = 0;
    280     s->control = 0;
    281 
    282     for (i = 0; i < s->num_cpu; i++) {
    283         A9GTimerPerCPU *gtb = &s->per_cpu[i];
    284 
    285         gtb->control = 0;
    286         gtb->status = 0;
    287         gtb->compare = 0;
    288         gtb->inc = 0;
    289     }
    290     a9_gtimer_update(s, false);
    291 }
    292 
    293 static void a9_gtimer_realize(DeviceState *dev, Error **errp)
    294 {
    295     A9GTimerState *s = A9_GTIMER(dev);
    296     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    297     int i;
    298 
    299     if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) {
    300         error_setg(errp, "%s: num-cpu must be between 1 and %d",
    301                    __func__, A9_GTIMER_MAX_CPUS);
    302         return;
    303     }
    304 
    305     memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s,
    306                           "a9gtimer shared", 0x20);
    307     sysbus_init_mmio(sbd, &s->iomem);
    308     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s);
    309 
    310     for (i = 0; i < s->num_cpu; i++) {
    311         A9GTimerPerCPU *gtb = &s->per_cpu[i];
    312 
    313         gtb->parent = s;
    314         sysbus_init_irq(sbd, &gtb->irq);
    315         memory_region_init_io(&gtb->iomem, OBJECT(dev), &a9_gtimer_ops, gtb,
    316                               "a9gtimer per cpu", 0x20);
    317         sysbus_init_mmio(sbd, &gtb->iomem);
    318     }
    319 }
    320 
    321 static bool vmstate_a9_gtimer_control_needed(void *opaque)
    322 {
    323     A9GTimerState *s = opaque;
    324     return s->control != 0;
    325 }
    326 
    327 static const VMStateDescription vmstate_a9_gtimer_per_cpu = {
    328     .name = "arm.cortex-a9-global-timer.percpu",
    329     .version_id = 1,
    330     .minimum_version_id = 1,
    331     .fields = (VMStateField[]) {
    332         VMSTATE_UINT32(control, A9GTimerPerCPU),
    333         VMSTATE_UINT64(compare, A9GTimerPerCPU),
    334         VMSTATE_UINT32(status, A9GTimerPerCPU),
    335         VMSTATE_UINT32(inc, A9GTimerPerCPU),
    336         VMSTATE_END_OF_LIST()
    337     }
    338 };
    339 
    340 static const VMStateDescription vmstate_a9_gtimer_control = {
    341     .name = "arm.cortex-a9-global-timer.control",
    342     .version_id = 1,
    343     .minimum_version_id = 1,
    344     .needed = vmstate_a9_gtimer_control_needed,
    345     .fields = (VMStateField[]) {
    346         VMSTATE_UINT32(control, A9GTimerState),
    347         VMSTATE_END_OF_LIST()
    348     }
    349 };
    350 
    351 static const VMStateDescription vmstate_a9_gtimer = {
    352     .name = "arm.cortex-a9-global-timer",
    353     .version_id = 1,
    354     .minimum_version_id = 1,
    355     .fields = (VMStateField[]) {
    356         VMSTATE_TIMER_PTR(timer, A9GTimerState),
    357         VMSTATE_UINT64(counter, A9GTimerState),
    358         VMSTATE_UINT64(ref_counter, A9GTimerState),
    359         VMSTATE_UINT64(cpu_ref_time, A9GTimerState),
    360         VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu,
    361                                      1, vmstate_a9_gtimer_per_cpu,
    362                                      A9GTimerPerCPU),
    363         VMSTATE_END_OF_LIST()
    364     },
    365     .subsections = (const VMStateDescription*[]) {
    366         &vmstate_a9_gtimer_control,
    367         NULL
    368     }
    369 };
    370 
    371 static Property a9_gtimer_properties[] = {
    372     DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0),
    373     DEFINE_PROP_END_OF_LIST()
    374 };
    375 
    376 static void a9_gtimer_class_init(ObjectClass *klass, void *data)
    377 {
    378     DeviceClass *dc = DEVICE_CLASS(klass);
    379 
    380     dc->realize = a9_gtimer_realize;
    381     dc->vmsd = &vmstate_a9_gtimer;
    382     dc->reset = a9_gtimer_reset;
    383     device_class_set_props(dc, a9_gtimer_properties);
    384 }
    385 
    386 static const TypeInfo a9_gtimer_info = {
    387     .name          = TYPE_A9_GTIMER,
    388     .parent        = TYPE_SYS_BUS_DEVICE,
    389     .instance_size = sizeof(A9GTimerState),
    390     .class_init    = a9_gtimer_class_init,
    391 };
    392 
    393 static void a9_gtimer_register_types(void)
    394 {
    395     type_register_static(&a9_gtimer_info);
    396 }
    397 
    398 type_init(a9_gtimer_register_types)