Statistics
| Branch: | Revision:

root / hw / pl011.c @ 5fafdf24

History | View | Annotate | Download (6 kB)

1 5fafdf24 ths
/*
2 cdbdb648 pbrook
 * Arm PrimeCell PL011 UART
3 cdbdb648 pbrook
 *
4 cdbdb648 pbrook
 * Copyright (c) 2006 CodeSourcery.
5 cdbdb648 pbrook
 * Written by Paul Brook
6 cdbdb648 pbrook
 *
7 cdbdb648 pbrook
 * This code is licenced under the GPL.
8 cdbdb648 pbrook
 */
9 cdbdb648 pbrook
10 cdbdb648 pbrook
#include "vl.h"
11 cdbdb648 pbrook
12 cdbdb648 pbrook
typedef struct {
13 cdbdb648 pbrook
    uint32_t base;
14 cdbdb648 pbrook
    uint32_t readbuff;
15 cdbdb648 pbrook
    uint32_t flags;
16 cdbdb648 pbrook
    uint32_t lcr;
17 cdbdb648 pbrook
    uint32_t cr;
18 cdbdb648 pbrook
    uint32_t dmacr;
19 cdbdb648 pbrook
    uint32_t int_enabled;
20 cdbdb648 pbrook
    uint32_t int_level;
21 cdbdb648 pbrook
    uint32_t read_fifo[16];
22 cdbdb648 pbrook
    uint32_t ilpr;
23 cdbdb648 pbrook
    uint32_t ibrd;
24 cdbdb648 pbrook
    uint32_t fbrd;
25 cdbdb648 pbrook
    uint32_t ifl;
26 cdbdb648 pbrook
    int read_pos;
27 cdbdb648 pbrook
    int read_count;
28 cdbdb648 pbrook
    int read_trigger;
29 cdbdb648 pbrook
    CharDriverState *chr;
30 d537cf6c pbrook
    qemu_irq irq;
31 cdbdb648 pbrook
} pl011_state;
32 cdbdb648 pbrook
33 cdbdb648 pbrook
#define PL011_INT_TX 0x20
34 cdbdb648 pbrook
#define PL011_INT_RX 0x10
35 cdbdb648 pbrook
36 cdbdb648 pbrook
#define PL011_FLAG_TXFE 0x80
37 cdbdb648 pbrook
#define PL011_FLAG_RXFF 0x40
38 cdbdb648 pbrook
#define PL011_FLAG_TXFF 0x20
39 cdbdb648 pbrook
#define PL011_FLAG_RXFE 0x10
40 cdbdb648 pbrook
41 cdbdb648 pbrook
static const unsigned char pl011_id[] =
42 cdbdb648 pbrook
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
43 cdbdb648 pbrook
44 cdbdb648 pbrook
static void pl011_update(pl011_state *s)
45 cdbdb648 pbrook
{
46 cdbdb648 pbrook
    uint32_t flags;
47 5fafdf24 ths
   
48 cdbdb648 pbrook
    flags = s->int_level & s->int_enabled;
49 d537cf6c pbrook
    qemu_set_irq(s->irq, flags != 0);
50 cdbdb648 pbrook
}
51 cdbdb648 pbrook
52 cdbdb648 pbrook
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
53 cdbdb648 pbrook
{
54 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
55 cdbdb648 pbrook
    uint32_t c;
56 cdbdb648 pbrook
57 cdbdb648 pbrook
    offset -= s->base;
58 cdbdb648 pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
59 cdbdb648 pbrook
        return pl011_id[(offset - 0xfe0) >> 2];
60 cdbdb648 pbrook
    }
