Statistics
| Branch: | Revision:

root / hw / ssi / pl022.c @ 3d29bcee

History | View | Annotate | Download (8.6 kB)

1 9ee6e8bb pbrook
/*
2 9ee6e8bb pbrook
 * Arm PrimeCell PL022 Synchronous Serial Port
3 9ee6e8bb pbrook
 *
4 9ee6e8bb pbrook
 * Copyright (c) 2007 CodeSourcery.
5 9ee6e8bb pbrook
 * Written by Paul Brook
6 9ee6e8bb pbrook
 *
7 8e31bf38 Matthew Fernandez
 * This code is licensed under the GPL.
8 9ee6e8bb pbrook
 */
9 9ee6e8bb pbrook
10 83c9f4ca Paolo Bonzini
#include "hw/sysbus.h"
11 83c9f4ca Paolo Bonzini
#include "hw/ssi.h"
12 9ee6e8bb pbrook
13 9ee6e8bb pbrook
//#define DEBUG_PL022 1
14 9ee6e8bb pbrook
15 9ee6e8bb pbrook
#ifdef DEBUG_PL022
16 001faf32 Blue Swirl
#define DPRINTF(fmt, ...) \
17 001faf32 Blue Swirl
do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
18 001faf32 Blue Swirl
#define BADF(fmt, ...) \
19 001faf32 Blue Swirl
do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
20 9ee6e8bb pbrook
#else
21 001faf32 Blue Swirl
#define DPRINTF(fmt, ...) do {} while(0)
22 001faf32 Blue Swirl
#define BADF(fmt, ...) \
23 001faf32 Blue Swirl
do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
24 9ee6e8bb pbrook
#endif
25 9ee6e8bb pbrook
26 9ee6e8bb pbrook
#define PL022_CR1_LBM 0x01
27 9ee6e8bb pbrook
#define PL022_CR1_SSE 0x02
28 9ee6e8bb pbrook
#define PL022_CR1_MS  0x04
29 9ee6e8bb pbrook
#define PL022_CR1_SDO 0x08
30 9ee6e8bb pbrook
31 9ee6e8bb pbrook
#define PL022_SR_TFE  0x01
32 9ee6e8bb pbrook
#define PL022_SR_TNF  0x02
33 9ee6e8bb pbrook
#define PL022_SR_RNE  0x04
34 9ee6e8bb pbrook
#define PL022_SR_RFF  0x08
35 9ee6e8bb pbrook
#define PL022_SR_BSY  0x10
36 9ee6e8bb pbrook
37 9ee6e8bb pbrook
#define PL022_INT_ROR 0x01
38 9ee6e8bb pbrook
#define PL022_INT_RT  0x04
39 9ee6e8bb pbrook
#define PL022_INT_RX  0x04
40 9ee6e8bb pbrook
#define PL022_INT_TX  0x08
41 9ee6e8bb pbrook
42 3d29bcee Andreas Färber
#define TYPE_PL022 "pl022"
43 3d29bcee Andreas Färber
#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022)
44 3d29bcee Andreas Färber
45 ce556e0b Andreas Färber
typedef struct PL022State {
46 3d29bcee Andreas Färber
    SysBusDevice parent_obj;
47 3d29bcee Andreas Färber
48 02a59c37 Avi Kivity
    MemoryRegion iomem;
49 9ee6e8bb pbrook
    uint32_t cr0;
50 9ee6e8bb pbrook
    uint32_t cr1;
51 9ee6e8bb pbrook
    uint32_t bitmask;
52 9ee6e8bb pbrook
    uint32_t sr;
53 9ee6e8bb pbrook
    uint32_t cpsr;
54 9ee6e8bb pbrook
    uint32_t is;
55 9ee6e8bb pbrook
    uint32_t im;
56 9ee6e8bb pbrook
    /* The FIFO head points to the next empty entry.  */
57 9ee6e8bb pbrook
    int tx_fifo_head;
58 9ee6e8bb pbrook
    int rx_fifo_head;
59 9ee6e8bb pbrook
    int tx_fifo_len;
60 9ee6e8bb pbrook
    int rx_fifo_len;
61 9ee6e8bb pbrook
    uint16_t tx_fifo[8];
62 9ee6e8bb pbrook
    uint16_t rx_fifo[8];
63 9ee6e8bb pbrook
    qemu_irq irq;
64 5493e33f Paul Brook
    SSIBus *ssi;
65 ce556e0b Andreas Färber
} PL022State;
66 9ee6e8bb pbrook
67 9ee6e8bb pbrook
static const unsigned char pl022_id[8] =
68 9ee6e8bb pbrook
  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
