1
2
3
4
5
6
7
8
9
10#include "qemu/osdep.h"
11#include "sysemu/blockdev.h"
12#include "hw/sysbus.h"
13#include "migration/vmstate.h"
14#include "hw/irq.h"
15#include "hw/sd/sd.h"
16#include "qemu/log.h"
17#include "qemu/module.h"
18#include "qemu/error-report.h"
19#include "qapi/error.h"
20#include "trace.h"
21#include "qom/object.h"
22
23#define PL181_FIFO_LEN 16
24
25#define TYPE_PL181 "pl181"
26OBJECT_DECLARE_SIMPLE_TYPE(PL181State, PL181)
27
28#define TYPE_PL181_BUS "pl181-bus"
29
30struct PL181State {
31 SysBusDevice parent_obj;
32
33 MemoryRegion iomem;
34 SDBus sdbus;
35 uint32_t clock;
36 uint32_t power;
37 uint32_t cmdarg;
38 uint32_t cmd;
39 uint32_t datatimer;
40 uint32_t datalength;
41 uint32_t respcmd;
42 uint32_t response[4];
43 uint32_t datactrl;
44 uint32_t datacnt;
45 uint32_t status;
46 uint32_t mask[2];
47 int32_t fifo_pos;
48 int32_t fifo_len;
49
50
51
52
53
54 int32_t linux_hack;
55 uint32_t fifo[PL181_FIFO_LEN];
56 qemu_irq irq[2];
57
58 qemu_irq card_readonly;
59 qemu_irq card_inserted;
60};
61
62static const VMStateDescription vmstate_pl181 = {
63 .name = "pl181",
64 .version_id = 1,
65 .minimum_version_id = 1,
66 .fields = (VMStateField[]) {
67 VMSTATE_UINT32(clock, PL181State),
68 VMSTATE_UINT32(power, PL181State),
69 VMSTATE_UINT32(cmdarg, PL181State),
70 VMSTATE_UINT32(cmd, PL181State),
71 VMSTATE_UINT32(datatimer, PL181State),
72 VMSTATE_UINT32(datalength, PL181State),
73 VMSTATE_UINT32(respcmd, PL181State),
74 VMSTATE_UINT32_ARRAY(response, PL181State, 4),
75 VMSTATE_UINT32(datactrl, PL181State),
76 VMSTATE_UINT32(datacnt, PL181State),
77 VMSTATE_UINT32(status, PL181State),
78 VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
79 VMSTATE_INT32(fifo_pos, PL181State),
80 VMSTATE_INT32(fifo_len, PL181State),
81 VMSTATE_INT32(linux_hack, PL181State),
82 VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
83 VMSTATE_END_OF_LIST()
84 }
85};
86
87#define PL181_CMD_INDEX 0x3f
88#define PL181_CMD_RESPONSE (1 << 6)
89#define PL181_CMD_LONGRESP (1 << 7)
90#define PL181_CMD_INTERRUPT (1 << 8)
91#define PL181_CMD_PENDING (1 << 9)
92#define PL181_CMD_ENABLE (1 << 10)
93
94#define PL181_DATA_ENABLE (1 << 0)
95#define PL181_DATA_DIRECTION (1 << 1)
96#define PL181_DATA_MODE (1 << 2)
97#define PL181_DATA_DMAENABLE (1 << 3)
98
99#define PL181_STATUS_CMDCRCFAIL (1 << 0)
100#define PL181_STATUS_DATACRCFAIL (1 << 1)
101#define PL181_STATUS_CMDTIMEOUT (1 << 2)
102#define PL181_STATUS_DATATIMEOUT (1 << 3)
103#define PL181_STATUS_TXUNDERRUN (1 << 4)
104#define PL181_STATUS_RXOVERRUN (1 << 5)
105#define PL181_STATUS_CMDRESPEND (1 << 6)
106#define PL181_STATUS_CMDSENT (1 << 7)
107#define PL181_STATUS_DATAEND (1 << 8)
108#define PL181_STATUS_DATABLOCKEND (1 << 10)
109#define PL181_STATUS_CMDACTIVE (1 << 11)
110#define PL181_STATUS_TXACTIVE (1 << 12)
111#define PL181_STATUS_RXACTIVE (1 << 13)
112#define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14)
113#define PL181_STATUS_RXFIFOHALFFULL (1 << 15)
114#define PL181_STATUS_TXFIFOFULL (1 << 16)
115#define PL181_STATUS_RXFIFOFULL (1 << 17)
116#define PL181_STATUS_TXFIFOEMPTY (1 << 18)
117#define PL181_STATUS_RXFIFOEMPTY (1 << 19)
118#define PL181_STATUS_TXDATAAVLBL (1 << 20)
119#define PL181_STATUS_RXDATAAVLBL (1 << 21)
120
121#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
122 |PL181_STATUS_TXFIFOHALFEMPTY \
123 |PL181_STATUS_TXFIFOFULL \
124 |PL181_STATUS_TXFIFOEMPTY \
125 |PL181_STATUS_TXDATAAVLBL)
126#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
127 |PL181_STATUS_RXFIFOHALFFULL \
128 |PL181_STATUS_RXFIFOFULL \
129 |PL181_STATUS_RXFIFOEMPTY \
130 |PL181_STATUS_RXDATAAVLBL)
131
132static const unsigned char pl181_id[] =
133{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
134
135static void pl181_update(PL181State *s)
136{
137 int i;
138 for (i = 0; i < 2; i++) {
139 qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
140 }
141}
142
143static void pl181_fifo_push(PL181State *s, uint32_t value)
144{
145 int n;
146
147 if (s->fifo_len == PL181_FIFO_LEN) {
148 error_report("%s: FIFO overflow", __func__);
149 return;
150 }
151 n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
152 s->fifo_len++;
153 s->fifo[n] = value;
154 trace_pl181_fifo_push(value);
155}
156
157static uint32_t pl181_fifo_pop(PL181State *s)
158{
159 uint32_t value;
160
161 if (s->fifo_len == 0) {
162 error_report("%s: FIFO underflow", __func__);
163 return 0;
164 }
165 value = s->fifo[s->fifo_pos];
166 s->fifo_len--;
167 s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
168 trace_pl181_fifo_pop(value);
169 return value;
170}
171
172static void pl181_do_command(PL181State *s)
173{
174 SDRequest request;
175 uint8_t response[16];
176 int rlen;
177
178 request.cmd = s->cmd & PL181_CMD_INDEX;
179 request.arg = s->cmdarg;
180 trace_pl181_command_send(request.cmd, request.arg);
181 rlen = sdbus_do_command(&s->sdbus, &request, response);
182 if (rlen < 0)
183 goto error;
184 if (s->cmd & PL181_CMD_RESPONSE) {
185 if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
186 goto error;
187 if (rlen != 4 && rlen != 16)
188 goto error;
189 s->response[0] = ldl_be_p(&response[0]);
190 if (rlen == 4) {
191 s->response[1] = s->response[2] = s->response[3] = 0;
192 } else {
193 s->response[1] = ldl_be_p(&response[4]);
194 s->response[2] = ldl_be_p(&response[8]);
195 s->response[3] = ldl_be_p(&response[12]) & ~1;
196 }
197 trace_pl181_command_response_pending();
198 s->status |= PL181_STATUS_CMDRESPEND;
199 } else {
200 trace_pl181_command_sent();
201 s->status |= PL181_STATUS_CMDSENT;
202 }
203 return;
204
205error:
206 trace_pl181_command_timeout();
207 s->status |= PL181_STATUS_CMDTIMEOUT;
208}
209
210
211
212
213
214static void pl181_fifo_run(PL181State *s)
215{
216 uint32_t bits;
217 uint32_t value = 0;
218 int n;
219 int is_read;
220
221 is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
222 if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
223 && !s->linux_hack) {
224 if (is_read) {
225 n = 0;
226 while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
227 value |= (uint32_t)sdbus_read_byte(&s->sdbus) << (n * 8);
228 s->datacnt--;
229 n++;
230 if (n == 4) {
231 pl181_fifo_push(s, value);
232 n = 0;
233 value = 0;
234 }
235 }
236 if (n != 0) {
237 pl181_fifo_push(s, value);
238 }
239 } else {
240 n = 0;
241 while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
242 if (n == 0) {
243 value = pl181_fifo_pop(s);
244 n = 4;
245 }
246 n--;
247 s->datacnt--;
248 sdbus_write_byte(&s->sdbus, value & 0xff);
249 value >>= 8;
250 }
251 }
252 }
253 s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
254 if (s->datacnt == 0) {
255 s->status |= PL181_STATUS_DATAEND;
256
257 s->status |= PL181_STATUS_DATABLOCKEND;
258 trace_pl181_fifo_transfer_complete();
259 }
260 if (s->datacnt == 0 && s->fifo_len == 0) {
261 s->datactrl &= ~PL181_DATA_ENABLE;
262 trace_pl181_data_engine_idle();
263 } else {
264
265 bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
266 if (s->fifo_len == 0) {
267 bits |= PL181_STATUS_TXFIFOEMPTY;
268 bits |= PL181_STATUS_RXFIFOEMPTY;
269 } else {
270 bits |= PL181_STATUS_TXDATAAVLBL;
271 bits |= PL181_STATUS_RXDATAAVLBL;
272 }
273 if (s->fifo_len == 16) {
274 bits |= PL181_STATUS_TXFIFOFULL;
275 bits |= PL181_STATUS_RXFIFOFULL;
276 }
277 if (s->fifo_len <= 8) {
278 bits |= PL181_STATUS_TXFIFOHALFEMPTY;
279 }
280 if (s->fifo_len >= 8) {
281 bits |= PL181_STATUS_RXFIFOHALFFULL;
282 }
283 if (s->datactrl & PL181_DATA_DIRECTION) {
284 bits &= PL181_STATUS_RX_FIFO;
285 } else {
286 bits &= PL181_STATUS_TX_FIFO;
287 }
288 s->status |= bits;
289 }
290}
291
292static uint64_t pl181_read(void *opaque, hwaddr offset,
293 unsigned size)
294{
295 PL181State *s = (PL181State *)opaque;
296 uint32_t tmp;
297
298 if (offset >= 0xfe0 && offset < 0x1000) {
299 return pl181_id[(offset - 0xfe0) >> 2];
300 }
301 switch (offset) {
302 case 0x00:
303 return s->power;
304 case 0x04:
305 return s->clock;
306 case 0x08:
307 return s->cmdarg;
308 case 0x0c:
309 return s->cmd;
310 case 0x10:
311 return s->respcmd;
312 case 0x14:
313 return s->response[0];
314 case 0x18:
315 return s->response[1];
316 case 0x1c:
317 return s->response[2];
318 case 0x20:
319 return s->response[3];
320 case 0x24:
321 return s->datatimer;
322 case 0x28:
323 return s->datalength;
324 case 0x2c:
325 return s->datactrl;
326 case 0x30:
327 return s->datacnt;
328 case 0x34:
329 tmp = s->status;
330 if (s->linux_hack) {
331 s->linux_hack = 0;
332 pl181_fifo_run(s);
333 pl181_update(s);
334 }
335 return tmp;
336 case 0x3c:
337 return s->mask[0];
338 case 0x40:
339 return s->mask[1];
340 case 0x48:
341
342
343
344
345
346
347 tmp = (s->datacnt + 3) >> 2;
348 if (s->linux_hack) {
349 s->linux_hack = 0;
350 pl181_fifo_run(s);
351 pl181_update(s);
352 }
353 return tmp;
354 case 0x80: case 0x84: case 0x88: case 0x8c:
355 case 0x90: case 0x94: case 0x98: case 0x9c:
356 case 0xa0: case 0xa4: case 0xa8: case 0xac:
357 case 0xb0: case 0xb4: case 0xb8: case 0xbc:
358 if (s->fifo_len == 0) {
359 qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
360 return 0;
361 } else {
362 uint32_t value;
363 value = pl181_fifo_pop(s);
364 s->linux_hack = 1;
365 pl181_fifo_run(s);
366 pl181_update(s);
367 return value;
368 }
369 default:
370 qemu_log_mask(LOG_GUEST_ERROR,
371 "pl181_read: Bad offset %x\n", (int)offset);
372 return 0;
373 }
374}
375
376static void pl181_write(void *opaque, hwaddr offset,
377 uint64_t value, unsigned size)
378{
379 PL181State *s = (PL181State *)opaque;
380
381 switch (offset) {
382 case 0x00:
383 s->power = value & 0xff;
384 break;
385 case 0x04:
386 s->clock = value & 0xff;
387 break;
388 case 0x08:
389 s->cmdarg = value;
390 break;
391 case 0x0c:
392 s->cmd = value;
393 if (s->cmd & PL181_CMD_ENABLE) {
394 if (s->cmd & PL181_CMD_INTERRUPT) {
395 qemu_log_mask(LOG_UNIMP,
396 "pl181: Interrupt mode not implemented\n");
397 } if (s->cmd & PL181_CMD_PENDING) {
398 qemu_log_mask(LOG_UNIMP,
399 "pl181: Pending commands not implemented\n");
400 } else {
401 pl181_do_command(s);
402 pl181_fifo_run(s);
403 }
404
405 s->cmd &= ~PL181_CMD_ENABLE;
406 }
407 break;
408 case 0x24:
409 s->datatimer = value;
410 break;
411 case 0x28:
412 s->datalength = value & 0xffff;
413 break;
414 case 0x2c:
415 s->datactrl = value & 0xff;
416 if (value & PL181_DATA_ENABLE) {
417 s->datacnt = s->datalength;
418 pl181_fifo_run(s);
419 }
420 break;
421 case 0x38:
422 s->status &= ~(value & 0x7ff);
423 break;
424 case 0x3c:
425 s->mask[0] = value;
426 break;
427 case 0x40:
428 s->mask[1] = value;
429 break;
430 case 0x80: case 0x84: case 0x88: case 0x8c:
431 case 0x90: case 0x94: case 0x98: case 0x9c:
432 case 0xa0: case 0xa4: case 0xa8: case 0xac:
433 case 0xb0: case 0xb4: case 0xb8: case 0xbc:
434 if (s->datacnt == 0) {
435 qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
436 } else {
437 pl181_fifo_push(s, value);
438 pl181_fifo_run(s);
439 }
440 break;
441 default:
442 qemu_log_mask(LOG_GUEST_ERROR,
443 "pl181_write: Bad offset %x\n", (int)offset);
444 }
445 pl181_update(s);
446}
447
448static const MemoryRegionOps pl181_ops = {
449 .read = pl181_read,
450 .write = pl181_write,
451 .endianness = DEVICE_NATIVE_ENDIAN,
452};
453
454static void pl181_set_readonly(DeviceState *dev, bool level)
455{
456 PL181State *s = (PL181State *)dev;
457
458 qemu_set_irq(s->card_readonly, level);
459}
460
461static void pl181_set_inserted(DeviceState *dev, bool level)
462{
463 PL181State *s = (PL181State *)dev;
464
465 qemu_set_irq(s->card_inserted, level);
466}
467
468static void pl181_reset(DeviceState *d)
469{
470 PL181State *s = PL181(d);
471
472 s->power = 0;
473 s->cmdarg = 0;
474 s->cmd = 0;
475 s->datatimer = 0;
476 s->datalength = 0;
477 s->respcmd = 0;
478 s->response[0] = 0;
479 s->response[1] = 0;
480 s->response[2] = 0;
481 s->response[3] = 0;
482 s->datatimer = 0;
483 s->datalength = 0;
484 s->datactrl = 0;
485 s->datacnt = 0;
486 s->status = 0;
487 s->linux_hack = 0;
488 s->mask[0] = 0;
489 s->mask[1] = 0;
490
491
492 pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus));
493 pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus));
494}
495
496static void pl181_init(Object *obj)
497{
498 DeviceState *dev = DEVICE(obj);
499 PL181State *s = PL181(obj);
500 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
501
502 memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
503 sysbus_init_mmio(sbd, &s->iomem);
504 sysbus_init_irq(sbd, &s->irq[0]);
505 sysbus_init_irq(sbd, &s->irq[1]);
506 qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
507 qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
508
509 qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus");
510}
511
512static void pl181_class_init(ObjectClass *klass, void *data)
513{
514 DeviceClass *k = DEVICE_CLASS(klass);
515
516 k->vmsd = &vmstate_pl181;
517 k->reset = pl181_reset;
518
519 k->user_creatable = false;
520}
521
522static const TypeInfo pl181_info = {
523 .name = TYPE_PL181,
524 .parent = TYPE_SYS_BUS_DEVICE,
525 .instance_size = sizeof(PL181State),
526 .instance_init = pl181_init,
527 .class_init = pl181_class_init,
528};
529
530static void pl181_bus_class_init(ObjectClass *klass, void *data)
531{
532 SDBusClass *sbc = SD_BUS_CLASS(klass);
533
534 sbc->set_inserted = pl181_set_inserted;
535 sbc->set_readonly = pl181_set_readonly;
536}
537
538static const TypeInfo pl181_bus_info = {
539 .name = TYPE_PL181_BUS,
540 .parent = TYPE_SD_BUS,
541 .instance_size = sizeof(SDBus),
542 .class_init = pl181_bus_class_init,
543};
544
545static void pl181_register_types(void)
546{
547 type_register_static(&pl181_info);
548 type_register_static(&pl181_bus_info);
549}
550
551type_init(pl181_register_types)
552