xref: /qemu/hw/misc/mps2-fpgaio.c (revision a1982f90a42b22f2858e7d8497ab7223cd49b65d)
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"
209a52d999SPeter Maydell #include "qapi/error.h"
219a52d999SPeter Maydell #include "trace.h"
229a52d999SPeter Maydell #include "hw/sysbus.h"
239a52d999SPeter Maydell #include "hw/registerfields.h"
249a52d999SPeter Maydell #include "hw/misc/mps2-fpgaio.h"
25*a1982f90SPeter Maydell #include "qemu/timer.h"
269a52d999SPeter Maydell 
279a52d999SPeter Maydell REG32(LED0, 0)
289a52d999SPeter Maydell REG32(BUTTON, 8)
299a52d999SPeter Maydell REG32(CLK1HZ, 0x10)
309a52d999SPeter Maydell REG32(CLK100HZ, 0x14)
319a52d999SPeter Maydell REG32(COUNTER, 0x18)
329a52d999SPeter Maydell REG32(PRESCALE, 0x1c)
339a52d999SPeter Maydell REG32(PSCNTR, 0x20)
349a52d999SPeter Maydell REG32(MISC, 0x4c)
359a52d999SPeter Maydell 
36*a1982f90SPeter Maydell static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq)
37*a1982f90SPeter Maydell {
38*a1982f90SPeter Maydell     return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND);
39*a1982f90SPeter Maydell }
40*a1982f90SPeter Maydell 
41*a1982f90SPeter Maydell static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq)
42*a1982f90SPeter Maydell {
43*a1982f90SPeter Maydell     return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq);
44*a1982f90SPeter Maydell }
45*a1982f90SPeter Maydell 
469a52d999SPeter Maydell static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size)
479a52d999SPeter Maydell {
489a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
499a52d999SPeter Maydell     uint64_t r;
50*a1982f90SPeter Maydell     int64_t now;
519a52d999SPeter Maydell 
529a52d999SPeter Maydell     switch (offset) {
539a52d999SPeter Maydell     case A_LED0:
549a52d999SPeter Maydell         r = s->led0;
559a52d999SPeter Maydell         break;
569a52d999SPeter Maydell     case A_BUTTON:
579a52d999SPeter Maydell         /* User-pressable board buttons. We don't model that, so just return
589a52d999SPeter Maydell          * zeroes.
599a52d999SPeter Maydell          */
609a52d999SPeter Maydell         r = 0;
619a52d999SPeter Maydell         break;
629a52d999SPeter Maydell     case A_PRESCALE:
639a52d999SPeter Maydell         r = s->prescale;
649a52d999SPeter Maydell         break;
659a52d999SPeter Maydell     case A_MISC:
669a52d999SPeter Maydell         r = s->misc;
679a52d999SPeter Maydell         break;
689a52d999SPeter Maydell     case A_CLK1HZ:
69*a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
70*a1982f90SPeter Maydell         r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1);
71*a1982f90SPeter Maydell         break;
729a52d999SPeter Maydell     case A_CLK100HZ:
73*a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
74*a1982f90SPeter Maydell         r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100);
75*a1982f90SPeter Maydell         break;
769a52d999SPeter Maydell     case A_COUNTER:
779a52d999SPeter Maydell     case A_PSCNTR:
789a52d999SPeter Maydell         qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n");
799a52d999SPeter Maydell         r = 0;
809a52d999SPeter Maydell         break;
819a52d999SPeter Maydell     default:
829a52d999SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
839a52d999SPeter Maydell                       "MPS2 FPGAIO read: bad offset %x\n", (int) offset);
849a52d999SPeter Maydell         r = 0;
859a52d999SPeter Maydell         break;
869a52d999SPeter Maydell     }
879a52d999SPeter Maydell 
889a52d999SPeter Maydell     trace_mps2_fpgaio_read(offset, r, size);
899a52d999SPeter Maydell     return r;
909a52d999SPeter Maydell }
919a52d999SPeter Maydell 
929a52d999SPeter Maydell static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value,
939a52d999SPeter Maydell                               unsigned size)
949a52d999SPeter Maydell {
959a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(opaque);
96*a1982f90SPeter Maydell     int64_t now;
979a52d999SPeter Maydell 
989a52d999SPeter Maydell     trace_mps2_fpgaio_write(offset, value, size);
999a52d999SPeter Maydell 
1009a52d999SPeter Maydell     switch (offset) {
1019a52d999SPeter Maydell     case A_LED0:
1029a52d999SPeter Maydell         /* LED bits [1:0] control board LEDs. We don't currently have
1039a52d999SPeter Maydell          * a mechanism for displaying this graphically, so use a trace event.
1049a52d999SPeter Maydell          */
1059a52d999SPeter Maydell         trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.',
1069a52d999SPeter Maydell                                value & 0x01 ? '*' : '.');
1079a52d999SPeter Maydell         s->led0 = value & 0x3;
1089a52d999SPeter Maydell         break;
1099a52d999SPeter Maydell     case A_PRESCALE:
1109a52d999SPeter Maydell         s->prescale = value;
1119a52d999SPeter Maydell         break;
1129a52d999SPeter Maydell     case A_MISC:
1139a52d999SPeter Maydell         /* These are control bits for some of the other devices on the
1149a52d999SPeter Maydell          * board (SPI, CLCD, etc). We don't implement that yet, so just
1159a52d999SPeter Maydell          * make the bits read as written.
1169a52d999SPeter Maydell          */
1179a52d999SPeter Maydell         qemu_log_mask(LOG_UNIMP,
1189a52d999SPeter Maydell                       "MPS2 FPGAIO: MISC control bits unimplemented\n");
1199a52d999SPeter Maydell         s->misc = value;
1209a52d999SPeter Maydell         break;
121*a1982f90SPeter Maydell     case A_CLK1HZ:
122*a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
123*a1982f90SPeter Maydell         s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1);
124*a1982f90SPeter Maydell         break;
125*a1982f90SPeter Maydell     case A_CLK100HZ:
126*a1982f90SPeter Maydell         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
127*a1982f90SPeter Maydell         s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100);
128*a1982f90SPeter Maydell         break;
1299a52d999SPeter Maydell     default:
1309a52d999SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
1319a52d999SPeter Maydell                       "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset);
1329a52d999SPeter Maydell         break;
1339a52d999SPeter Maydell     }
1349a52d999SPeter Maydell }
1359a52d999SPeter Maydell 
1369a52d999SPeter Maydell static const MemoryRegionOps mps2_fpgaio_ops = {
1379a52d999SPeter Maydell     .read = mps2_fpgaio_read,
1389a52d999SPeter Maydell     .write = mps2_fpgaio_write,
1399a52d999SPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
1409a52d999SPeter Maydell };
1419a52d999SPeter Maydell 
1429a52d999SPeter Maydell static void mps2_fpgaio_reset(DeviceState *dev)
1439a52d999SPeter Maydell {
1449a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(dev);
145*a1982f90SPeter Maydell     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1469a52d999SPeter Maydell 
1479a52d999SPeter Maydell     trace_mps2_fpgaio_reset();
1489a52d999SPeter Maydell     s->led0 = 0;
1499a52d999SPeter Maydell     s->prescale = 0;
1509a52d999SPeter Maydell     s->misc = 0;
151*a1982f90SPeter Maydell     s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1);
152*a1982f90SPeter Maydell     s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100);
1539a52d999SPeter Maydell }
1549a52d999SPeter Maydell 
1559a52d999SPeter Maydell static void mps2_fpgaio_init(Object *obj)
1569a52d999SPeter Maydell {
1579a52d999SPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1589a52d999SPeter Maydell     MPS2FPGAIO *s = MPS2_FPGAIO(obj);
1599a52d999SPeter Maydell 
1609a52d999SPeter Maydell     memory_region_init_io(&s->iomem, obj, &mps2_fpgaio_ops, s,
1619a52d999SPeter Maydell                           "mps2-fpgaio", 0x1000);
1629a52d999SPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
1639a52d999SPeter Maydell }
1649a52d999SPeter Maydell 
165*a1982f90SPeter Maydell static bool mps2_fpgaio_counters_needed(void *opaque)
166*a1982f90SPeter Maydell {
167*a1982f90SPeter Maydell     /* Currently vmstate.c insists all subsections have a 'needed' function */
168*a1982f90SPeter Maydell     return true;
169*a1982f90SPeter Maydell }
170*a1982f90SPeter Maydell 
171*a1982f90SPeter Maydell static const VMStateDescription mps2_fpgaio_counters_vmstate = {
172*a1982f90SPeter Maydell     .name = "mps2-fpgaio/counters",
173*a1982f90SPeter Maydell     .version_id = 1,
174*a1982f90SPeter Maydell     .minimum_version_id = 1,
175*a1982f90SPeter Maydell     .needed = mps2_fpgaio_counters_needed,
176*a1982f90SPeter Maydell     .fields = (VMStateField[]) {
177*a1982f90SPeter Maydell         VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO),
178*a1982f90SPeter Maydell         VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO),
179*a1982f90SPeter Maydell         VMSTATE_END_OF_LIST()
180*a1982f90SPeter Maydell     }
181*a1982f90SPeter Maydell };
182*a1982f90SPeter Maydell 
1839a52d999SPeter Maydell static const VMStateDescription mps2_fpgaio_vmstate = {
1849a52d999SPeter Maydell     .name = "mps2-fpgaio",
1859a52d999SPeter Maydell     .version_id = 1,
1869a52d999SPeter Maydell     .minimum_version_id = 1,
1879a52d999SPeter Maydell     .fields = (VMStateField[]) {
1889a52d999SPeter Maydell         VMSTATE_UINT32(led0, MPS2FPGAIO),
1899a52d999SPeter Maydell         VMSTATE_UINT32(prescale, MPS2FPGAIO),
1909a52d999SPeter Maydell         VMSTATE_UINT32(misc, MPS2FPGAIO),
1919a52d999SPeter Maydell         VMSTATE_END_OF_LIST()
192*a1982f90SPeter Maydell     },
193*a1982f90SPeter Maydell     .subsections = (const VMStateDescription*[]) {
194*a1982f90SPeter Maydell         &mps2_fpgaio_counters_vmstate,
195*a1982f90SPeter Maydell         NULL
1969a52d999SPeter Maydell     }
1979a52d999SPeter Maydell };
1989a52d999SPeter Maydell 
1999a52d999SPeter Maydell static Property mps2_fpgaio_properties[] = {
2009a52d999SPeter Maydell     /* Frequency of the prescale counter */
2019a52d999SPeter Maydell     DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000),
2029a52d999SPeter Maydell     DEFINE_PROP_END_OF_LIST(),
2039a52d999SPeter Maydell };
2049a52d999SPeter Maydell 
2059a52d999SPeter Maydell static void mps2_fpgaio_class_init(ObjectClass *klass, void *data)
2069a52d999SPeter Maydell {
2079a52d999SPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
2089a52d999SPeter Maydell 
2099a52d999SPeter Maydell     dc->vmsd = &mps2_fpgaio_vmstate;
2109a52d999SPeter Maydell     dc->reset = mps2_fpgaio_reset;
2119a52d999SPeter Maydell     dc->props = mps2_fpgaio_properties;
2129a52d999SPeter Maydell }
2139a52d999SPeter Maydell 
2149a52d999SPeter Maydell static const TypeInfo mps2_fpgaio_info = {
2159a52d999SPeter Maydell     .name = TYPE_MPS2_FPGAIO,
2169a52d999SPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
2179a52d999SPeter Maydell     .instance_size = sizeof(MPS2FPGAIO),
2189a52d999SPeter Maydell     .instance_init = mps2_fpgaio_init,
2199a52d999SPeter Maydell     .class_init = mps2_fpgaio_class_init,
2209a52d999SPeter Maydell };
2219a52d999SPeter Maydell 
2229a52d999SPeter Maydell static void mps2_fpgaio_register_types(void)
2239a52d999SPeter Maydell {
2249a52d999SPeter Maydell     type_register_static(&mps2_fpgaio_info);
2259a52d999SPeter Maydell }
2269a52d999SPeter Maydell 
2279a52d999SPeter Maydell type_init(mps2_fpgaio_register_types);
228