69 9ee6e8bb pbrook
70 ce556e0b Andreas Färber
static void pl022_update(PL022State *s)
71 9ee6e8bb pbrook
{
72 9ee6e8bb pbrook
    s->sr = 0;
73 9ee6e8bb pbrook
    if (s->tx_fifo_len == 0)
74 9ee6e8bb pbrook
        s->sr |= PL022_SR_TFE;
75 9ee6e8bb pbrook
    if (s->tx_fifo_len != 8)
76 9ee6e8bb pbrook
        s->sr |= PL022_SR_TNF;
77 9ee6e8bb pbrook
    if (s->rx_fifo_len != 0)
78 9ee6e8bb pbrook
        s->sr |= PL022_SR_RNE;
79 9ee6e8bb pbrook
    if (s->rx_fifo_len == 8)
80 9ee6e8bb pbrook
        s->sr |= PL022_SR_RFF;
81 9ee6e8bb pbrook
    if (s->tx_fifo_len)
82 9ee6e8bb pbrook
        s->sr |= PL022_SR_BSY;
83 9ee6e8bb pbrook
    s->is = 0;
84 9ee6e8bb pbrook
    if (s->rx_fifo_len >= 4)
85 9ee6e8bb pbrook
        s->is |= PL022_INT_RX;
86 9ee6e8bb pbrook
    if (s->tx_fifo_len <= 4)
87 9ee6e8bb pbrook
        s->is |= PL022_INT_TX;
88 9ee6e8bb pbrook
89 9ee6e8bb pbrook
    qemu_set_irq(s->irq, (s->is & s->im) != 0);
90 9ee6e8bb pbrook
}
91 9ee6e8bb pbrook
92 ce556e0b Andreas Färber
static void pl022_xfer(PL022State *s)
93 9ee6e8bb pbrook
{
94 9ee6e8bb pbrook
    int i;
95 9ee6e8bb pbrook
    int o;
96 9ee6e8bb pbrook
    int val;
97 9ee6e8bb pbrook
98 9ee6e8bb pbrook
    if ((s->cr1 & PL022_CR1_SSE) == 0) {
99 9ee6e8bb pbrook
        pl022_update(s);
100 9ee6e8bb pbrook
        DPRINTF("Disabled\n");
101 9ee6e8bb pbrook
        return;
102 9ee6e8bb pbrook
    }
103 9ee6e8bb pbrook
104 9ee6e8bb pbrook
    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
105 9ee6e8bb pbrook
    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
106 9ee6e8bb pbrook
    o = s->rx_fifo_head;
107 9ee6e8bb pbrook
    /* ??? We do not emulate the line speed.
108 9ee6e8bb pbrook
       This may break some applications.  The are two problematic cases:
109 9ee6e8bb pbrook
        (a) A driver feeds data into the TX FIFO until it is full,
110 9ee6e8bb pbrook
         and only then drains the RX FIFO.  On real hardware the CPU can
111 9ee6e8bb pbrook
         feed data fast enough that the RX fifo never gets chance to overflow.
112 9ee6e8bb pbrook
        (b) A driver transmits data, deliberately allowing the RX FIFO to
113 9ee6e8bb pbrook
         overflow because it ignores the RX data anyway.
114 9ee6e8bb pbrook

115 9ee6e8bb pbrook
       We choose to support (a) by stalling the transmit engine if it would
116 9ee6e8bb pbrook
       cause the RX FIFO to overflow.  In practice much transmit-only code
117 9ee6e8bb pbrook
       falls into (a) because it flushes the RX FIFO to determine when
118 9ee6e8bb pbrook
       the transfer has completed.  */
119 9ee6e8bb pbrook
    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
120 9ee6e8bb pbrook
        DPRINTF("xfer\n");
121 9ee6e8bb pbrook
        val = s->tx_fifo[i];
122 9ee6e8bb pbrook
        if (s->cr1 & PL022_CR1_LBM) {
123 9ee6e8bb pbrook
            /* Loopback mode.  */
124 9ee6e8bb pbrook
        } else {
125 5493e33f Paul Brook
            val = ssi_transfer(s->ssi, val);
126 9ee6e8bb pbrook
        }
127 9ee6e8bb pbrook
        s->rx_fifo[o] = val & s->bitmask;
128 9ee6e8bb pbrook
        i = (i + 1) & 7;
129 9ee6e8bb pbrook
        o = (o + 1) & 7;
130 9ee6e8bb pbrook
        s->tx_fifo_len--;
131 9ee6e8bb pbrook
        s->rx_fifo_len++;
132 9ee6e8bb pbrook
    }
