Statistics
| Branch: | Revision:

root / hw / pl022.c @ 9c22a623

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

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