Statistics
| Branch: | Revision:

root / hw / pl022.c @ 5493e33f

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

111 9ee6e8bb pbrook
       We choose to support (a) by stalling the transmit engine if it would
112 9ee6e8bb pbrook
       cause the RX FIFO to overflow.  In practice much transmit-only code
113 9ee6e8bb pbrook
       falls into (a) because it flushes the RX FIFO to determine when
114 9ee6e8bb pbrook
       the transfer has completed.  */
115 9ee6e8bb pbrook
    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
116 9ee6e8bb pbrook
        DPRINTF("xfer\n");
117 9ee6e8bb pbrook
        val = s->tx_fifo[i];
118 9ee6e8bb pbrook
        if (s->cr1 & PL022_CR1_LBM) {
119 9ee6e8bb pbrook
            /* Loopback mode.  */
120 9ee6e8bb pbrook
        } else {
121 5493e33f Paul Brook
            val = ssi_transfer(s->ssi, val);
122 9ee6e8bb pbrook
        }
123 9ee6e8bb pbrook
        s->rx_fifo[o] = val & s->bitmask;
124 9ee6e8bb pbrook
        i = (i + 1) & 7;
125 9ee6e8bb pbrook
        o = (o + 1) & 7;
126 9ee6e8bb pbrook
        s->tx_fifo_len--;
127 9ee6e8bb pbrook
        s->rx_fifo_len++;
128 9ee6e8bb pbrook
    }
129 9ee6e8bb pbrook
    s->rx_fifo_head = o;
130 9ee6e8bb pbrook
    pl022_update(s);
131 9ee6e8bb pbrook
}
132 9ee6e8bb pbrook
133 9ee6e8bb pbrook
static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
134 9ee6e8bb pbrook
{
135 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
136 9ee6e8bb pbrook
    int val;
137 9ee6e8bb pbrook
138 9ee6e8bb pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
139 9ee6e8bb pbrook
        return pl022_id[(offset - 0xfe0) >> 2];
140 9ee6e8bb pbrook
    }
141 9ee6e8bb pbrook
    switch (offset) {
142 9ee6e8bb pbrook
    case 0x00: /* CR0 */
143 9ee6e8bb pbrook
      return s->cr0;
144 9ee6e8bb pbrook
    case 0x04: /* CR1 */
145 9ee6e8bb pbrook
      return s->cr1;
146 9ee6e8bb pbrook
    case 0x08: /* DR */
147 9ee6e8bb pbrook
        if (s->rx_fifo_len) {
148 9ee6e8bb pbrook
            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
149 9ee6e8bb pbrook
            DPRINTF("RX %02x\n", val);
150 9ee6e8bb pbrook
            s->rx_fifo_len--;
151 9ee6e8bb pbrook
            pl022_xfer(s);
152 9ee6e8bb pbrook
        } else {
153 9ee6e8bb pbrook
            val = 0;
154 9ee6e8bb pbrook
        }
155 9ee6e8bb pbrook
        return val;
156 9ee6e8bb pbrook
    case 0x0c: /* SR */
157 9ee6e8bb pbrook
        return s->sr;
158 9ee6e8bb pbrook
    case 0x10: /* CPSR */
159 9ee6e8bb pbrook
        return s->cpsr;
160 9ee6e8bb pbrook
    case 0x14: /* IMSC */
161 9ee6e8bb pbrook
        return s->im;
162 9ee6e8bb pbrook
    case 0x18: /* RIS */
163 9ee6e8bb pbrook
        return s->is;
164 9ee6e8bb pbrook
    case 0x1c: /* MIS */
165 9ee6e8bb pbrook
        return s->im & s->is;
166 9ee6e8bb pbrook
    case 0x20: /* DMACR */
167 9ee6e8bb pbrook
        /* Not implemented.  */
168 9ee6e8bb pbrook
        return 0;
169 9ee6e8bb pbrook
    default:
170 2ac71179 Paul Brook
        hw_error("pl022_read: Bad offset %x\n", (int)offset);
171 9ee6e8bb pbrook
        return 0;
172 9ee6e8bb pbrook
    }
173 9ee6e8bb pbrook
}
174 9ee6e8bb pbrook
175 9ee6e8bb pbrook
static void pl022_write(void *opaque, target_phys_addr_t offset,
176 9ee6e8bb pbrook
                        uint32_t value)