133 9ee6e8bb pbrook
    s->rx_fifo_head = o;
134 9ee6e8bb pbrook
    pl022_update(s);
135 9ee6e8bb pbrook
}
136 9ee6e8bb pbrook
137 a8170e5e Avi Kivity
static uint64_t pl022_read(void *opaque, hwaddr offset,
138 02a59c37 Avi Kivity
                           unsigned size)
139 9ee6e8bb pbrook
{
140 ce556e0b Andreas Färber
    PL022State *s = (PL022State *)opaque;
141 9ee6e8bb pbrook
    int val;
142 9ee6e8bb pbrook
143 9ee6e8bb pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
144 9ee6e8bb pbrook
        return pl022_id[(offset - 0xfe0) >> 2];
145 9ee6e8bb pbrook
    }
146 9ee6e8bb pbrook
    switch (offset) {
147 9ee6e8bb pbrook
    case 0x00: /* CR0 */
148 9ee6e8bb pbrook
      return s->cr0;
149 9ee6e8bb pbrook
    case 0x04: /* CR1 */
150 9ee6e8bb pbrook
      return s->cr1;
151 9ee6e8bb pbrook
    case 0x08: /* DR */
152 9ee6e8bb pbrook
        if (s->rx_fifo_len) {
153 9ee6e8bb pbrook
            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
154 9ee6e8bb pbrook
            DPRINTF("RX %02x\n", val);
155 9ee6e8bb pbrook
            s->rx_fifo_len--;
156 9ee6e8bb pbrook
            pl022_xfer(s);
157 9ee6e8bb pbrook
        } else {
158 9ee6e8bb pbrook
            val = 0;
159 9ee6e8bb pbrook
        }
160 9ee6e8bb pbrook
        return val;
161 9ee6e8bb pbrook
    case 0x0c: /* SR */
162 9ee6e8bb pbrook
        return s->sr;
163 9ee6e8bb pbrook
    case 0x10: /* CPSR */
164 9ee6e8bb pbrook
        return s->cpsr;
165 9ee6e8bb pbrook
    case 0x14: /* IMSC */
166 9ee6e8bb pbrook
        return s->im;
167 9ee6e8bb pbrook
    case 0x18: /* RIS */
168 9ee6e8bb pbrook
        return s->is;
169 9ee6e8bb pbrook
    case 0x1c: /* MIS */
170 9ee6e8bb pbrook
        return s->im & s->is;
171 9ee6e8bb pbrook
    case 0x20: /* DMACR */
172 9ee6e8bb pbrook
        /* Not implemented.  */
173 9ee6e8bb pbrook
        return 0;
174 9ee6e8bb pbrook
    default:
175 af83c32b Peter Maydell
        qemu_log_mask(LOG_GUEST_ERROR,
176 af83c32b Peter Maydell
                      "pl022_read: Bad offset %x\n", (int)offset);
177 9ee6e8bb pbrook
        return 0;
178 9ee6e8bb pbrook
    }
179 9ee6e8bb pbrook
}
180 9ee6e8bb pbrook
181 a8170e5e Avi Kivity
static void pl022_write(void *opaque, hwaddr offset,
182 02a59c37 Avi Kivity
                        uint64_t value, unsigned size)
