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