177 9ee6e8bb pbrook
{
178 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
179 9ee6e8bb pbrook
180 9ee6e8bb pbrook
    switch (offset) {
181 9ee6e8bb pbrook
    case 0x00: /* CR0 */
182 9ee6e8bb pbrook
        s->cr0 = value;
183 9ee6e8bb pbrook
        /* Clock rate and format are ignored.  */
184 9ee6e8bb pbrook
        s->bitmask = (1 << ((value & 15) + 1)) - 1;
185 9ee6e8bb pbrook
        break;
186 9ee6e8bb pbrook
    case 0x04: /* CR1 */
187 9ee6e8bb pbrook
        s->cr1 = value;
188 9ee6e8bb pbrook
        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
189 9ee6e8bb pbrook
                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
190 9ee6e8bb pbrook
            BADF("SPI slave mode not implemented\n");
191 9ee6e8bb pbrook
        }
192 9ee6e8bb pbrook
        pl022_xfer(s);
193 9ee6e8bb pbrook
        break;
194 9ee6e8bb pbrook
    case 0x08: /* DR */
195 9ee6e8bb pbrook
        if (s->tx_fifo_len < 8) {
196 9ee6e8bb pbrook
            DPRINTF("TX %02x\n", value);
197 9ee6e8bb pbrook
            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
198 9ee6e8bb pbrook
            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
199 9ee6e8bb pbrook
            s->tx_fifo_len++;
200 9ee6e8bb pbrook
            pl022_xfer(s);
201 9ee6e8bb pbrook
        }
202 9ee6e8bb pbrook
        break;
203 9ee6e8bb pbrook
    case 0x10: /* CPSR */
204 9ee6e8bb pbrook
        /* Prescaler.  Ignored.  */
205 9ee6e8bb pbrook
        s->cpsr = value & 0xff;
206 9ee6e8bb pbrook
        break;
207 9ee6e8bb pbrook
    case 0x14: /* IMSC */
208 9ee6e8bb pbrook
        s->im = value;
209 9ee6e8bb pbrook
        pl022_update(s);
210 9ee6e8bb pbrook
        break;
211 9ee6e8bb pbrook
    case 0x20: /* DMACR */
212 2ac71179 Paul Brook
        if (value) {
213 2ac71179 Paul Brook
            hw_error("pl022: DMA not implemented\n");
214 2ac71179 Paul Brook
        }
215 9ee6e8bb pbrook
        break;
216 9ee6e8bb pbrook
    default:
217 2ac71179 Paul Brook
        hw_error("pl022_write: Bad offset %x\n", (int)offset);
218 9ee6e8bb pbrook
    }