61 cdbdb648 pbrook
    switch (offset >> 2) {
62 cdbdb648 pbrook
    case 0: /* UARTDR */
63 cdbdb648 pbrook
        s->flags &= ~PL011_FLAG_RXFF;
64 cdbdb648 pbrook
        c = s->read_fifo[s->read_pos];
65 cdbdb648 pbrook
        if (s->read_count > 0) {
66 cdbdb648 pbrook
            s->read_count--;
67 cdbdb648 pbrook
            if (++s->read_pos == 16)
68 cdbdb648 pbrook
                s->read_pos = 0;
69 cdbdb648 pbrook
        }
70 cdbdb648 pbrook
        if (s->read_count == 0) {
71 cdbdb648 pbrook
            s->flags |= PL011_FLAG_RXFE;
72 cdbdb648 pbrook
        }
73 cdbdb648 pbrook
        if (s->read_count == s->read_trigger - 1)
74 cdbdb648 pbrook
            s->int_level &= ~ PL011_INT_RX;
75 cdbdb648 pbrook
        pl011_update(s);
76 cdbdb648 pbrook
        return c;
77 cdbdb648 pbrook
    case 1: /* UARTCR */
78 cdbdb648 pbrook
        return 0;
79 cdbdb648 pbrook
    case 6: /* UARTFR */
80 cdbdb648 pbrook
        return s->flags;
81 cdbdb648 pbrook
    case 8: /* UARTILPR */
82 cdbdb648 pbrook
        return s->ilpr;
83 cdbdb648 pbrook
    case 9: /* UARTIBRD */
84 cdbdb648 pbrook
        return s->ibrd;
85 cdbdb648 pbrook
    case 10: /* UARTFBRD */
86 cdbdb648 pbrook
        return s->fbrd;
87 cdbdb648 pbrook
    case 11: /* UARTLCR_H */
88 cdbdb648 pbrook
        return s->lcr;
89 cdbdb648 pbrook
    case 12: /* UARTCR */
90 cdbdb648 pbrook
        return s->cr;
91 cdbdb648 pbrook
    case 13: /* UARTIFLS */
92 cdbdb648 pbrook
        return s->ifl;
93 cdbdb648 pbrook
    case 14: /* UARTIMSC */
94 cdbdb648 pbrook
        return s->int_enabled;
95 cdbdb648 pbrook
    case 15: /* UARTRIS */
96 cdbdb648 pbrook
        return s->int_level;
97 cdbdb648 pbrook
    case 16: /* UARTMIS */
98 cdbdb648 pbrook
        return s->int_level & s->int_enabled;
99 cdbdb648 pbrook
    case 18: /* UARTDMACR */
100 cdbdb648 pbrook
        return s->dmacr;
101 cdbdb648 pbrook
    default:
102 cdbdb648 pbrook
        cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset);
103 cdbdb648 pbrook
        return 0;
104 cdbdb648 pbrook
    }
105 cdbdb648 pbrook
}
106 cdbdb648 pbrook
107 cdbdb648 pbrook
static void pl011_set_read_trigger(pl011_state *s)
108 cdbdb648 pbrook
{
109 cdbdb648 pbrook
#if 0
110 cdbdb648 pbrook
    /* The docs say the RX interrupt is triggered when the FIFO exceeds
111 cdbdb648 pbrook
       the threshold.  However linux only reads the FIFO in response to an
112 cdbdb648 pbrook
       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
113 cdbdb648 pbrook
       to make things work.  */
114 cdbdb648 pbrook
    if (s->lcr & 0x10)
115 cdbdb648 pbrook
        s->read_trigger = (s->ifl >> 1) & 0x1c;
116 cdbdb648 pbrook
    else
117 cdbdb648 pbrook
#endif
118 cdbdb648 pbrook
        s->read_trigger = 1;
119 cdbdb648 pbrook
}
120 cdbdb648 pbrook
121 cdbdb648 pbrook
static void pl011_write(void *opaque, target_phys_addr_t offset,
122 cdbdb648 pbrook
                          uint32_t value)
123 cdbdb648 pbrook
{
124 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
125 cdbdb648 pbrook
    unsigned char ch;
126 cdbdb648 pbrook
127 cdbdb648 pbrook
    offset -= s->base;
128 cdbdb648 pbrook
    switch (offset >> 2) {
129 cdbdb648 pbrook
    case 0: /* UARTDR */
130 cdbdb648 pbrook
        /* ??? Check if transmitter is enabled.  */
131 cdbdb648 pbrook
        ch = value;
132 cdbdb648 pbrook
        if (s->chr)
133 cdbdb648 pbrook
            qemu_chr_write(s->chr, &ch, 1);
134 cdbdb648 pbrook
        s->int_level |= PL011_INT_TX;
135 cdbdb648 pbrook
        pl011_update(s);
136 cdbdb648 pbrook
        break;
137 cdbdb648 pbrook
    case 1: /* UARTCR */
138 cdbdb648 pbrook
        s->cr = value;
139 cdbdb648 pbrook
        break;
140 cdbdb648 pbrook
    case 8: /* UARTUARTILPR */
141 cdbdb648 pbrook
        s->ilpr = value;
142 cdbdb648 pbrook
        break;
143 cdbdb648 pbrook
    case 9: /* UARTIBRD */
144 cdbdb648 pbrook
        s->ibrd = value;
145 cdbdb648 pbrook
        break;
146 cdbdb648 pbrook
    case 10: /* UARTFBRD */
147 cdbdb648 pbrook
        s->fbrd = value;
148 cdbdb648 pbrook
        break;
149 cdbdb648 pbrook
    case 11: /* UARTLCR_H */
150 cdbdb648 pbrook
        s->lcr = value;
151 cdbdb648 pbrook
        pl011_set_read_trigger(s);
152 cdbdb648 pbrook
        break;
153 cdbdb648 pbrook
    case 12: /* UARTCR */
154 cdbdb648 pbrook
        /* ??? Need to implement the enable and loopback bits.  */
155 cdbdb648 pbrook
        s->cr = value;
156 cdbdb648 pbrook
        break;
157 cdbdb648 pbrook
    case 13: /* UARTIFS */
158 cdbdb648 pbrook
        s->ifl = value;
159 cdbdb648 pbrook
        pl011_set_read_trigger(s);
160 cdbdb648 pbrook
        break;
161 cdbdb648 pbrook
    case 14: /* UARTIMSC */
162 cdbdb648 pbrook
        s->int_enabled = value;
163 cdbdb648 pbrook
        pl011_update(s);
164 cdbdb648 pbrook
        break;
165 cdbdb648 pbrook
    case 17: /* UARTICR */
166 cdbdb648 pbrook
        s->int_level &= ~value;
167 cdbdb648 pbrook
        pl011_update(s);
168 cdbdb648 pbrook
        break;
169 cdbdb648 pbrook
    case 18: /* UARTDMACR */
170 cdbdb648 pbrook
        s->dmacr = value;
171 cdbdb648 pbrook
        if (value & 3)
172 cdbdb648 pbrook
            cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
173 cdbdb648 pbrook
        break;
174 cdbdb648 pbrook
    default:
175 cdbdb648 pbrook
        cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset);
176 cdbdb648 pbrook
    }
