]> Git Repo - qemu.git/blobdiff - hw/pl181.c
net: reorganize headers
[qemu.git] / hw / pl181.c
index 62ccad9e4e3fad0c1bb723b7493f6760688eee58..8a2895ce1ba532dbdae6ac9ea3967d1523e9a3ad 100644 (file)
@@ -1,29 +1,31 @@
-/* 
+/*
  * Arm PrimeCell PL181 MultiMedia Card Interface
  *
  * Copyright (c) 2007 CodeSourcery.
  * Written by Paul Brook
  *
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
  */
 
-#include "vl.h"
+#include "blockdev.h"
+#include "sysbus.h"
 #include "sd.h"
 
 //#define DEBUG_PL181 1
 
 #ifdef DEBUG_PL181
-#define DPRINTF(fmt, args...) \
-do { printf("pl181: " fmt , ##args); } while (0)
+#define DPRINTF(fmt, ...) \
+do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
 #else
-#define DPRINTF(fmt, args...) do {} while(0)
+#define DPRINTF(fmt, ...) do {} while(0)
 #endif
 
 #define PL181_FIFO_LEN 16
 
 typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
     SDState *card;
-    uint32_t base;
     uint32_t clock;
     uint32_t power;
     uint32_t cmdarg;
@@ -36,18 +38,45 @@ typedef struct {
     uint32_t datacnt;
     uint32_t status;
     uint32_t mask[2];
-    int fifo_pos;
-    int fifo_len;
+    int32_t fifo_pos;
+    int32_t fifo_len;
     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
        while it is reading the FIFO.  We hack around this be defering
        subsequent transfers until after the driver polls the status word.
        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
      */
-    int linux_hack;
+    int32_t linux_hack;
     uint32_t fifo[PL181_FIFO_LEN];
     qemu_irq irq[2];
+    /* GPIO outputs for 'card is readonly' and 'card inserted' */
+    qemu_irq cardstatus[2];
 } pl181_state;
 
+static const VMStateDescription vmstate_pl181 = {
+    .name = "pl181",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(clock, pl181_state),
+        VMSTATE_UINT32(power, pl181_state),
+        VMSTATE_UINT32(cmdarg, pl181_state),
+        VMSTATE_UINT32(cmd, pl181_state),
+        VMSTATE_UINT32(datatimer, pl181_state),
+        VMSTATE_UINT32(datalength, pl181_state),
+        VMSTATE_UINT32(respcmd, pl181_state),
+        VMSTATE_UINT32_ARRAY(response, pl181_state, 4),
+        VMSTATE_UINT32(datactrl, pl181_state),
+        VMSTATE_UINT32(datacnt, pl181_state),
+        VMSTATE_UINT32(status, pl181_state),
+        VMSTATE_UINT32_ARRAY(mask, pl181_state, 2),
+        VMSTATE_INT32(fifo_pos, pl181_state),
+        VMSTATE_INT32(fifo_len, pl181_state),
+        VMSTATE_INT32(linux_hack, pl181_state),
+        VMSTATE_UINT32_ARRAY(fifo, pl181_state, PL181_FIFO_LEN),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 #define PL181_CMD_INDEX     0x3f
 #define PL181_CMD_RESPONSE  (1 << 6)
 #define PL181_CMD_LONGRESP  (1 << 7)
@@ -135,7 +164,7 @@ static uint32_t pl181_fifo_pop(pl181_state *s)
 
 static void pl181_send_command(pl181_state *s)
 {
-    struct sd_request_s request;
+    SDRequest request;
     uint8_t response[16];
     int rlen;
 
@@ -177,43 +206,44 @@ error:
 /* Transfer data between the card and the FIFO.  This is complicated by
    the FIFO holding 32-bit words and the card taking data in single byte
    chunks.  FIFO bytes are transferred in little-endian order.  */
-   
+
 static void pl181_fifo_run(pl181_state *s)
 {
     uint32_t bits;
-    uint32_t value;
+    uint32_t value = 0;
     int n;
-    int limit;
     int is_read;
 
     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
             && !s->linux_hack) {
-        limit = is_read ? PL181_FIFO_LEN : 0;
-        n = 0;
-        value = 0;
-        while (s->datacnt && s->fifo_len != limit) {
-            if (is_read) {
+        if (is_read) {
+            n = 0;
+            while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
+                s->datacnt--;
                 n++;
                 if (n == 4) {
                     pl181_fifo_push(s, value);
-                    value = 0;
                     n = 0;
+                    value = 0;
                 }
-            } else {
+            }
+            if (n != 0) {
+                pl181_fifo_push(s, value);
+            }
+        } else { /* write */
+            n = 0;
+            while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
                 if (n == 0) {
                     value = pl181_fifo_pop(s);
                     n = 4;
                 }
+                n--;
+                s->datacnt--;
                 sd_write_data(s->card, value & 0xff);
                 value >>= 8;
-                n--;
             }
-            s->datacnt--;
-        }
-        if (n && is_read) {
-            pl181_fifo_push(s, value);
         }
     }
     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
@@ -255,12 +285,12 @@ static void pl181_fifo_run(pl181_state *s)
     }
 }
 
-static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
+static uint64_t pl181_read(void *opaque, hwaddr offset,
+                           unsigned size)
 {
     pl181_state *s = (pl181_state *)opaque;
     uint32_t tmp;
 
-    offset -= s->base;
     if (offset >= 0xfe0 && offset < 0x1000) {
         return pl181_id[(offset - 0xfe0) >> 2];
     }
@@ -306,9 +336,9 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
     case 0x48: /* FifoCnt */
         /* The documentation is somewhat vague about exactly what FifoCnt
            does.  On real hardware it appears to be when decrememnted
-           when a word is transfered between the FIFO and the serial
+           when a word is transferred between the FIFO and the serial
            data engine.  DataCnt is decremented after each byte is
-           transfered between the serial engine and the card.
+           transferred between the serial engine and the card.
            We don't emulate this level of detail, so both can be the same.  */
         tmp = (s->datacnt + 3) >> 2;
         if (s->linux_hack) {
@@ -322,7 +352,7 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
     case 0xa0: case 0xa4: case 0xa8: case 0xac:
     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
         if (s->fifo_len == 0) {
-            fprintf(stderr, "pl181: Unexpected FIFO read\n");
+            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
             return 0;
         } else {
             uint32_t value;
@@ -333,17 +363,17 @@ static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
             return value;
         }
     default:
-        cpu_abort (cpu_single_env, "pl181_read: Bad offset %x\n", offset);
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl181_read: Bad offset %x\n", (int)offset);
         return 0;
     }
 }
 
-static void pl181_write(void *opaque, target_phys_addr_t offset,
-                          uint32_t value)
+static void pl181_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
 {
     pl181_state *s = (pl181_state *)opaque;
 
-    offset -= s->base;
     switch (offset) {
     case 0x00: /* Power */
         s->power = value & 0xff;
@@ -358,11 +388,11 @@ static void pl181_write(void *opaque, target_phys_addr_t offset,
         s->cmd = value;
         if (s->cmd & PL181_CMD_ENABLE) {
             if (s->cmd & PL181_CMD_INTERRUPT) {
-                fprintf(stderr, "pl181: Interrupt mode not implemented\n");
-                abort();
+                qemu_log_mask(LOG_UNIMP,
+                              "pl181: Interrupt mode not implemented\n");
             } if (s->cmd & PL181_CMD_PENDING) {
-                fprintf(stderr, "pl181: Pending commands not implemented\n");
-                abort();
+                qemu_log_mask(LOG_UNIMP,
+                              "pl181: Pending commands not implemented\n");
             } else {
                 pl181_send_command(s);
                 pl181_fifo_run(s);
@@ -398,33 +428,28 @@ static void pl181_write(void *opaque, target_phys_addr_t offset,
     case 0xa0: case 0xa4: case 0xa8: case 0xac:
     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
         if (s->datacnt == 0) {
-            fprintf(stderr, "pl181: Unexpected FIFO write\n");
+            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
         } else {
             pl181_fifo_push(s, value);
             pl181_fifo_run(s);
         }
         break;
     default:
-        cpu_abort (cpu_single_env, "pl181_write: Bad offset %x\n", offset);
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl181_write: Bad offset %x\n", (int)offset);
     }
     pl181_update(s);
 }
 
-static CPUReadMemoryFunc *pl181_readfn[] = {
-   pl181_read,
-   pl181_read,
-   pl181_read
+static const MemoryRegionOps pl181_ops = {
+    .read = pl181_read,
+    .write = pl181_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-static CPUWriteMemoryFunc *pl181_writefn[] = {
-   pl181_write,
-   pl181_write,
-   pl181_write
-};
-
-static void pl181_reset(void *opaque)
+static void pl181_reset(DeviceState *d)
 {
-    pl181_state *s = (pl181_state *)opaque;
+    pl181_state *s = DO_UPCAST(pl181_state, busdev.qdev, d);
 
     s->power = 0;
     s->cmdarg = 0;
@@ -444,23 +469,47 @@ static void pl181_reset(void *opaque)
     s->linux_hack = 0;
     s->mask[0] = 0;
     s->mask[1] = 0;
+
+    /* We can assume our GPIO outputs have been wired up now */
+    sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
+}
+
+static int pl181_init(SysBusDevice *dev)
+{
+    pl181_state *s = FROM_SYSBUS(pl181_state, dev);
+    DriveInfo *dinfo;
+
+    memory_region_init_io(&s->iomem, &pl181_ops, s, "pl181", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq[0]);
+    sysbus_init_irq(dev, &s->irq[1]);
+    qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2);
+    dinfo = drive_get_next(IF_SD);
+    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+    return 0;
 }
 
-void pl181_init(uint32_t base, BlockDriverState *bd,
-                qemu_irq irq0, qemu_irq irq1)
+static void pl181_class_init(ObjectClass *klass, void *data)
 {
-    int iomemtype;
-    pl181_state *s;
-
-    s = (pl181_state *)qemu_mallocz(sizeof(pl181_state));
-    iomemtype = cpu_register_io_memory(0, pl181_readfn,
-                                       pl181_writefn, s);
-    cpu_register_physical_memory(base, 0x00001000, iomemtype);
-    s->base = base;
-    s->card = sd_init(bd);
-    s->irq[0] = irq0;
-    s->irq[1] = irq1;
-    qemu_register_reset(pl181_reset, s);
-    pl181_reset(s);
-    /* ??? Save/restore.  */
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init = pl181_init;
+    k->vmsd = &vmstate_pl181;
+    k->reset = pl181_reset;
+    k->no_user = 1;
 }
+
+static TypeInfo pl181_info = {
+    .name          = "pl181",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl181_state),
+    .class_init    = pl181_class_init,
+};
+
+static void pl181_register_types(void)
+{
+    type_register_static(&pl181_info);
+}
+
+type_init(pl181_register_types)
This page took 0.036411 seconds and 4 git commands to generate.