Statistics
| Branch: | Revision:

root / hw / pl022.c @ 0dad6c35

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

112 9ee6e8bb pbrook
       We choose to support (a) by stalling the transmit engine if it would
113 9ee6e8bb pbrook
       cause the RX FIFO to overflow.  In practice much transmit-only code
114 9ee6e8bb pbrook
       falls into (a) because it flushes the RX FIFO to determine when
115 9ee6e8bb pbrook
       the transfer has completed.  */
116 9ee6e8bb pbrook
    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
117 9ee6e8bb pbrook
        DPRINTF("xfer\n");
118 9ee6e8bb pbrook
        val = s->tx_fifo[i];
119 9ee6e8bb pbrook
        if (s->cr1 & PL022_CR1_LBM) {
120 9ee6e8bb pbrook
            /* Loopback mode.  */
121 9ee6e8bb pbrook
        } else {
122 5493e33f Paul Brook
            val = ssi_transfer(s->ssi, val);
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 02a59c37 Avi Kivity
static uint64_t pl022_read(void *opaque, target_phys_addr_t offset,
135 02a59c37 Avi Kivity
                           unsigned size)
136 9ee6e8bb pbrook
{
137 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
138 9ee6e8bb pbrook
    int val;
139 9ee6e8bb pbrook
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 2ac71179 Paul Brook
        hw_error("pl022_read: Bad offset %x\n", (int)offset);
173 9ee6e8bb pbrook
        return 0;
174 9ee6e8bb pbrook
    }
175 9ee6e8bb pbrook
}
176 9ee6e8bb pbrook
177 c227f099 Anthony Liguori
static void pl022_write(void *opaque, target_phys_addr_t offset,
178 02a59c37 Avi Kivity
                        uint64_t value, unsigned size)
179 9ee6e8bb pbrook
{
180 9ee6e8bb pbrook
    pl022_state *s = (pl022_state *)opaque;
181 9ee6e8bb pbrook
182 9ee6e8bb pbrook
    switch (offset) {
183 9ee6e8bb pbrook
    case 0x00: /* CR0 */
184 9ee6e8bb pbrook
        s->cr0 = value;
185 9ee6e8bb pbrook
        /* Clock rate and format are ignored.  */
186 9ee6e8bb pbrook
        s->bitmask = (1 << ((value & 15) + 1)) - 1;
187 9ee6e8bb pbrook
        break;
188 9ee6e8bb pbrook
    case 0x04: /* CR1 */
189 9ee6e8bb pbrook
        s->cr1 = value;
190 9ee6e8bb pbrook
        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
191 9ee6e8bb pbrook
                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
192 9ee6e8bb pbrook
            BADF("SPI slave mode not implemented\n");
193 9ee6e8bb pbrook
        }
194 9ee6e8bb pbrook
        pl022_xfer(s);
195 9ee6e8bb pbrook
        break;
196 9ee6e8bb pbrook
    case 0x08: /* DR */
197 9ee6e8bb pbrook
        if (s->tx_fifo_len < 8) {
198 02a59c37 Avi Kivity
            DPRINTF("TX %02x\n", (unsigned)value);
199 9ee6e8bb pbrook
            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
200 9ee6e8bb pbrook
            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
201 9ee6e8bb pbrook
            s->tx_fifo_len++;
202 9ee6e8bb pbrook
            pl022_xfer(s);
203 9ee6e8bb pbrook
        }
204 9ee6e8bb pbrook
        break;
205 9ee6e8bb pbrook
    case 0x10: /* CPSR */
206 9ee6e8bb pbrook
        /* Prescaler.  Ignored.  */
207 9ee6e8bb pbrook
        s->cpsr = value & 0xff;
208 9ee6e8bb pbrook
        break;
209 9ee6e8bb pbrook
    case 0x14: /* IMSC */
210 9ee6e8bb pbrook
        s->im = value;
211 9ee6e8bb pbrook
        pl022_update(s);
212 9ee6e8bb pbrook
        break;
213 9ee6e8bb pbrook
    case 0x20: /* DMACR */
214 2ac71179 Paul Brook
        if (value) {
215 2ac71179 Paul Brook
            hw_error("pl022: DMA not implemented\n");
216 2ac71179 Paul Brook
        }
217 9ee6e8bb pbrook
        break;
218 9ee6e8bb pbrook
    default:
219 2ac71179 Paul Brook
        hw_error("pl022_write: Bad offset %x\n", (int)offset);
220 9ee6e8bb pbrook
    }
