Statistics
| Branch: | Revision:

root / hw / pl022.c @ 88738c09

History | View | Annotate | Download (6.8 kB)

1 9ee6e8bb pbrook
/*
2 9ee6e8bb pbrook
 * Arm PrimeCell PL022 Synchronous Serial Port
3 9ee6e8bb pbrook
 *
4 9ee6e8bb pbrook
 * Copyright (c) 2007 CodeSourcery.
5 9ee6e8bb pbrook
 * Written by Paul Brook
6 9ee6e8bb pbrook
 *
7 9ee6e8bb pbrook
 * This code is licenced under the GPL.
8 9ee6e8bb pbrook
 */
9 9ee6e8bb pbrook
10 87ecb68b pbrook
#include "hw.h"
11 87ecb68b pbrook
#include "primecell.h"
12 9ee6e8bb pbrook
13 9ee6e8bb pbrook
//#define DEBUG_PL022 1
14 9ee6e8bb pbrook
15 9ee6e8bb pbrook
#ifdef DEBUG_PL022
16 9ee6e8bb pbrook
#define DPRINTF(fmt, args...) \
17 9ee6e8bb pbrook
do { printf("pl022: " fmt , ##args); } while (0)
18 9ee6e8bb pbrook
#define BADF(fmt, args...) \
19 9ee6e8bb pbrook
do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
20 9ee6e8bb pbrook
#else
21 9ee6e8bb pbrook
#define DPRINTF(fmt, args...) do {} while(0)
22 9ee6e8bb pbrook
#define BADF(fmt, args...) \
23 9ee6e8bb pbrook
do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
24 9ee6e8bb pbrook
#endif
25 9ee6e8bb pbrook
26 9ee6e8bb pbrook
#define PL022_CR1_LBM 0x01
27 9ee6e8bb pbrook
#define PL022_CR1_SSE 0x02
28 9ee6e8bb pbrook
#define PL022_CR1_MS  0x04
29 9ee6e8bb pbrook
#define PL022_CR1_SDO 0x08
30 9ee6e8bb pbrook
31 9ee6e8bb pbrook
#define PL022_SR_TFE  0x01
32 9ee6e8bb pbrook
#define PL022_SR_TNF  0x02
33 9ee6e8bb pbrook
#define PL022_SR_RNE  0x04
34 9ee6e8bb pbrook
#define PL022_SR_RFF  0x08
35 9ee6e8bb pbrook
#define PL022_SR_BSY  0x10
36 9ee6e8bb pbrook
37 9ee6e8bb pbrook
#define PL022_INT_ROR 0x01
38 9ee6e8bb pbrook
#define PL022_INT_RT  0x04
39 9ee6e8bb pbrook
#define PL022_INT_RX  0x04
40 9ee6e8bb pbrook
#define PL022_INT_TX  0x08
41 9ee6e8bb pbrook
42 9ee6e8bb pbrook
typedef struct {
43 9ee6e8bb pbrook
    uint32_t base;
44 9ee6e8bb pbrook
    uint32_t cr0;
45 9ee6e8bb pbrook
    uint32_t cr1;
46 9ee6e8bb pbrook
    uint32_t bitmask;
47 9ee6e8bb pbrook
    uint32_t sr;
48 9ee6e8bb pbrook
    uint32_t cpsr;
49 9ee6e8bb pbrook
    uint32_t is;
50 9ee6e8bb pbrook
    uint32_t im;
51 9ee6e8bb pbrook
    /* The FIFO head points to the next empty entry.  */
52 9ee6e8bb pbrook
    int tx_fifo_head;
53 9ee6e8bb pbrook
    int rx_fifo_head;
54 9ee6e8bb pbrook
    int tx_fifo_len;
55 9ee6e8bb pbrook
    int rx_fifo_len;
56 9ee6e8bb pbrook
    uint16_t tx_fifo[8];
57 9ee6e8bb pbrook
    uint16_t rx_fifo[8];
58 9ee6e8bb pbrook
    qemu_irq irq;
59 9ee6e8bb pbrook
    int (*xfer_cb)(void *, int);
60 9ee6e8bb pbrook
    void *opaque;
61 9ee6e8bb pbrook
} pl022_state;
62 9ee6e8bb pbrook
63 9ee6e8bb pbrook
static const unsigned char pl022_id[8] =
64 9ee6e8bb pbrook
  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
65 9ee6e8bb pbrook
66 9ee6e8bb pbrook
static void pl022_update(pl022_state *s)
67 9ee6e8bb pbrook
{
68 9ee6e8bb pbrook
    s->sr = 0;
69 9ee6e8bb pbrook
    if (s->tx_fifo_len == 0)
70 9ee6e8bb pbrook
        s->sr |= PL022_SR_TFE;
71 9ee6e8bb pbrook
    if (s->tx_fifo_len != 8)
72 9ee6e8bb pbrook
        s->sr |= PL022_SR_TNF;
73 9ee6e8bb pbrook
    if (s->rx_fifo_len != 0)
74 9ee6e8bb pbrook
        s->sr |= PL022_SR_RNE;
75 9ee6e8bb pbrook
    if (s->rx_fifo_len == 8)
76 9ee6e8bb pbrook
        s->sr |= PL022_SR_RFF;
77 9ee6e8bb pbrook
    if (s->tx_fifo_len)
78 9ee6e8bb pbrook
        s->sr |= PL022_SR_BSY;
79 9ee6e8bb pbrook
    s->is = 0;
80 9ee6e8bb pbrook
    if (s->rx_fifo_len >= 4)
81 9ee6e8bb pbrook
        s->is |= PL022_INT_RX;
82 9ee6e8bb pbrook
    if (s->tx_fifo_len <= 4)
83 9ee6e8bb pbrook
        s->is |= PL022_INT_TX;
84 9ee6e8bb pbrook
85 9ee6e8bb pbrook
    qemu_set_irq(s->irq, (s->is & s->im) != 0);
86 9ee6e8bb pbrook
}
87 9ee6e8bb pbrook
88 9ee6e8bb pbrook
static void pl022_xfer(pl022_state *s)
89 9ee6e8bb pbrook
{
90 9ee6e8bb pbrook
    int i;
91 9ee6e8bb pbrook
    int o;
92 9ee6e8bb pbrook
    int val;
93 9ee6e8bb pbrook
94 9ee6e8bb pbrook
    if ((s->cr1 & PL022_CR1_SSE) == 0) {
95 9ee6e8bb pbrook
        pl022_update(s);
96 9ee6e8bb pbrook
        DPRINTF("Disabled\n");
97 9ee6e8bb pbrook
        return;
98 9ee6e8bb pbrook
    }
99 9ee6e8bb pbrook
100 9ee6e8bb pbrook
    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
101 9ee6e8bb pbrook
    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
102 9ee6e8bb pbrook
    o = s->rx_fifo_head;
103 9ee6e8bb pbrook
    /* ??? We do not emulate the line speed.
104 9ee6e8bb pbrook
       This may break some applications.  The are two problematic cases:
105 9ee6e8bb pbrook
        (a) A driver feeds data into the TX FIFO until it is full,
106 9ee6e8bb pbrook
         and only then drains the RX FIFO.  On real hardware the CPU can
107 9ee6e8bb pbrook
         feed data fast enough that the RX fifo never gets chance to overflow.
108 9ee6e8bb pbrook
        (b) A driver transmits data, deliberately allowing the RX FIFO to
109 9ee6e8bb pbrook
         overflow because it ignores the RX data anyway.
110 9ee6e8bb pbrook

111 9ee6e8bb pbrook
       We choose to support (a) by stalling the transmit engine if it would
112 9ee6e8bb pbrook
       cause the RX FIFO to overflow.  In practice much transmit-only code
113 9ee6e8bb pbrook
       falls into (a) because it flushes the RX FIFO to determine when
114 9ee6e8bb pbrook
       the transfer has completed.  */
115 9ee6e8bb pbrook
    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
116 9ee6e8bb pbrook
        DPRINTF("xfer\n");
117 9ee6e8bb pbrook
        val = s->tx_fifo[i];
118 9ee6e8bb pbrook
        if (s->cr1 & PL022_CR1_LBM) {
119 9ee6e8bb pbrook
            /* Loopback mode.  */
120 9ee6e8bb pbrook
        } else if (s->xfer_cb) {
121 9ee6e8bb pbrook
            val = s->xfer_cb(s->opaque, val);
122 9ee6e8bb pbrook
        } else {
123 9ee6e8bb pbrook
            val = 0;
124 9ee6e8bb pbrook
        }
125 9ee6e8bb pbrook
        s->rx_fifo[o] = val & s->bitmask;
126 9ee6e8bb pbrook
        i = (i + 1) & 7;
127 9ee6e8bb pbrook
        o = (o + 1) & 7;
128 9ee6e8bb pbrook
        s->tx_fifo_len--;
129 9ee6e8bb pbrook
        s->rx_fifo_len++;
130 9ee6e8bb pbrook
    }
131 9ee6e8bb pbrook
    s->rx_fifo_head = o;
132 9ee6e8bb pbrook
    pl022_update(s);
133 9ee6e8bb pbrook
}
134 9ee6e8bb pbrook
135 9ee6e8bb pbrook
static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
136 9ee6e8bb pbrook
{
137 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
138 9ee6e8bb pbrook
    int val;
139 9ee6e8bb pbrook
140 9ee6e8bb pbrook
    offset -= s->base;
141 9ee6e8bb pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
142 9ee6e8bb pbrook
        return pl022_id[(offset - 0xfe0) >> 2];
143 9ee6e8bb pbrook
    }
144 9ee6e8bb pbrook
    switch (offset) {
145 9ee6e8bb pbrook
    case 0x00: /* CR0 */
146 9ee6e8bb pbrook
      return s->cr0;
147 9ee6e8bb pbrook
    case 0x04: /* CR1 */
148 9ee6e8bb pbrook
      return s->cr1;
149 9ee6e8bb pbrook
    case 0x08: /* DR */
150 9ee6e8bb pbrook
        if (s->rx_fifo_len) {
151 9ee6e8bb pbrook
            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
152 9ee6e8bb pbrook
            DPRINTF("RX %02x\n", val);
153 9ee6e8bb pbrook
            s->rx_fifo_len--;
154 9ee6e8bb pbrook
            pl022_xfer(s);
155 9ee6e8bb pbrook
        } else {
156 9ee6e8bb pbrook
            val = 0;
157 9ee6e8bb pbrook
        }
158 9ee6e8bb pbrook
        return val;
159 9ee6e8bb pbrook
    case 0x0c: /* SR */
160 9ee6e8bb pbrook
        return s->sr;
161 9ee6e8bb pbrook
    case 0x10: /* CPSR */
162 9ee6e8bb pbrook
        return s->cpsr;
163 9ee6e8bb pbrook
    case 0x14: /* IMSC */
164 9ee6e8bb pbrook
        return s->im;
165 9ee6e8bb pbrook
    case 0x18: /* RIS */
166 9ee6e8bb pbrook
        return s->is;
167 9ee6e8bb pbrook
    case 0x1c: /* MIS */
168 9ee6e8bb pbrook
        return s->im & s->is;
169 9ee6e8bb pbrook
    case 0x20: /* DMACR */
170 9ee6e8bb pbrook
        /* Not implemented.  */
171 9ee6e8bb pbrook
        return 0;
172 9ee6e8bb pbrook
    default:
173 9ee6e8bb pbrook
        cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
174 9ee6e8bb pbrook
                   (int)offset);
175 9ee6e8bb pbrook
        return 0;
176 9ee6e8bb pbrook
    }