183 9ee6e8bb pbrook
{
184 ce556e0b Andreas Färber
    PL022State *s = (PL022State *)opaque;
185 9ee6e8bb pbrook
186 9ee6e8bb pbrook
    switch (offset) {
187 9ee6e8bb pbrook
    case 0x00: /* CR0 */
188 9ee6e8bb pbrook
        s->cr0 = value;
189 9ee6e8bb pbrook
        /* Clock rate and format are ignored.  */
190 9ee6e8bb pbrook
        s->bitmask = (1 << ((value & 15) + 1)) - 1;
191 9ee6e8bb pbrook
        break;
192 9ee6e8bb pbrook
    case 0x04: /* CR1 */
193 9ee6e8bb pbrook
        s->cr1 = value;
194 9ee6e8bb pbrook
        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
195 9ee6e8bb pbrook
                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
196 9ee6e8bb pbrook
            BADF("SPI slave mode not implemented\n");
197 9ee6e8bb pbrook
        }
198 9ee6e8bb pbrook
        pl022_xfer(s);
199 9ee6e8bb pbrook
        break;
200 9ee6e8bb pbrook
    case 0x08: /* DR */
201 9ee6e8bb pbrook
        if (s->tx_fifo_len < 8) {
202 02a59c37 Avi Kivity
            DPRINTF("TX %02x\n", (unsigned)value);
203 9ee6e8bb pbrook
            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
204 9ee6e8bb pbrook
            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
205 9ee6e8bb pbrook
            s->tx_fifo_len++;
206 9ee6e8bb pbrook
            pl022_xfer(s);
207 9ee6e8bb pbrook
        }
208 9ee6e8bb pbrook
        break;
209 9ee6e8bb pbrook
    case 0x10: /* CPSR */
210 9ee6e8bb pbrook
        /* Prescaler.  Ignored.  */
211 9ee6e8bb pbrook
        s->cpsr = value & 0xff;
212 9ee6e8bb pbrook
        break;
213 9ee6e8bb pbrook
    case 0x14: /* IMSC */
214 9ee6e8bb pbrook
        s->im = value;
215 9ee6e8bb pbrook
        pl022_update(s);
216 9ee6e8bb pbrook
        break;
217 9ee6e8bb pbrook
    case 0x20: /* DMACR */
218 2ac71179 Paul Brook
        if (value) {
219 af83c32b Peter Maydell
            qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
220 2ac71179 Paul Brook
        }
221 9ee6e8bb pbrook
        break;
222 9ee6e8bb pbrook
    default:
223 af83c32b Peter Maydell
        qemu_log_mask(LOG_GUEST_ERROR,
224 af83c32b Peter Maydell
                      "pl022_write: Bad offset %x\n", (int)offset);
225 9ee6e8bb pbrook
    }
