Annotation of qemu/hw/arm_mptimer.c, revision 1.1.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