177 9ee6e8bb pbrook
}
178 9ee6e8bb pbrook
179 9ee6e8bb pbrook
static void pl022_write(void *opaque, target_phys_addr_t offset,
180 9ee6e8bb pbrook
                        uint32_t value)
181 9ee6e8bb pbrook
{
182 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
183 9ee6e8bb pbrook
184 9ee6e8bb pbrook
    offset -= s->base;
185 9ee6e8bb pbrook
    switch (offset) {
186 9ee6e8bb pbrook
    case 0x00: /* CR0 */
187 9ee6e8bb pbrook
        s->cr0 = value;
188 9ee6e8bb pbrook
        /* Clock rate and format are ignored.  */
189 9ee6e8bb pbrook
        s->bitmask = (1 << ((value & 15) + 1)) - 1;
190 9ee6e8bb pbrook
        break;
191 9ee6e8bb pbrook
    case 0x04: /* CR1 */
192 9ee6e8bb pbrook
        s->cr1 = value;
193 9ee6e8bb pbrook
        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
194 9ee6e8bb pbrook
                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
195 9ee6e8bb pbrook
            BADF("SPI slave mode not implemented\n");
196 9ee6e8bb pbrook
        }
197 9ee6e8bb pbrook
        pl022_xfer(s);
198 9ee6e8bb pbrook
        break;
199 9ee6e8bb pbrook
    case 0x08: /* DR */
200 9ee6e8bb pbrook
        if (s->tx_fifo_len < 8) {
201 9ee6e8bb pbrook
            DPRINTF("TX %02x\n", value);
202 9ee6e8bb pbrook
            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
203 9ee6e8bb pbrook
            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
204 9ee6e8bb pbrook
            s->tx_fifo_len++;
205 9ee6e8bb pbrook
            pl022_xfer(s);
206 9ee6e8bb pbrook
        }
207 9ee6e8bb pbrook
        break;
208 9ee6e8bb pbrook
    case 0x10: /* CPSR */
209 9ee6e8bb pbrook
        /* Prescaler.  Ignored.  */
210 9ee6e8bb pbrook
        s->cpsr = value & 0xff;
211 9ee6e8bb pbrook
        break;
212 9ee6e8bb pbrook
    case 0x14: /* IMSC */
213 9ee6e8bb pbrook
        s->im = value;
214 9ee6e8bb pbrook
        pl022_update(s);
215 9ee6e8bb pbrook
        break;
216 9ee6e8bb pbrook
    case 0x20: /* DMACR */
217 9ee6e8bb pbrook
        if (value)
218 9ee6e8bb pbrook
            cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
219 9ee6e8bb pbrook
        break;
220 9ee6e8bb pbrook
    default:
221 9ee6e8bb pbrook
        cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
222 9ee6e8bb pbrook
                   (int)offset);
