]> git.proxmox.com Git - mirror_qemu.git/blame - hw/ssi/pl022.c
migration: Move the VMStateDescription typedef to typedefs.h
[mirror_qemu.git] / hw / ssi / pl022.c
CommitLineData
9ee6e8bb
PB
1/*
2 * Arm PrimeCell PL022 Synchronous Serial Port
3 *
4 * Copyright (c) 2007 CodeSourcery.
5 * Written by Paul Brook
6 *
8e31bf38 7 * This code is licensed under the GPL.
9ee6e8bb
PB
8 */
9
17b7f2db 10#include "qemu/osdep.h"
83c9f4ca 11#include "hw/sysbus.h"
64552b6b 12#include "hw/irq.h"
1d52866f 13#include "hw/ssi/pl022.h"
8fd06719 14#include "hw/ssi/ssi.h"
03dd024f 15#include "qemu/log.h"
0b8fa32f 16#include "qemu/module.h"
9ee6e8bb
PB
17
18//#define DEBUG_PL022 1
19
20#ifdef DEBUG_PL022
001faf32
BS
21#define DPRINTF(fmt, ...) \
22do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
23#define BADF(fmt, ...) \
24do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
9ee6e8bb 25#else
001faf32
BS
26#define DPRINTF(fmt, ...) do {} while(0)
27#define BADF(fmt, ...) \
28do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
9ee6e8bb
PB
29#endif
30
31#define PL022_CR1_LBM 0x01
32#define PL022_CR1_SSE 0x02
33#define PL022_CR1_MS 0x04
34#define PL022_CR1_SDO 0x08
35
36#define PL022_SR_TFE 0x01
37#define PL022_SR_TNF 0x02
38#define PL022_SR_RNE 0x04
39#define PL022_SR_RFF 0x08
40#define PL022_SR_BSY 0x10
41
42#define PL022_INT_ROR 0x01
139d941e 43#define PL022_INT_RT 0x02
9ee6e8bb
PB
44#define PL022_INT_RX 0x04
45#define PL022_INT_TX 0x08
46
9ee6e8bb
PB
47static const unsigned char pl022_id[8] =
48 { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
49
ce556e0b 50static void pl022_update(PL022State *s)
9ee6e8bb
PB
51{
52 s->sr = 0;
53 if (s->tx_fifo_len == 0)
54 s->sr |= PL022_SR_TFE;
55 if (s->tx_fifo_len != 8)
56 s->sr |= PL022_SR_TNF;
57 if (s->rx_fifo_len != 0)
58 s->sr |= PL022_SR_RNE;
59 if (s->rx_fifo_len == 8)
60 s->sr |= PL022_SR_RFF;
61 if (s->tx_fifo_len)
62 s->sr |= PL022_SR_BSY;
63 s->is = 0;
64 if (s->rx_fifo_len >= 4)
65 s->is |= PL022_INT_RX;
66 if (s->tx_fifo_len <= 4)
67 s->is |= PL022_INT_TX;
68
69 qemu_set_irq(s->irq, (s->is & s->im) != 0);
70}
71
ce556e0b 72static void pl022_xfer(PL022State *s)
9ee6e8bb
PB
73{
74 int i;
75 int o;
76 int val;
77
78 if ((s->cr1 & PL022_CR1_SSE) == 0) {
79 pl022_update(s);
80 DPRINTF("Disabled\n");
81 return;
82 }
83
84 DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
85 i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
86 o = s->rx_fifo_head;
87 /* ??? We do not emulate the line speed.
88 This may break some applications. The are two problematic cases:
89 (a) A driver feeds data into the TX FIFO until it is full,
90 and only then drains the RX FIFO. On real hardware the CPU can
91 feed data fast enough that the RX fifo never gets chance to overflow.
92 (b) A driver transmits data, deliberately allowing the RX FIFO to
93 overflow because it ignores the RX data anyway.
94
95 We choose to support (a) by stalling the transmit engine if it would
96 cause the RX FIFO to overflow. In practice much transmit-only code
97 falls into (a) because it flushes the RX FIFO to determine when
98 the transfer has completed. */
99 while (s->tx_fifo_len && s->rx_fifo_len < 8) {
100 DPRINTF("xfer\n");
101 val = s->tx_fifo[i];
102 if (s->cr1 & PL022_CR1_LBM) {
103 /* Loopback mode. */
9ee6e8bb 104 } else {
5493e33f 105 val = ssi_transfer(s->ssi, val);
9ee6e8bb
PB
106 }
107 s->rx_fifo[o] = val & s->bitmask;
108 i = (i + 1) & 7;
109 o = (o + 1) & 7;
110 s->tx_fifo_len--;
111 s->rx_fifo_len++;
112 }
113 s->rx_fifo_head = o;
114 pl022_update(s);
115}
116
a8170e5e 117static uint64_t pl022_read(void *opaque, hwaddr offset,
02a59c37 118 unsigned size)
9ee6e8bb 119{
ce556e0b 120 PL022State *s = (PL022State *)opaque;
9ee6e8bb
PB
121 int val;
122
9ee6e8bb
PB
123 if (offset >= 0xfe0 && offset < 0x1000) {
124 return pl022_id[(offset - 0xfe0) >> 2];
125 }
126 switch (offset) {
127 case 0x00: /* CR0 */
128 return s->cr0;
129 case 0x04: /* CR1 */
130 return s->cr1;
131 case 0x08: /* DR */
132 if (s->rx_fifo_len) {
133 val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
134 DPRINTF("RX %02x\n", val);
135 s->rx_fifo_len--;
136 pl022_xfer(s);
137 } else {
138 val = 0;
139 }
140 return val;
141 case 0x0c: /* SR */
142 return s->sr;
143 case 0x10: /* CPSR */
144 return s->cpsr;
145 case 0x14: /* IMSC */
146 return s->im;
147 case 0x18: /* RIS */
148 return s->is;
149 case 0x1c: /* MIS */
150 return s->im & s->is;
7d3912f5 151 case 0x24: /* DMACR */
9ee6e8bb
PB
152 /* Not implemented. */
153 return 0;
154 default:
af83c32b
PM
155 qemu_log_mask(LOG_GUEST_ERROR,
156 "pl022_read: Bad offset %x\n", (int)offset);
9ee6e8bb
PB
157 return 0;
158 }
159}
160
a8170e5e 161static void pl022_write(void *opaque, hwaddr offset,
02a59c37 162 uint64_t value, unsigned size)
9ee6e8bb 163{
ce556e0b 164 PL022State *s = (PL022State *)opaque;
9ee6e8bb 165
9ee6e8bb
PB
166 switch (offset) {
167 case 0x00: /* CR0 */
168 s->cr0 = value;
169 /* Clock rate and format are ignored. */
170 s->bitmask = (1 << ((value & 15) + 1)) - 1;
171 break;
172 case 0x04: /* CR1 */
173 s->cr1 = value;
174 if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
175 == (PL022_CR1_MS | PL022_CR1_SSE)) {
176 BADF("SPI slave mode not implemented\n");
177 }
178 pl022_xfer(s);
179 break;
180 case 0x08: /* DR */
181 if (s->tx_fifo_len < 8) {
02a59c37 182 DPRINTF("TX %02x\n", (unsigned)value);
9ee6e8bb
PB
183 s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
184 s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
185 s->tx_fifo_len++;
186 pl022_xfer(s);
187 }
188 break;
189 case 0x10: /* CPSR */
190 /* Prescaler. Ignored. */
191 s->cpsr = value & 0xff;
192 break;
193 case 0x14: /* IMSC */
194 s->im = value;
195 pl022_update(s);
196 break;
7d3912f5
PM
197 case 0x20: /* ICR */
198 /*
199 * write-1-to-clear: bit 0 clears ROR, bit 1 clears RT;
200 * RX and TX interrupts cannot be cleared this way.
201 */
202 value &= PL022_INT_ROR | PL022_INT_RT;
203 s->is &= ~value;
204 break;
205 case 0x24: /* DMACR */
2ac71179 206 if (value) {
af83c32b 207 qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
2ac71179 208 }
9ee6e8bb
PB
209 break;
210 default:
af83c32b
PM
211 qemu_log_mask(LOG_GUEST_ERROR,
212 "pl022_write: Bad offset %x\n", (int)offset);
9ee6e8bb
PB
213 }
214}
215
66d9aa79 216static void pl022_reset(DeviceState *dev)
9ee6e8bb 217{
66d9aa79
PM
218 PL022State *s = PL022(dev);
219
9ee6e8bb
PB
220 s->rx_fifo_len = 0;
221 s->tx_fifo_len = 0;
222 s->im = 0;
223 s->is = PL022_INT_TX;
224 s->sr = PL022_SR_TFE | PL022_SR_TNF;
225}
226
02a59c37
AK
227static const MemoryRegionOps pl022_ops = {
228 .read = pl022_read,
229 .write = pl022_write,
230 .endianness = DEVICE_NATIVE_ENDIAN,
9ee6e8bb
PB
231};
232
d8d0a0bc
MT
233static int pl022_post_load(void *opaque, int version_id)
234{
235 PL022State *s = opaque;
236
237 if (s->tx_fifo_head < 0 ||
238 s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) ||
239 s->rx_fifo_head < 0 ||
240 s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) {
241 return -1;
242 }
243 return 0;
244}
245
075790c2
JQ
246static const VMStateDescription vmstate_pl022 = {
247 .name = "pl022_ssp",
248 .version_id = 1,
249 .minimum_version_id = 1,
d8d0a0bc 250 .post_load = pl022_post_load,
8f1e884b 251 .fields = (VMStateField[]) {
ce556e0b
AF
252 VMSTATE_UINT32(cr0, PL022State),
253 VMSTATE_UINT32(cr1, PL022State),
254 VMSTATE_UINT32(bitmask, PL022State),
255 VMSTATE_UINT32(sr, PL022State),
256 VMSTATE_UINT32(cpsr, PL022State),
257 VMSTATE_UINT32(is, PL022State),
258 VMSTATE_UINT32(im, PL022State),
259 VMSTATE_INT32(tx_fifo_head, PL022State),
260 VMSTATE_INT32(rx_fifo_head, PL022State),
261 VMSTATE_INT32(tx_fifo_len, PL022State),
262 VMSTATE_INT32(rx_fifo_len, PL022State),
263 VMSTATE_UINT16(tx_fifo[0], PL022State),
264 VMSTATE_UINT16(rx_fifo[0], PL022State),
265 VMSTATE_UINT16(tx_fifo[1], PL022State),
266 VMSTATE_UINT16(rx_fifo[1], PL022State),
267 VMSTATE_UINT16(tx_fifo[2], PL022State),
268 VMSTATE_UINT16(rx_fifo[2], PL022State),
269 VMSTATE_UINT16(tx_fifo[3], PL022State),
270 VMSTATE_UINT16(rx_fifo[3], PL022State),
271 VMSTATE_UINT16(tx_fifo[4], PL022State),
272 VMSTATE_UINT16(rx_fifo[4], PL022State),
273 VMSTATE_UINT16(tx_fifo[5], PL022State),
274 VMSTATE_UINT16(rx_fifo[5], PL022State),
275 VMSTATE_UINT16(tx_fifo[6], PL022State),
276 VMSTATE_UINT16(rx_fifo[6], PL022State),
277 VMSTATE_UINT16(tx_fifo[7], PL022State),
278 VMSTATE_UINT16(rx_fifo[7], PL022State),
075790c2 279 VMSTATE_END_OF_LIST()
23e39294 280 }
075790c2 281};
23e39294 282
13391a56 283static void pl022_realize(DeviceState *dev, Error **errp)
9ee6e8bb 284{
13391a56 285 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
3d29bcee 286 PL022State *s = PL022(dev);
9ee6e8bb 287
29776739 288 memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
3d29bcee
AF
289 sysbus_init_mmio(sbd, &s->iomem);
290 sysbus_init_irq(sbd, &s->irq);
291 s->ssi = ssi_create_bus(dev, "ssi");
9ee6e8bb 292}
5493e33f 293
999e12bb
AL
294static void pl022_class_init(ObjectClass *klass, void *data)
295{
66d9aa79 296 DeviceClass *dc = DEVICE_CLASS(klass);
999e12bb 297
66d9aa79 298 dc->reset = pl022_reset;
275ff67f 299 dc->vmsd = &vmstate_pl022;
13391a56 300 dc->realize = pl022_realize;
999e12bb
AL
301}
302
8c43a6f0 303static const TypeInfo pl022_info = {
3d29bcee 304 .name = TYPE_PL022,
39bffca2 305 .parent = TYPE_SYS_BUS_DEVICE,
ce556e0b 306 .instance_size = sizeof(PL022State),
39bffca2 307 .class_init = pl022_class_init,
999e12bb
AL
308};
309
83f7d43a 310static void pl022_register_types(void)
5493e33f 311{
39bffca2 312 type_register_static(&pl022_info);
5493e33f
PB
313}
314
83f7d43a 315type_init(pl022_register_types)