226 9ee6e8bb pbrook
}
227 9ee6e8bb pbrook
228 ce556e0b Andreas Färber
static void pl022_reset(PL022State *s)
229 9ee6e8bb pbrook
{
230 9ee6e8bb pbrook
    s->rx_fifo_len = 0;
231 9ee6e8bb pbrook
    s->tx_fifo_len = 0;
232 9ee6e8bb pbrook
    s->im = 0;
233 9ee6e8bb pbrook
    s->is = PL022_INT_TX;
234 9ee6e8bb pbrook
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
235 9ee6e8bb pbrook
}
236 9ee6e8bb pbrook
237 02a59c37 Avi Kivity
static const MemoryRegionOps pl022_ops = {
238 02a59c37 Avi Kivity
    .read = pl022_read,
239 02a59c37 Avi Kivity
    .write = pl022_write,
240 02a59c37 Avi Kivity
    .endianness = DEVICE_NATIVE_ENDIAN,
241 9ee6e8bb pbrook
};
242 9ee6e8bb pbrook
243 075790c2 Juan Quintela
static const VMStateDescription vmstate_pl022 = {
244 075790c2 Juan Quintela
    .name = "pl022_ssp",
245 075790c2 Juan Quintela
    .version_id = 1,
246 075790c2 Juan Quintela
    .minimum_version_id = 1,
247 075790c2 Juan Quintela
    .minimum_version_id_old = 1,
248 075790c2 Juan Quintela
    .fields      = (VMStateField[]) {
249 ce556e0b Andreas Färber
        VMSTATE_UINT32(cr0, PL022State),
250 ce556e0b Andreas Färber
        VMSTATE_UINT32(cr1, PL022State),
251 ce556e0b Andreas Färber
        VMSTATE_UINT32(bitmask, PL022State),
252 ce556e0b Andreas Färber
        VMSTATE_UINT32(sr, PL022State),
253 ce556e0b Andreas Färber
        VMSTATE_UINT32(cpsr, PL022State),
254 ce556e0b Andreas Färber
        VMSTATE_UINT32(is, PL022State),
255 ce556e0b Andreas Färber
        VMSTATE_UINT32(im, PL022State),
256 ce556e0b Andreas Färber
        VMSTATE_INT32(tx_fifo_head, PL022State),
257 ce556e0b Andreas Färber
        VMSTATE_INT32(rx_fifo_head, PL022State),
258 ce556e0b Andreas Färber
        VMSTATE_INT32(tx_fifo_len, PL022State),
259 ce556e0b Andreas Färber
        VMSTATE_INT32(rx_fifo_len, PL022State),
260 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[0], PL022State),
261 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[0], PL022State),
262 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[1], PL022State),
263 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[1], PL022State),
264 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[2], PL022State),
265 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[2], PL022State),
266 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[3], PL022State),
267 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[3], PL022State),
268 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[4], PL022State),
269 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[4], PL022State),
270 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[5], PL022State),
271 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[5], PL022State),
272 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[6], PL022State),
273 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[6], PL022State),
274 ce556e0b Andreas Färber
        VMSTATE_UINT16(tx_fifo[7], PL022State),
275 ce556e0b Andreas Färber
        VMSTATE_UINT16(rx_fifo[7], PL022State),
276 075790c2 Juan Quintela
        VMSTATE_END_OF_LIST()
277 23e39294 pbrook
    }
278 075790c2 Juan Quintela
};
279 23e39294 pbrook
280 3d29bcee Andreas Färber
static int pl022_init(SysBusDevice *sbd)
281 9ee6e8bb pbrook
{
282 3d29bcee Andreas Färber
    DeviceState *dev = DEVICE(sbd);
283 3d29bcee Andreas Färber
    PL022State *s = PL022(dev);
284 9ee6e8bb pbrook
285 29776739 Paolo Bonzini
    memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
286 3d29bcee Andreas Färber
    sysbus_init_mmio(sbd, &s->iomem);
287 3d29bcee Andreas Färber
    sysbus_init_irq(sbd, &s->irq);
288 3d29bcee Andreas Färber
    s->ssi = ssi_create_bus(dev, "ssi");
289 9ee6e8bb pbrook
    pl022_reset(s);
290 3d29bcee Andreas Färber
    vmstate_register(dev, -1, &vmstate_pl022, s);
291 81a322d4 Gerd Hoffmann
    return 0;
292 9ee6e8bb pbrook
}
293 5493e33f Paul Brook
294 999e12bb Anthony Liguori
static void pl022_class_init(ObjectClass *klass, void *data)
295 999e12bb Anthony Liguori
{
296 999e12bb Anthony Liguori
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
297 999e12bb Anthony Liguori
298 999e12bb Anthony Liguori
    sdc->init = pl022_init;
299 999e12bb Anthony Liguori
}
300 999e12bb Anthony Liguori
301 8c43a6f0 Andreas Färber
static const TypeInfo pl022_info = {
302 3d29bcee Andreas Färber
    .name          = TYPE_PL022,
303 39bffca2 Anthony Liguori
    .parent        = TYPE_SYS_BUS_DEVICE,
304 ce556e0b Andreas Färber
    .instance_size = sizeof(PL022State),
305 39bffca2 Anthony Liguori
    .class_init    = pl022_class_init,
306 999e12bb Anthony Liguori
};
307 999e12bb Anthony Liguori
308 83f7d43a Andreas Färber
static void pl022_register_types(void)
309 5493e33f Paul Brook
{
310 39bffca2 Anthony Liguori
    type_register_static(&pl022_info);
311 5493e33f Paul Brook
}
312 5493e33f Paul Brook
313 83f7d43a Andreas Färber
type_init(pl022_register_types)