Statistics
| Branch: | Revision:

root / hw / pl022.c @ 2ac71179

History | View | Annotate | Download (7.9 kB)

1
/*
2
 * Arm PrimeCell PL022 Synchronous Serial Port
3
 *
4
 * Copyright (c) 2007 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL.
8
 */
9

    
10
#include "hw.h"
11
#include "primecell.h"
12

    
13
//#define DEBUG_PL022 1
14

    
15
#ifdef DEBUG_PL022
16
#define DPRINTF(fmt, args...) \
17
do { printf("pl022: " fmt , ##args); } while (0)
18
#define BADF(fmt, args...) \
19
do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
20
#else
21
#define DPRINTF(fmt, args...) do {} while(0)
22
#define BADF(fmt, args...) \
23
do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
24
#endif
25

    
26
#define PL022_CR1_LBM 0x01
27
#define PL022_CR1_SSE 0x02
28
#define PL022_CR1_MS  0x04
29
#define PL022_CR1_SDO 0x08
30

    
31
#define PL022_SR_TFE  0x01
32
#define PL022_SR_TNF  0x02
33
#define PL022_SR_RNE  0x04
34
#define PL022_SR_RFF  0x08
35
#define PL022_SR_BSY  0x10
36

    
37
#define PL022_INT_ROR 0x01
38
#define PL022_INT_RT  0x04
39
#define PL022_INT_RX  0x04
40
#define PL022_INT_TX  0x08
41

    
42
typedef struct {
43
    uint32_t cr0;
44
    uint32_t cr1;
45
    uint32_t bitmask;
46
    uint32_t sr;
47
    uint32_t cpsr;
48
    uint32_t is;
49
    uint32_t im;
50
    /* The FIFO head points to the next empty entry.  */
51
    int tx_fifo_head;
52
    int rx_fifo_head;
53
    int tx_fifo_len;
54
    int rx_fifo_len;
55
    uint16_t tx_fifo[8];
56
    uint16_t rx_fifo[8];
57
    qemu_irq irq;
58
    int (*xfer_cb)(void *, int);
59
    void *opaque;
60
} pl022_state;
61

    
62
static const unsigned char pl022_id[8] =
63
  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
64

    
65
static void pl022_update(pl022_state *s)
66
{
67
    s->sr = 0;
68
    if (s->tx_fifo_len == 0)
69
        s->sr |= PL022_SR_TFE;
70
    if (s->tx_fifo_len != 8)
71
        s->sr |= PL022_SR_TNF;
72
    if (s->rx_fifo_len != 0)
73
        s->sr |= PL022_SR_RNE;
74
    if (s->rx_fifo_len == 8)
75
        s->sr |= PL022_SR_RFF;
76
    if (s->tx_fifo_len)
77
        s->sr |= PL022_SR_BSY;
78
    s->is = 0;
79
    if (s->rx_fifo_len >= 4)
80
        s->is |= PL022_INT_RX;
81
    if (s->tx_fifo_len <= 4)
82
        s->is |= PL022_INT_TX;
83

    
84
    qemu_set_irq(s->irq, (s->is & s->im) != 0);
85
}
86

    
87
static void pl022_xfer(pl022_state *s)
88
{
89
    int i;
90
    int o;
91
    int val;
92

    
93
    if ((s->cr1 & PL022_CR1_SSE) == 0) {
94
        pl022_update(s);
95
        DPRINTF("Disabled\n");
96
        return;
97
    }
98

    
99
    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
100
    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
101
    o = s->rx_fifo_head;
102
    /* ??? We do not emulate the line speed.
103
       This may break some applications.  The are two problematic cases:
104
        (a) A driver feeds data into the TX FIFO until it is full,
105
         and only then drains the RX FIFO.  On real hardware the CPU can
106
         feed data fast enough that the RX fifo never gets chance to overflow.
107
        (b) A driver transmits data, deliberately allowing the RX FIFO to
108
         overflow because it ignores the RX data anyway.
109

110
       We choose to support (a) by stalling the transmit engine if it would
111
       cause the RX FIFO to overflow.  In practice much transmit-only code
112
       falls into (a) because it flushes the RX FIFO to determine when
113
       the transfer has completed.  */
114
    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
115
        DPRINTF("xfer\n");
116
        val = s->tx_fifo[i];
117
        if (s->cr1 & PL022_CR1_LBM) {
118
            /* Loopback mode.  */
119
        } else if (s->xfer_cb) {
120
            val = s->xfer_cb(s->opaque, val);
121
        } else {
122
            val = 0;
123
        }
124
        s->rx_fifo[o] = val & s->bitmask;
125
        i = (i + 1) & 7;
126
        o = (o + 1) & 7;
127
        s->tx_fifo_len--;
128
        s->rx_fifo_len++;
129
    }
130
    s->rx_fifo_head = o;
131
    pl022_update(s);
132
}
133

    
134
static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
135
{
136
    pl022_state *s = (pl022_state *)opaque;
137
    int val;
138

    
139
    if (offset >= 0xfe0 && offset < 0x1000) {
140
        return pl022_id[(offset - 0xfe0) >> 2];
141
    }
142
    switch (offset) {
143
    case 0x00: /* CR0 */
144
      return s->cr0;
145
    case 0x04: /* CR1 */
146
      return s->cr1;
147
    case 0x08: /* DR */
148
        if (s->rx_fifo_len) {
149
            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
150
            DPRINTF("RX %02x\n", val);
151
            s->rx_fifo_len--;
152
            pl022_xfer(s);
153
        } else {
154
            val = 0;
155
        }
156
        return val;
157
    case 0x0c: /* SR */
158
        return s->sr;
159
    case 0x10: /* CPSR */
160
        return s->cpsr;
161
    case 0x14: /* IMSC */
162
        return s->im;
163
    case 0x18: /* RIS */
164
        return s->is;
165
    case 0x1c: /* MIS */
166
        return s->im & s->is;
167
    case 0x20: /* DMACR */
168
        /* Not implemented.  */
169
        return 0;
170
    default:
171
        hw_error("pl022_read: Bad offset %x\n", (int)offset);
172
        return 0;
173
    }
174
}
175

    
176
static void pl022_write(void *opaque, target_phys_addr_t offset,
177
                        uint32_t value)
178
{
179
    pl022_state *s = (pl022_state *)opaque;
180

    
181
    switch (offset) {
182
    case 0x00: /* CR0 */
183
        s->cr0 = value;
184
        /* Clock rate and format are ignored.  */
185
        s->bitmask = (1 << ((value & 15) + 1)) - 1;
186
        break;
187
    case 0x04: /* CR1 */
188
        s->cr1 = value;
189
        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
190
                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
191
            BADF("SPI slave mode not implemented\n");
192
        }
193
        pl022_xfer(s);
194
        break;
195
    case 0x08: /* DR */
196
        if (s->tx_fifo_len < 8) {
197
            DPRINTF("TX %02x\n", value);
198
            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
199
            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
200
            s->tx_fifo_len++;
201
            pl022_xfer(s);
202
        }
203
        break;
204
    case 0x10: /* CPSR */
205
        /* Prescaler.  Ignored.  */
206
        s->cpsr = value & 0xff;
207
        break;
208
    case 0x14: /* IMSC */
209
        s->im = value;
210
        pl022_update(s);
211
        break;
212
    case 0x20: /* DMACR */
213
        if (value) {
214
            hw_error("pl022: DMA not implemented\n");
215
        }
216
        break;
217
    default:
218
        hw_error("pl022_write: Bad offset %x\n", (int)offset);
219
    }
220
}
221

    
222
static void pl022_reset(pl022_state *s)
223
{
224
    s->rx_fifo_len = 0;
225
    s->tx_fifo_len = 0;
226
    s->im = 0;
227
    s->is = PL022_INT_TX;
228
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
229
}
230

    
231
static CPUReadMemoryFunc *pl022_readfn[] = {
232
   pl022_read,
233
   pl022_read,
234
   pl022_read
235
};
236

    
237
static CPUWriteMemoryFunc *pl022_writefn[] = {
238
   pl022_write,
239
   pl022_write,
240
   pl022_write
241
};
242

    
243
static void pl022_save(QEMUFile *f, void *opaque)
244
{
245
    pl022_state *s = (pl022_state *)opaque;
246
    int i;
247

    
248
    qemu_put_be32(f, s->cr0);
249
    qemu_put_be32(f, s->cr1);
250
    qemu_put_be32(f, s->bitmask);
251
    qemu_put_be32(f, s->sr);
252
    qemu_put_be32(f, s->cpsr);
253
    qemu_put_be32(f, s->is);
254
    qemu_put_be32(f, s->im);
255
    qemu_put_be32(f, s->tx_fifo_head);
256
    qemu_put_be32(f, s->rx_fifo_head);
257
    qemu_put_be32(f, s->tx_fifo_len);
258
    qemu_put_be32(f, s->rx_fifo_len);
259
    for (i = 0; i < 8; i++) {
260
        qemu_put_be16(f, s->tx_fifo[i]);
261
        qemu_put_be16(f, s->rx_fifo[i]);
262
    }
263
}
264

    
265
static int pl022_load(QEMUFile *f, void *opaque, int version_id)
266
{
267
    pl022_state *s = (pl022_state *)opaque;
268
    int i;
269

    
270
    if (version_id != 1)
271
        return -EINVAL;
272

    
273
    s->cr0 = qemu_get_be32(f);
274
    s->cr1 = qemu_get_be32(f);
275
    s->bitmask = qemu_get_be32(f);
276
    s->sr = qemu_get_be32(f);
277
    s->cpsr = qemu_get_be32(f);
278
    s->is = qemu_get_be32(f);
279
    s->im = qemu_get_be32(f);
280
    s->tx_fifo_head = qemu_get_be32(f);
281
    s->rx_fifo_head = qemu_get_be32(f);
282
    s->tx_fifo_len = qemu_get_be32(f);
283
    s->rx_fifo_len = qemu_get_be32(f);
284
    for (i = 0; i < 8; i++) {
285
        s->tx_fifo[i] = qemu_get_be16(f);
286
        s->rx_fifo[i] = qemu_get_be16(f);
287
    }
288

    
289
    return 0;
290
}
291

    
292
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
293
                void * opaque)
294
{
295
    int iomemtype;
296
    pl022_state *s;
297

    
298
    s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
299
    iomemtype = cpu_register_io_memory(0, pl022_readfn,
300
                                       pl022_writefn, s);
301
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
302
    s->irq = irq;
303
    s->xfer_cb = xfer_cb;
304
    s->opaque = opaque;
305
    pl022_reset(s);
306
    register_savevm("pl022_ssp", -1, 1, pl022_save, pl022_load, s);
307
}