~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to hw/timer/armv7m_systick.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ARMv7M SysTick timer
 
3
 *
 
4
 * Copyright (c) 2006-2007 CodeSourcery.
 
5
 * Written by Paul Brook
 
6
 * Copyright (c) 2017 Linaro Ltd
 
7
 * Written by Peter Maydell
 
8
 *
 
9
 * This code is licensed under the GPL (version 2 or later).
 
10
 */
 
11
 
 
12
#include "qemu/osdep.h"
 
13
#include "hw/timer/armv7m_systick.h"
 
14
#include "qemu-common.h"
 
15
#include "hw/sysbus.h"
 
16
#include "qemu/timer.h"
 
17
#include "qemu/log.h"
 
18
#include "trace.h"
 
19
 
 
20
/* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
 
21
#define SYSTICK_SCALE 1000ULL
 
22
 
 
23
#define SYSTICK_ENABLE    (1 << 0)
 
24
#define SYSTICK_TICKINT   (1 << 1)
 
25
#define SYSTICK_CLKSOURCE (1 << 2)
 
26
#define SYSTICK_COUNTFLAG (1 << 16)
 
27
 
 
28
int system_clock_scale;
 
29
 
 
30
/* Conversion factor from qemu timer to SysTick frequencies.  */
 
31
static inline int64_t systick_scale(SysTickState *s)
 
32
{
 
33
    if (s->control & SYSTICK_CLKSOURCE) {
 
34
        return system_clock_scale;
 
35
    } else {
 
36
        return 1000;
 
37
    }
 
38
}
 
39
 
 
40
static void systick_reload(SysTickState *s, int reset)
 
41
{
 
42
    /* The Cortex-M3 Devices Generic User Guide says that "When the
 
43
     * ENABLE bit is set to 1, the counter loads the RELOAD value from the
 
44
     * SYST RVR register and then counts down". So, we need to check the
 
45
     * ENABLE bit before reloading the value.
 
46
     */
 
47
    trace_systick_reload();
 
48
 
 
49
    if ((s->control & SYSTICK_ENABLE) == 0) {
 
50
        return;
 
51
    }
 
52
 
 
53
    if (reset) {
 
54
        s->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 
55
    }
 
56
    s->tick += (s->reload + 1) * systick_scale(s);
 
57
    timer_mod(s->timer, s->tick);
 
58
}
 
59
 
 
60
static void systick_timer_tick(void *opaque)
 
61
{
 
62
    SysTickState *s = (SysTickState *)opaque;
 
63
 
 
64
    trace_systick_timer_tick();
 
65
 
 
66
    s->control |= SYSTICK_COUNTFLAG;
 
67
    if (s->control & SYSTICK_TICKINT) {
 
68
        /* Tell the NVIC to pend the SysTick exception */
 
69
        qemu_irq_pulse(s->irq);
 
70
    }
 
71
    if (s->reload == 0) {
 
72
        s->control &= ~SYSTICK_ENABLE;
 
73
    } else {
 
74
        systick_reload(s, 0);
 
75
    }
 
76
}
 
77
 
 
78
static uint64_t systick_read(void *opaque, hwaddr addr, unsigned size)
 
79
{
 
80
    SysTickState *s = opaque;
 
81
    uint32_t val;
 
82
 
 
83
    switch (addr) {
 
84
    case 0x0: /* SysTick Control and Status.  */
 
85
        val = s->control;
 
86
        s->control &= ~SYSTICK_COUNTFLAG;
 
87
        break;
 
88
    case 0x4: /* SysTick Reload Value.  */
 
89
        val = s->reload;
 
90
        break;
 
91
    case 0x8: /* SysTick Current Value.  */
 
92
    {
 
93
        int64_t t;
 
94
 
 
95
        if ((s->control & SYSTICK_ENABLE) == 0) {
 
96
            val = 0;
 
97
            break;
 
98
        }
 
99
        t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 
100
        if (t >= s->tick) {
 
101
            val = 0;
 
102
            break;
 
103
        }
 
104
        val = ((s->tick - (t + 1)) / systick_scale(s)) + 1;
 
105
        /* The interrupt in triggered when the timer reaches zero.
 
106
           However the counter is not reloaded until the next clock
 
107
           tick.  This is a hack to return zero during the first tick.  */
 
108
        if (val > s->reload) {
 
109
            val = 0;
 
110
        }
 
111
        break;
 
112
    }
 
113
    case 0xc: /* SysTick Calibration Value.  */
 
114
        val = 10000;
 
115
        break;
 
116
    default:
 
117
        val = 0;
 
118
        qemu_log_mask(LOG_GUEST_ERROR,
 
119
                      "SysTick: Bad read offset 0x%" HWADDR_PRIx "\n", addr);
 
120
        break;
 
121
    }
 
122
 
 
123
    trace_systick_read(addr, val, size);
 
124
    return val;
 
125
}
 
