Statistics
| Branch: | Revision:

root / hw / pl181.c @ 5fafdf24

History | View | Annotate | Download (13.5 kB)

1 5fafdf24 ths
/*
2 a1bb27b1 pbrook
 * Arm PrimeCell PL181 MultiMedia Card Interface
3 a1bb27b1 pbrook
 *
4 a1bb27b1 pbrook
 * Copyright (c) 2007 CodeSourcery.
5 a1bb27b1 pbrook
 * Written by Paul Brook
6 a1bb27b1 pbrook
 *
7 a1bb27b1 pbrook
 * This code is licenced under the GPL.
8 a1bb27b1 pbrook
 */
9 a1bb27b1 pbrook
10 a1bb27b1 pbrook
#include "vl.h"
11 a1bb27b1 pbrook
#include "sd.h"
12 a1bb27b1 pbrook
13 a1bb27b1 pbrook
//#define DEBUG_PL181 1
14 a1bb27b1 pbrook
15 a1bb27b1 pbrook
#ifdef DEBUG_PL181
16 a1bb27b1 pbrook
#define DPRINTF(fmt, args...) \
17 a1bb27b1 pbrook
do { printf("pl181: " fmt , ##args); } while (0)
18 a1bb27b1 pbrook
#else
19 a1bb27b1 pbrook
#define DPRINTF(fmt, args...) do {} while(0)
20 a1bb27b1 pbrook
#endif
21 a1bb27b1 pbrook
22 a1bb27b1 pbrook
#define PL181_FIFO_LEN 16
23 a1bb27b1 pbrook
24 a1bb27b1 pbrook
typedef struct {
25 42a10898 pbrook
    SDState *card;
26 a1bb27b1 pbrook
    uint32_t base;
27 a1bb27b1 pbrook
    uint32_t clock;
28 a1bb27b1 pbrook
    uint32_t power;
29 a1bb27b1 pbrook
    uint32_t cmdarg;
30 a1bb27b1 pbrook
    uint32_t cmd;
31 a1bb27b1 pbrook
    uint32_t datatimer;
32 a1bb27b1 pbrook
    uint32_t datalength;
33 a1bb27b1 pbrook
    uint32_t respcmd;
34 a1bb27b1 pbrook
    uint32_t response[4];
35 a1bb27b1 pbrook
    uint32_t datactrl;
36 a1bb27b1 pbrook
    uint32_t datacnt;
37 a1bb27b1 pbrook
    uint32_t status;
38 a1bb27b1 pbrook
    uint32_t mask[2];
39 a1bb27b1 pbrook
    int fifo_pos;
40 a1bb27b1 pbrook
    int fifo_len;
41 6361cdb6 pbrook
    /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
42 6361cdb6 pbrook
       while it is reading the FIFO.  We hack around this be defering
43 6361cdb6 pbrook
       subsequent transfers until after the driver polls the status word.
44 6361cdb6 pbrook
       http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
45 6361cdb6 pbrook
     */
46 6361cdb6 pbrook
    int linux_hack;
47 a1bb27b1 pbrook
    uint32_t fifo[PL181_FIFO_LEN];
48 d537cf6c pbrook
    qemu_irq irq[2];
49 a1bb27b1 pbrook
} pl181_state;
50 a1bb27b1 pbrook
51 a1bb27b1 pbrook
#define PL181_CMD_INDEX     0x3f
52 a1bb27b1 pbrook
#define PL181_CMD_RESPONSE  (1 << 6)
53 a1bb27b1 pbrook
#define PL181_CMD_LONGRESP  (1 << 7)
54 a1bb27b1 pbrook
#define PL181_CMD_INTERRUPT (1 << 8)
55 a1bb27b1 pbrook
#define PL181_CMD_PENDING   (1 << 9)
56 a1bb27b1 pbrook
#define PL181_CMD_ENABLE    (1 << 10)
57 a1bb27b1 pbrook
58 a1bb27b1 pbrook
#define PL181_DATA_ENABLE             (1 << 0)
59 a1bb27b1 pbrook
#define PL181_DATA_DIRECTION          (1 << 1)
60 a1bb27b1 pbrook
#define PL181_DATA_MODE               (1 << 2)
61 a1bb27b1 pbrook
#define PL181_DATA_DMAENABLE          (1 << 3)
62 a1bb27b1 pbrook
63 a1bb27b1 pbrook
#define PL181_STATUS_CMDCRCFAIL       (1 << 0)
64 a1bb27b1 pbrook
#define PL181_STATUS_DATACRCFAIL      (1 << 1)
65 a1bb27b1 pbrook
#define PL181_STATUS_CMDTIMEOUT       (1 << 2)
66 a1bb27b1 pbrook
#define PL181_STATUS_DATATIMEOUT      (1 << 3)
67 a1bb27b1 pbrook
#define PL181_STATUS_TXUNDERRUN       (1 << 4)
68 a1bb27b1 pbrook
#define PL181_STATUS_RXOVERRUN        (1 << 5)
69 a1bb27b1 pbrook
#define PL181_STATUS_CMDRESPEND       (1 << 6)
70 a1bb27b1 pbrook
#define PL181_STATUS_CMDSENT          (1 << 7)
71 a1bb27b1 pbrook
#define PL181_STATUS_DATAEND          (1 << 8)
72 a1bb27b1 pbrook
#define PL181_STATUS_DATABLOCKEND     (1 << 10)
73 a1bb27b1 pbrook
#define PL181_STATUS_CMDACTIVE        (1 << 11)
74 a1bb27b1 pbrook
#define PL181_STATUS_TXACTIVE         (1 << 12)
75 a1bb27b1 pbrook
#define PL181_STATUS_RXACTIVE         (1 << 13)
76 a1bb27b1 pbrook
#define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
77 a1bb27b1 pbrook
#define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
78 a1bb27b1 pbrook
#define PL181_STATUS_TXFIFOFULL       (1 << 16)
79 a1bb27b1 pbrook
#define PL181_STATUS_RXFIFOFULL       (1 << 17)
80 a1bb27b1 pbrook
#define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
81 a1bb27b1 pbrook
#define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
82 a1bb27b1 pbrook
#define PL181_STATUS_TXDATAAVLBL      (1 << 20)
83 a1bb27b1 pbrook
#define PL181_STATUS_RXDATAAVLBL      (1 << 21)
84 a1bb27b1 pbrook
85 a1bb27b1 pbrook
#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
86 a1bb27b1 pbrook
                             |PL181_STATUS_TXFIFOHALFEMPTY \
87 a1bb27b1 pbrook
                             |PL181_STATUS_TXFIFOFULL \
88 a1bb27b1 pbrook
                             |PL181_STATUS_TXFIFOEMPTY \
89 a1bb27b1 pbrook
                             |PL181_STATUS_TXDATAAVLBL)
90 a1bb27b1 pbrook
#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
91 a1bb27b1 pbrook
                             |PL181_STATUS_RXFIFOHALFFULL \
92 a1bb27b1 pbrook
                             |PL181_STATUS_RXFIFOFULL \
93 a1bb27b1 pbrook
                             |PL181_STATUS_RXFIFOEMPTY \
94 a1bb27b1 pbrook
                             |PL181_STATUS_RXDATAAVLBL)
