Statistics
| Branch: | Revision:

root / hw / pl022.c @ a1bc20df

History | View | Annotate | Download (8.5 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 licensed under the GPL.
8
 */
9

    
10
#include "sysbus.h"
11
#include "ssi.h"
12

    
13
//#define DEBUG_PL022 1
14

    
15
#ifdef DEBUG_PL022
16
#define DPRINTF(fmt, ...) \
17
do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
18
#define BADF(fmt, ...) \
19
do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
20
#else
21
#define DPRINTF(fmt, ...) do {} while(0)
22
#define BADF(fmt, ...) \
23
do { fprintf(stderr, "pl022: error: " fmt , ## __VA_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
    SysBusDevice busdev;
44
    MemoryRegion iomem;
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 uint64_t pl022_read(void *opaque, hwaddr offset,
134
                           unsigned size)
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
        qemu_log_mask(LOG_GUEST_ERROR,
172
                      "pl022_read: Bad offset %x\n", (int)offset);
173
        return 0;
174
    }
175
}
176

    
177
static void pl022_write(void *opaque, hwaddr offset,
178
                        uint64_t value, unsigned size)
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", (unsigned)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
            qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
216
        }
217
        break;
218
    default:
219
        qemu_log_mask(LOG_GUEST_ERROR,
220
                      "pl022_write: Bad offset %x\n", (int)offset);
221
    }
222
}
223

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

    
233
static const MemoryRegionOps pl022_ops = {
234
    .read = pl022_read,
235
    .write = pl022_write,
236
    .endianness = DEVICE_NATIVE_ENDIAN,
237
};
238

    
239
static const VMStateDescription vmstate_pl022 = {
240
    .name = "pl022_ssp",
241
    .version_id = 1,
242
    .minimum_version_id = 1,
243
    .minimum_version_id_old = 1,
244
    .fields      = (VMStateField[]) {
245
        VMSTATE_UINT32(cr0, pl022_state),
246
        VMSTATE_UINT32(cr1, pl022_state),
247
        VMSTATE_UINT32(bitmask, pl022_state),
248
        VMSTATE_UINT32(sr, pl022_state),
249
        VMSTATE_UINT32(cpsr, pl022_state),
250
        VMSTATE_UINT32(is, pl022_state),
251
        VMSTATE_UINT32(im, pl022_state),
252
        VMSTATE_INT32(tx_fifo_head, pl022_state),
253
        VMSTATE_INT32(rx_fifo_head, pl022_state),
254
        VMSTATE_INT32(tx_fifo_len, pl022_state),
255
        VMSTATE_INT32(rx_fifo_len, pl022_state),
256
        VMSTATE_UINT16(tx_fifo[0], pl022_state),
257
        VMSTATE_UINT16(rx_fifo[0], pl022_state),
258
        VMSTATE_UINT16(tx_fifo[1], pl022_state),
259
        VMSTATE_UINT16(rx_fifo[1], pl022_state),
260
        VMSTATE_UINT16(tx_fifo[2], pl022_state),
261
        VMSTATE_UINT16(rx_fifo[2], pl022_state),
262
        VMSTATE_UINT16(tx_fifo[3], pl022_state),
263
        VMSTATE_UINT16(rx_fifo[3], pl022_state),
264
        VMSTATE_UINT16(tx_fifo[4], pl022_state),
265
        VMSTATE_UINT16(rx_fifo[4], pl022_state),
266
        VMSTATE_UINT16(tx_fifo[5], pl022_state),
267
        VMSTATE_UINT16(rx_fifo[5], pl022_state),
268
        VMSTATE_UINT16(tx_fifo[6], pl022_state),
269
        VMSTATE_UINT16(rx_fifo[6], pl022_state),
270
        VMSTATE_UINT16(tx_fifo[7], pl022_state),
271
        VMSTATE_UINT16(rx_fifo[7], pl022_state),
272
        VMSTATE_END_OF_LIST()
273
    }
274
};
275

    
276
static int pl022_init(SysBusDevice *dev)
277
{
278
    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
279

    
280
    memory_region_init_io(&s->iomem, &pl022_ops, s, "pl022", 0x1000);
281
    sysbus_init_mmio(dev, &s->iomem);
282
    sysbus_init_irq(dev, &s->irq);
283
    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
284
    pl022_reset(s);
285
    vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
286
    return 0;
287
}
288

    
289
static void pl022_class_init(ObjectClass *klass, void *data)
290
{
291
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
292

    
293
    sdc->init = pl022_init;
294
}
295

    
296
static TypeInfo pl022_info = {
297
    .name          = "pl022",
298
    .parent        = TYPE_SYS_BUS_DEVICE,
299
    .instance_size = sizeof(pl022_state),
300
    .class_init    = pl022_class_init,
301
};
302

    
303
static void pl022_register_types(void)
304
{
305
    type_register_static(&pl022_info);
306
}
307

    
308
type_init(pl022_register_types)