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