Statistics
| Branch: | Revision:

root / hw / pl011.c @ cdbdb648

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
    void *pic;
31
    int irq;
32
} pl011_state;
33

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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