Statistics
| Branch: | Revision:

root / hw / pl011.c @ 87ecb68b

History | View | Annotate | Download (6.3 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
        return c;
82
    case 1: /* UARTCR */
83
        return 0;
84
    case 6: /* UARTFR */
85
        return s->flags;
86
    case 8: /* UARTILPR */
87
        return s->ilpr;
88
    case 9: /* UARTIBRD */
89
        return s->ibrd;
90
    case 10: /* UARTFBRD */
91
        return s->fbrd;
92
    case 11: /* UARTLCR_H */
93
        return s->lcr;
94
    case 12: /* UARTCR */
95
        return s->cr;
96
    case 13: /* UARTIFLS */
97
        return s->ifl;
98
    case 14: /* UARTIMSC */
99
        return s->int_enabled;
100
    case 15: /* UARTRIS */
101
        return s->int_level;
102
    case 16: /* UARTMIS */
103
        return s->int_level & s->int_enabled;
104
    case 18: /* UARTDMACR */
105
        return s->dmacr;
106
    default:
107
        cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
108
        return 0;
109
    }
110
}
111

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

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

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

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

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

    
197
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
198
{
199
    pl011_state *s = (pl011_state *)opaque;
200
    int slot;
201

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

    
217
static void pl011_event(void *opaque, int event)
218
{
219
    /* ??? Should probably implement break.  */
220
}
221

    
222
static CPUReadMemoryFunc *pl011_readfn[] = {
223
   pl011_read,
224
   pl011_read,
225
   pl011_read
226
};
227

    
228
static CPUWriteMemoryFunc *pl011_writefn[] = {
229
   pl011_write,
230
   pl011_write,
231
   pl011_write
232
};
233

    
234
void pl011_init(uint32_t base, qemu_irq irq,
235
                CharDriverState *chr, enum pl011_type type)
236
{
237
    int iomemtype;
238
    pl011_state *s;
239

    
240
    s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
241
    iomemtype = cpu_register_io_memory(0, pl011_readfn,
242
                                       pl011_writefn, s);
243
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
244
    s->base = base;
245
    s->irq = irq;
246
    s->type = type;
247
    s->chr = chr;
248
    s->read_trigger = 1;
249
    s->ifl = 0x12;
250
    s->cr = 0x300;
251
    s->flags = 0x90;
252
    if (chr){
253
        qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
254
                              pl011_event, s);
255
    }
256
    /* ??? Save/restore.  */
257
}
258