1
2
3
4
5
6
7
8
9
10#include "qemu/osdep.h"
11#include "hw/sysbus.h"
12#include "migration/vmstate.h"
13#include "hw/irq.h"
14#include "hw/ssi/pl022.h"
15#include "hw/ssi/ssi.h"
16#include "qemu/log.h"
17#include "qemu/module.h"
18
19
20
21#ifdef DEBUG_PL022
22#define DPRINTF(fmt, ...) \
23do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
24#define BADF(fmt, ...) \
25do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
26#else
27#define DPRINTF(fmt, ...) do {} while(0)
28#define BADF(fmt, ...) \
29do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
30#endif
31
32#define PL022_CR1_LBM 0x01
33#define PL022_CR1_SSE 0x02
34#define PL022_CR1_MS 0x04
35#define PL022_CR1_SDO 0x08
36
37#define PL022_SR_TFE 0x01
38#define PL022_SR_TNF 0x02
39#define PL022_SR_RNE 0x04
40#define PL022_SR_RFF 0x08
41#define PL022_SR_BSY 0x10
42
43#define PL022_INT_ROR 0x01
44#define PL022_INT_RT 0x02
45#define PL022_INT_RX 0x04
46#define PL022_INT_TX 0x08
47
48static const unsigned char pl022_id[8] =
49 { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
50
51static void pl022_update(PL022State *s)
52{
53 s->sr = 0;
54 if (s->tx_fifo_len == 0)
55 s->sr |= PL022_SR_TFE;
56 if (s->tx_fifo_len != 8)
57 s->sr |= PL022_SR_TNF;
58 if (s->rx_fifo_len != 0)
59 s->sr |= PL022_SR_RNE;
60 if (s->rx_fifo_len == 8)
61 s->sr |= PL022_SR_RFF;
62 if (s->tx_fifo_len)
63 s->sr |= PL022_SR_BSY;
64 s->is = 0;
65 if (s->rx_fifo_len >= 4)
66 s->is |= PL022_INT_RX;
67 if (s->tx_fifo_len <= 4)
68 s->is |= PL022_INT_TX;
69
70 qemu_set_irq(s->irq, (s->is & s->im) != 0);
71}
72
73static void pl022_xfer(PL022State *s)
74{
75 int i;
76 int o;
77 int val;
78
79 if ((s->cr1 & PL022_CR1_SSE) == 0) {
80 pl022_update(s);
81 DPRINTF("Disabled\n");
82 return;
83 }
84
85 DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
86 i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
87 o = s->rx_fifo_head;
88
89
90
91
92
93
94
95
96
97
98
99
100 while (s->tx_fifo_len && s->rx_fifo_len < 8) {
101 DPRINTF("xfer\n");
102 val = s->tx_fifo[i];
103 if (s->cr1 & PL022_CR1_LBM) {
104
105 } else {
106 val = ssi_transfer(s->ssi, val);
107 }
108 s->rx_fifo[o] = val & s->bitmask;
109 i = (i + 1) & 7;
110 o = (o + 1) & 7;
111 s->tx_fifo_len--;
112 s->rx_fifo_len++;
113 }
114 s->rx_fifo_head = o;
115 pl022_update(s);
116}
117
118static uint64_t pl022_read(void *opaque, hwaddr offset,
119 unsigned size)
120{
121 PL022State *s = (PL022State *)opaque;
122 int val;
123
124 if (offset >= 0xfe0 && offset < 0x1000) {
125 return pl022_id[(offset - 0xfe0) >> 2];
126 }
127 switch (offset) {
128 case 0x00:
129 return s->cr0;
130 case 0x04:
131 return s->cr1;
132 case 0x08:
133 if (s->rx_fifo_len) {
134 val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
135 DPRINTF("RX %02x\n", val);
136 s->rx_fifo_len--;
137 pl022_xfer(s);
138 } else {
139 val = 0;
140 }
141 return val;
142 case 0x0c:
143 return s->sr;
144 case 0x10:
145 return s->cpsr;
146 case 0x14:
147 return s->im;
148 case 0x18:
149 return s->is;
150 case 0x1c:
151 return s->im & s->is;
152 case 0x24:
153
154 return 0;
155 default:
156 qemu_log_mask(LOG_GUEST_ERROR,
157 "pl022_read: Bad offset %x\n", (int)offset);
158 return 0;
159 }
160}
161
162static void pl022_write(void *opaque, hwaddr offset,
163 uint64_t value, unsigned size)
164{
165 PL022State *s = (PL022State *)opaque;
166
167 switch (offset) {
168 case 0x00:
169 s->cr0 = value;
170
171 s->bitmask = (1 << ((value & 15) + 1)) - 1;
172 break;
173 case 0x04:
174 s->cr1 = value;
175 if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
176 == (PL022_CR1_MS | PL022_CR1_SSE)) {
177 BADF("SPI peripheral mode not implemented\n");
178 }
179 pl022_xfer(s);
180 break;
181 case 0x08:
182 if (s->tx_fifo_len < 8) {
183 DPRINTF("TX %02x\n", (unsigned)value);
184 s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
185 s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
186 s->tx_fifo_len++;
187 pl022_xfer(s);
188 }
189 break;
190 case 0x10:
191
192 s->cpsr = value & 0xff;
193 break;
194 case 0x14:
195 s->im = value;
196 pl022_update(s);
197 break;
198 case 0x20:
199
200
201
202
203 value &= PL022_INT_ROR | PL022_INT_RT;
204 s->is &= ~value;
205 break;
206 case 0x24:
207 if (value) {
208 qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
209 }
210 break;
211 default:
212 qemu_log_mask(LOG_GUEST_ERROR,
213 "pl022_write: Bad offset %x\n", (int)offset);
214 }
215}
216
217static void pl022_reset(DeviceState *dev)
218{
219 PL022State *s = PL022(dev);
220
221 s->rx_fifo_len = 0;
222 s->tx_fifo_len = 0;
223 s->im = 0;
224 s->is = PL022_INT_TX;
225 s->sr = PL022_SR_TFE | PL022_SR_TNF;
226}
227
228static const MemoryRegionOps pl022_ops = {
229 .read = pl022_read,
230 .write = pl022_write,
231 .endianness = DEVICE_NATIVE_ENDIAN,
232};
233
234static int pl022_post_load(void *opaque, int version_id)
235{
236 PL022State *s = opaque;
237
238 if (s->tx_fifo_head < 0 ||
239 s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) ||
240 s->rx_fifo_head < 0 ||
241 s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) {
242 return -1;
243 }
244 return 0;
245}
246
247static const VMStateDescription vmstate_pl022 = {
248 .name = "pl022_ssp",
249 .version_id = 1,
250 .minimum_version_id = 1,
251 .post_load = pl022_post_load,
252 .fields = (VMStateField[]) {
253 VMSTATE_UINT32(cr0, PL022State),
254 VMSTATE_UINT32(cr1, PL022State),
255 VMSTATE_UINT32(bitmask, PL022State),
256 VMSTATE_UINT32(sr, PL022State),
257 VMSTATE_UINT32(cpsr, PL022State),
258 VMSTATE_UINT32(is, PL022State),
259 VMSTATE_UINT32(im, PL022State),
260 VMSTATE_INT32(tx_fifo_head, PL022State),
261 VMSTATE_INT32(rx_fifo_head, PL022State),
262 VMSTATE_INT32(tx_fifo_len, PL022State),
263 VMSTATE_INT32(rx_fifo_len, PL022State),
264 VMSTATE_UINT16(tx_fifo[0], PL022State),
265 VMSTATE_UINT16(rx_fifo[0], PL022State),
266 VMSTATE_UINT16(tx_fifo[1], PL022State),
267 VMSTATE_UINT16(rx_fifo[1], PL022State),
268 VMSTATE_UINT16(tx_fifo[2], PL022State),
269 VMSTATE_UINT16(rx_fifo[2], PL022State),
270 VMSTATE_UINT16(tx_fifo[3], PL022State),
271 VMSTATE_UINT16(rx_fifo[3], PL022State),
272 VMSTATE_UINT16(tx_fifo[4], PL022State),
273 VMSTATE_UINT16(rx_fifo[4], PL022State),
274 VMSTATE_UINT16(tx_fifo[5], PL022State),
275 VMSTATE_UINT16(rx_fifo[5], PL022State),
276 VMSTATE_UINT16(tx_fifo[6], PL022State),
277 VMSTATE_UINT16(rx_fifo[6], PL022State),
278 VMSTATE_UINT16(tx_fifo[7], PL022State),
279 VMSTATE_UINT16(rx_fifo[7], PL022State),
280 VMSTATE_END_OF_LIST()
281 }
282};
283
284static void pl022_realize(DeviceState *dev, Error **errp)
285{
286 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
287 PL022State *s = PL022(dev);
288
289 memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
290 sysbus_init_mmio(sbd, &s->iomem);
291 sysbus_init_irq(sbd, &s->irq);
292 s->ssi = ssi_create_bus(dev, "ssi");
293}
294
295static void pl022_class_init(ObjectClass *klass, void *data)
296{
297 DeviceClass *dc = DEVICE_CLASS(klass);
298
299 dc->reset = pl022_reset;
300 dc->vmsd = &vmstate_pl022;
301 dc->realize = pl022_realize;
302}
303
304static const TypeInfo pl022_info = {
305 .name = TYPE_PL022,
306 .parent = TYPE_SYS_BUS_DEVICE,
307 .instance_size = sizeof(PL022State),
308 .class_init = pl022_class_init,
309};
310
311static void pl022_register_types(void)
312{
313 type_register_static(&pl022_info);
314}
315
316type_init(pl022_register_types)
317