219 9ee6e8bb pbrook
}
220 9ee6e8bb pbrook
221 9ee6e8bb pbrook
static void pl022_reset(pl022_state *s)
222 9ee6e8bb pbrook
{
223 9ee6e8bb pbrook
    s->rx_fifo_len = 0;
224 9ee6e8bb pbrook
    s->tx_fifo_len = 0;
225 9ee6e8bb pbrook
    s->im = 0;
226 9ee6e8bb pbrook
    s->is = PL022_INT_TX;
227 9ee6e8bb pbrook
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
228 9ee6e8bb pbrook
}
229 9ee6e8bb pbrook
230 9ee6e8bb pbrook
static CPUReadMemoryFunc *pl022_readfn[] = {
231 9ee6e8bb pbrook
   pl022_read,
232 9ee6e8bb pbrook
   pl022_read,
233 9ee6e8bb pbrook
   pl022_read
234 9ee6e8bb pbrook
};
235 9ee6e8bb pbrook
236 9ee6e8bb pbrook
static CPUWriteMemoryFunc *pl022_writefn[] = {
237 9ee6e8bb pbrook
   pl022_write,
238 9ee6e8bb pbrook
   pl022_write,
239 9ee6e8bb pbrook
   pl022_write
240 9ee6e8bb pbrook
};
241 9ee6e8bb pbrook
242 23e39294 pbrook
static void pl022_save(QEMUFile *f, void *opaque)
243 23e39294 pbrook
{
244 23e39294 pbrook
    pl022_state *s = (pl022_state *)opaque;
245 23e39294 pbrook
    int i;
246 23e39294 pbrook
247 23e39294 pbrook
    qemu_put_be32(f, s->cr0);
248 23e39294 pbrook
    qemu_put_be32(f, s->cr1);
249 23e39294 pbrook
    qemu_put_be32(f, s->bitmask);
250 23e39294 pbrook
    qemu_put_be32(f, s->sr);
251 23e39294 pbrook
    qemu_put_be32(f, s->cpsr);
252 23e39294 pbrook
    qemu_put_be32(f, s->is);
253 23e39294 pbrook
    qemu_put_be32(f, s->im);
254 23e39294 pbrook
    qemu_put_be32(f, s->tx_fifo_head);
255 23e39294 pbrook
    qemu_put_be32(f, s->rx_fifo_head);
256 23e39294 pbrook
    qemu_put_be32(f, s->tx_fifo_len);
257 23e39294 pbrook
    qemu_put_be32(f, s->rx_fifo_len);
258 23e39294 pbrook
    for (i = 0; i < 8; i++) {
259 23e39294 pbrook
        qemu_put_be16(f, s->tx_fifo[i]);
260 23e39294 pbrook
        qemu_put_be16(f, s->rx_fifo[i]);
261 23e39294 pbrook
    }
262 23e39294 pbrook
}
263 23e39294 pbrook
264 23e39294 pbrook
static int pl022_load(QEMUFile *f, void *opaque, int version_id)
265 23e39294 pbrook
{
266 23e39294 pbrook
    pl022_state *s = (pl022_state *)opaque;
267 23e39294 pbrook
    int i;
268 23e39294 pbrook
269 23e39294 pbrook
    if (version_id != 1)
270 23e39294 pbrook
        return -EINVAL;
271 23e39294 pbrook
272 23e39294 pbrook
    s->cr0 = qemu_get_be32(f);
273 23e39294 pbrook
    s->cr1 = qemu_get_be32(f);
274 23e39294 pbrook
    s->bitmask = qemu_get_be32(f);
275 23e39294 pbrook
    s->sr = qemu_get_be32(f);
276 23e39294 pbrook
    s->cpsr = qemu_get_be32(f);
277 23e39294 pbrook
    s->is = qemu_get_be32(f);
278 23e39294 pbrook
    s->im = qemu_get_be32(f);
279 23e39294 pbrook
    s->tx_fifo_head = qemu_get_be32(f);
280 23e39294 pbrook
    s->rx_fifo_head = qemu_get_be32(f);
281 23e39294 pbrook
    s->tx_fifo_len = qemu_get_be32(f);
282 23e39294 pbrook
    s->rx_fifo_len = qemu_get_be32(f);
283 23e39294 pbrook
    for (i = 0; i < 8; i++) {
284 23e39294 pbrook
        s->tx_fifo[i] = qemu_get_be16(f);
285 23e39294 pbrook
        s->rx_fifo[i] = qemu_get_be16(f);
286 23e39294 pbrook
    }
287 23e39294 pbrook
288 23e39294 pbrook
    return 0;
289 23e39294 pbrook
}
290 23e39294 pbrook
291 5493e33f Paul Brook
static void pl022_init(SysBusDevice *dev)
292 9ee6e8bb pbrook
{
293 5493e33f Paul Brook
    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
294 9ee6e8bb pbrook
    int iomemtype;
295 9ee6e8bb pbrook
296 9ee6e8bb pbrook
    iomemtype = cpu_register_io_memory(0, pl022_readfn,
297 9ee6e8bb pbrook
                                       pl022_writefn, s);
298 5493e33f Paul Brook
    sysbus_init_mmio(dev, 0x1000, iomemtype);
299 5493e33f Paul Brook
    sysbus_init_irq(dev, &s->irq);
300 5493e33f Paul Brook
    s->ssi = ssi_create_bus();
301 5493e33f Paul Brook
    qdev_attach_child_bus(&dev->qdev, "ssi", s->ssi);
302 9ee6e8bb pbrook
    pl022_reset(s);
303 23e39294 pbrook
    register_savevm("pl022_ssp", -1, 1, pl022_save, pl022_load, s);
304 9ee6e8bb pbrook
}
305 5493e33f Paul Brook
306 5493e33f Paul Brook
static void pl022_register_devices(void)
307 5493e33f Paul Brook
{
308 5493e33f Paul Brook
    sysbus_register_dev("pl022", sizeof(pl022_state), pl022_init);
309 5493e33f Paul Brook
}
310 5493e33f Paul Brook
311 5493e33f Paul Brook
device_init(pl022_register_devices)