Statistics
| Branch: | Revision:

root / hw / pl022.c @ 9ee6e8bb

History | View | Annotate | Download (6.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 "vl.h"
11

    
12
//#define DEBUG_PL022 1
13

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

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

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

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

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

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

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

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

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

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

    
246
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
247
                void * opaque)
248
{
249
    int iomemtype;
250
    pl022_state *s;
251

    
252
    s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
253
    iomemtype = cpu_register_io_memory(0, pl022_readfn,
254
                                       pl022_writefn, s);
255
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
256
    s->base = base;
257
    s->irq = irq;
258
    s->xfer_cb = xfer_cb;
259
    s->opaque = opaque;
260
    pl022_reset(s);
261
    /* ??? Save/restore.  */
262
}
263

    
264