Statistics
| Branch: | Revision:

root / hw / pl022.c @ 87ecb68b

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 "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 base;
44
    uint32_t cr0;
45
    uint32_t cr1;
46
    uint32_t bitmask;
47
    uint32_t sr;
48
    uint32_t cpsr;
49
    uint32_t is;
50
    uint32_t im;
51
    /* The FIFO head points to the next empty entry.  */
52
    int tx_fifo_head;
53
    int rx_fifo_head;
54
    int tx_fifo_len;
55
    int rx_fifo_len;
56
    uint16_t tx_fifo[8];
57
    uint16_t rx_fifo[8];
58
    qemu_irq irq;
59
    int (*xfer_cb)(void *, int);
60
    void *opaque;
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 if (s->xfer_cb) {
121
            val = s->xfer_cb(s->opaque, val);
122
        } else {
123
            val = 0;
124
        }
125
        s->rx_fifo[o] = val & s->bitmask;
126
        i = (i + 1) & 7;
127
        o = (o + 1) & 7;
128
        s->tx_fifo_len--;
129
        s->rx_fifo_len++;
130
    }
131
    s->rx_fifo_head = o;
132
    pl022_update(s);
133
}
134

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

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

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

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

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

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

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

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

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

    
265