95 a1bb27b1 pbrook
96 a1bb27b1 pbrook
static const unsigned char pl181_id[] =
97 a1bb27b1 pbrook
{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
98 a1bb27b1 pbrook
99 a1bb27b1 pbrook
static void pl181_update(pl181_state *s)
100 a1bb27b1 pbrook
{
101 a1bb27b1 pbrook
    int i;
102 a1bb27b1 pbrook
    for (i = 0; i < 2; i++) {
103 d537cf6c pbrook
        qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
104 a1bb27b1 pbrook
    }
105 a1bb27b1 pbrook
}
106 a1bb27b1 pbrook
107 a1bb27b1 pbrook
static void pl181_fifo_push(pl181_state *s, uint32_t value)
108 a1bb27b1 pbrook
{
109 a1bb27b1 pbrook
    int n;
110 a1bb27b1 pbrook
111 a1bb27b1 pbrook
    if (s->fifo_len == PL181_FIFO_LEN) {
112 a1bb27b1 pbrook
        fprintf(stderr, "pl181: FIFO overflow\n");
113 a1bb27b1 pbrook
        return;
114 a1bb27b1 pbrook
    }
115 a1bb27b1 pbrook
    n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
116 a1bb27b1 pbrook
    s->fifo_len++;
117 a1bb27b1 pbrook
    s->fifo[n] = value;
118 a1bb27b1 pbrook
    DPRINTF("FIFO push %08x\n", (int)value);
119 a1bb27b1 pbrook
}
120 a1bb27b1 pbrook
121 a1bb27b1 pbrook
static uint32_t pl181_fifo_pop(pl181_state *s)
122 a1bb27b1 pbrook
{
123 a1bb27b1 pbrook
    uint32_t value;
124 a1bb27b1 pbrook
125 a1bb27b1 pbrook
    if (s->fifo_len == 0) {
126 a1bb27b1 pbrook
        fprintf(stderr, "pl181: FIFO underflow\n");
127 a1bb27b1 pbrook
        return 0;
128 a1bb27b1 pbrook
    }
129 a1bb27b1 pbrook
    value = s->fifo[s->fifo_pos];
130 a1bb27b1 pbrook
    s->fifo_len--;
131 a1bb27b1 pbrook
    s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
132 a1bb27b1 pbrook
    DPRINTF("FIFO pop %08x\n", (int)value);
133 a1bb27b1 pbrook
    return value;
134 a1bb27b1 pbrook
}
135 a1bb27b1 pbrook
136 a1bb27b1 pbrook
static void pl181_send_command(pl181_state *s)
137 a1bb27b1 pbrook
{
138 a1bb27b1 pbrook
    struct sd_request_s request;
139 a1bb27b1 pbrook
    uint8_t response[16];
140 a1bb27b1 pbrook
    int rlen;
141 a1bb27b1 pbrook
142 a1bb27b1 pbrook
    request.cmd = s->cmd & PL181_CMD_INDEX;
143 a1bb27b1 pbrook
    request.arg = s->cmdarg;
144 a1bb27b1 pbrook
    DPRINTF("Command %d %08x\n", request.cmd, request.arg);
145 a1bb27b1 pbrook
    rlen = sd_do_command(s->card, &request, response);
146 a1bb27b1 pbrook
    if (rlen < 0)
147 a1bb27b1 pbrook
        goto error;
148 a1bb27b1 pbrook
    if (s->cmd & PL181_CMD_RESPONSE) {
149 a1bb27b1 pbrook
#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
150 a1bb27b1 pbrook
                  | (response[n + 2] << 8) | response[n + 3])
151 a1bb27b1 pbrook
        if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
152 a1bb27b1 pbrook
            goto error;
153 a1bb27b1 pbrook
        if (rlen != 4 && rlen != 16)
154 a1bb27b1 pbrook
            goto error;
155 a1bb27b1 pbrook
        s->response[0] = RWORD(0);
156 a1bb27b1 pbrook
        if (rlen == 4) {
157 a1bb27b1 pbrook
            s->response[1] = s->response[2] = s->response[3] = 0;
158 a1bb27b1 pbrook
        } else {
159 a1bb27b1 pbrook
            s->response[1] = RWORD(4);
160 a1bb27b1 pbrook
            s->response[2] = RWORD(8);
161 a1bb27b1 pbrook
            s->response[3] = RWORD(12) & ~1;
162 a1bb27b1 pbrook
        }
163 aa1f17c1 ths
        DPRINTF("Response received\n");
164 a1bb27b1 pbrook
        s->status |= PL181_STATUS_CMDRESPEND;
165 a1bb27b1 pbrook
#undef RWORD
166 a1bb27b1 pbrook
    } else {
167 a1bb27b1 pbrook
        DPRINTF("Command sent\n");
168 a1bb27b1 pbrook
        s->status |= PL181_STATUS_CMDSENT;
169 a1bb27b1 pbrook
    }
170 a1bb27b1 pbrook
    return;
171 a1bb27b1 pbrook
172 a1bb27b1 pbrook
error:
173 a1bb27b1 pbrook
    DPRINTF("Timeout\n");
174 a1bb27b1 pbrook
    s->status |= PL181_STATUS_CMDTIMEOUT;
175 a1bb27b1 pbrook
}
176 a1bb27b1 pbrook
177 aa1f17c1 ths
/* Transfer data between the card and the FIFO.  This is complicated by
178 a1bb27b1 pbrook
   the FIFO holding 32-bit words and the card taking data in single byte
179 a1bb27b1 pbrook
   chunks.  FIFO bytes are transferred in little-endian order.  */
