Statistics
| Branch: | Revision:

root / hw / pl011.c @ 5e65a310

History | View | Annotate | Download (6.4 kB)

1
/*
2
 * Arm PrimeCell PL011 UART
3
 *
4
 * Copyright (c) 2006 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL.
8
 */
9

    
10
#include "hw.h"
11
#include "qemu-char.h"
12
#include "primecell.h"
13

    
14
typedef struct {
15
    uint32_t base;
16
    uint32_t readbuff;
17
    uint32_t flags;
18
    uint32_t lcr;
19
    uint32_t cr;
20
    uint32_t dmacr;
21
    uint32_t int_enabled;
22
    uint32_t int_level;
23
    uint32_t read_fifo[16];
24
    uint32_t ilpr;
25
    uint32_t ibrd;
26
    uint32_t fbrd;
27
    uint32_t ifl;
28
    int read_pos;
29
    int read_count;
30
    int read_trigger;
31
    CharDriverState *chr;
32
    qemu_irq irq;
33
    enum pl011_type type;
34
} pl011_state;
35

    
36
#define PL011_INT_TX 0x20
37
#define PL011_INT_RX 0x10
38

    
39
#define PL011_FLAG_TXFE 0x80
40
#define PL011_FLAG_RXFF 0x40
41
#define PL011_FLAG_TXFF 0x20
42
#define PL011_FLAG_RXFE 0x10
43

    
44
static const unsigned char pl011_id[2][8] = {
45
  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
46
  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
47
};
48

    
49
static void pl011_update(pl011_state *s)
50
{
51
    uint32_t flags;
52

    
53
    flags = s->int_level & s->int_enabled;
54
    qemu_set_irq(s->irq, flags != 0);
55
}
56

    
57
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
58
{
59
    pl011_state *s = (pl011_state *)opaque;
60
    uint32_t c;
61

    
62
    offset -= s->base;
63
    if (offset >= 0xfe0 && offset < 0x1000) {
64
        return pl011_id[s->type][(offset - 0xfe0) >> 2];
65
    }
66
    switch (offset >> 2) {
67
    case 0: /* UARTDR */
68
        s->flags &= ~PL011_FLAG_RXFF;
69
        c = s->read_fifo[s->read_pos];
70
        if (s->read_count > 0) {
71
            s->read_count--;
72
            if (++s->read_pos == 16)
73
                s->read_pos = 0;
74
        }
75
        if (s->read_count == 0) {
76
            s->flags |= PL011_FLAG_RXFE;
77
        }
78
        if (s->read_count == s->read_trigger - 1)
79
            s->int_level &= ~ PL011_INT_RX;
80
        pl011_update(s);
81
        qemu_chr_accept_input(s->chr);
82
        return c;
83
    case 1: /* UARTCR */
84
        return 0;
85
    case 6: /* UARTFR */
86
        return s->flags;
87
    case 8: /* UARTILPR */
88
        return s->ilpr;
89
    case 9: /* UARTIBRD */
90
        return s->ibrd;
91
    case 10: /* UARTFBRD */
92
        return s->fbrd;
93
    case 11: /* UARTLCR_H */
94
        return s->lcr;
95
    case 12: /* UARTCR */
96
        return s->cr;
97
    case 13: /* UARTIFLS */
98
        return s->ifl;
99
    case 14: /* UARTIMSC */
100
        return s->int_enabled;
101
    case 15: /* UARTRIS */
102
        return s->int_level;
103
    case 16: /* UARTMIS */
104
        return s->int_level & s->int_enabled;
105
    case 18: /* UARTDMACR */
106
        return s->dmacr;
107
    default:
108
        cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
109
        return 0;
110
    }
111
}
112

    
113
static void pl011_set_read_trigger(pl011_state *s)
114
{
115
#if 0
116
    /* The docs say the RX interrupt is triggered when the FIFO exceeds
117
       the threshold.  However linux only reads the FIFO in response to an
118
       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
119
       to make things work.  */
120
    if (s->lcr & 0x10)
121
        s->read_trigger = (s->ifl >> 1) & 0x1c;
122
    else
123
#endif
124
        s->read_trigger = 1;
125
}
126

    
127
static void pl011_write(void *opaque, target_phys_addr_t offset,
128
                          uint32_t value)
