Statistics
| Branch: | Revision:

root / hw / pl022.c @ c6ee607c

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

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