xref: /qemu/hw/misc/mps2-fpgaio.c (revision a27bd6c779badb8d76e4430d810ef710a1b98f4e)
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:
159a52d999SPeter Maydell  * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html
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"
27*a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
28a1982f90SPeter Maydell #include "qemu/timer.h"
299a52d999SPeter Maydell 
309a52d999SPeter Maydell REG32(LED0, 0)
319a52d999SPeter Maydell REG32(BUTTON, 8)
329a52d999SPeter Maydell REG32(CLK1HZ, 0x10)
339a52d999SPeter Maydell REG32(CLK100HZ, 0x14)
349a52d999SPeter Maydell REG32(COUNTER, 0x18)
359a52d999SPeter Maydell REG32(PRESCALE, 0x1c)
369a52d999SPeter Maydell REG32(PSCNTR, 0x20)
379a52d999SPeter Maydell REG32(MISC, 0x4c)
389a52d999SPeter Maydell 
39a1982f90SPeter Maydell static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq)
40a1982f90SPeter Maydell {
41a1982f90SPeter Maydell     return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND);
42a1982f90SPeter Maydell }
43a1982f90SPeter Maydell 
44a1982f90SPeter Maydell static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq)
45a1982f90SPeter Maydell {
46a1982f90SPeter Maydell     return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq);
47a1982f90SPeter Maydell }
48a1982f90SPeter Maydell 
4993739075SPeter Maydell static void resync_counter(MPS2FPGAIO *s)
5093739075SPeter Maydell {
5193739075SPeter Maydell     /*
5293739075SPeter Maydell      * Update s->counter and s->pscntr to their true current values
5393739075SPeter Maydell      * by calculating how many times PSCNTR has ticked since the
5493739075SPeter Maydell      * last time we did a resync.
5593739075SPeter Maydell      */
5693739075SPeter Maydell     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
5793739075SPeter Maydell     int64_t elapsed = now - s->pscntr_sync_ticks;
5893739075SPeter Maydell 
5993739075SPeter Maydell     /*
6093739075SPeter Maydell      * Round elapsed down to a whole number of PSCNTR ticks, so we don't
6193739075SPeter Maydell      * lose time if we do multiple resyncs in a single tick.
6293739075SPeter Maydell      */
6393739075SPeter Maydell     uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND);
6493739075SPeter Maydell 
6593739075SPeter Maydell     /*
6693739075SPeter Maydell      * Work out what PSCNTR and COUNTER have moved to. We assume that
6793739075SPeter Maydell      * PSCNTR reloads from PRESCALE one tick-period after it hits zero,
6893739075SPeter Maydell      * and that COUNTER increments at the same moment.
6993739075SPeter Maydell      */
7093739075SPeter Maydell     if (ticks == 0) {
7193739075SPeter Maydell         /* We haven't ticked since the last time we were asked */
7293739075SPeter Maydell         return;
7393739075SPeter Maydell     } else if (ticks < s->pscntr) {
7493739075SPeter Maydell         /* We haven't yet reached zero, just reduce the PSCNTR */
7593739075SPeter Maydell         s->pscntr -= ticks;
7693739075SPeter Maydell     } else {
7793739075SPeter Maydell         if (s->prescale == 0) {
7893739075SPeter Maydell             /*
7993739075SPeter Maydell              * If the reload value is zero then the PSCNTR will stick
8093739075SPeter Maydell              * at zero once it reaches it, and so we will increment
8193739075SPeter Maydell              * COUNTER every tick after that.
8293739075SPeter Maydell              */
8393739075SPeter Maydell             s->counter += ticks - s->pscntr;
8493739075SPeter Maydell             s->pscntr = 0;
8593739075SPeter Maydell         } else {
8693739075SPeter Maydell             /*
8793739075SPeter Maydell              * This is the complicated bit. This ASCII art diagram gives an
8893739075SPeter Maydell              * example with PRESCALE==5 PSCNTR==7:
8993739075SPeter Maydell              *
9093739075SPeter Maydell              * ticks  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
9193739075SPeter Maydell              * PSCNTR 7  6  5  4  3  2  1  0  5  4  3  2  1  0  5
9293739075SPeter Maydell              * cinc                           1                 2
9393739075SPeter Maydell              * y            0  1  2  3  4  5  6  7  8  9 10 11 12
9493739075SPeter Maydell              * x            0  1  2  3  4  5  0  1  2  3  4  5  0
9593739075SPeter Maydell              *
9693739075SPeter Maydell              * where x = y % (s->prescale + 1)
9793739075SPeter Maydell              * and so PSCNTR = s->prescale - x
9893739075SPeter Maydell              * and COUNTER is incremented by y / (s->prescale + 1)
9993739075SPeter Maydell              *
10093739075SPeter Maydell              * The case where PSCNTR < PRESCALE works out the same,
10193739075SPeter Maydell              * though we must be careful to calculate y as 64-bit unsigned
10293739075SPeter Maydell              * for all parts of the expression.
10393739075SPeter Maydell              * y < 0 is not possible because that implies ticks < s->pscntr.
10493739075SPeter Maydell              */
10593739075SPeter Maydell             uint64_t y = ticks - s->pscntr + s->prescale;
10693739075SPeter Maydell             s->pscntr = s->prescale - (y % (s->prescale + 1));
10793739075SPeter Maydell             s->counter += y / (s->prescale + 1);
10893739075SPeter Maydell         }
10993739075SPeter Maydell     }
11093739075SPeter Maydell 
11193739075SPeter Maydell     /*
11293739075SPeter Maydell      * Only advance the sync time to the timestamp of the last PSCNTR tick,
11393739075SPeter Maydell      * not all the way to 'now', so we don't lose time if we do multiple
11493739075SPeter Maydell      * resyncs in a single tick.
11593739075SPeter Maydell      */
11693739075SPeter Maydell     s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND,
11793739075SPeter Maydell                                      s->prescale_clk);
11893739075SPeter Maydell }
11993739075SPeter Maydell 
1209a52d999SPeter Maydell static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
1219a52d999SPeter Maydell {
1229a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
1239a52d999SPeter Maydell     uint64_t r;
124a1982f90SPeter Maydell     int64_t now;
1259a52d999SPeter Maydell 
1269a52d999SPeter Maydell     switch (offset) {
1279a52d999SPeter Maydell     case A_LED0:
1289a52d999SPeter Maydell         r = s->led0;
1299a52d999SPeter Maydell         break;
1309a52d999SPeter Maydell     case A_BUTTON:
1319a52d999SPeter Maydell         /* User-pressable board buttons. We don't model that, so just return
1329a52d999SPeter Maydell          * zeroes.
1339a52d999SPeter Maydell          */
1349a52d999SPeter Maydell         r = 0;
1359a52d999SPeter Maydell         break;
1369a52d999SPeter Maydell     case A_PRESCALE:
1379a52d999SPeter Maydell         r = s->prescale;
1389a52d999SPeter Maydell         break;
1399a52d999SPeter Maydell     case A_MISC:
1409a52d999SPeter Maydell         r = s->misc;
1419a52d999SPeter Maydell         break;
1429a52d999SPeter Maydell     case A_CLK1HZ:
143a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
144a1982f90SPeter Maydell         r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1);
145a1982f90SPeter Maydell         break;
1469a52d999SPeter Maydell     case A_CLK100HZ:
147a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
148a1982f90SPeter Maydell         r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100);
149a1982f90SPeter Maydell         break;
1509a52d999SPeter Maydell     case A_COUNTER:
15193739075SPeter Maydell         resync_counter(s);
15293739075SPeter Maydell         r = s->counter;
15393739075SPeter Maydell         break;
1549a52d999SPeter Maydell     case A_PSCNTR:
15593739075SPeter Maydell         resync_counter(s);
15693739075SPeter Maydell         r = s->pscntr;
1579a52d999SPeter Maydell         break;
1589a52d999SPeter Maydell     default:
1599a52d999SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1609a52d999SPeter Maydell                       "MPS2 FPGAIO read: bad offset %x\n", (int) offset);
1619a52d999SPeter Maydell         r = 0;
1629a52d999SPeter Maydell         break;
1639a52d999SPeter Maydell     }
1649a52d999SPeter Maydell 
1659a52d999SPeter Maydell     trace_mps2_fpgaio_read(offset, r, size);
1669a52d999SPeter Maydell     return r;
1679a52d999SPeter Maydell }
1689a52d999SPeter Maydell 
1699a52d999SPeter Maydell static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
1709a52d999SPeter Maydell                               unsigned size)
1719a52d999SPeter Maydell {
1729a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
173a1982f90SPeter Maydell     int64_t now;
1749a52d999SPeter Maydell 
1759a52d999SPeter Maydell     trace_mps2_fpgaio_write(offset, value, size);
1769a52d999SPeter Maydell 
1779a52d999SPeter Maydell     switch (offset) {
1789a52d999SPeter Maydell     case A_LED0:
1799a52d999SPeter Maydell         /* LED bits [1:0] control board LEDs. We don't currently have
1809a52d999SPeter Maydell          * a mechanism for displaying this graphically, so use a trace event.
1819a52d999SPeter Maydell          */
1829a52d999SPeter Maydell         trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.',
1839a52d999SPeter Maydell                                value & 0x01 ? '*' : '.');
1849a52d999SPeter Maydell         s->led0 = value & 0x3;
1859a52d999SPeter Maydell         break;
1869a52d999SPeter Maydell     case A_PRESCALE:
18793739075SPeter Maydell         resync_counter(s);
1889a52d999SPeter Maydell         s->prescale = value;
1899a52d999SPeter Maydell         break;
1909a52d999SPeter Maydell     case A_MISC:
1919a52d999SPeter Maydell         /* These are control bits for some of the other devices on the
1929a52d999SPeter Maydell          * board (SPI, CLCD, etc). We don't implement that yet, so just
1939a52d999SPeter Maydell          * make the bits read as written.
1949a52d999SPeter Maydell          */
1959a52d999SPeter Maydell         qemu_log_mask(LOG_UNIMP,
1969a52d999SPeter Maydell                       "MPS2 FPGAIO: MISC control bits unimplemented\n");
1979a52d999SPeter Maydell         s->misc = value;
1989a52d999SPeter Maydell         break;
199a1982f90SPeter Maydell     case A_CLK1HZ:
200a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
201a1982f90SPeter Maydell         s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1);
202a1982f90SPeter Maydell         break;
203a1982f90SPeter Maydell     case A_CLK100HZ:
204a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
205a1982f90SPeter Maydell         s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100);
206a1982f90SPeter Maydell         break;
20793739075SPeter Maydell     case A_COUNTER:
20893739075SPeter Maydell         resync_counter(s);
20993739075SPeter Maydell         s->counter = value;
21093739075SPeter Maydell         break;
21193739075SPeter Maydell     case A_PSCNTR:
21293739075SPeter Maydell         resync_counter(s);
21393739075SPeter Maydell         s->pscntr = value;
21493739075SPeter Maydell         break;
2159a52d999SPeter Maydell     default:
2169a52d999SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
2179a52d999SPeter Maydell                       "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
2189a52d999SPeter Maydell         break;
2199a52d999SPeter Maydell     }
2209a52d999SPeter Maydell }
2219a52d999SPeter Maydell 
2229a52d999SPeter Maydell static const MemoryRegionOps mps2_fpgaio_ops = {
2239a52d999SPeter Maydell     .read = mps2_fpgaio_read,
2249a52d999SPeter Maydell     .write = mps2_fpgaio_write,
2259a52d999SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
2269a52d999SPeter Maydell };
2279a52d999SPeter Maydell 
2289a52d999SPeter Maydell static void mps2_fpgaio_reset(DeviceState *dev)
2299a52d999SPeter Maydell {
2309a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(dev);
231a1982f90SPeter Maydell     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2329a52d999SPeter Maydell 
2339a52d999SPeter Maydell     trace_mps2_fpgaio_reset();
2349a52d999SPeter Maydell     s->led0 = 0;
2359a52d999SPeter Maydell     s->prescale = 0;
2369a52d999SPeter Maydell     s->misc = 0;
237a1982f90SPeter Maydell     s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1);
238a1982f90SPeter Maydell     s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100);
23993739075SPeter Maydell     s->counter = 0;
24093739075SPeter Maydell     s->pscntr = 0;
24193739075SPeter Maydell     s->pscntr_sync_ticks = now;
2429a52d999SPeter Maydell }
2439a52d999SPeter Maydell 
2449a52d999SPeter Maydell static void mps2_fpgaio_init(Object *obj)
2459a52d999SPeter Maydell {
2469a52d999SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2479a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(obj);
2489a52d999SPeter Maydell 
2499a52d999SPeter Maydell     memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s,
2509a52d999SPeter Maydell                           "mps2-fpgaio", 0x1000);
2519a52d999SPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
2529a52d999SPeter Maydell }
2539a52d999SPeter Maydell 
254a1982f90SPeter Maydell static bool mps2_fpgaio_counters_needed(void *opaque)
255a1982f90SPeter Maydell {
256a1982f90SPeter Maydell     /* Currently vmstate.c insists all subsections have a 'needed' function */
257a1982f90SPeter Maydell     return true;
258a1982f90SPeter Maydell }
259a1982f90SPeter Maydell 
260a1982f90SPeter Maydell static const VMStateDescription mps2_fpgaio_counters_vmstate = {
261a1982f90SPeter Maydell     .name = "mps2-fpgaio/counters",
26293739075SPeter Maydell     .version_id = 2,
26393739075SPeter Maydell     .minimum_version_id = 2,
264a1982f90SPeter Maydell     .needed = mps2_fpgaio_counters_needed,
265a1982f90SPeter Maydell     .fields = (VMStateField[]) {
266a1982f90SPeter Maydell         VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
267a1982f90SPeter Maydell         VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
26893739075SPeter Maydell         VMSTATE_UINT32(counter, MPS2FPGAIO),
26993739075SPeter Maydell         VMSTATE_UINT32(pscntr, MPS2FPGAIO),
27093739075SPeter Maydell         VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO),
271a1982f90SPeter Maydell         VMSTATE_END_OF_LIST()
272a1982f90SPeter Maydell     }
273a1982f90SPeter Maydell };
274a1982f90SPeter Maydell 
2759a52d999SPeter Maydell static const VMStateDescription mps2_fpgaio_vmstate = {
2769a52d999SPeter Maydell     .name = "mps2-fpgaio",
2779a52d999SPeter Maydell     .version_id = 1,
2789a52d999SPeter Maydell     .minimum_version_id = 1,
2799a52d999SPeter Maydell     .fields = (VMStateField[]) {
2809a52d999SPeter Maydell         VMSTATE_UINT32(led0, MPS2FPGAIO),
2819a52d999SPeter Maydell         VMSTATE_UINT32(prescale, MPS2FPGAIO),
2829a52d999SPeter Maydell         VMSTATE_UINT32(misc, MPS2FPGAIO),
2839a52d999SPeter Maydell         VMSTATE_END_OF_LIST()
284a1982f90SPeter Maydell     },
285a1982f90SPeter Maydell     .subsections = (const VMStateDescription*[]) {
286a1982f90SPeter Maydell         &mps2_fpgaio_counters_vmstate,
287a1982f90SPeter Maydell         NULL
2889a52d999SPeter Maydell     }
2899a52d999SPeter Maydell };
2909a52d999SPeter Maydell 
2919a52d999SPeter Maydell static Property mps2_fpgaio_properties[] = {
2929a52d999SPeter Maydell     /* Frequency of the prescale counter */
2939a52d999SPeter Maydell     DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000),
2949a52d999SPeter Maydell     DEFINE_PROP_END_OF_LIST(),
2959a52d999SPeter Maydell };
2969a52d999SPeter Maydell 
2979a52d999SPeter Maydell static void mps2_fpgaio_class_init(ObjectClass *klass, void *data)
2989a52d999SPeter Maydell {
2999a52d999SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
3009a52d999SPeter Maydell 
3019a52d999SPeter Maydell     dc->vmsd = &mps2_fpgaio_vmstate;
3029a52d999SPeter Maydell     dc->reset = mps2_fpgaio_reset;
3039a52d999SPeter Maydell     dc->props = mps2_fpgaio_properties;
3049a52d999SPeter Maydell }
3059a52d999SPeter Maydell 
3069a52d999SPeter Maydell static const TypeInfo mps2_fpgaio_info = {
3079a52d999SPeter Maydell     .name = TYPE_MPS2_FPGAIO,
3089a52d999SPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
3099a52d999SPeter Maydell     .instance_size = sizeof(MPS2FPGAIO),
3109a52d999SPeter Maydell     .instance_init = mps2_fpgaio_init,
3119a52d999SPeter Maydell     .class_init = mps2_fpgaio_class_init,
3129a52d999SPeter Maydell };
3139a52d999SPeter Maydell 
3149a52d999SPeter Maydell static void mps2_fpgaio_register_types(void)
3159a52d999SPeter Maydell {
3169a52d999SPeter Maydell     type_register_static(&mps2_fpgaio_info);
3179a52d999SPeter Maydell }
3189a52d999SPeter Maydell 
3199a52d999SPeter Maydell type_init(mps2_fpgaio_register_types);
320