xref: /qemu/hw/misc/mps2-scc.c (revision 435db7eb)
1dd73185bSPeter Maydell /*
2dd73185bSPeter Maydell  * ARM MPS2 SCC emulation
3dd73185bSPeter Maydell  *
4dd73185bSPeter Maydell  * Copyright (c) 2017 Linaro Limited
5dd73185bSPeter Maydell  * Written by Peter Maydell
6dd73185bSPeter Maydell  *
7dd73185bSPeter Maydell  *  This program is free software; you can redistribute it and/or modify
8dd73185bSPeter Maydell  *  it under the terms of the GNU General Public License version 2 or
9dd73185bSPeter Maydell  *  (at your option) any later version.
10dd73185bSPeter Maydell  */
11dd73185bSPeter Maydell 
12dd73185bSPeter Maydell /* This is a model of the SCC (Serial Communication Controller)
13dd73185bSPeter Maydell  * found in the FPGA images of MPS2 development boards.
14dd73185bSPeter Maydell  *
15dd73185bSPeter Maydell  * Documentation of it can be found in the MPS2 TRM:
16dd73185bSPeter Maydell  * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100112_0100_03_en/index.html
17dd73185bSPeter Maydell  * and also in the Application Notes documenting individual FPGA images.
18dd73185bSPeter Maydell  */
19dd73185bSPeter Maydell 
20dd73185bSPeter Maydell #include "qemu/osdep.h"
21dd73185bSPeter Maydell #include "qemu/log.h"
220b8fa32fSMarkus Armbruster #include "qemu/module.h"
23*435db7ebSPhilippe Mathieu-Daudé #include "qemu/bitops.h"
24dd73185bSPeter Maydell #include "trace.h"
25dd73185bSPeter Maydell #include "hw/sysbus.h"
26d6454270SMarkus Armbruster #include "migration/vmstate.h"
27dd73185bSPeter Maydell #include "hw/registerfields.h"
28dd73185bSPeter Maydell #include "hw/misc/mps2-scc.h"
29*435db7ebSPhilippe Mathieu-Daudé #include "hw/misc/led.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
31dd73185bSPeter Maydell 
32dd73185bSPeter Maydell REG32(CFG0, 0)
33dd73185bSPeter Maydell REG32(CFG1, 4)
34dd73185bSPeter Maydell REG32(CFG3, 0xc)
35dd73185bSPeter Maydell REG32(CFG4, 0x10)
36dd73185bSPeter Maydell REG32(CFGDATA_RTN, 0xa0)
37dd73185bSPeter Maydell REG32(CFGDATA_OUT, 0xa4)
38dd73185bSPeter Maydell REG32(CFGCTRL, 0xa8)
39dd73185bSPeter Maydell     FIELD(CFGCTRL, DEVICE, 0, 12)
40dd73185bSPeter Maydell     FIELD(CFGCTRL, RES1, 12, 8)
41dd73185bSPeter Maydell     FIELD(CFGCTRL, FUNCTION, 20, 6)
42dd73185bSPeter Maydell     FIELD(CFGCTRL, RES2, 26, 4)
43dd73185bSPeter Maydell     FIELD(CFGCTRL, WRITE, 30, 1)
44dd73185bSPeter Maydell     FIELD(CFGCTRL, START, 31, 1)
45dd73185bSPeter Maydell REG32(CFGSTAT, 0xac)
46dd73185bSPeter Maydell     FIELD(CFGSTAT, DONE, 0, 1)
47dd73185bSPeter Maydell     FIELD(CFGSTAT, ERROR, 1, 1)
48dd73185bSPeter Maydell REG32(DLL, 0x100)
49dd73185bSPeter Maydell REG32(AID, 0xFF8)
50dd73185bSPeter Maydell REG32(ID, 0xFFC)
51dd73185bSPeter Maydell 
52dd73185bSPeter Maydell /* Handle a write via the SYS_CFG channel to the specified function/device.
53dd73185bSPeter Maydell  * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit).
54dd73185bSPeter Maydell  */
55dd73185bSPeter Maydell static bool scc_cfg_write(MPS2SCC *s, unsigned function,
56dd73185bSPeter Maydell                           unsigned device, uint32_t value)
57dd73185bSPeter Maydell {
58dd73185bSPeter Maydell     trace_mps2_scc_cfg_write(function, device, value);
59dd73185bSPeter Maydell 
60dd73185bSPeter Maydell     if (function != 1 || device >= NUM_OSCCLK) {
61dd73185bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
62dd73185bSPeter Maydell                       "MPS2 SCC config write: bad function %d device %d\n",
63dd73185bSPeter Maydell                       function, device);
64dd73185bSPeter Maydell         return false;
65dd73185bSPeter Maydell     }
66dd73185bSPeter Maydell 
67dd73185bSPeter Maydell     s->oscclk[device] = value;
68dd73185bSPeter Maydell     return true;
69dd73185bSPeter Maydell }
70dd73185bSPeter Maydell 
71dd73185bSPeter Maydell /* Handle a read via the SYS_CFG channel to the specified function/device.
72dd73185bSPeter Maydell  * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit),
73dd73185bSPeter Maydell  * or set *value on success.
74dd73185bSPeter Maydell  */
75dd73185bSPeter Maydell static bool scc_cfg_read(MPS2SCC *s, unsigned function,
76dd73185bSPeter Maydell                          unsigned device, uint32_t *value)
77dd73185bSPeter Maydell {
78dd73185bSPeter Maydell     if (function != 1 || device >= NUM_OSCCLK) {
79dd73185bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
80dd73185bSPeter Maydell                       "MPS2 SCC config read: bad function %d device %d\n",
81dd73185bSPeter Maydell                       function, device);
82dd73185bSPeter Maydell         return false;
83dd73185bSPeter Maydell     }
84dd73185bSPeter Maydell 
85dd73185bSPeter Maydell     *value = s->oscclk[device];
86dd73185bSPeter Maydell 
87dd73185bSPeter Maydell     trace_mps2_scc_cfg_read(function, device, *value);
88dd73185bSPeter Maydell     return true;
89dd73185bSPeter Maydell }
90dd73185bSPeter Maydell 
91dd73185bSPeter Maydell static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
92dd73185bSPeter Maydell {
93dd73185bSPeter Maydell     MPS2SCC *s = MPS2_SCC(opaque);
94dd73185bSPeter Maydell     uint64_t r;
95dd73185bSPeter Maydell 
96dd73185bSPeter Maydell     switch (offset) {
97dd73185bSPeter Maydell     case A_CFG0:
98dd73185bSPeter Maydell         r = s->cfg0;
99dd73185bSPeter Maydell         break;
100dd73185bSPeter Maydell     case A_CFG1:
101dd73185bSPeter Maydell         r = s->cfg1;
102dd73185bSPeter Maydell         break;
103dd73185bSPeter Maydell     case A_CFG3:
104dd73185bSPeter Maydell         /* These are user-settable DIP switches on the board. We don't
105dd73185bSPeter Maydell          * model that, so just return zeroes.
106dd73185bSPeter Maydell          */
107dd73185bSPeter Maydell         r = 0;
108dd73185bSPeter Maydell         break;
109dd73185bSPeter Maydell     case A_CFG4:
110dd73185bSPeter Maydell         r = s->cfg4;
111dd73185bSPeter Maydell         break;
112dd73185bSPeter Maydell     case A_CFGDATA_RTN:
113dd73185bSPeter Maydell         r = s->cfgdata_rtn;
114dd73185bSPeter Maydell         break;
115dd73185bSPeter Maydell     case A_CFGDATA_OUT:
116dd73185bSPeter Maydell         r = s->cfgdata_out;
117dd73185bSPeter Maydell         break;
118dd73185bSPeter Maydell     case A_CFGCTRL:
119dd73185bSPeter Maydell         r = s->cfgctrl;
120dd73185bSPeter Maydell         break;
121dd73185bSPeter Maydell     case A_CFGSTAT:
122dd73185bSPeter Maydell         r = s->cfgstat;
123dd73185bSPeter Maydell         break;
124dd73185bSPeter Maydell     case A_DLL:
125dd73185bSPeter Maydell         r = s->dll;
126dd73185bSPeter Maydell         break;
127dd73185bSPeter Maydell     case A_AID:
128dd73185bSPeter Maydell         r = s->aid;
129dd73185bSPeter Maydell         break;
130dd73185bSPeter Maydell     case A_ID:
131dd73185bSPeter Maydell         r = s->id;
132dd73185bSPeter Maydell         break;
133dd73185bSPeter Maydell     default:
134dd73185bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
135dd73185bSPeter Maydell                       "MPS2 SCC read: bad offset %x\n", (int) offset);
136dd73185bSPeter Maydell         r = 0;
137dd73185bSPeter Maydell         break;
138dd73185bSPeter Maydell     }
139dd73185bSPeter Maydell 
140dd73185bSPeter Maydell     trace_mps2_scc_read(offset, r, size);
141dd73185bSPeter Maydell     return r;
142dd73185bSPeter Maydell }
143dd73185bSPeter Maydell 
144dd73185bSPeter Maydell static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
145dd73185bSPeter Maydell                            unsigned size)
146dd73185bSPeter Maydell {
147dd73185bSPeter Maydell     MPS2SCC *s = MPS2_SCC(opaque);
148dd73185bSPeter Maydell 
149dd73185bSPeter Maydell     trace_mps2_scc_write(offset, value, size);
150dd73185bSPeter Maydell 
151dd73185bSPeter Maydell     switch (offset) {
152dd73185bSPeter Maydell     case A_CFG0:
153dd73185bSPeter Maydell         /* TODO on some boards bit 0 controls RAM remapping */
154dd73185bSPeter Maydell         s->cfg0 = value;
155dd73185bSPeter Maydell         break;
156dd73185bSPeter Maydell     case A_CFG1:
157dd73185bSPeter Maydell         s->cfg1 = value;
158*435db7ebSPhilippe Mathieu-Daudé         for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) {
159*435db7ebSPhilippe Mathieu-Daudé             led_set_state(s->led[i], extract32(value, i, 1));
160*435db7ebSPhilippe Mathieu-Daudé         }
161dd73185bSPeter Maydell         break;
162dd73185bSPeter Maydell     case A_CFGDATA_OUT:
163dd73185bSPeter Maydell         s->cfgdata_out = value;
164dd73185bSPeter Maydell         break;
165dd73185bSPeter Maydell     case A_CFGCTRL:
166dd73185bSPeter Maydell         /* Writing to CFGCTRL clears SYS_CFGSTAT */
167dd73185bSPeter Maydell         s->cfgstat = 0;
168dd73185bSPeter Maydell         s->cfgctrl = value & ~(R_CFGCTRL_RES1_MASK |
169dd73185bSPeter Maydell                                R_CFGCTRL_RES2_MASK |
170dd73185bSPeter Maydell                                R_CFGCTRL_START_MASK);
171dd73185bSPeter Maydell 
172dd73185bSPeter Maydell         if (value & R_CFGCTRL_START_MASK) {
173dd73185bSPeter Maydell             /* Start bit set -- do a read or write (instantaneously) */
174dd73185bSPeter Maydell             int device = extract32(s->cfgctrl, R_CFGCTRL_DEVICE_SHIFT,
175dd73185bSPeter Maydell                                    R_CFGCTRL_DEVICE_LENGTH);
176dd73185bSPeter Maydell             int function = extract32(s->cfgctrl, R_CFGCTRL_FUNCTION_SHIFT,
177dd73185bSPeter Maydell                                      R_CFGCTRL_FUNCTION_LENGTH);
178dd73185bSPeter Maydell 
179dd73185bSPeter Maydell             s->cfgstat = R_CFGSTAT_DONE_MASK;
180dd73185bSPeter Maydell             if (s->cfgctrl & R_CFGCTRL_WRITE_MASK) {
181dd73185bSPeter Maydell                 if (!scc_cfg_write(s, function, device, s->cfgdata_out)) {
182dd73185bSPeter Maydell                     s->cfgstat |= R_CFGSTAT_ERROR_MASK;
183dd73185bSPeter Maydell                 }
184dd73185bSPeter Maydell             } else {
185dd73185bSPeter Maydell                 uint32_t result;
186dd73185bSPeter Maydell                 if (!scc_cfg_read(s, function, device, &result)) {
187dd73185bSPeter Maydell                     s->cfgstat |= R_CFGSTAT_ERROR_MASK;
188dd73185bSPeter Maydell                 } else {
189dd73185bSPeter Maydell                     s->cfgdata_rtn = result;
190dd73185bSPeter Maydell                 }
191dd73185bSPeter Maydell             }
192dd73185bSPeter Maydell         }
193dd73185bSPeter Maydell         break;
194dd73185bSPeter Maydell     case A_DLL:
195dd73185bSPeter Maydell         /* DLL stands for Digital Locked Loop.
196dd73185bSPeter Maydell          * Bits [31:24] (DLL_LOCK_MASK) are writable, and indicate a
197dd73185bSPeter Maydell          * mask of which of the DLL_LOCKED bits [16:23] should be ORed
198dd73185bSPeter Maydell          * together to determine the ALL_UNMASKED_DLLS_LOCKED bit [0].
199dd73185bSPeter Maydell          * For QEMU, our DLLs are always locked, so we can leave bit 0
200dd73185bSPeter Maydell          * as 1 always and don't need to recalculate it.
201dd73185bSPeter Maydell          */
202dd73185bSPeter Maydell         s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8));
203dd73185bSPeter Maydell         break;
204dd73185bSPeter Maydell     default:
205dd73185bSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
206dd73185bSPeter Maydell                       "MPS2 SCC write: bad offset 0x%x\n", (int) offset);
207dd73185bSPeter Maydell         break;
208dd73185bSPeter Maydell     }
209dd73185bSPeter Maydell }
210dd73185bSPeter Maydell 
211dd73185bSPeter Maydell static const MemoryRegionOps mps2_scc_ops = {
212dd73185bSPeter Maydell     .read = mps2_scc_read,
213dd73185bSPeter Maydell     .write = mps2_scc_write,
214dd73185bSPeter Maydell     .endianness = DEVICE_LITTLE_ENDIAN,
215dd73185bSPeter Maydell };
216dd73185bSPeter Maydell 
217dd73185bSPeter Maydell static void mps2_scc_reset(DeviceState *dev)
218dd73185bSPeter Maydell {
219dd73185bSPeter Maydell     MPS2SCC *s = MPS2_SCC(dev);
220dd73185bSPeter Maydell     int i;
221dd73185bSPeter Maydell 
222dd73185bSPeter Maydell     trace_mps2_scc_reset();
223dd73185bSPeter Maydell     s->cfg0 = 0;
224dd73185bSPeter Maydell     s->cfg1 = 0;
225dd73185bSPeter Maydell     s->cfgdata_rtn = 0;
226dd73185bSPeter Maydell     s->cfgdata_out = 0;
227dd73185bSPeter Maydell     s->cfgctrl = 0x100000;
228dd73185bSPeter Maydell     s->cfgstat = 0;
229dd73185bSPeter Maydell     s->dll = 0xffff0001;
230dd73185bSPeter Maydell     for (i = 0; i < NUM_OSCCLK; i++) {
231dd73185bSPeter Maydell         s->oscclk[i] = s->oscclk_reset[i];
232dd73185bSPeter Maydell     }
233*435db7ebSPhilippe Mathieu-Daudé     for (i = 0; i < ARRAY_SIZE(s->led); i++) {
234*435db7ebSPhilippe Mathieu-Daudé         device_cold_reset(DEVICE(s->led[i]));
235*435db7ebSPhilippe Mathieu-Daudé     }
236dd73185bSPeter Maydell }
237dd73185bSPeter Maydell 
238dd73185bSPeter Maydell static void mps2_scc_init(Object *obj)
239dd73185bSPeter Maydell {
240dd73185bSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
241dd73185bSPeter Maydell     MPS2SCC *s = MPS2_SCC(obj);
242dd73185bSPeter Maydell 
243dd73185bSPeter Maydell     memory_region_init_io(&s->iomem, obj, &mps2_scc_ops, s, "mps2-scc", 0x1000);
244dd73185bSPeter Maydell     sysbus_init_mmio(sbd, &s->iomem);
245dd73185bSPeter Maydell }
246dd73185bSPeter Maydell 
247dd73185bSPeter Maydell static void mps2_scc_realize(DeviceState *dev, Error **errp)
248dd73185bSPeter Maydell {
249*435db7ebSPhilippe Mathieu-Daudé     MPS2SCC *s = MPS2_SCC(dev);
250*435db7ebSPhilippe Mathieu-Daudé 
251*435db7ebSPhilippe Mathieu-Daudé     for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) {
252*435db7ebSPhilippe Mathieu-Daudé         char *name = g_strdup_printf("SCC LED%zu", i);
253*435db7ebSPhilippe Mathieu-Daudé         s->led[i] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH,
254*435db7ebSPhilippe Mathieu-Daudé                                       LED_COLOR_GREEN, name);
255*435db7ebSPhilippe Mathieu-Daudé         g_free(name);
256*435db7ebSPhilippe Mathieu-Daudé     }
257dd73185bSPeter Maydell }
258dd73185bSPeter Maydell 
259dd73185bSPeter Maydell static const VMStateDescription mps2_scc_vmstate = {
260dd73185bSPeter Maydell     .name = "mps2-scc",
261dd73185bSPeter Maydell     .version_id = 1,
262dd73185bSPeter Maydell     .minimum_version_id = 1,
263dd73185bSPeter Maydell     .fields = (VMStateField[]) {
264dd73185bSPeter Maydell         VMSTATE_UINT32(cfg0, MPS2SCC),
265dd73185bSPeter Maydell         VMSTATE_UINT32(cfg1, MPS2SCC),
266dd73185bSPeter Maydell         VMSTATE_UINT32(cfgdata_rtn, MPS2SCC),
267dd73185bSPeter Maydell         VMSTATE_UINT32(cfgdata_out, MPS2SCC),
268dd73185bSPeter Maydell         VMSTATE_UINT32(cfgctrl, MPS2SCC),
269dd73185bSPeter Maydell         VMSTATE_UINT32(cfgstat, MPS2SCC),
270dd73185bSPeter Maydell         VMSTATE_UINT32(dll, MPS2SCC),
271dd73185bSPeter Maydell         VMSTATE_UINT32_ARRAY(oscclk, MPS2SCC, NUM_OSCCLK),
272dd73185bSPeter Maydell         VMSTATE_END_OF_LIST()
273dd73185bSPeter Maydell     }
274dd73185bSPeter Maydell };
275dd73185bSPeter Maydell 
276dd73185bSPeter Maydell static Property mps2_scc_properties[] = {
277dd73185bSPeter Maydell     /* Values for various read-only ID registers (which are specific
278dd73185bSPeter Maydell      * to the board model or FPGA image)
279dd73185bSPeter Maydell      */
28089cbc377SPhilippe Mathieu-Daudé     DEFINE_PROP_UINT32("scc-cfg4", MPS2SCC, cfg4, 0),
281dd73185bSPeter Maydell     DEFINE_PROP_UINT32("scc-aid", MPS2SCC, aid, 0),
28289cbc377SPhilippe Mathieu-Daudé     DEFINE_PROP_UINT32("scc-id", MPS2SCC, id, 0),
283dd73185bSPeter Maydell     /* These are the initial settings for the source clocks on the board.
284dd73185bSPeter Maydell      * In hardware they can be configured via a config file read by the
285dd73185bSPeter Maydell      * motherboard configuration controller to suit the FPGA image.
286dd73185bSPeter Maydell      * These default values are used by most of the standard FPGA images.
287dd73185bSPeter Maydell      */
288dd73185bSPeter Maydell     DEFINE_PROP_UINT32("oscclk0", MPS2SCC, oscclk_reset[0], 50000000),
289dd73185bSPeter Maydell     DEFINE_PROP_UINT32("oscclk1", MPS2SCC, oscclk_reset[1], 24576000),
290dd73185bSPeter Maydell     DEFINE_PROP_UINT32("oscclk2", MPS2SCC, oscclk_reset[2], 25000000),
291dd73185bSPeter Maydell     DEFINE_PROP_END_OF_LIST(),
292dd73185bSPeter Maydell };
293dd73185bSPeter Maydell 
294dd73185bSPeter Maydell static void mps2_scc_class_init(ObjectClass *klass, void *data)
295dd73185bSPeter Maydell {
296dd73185bSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
297dd73185bSPeter Maydell 
298dd73185bSPeter Maydell     dc->realize = mps2_scc_realize;
299dd73185bSPeter Maydell     dc->vmsd = &mps2_scc_vmstate;
300dd73185bSPeter Maydell     dc->reset = mps2_scc_reset;
3014f67d30bSMarc-André Lureau     device_class_set_props(dc, mps2_scc_properties);
302dd73185bSPeter Maydell }
303dd73185bSPeter Maydell 
304dd73185bSPeter Maydell static const TypeInfo mps2_scc_info = {
305dd73185bSPeter Maydell     .name = TYPE_MPS2_SCC,
306dd73185bSPeter Maydell     .parent = TYPE_SYS_BUS_DEVICE,
307dd73185bSPeter Maydell     .instance_size = sizeof(MPS2SCC),
308dd73185bSPeter Maydell     .instance_init = mps2_scc_init,
309dd73185bSPeter Maydell     .class_init = mps2_scc_class_init,
310dd73185bSPeter Maydell };
311dd73185bSPeter Maydell 
312dd73185bSPeter Maydell static void mps2_scc_register_types(void)
313dd73185bSPeter Maydell {
314dd73185bSPeter Maydell     type_register_static(&mps2_scc_info);
315dd73185bSPeter Maydell }
316dd73185bSPeter Maydell 
317dd73185bSPeter Maydell type_init(mps2_scc_register_types);
318