Annotation of qemu/hw/arm_mptimer.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
        !             3:  *
        !             4:  * Copyright (c) 2006-2007 CodeSourcery.
        !             5:  * Copyright (c) 2011 Linaro Limited
        !             6:  * Written by Paul Brook, Peter Maydell
        !             7:  *
        !             8:  * This program is free software; you can redistribute it and/or
        !             9:  * modify it under the terms of the GNU General Public License
        !            10:  * as published by the Free Software Foundation; either version
        !            11:  * 2 of the License, or (at your option) any later version.
        !            12:  *
        !            13:  * This program is distributed in the hope that it will be useful,
        !            14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            16:  * GNU General Public License for more details.
        !            17:  *
        !            18:  * You should have received a copy of the GNU General Public License along
        !            19:  * with this program; if not, see <http://www.gnu.org/licenses/>.
        !            20:  */
        !            21: 
        !            22: #include "sysbus.h"
        !            23: #include "qemu-timer.h"
        !            24: 
        !            25: /* This device implements the per-cpu private timer and watchdog block
        !            26:  * which is used in both the ARM11MPCore and Cortex-A9MP.
        !            27:  */
        !            28: 
        !            29: #define MAX_CPUS 4
        !            30: 
        !            31: /* State of a single timer or watchdog block */
        !            32: typedef struct {
        !            33:     uint32_t count;
        !            34:     uint32_t load;
        !            35:     uint32_t control;
        !            36:     uint32_t status;
        !            37:     int64_t tick;
        !            38:     QEMUTimer *timer;
        !            39:     qemu_irq irq;
        !            40:     MemoryRegion iomem;
        !            41: } timerblock;
        !            42: 
        !            43: typedef struct {
        !            44:     SysBusDevice busdev;
        !            45:     uint32_t num_cpu;
        !            46:     timerblock timerblock[MAX_CPUS * 2];
        !            47:     MemoryRegion iomem[2];
        !            48: } arm_mptimer_state;
        !            49: 
        !            50: static inline int get_current_cpu(arm_mptimer_state *s)
        !            51: {
        !            52:     if (cpu_single_env->cpu_index >= s->num_cpu) {
        !            53:         hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
        !            54:                  s->num_cpu, cpu_single_env->cpu_index);
        !            55:     }
        !            56:     return cpu_single_env->cpu_index;
        !            57: }
        !            58: 
        !            59: static inline void timerblock_update_irq(timerblock *tb)
        !            60: {
        !            61:     qemu_set_irq(tb->irq, tb->status);
        !            62: }
        !            63: 
        !            64: /* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
        !            65: static inline uint32_t timerblock_scale(timerblock *tb)
        !            66: {
        !            67:     return (((tb->control >> 8) & 0xff) + 1) * 10;
        !            68: }
        !            69: 
        !            70: static void timerblock_reload(timerblock *tb, int restart)
        !            71: {
        !            72:     if (tb->count == 0) {
        !            73:         return;
        !            74:     }
        !            75:     if (restart) {
        !            76:         tb->tick = qemu_get_clock_ns(vm_clock);
        !            77:     }
        !            78:     tb->tick += (int64_t)tb->count * timerblock_scale(tb);
        !            79:     qemu_mod_timer(tb->timer, tb->tick);
        !            80: }
        !            81: 
        !            82: static void timerblock_tick(void *opaque)
        !            83: {
        !            84:     timerblock *tb = (timerblock *)opaque;
        !            85:     tb->status = 1;
        !            86:     if (tb->control & 2) {
        !            87:         tb->count = tb->load;
        !            88:         timerblock_reload(tb, 0);
        !            89:     } else {
        !            90:         tb->count = 0;
        !            91:     }
        !            92:     timerblock_update_irq(tb);
        !            93: }
        !            94: 
        !            95: static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr,
        !            96:                                 unsigned size)
        !            97: {
        !            98:     timerblock *tb = (timerblock *)opaque;
        !            99:     int64_t val;
        !           100:     switch (addr) {
        !           101:     case 0: /* Load */
        !           102:         return tb->load;
        !           103:     case 4: /* Counter.  */
        !           104:         if (((tb->control & 1) == 0) || (tb->count == 0)) {
        !           105:             return 0;
        !           106:         }
        !           107:         /* Slow and ugly, but hopefully won't happen too often.  */
        !           108:         val = tb->tick - qemu_get_clock_ns(vm_clock);
        !           109:         val /= timerblock_scale(tb);
        !           110:         if (val < 0) {
        !           111:             val = 0;
        !           112:         }
        !           113:         return val;
        !           114:     case 8: /* Control.  */
        !           115:         return tb->control;
        !           116:     case 12: /* Interrupt status.  */
        !           117:         return tb->status;
        !           118:     default:
        !           119:         return 0;
        !           120:     }
        !           121: }
        !           122: 
        !           123: static void timerblock_write(void *opaque, target_phys_addr_t addr,
        !           124:                              uint64_t value, unsigned size)
        !           125: {
        !           126:     timerblock *tb = (timerblock *)opaque;
        !           127:     int64_t old;
        !           128:     switch (addr) {
        !           129:     case 0: /* Load */
        !           130:         tb->load = value;
        !           131:         /* Fall through.  */
        !           132:     case 4: /* Counter.  */
        !           133:         if ((tb->control & 1) && tb->count) {
        !           134:             /* Cancel the previous timer.  */
        !           135:             qemu_del_timer(tb->timer);
        !           136:         }
        !           137:         tb->count = value;
        !           138:         if (tb->control & 1) {
        !           139:             timerblock_reload(tb, 1);
        !           140:         }
        !           141:         break;
        !           142:     case 8: /* Control.  */
        !           143:         old = tb->control;
        !           144:         tb->control = value;
        !           145:         if (((old & 1) == 0) && (value & 1)) {
        !           146:             if (tb->count == 0 && (tb->control & 2)) {
        !           147:                 tb->count = tb->load;
        !           148:             }
        !           149:             timerblock_reload(tb, 1);
        !           150:         }
        !           151:         break;
        !           152:     case 12: /* Interrupt status.  */
        !           153:         tb->status &= ~value;
        !           154:         timerblock_update_irq(tb);
        !           155:         break;
        !           156:     }
        !           157: }
        !           158: 
        !           159: /* Wrapper functions to implement the "read timer/watchdog for
        !           160:  * the current CPU" memory regions.
        !           161:  */
        !           162: static uint64_t arm_thistimer_read(void *opaque, target_phys_addr_t addr,
        !           163:                                    unsigned size)
        !           164: {
        !           165:     arm_mptimer_state *s = (arm_mptimer_state *)opaque;
        !           166:     int id = get_current_cpu(s);
        !           167:     return timerblock_read(&s->timerblock[id * 2], addr, size);
        !           168: }
        !           169: 
        !           170: static void arm_thistimer_write(void *opaque, target_phys_addr_t addr,
        !           171:                                 uint64_t value, unsigned size)
        !           172: {
        !           173:     arm_mptimer_state *s = (arm_mptimer_state *)opaque;
        !           174:     int id = get_current_cpu(s);
        !           175:     timerblock_write(&s->timerblock[id * 2], addr, value, size);
        !           176: }
        !           177: 
        !           178: static uint64_t arm_thiswdog_read(void *opaque, target_phys_addr_t addr,
        !           179:                                   unsigned size)
        !           180: {
        !           181:     arm_mptimer_state *s = (arm_mptimer_state *)opaque;
        !           182:     int id = get_current_cpu(s);
        !           183:     return timerblock_read(&s->timerblock[id * 2 + 1], addr, size);
        !           184: }
        !           185: 
        !           186: static void arm_thiswdog_write(void *opaque, target_phys_addr_t addr,
        !           187:                                uint64_t value, unsigned size)
        !           188: {
        !           189:     arm_mptimer_state *s = (arm_mptimer_state *)opaque;
        !           190:     int id = get_current_cpu(s);
        !           191:     timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size);
        !           192: }
        !           193: 
        !           194: static const MemoryRegionOps arm_thistimer_ops = {
        !           195:     .read = arm_thistimer_read,
        !           196:     .write = arm_thistimer_write,
        !           197:     .valid = {
        !           198:         .min_access_size = 4,
        !           199:         .max_access_size = 4,
        !           200:     },
        !           201:     .endianness = DEVICE_NATIVE_ENDIAN,
        !           202: };
        !           203: 
        !           204: static const MemoryRegionOps arm_thiswdog_ops = {
        !           205:     .read = arm_thiswdog_read,
        !           206:     .write = arm_thiswdog_write,
        !           207:     .valid = {
        !           208:         .min_access_size = 4,
        !           209:         .max_access_size = 4,
        !           210:     },
        !           211:     .endianness = DEVICE_NATIVE_ENDIAN,
        !           212: };
        !           213: 
        !           214: static const MemoryRegionOps timerblock_ops = {
        !           215:     .read = timerblock_read,
        !           216:     .write = timerblock_write,
        !           217:     .valid = {
        !           218:         .min_access_size = 4,
        !           219:         .max_access_size = 4,
        !           220:     },
        !           221:     .endianness = DEVICE_NATIVE_ENDIAN,
        !           222: };
        !           223: 
        !           224: static void timerblock_reset(timerblock *tb)
        !           225: {
        !           226:     tb->count = 0;
        !           227:     tb->load = 0;
        !           228:     tb->control = 0;
        !           229:     tb->status = 0;
        !           230:     tb->tick = 0;
        !           231:     if (tb->timer) {
        !           232:         qemu_del_timer(tb->timer);
        !           233:     }
        !           234: }
        !           235: 
        !           236: static void arm_mptimer_reset(DeviceState *dev)
        !           237: {
        !           238:     arm_mptimer_state *s =
        !           239:         FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev));
        !           240:     int i;
        !           241:     /* We reset every timer in the array, not just the ones we're using,
        !           242:      * because vmsave will look at every array element.
        !           243:      */
        !           244:     for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
        !           245:         timerblock_reset(&s->timerblock[i]);
        !           246:     }
        !           247: }
        !           248: 
        !           249: static int arm_mptimer_init(SysBusDevice *dev)
        !           250: {
        !           251:     arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, dev);
        !           252:     int i;
        !           253:     if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
        !           254:         hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
        !           255:     }
        !           256:     /* We implement one timer and one watchdog block per CPU, and
        !           257:      * expose multiple MMIO regions:
        !           258:      *  * region 0 is "timer for this core"
        !           259:      *  * region 1 is "watchdog for this core"
        !           260:      *  * region 2 is "timer for core 0"
        !           261:      *  * region 3 is "watchdog for core 0"
        !           262:      *  * region 4 is "timer for core 1"
        !           263:      *  * region 5 is "watchdog for core 1"
        !           264:      * and so on.
        !           265:      * The outgoing interrupt lines are
        !           266:      *  * timer for core 0
        !           267:      *  * watchdog for core 0
        !           268:      *  * timer for core 1
        !           269:      *  * watchdog for core 1
        !           270:      * and so on.
        !           271:      */
        !           272:     memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s,
        !           273:                           "arm_mptimer_timer", 0x20);
        !           274:     sysbus_init_mmio(dev, &s->iomem[0]);
        !           275:     memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s,
        !           276:                           "arm_mptimer_wdog", 0x20);
        !           277:     sysbus_init_mmio(dev, &s->iomem[1]);
        !           278:     for (i = 0; i < (s->num_cpu * 2); i++) {
        !           279:         timerblock *tb = &s->timerblock[i];
        !           280:         tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
        !           281:         sysbus_init_irq(dev, &tb->irq);
        !           282:         memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
        !           283:                               "arm_mptimer_timerblock", 0x20);
        !           284:         sysbus_init_mmio(dev, &tb->iomem);
        !           285:     }
        !           286: 
        !           287:     return 0;
        !           288: }
        !           289: 
        !           290: static const VMStateDescription vmstate_timerblock = {
        !           291:     .name = "arm_mptimer_timerblock",
        !           292:     .version_id = 1,
        !           293:     .minimum_version_id = 1,
        !           294:     .fields = (VMStateField[]) {
        !           295:         VMSTATE_UINT32(count, timerblock),
        !           296:         VMSTATE_UINT32(load, timerblock),
        !           297:         VMSTATE_UINT32(control, timerblock),
        !           298:         VMSTATE_UINT32(status, timerblock),
        !           299:         VMSTATE_INT64(tick, timerblock),
        !           300:         VMSTATE_END_OF_LIST()
        !           301:     }
        !           302: };
        !           303: 
        !           304: static const VMStateDescription vmstate_arm_mptimer = {
        !           305:     .name = "arm_mptimer",
        !           306:     .version_id = 1,
        !           307:     .minimum_version_id = 1,
        !           308:     .fields = (VMStateField[]) {
        !           309:         VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2),
        !           310:                              1, vmstate_timerblock, timerblock),
        !           311:         VMSTATE_END_OF_LIST()
        !           312:     }
        !           313: };
        !           314: 
        !           315: static Property arm_mptimer_properties[] = {
        !           316:     DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0),
        !           317:     DEFINE_PROP_END_OF_LIST()
        !           318: };
        !           319: 
        !           320: static void arm_mptimer_class_init(ObjectClass *klass, void *data)
        !           321: {
        !           322:     DeviceClass *dc = DEVICE_CLASS(klass);
        !           323:     SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
        !           324: 
        !           325:     sbc->init = arm_mptimer_init;
        !           326:     dc->vmsd = &vmstate_arm_mptimer;
        !           327:     dc->reset = arm_mptimer_reset;
        !           328:     dc->no_user = 1;
        !           329:     dc->props = arm_mptimer_properties;
        !           330: }
        !           331: 
        !           332: static TypeInfo arm_mptimer_info = {
        !           333:     .name          = "arm_mptimer",
        !           334:     .parent        = TYPE_SYS_BUS_DEVICE,
        !           335:     .instance_size = sizeof(arm_mptimer_state),
        !           336:     .class_init    = arm_mptimer_class_init,
        !           337: };
        !           338: 
        !           339: static void arm_mptimer_register_types(void)
        !           340: {
        !           341:     type_register_static(&arm_mptimer_info);
        !           342: }
        !           343: 
        !           344: type_init(arm_mptimer_register_types)

unix.superglobalmegacorp.com