2
* QEMU model of the Milkymist System Controller.
4
* Copyright (c) 2010 Michael Walle <michael@walle.cc>
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2 of the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
20
* Specification available at:
21
* http://www.milkymist.org/socdoc/sysctl.pdf
28
#include "qemu-timer.h"
29
#include "qemu-error.h"
33
CTRL_AUTORESTART = (1<<1),
60
struct MilkymistSysctlState {
65
ptimer_state *ptimer0;
66
ptimer_state *ptimer1;
69
uint32_t capabilities;
79
typedef struct MilkymistSysctlState MilkymistSysctlState;
81
static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value)
83
trace_milkymist_sysctl_icap_write(value);
84
switch (value & 0xffff) {
86
qemu_system_shutdown_request();
91
static uint32_t sysctl_read(void *opaque, target_phys_addr_t addr)
93
MilkymistSysctlState *s = opaque;
98
case R_TIMER0_COUNTER:
99
r = (uint32_t)ptimer_get_count(s->ptimer0);
100
/* milkymist timer counts up */
101
r = s->regs[R_TIMER0_COMPARE] - r;
103
case R_TIMER1_COUNTER:
104
r = (uint32_t)ptimer_get_count(s->ptimer1);
105
/* milkymist timer counts up */
106
r = s->regs[R_TIMER1_COMPARE] - r;
111
case R_TIMER0_CONTROL:
112
case R_TIMER0_COMPARE:
113
case R_TIMER1_CONTROL:
114
case R_TIMER1_COMPARE:
122
error_report("milkymist_sysctl: read access to unknown register 0x"
123
TARGET_FMT_plx, addr << 2);
127
trace_milkymist_sysctl_memory_read(addr << 2, r);
132
static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value)
134
MilkymistSysctlState *s = opaque;
136
trace_milkymist_sysctl_memory_write(addr, value);
142
case R_TIMER0_COUNTER:
143
case R_TIMER1_COUNTER:
144
s->regs[addr] = value;
146
case R_TIMER0_COMPARE:
147
ptimer_set_limit(s->ptimer0, value, 0);
148
s->regs[addr] = value;
150
case R_TIMER1_COMPARE:
151
ptimer_set_limit(s->ptimer1, value, 0);
152
s->regs[addr] = value;
154
case R_TIMER0_CONTROL:
155
s->regs[addr] = value;
156
if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
157
trace_milkymist_sysctl_start_timer0();
158
ptimer_set_count(s->ptimer0,
159
s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
160
ptimer_run(s->ptimer0, 0);
162
trace_milkymist_sysctl_stop_timer0();
163
ptimer_stop(s->ptimer0);
166
case R_TIMER1_CONTROL:
167
s->regs[addr] = value;
168
if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
169
trace_milkymist_sysctl_start_timer1();
170
ptimer_set_count(s->ptimer1,
171
s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
172
ptimer_run(s->ptimer1, 0);
174
trace_milkymist_sysctl_stop_timer1();
175
ptimer_stop(s->ptimer1);
179
sysctl_icap_write(s, value);
182
qemu_system_reset_request();
187
error_report("milkymist_sysctl: write to read-only register 0x"
188
TARGET_FMT_plx, addr << 2);
192
error_report("milkymist_sysctl: write access to unknown register 0x"
193
TARGET_FMT_plx, addr << 2);
198
static CPUReadMemoryFunc * const sysctl_read_fn[] = {
204
static CPUWriteMemoryFunc * const sysctl_write_fn[] = {
210
static void timer0_hit(void *opaque)
212
MilkymistSysctlState *s = opaque;
214
if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) {
215
s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE;
216
trace_milkymist_sysctl_stop_timer0();
217
ptimer_stop(s->ptimer0);
220
trace_milkymist_sysctl_pulse_irq_timer0();
221
qemu_irq_pulse(s->timer0_irq);
224
static void timer1_hit(void *opaque)
226
MilkymistSysctlState *s = opaque;
228
if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) {
229
s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE;
230
trace_milkymist_sysctl_stop_timer1();
231
ptimer_stop(s->ptimer1);
234
trace_milkymist_sysctl_pulse_irq_timer1();
235
qemu_irq_pulse(s->timer1_irq);
238
static void milkymist_sysctl_reset(DeviceState *d)
240
MilkymistSysctlState *s =
241
container_of(d, MilkymistSysctlState, busdev.qdev);
244
for (i = 0; i < R_MAX; i++) {
248
ptimer_stop(s->ptimer0);
249
ptimer_stop(s->ptimer1);
252
s->regs[R_ICAP] = ICAP_READY;
253
s->regs[R_SYSTEM_ID] = s->systemid;
254
s->regs[R_CAPABILITIES] = s->capabilities;
255
s->regs[R_GPIO_IN] = s->strappings;
258
static int milkymist_sysctl_init(SysBusDevice *dev)
260
MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev);
263
sysbus_init_irq(dev, &s->gpio_irq);
264
sysbus_init_irq(dev, &s->timer0_irq);
265
sysbus_init_irq(dev, &s->timer1_irq);
267
s->bh0 = qemu_bh_new(timer0_hit, s);
268
s->bh1 = qemu_bh_new(timer1_hit, s);
269
s->ptimer0 = ptimer_init(s->bh0);
270
s->ptimer1 = ptimer_init(s->bh1);
271
ptimer_set_freq(s->ptimer0, s->freq_hz);
272
ptimer_set_freq(s->ptimer1, s->freq_hz);
274
sysctl_regs = cpu_register_io_memory(sysctl_read_fn, sysctl_write_fn, s,
275
DEVICE_NATIVE_ENDIAN);
276
sysbus_init_mmio(dev, R_MAX * 4, sysctl_regs);
281
static const VMStateDescription vmstate_milkymist_sysctl = {
282
.name = "milkymist-sysctl",
284
.minimum_version_id = 1,
285
.minimum_version_id_old = 1,
286
.fields = (VMStateField[]) {
287
VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX),
288
VMSTATE_PTIMER(ptimer0, MilkymistSysctlState),
289
VMSTATE_PTIMER(ptimer1, MilkymistSysctlState),
290
VMSTATE_END_OF_LIST()
294
static SysBusDeviceInfo milkymist_sysctl_info = {
295
.init = milkymist_sysctl_init,
296
.qdev.name = "milkymist-sysctl",
297
.qdev.size = sizeof(MilkymistSysctlState),
298
.qdev.vmsd = &vmstate_milkymist_sysctl,
299
.qdev.reset = milkymist_sysctl_reset,
300
.qdev.props = (Property[]) {
301
DEFINE_PROP_UINT32("frequency", MilkymistSysctlState,
303
DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState,
304
capabilities, 0x00000000),
305
DEFINE_PROP_UINT32("systemid", MilkymistSysctlState,
306
systemid, 0x10014d31),
307
DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState,
308
strappings, 0x00000001),
309
DEFINE_PROP_END_OF_LIST(),
313
static void milkymist_sysctl_register(void)
315
sysbus_register_withprop(&milkymist_sysctl_info);
318
device_init(milkymist_sysctl_register)