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