180 5fafdf24 ths
  
181 a1bb27b1 pbrook
static void pl181_fifo_run(pl181_state *s)
182 a1bb27b1 pbrook
{
183 a1bb27b1 pbrook
    uint32_t bits;
184 a1bb27b1 pbrook
    uint32_t value;
185 a1bb27b1 pbrook
    int n;
186 a1bb27b1 pbrook
    int limit;
187 a1bb27b1 pbrook
    int is_read;
188 a1bb27b1 pbrook
189 a1bb27b1 pbrook
    is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
190 6361cdb6 pbrook
    if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
191 6361cdb6 pbrook
            && !s->linux_hack) {
192 a1bb27b1 pbrook
        limit = is_read ? PL181_FIFO_LEN : 0;
193 a1bb27b1 pbrook
        n = 0;
194 a1bb27b1 pbrook
        value = 0;
195 a1bb27b1 pbrook
        while (s->datacnt && s->fifo_len != limit) {
196 a1bb27b1 pbrook
            if (is_read) {
197 a1bb27b1 pbrook
                value |= (uint32_t)sd_read_data(s->card) << (n * 8);
198 a1bb27b1 pbrook
                n++;
199 a1bb27b1 pbrook
                if (n == 4) {
200 a1bb27b1 pbrook
                    pl181_fifo_push(s, value);
201 a1bb27b1 pbrook
                    value = 0;
202 a1bb27b1 pbrook
                    n = 0;
203 a1bb27b1 pbrook
                }
204 a1bb27b1 pbrook
            } else {
205 a1bb27b1 pbrook
                if (n == 0) {
206 a1bb27b1 pbrook
                    value = pl181_fifo_pop(s);
207 a1bb27b1 pbrook
                    n = 4;
208 a1bb27b1 pbrook
                }
209 a1bb27b1 pbrook
                sd_write_data(s->card, value & 0xff);
210 a1bb27b1 pbrook
                value >>= 8;
211 a1bb27b1 pbrook
                n--;
212 a1bb27b1 pbrook
            }
213 a1bb27b1 pbrook
            s->datacnt--;
214 a1bb27b1 pbrook
        }
215 a1bb27b1 pbrook
        if (n && is_read) {
216 a1bb27b1 pbrook
            pl181_fifo_push(s, value);
217 a1bb27b1 pbrook
        }
218 a1bb27b1 pbrook
    }
