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