223 9ee6e8bb pbrook
    }
224 9ee6e8bb pbrook
}
225 9ee6e8bb pbrook
226 9ee6e8bb pbrook
static void pl022_reset(pl022_state *s)
227 9ee6e8bb pbrook
{
228 9ee6e8bb pbrook
    s->rx_fifo_len = 0;
229 9ee6e8bb pbrook
    s->tx_fifo_len = 0;
230 9ee6e8bb pbrook
    s->im = 0;
231 9ee6e8bb pbrook
    s->is = PL022_INT_TX;
232 9ee6e8bb pbrook
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
233 9ee6e8bb pbrook
}
234 9ee6e8bb pbrook
235 9ee6e8bb pbrook
static CPUReadMemoryFunc *pl022_readfn[] = {
236 9ee6e8bb pbrook
   pl022_read,
237 9ee6e8bb pbrook
   pl022_read,
238 9ee6e8bb pbrook
   pl022_read
239 9ee6e8bb pbrook
};
240 9ee6e8bb pbrook
241 9ee6e8bb pbrook
static CPUWriteMemoryFunc *pl022_writefn[] = {
242 9ee6e8bb pbrook
   pl022_write,
243 9ee6e8bb pbrook
   pl022_write,
244 9ee6e8bb pbrook
   pl022_write
245 9ee6e8bb pbrook
};
246 9ee6e8bb pbrook
247 9ee6e8bb pbrook
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
248 9ee6e8bb pbrook
                void * opaque)
249 9ee6e8bb pbrook
{
250 9ee6e8bb pbrook
    int iomemtype;
251 9ee6e8bb pbrook
    pl022_state *s;
252 9ee6e8bb pbrook
253 9ee6e8bb pbrook
    s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
254 9ee6e8bb pbrook
    iomemtype = cpu_register_io_memory(0, pl022_readfn,
255 9ee6e8bb pbrook
                                       pl022_writefn, s);
256 9ee6e8bb pbrook
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
257 9ee6e8bb pbrook
    s->base = base;
258 9ee6e8bb pbrook
    s->irq = irq;
259 9ee6e8bb pbrook
    s->xfer_cb = xfer_cb;
260 9ee6e8bb pbrook
    s->opaque = opaque;
261 9ee6e8bb pbrook
    pl022_reset(s);
262 9ee6e8bb pbrook
    /* ??? Save/restore.  */
263 9ee6e8bb pbrook
}
264 9ee6e8bb pbrook