219 a1bb27b1 pbrook
    s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
220 a1bb27b1 pbrook
    if (s->datacnt == 0) {
221 a1bb27b1 pbrook
        s->status |= PL181_STATUS_DATAEND;
222 a1bb27b1 pbrook
        /* HACK: */
223 a1bb27b1 pbrook
        s->status |= PL181_STATUS_DATABLOCKEND;
224 a1bb27b1 pbrook
        DPRINTF("Transfer Complete\n");
225 a1bb27b1 pbrook
    }
226 6361cdb6 pbrook
    if (s->datacnt == 0 && s->fifo_len == 0) {
227 a1bb27b1 pbrook
        s->datactrl &= ~PL181_DATA_ENABLE;
228 a1bb27b1 pbrook
        DPRINTF("Data engine idle\n");
229 a1bb27b1 pbrook
    } else {
230 a1bb27b1 pbrook
        /* Update FIFO bits.  */
231 a1bb27b1 pbrook
        bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
232 a1bb27b1 pbrook
        if (s->fifo_len == 0) {
233 a1bb27b1 pbrook
            bits |= PL181_STATUS_TXFIFOEMPTY;
234 a1bb27b1 pbrook
            bits |= PL181_STATUS_RXFIFOEMPTY;
235 a1bb27b1 pbrook
        } else {
236 a1bb27b1 pbrook
            bits |= PL181_STATUS_TXDATAAVLBL;
237 a1bb27b1 pbrook
            bits |= PL181_STATUS_RXDATAAVLBL;
238 a1bb27b1 pbrook
        }
239 a1bb27b1 pbrook
        if (s->fifo_len == 16) {
240 a1bb27b1 pbrook
            bits |= PL181_STATUS_TXFIFOFULL;
241 a1bb27b1 pbrook
            bits |= PL181_STATUS_RXFIFOFULL;
242 a1bb27b1 pbrook
        }
243 a1bb27b1 pbrook
        if (s->fifo_len <= 8) {
244 a1bb27b1 pbrook
            bits |= PL181_STATUS_TXFIFOHALFEMPTY;
245 a1bb27b1 pbrook
        }
246 a1bb27b1 pbrook
        if (s->fifo_len >= 8) {
247 a1bb27b1 pbrook
            bits |= PL181_STATUS_RXFIFOHALFFULL;
248 a1bb27b1 pbrook
        }
249 a1bb27b1 pbrook
        if (s->datactrl & PL181_DATA_DIRECTION) {
250 a1bb27b1 pbrook
            bits &= PL181_STATUS_RX_FIFO;
251 a1bb27b1 pbrook
        } else {
252 a1bb27b1 pbrook
            bits &= PL181_STATUS_TX_FIFO;
253 a1bb27b1 pbrook
        }
254 a1bb27b1 pbrook
        s->status |= bits;
255 a1bb27b1 pbrook
    }
