Statistics
| Branch: | Revision:

root / hw / pl022.c @ d94f9486

History | View | Annotate | Download (8 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
        cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
172
                   (int)offset);
173
        return 0;
174
    }
175
}
176

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

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

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

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

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

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

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

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

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

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

    
290
    return 0;
291
}
292

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

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