221 9ee6e8bb pbrook
}
222 9ee6e8bb pbrook
223 9ee6e8bb pbrook
static void pl022_reset(pl022_state *s)
224 9ee6e8bb pbrook
{
225 9ee6e8bb pbrook
    s->rx_fifo_len = 0;
226 9ee6e8bb pbrook
    s->tx_fifo_len = 0;
227 9ee6e8bb pbrook
    s->im = 0;
228 9ee6e8bb pbrook
    s->is = PL022_INT_TX;
229 9ee6e8bb pbrook
    s->sr = PL022_SR_TFE | PL022_SR_TNF;
230 9ee6e8bb pbrook
}
231 9ee6e8bb pbrook
232 02a59c37 Avi Kivity
static const MemoryRegionOps pl022_ops = {
233 02a59c37 Avi Kivity
    .read = pl022_read,
234 02a59c37 Avi Kivity
    .write = pl022_write,
235 02a59c37 Avi Kivity
    .endianness = DEVICE_NATIVE_ENDIAN,
236 9ee6e8bb pbrook
};
237 9ee6e8bb pbrook
238 075790c2 Juan Quintela
static const VMStateDescription vmstate_pl022 = {
239 075790c2 Juan Quintela
    .name = "pl022_ssp",
240 075790c2 Juan Quintela
    .version_id = 1,
241 075790c2 Juan Quintela
    .minimum_version_id = 1,
242 075790c2 Juan Quintela
    .minimum_version_id_old = 1,
243 075790c2 Juan Quintela
    .fields      = (VMStateField[]) {
244 075790c2 Juan Quintela
        VMSTATE_UINT32(cr0, pl022_state),
245 075790c2 Juan Quintela
        VMSTATE_UINT32(cr1, pl022_state),
246 075790c2 Juan Quintela
        VMSTATE_UINT32(bitmask, pl022_state),
247 075790c2 Juan Quintela
        VMSTATE_UINT32(sr, pl022_state),
248 075790c2 Juan Quintela
        VMSTATE_UINT32(cpsr, pl022_state),
249 075790c2 Juan Quintela
        VMSTATE_UINT32(is, pl022_state),
250 075790c2 Juan Quintela
        VMSTATE_UINT32(im, pl022_state),
251 075790c2 Juan Quintela
        VMSTATE_INT32(tx_fifo_head, pl022_state),
252 075790c2 Juan Quintela
        VMSTATE_INT32(rx_fifo_head, pl022_state),
253 075790c2 Juan Quintela
        VMSTATE_INT32(tx_fifo_len, pl022_state),
254 075790c2 Juan Quintela
        VMSTATE_INT32(rx_fifo_len, pl022_state),
255 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[0], pl022_state),
256 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[0], pl022_state),
257 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[1], pl022_state),
258 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[1], pl022_state),
259 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[2], pl022_state),
260 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[2], pl022_state),
261 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[3], pl022_state),
262 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[3], pl022_state),
263 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[4], pl022_state),
264 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[4], pl022_state),
265 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[5], pl022_state),
266 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[5], pl022_state),
267 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[6], pl022_state),
268 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[6], pl022_state),
269 075790c2 Juan Quintela
        VMSTATE_UINT16(tx_fifo[7], pl022_state),
270 075790c2 Juan Quintela
        VMSTATE_UINT16(rx_fifo[7], pl022_state),
271 075790c2 Juan Quintela
        VMSTATE_END_OF_LIST()
272 23e39294 pbrook
    }
273 075790c2 Juan Quintela
};
274 23e39294 pbrook
275 81a322d4 Gerd Hoffmann
static int pl022_init(SysBusDevice *dev)
276 9ee6e8bb pbrook
{
277 5493e33f Paul Brook
    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
278 9ee6e8bb pbrook
279 02a59c37 Avi Kivity
    memory_region_init_io(&s->iomem, &pl022_ops, s, "pl022", 0x1000);
280 750ecd44 Avi Kivity
    sysbus_init_mmio(dev, &s->iomem);
281 5493e33f Paul Brook
    sysbus_init_irq(dev, &s->irq);
282 02e2da45 Paul Brook
    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
283 9ee6e8bb pbrook
    pl022_reset(s);
284 075790c2 Juan Quintela
    vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
285 81a322d4 Gerd Hoffmann
    return 0;
286 9ee6e8bb pbrook
}
287 5493e33f Paul Brook
288 999e12bb Anthony Liguori
static void pl022_class_init(ObjectClass *klass, void *data)
289 999e12bb Anthony Liguori
{
290 999e12bb Anthony Liguori
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
291 999e12bb Anthony Liguori
292 999e12bb Anthony Liguori
    sdc->init = pl022_init;
293 999e12bb Anthony Liguori
}
294 999e12bb Anthony Liguori
295 39bffca2 Anthony Liguori
static TypeInfo pl022_info = {
296 39bffca2 Anthony Liguori
    .name          = "pl022",
297 39bffca2 Anthony Liguori
    .parent        = TYPE_SYS_BUS_DEVICE,
298 39bffca2 Anthony Liguori
    .instance_size = sizeof(pl022_state),
299 39bffca2 Anthony Liguori
    .class_init    = pl022_class_init,
300 999e12bb Anthony Liguori
};
301 999e12bb Anthony Liguori
302 5493e33f Paul Brook
static void pl022_register_devices(void)
303 5493e33f Paul Brook
{
304 39bffca2 Anthony Liguori
    type_register_static(&pl022_info);
305 5493e33f Paul Brook
}
306 5493e33f Paul Brook
307 5493e33f Paul Brook
device_init(pl022_register_devices)