Statistics
| Branch: | Revision:

root / hw / pl011.c @ c570fd16

History | View | Annotate | Download (6 kB)

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