xref: /openbmc/qemu/hw/misc/mips_itu.c (revision 3ae8a100)
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 "qemu/log.h"
22 #include "qapi/error.h"
23 #include "cpu.h"
24 #include "exec/exec-all.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_PV_MAX_VAL 0xFFFF
37 
38 #define ITC_CELL_TAG_FIFO_DEPTH 28
39 #define ITC_CELL_TAG_FIFO_PTR 18
40 #define ITC_CELL_TAG_FIFO 17
41 #define ITC_CELL_TAG_T 16
42 #define ITC_CELL_TAG_F 1
43 #define ITC_CELL_TAG_E 0
44 
45 #define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
46 #define ITC_AM0_EN_MASK 0x1
47 
48 #define ITC_AM1_ADDR_MASK_MASK 0x1FC00
49 #define ITC_AM1_ENTRY_GRAIN_MASK 0x7
50 
51 typedef enum ITCView {
52     ITCVIEW_BYPASS  = 0,
53     ITCVIEW_CONTROL = 1,
54     ITCVIEW_EF_SYNC = 2,
55     ITCVIEW_EF_TRY  = 3,
56     ITCVIEW_PV_SYNC = 4,
57     ITCVIEW_PV_TRY  = 5
58 } ITCView;
59 
60 MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
61 {
62     return &itu->tag_io;
63 }
64 
65 static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
66 {
67     MIPSITUState *tag = (MIPSITUState *)opaque;
68     uint64_t index = addr >> 3;
69 
70     if (index >= ITC_ADDRESSMAP_NUM) {
71         qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
72         return 0;
73     }
74 
75     return tag->ITCAddressMap[index];
76 }
77 
78 static void itc_reconfigure(MIPSITUState *tag)
79 {
80     uint64_t *am = &tag->ITCAddressMap[0];
81     MemoryRegion *mr = &tag->storage_io;
82     hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
83     uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
84     bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
85 
86     memory_region_transaction_begin();
87     if (!(size & (size - 1))) {
88         memory_region_set_size(mr, size);
89     }
90     memory_region_set_address(mr, address);
91     memory_region_set_enabled(mr, is_enabled);
92     memory_region_transaction_commit();
93 }
94 
95 static void itc_tag_write(void *opaque, hwaddr addr,
96                           uint64_t data, unsigned size)
97 {
98     MIPSITUState *tag = (MIPSITUState *)opaque;
99     uint64_t *am = &tag->ITCAddressMap[0];
100     uint64_t am_old, mask;
101     uint64_t index = addr >> 3;
102 
103     switch (index) {
104     case 0:
105         mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
106         break;
107     case 1:
108         mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
109         break;
110     default:
111         qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
112         return;
113     }
114 
115     am_old = am[index];
116     am[index] = (data & mask) | (am_old & ~mask);
117     if (am_old != am[index]) {
118         itc_reconfigure(tag);
119     }
120 }
121 
122 static const MemoryRegionOps itc_tag_ops = {
123     .read = itc_tag_read,
124     .write = itc_tag_write,
125     .impl = {
126         .max_access_size = 8,
127     },
128     .endianness = DEVICE_NATIVE_ENDIAN,
129 };
130 
131 static inline uint32_t get_num_cells(MIPSITUState *s)
132 {
133     return s->num_fifo + s->num_semaphores;
134 }
135 
136 static inline ITCView get_itc_view(hwaddr addr)
137 {
138     return (addr >> 3) & 0xf;
139 }
140 
141 static inline int get_cell_stride_shift(const MIPSITUState *s)
142 {
143     /* Minimum interval (for EntryGain = 0) is 128 B */
144     return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
145 }
146 
147 static inline ITCStorageCell *get_cell(MIPSITUState *s,
148                                        hwaddr addr)
149 {
150     uint32_t cell_idx = addr >> get_cell_stride_shift(s);
151     uint32_t num_cells = get_num_cells(s);
152 
153     if (cell_idx >= num_cells) {
154         cell_idx = num_cells - 1;
155     }
156 
157     return &s->cell[cell_idx];
158 }
159 
160 static void wake_blocked_threads(ITCStorageCell *c)
161 {
162     CPUState *cs;
163     CPU_FOREACH(cs) {
164         if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
165             cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
166         }
167     }
168     c->blocked_threads = 0;
169 }
170 
171 static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c)
172 {
173     c->blocked_threads |= 1ULL << current_cpu->cpu_index;
174     current_cpu->halted = 1;
175     current_cpu->exception_index = EXCP_HLT;
176     cpu_loop_exit_restore(current_cpu, current_cpu->mem_io_pc);
177 }
178 
179 /* ITC Bypass View */
180 
181 static inline uint64_t view_bypass_read(ITCStorageCell *c)
182 {
183     if (c->tag.FIFO) {
184         return c->data[c->fifo_out];
185     } else {
186         return c->data[0];
187     }
188 }
189 
190 static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
191 {
192     if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
193         int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
194         c->data[idx] = val;
195     }
196 
197     /* ignore a write to the semaphore cell */
198 }
199 
200 /* ITC Control View */
201 
202 static inline uint64_t view_control_read(ITCStorageCell *c)
203 {
204     return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
205            (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
206            (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
207            (c->tag.T << ITC_CELL_TAG_T) |
208            (c->tag.E << ITC_CELL_TAG_E) |
209            (c->tag.F << ITC_CELL_TAG_F);
210 }
211 
212 static inline void view_control_write(ITCStorageCell *c, uint64_t val)
213 {
214     c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
215     c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
216     c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
217 
218     if (c->tag.E) {
219         c->tag.FIFOPtr = 0;
220     }
221 }
222 
223 /* ITC Empty/Full View */
224 
225 static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
226 {
227     uint64_t ret = 0;
228 
229     if (!c->tag.FIFO) {
230         return 0;
231     }
232 
233     c->tag.F = 0;
234 
235     if (blocking && c->tag.E) {
236         block_thread_and_exit(c);
237     }
238 
239     if (c->blocked_threads) {
240         wake_blocked_threads(c);
241     }
242 
243     if (c->tag.FIFOPtr > 0) {
244         ret = c->data[c->fifo_out];
245         c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
246         c->tag.FIFOPtr--;
247     }
248 
249     if (c->tag.FIFOPtr == 0) {
250         c->tag.E = 1;
251     }
252 
253     return ret;
254 }
255 
256 static uint64_t view_ef_sync_read(ITCStorageCell *c)
257 {
258     return view_ef_common_read(c, true);
259 }
260 
261 static uint64_t view_ef_try_read(ITCStorageCell *c)
262 {
263     return view_ef_common_read(c, false);
264 }
265 
266 static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
267                                         bool blocking)
268 {
269     if (!c->tag.FIFO) {
270         return;
271     }
272 
273     c->tag.E = 0;
274 
275     if (blocking && c->tag.F) {
276         block_thread_and_exit(c);
277     }
278 
279     if (c->blocked_threads) {
280         wake_blocked_threads(c);
281     }
282 
283     if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
284         int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
285         c->data[idx] = val;
286         c->tag.FIFOPtr++;
287     }
288 
289     if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
290         c->tag.F = 1;
291     }
292 }
293 
294 static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
295 {
296     view_ef_common_write(c, val, true);
297 }
298 
299 static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
300 {
301     view_ef_common_write(c, val, false);
302 }
303 
304 /* ITC P/V View */
305 
306 static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
307 {
308     uint64_t ret = c->data[0];
309 
310     if (c->tag.FIFO) {
311         return 0;
312     }
313 
314     if (c->data[0] > 0) {
315         c->data[0]--;
316     } else if (blocking) {
317         block_thread_and_exit(c);
318     }
319 
320     return ret;
321 }
322 
323 static uint64_t view_pv_sync_read(ITCStorageCell *c)
324 {
325     return view_pv_common_read(c, true);
326 }
327 
328 static uint64_t view_pv_try_read(ITCStorageCell *c)
329 {
330     return view_pv_common_read(c, false);
331 }
332 
333 static inline void view_pv_common_write(ITCStorageCell *c)
334 {
335     if (c->tag.FIFO) {
336         return;
337     }
338 
339     if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
340         c->data[0]++;
341     }
342 
343     if (c->blocked_threads) {
344         wake_blocked_threads(c);
345     }
346 }
347 
348 static void view_pv_sync_write(ITCStorageCell *c)
349 {
350     view_pv_common_write(c);
351 }
352 
353 static void view_pv_try_write(ITCStorageCell *c)
354 {
355     view_pv_common_write(c);
356 }
357 
358 static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
359 {
360     MIPSITUState *s = (MIPSITUState *)opaque;
361     ITCStorageCell *cell = get_cell(s, addr);
362     ITCView view = get_itc_view(addr);
363     uint64_t ret = -1;
364 
365     switch (view) {
366     case ITCVIEW_BYPASS:
367         ret = view_bypass_read(cell);
368         break;
369     case ITCVIEW_CONTROL:
370         ret = view_control_read(cell);
371         break;
372     case ITCVIEW_EF_SYNC:
373         ret = view_ef_sync_read(cell);
374         break;
375     case ITCVIEW_EF_TRY:
376         ret = view_ef_try_read(cell);
377         break;
378     case ITCVIEW_PV_SYNC:
379         ret = view_pv_sync_read(cell);
380         break;
381     case ITCVIEW_PV_TRY:
382         ret = view_pv_try_read(cell);
383         break;
384     default:
385         qemu_log_mask(LOG_GUEST_ERROR,
386                       "itc_storage_read: Bad ITC View %d\n", (int)view);
387         break;
388     }
389 
390     return ret;
391 }
392 
393 static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
394                               unsigned size)
395 {
396     MIPSITUState *s = (MIPSITUState *)opaque;
397     ITCStorageCell *cell = get_cell(s, addr);
398     ITCView view = get_itc_view(addr);
399 
400     switch (view) {
401     case ITCVIEW_BYPASS:
402         view_bypass_write(cell, data);
403         break;
404     case ITCVIEW_CONTROL:
405         view_control_write(cell, data);
406         break;
407     case ITCVIEW_EF_SYNC:
408         view_ef_sync_write(cell, data);
409         break;
410     case ITCVIEW_EF_TRY:
411         view_ef_try_write(cell, data);
412         break;
413     case ITCVIEW_PV_SYNC:
414         view_pv_sync_write(cell);
415         break;
416     case ITCVIEW_PV_TRY:
417         view_pv_try_write(cell);
418         break;
419     default:
420         qemu_log_mask(LOG_GUEST_ERROR,
421                       "itc_storage_write: Bad ITC View %d\n", (int)view);
422         break;
423     }
424 
425 }
426 
427 static const MemoryRegionOps itc_storage_ops = {
428     .read = itc_storage_read,
429     .write = itc_storage_write,
430     .endianness = DEVICE_NATIVE_ENDIAN,
431 };
432 
433 static void itc_reset_cells(MIPSITUState *s)
434 {
435     int i;
436 
437     memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
438 
439     for (i = 0; i < s->num_fifo; i++) {
440         s->cell[i].tag.E = 1;
441         s->cell[i].tag.FIFO = 1;
442         s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
443     }
444 }
445 
446 static void mips_itu_init(Object *obj)
447 {
448     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
449     MIPSITUState *s = MIPS_ITU(obj);
450 
451     memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
452                           "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
453     sysbus_init_mmio(sbd, &s->storage_io);
454 
455     memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
456                           "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
457 }
458 
459 static void mips_itu_realize(DeviceState *dev, Error **errp)
460 {
461     MIPSITUState *s = MIPS_ITU(dev);
462 
463     if (s->num_fifo > ITC_FIFO_NUM_MAX) {
464         error_setg(errp, "Exceed maximum number of FIFO cells: %d",
465                    s->num_fifo);
466         return;
467     }
468     if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
469         error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
470                    s->num_semaphores);
471         return;
472     }
473 
474     s->cell = g_new(ITCStorageCell, get_num_cells(s));
475 }
476 
477 static void mips_itu_reset(DeviceState *dev)
478 {
479     MIPSITUState *s = MIPS_ITU(dev);
480 
481     s->ITCAddressMap[0] = 0;
482     s->ITCAddressMap[1] =
483         ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
484         (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
485     itc_reconfigure(s);
486 
487     itc_reset_cells(s);
488 }
489 
490 static Property mips_itu_properties[] = {
491     DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
492                       ITC_FIFO_NUM_MAX),
493     DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
494                       ITC_SEMAPH_NUM_MAX),
495     DEFINE_PROP_END_OF_LIST(),
496 };
497 
498 static void mips_itu_class_init(ObjectClass *klass, void *data)
499 {
500     DeviceClass *dc = DEVICE_CLASS(klass);
501 
502     dc->props = mips_itu_properties;
503     dc->realize = mips_itu_realize;
504     dc->reset = mips_itu_reset;
505 }
506 
507 static const TypeInfo mips_itu_info = {
508     .name          = TYPE_MIPS_ITU,
509     .parent        = TYPE_SYS_BUS_DEVICE,
510     .instance_size = sizeof(MIPSITUState),
511     .instance_init = mips_itu_init,
512     .class_init    = mips_itu_class_init,
513 };
514 
515 static void mips_itu_register_types(void)
516 {
517     type_register_static(&mips_itu_info);
518 }
519 
520 type_init(mips_itu_register_types)
521