126
 
 
127
static void systick_write(void *opaque, hwaddr addr,
 
128
                          uint64_t value, unsigned size)
 
129
{
 
130
    SysTickState *s = opaque;
 
131
 
 
132
    trace_systick_write(addr, value, size);
 
133
 
 
134
    switch (addr) {
 
135
    case 0x0: /* SysTick Control and Status.  */
 
136
    {
 
137
        uint32_t oldval = s->control;
 
138
 
 
139
        s->control &= 0xfffffff8;
 
140
        s->control |= value & 7;
 
141
        if ((oldval ^ value) & SYSTICK_ENABLE) {
 
142
            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 
143
            if (value & SYSTICK_ENABLE) {
 
144
                if (s->tick) {
 
145
                    s->tick += now;
 
146
                    timer_mod(s->timer, s->tick);
 
147
                } else {
 
148
                    systick_reload(s, 1);
 
149
                }
 
150
            } else {
 
151
                timer_del(s->timer);
 
152
                s->tick -= now;
 
153
                if (s->tick < 0) {
 
154
                    s->tick = 0;
 
155
                }
 
156
            }
 
157
        } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
 
158
            /* This is a hack. Force the timer to be reloaded
 
159
               when the reference clock is changed.  */
 
160
            systick_reload(s, 1);
 
161
        }
 
162
        break;
 
163
    }
 
164
    case 0x4: /* SysTick Reload Value.  */
 
165
        s->reload = value;
 
166
        break;
 
167
    case 0x8: /* SysTick Current Value.  Writes reload the timer.  */
 
168
        systick_reload(s, 1);
 
169
        s->control &= ~SYSTICK_COUNTFLAG;
 
170
        break;
 
171
    default:
 
172
        qemu_log_mask(LOG_GUEST_ERROR,
 
173
                      "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr);
 
174
    }
 
175
}
 
176
 
 
177
static const MemoryRegionOps systick_ops = {
 
178
    .read = systick_read,
 
179
    .write = systick_write,
 
180
    .endianness = DEVICE_NATIVE_ENDIAN,
 
181
    .valid.min_access_size = 4,
 
182
    .valid.max_access_size = 4,
 
183
};
 
184
 
 
185
static void systick_reset(DeviceState *dev)
 
186
{
 
187
    SysTickState *s = SYSTICK(dev);
 
188
 
 
189
    s->control = 0;
 
190
    s->reload = 0;
 
191
    s->tick = 0;
 
192
    timer_del(s->timer);
 
193
}
 
194
 
 
195
static void systick_instance_init(Object *obj)
 
196
{
 
197
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
198
    SysTickState *s = SYSTICK(obj);
 
199
 
 
200
    memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
 
201
    sysbus_init_mmio(sbd, &s->iomem);
 
202
    sysbus_init_irq(sbd, &s->irq);
 
203
    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s);
 
204
}
 
205
 
 
206
static const VMStateDescription vmstate_systick = {
 
207
    .name = "armv7m_systick",
 
208
    .version_id = 1,
 
209
    .minimum_version_id = 1,
 
210
    .fields = (VMStateField[]) {
 
211
        VMSTATE_UINT32(control, SysTickState),
 
212
        VMSTATE_UINT32(reload, SysTickState),
 
213
        VMSTATE_INT64(tick, SysTickState),
 
214
        VMSTATE_TIMER_PTR(timer, SysTickState),
 
215
        VMSTATE_END_OF_LIST()
 
216
    }
 
217
};
 
218
 
 
219
static void systick_class_init(ObjectClass *klass, void *data)
 
220
{
 
221
    DeviceClass *dc = DEVICE_CLASS(klass);
 
222
 
 
223
    dc->vmsd = &vmstate_systick;
 
224
    dc->reset = systick_reset;
 
225
}
 
226
 
 
227
static const TypeInfo armv7m_systick_info = {
 
228
    .name = TYPE_SYSTICK,
 
229
    .parent = TYPE_SYS_BUS_DEVICE,
 
230
    .instance_init = systick_instance_init,
 
231
    .instance_size = sizeof(SysTickState),
 
232
    .class_init = systick_class_init,
 
233
};
 
234
 
 
235
static void armv7m_systick_register_types(void)
 
236
{
 
237
    type_register_static(&armv7m_systick_info);
 
238
}
 
239
 
 
240
type_init(armv7m_systick_register_types)