256 a1bb27b1 pbrook
}
257 a1bb27b1 pbrook
258 a1bb27b1 pbrook
static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
259 a1bb27b1 pbrook
{
260 a1bb27b1 pbrook
    pl181_state *s = (pl181_state *)opaque;
261 6361cdb6 pbrook
    uint32_t tmp;
262 a1bb27b1 pbrook
263 a1bb27b1 pbrook
    offset -= s->base;
264 a1bb27b1 pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
265 a1bb27b1 pbrook
        return pl181_id[(offset - 0xfe0) >> 2];
266 a1bb27b1 pbrook
    }
267 a1bb27b1 pbrook
    switch (offset) {
268 a1bb27b1 pbrook
    case 0x00: /* Power */
269 a1bb27b1 pbrook
        return s->power;
270 a1bb27b1 pbrook
    case 0x04: /* Clock */
271 a1bb27b1 pbrook
        return s->clock;
272 a1bb27b1 pbrook
    case 0x08: /* Argument */
273 a1bb27b1 pbrook
        return s->cmdarg;
274 a1bb27b1 pbrook
    case 0x0c: /* Command */
275 a1bb27b1 pbrook
        return s->cmd;
276 a1bb27b1 pbrook
    case 0x10: /* RespCmd */
277 a1bb27b1 pbrook
        return s->respcmd;
278 a1bb27b1 pbrook
    case 0x14: /* Response0 */
279 a1bb27b1 pbrook
        return s->response[0];
280 a1bb27b1 pbrook
    case 0x18: /* Response1 */
281 a1bb27b1 pbrook
        return s->response[1];
282 a1bb27b1 pbrook
    case 0x1c: /* Response2 */
283 a1bb27b1 pbrook
        return s->response[2];
284 a1bb27b1 pbrook
    case 0x20: /* Response3 */
285 a1bb27b1 pbrook
        return s->response[3];
286 a1bb27b1 pbrook
    case 0x24: /* DataTimer */
287 a1bb27b1 pbrook
        return s->datatimer;
288 a1bb27b1 pbrook
    case 0x28: /* DataLength */
289 a1bb27b1 pbrook
        return s->datalength;
290 a1bb27b1 pbrook
    case 0x2c: /* DataCtrl */
291 a1bb27b1 pbrook
        return s->datactrl;
292 a1bb27b1 pbrook
    case 0x30: /* DataCnt */
293 a1bb27b1 pbrook
        return s->datacnt;
294 a1bb27b1 pbrook
    case 0x34: /* Status */
295 6361cdb6 pbrook
        tmp = s->status;
296 6361cdb6 pbrook
        if (s->linux_hack) {
297 6361cdb6 pbrook
            s->linux_hack = 0;
298 6361cdb6 pbrook
            pl181_fifo_run(s);
299 6361cdb6 pbrook
            pl181_update(s);
300 6361cdb6 pbrook
        }
301 6361cdb6 pbrook
        return tmp;
302 a1bb27b1 pbrook
    case 0x3c: /* Mask0 */
303 a1bb27b1 pbrook
        return s->mask[0];
304 a1bb27b1 pbrook
    case 0x40: /* Mask1 */
305 a1bb27b1 pbrook
        return s->mask[1];
306 a1bb27b1 pbrook
    case 0x48: /* FifoCnt */
307 6361cdb6 pbrook
        /* The documentation is somewhat vague about exactly what FifoCnt
308 6361cdb6 pbrook
           does.  On real hardware it appears to be when decrememnted
309 6361cdb6 pbrook
           when a word is transfered between the FIFO and the serial
310 6361cdb6 pbrook
           data engine.  DataCnt is decremented after each byte is
311 6361cdb6 pbrook
           transfered between the serial engine and the card.
312 6361cdb6 pbrook
           We don't emulate this level of detail, so both can be the same.  */
313 6361cdb6 pbrook
        tmp = (s->datacnt + 3) >> 2;
314 6361cdb6 pbrook
        if (s->linux_hack) {
315 6361cdb6 pbrook
            s->linux_hack = 0;
316 6361cdb6 pbrook
            pl181_fifo_run(s);
317 6361cdb6 pbrook
            pl181_update(s);
318 6361cdb6 pbrook
        }
319 6361cdb6 pbrook
        return tmp;
320 a1bb27b1 pbrook
    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
321 a1bb27b1 pbrook
    case 0x90: case 0x94: case 0x98: case 0x9c:
322 a1bb27b1 pbrook
    case 0xa0: case 0xa4: case 0xa8: case 0xac:
323 a1bb27b1 pbrook
    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
324 6361cdb6 pbrook
        if (s->fifo_len == 0) {
325 a1bb27b1 pbrook
            fprintf(stderr, "pl181: Unexpected FIFO read\n");
326 a1bb27b1 pbrook
            return 0;
327 a1bb27b1 pbrook
        } else {
328 a1bb27b1 pbrook
            uint32_t value;
329 a1bb27b1 pbrook
            value = pl181_fifo_pop(s);
330 6361cdb6 pbrook
            s->linux_hack = 1;
331 a1bb27b1 pbrook
            pl181_fifo_run(s);
332 a1bb27b1 pbrook
            pl181_update(s);
333 a1bb27b1 pbrook
            return value;
334 a1bb27b1 pbrook
        }
335 a1bb27b1 pbrook
    default:
336 a1bb27b1 pbrook
        cpu_abort (cpu_single_env, "pl181_read: Bad offset %x\n", offset);
337 a1bb27b1 pbrook
        return 0;
338 a1bb27b1 pbrook
    }
