Statistics
| Branch: | Revision:

root / hw / pl022.c @ c227f099

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 "sysbus.h"
11
#include "ssi.h"
12
#include "primecell.h"
13

    
14
//#define DEBUG_PL022 1
15

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
288
    return 0;
289
}
290

    
291
static int pl022_init(SysBusDevice *dev)
292
{
293
    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
294
    int iomemtype;
295

    
296
    iomemtype = cpu_register_io_memory(pl022_readfn,
297
                                       pl022_writefn, s);
298
    sysbus_init_mmio(dev, 0x1000, iomemtype);
299
    sysbus_init_irq(dev, &s->irq);
300
    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
301
    pl022_reset(s);
302
    register_savevm("pl022_ssp", -1, 1, pl022_save, pl022_load, s);
303
    return 0;
304
}
305

    
306
static void pl022_register_devices(void)
307
{
308
    sysbus_register_dev("pl022", sizeof(pl022_state), pl022_init);
309
}
310

    
311
device_init(pl022_register_devices)