1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "qemu/osdep.h"
25#include "hw/sysbus.h"
26#include "qemu/log.h"
27
28#include "qemu/bitops.h"
29#include "qapi/qmp/qerror.h"
30#include "migration/vmstate.h"
31#include "hw/qdev-properties.h"
32#include "hw/register.h"
33#include "hw/stream.h"
34#include "qemu/fifo.h"
35
36#ifndef STREAM_FIFO_ERR_DEBUG
37#define STREAM_FIFO_ERR_DEBUG 0
38#endif
39
40#define TYPE_STREAM_FIFO "stream-fifo"
41
42#define STREAM_FIFO(obj) \
43 OBJECT_CHECK(StreamFifo, (obj), TYPE_STREAM_FIFO)
44
45REG32(DP, 0x00)
46REG32(CTL, 0x04)
47 #define R_CTL_CORK (1 << 0)
48 #define R_CTL_RSVD ~1ull
49
50#define R_MAX ((R_CTL) + 1)
51
52typedef struct StreamFifo StreamFifo;
53
54struct StreamFifo {
55 SysBusDevice busdev;
56 MemoryRegion iomem;
57
58 Fifo fifo;
59
60 uint32_t regs[R_MAX];
61 RegisterInfo regs_info[R_MAX];
62
63 StreamSlave *tx_dev;
64
65 StreamCanPushNotifyFn notify;
66 void *notify_opaque;
67};
68
69static void stream_fifo_notify(void *opaque)
70{
71 StreamFifo *s = STREAM_FIFO(opaque);
72
73 while (!fifo_is_empty(&s->fifo) && !(s->regs[R_CTL] & R_CTL_CORK) &&
74 stream_can_push(s->tx_dev, stream_fifo_notify, s)) {
75 size_t ret;
76 uint8_t buf[4];
77
78 *((uint32_t *)buf) = cpu_to_le32(fifo_pop32(&s->fifo));
79 ret = stream_push(s->tx_dev, buf, 4, false);
80 assert(ret == 4);
81 }
82
83 if (s->notify) {
84 StreamCanPushNotifyFn notify = s->notify;
85 s->notify = NULL;
86 notify(s->notify_opaque);
87 }
88}
89
90static bool stream_fifo_stream_can_push(StreamSlave *obj,
91 StreamCanPushNotifyFn notify,
92 void *notify_opaque)
93{
94 StreamFifo *s = STREAM_FIFO(obj);
95 bool ret = !(s->regs[R_CTL] & R_CTL_CORK) && !fifo_is_full(&s->fifo);
96
97 if (!ret) {
98 s->notify = notify;
99 s->notify_opaque = notify_opaque;
100 }
101 return ret;
102}
103
104static size_t stream_fifo_stream_push(StreamSlave *obj, uint8_t *buf,
105 size_t len, bool eop)
106{
107 StreamFifo *s = STREAM_FIFO(obj);
108 size_t ret = 0;
109
110 assert(!(len % 4));
111 while (len && !(s->regs[R_CTL] & R_CTL_CORK) && !fifo_is_full(&s->fifo)) {
112 fifo_push32(&s->fifo, le32_to_cpu(*(uint32_t *)buf));
113 buf += (sizeof(uint32_t));
114 len -= 4;
115 ret += 4;
116 }
117 return ret;
118}
119
120
121static void stream_fifo_update(RegisterInfo *reg, uint64_t val)
122{
123 StreamFifo *s = STREAM_FIFO(reg->opaque);
124
125 stream_fifo_notify(s);
126}
127
128static void stream_fifo_dp_post_write(RegisterInfo *reg, uint64_t val)
129{
130 StreamFifo *s = STREAM_FIFO(reg->opaque);
131
132 if (fifo_is_full(&s->fifo)) {
133 qemu_log_mask(LOG_GUEST_ERROR, "Write to full fifo\n");
134 } else {
135 fifo_push32(&s->fifo, val);
136 }
137 stream_fifo_update(reg, val);
138}
139
140static uint64_t stream_fifo_dp_post_read(RegisterInfo *reg, uint64_t val)
141{
142 StreamFifo *s = STREAM_FIFO(reg->opaque);
143
144 if (fifo_is_empty(&s->fifo)) {
145 qemu_log_mask(LOG_GUEST_ERROR, "Write to full fifo\n");
146 } else {
147 return fifo_pop32(&s->fifo);
148 }
149 return 0;
150}
151
152
153
154static const RegisterAccessInfo stream_fifo_regs_info[] = {
155 { .name = "data port", .addr = A_DP,
156 .post_write = stream_fifo_dp_post_write,
157 .post_read = stream_fifo_dp_post_read,
158 },{ .name = "control", .addr = A_CTL,
159 .rsvd = R_CTL_RSVD,
160 .reset = R_CTL_CORK,
161 }
162};
163
164static void stream_fifo_reset(DeviceState *dev)
165{
166 StreamFifo *s = STREAM_FIFO(dev);
167 int i;
168
169 for (i = 0; i < R_MAX; ++i) {
170 register_reset(&s->regs_info[i]);
171 }
172
173 fifo_reset(&s->fifo);
174}
175
176static const MemoryRegionOps stream_fifo_ops = {
177 .read = register_read_memory,
178 .write = register_write_memory,
179 .endianness = DEVICE_LITTLE_ENDIAN,
180 .valid = {
181 .min_access_size = 4,
182 .max_access_size = 4,
183 }
184};
185
186static void stream_fifo_realize(DeviceState *dev, Error **errp)
187{
188 StreamFifo *s = STREAM_FIFO(dev);
189
190#define STREAM_FIFO_DEPTH 64
191 fifo_create32(&s->fifo, STREAM_FIFO_DEPTH);
192}
193
194static void stream_fifo_init(Object *obj)
195{
196 StreamFifo *s = STREAM_FIFO(obj);
197 RegisterInfoArray *reg_array;
198
199 memory_region_init(&s->iomem, obj, "MMIO", R_MAX * 4);
200 reg_array =
201 register_init_block32(DEVICE(obj), stream_fifo_regs_info,
202 ARRAY_SIZE(stream_fifo_regs_info),
203 s->regs_info, s->regs,
204 &stream_fifo_ops,
205 STREAM_FIFO_ERR_DEBUG,
206 R_MAX * 4);
207 memory_region_add_subregion(&s->iomem,
208 0x0,
209 ®_array->mem);
210 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
211
212 object_property_add_link(obj, "stream-connected", TYPE_STREAM_SLAVE,
213 (Object **)&s->tx_dev,
214 qdev_prop_allow_set_link_before_realize,
215 OBJ_PROP_LINK_STRONG);
216
217}
218
219static const VMStateDescription vmstate_stream_fifo = {
220 .name = "stream_fifo",
221 .version_id = 1,
222 .minimum_version_id = 1,
223 .minimum_version_id_old = 1,
224 .fields = (VMStateField[]) {
225 VMSTATE_UINT32_ARRAY(regs, StreamFifo, R_MAX),
226 VMSTATE_END_OF_LIST(),
227 }
228};
229
230static void stream_fifo_class_init(ObjectClass *klass, void *data)
231{
232 DeviceClass *dc = DEVICE_CLASS(klass);
233 StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
234
235 dc->reset = stream_fifo_reset;
236 dc->realize = stream_fifo_realize;
237 dc->vmsd = &vmstate_stream_fifo;
238
239 ssc->push = stream_fifo_stream_push;
240 ssc->can_push = stream_fifo_stream_can_push;
241}
242
243static const TypeInfo stream_fifo_info = {
244 .name = TYPE_STREAM_FIFO,
245 .parent = TYPE_SYS_BUS_DEVICE,
246 .instance_size = sizeof(StreamFifo),
247 .class_init = stream_fifo_class_init,
248 .instance_init = stream_fifo_init,
249 .interfaces = (InterfaceInfo[]) {
250 { TYPE_STREAM_SLAVE },
251 { },
252 }
253};
254
255static void stream_fifo_register_types(void)
256{
257 type_register_static(&stream_fifo_info);
258}
259
260type_init(stream_fifo_register_types)
261