1
2
3
4
5
6
7
8
9
10#include "sysbus.h"
11#include "ssi.h"
12#include "primecell.h"
13
14
15
16#ifdef DEBUG_PL022
17#define DPRINTF(fmt, ...) \
18do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
19#define BADF(fmt, ...) \
20do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
21#else
22#define DPRINTF(fmt, ...) do {} while(0)
23#define BADF(fmt, ...) \
24do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
25#endif
26
27#define PL022_CR1_LBM 0x01
28#define PL022_CR1_SSE 0x02
29#define PL022_CR1_MS 0x04
30#define PL022_CR1_SDO 0x08
31
32#define PL022_SR_TFE 0x01
33#define PL022_SR_TNF 0x02
34#define PL022_SR_RNE 0x04
35#define PL022_SR_RFF 0x08
36#define PL022_SR_BSY 0x10
37
38#define PL022_INT_ROR 0x01
39#define PL022_INT_RT 0x04
40#define PL022_INT_RX 0x04
41#define PL022_INT_TX 0x08
42
43typedef struct {
44 SysBusDevice busdev;
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 uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
134{
135 pl022_state *s = (pl022_state *)opaque;
136 int val;
137
138 if (offset >= 0xfe0 && offset < 0x1000) {
139 return pl022_id[(offset - 0xfe0) >> 2];
140 }
141 switch (offset) {
142 case 0x00:
143 return s->cr0;
144 case 0x04:
145 return s->cr1;
146 case 0x08:
147 if (s->rx_fifo_len) {
148 val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
149 DPRINTF("RX %02x\n", val);
150 s->rx_fifo_len--;
151 pl022_xfer(s);
152 } else {
153 val = 0;
154 }
155 return val;
156 case 0x0c:
157 return s->sr;
158 case 0x10:
159 return s->cpsr;
160 case 0x14:
161 return s->im;
162 case 0x18:
163 return s->is;
164 case 0x1c:
165 return s->im & s->is;
166 case 0x20:
167
168 return 0;
169 default:
170 hw_error("pl022_read: Bad offset %x\n", (int)offset);
171 return 0;
172 }
173}
174
175static void pl022_write(void *opaque, target_phys_addr_t offset,
176 uint32_t value)
177{
178 pl022_state *s = (pl022_state *)opaque;
179
180 switch (offset) {
181 case 0x00:
182 s->cr0 = value;
183
184 s->bitmask = (1 << ((value & 15) + 1)) - 1;
185 break;
186 case 0x04:
187 s->cr1 = value;
188 if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
189 == (PL022_CR1_MS | PL022_CR1_SSE)) {
190 BADF("SPI slave mode not implemented\n");
191 }
192 pl022_xfer(s);
193 break;
194 case 0x08:
195 if (s->tx_fifo_len < 8) {
196 DPRINTF("TX %02x\n", value);
197 s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
198 s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
199 s->tx_fifo_len++;
200 pl022_xfer(s);
201 }
202 break;
203 case 0x10:
204
205 s->cpsr = value & 0xff;
206 break;
207 case 0x14:
208 s->im = value;
209 pl022_update(s);
210 break;
211 case 0x20:
212 if (value) {
213 hw_error("pl022: DMA not implemented\n");
214 }
215 break;
216 default:
217 hw_error("pl022_write: Bad offset %x\n", (int)offset);
218 }
219}
220
221static void pl022_reset(pl022_state *s)
222{
223 s->rx_fifo_len = 0;
224 s->tx_fifo_len = 0;
225 s->im = 0;
226 s->is = PL022_INT_TX;
227 s->sr = PL022_SR_TFE | PL022_SR_TNF;
228}
229
230static CPUReadMemoryFunc * const pl022_readfn[] = {
231 pl022_read,
232 pl022_read,
233 pl022_read
234};
235
236static CPUWriteMemoryFunc * const pl022_writefn[] = {
237 pl022_write,
238 pl022_write,
239 pl022_write
240};
241
242static const VMStateDescription vmstate_pl022 = {
243 .name = "pl022_ssp",
244 .version_id = 1,
245 .minimum_version_id = 1,
246 .minimum_version_id_old = 1,
247 .fields = (VMStateField[]) {
248 VMSTATE_UINT32(cr0, pl022_state),
249 VMSTATE_UINT32(cr1, pl022_state),
250 VMSTATE_UINT32(bitmask, pl022_state),
251 VMSTATE_UINT32(sr, pl022_state),
252 VMSTATE_UINT32(cpsr, pl022_state),
253 VMSTATE_UINT32(is, pl022_state),
254 VMSTATE_UINT32(im, pl022_state),
255 VMSTATE_INT32(tx_fifo_head, pl022_state),
256 VMSTATE_INT32(rx_fifo_head, pl022_state),
257 VMSTATE_INT32(tx_fifo_len, pl022_state),
258 VMSTATE_INT32(rx_fifo_len, pl022_state),
259 VMSTATE_UINT16(tx_fifo[0], pl022_state),
260 VMSTATE_UINT16(rx_fifo[0], pl022_state),
261 VMSTATE_UINT16(tx_fifo[1], pl022_state),
262 VMSTATE_UINT16(rx_fifo[1], pl022_state),
263 VMSTATE_UINT16(tx_fifo[2], pl022_state),
264 VMSTATE_UINT16(rx_fifo[2], pl022_state),
265 VMSTATE_UINT16(tx_fifo[3], pl022_state),
266 VMSTATE_UINT16(rx_fifo[3], pl022_state),
267 VMSTATE_UINT16(tx_fifo[4], pl022_state),
268 VMSTATE_UINT16(rx_fifo[4], pl022_state),
269 VMSTATE_UINT16(tx_fifo[5], pl022_state),
270 VMSTATE_UINT16(rx_fifo[5], pl022_state),
271 VMSTATE_UINT16(tx_fifo[6], pl022_state),
272 VMSTATE_UINT16(rx_fifo[6], pl022_state),
273 VMSTATE_UINT16(tx_fifo[7], pl022_state),
274 VMSTATE_UINT16(rx_fifo[7], pl022_state),
275 VMSTATE_END_OF_LIST()
276 }
277};
278
279static int pl022_init(SysBusDevice *dev)
280{
281 pl022_state *s = FROM_SYSBUS(pl022_state, dev);
282 int iomemtype;
283
284 iomemtype = cpu_register_io_memory(pl022_readfn,
285 pl022_writefn, s,
286 DEVICE_NATIVE_ENDIAN);
287 sysbus_init_mmio(dev, 0x1000, iomemtype);
288 sysbus_init_irq(dev, &s->irq);
289 s->ssi = ssi_create_bus(&dev->qdev, "ssi");
290 pl022_reset(s);
291 vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
292 return 0;
293}
294
295static void pl022_register_devices(void)
296{
297 sysbus_register_dev("pl022", sizeof(pl022_state), pl022_init);
298}
299
300device_init(pl022_register_devices)
301