177 cdbdb648 pbrook
}
178 cdbdb648 pbrook
179 aa1f17c1 ths
static int pl011_can_receive(void *opaque)
180 cdbdb648 pbrook
{
181 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
182 cdbdb648 pbrook
183 cdbdb648 pbrook
    if (s->lcr & 0x10)
184 cdbdb648 pbrook
        return s->read_count < 16;
185 cdbdb648 pbrook
    else
186 cdbdb648 pbrook
        return s->read_count < 1;
187 cdbdb648 pbrook
}
188 cdbdb648 pbrook
189 aa1f17c1 ths
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
190 cdbdb648 pbrook
{
191 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
192 cdbdb648 pbrook
    int slot;
193 cdbdb648 pbrook
194 cdbdb648 pbrook
    slot = s->read_pos + s->read_count;
195 cdbdb648 pbrook
    if (slot >= 16)
196 cdbdb648 pbrook
        slot -= 16;
197 cdbdb648 pbrook
    s->read_fifo[slot] = *buf;
198 cdbdb648 pbrook
    s->read_count++;
199 cdbdb648 pbrook
    s->flags &= ~PL011_FLAG_RXFE;
200 cdbdb648 pbrook
    if (s->cr & 0x10 || s->read_count == 16) {
201 cdbdb648 pbrook
        s->flags |= PL011_FLAG_RXFF;
202 cdbdb648 pbrook
    }
203 cdbdb648 pbrook
    if (s->read_count == s->read_trigger) {
204 cdbdb648 pbrook
        s->int_level |= PL011_INT_RX;
205 cdbdb648 pbrook
        pl011_update(s);
206 cdbdb648 pbrook
    }
207 cdbdb648 pbrook
}
208 cdbdb648 pbrook
209 cdbdb648 pbrook
static void pl011_event(void *opaque, int event)
210 cdbdb648 pbrook
{
211 cdbdb648 pbrook
    /* ??? Should probably implement break.  */
212 cdbdb648 pbrook
}
213 cdbdb648 pbrook
214 cdbdb648 pbrook
static CPUReadMemoryFunc *pl011_readfn[] = {
215 cdbdb648 pbrook
   pl011_read,
216 cdbdb648 pbrook
   pl011_read,
217 cdbdb648 pbrook
   pl011_read
218 cdbdb648 pbrook
};
219 cdbdb648 pbrook
220 cdbdb648 pbrook
static CPUWriteMemoryFunc *pl011_writefn[] = {
221 cdbdb648 pbrook
   pl011_write,
222 cdbdb648 pbrook
   pl011_write,
223 cdbdb648 pbrook
   pl011_write
224 cdbdb648 pbrook
};
225 cdbdb648 pbrook
226 d537cf6c pbrook
void pl011_init(uint32_t base, qemu_irq irq,
227 cdbdb648 pbrook
                CharDriverState *chr)
228 cdbdb648 pbrook
{
229 cdbdb648 pbrook
    int iomemtype;
230 cdbdb648 pbrook
    pl011_state *s;
231 cdbdb648 pbrook
232 cdbdb648 pbrook
    s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
233 cdbdb648 pbrook
    iomemtype = cpu_register_io_memory(0, pl011_readfn,
234 cdbdb648 pbrook
                                       pl011_writefn, s);
235 187337f8 pbrook
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
236 cdbdb648 pbrook
    s->base = base;
237 cdbdb648 pbrook
    s->irq = irq;
238 cdbdb648 pbrook
    s->chr = chr;
239 cdbdb648 pbrook
    s->read_trigger = 1;
240 cdbdb648 pbrook
    s->ifl = 0x12;
241 cdbdb648 pbrook
    s->cr = 0x300;
242 cdbdb648 pbrook
    s->flags = 0x90;
243 5fafdf24 ths
    if (chr){
244 aa1f17c1 ths
        qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
245 e5b0bc44 pbrook
                              pl011_event, s);
246 cdbdb648 pbrook
    }
247 cdbdb648 pbrook
    /* ??? Save/restore.  */
248 cdbdb648 pbrook
}