Statistics
| Branch: | Revision:

root / hw / pl011.c @ d537cf6c

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

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

    
33
#define PL011_INT_TX 0x20
34
#define PL011_INT_RX 0x10
35

    
36
#define PL011_FLAG_TXFE 0x80
37
#define PL011_FLAG_RXFF 0x40
38
#define PL011_FLAG_TXFF 0x20
39
#define PL011_FLAG_RXFE 0x10
40

    
41
static const unsigned char pl011_id[] =
42
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
43

    
44
static void pl011_update(pl011_state *s)
45
{
46
    uint32_t flags;
47
    
48
    flags = s->int_level & s->int_enabled;
49
    qemu_set_irq(s->irq, flags != 0);
50
}
51

    
52
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
53
{
54
    pl011_state *s = (pl011_state *)opaque;
55
    uint32_t c;
56

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

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

    
121
static void pl011_write(void *opaque, target_phys_addr_t offset,
122
                          uint32_t value)
123
{
124
    pl011_state *s = (pl011_state *)opaque;
125
    unsigned char ch;
126

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

    
179
static int pl011_can_recieve(void *opaque)
180
{
181
    pl011_state *s = (pl011_state *)opaque;
182

    
183
    if (s->lcr & 0x10)
184
        return s->read_count < 16;
185
    else
186
        return s->read_count < 1;
187
}
188

    
189
static void pl011_recieve(void *opaque, const uint8_t *buf, int size)
190
{
191
    pl011_state *s = (pl011_state *)opaque;
192
    int slot;
193

    
194
    slot = s->read_pos + s->read_count;
195
    if (slot >= 16)
196
        slot -= 16;
197
    s->read_fifo[slot] = *buf;
198
    s->read_count++;
199
    s->flags &= ~PL011_FLAG_RXFE;
200
    if (s->cr & 0x10 || s->read_count == 16) {
201
        s->flags |= PL011_FLAG_RXFF;
202
    }
203
    if (s->read_count == s->read_trigger) {
204
        s->int_level |= PL011_INT_RX;
205
        pl011_update(s);
206
    }
207
}
208

    
209
static void pl011_event(void *opaque, int event)
210
{
211
    /* ??? Should probably implement break.  */
212
}
213

    
214
static CPUReadMemoryFunc *pl011_readfn[] = {
215
   pl011_read,
216
   pl011_read,
217
   pl011_read
218
};
219

    
220
static CPUWriteMemoryFunc *pl011_writefn[] = {
221
   pl011_write,
222
   pl011_write,
223
   pl011_write
224
};
225

    
226
void pl011_init(uint32_t base, qemu_irq irq,
227
                CharDriverState *chr)
228
{
229
    int iomemtype;
230
    pl011_state *s;
231

    
232
    s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
233
    iomemtype = cpu_register_io_memory(0, pl011_readfn,
234
                                       pl011_writefn, s);
235
    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
236
    s->base = base;
237
    s->irq = irq;
238
    s->chr = chr;
239
    s->read_trigger = 1;
240
    s->ifl = 0x12;
241
    s->cr = 0x300;
242
    s->flags = 0x90;
243
    if (chr){ 
244
        qemu_chr_add_handlers(chr, pl011_can_recieve, pl011_recieve,
245
                              pl011_event, s);
246
    }
247
    /* ??? Save/restore.  */
248
}
249