129
{
130
    pl011_state *s = (pl011_state *)opaque;
131
    unsigned char ch;
132

    
133
    offset -= s->base;
134
    switch (offset >> 2) {
135
    case 0: /* UARTDR */
136
        /* ??? Check if transmitter is enabled.  */
137
        ch = value;
138
        if (s->chr)
139
            qemu_chr_write(s->chr, &ch, 1);
140
        s->int_level |= PL011_INT_TX;
141
        pl011_update(s);
142
        break;
143
    case 1: /* UARTCR */
144
        s->cr = value;
145
        break;
146
    case 6: /* UARTFR */
147
        /* Writes to Flag register are ignored.  */
148
        break;
149
    case 8: /* UARTUARTILPR */
150
        s->ilpr = value;
151
        break;
152
    case 9: /* UARTIBRD */
153
        s->ibrd = value;
154
        break;
155
    case 10: /* UARTFBRD */
156
        s->fbrd = value;
157
        break;
158
    case 11: /* UARTLCR_H */
159
        s->lcr = value;
160
        pl011_set_read_trigger(s);
161
        break;
162
    case 12: /* UARTCR */
163
        /* ??? Need to implement the enable and loopback bits.  */
164
        s->cr = value;
165
        break;
166
    case 13: /* UARTIFS */
167
        s->ifl = value;
168
        pl011_set_read_trigger(s);
169
        break;
170
    case 14: /* UARTIMSC */
171
        s->int_enabled = value;
172
        pl011_update(s);
173
        break;
174
    case 17: /* UARTICR */
175
        s->int_level &= ~value;
176
        pl011_update(s);
177
        break;
178
    case 18: /* UARTDMACR */
179
        s->dmacr = value;
180
        if (value & 3)
181
            cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
182
        break;
183
    default:
184
        cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
185
    }
186
}
187

    
188
static int pl011_can_receive(void *opaque)
189
{
190
    pl011_state *s = (pl011_state *)opaque;
191

    
192
    if (s->lcr & 0x10)
193
        return s->read_count < 16;
194
    else
195
        return s->read_count < 1;
196
}
197

    
198
static void pl011_put_fifo(void *opaque, uint32_t value)
199
{
200
    pl011_state *s = (pl011_state *)opaque;
201
    int slot;
202

    
203
    slot = s->read_pos + s->read_count;
204
    if (slot >= 16)
205
        slot -= 16;
206
    s->read_fifo[slot] = value;
207
    s->read_count++;
208
    s->flags &= ~PL011_FLAG_RXFE;
209
    if (s->cr & 0x10 || s->read_count == 16) {
210
        s->flags |= PL011_FLAG_RXFF;
211
    }
212
    if (s->read_count == s->read_trigger) {
213
        s->int_level |= PL011_INT_RX;
214
        pl011_update(s);
215
    }
216
}
217

    
218
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
219
{
220
    pl011_put_fifo(opaque, *buf);
221
}
222

    
223
static void pl011_event(void *opaque, int event)
224
{
225
    if (event == CHR_EVENT_BREAK)
226
        pl011_put_fifo(opaque, 0x400);
227
}
228

    
229
static CPUReadMemoryFunc *pl011_readfn[] = {
230
   pl011_read,
231
   pl011_read,
232
   pl011_read
233
};
234

    
235
static CPUWriteMemoryFunc *pl011_writefn[] = {
236
   pl011_write,
237
   pl011_write,
238
   pl011_write
239
};
240

    
241
void pl011_init(uint32_t base, qemu_irq irq,
242
                CharDriverState *chr, enum pl011_type type)
243
{
244
    int iomemtype;
245
    pl011_state *s;
246

    
247
    s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
248
    iomemtype = cpu_register_io_memory(0, pl011_readfn,
249
                                       pl011_writefn, s);
250
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
251
    s->base = base;
252
    s->irq = irq;
253
    s->type = type;
254
    s->chr = chr;
255
    s->read_trigger = 1;
256
    s->ifl = 0x12;
257
    s->cr = 0x300;
258
    s->flags = 0x90;
259
    if (chr){
260
        qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
261
                              pl011_event, s);
262
    }
263
    /* ??? Save/restore.  */
264
}
265