Statistics
| Branch: | Revision:

root / hw / pl011.c @ cf0dbb21

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