339 a1bb27b1 pbrook
}
340 a1bb27b1 pbrook
341 a1bb27b1 pbrook
static void pl181_write(void *opaque, target_phys_addr_t offset,
342 a1bb27b1 pbrook
                          uint32_t value)
343 a1bb27b1 pbrook
{
344 a1bb27b1 pbrook
    pl181_state *s = (pl181_state *)opaque;
345 a1bb27b1 pbrook
346 a1bb27b1 pbrook
    offset -= s->base;
347 a1bb27b1 pbrook
    switch (offset) {
348 a1bb27b1 pbrook
    case 0x00: /* Power */
349 a1bb27b1 pbrook
        s->power = value & 0xff;
350 a1bb27b1 pbrook
        break;
351 a1bb27b1 pbrook
    case 0x04: /* Clock */
352 a1bb27b1 pbrook
        s->clock = value & 0xff;
353 a1bb27b1 pbrook
        break;
354 a1bb27b1 pbrook
    case 0x08: /* Argument */
355 a1bb27b1 pbrook
        s->cmdarg = value;
356 a1bb27b1 pbrook
        break;
357 a1bb27b1 pbrook
    case 0x0c: /* Command */
358 a1bb27b1 pbrook
        s->cmd = value;
359 a1bb27b1 pbrook
        if (s->cmd & PL181_CMD_ENABLE) {
360 a1bb27b1 pbrook
            if (s->cmd & PL181_CMD_INTERRUPT) {
361 a1bb27b1 pbrook
                fprintf(stderr, "pl181: Interrupt mode not implemented\n");
362 a1bb27b1 pbrook
                abort();
363 a1bb27b1 pbrook
            } if (s->cmd & PL181_CMD_PENDING) {
364 a1bb27b1 pbrook
                fprintf(stderr, "pl181: Pending commands not implemented\n");
365 a1bb27b1 pbrook
                abort();
366 a1bb27b1 pbrook
            } else {
367 a1bb27b1 pbrook
                pl181_send_command(s);
368 a1bb27b1 pbrook
                pl181_fifo_run(s);
369 a1bb27b1 pbrook
            }
370 a1bb27b1 pbrook
            /* The command has completed one way or the other.  */
371 a1bb27b1 pbrook
            s->cmd &= ~PL181_CMD_ENABLE;
372 a1bb27b1 pbrook
        }
373 a1bb27b1 pbrook
        break;
374 a1bb27b1 pbrook
    case 0x24: /* DataTimer */
375 a1bb27b1 pbrook
        s->datatimer = value;
376 a1bb27b1 pbrook
        break;
377 a1bb27b1 pbrook
    case 0x28: /* DataLength */
378 a1bb27b1 pbrook
        s->datalength = value & 0xffff;
379 a1bb27b1 pbrook
        break;
380 a1bb27b1 pbrook
    case 0x2c: /* DataCtrl */
381 a1bb27b1 pbrook
        s->datactrl = value & 0xff;
382 a1bb27b1 pbrook
        if (value & PL181_DATA_ENABLE) {
383 a1bb27b1 pbrook
            s->datacnt = s->datalength;
384 a1bb27b1 pbrook
            pl181_fifo_run(s);
385 a1bb27b1 pbrook
        }
386 a1bb27b1 pbrook
        break;
387 a1bb27b1 pbrook
    case 0x38: /* Clear */
388 a1bb27b1 pbrook
        s->status &= ~(value & 0x7ff);
389 a1bb27b1 pbrook
        break;
390 a1bb27b1 pbrook
    case 0x3c: /* Mask0 */
391 a1bb27b1 pbrook
        s->mask[0] = value;
392 a1bb27b1 pbrook
        break;
393 a1bb27b1 pbrook
    case 0x40: /* Mask1 */
394 a1bb27b1 pbrook
        s->mask[1] = value;
395 a1bb27b1 pbrook
        break;
396 a1bb27b1 pbrook
    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
397 a1bb27b1 pbrook
    case 0x90: case 0x94: case 0x98: case 0x9c:
398 a1bb27b1 pbrook
    case 0xa0: case 0xa4: case 0xa8: case 0xac:
399 a1bb27b1 pbrook
    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
400 6361cdb6 pbrook
        if (s->datacnt == 0) {
401 a1bb27b1 pbrook
            fprintf(stderr, "pl181: Unexpected FIFO write\n");
402 a1bb27b1 pbrook
        } else {
403 a1bb27b1 pbrook
            pl181_fifo_push(s, value);
404 a1bb27b1 pbrook
            pl181_fifo_run(s);
405 a1bb27b1 pbrook
        }
406 a1bb27b1 pbrook
        break;
407 a1bb27b1 pbrook
    default:
408 a1bb27b1 pbrook
        cpu_abort (cpu_single_env, "pl181_write: Bad offset %x\n", offset);
409 a1bb27b1 pbrook
    }
410 a1bb27b1 pbrook
    pl181_update(s);
411 a1bb27b1 pbrook
}
412 a1bb27b1 pbrook
413 a1bb27b1 pbrook
static CPUReadMemoryFunc *pl181_readfn[] = {
414 a1bb27b1 pbrook
   pl181_read,
415 a1bb27b1 pbrook
   pl181_read,
416 a1bb27b1 pbrook
   pl181_read
417 a1bb27b1 pbrook
};
418 a1bb27b1 pbrook
419 a1bb27b1 pbrook
static CPUWriteMemoryFunc *pl181_writefn[] = {
420 a1bb27b1 pbrook
   pl181_write,
421 a1bb27b1 pbrook
   pl181_write,
422 a1bb27b1 pbrook
   pl181_write
423 a1bb27b1 pbrook
};
424 a1bb27b1 pbrook
425 a1bb27b1 pbrook
static void pl181_reset(void *opaque)
426 a1bb27b1 pbrook
{
427 a1bb27b1 pbrook
    pl181_state *s = (pl181_state *)opaque;
428 a1bb27b1 pbrook
429 a1bb27b1 pbrook
    s->power = 0;
430 a1bb27b1 pbrook
    s->cmdarg = 0;
431 a1bb27b1 pbrook
    s->cmd = 0;
432 a1bb27b1 pbrook
    s->datatimer = 0;
433 a1bb27b1 pbrook
    s->datalength = 0;
434 a1bb27b1 pbrook
    s->respcmd = 0;
435 a1bb27b1 pbrook
    s->response[0] = 0;
436 a1bb27b1 pbrook
    s->response[1] = 0;
437 a1bb27b1 pbrook
    s->response[2] = 0;
438 a1bb27b1 pbrook
    s->response[3] = 0;
439 a1bb27b1 pbrook
    s->datatimer = 0;
440 a1bb27b1 pbrook
    s->datalength = 0;
441 a1bb27b1 pbrook
    s->datactrl = 0;
442 a1bb27b1 pbrook
    s->datacnt = 0;
443 a1bb27b1 pbrook
    s->status = 0;
444 6361cdb6 pbrook
    s->linux_hack = 0;
445 a1bb27b1 pbrook
    s->mask[0] = 0;
446 a1bb27b1 pbrook
    s->mask[1] = 0;
447 a1bb27b1 pbrook
}
448 a1bb27b1 pbrook
449 a1bb27b1 pbrook
void pl181_init(uint32_t base, BlockDriverState *bd,
450 d537cf6c pbrook
                qemu_irq irq0, qemu_irq irq1)
451 a1bb27b1 pbrook
{
452 a1bb27b1 pbrook
    int iomemtype;
453 a1bb27b1 pbrook
    pl181_state *s;
454 a1bb27b1 pbrook
455 a1bb27b1 pbrook
    s = (pl181_state *)qemu_mallocz(sizeof(pl181_state));
456 a1bb27b1 pbrook
    iomemtype = cpu_register_io_memory(0, pl181_readfn,
457 a1bb27b1 pbrook
                                       pl181_writefn, s);
458 187337f8 pbrook
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
459 a1bb27b1 pbrook
    s->base = base;
460 a1bb27b1 pbrook
    s->card = sd_init(bd);
461 a1bb27b1 pbrook
    s->irq[0] = irq0;
462 a1bb27b1 pbrook
    s->irq[1] = irq1;
463 a1bb27b1 pbrook
    qemu_register_reset(pl181_reset, s);
464 a1bb27b1 pbrook
    pl181_reset(s);
465 a1bb27b1 pbrook
    /* ??? Save/restore.  */
466 a1bb27b1 pbrook
}