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