Statistics
| Branch: | Revision:

root / hw / ssi / pl022.c @ 3d29bcee

History | View | Annotate | Download (8.6 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 "hw/sysbus.h"
11
#include "hw/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
#define TYPE_PL022 "pl022"
43
#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022)
44

    
45
typedef struct PL022State {
46
    SysBusDevice parent_obj;
47

    
48
    MemoryRegion iomem;
49
    uint32_t cr0;
50
    uint32_t cr1;
51
    uint32_t bitmask;
52
    uint32_t sr;
53
    uint32_t cpsr;
54
    uint32_t is;
55
    uint32_t im;
56
    /* The FIFO head points to the next empty entry.  */
57
    int tx_fifo_head;
58
    int rx_fifo_head;
59
    int tx_fifo_len;
60
    int rx_fifo_len;
61
    uint16_t tx_fifo[8];
62
    uint16_t rx_fifo[8];
63
    qemu_irq irq;
64
    SSIBus *ssi;
65
} PL022State;
66

    
67
static const unsigned char pl022_id[8] =
68
  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
69

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

    
89
    qemu_set_irq(s->irq, (s->is & s->im) != 0);
90
}
91

    
92
static void pl022_xfer(PL022State *s)
93
{
94
    int i;
95
    int o;
96
    int val;
97

    
98
    if ((s->cr1 & PL022_CR1_SSE) == 0) {
99
        pl022_update(s);
100
        DPRINTF("Disabled\n");
101
        return;
102
    }
103

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

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

    
137
static uint64_t pl022_read(void *opaque, hwaddr offset,
138
                           unsigned size)
139
{
140
    PL022State *s = (PL022State *)opaque;
141
    int val;
142

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

    
181
static void pl022_write(void *opaque, hwaddr offset,
182
                        uint64_t value, unsigned size)
183
{
184
    PL022State *s = (PL022State *)opaque;
185

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

    
228
static void pl022_reset(PL022State *s)
229
{
230
    s->rx_fifo_len = 0;
231
    s->tx_fifo_len = 0;
232
    s->im = 0;
233
    s->is = PL022_INT_TX;
234
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
235
}
236

    
237
static const MemoryRegionOps pl022_ops = {
238
    .read = pl022_read,
239
    .write = pl022_write,
240
    .endianness = DEVICE_NATIVE_ENDIAN,
241
};
242

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

    
280
static int pl022_init(SysBusDevice *sbd)
281
{
282
    DeviceState *dev = DEVICE(sbd);
283
    PL022State *s = PL022(dev);
284

    
285
    memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000);
286
    sysbus_init_mmio(sbd, &s->iomem);
287
    sysbus_init_irq(sbd, &s->irq);
288
    s->ssi = ssi_create_bus(dev, "ssi");
289
    pl022_reset(s);
290
    vmstate_register(dev, -1, &vmstate_pl022, s);
291
    return 0;
292
}
293

    
294
static void pl022_class_init(ObjectClass *klass, void *data)
295
{
296
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
297

    
298
    sdc->init = pl022_init;
299
}
300

    
301
static const TypeInfo pl022_info = {
302
    .name          = TYPE_PL022,
303
    .parent        = TYPE_SYS_BUS_DEVICE,
304
    .instance_size = sizeof(PL022State),
305
    .class_init    = pl022_class_init,
306
};
307

    
308
static void pl022_register_types(void)
309
{
310
    type_register_static(&pl022_info);
311
}
312

    
313
type_init(pl022_register_types)