]> Git Repo - qemu.git/blob - hw/misc/mips_itu.c
hw/mips: implement ITC Storage - Empty/Full Sync and Try Views
[qemu.git] / hw / misc / mips_itu.c
1 /*
2  * Inter-Thread Communication Unit emulation.
3  *
4  * Copyright (c) 2016 Imagination Technologies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "qemu/osdep.h"
21 #include "qapi/error.h"
22 #include "hw/hw.h"
23 #include "hw/sysbus.h"
24 #include "sysemu/sysemu.h"
25 #include "hw/misc/mips_itu.h"
26
27 #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
28 /* Initialize as 4kB area to fit all 32 cells with default 128B grain.
29    Storage may be resized by the software. */
30 #define ITC_STORAGE_ADDRSPACE_SZ 0x1000
31
32 #define ITC_FIFO_NUM_MAX 16
33 #define ITC_SEMAPH_NUM_MAX 16
34 #define ITC_AM1_NUMENTRIES_OFS 20
35
36 #define ITC_CELL_TAG_FIFO_DEPTH 28
37 #define ITC_CELL_TAG_FIFO_PTR 18
38 #define ITC_CELL_TAG_FIFO 17
39 #define ITC_CELL_TAG_T 16
40 #define ITC_CELL_TAG_F 1
41 #define ITC_CELL_TAG_E 0
42
43 #define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
44 #define ITC_AM0_EN_MASK 0x1
45
46 #define ITC_AM1_ADDR_MASK_MASK 0x1FC00
47 #define ITC_AM1_ENTRY_GRAIN_MASK 0x7
48
49 typedef enum ITCView {
50     ITCVIEW_BYPASS  = 0,
51     ITCVIEW_CONTROL = 1,
52     ITCVIEW_EF_SYNC = 2,
53     ITCVIEW_EF_TRY  = 3,
54     ITCVIEW_PV_SYNC = 4,
55     ITCVIEW_PV_TRY  = 5
56 } ITCView;
57
58 MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
59 {
60     return &itu->tag_io;
61 }
62
63 static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
64 {
65     MIPSITUState *tag = (MIPSITUState *)opaque;
66     uint64_t index = addr >> 3;
67     uint64_t ret = 0;
68
69     switch (index) {
70     case 0 ... ITC_ADDRESSMAP_NUM:
71         ret = tag->ITCAddressMap[index];
72         break;
73     default:
74         qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
75         break;
76     }
77
78     return ret;
79 }
80
81 static void itc_reconfigure(MIPSITUState *tag)
82 {
83     uint64_t *am = &tag->ITCAddressMap[0];
84     MemoryRegion *mr = &tag->storage_io;
85     hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
86     uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
87     bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
88
89     memory_region_transaction_begin();
90     if (!(size & (size - 1))) {
91         memory_region_set_size(mr, size);
92     }
93     memory_region_set_address(mr, address);
94     memory_region_set_enabled(mr, is_enabled);
95     memory_region_transaction_commit();
96 }
97
98 static void itc_tag_write(void *opaque, hwaddr addr,
99                           uint64_t data, unsigned size)
100 {
101     MIPSITUState *tag = (MIPSITUState *)opaque;
102     uint64_t *am = &tag->ITCAddressMap[0];
103     uint64_t am_old, mask;
104     uint64_t index = addr >> 3;
105
106     switch (index) {
107     case 0:
108         mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
109         break;
110     case 1:
111         mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
112         break;
113     default:
114         qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
115         return;
116     }
117
118     am_old = am[index];
119     am[index] = (data & mask) | (am_old & ~mask);
120     if (am_old != am[index]) {
121         itc_reconfigure(tag);
122     }
123 }
124
125 static const MemoryRegionOps itc_tag_ops = {
126     .read = itc_tag_read,
127     .write = itc_tag_write,
128     .impl = {
129         .max_access_size = 8,
130     },
131     .endianness = DEVICE_NATIVE_ENDIAN,
132 };
133
134 static inline uint32_t get_num_cells(MIPSITUState *s)
135 {
136     return s->num_fifo + s->num_semaphores;
137 }
138
139 static inline ITCView get_itc_view(hwaddr addr)
140 {
141     return (addr >> 3) & 0xf;
142 }
143
144 static inline int get_cell_stride_shift(const MIPSITUState *s)
145 {
146     /* Minimum interval (for EntryGain = 0) is 128 B */
147     return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
148 }
149
150 static inline ITCStorageCell *get_cell(MIPSITUState *s,
151                                        hwaddr addr)
152 {
153     uint32_t cell_idx = addr >> get_cell_stride_shift(s);
154     uint32_t num_cells = get_num_cells(s);
155
156     if (cell_idx >= num_cells) {
157         cell_idx = num_cells - 1;
158     }
159
160     return &s->cell[cell_idx];
161 }
162
163 static void wake_blocked_threads(ITCStorageCell *c)
164 {
165     CPUState *cs;
166     CPU_FOREACH(cs) {
167         if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
168             cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
169         }
170     }
171     c->blocked_threads = 0;
172 }
173
174 static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c)
175 {
176     c->blocked_threads |= 1ULL << current_cpu->cpu_index;
177     cpu_restore_state(current_cpu, current_cpu->mem_io_pc);
178     current_cpu->halted = 1;
179     current_cpu->exception_index = EXCP_HLT;
180     cpu_loop_exit(current_cpu);
181 }
182
183 /* ITC Control View */
184
185 static inline uint64_t view_control_read(ITCStorageCell *c)
186 {
187     return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
188            (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
189            (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
190            (c->tag.T << ITC_CELL_TAG_T) |
191            (c->tag.E << ITC_CELL_TAG_E) |
192            (c->tag.F << ITC_CELL_TAG_F);
193 }
194
195 static inline void view_control_write(ITCStorageCell *c, uint64_t val)
196 {
197     c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
198     c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
199     c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
200
201     if (c->tag.E) {
202         c->tag.FIFOPtr = 0;
203     }
204 }
205
206 /* ITC Empty/Full View */
207
208 static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
209 {
210     uint64_t ret = 0;
211
212     if (!c->tag.FIFO) {
213         return 0;
214     }
215
216     c->tag.F = 0;
217
218     if (blocking && c->tag.E) {
219         block_thread_and_exit(c);
220     }
221
222     if (c->blocked_threads) {
223         wake_blocked_threads(c);
224     }
225
226     if (c->tag.FIFOPtr > 0) {
227         ret = c->data[c->fifo_out];
228         c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
229         c->tag.FIFOPtr--;
230     }
231
232     if (c->tag.FIFOPtr == 0) {
233         c->tag.E = 1;
234     }
235
236     return ret;
237 }
238
239 static uint64_t view_ef_sync_read(ITCStorageCell *c)
240 {
241     return view_ef_common_read(c, true);
242 }
243
244 static uint64_t view_ef_try_read(ITCStorageCell *c)
245 {
246     return view_ef_common_read(c, false);
247 }
248
249 static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
250                                         bool blocking)
251 {
252     if (!c->tag.FIFO) {
253         return;
254     }
255
256     c->tag.E = 0;
257
258     if (blocking && c->tag.F) {
259         block_thread_and_exit(c);
260     }
261
262     if (c->blocked_threads) {
263         wake_blocked_threads(c);
264     }
265
266     if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
267         int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
268         c->data[idx] = val;
269         c->tag.FIFOPtr++;
270     }
271
272     if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
273         c->tag.F = 1;
274     }
275 }
276
277 static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
278 {
279     view_ef_common_write(c, val, true);
280 }
281
282 static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
283 {
284     view_ef_common_write(c, val, false);
285 }
286
287 static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
288 {
289     MIPSITUState *s = (MIPSITUState *)opaque;
290     ITCStorageCell *cell = get_cell(s, addr);
291     ITCView view = get_itc_view(addr);
292     uint64_t ret = -1;
293
294     switch (view) {
295     case ITCVIEW_CONTROL:
296         ret = view_control_read(cell);
297         break;
298     case ITCVIEW_EF_SYNC:
299         ret = view_ef_sync_read(cell);
300         break;
301     case ITCVIEW_EF_TRY:
302         ret = view_ef_try_read(cell);
303         break;
304     default:
305         qemu_log_mask(LOG_GUEST_ERROR,
306                       "itc_storage_read: Bad ITC View %d\n", (int)view);
307         break;
308     }
309
310     return ret;
311 }
312
313 static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
314                               unsigned size)
315 {
316     MIPSITUState *s = (MIPSITUState *)opaque;
317     ITCStorageCell *cell = get_cell(s, addr);
318     ITCView view = get_itc_view(addr);
319
320     switch (view) {
321     case ITCVIEW_CONTROL:
322         view_control_write(cell, data);
323         break;
324     case ITCVIEW_EF_SYNC:
325         view_ef_sync_write(cell, data);
326         break;
327     case ITCVIEW_EF_TRY:
328         view_ef_try_write(cell, data);
329         break;
330     default:
331         qemu_log_mask(LOG_GUEST_ERROR,
332                       "itc_storage_write: Bad ITC View %d\n", (int)view);
333         break;
334     }
335
336 }
337
338 static const MemoryRegionOps itc_storage_ops = {
339     .read = itc_storage_read,
340     .write = itc_storage_write,
341     .endianness = DEVICE_NATIVE_ENDIAN,
342 };
343
344 static void itc_reset_cells(MIPSITUState *s)
345 {
346     int i;
347
348     memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
349
350     for (i = 0; i < s->num_fifo; i++) {
351         s->cell[i].tag.E = 1;
352         s->cell[i].tag.FIFO = 1;
353         s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
354     }
355 }
356
357 static void mips_itu_init(Object *obj)
358 {
359     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
360     MIPSITUState *s = MIPS_ITU(obj);
361
362     memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
363                           "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
364     sysbus_init_mmio(sbd, &s->storage_io);
365
366     memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
367                           "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
368 }
369
370 static void mips_itu_realize(DeviceState *dev, Error **errp)
371 {
372     MIPSITUState *s = MIPS_ITU(dev);
373
374     if (s->num_fifo > ITC_FIFO_NUM_MAX) {
375         error_setg(errp, "Exceed maximum number of FIFO cells: %d",
376                    s->num_fifo);
377         return;
378     }
379     if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
380         error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
381                    s->num_semaphores);
382         return;
383     }
384
385     s->cell = g_new(ITCStorageCell, get_num_cells(s));
386 }
387
388 static void mips_itu_reset(DeviceState *dev)
389 {
390     MIPSITUState *s = MIPS_ITU(dev);
391
392     s->ITCAddressMap[0] = 0;
393     s->ITCAddressMap[1] =
394         ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
395         (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
396     itc_reconfigure(s);
397
398     itc_reset_cells(s);
399 }
400
401 static Property mips_itu_properties[] = {
402     DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
403                       ITC_FIFO_NUM_MAX),
404     DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
405                       ITC_SEMAPH_NUM_MAX),
406     DEFINE_PROP_END_OF_LIST(),
407 };
408
409 static void mips_itu_class_init(ObjectClass *klass, void *data)
410 {
411     DeviceClass *dc = DEVICE_CLASS(klass);
412
413     dc->props = mips_itu_properties;
414     dc->realize = mips_itu_realize;
415     dc->reset = mips_itu_reset;
416 }
417
418 static const TypeInfo mips_itu_info = {
419     .name          = TYPE_MIPS_ITU,
420     .parent        = TYPE_SYS_BUS_DEVICE,
421     .instance_size = sizeof(MIPSITUState),
422     .instance_init = mips_itu_init,
423     .class_init    = mips_itu_class_init,
424 };
425
426 static void mips_itu_register_types(void)
427 {
428     type_register_static(&mips_itu_info);
429 }
430
431 type_init(mips_itu_register_types)
This page took 0.046124 seconds and 4 git commands to generate.