19a52d999SPeter Maydell /*
29a52d999SPeter Maydell * ARM MPS2 AN505 FPGAIO emulation
39a52d999SPeter Maydell *
49a52d999SPeter Maydell * Copyright (c) 2018 Linaro Limited
59a52d999SPeter Maydell * Written by Peter Maydell
69a52d999SPeter Maydell *
79a52d999SPeter Maydell * This program is free software; you can redistribute it and/or modify
89a52d999SPeter Maydell * it under the terms of the GNU General Public License version 2 or
99a52d999SPeter Maydell * (at your option) any later version.
109a52d999SPeter Maydell */
119a52d999SPeter Maydell
129a52d999SPeter Maydell /* This is a model of the "FPGA system control and I/O" block found
139a52d999SPeter Maydell * in the AN505 FPGA image for the MPS2 devboard.
149a52d999SPeter Maydell * It is documented in AN505:
1550b52b18SPeter Maydell * https://developer.arm.com/documentation/dai0505/latest/
169a52d999SPeter Maydell */
179a52d999SPeter Maydell
189a52d999SPeter Maydell #include "qemu/osdep.h"
199a52d999SPeter Maydell #include "qemu/log.h"
200b8fa32fSMarkus Armbruster #include "qemu/module.h"
219a52d999SPeter Maydell #include "qapi/error.h"
229a52d999SPeter Maydell #include "trace.h"
239a52d999SPeter Maydell #include "hw/sysbus.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
259a52d999SPeter Maydell #include "hw/registerfields.h"
269a52d999SPeter Maydell #include "hw/misc/mps2-fpgaio.h"
2765ad1da2SPhilippe Mathieu-Daudé #include "hw/misc/led.h"
28a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
29a1982f90SPeter Maydell #include "qemu/timer.h"
309a52d999SPeter Maydell
319a52d999SPeter Maydell REG32(LED0, 0)
3239901aeaSPeter Maydell REG32(DBGCTRL, 4)
339a52d999SPeter Maydell REG32(BUTTON, 8)
349a52d999SPeter Maydell REG32(CLK1HZ, 0x10)
359a52d999SPeter Maydell REG32(CLK100HZ, 0x14)
369a52d999SPeter Maydell REG32(COUNTER, 0x18)
379a52d999SPeter Maydell REG32(PRESCALE, 0x1c)
389a52d999SPeter Maydell REG32(PSCNTR, 0x20)
39b2234223SPeter Maydell REG32(SWITCH, 0x28)
409a52d999SPeter Maydell REG32(MISC, 0x4c)
419a52d999SPeter Maydell
counter_from_tickoff(int64_t now,int64_t tick_offset,int frq)42a1982f90SPeter Maydell static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq)
43a1982f90SPeter Maydell {
44a1982f90SPeter Maydell return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND);
45a1982f90SPeter Maydell }
46a1982f90SPeter Maydell
tickoff_from_counter(int64_t now,uint32_t count,int frq)47a1982f90SPeter Maydell static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq)
48a1982f90SPeter Maydell {
49a1982f90SPeter Maydell return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq);
50a1982f90SPeter Maydell }
51a1982f90SPeter Maydell
resync_counter(MPS2FPGAIO * s)5293739075SPeter Maydell static void resync_counter(MPS2FPGAIO *s)
5393739075SPeter Maydell {
5493739075SPeter Maydell /*
5593739075SPeter Maydell * Update s->counter and s->pscntr to their true current values
5693739075SPeter Maydell * by calculating how many times PSCNTR has ticked since the
5793739075SPeter Maydell * last time we did a resync.
5893739075SPeter Maydell */
5993739075SPeter Maydell int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
6093739075SPeter Maydell int64_t elapsed = now - s->pscntr_sync_ticks;
6193739075SPeter Maydell
6293739075SPeter Maydell /*
6393739075SPeter Maydell * Round elapsed down to a whole number of PSCNTR ticks, so we don't
6493739075SPeter Maydell * lose time if we do multiple resyncs in a single tick.
6593739075SPeter Maydell */
6693739075SPeter Maydell uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND);
6793739075SPeter Maydell
6893739075SPeter Maydell /*
6993739075SPeter Maydell * Work out what PSCNTR and COUNTER have moved to. We assume that
7093739075SPeter Maydell * PSCNTR reloads from PRESCALE one tick-period after it hits zero,
7193739075SPeter Maydell * and that COUNTER increments at the same moment.
7293739075SPeter Maydell */
7393739075SPeter Maydell if (ticks == 0) {
7493739075SPeter Maydell /* We haven't ticked since the last time we were asked */
7593739075SPeter Maydell return;
7693739075SPeter Maydell } else if (ticks < s->pscntr) {
7793739075SPeter Maydell /* We haven't yet reached zero, just reduce the PSCNTR */
7893739075SPeter Maydell s->pscntr -= ticks;
7993739075SPeter Maydell } else {
8093739075SPeter Maydell if (s->prescale == 0) {
8193739075SPeter Maydell /*
8293739075SPeter Maydell * If the reload value is zero then the PSCNTR will stick
8393739075SPeter Maydell * at zero once it reaches it, and so we will increment
8493739075SPeter Maydell * COUNTER every tick after that.
8593739075SPeter Maydell */
8693739075SPeter Maydell s->counter += ticks - s->pscntr;
8793739075SPeter Maydell s->pscntr = 0;
8893739075SPeter Maydell } else {
8993739075SPeter Maydell /*
9093739075SPeter Maydell * This is the complicated bit. This ASCII art diagram gives an
9193739075SPeter Maydell * example with PRESCALE==5 PSCNTR==7:
9293739075SPeter Maydell *
9393739075SPeter Maydell * ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
9493739075SPeter Maydell * PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5
9593739075SPeter Maydell * cinc 1 2
9693739075SPeter Maydell * y 0 1 2 3 4 5 6 7 8 9 10 11 12
9793739075SPeter Maydell * x 0 1 2 3 4 5 0 1 2 3 4 5 0
9893739075SPeter Maydell *
9993739075SPeter Maydell * where x = y % (s->prescale + 1)
10093739075SPeter Maydell * and so PSCNTR = s->prescale - x
10193739075SPeter Maydell * and COUNTER is incremented by y / (s->prescale + 1)
10293739075SPeter Maydell *
10393739075SPeter Maydell * The case where PSCNTR < PRESCALE works out the same,
10493739075SPeter Maydell * though we must be careful to calculate y as 64-bit unsigned
10593739075SPeter Maydell * for all parts of the expression.
10693739075SPeter Maydell * y < 0 is not possible because that implies ticks < s->pscntr.
10793739075SPeter Maydell */
10893739075SPeter Maydell uint64_t y = ticks - s->pscntr + s->prescale;
10993739075SPeter Maydell s->pscntr = s->prescale - (y % (s->prescale + 1));
11093739075SPeter Maydell s->counter += y / (s->prescale + 1);
11193739075SPeter Maydell }
11293739075SPeter Maydell }
11393739075SPeter Maydell
11493739075SPeter Maydell /*
11593739075SPeter Maydell * Only advance the sync time to the timestamp of the last PSCNTR tick,
11693739075SPeter Maydell * not all the way to 'now', so we don't lose time if we do multiple
11793739075SPeter Maydell * resyncs in a single tick.
11893739075SPeter Maydell */
11993739075SPeter Maydell s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND,
12093739075SPeter Maydell s->prescale_clk);
12193739075SPeter Maydell }
12293739075SPeter Maydell
mps2_fpgaio_read(void * opaque,hwaddr offset,unsigned size)1239a52d999SPeter Maydell static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
1249a52d999SPeter Maydell {
1259a52d999SPeter Maydell MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
1269a52d999SPeter Maydell uint64_t r;
127a1982f90SPeter Maydell int64_t now;
1289a52d999SPeter Maydell
1299a52d999SPeter Maydell switch (offset) {
1309a52d999SPeter Maydell case A_LED0:
1319a52d999SPeter Maydell r = s->led0;
1329a52d999SPeter Maydell break;
13339901aeaSPeter Maydell case A_DBGCTRL:
13439901aeaSPeter Maydell if (!s->has_dbgctrl) {
13539901aeaSPeter Maydell goto bad_offset;
13639901aeaSPeter Maydell }
13739901aeaSPeter Maydell r = s->dbgctrl;
13839901aeaSPeter Maydell break;
1399a52d999SPeter Maydell case A_BUTTON:
1409a52d999SPeter Maydell /* User-pressable board buttons. We don't model that, so just return
1419a52d999SPeter Maydell * zeroes.
1429a52d999SPeter Maydell */
1439a52d999SPeter Maydell r = 0;
1449a52d999SPeter Maydell break;
1459a52d999SPeter Maydell case A_PRESCALE:
1469a52d999SPeter Maydell r = s->prescale;
1479a52d999SPeter Maydell break;
1489a52d999SPeter Maydell case A_MISC:
1499a52d999SPeter Maydell r = s->misc;
1509a52d999SPeter Maydell break;
1519a52d999SPeter Maydell case A_CLK1HZ:
152a1982f90SPeter Maydell now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
153a1982f90SPeter Maydell r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1);
154a1982f90SPeter Maydell break;
1559a52d999SPeter Maydell case A_CLK100HZ:
156a1982f90SPeter Maydell now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
157a1982f90SPeter Maydell r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100);
158a1982f90SPeter Maydell break;
1599a52d999SPeter Maydell case A_COUNTER:
16093739075SPeter Maydell resync_counter(s);
16193739075SPeter Maydell r = s->counter;
16293739075SPeter Maydell break;
1639a52d999SPeter Maydell case A_PSCNTR:
16493739075SPeter Maydell resync_counter(s);
16593739075SPeter Maydell r = s->pscntr;
1669a52d999SPeter Maydell break;
167b2234223SPeter Maydell case A_SWITCH:
168b2234223SPeter Maydell if (!s->has_switches) {
169b2234223SPeter Maydell goto bad_offset;
170b2234223SPeter Maydell }
171b2234223SPeter Maydell /* User-togglable board switches. We don't model that, so report 0. */
172b2234223SPeter Maydell r = 0;
173b2234223SPeter Maydell break;
1749a52d999SPeter Maydell default:
175b2234223SPeter Maydell bad_offset:
1769a52d999SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
1779a52d999SPeter Maydell "MPS2 FPGAIO read: bad offset %x\n", (int) offset);
1789a52d999SPeter Maydell r = 0;
1799a52d999SPeter Maydell break;
1809a52d999SPeter Maydell }
1819a52d999SPeter Maydell
1829a52d999SPeter Maydell trace_mps2_fpgaio_read(offset, r, size);
1839a52d999SPeter Maydell return r;
1849a52d999SPeter Maydell }
1859a52d999SPeter Maydell
mps2_fpgaio_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)1869a52d999SPeter Maydell static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
1879a52d999SPeter Maydell unsigned size)
1889a52d999SPeter Maydell {
1899a52d999SPeter Maydell MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
190a1982f90SPeter Maydell int64_t now;
1919a52d999SPeter Maydell
1929a52d999SPeter Maydell trace_mps2_fpgaio_write(offset, value, size);
1939a52d999SPeter Maydell
1949a52d999SPeter Maydell switch (offset) {
1959a52d999SPeter Maydell case A_LED0:
196e8556f43SPeter Maydell if (s->num_leds != 0) {
197e8556f43SPeter Maydell uint32_t i;
198e8556f43SPeter Maydell
199e8556f43SPeter Maydell s->led0 = value & MAKE_64BIT_MASK(0, s->num_leds);
200e8556f43SPeter Maydell for (i = 0; i < s->num_leds; i++) {
201e8556f43SPeter Maydell led_set_state(s->led[i], value & (1 << i));
202e8556f43SPeter Maydell }
203e8556f43SPeter Maydell }
2049a52d999SPeter Maydell break;
20539901aeaSPeter Maydell case A_DBGCTRL:
20639901aeaSPeter Maydell if (!s->has_dbgctrl) {
20739901aeaSPeter Maydell goto bad_offset;
20839901aeaSPeter Maydell }
20939901aeaSPeter Maydell qemu_log_mask(LOG_UNIMP,
21039901aeaSPeter Maydell "MPS2 FPGAIO: DBGCTRL unimplemented\n");
21139901aeaSPeter Maydell s->dbgctrl = value;
21239901aeaSPeter Maydell break;
2139a52d999SPeter Maydell case A_PRESCALE:
21493739075SPeter Maydell resync_counter(s);
2159a52d999SPeter Maydell s->prescale = value;
2169a52d999SPeter Maydell break;
2179a52d999SPeter Maydell case A_MISC:
2189a52d999SPeter Maydell /* These are control bits for some of the other devices on the
2199a52d999SPeter Maydell * board (SPI, CLCD, etc). We don't implement that yet, so just
2209a52d999SPeter Maydell * make the bits read as written.
2219a52d999SPeter Maydell */
2229a52d999SPeter Maydell qemu_log_mask(LOG_UNIMP,
2239a52d999SPeter Maydell "MPS2 FPGAIO: MISC control bits unimplemented\n");
2249a52d999SPeter Maydell s->misc = value;
2259a52d999SPeter Maydell break;
226a1982f90SPeter Maydell case A_CLK1HZ:
227a1982f90SPeter Maydell now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
228a1982f90SPeter Maydell s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1);
229a1982f90SPeter Maydell break;
230a1982f90SPeter Maydell case A_CLK100HZ:
231a1982f90SPeter Maydell now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
232a1982f90SPeter Maydell s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100);
233a1982f90SPeter Maydell break;
23493739075SPeter Maydell case A_COUNTER:
23593739075SPeter Maydell resync_counter(s);
23693739075SPeter Maydell s->counter = value;
23793739075SPeter Maydell break;
23893739075SPeter Maydell case A_PSCNTR:
23993739075SPeter Maydell resync_counter(s);
24093739075SPeter Maydell s->pscntr = value;
24193739075SPeter Maydell break;
2429a52d999SPeter Maydell default:
24339901aeaSPeter Maydell bad_offset:
2449a52d999SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
2459a52d999SPeter Maydell "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
2469a52d999SPeter Maydell break;
2479a52d999SPeter Maydell }
2489a52d999SPeter Maydell }
2499a52d999SPeter Maydell
2509a52d999SPeter Maydell static const MemoryRegionOps mps2_fpgaio_ops = {
2519a52d999SPeter Maydell .read = mps2_fpgaio_read,
2529a52d999SPeter Maydell .write = mps2_fpgaio_write,
2539a52d999SPeter Maydell .endianness = DEVICE_LITTLE_ENDIAN,
2549a52d999SPeter Maydell };
2559a52d999SPeter Maydell
mps2_fpgaio_reset(DeviceState * dev)2569a52d999SPeter Maydell static void mps2_fpgaio_reset(DeviceState *dev)
2579a52d999SPeter Maydell {
2589a52d999SPeter Maydell MPS2FPGAIO *s = MPS2_FPGAIO(dev);
259a1982f90SPeter Maydell int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2609a52d999SPeter Maydell
2619a52d999SPeter Maydell trace_mps2_fpgaio_reset();
2629a52d999SPeter Maydell s->led0 = 0;
2639a52d999SPeter Maydell s->prescale = 0;
2649a52d999SPeter Maydell s->misc = 0;
265a1982f90SPeter Maydell s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1);
266a1982f90SPeter Maydell s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100);
26793739075SPeter Maydell s->counter = 0;
26893739075SPeter Maydell s->pscntr = 0;
26993739075SPeter Maydell s->pscntr_sync_ticks = now;
27065ad1da2SPhilippe Mathieu-Daudé
271e8556f43SPeter Maydell for (size_t i = 0; i < s->num_leds; i++) {
27265ad1da2SPhilippe Mathieu-Daudé device_cold_reset(DEVICE(s->led[i]));
27365ad1da2SPhilippe Mathieu-Daudé }
2749a52d999SPeter Maydell }
2759a52d999SPeter Maydell
mps2_fpgaio_init(Object * obj)2769a52d999SPeter Maydell static void mps2_fpgaio_init(Object *obj)
2779a52d999SPeter Maydell {
2789a52d999SPeter Maydell SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2799a52d999SPeter Maydell MPS2FPGAIO *s = MPS2_FPGAIO(obj);
2809a52d999SPeter Maydell
2819a52d999SPeter Maydell memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s,
2829a52d999SPeter Maydell "mps2-fpgaio", 0x1000);
2839a52d999SPeter Maydell sysbus_init_mmio(sbd, &s->iomem);
2849a52d999SPeter Maydell }
2859a52d999SPeter Maydell
mps2_fpgaio_realize(DeviceState * dev,Error ** errp)28665ad1da2SPhilippe Mathieu-Daudé static void mps2_fpgaio_realize(DeviceState *dev, Error **errp)
28765ad1da2SPhilippe Mathieu-Daudé {
28865ad1da2SPhilippe Mathieu-Daudé MPS2FPGAIO *s = MPS2_FPGAIO(dev);
289e8556f43SPeter Maydell uint32_t i;
29065ad1da2SPhilippe Mathieu-Daudé
291e8556f43SPeter Maydell if (s->num_leds > MPS2FPGAIO_MAX_LEDS) {
292e8556f43SPeter Maydell error_setg(errp, "num-leds cannot be greater than %d",
293e8556f43SPeter Maydell MPS2FPGAIO_MAX_LEDS);
294e8556f43SPeter Maydell return;
295e8556f43SPeter Maydell }
296e8556f43SPeter Maydell
297e8556f43SPeter Maydell for (i = 0; i < s->num_leds; i++) {
298e8556f43SPeter Maydell g_autofree char *ledname = g_strdup_printf("USERLED%d", i);
299e8556f43SPeter Maydell s->led[i] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH,
300e8556f43SPeter Maydell LED_COLOR_GREEN, ledname);
301e8556f43SPeter Maydell }
30265ad1da2SPhilippe Mathieu-Daudé }
30365ad1da2SPhilippe Mathieu-Daudé
3047fa85991SPeter Maydell static const VMStateDescription mps2_fpgaio_vmstate = {
3057fa85991SPeter Maydell .name = "mps2-fpgaio",
30639901aeaSPeter Maydell .version_id = 3,
30739901aeaSPeter Maydell .minimum_version_id = 3,
308e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
3097fa85991SPeter Maydell VMSTATE_UINT32(led0, MPS2FPGAIO),
3107fa85991SPeter Maydell VMSTATE_UINT32(prescale, MPS2FPGAIO),
3117fa85991SPeter Maydell VMSTATE_UINT32(misc, MPS2FPGAIO),
31239901aeaSPeter Maydell VMSTATE_UINT32(dbgctrl, MPS2FPGAIO),
313a1982f90SPeter Maydell VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
314a1982f90SPeter Maydell VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
31593739075SPeter Maydell VMSTATE_UINT32(counter, MPS2FPGAIO),
31693739075SPeter Maydell VMSTATE_UINT32(pscntr, MPS2FPGAIO),
31793739075SPeter Maydell VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO),
318a1982f90SPeter Maydell VMSTATE_END_OF_LIST()
319a1982f90SPeter Maydell },
3209a52d999SPeter Maydell };
3219a52d999SPeter Maydell
3229a52d999SPeter Maydell static Property mps2_fpgaio_properties[] = {
3239a52d999SPeter Maydell /* Frequency of the prescale counter */
3249a52d999SPeter Maydell DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000),
325e8556f43SPeter Maydell /* Number of LEDs controlled by LED0 register */
326e8556f43SPeter Maydell DEFINE_PROP_UINT32("num-leds", MPS2FPGAIO, num_leds, 2),
327b2234223SPeter Maydell DEFINE_PROP_BOOL("has-switches", MPS2FPGAIO, has_switches, false),
32839901aeaSPeter Maydell DEFINE_PROP_BOOL("has-dbgctrl", MPS2FPGAIO, has_dbgctrl, false),
3299a52d999SPeter Maydell DEFINE_PROP_END_OF_LIST(),
3309a52d999SPeter Maydell };
3319a52d999SPeter Maydell
mps2_fpgaio_class_init(ObjectClass * klass,void * data)3329a52d999SPeter Maydell static void mps2_fpgaio_class_init(ObjectClass *klass, void *data)
3339a52d999SPeter Maydell {
3349a52d999SPeter Maydell DeviceClass *dc = DEVICE_CLASS(klass);
3359a52d999SPeter Maydell
3369a52d999SPeter Maydell dc->vmsd = &mps2_fpgaio_vmstate;
33765ad1da2SPhilippe Mathieu-Daudé dc->realize = mps2_fpgaio_realize;
338*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, mps2_fpgaio_reset);
3394f67d30bSMarc-André Lureau device_class_set_props(dc, mps2_fpgaio_properties);
3409a52d999SPeter Maydell }
3419a52d999SPeter Maydell
3429a52d999SPeter Maydell static const TypeInfo mps2_fpgaio_info = {
3439a52d999SPeter Maydell .name = TYPE_MPS2_FPGAIO,
3449a52d999SPeter Maydell .parent = TYPE_SYS_BUS_DEVICE,
3459a52d999SPeter Maydell .instance_size = sizeof(MPS2FPGAIO),
3469a52d999SPeter Maydell .instance_init = mps2_fpgaio_init,
3479a52d999SPeter Maydell .class_init = mps2_fpgaio_class_init,
3489a52d999SPeter Maydell };
3499a52d999SPeter Maydell
mps2_fpgaio_register_types(void)3509a52d999SPeter Maydell static void mps2_fpgaio_register_types(void)
3519a52d999SPeter Maydell {
3529a52d999SPeter Maydell type_register_static(&mps2_fpgaio_info);
3539a52d999SPeter Maydell }
3549a52d999SPeter Maydell
3559a52d999SPeter Maydell type_init(mps2_fpgaio_register_types);
356