diff mbox series

[10/11] hw/sd/sdhci: Implement Freescale eSDHC device model

Message ID 20220915152520.21948-11-shentey@gmail.com (mailing list archive)
State New, archived
Headers show
Series ppc/e500: Add support for two types of flash, cleanup | expand

Commit Message

Bernhard Beschow Sept. 15, 2022, 3:25 p.m. UTC
Will allow e500 boards to access SD cards using just their own devices.

Signed-off-by: Bernhard Beschow <shentey@gmail.com>
---
 hw/sd/sdhci.c         | 147 +++++++++++++++++++++++++++++++++++++++++-
 include/hw/sd/sdhci.h |   3 +
 2 files changed, 149 insertions(+), 1 deletion(-)

Comments

Bin Meng Sept. 16, 2022, 3:15 p.m. UTC | #1
On Thu, Sep 15, 2022 at 11:30 PM Bernhard Beschow <shentey@gmail.com> wrote:
>
> Will allow e500 boards to access SD cards using just their own devices.
>
> Signed-off-by: Bernhard Beschow <shentey@gmail.com>
> ---
>  hw/sd/sdhci.c         | 147 +++++++++++++++++++++++++++++++++++++++++-
>  include/hw/sd/sdhci.h |   3 +
>  2 files changed, 149 insertions(+), 1 deletion(-)
>
> diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
> index 7a5996caad..09285ccfa1 100644
> --- a/hw/sd/sdhci.c
> +++ b/hw/sd/sdhci.c
> @@ -1369,6 +1369,7 @@ void sdhci_initfn(SDHCIState *s)
>      s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
>
>      s->io_ops = &sdhci_mmio_ops;
> +    s->io_registers_map_size = SDHC_REGISTERS_MAP_SIZE;
>  }
>
>  void sdhci_uninitfn(SDHCIState *s)
> @@ -1392,7 +1393,7 @@ void sdhci_common_realize(SDHCIState *s, Error **errp)
>      s->fifo_buffer = g_malloc0(s->buf_maxsz);
>
>      memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
> -                          SDHC_REGISTERS_MAP_SIZE);
> +                          s->io_registers_map_size);
>  }
>
>  void sdhci_common_unrealize(SDHCIState *s)
> @@ -1575,6 +1576,149 @@ static const TypeInfo sdhci_bus_info = {
>      .class_init = sdhci_bus_class_init,
>  };
>
> +/* --- qdev Freescale eSDHC --- */
> +
> +/* Host Controller Capabilities Register 2 */
> +#define ESDHC_CAPABILITIES_1        0x114
> +
> +/* Control Register for DMA transfer */
> +#define ESDHC_DMA_SYSCTL            0x40c
> +#define ESDHC_PERIPHERAL_CLK_SEL    0x00080000
> +#define ESDHC_FLUSH_ASYNC_FIFO      0x00040000
> +#define ESDHC_DMA_SNOOP             0x00000040

It looks the above 3 bit fields are not used?

> +
> +#define ESDHC_REGISTERS_MAP_SIZE    0x410
> +
> +static uint64_t esdhci_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    uint64_t ret;
> +
> +    if (size != 4) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
> +                      " wrong size\n", size, offset);
> +        return 0;
> +    }
> +
> +    if (offset & 0x3) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
> +                      " unaligned\n", size, offset);
> +        return 0;
> +    }
> +
> +    switch (offset) {
> +    case SDHC_SYSAD:
> +    case SDHC_BLKSIZE:
> +    case SDHC_ARGUMENT:
> +    case SDHC_TRNMOD:
> +    case SDHC_RSPREG0:
> +    case SDHC_RSPREG1:
> +    case SDHC_RSPREG2:
> +    case SDHC_RSPREG3:
> +    case SDHC_BDATA:
> +    case SDHC_PRNSTS:
> +    case SDHC_HOSTCTL:
> +    case SDHC_CLKCON:
> +    case SDHC_NORINTSTS:
> +    case SDHC_NORINTSTSEN:
> +    case SDHC_NORINTSIGEN:
> +    case SDHC_ACMD12ERRSTS:
> +    case SDHC_CAPAB:
> +    case SDHC_SLOT_INT_STATUS:
> +        ret = sdhci_read(opaque, offset, size);
> +        break;
> +
> +    case ESDHC_DMA_SYSCTL:
> +    case 0x44:

Can we define a macro for this offset?

> +        ret = 0;
> +        qemu_log_mask(LOG_UNIMP, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
> +                      " not implemented\n", size, offset);
> +        break;
> +
> +    default:
> +        ret = 0;
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
> +                      " unknown offset\n", size, offset);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void esdhci_write(void *opaque, hwaddr offset, uint64_t val,
> +                         unsigned size)
> +{
> +    if (size != 4) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
> +                      " <- 0x%08lx wrong size\n", size, offset, val);
> +        return;
> +    }
> +
> +    if (offset & 0x3) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
> +                      " <- 0x%08lx unaligned\n", size, offset, val);
> +        return;
> +    }
> +
> +    switch (offset) {
> +    case SDHC_SYSAD:
> +    case SDHC_BLKSIZE:
> +    case SDHC_ARGUMENT:
> +    case SDHC_TRNMOD:
> +    case SDHC_BDATA:
> +    case SDHC_HOSTCTL:
> +    case SDHC_CLKCON:
> +    case SDHC_NORINTSTS:
> +    case SDHC_NORINTSTSEN:
> +    case SDHC_NORINTSIGEN:
> +    case SDHC_FEAER:
> +        sdhci_write(opaque, offset, val, size);
> +        break;
> +
> +    case ESDHC_DMA_SYSCTL:
> +    case 0x44:

ditto

> +        qemu_log_mask(LOG_UNIMP, "ESDHC wr_%ub @0x%02" HWADDR_PRIx " <- 0x%08lx "
> +                      "not implemented\n", size, offset, val);
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
> +                      " <- 0x%08lx unknown offset\n", size, offset, val);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps esdhc_mmio_ops = {
> +    .read = esdhci_read,
> +    .write = esdhci_write,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +        .unaligned = false
> +    },
> +    .endianness = DEVICE_BIG_ENDIAN,
> +};
> +
> +static void esdhci_init(Object *obj)
> +{
> +    DeviceState *dev = DEVICE(obj);
> +    SDHCIState *s = SYSBUS_SDHCI(obj);
> +
> +    s->io_ops = &esdhc_mmio_ops;
> +    s->io_registers_map_size = ESDHC_REGISTERS_MAP_SIZE;
> +
> +    /*
> +     * Compatible with:
> +     * - SD Host Controller Specification Version 2.0 Part A2
> +     */
> +    qdev_prop_set_uint8(dev, "sd-spec-version", 2);
> +}
> +
> +static const TypeInfo esdhc_info = {
> +    .name = TYPE_FSL_ESDHC,
> +    .parent = TYPE_SYSBUS_SDHCI,
> +    .instance_init = esdhci_init,
> +};
> +
>  /* --- qdev i.MX eSDHC --- */
>
>  #define USDHC_MIX_CTRL                  0x48
> @@ -1907,6 +2051,7 @@ static void sdhci_register_types(void)
>  {
>      type_register_static(&sdhci_sysbus_info);
>      type_register_static(&sdhci_bus_info);
> +    type_register_static(&esdhc_info);
>      type_register_static(&imx_usdhc_info);
>      type_register_static(&sdhci_s3c_info);
>  }
> diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
> index 01a64c5442..5b32e83eee 100644
> --- a/include/hw/sd/sdhci.h
> +++ b/include/hw/sd/sdhci.h
> @@ -45,6 +45,7 @@ struct SDHCIState {
>      AddressSpace *dma_as;
>      MemoryRegion *dma_mr;
>      const MemoryRegionOps *io_ops;
> +    uint64_t io_registers_map_size;
>
>      QEMUTimer *insert_timer;       /* timer for 'changing' sd card. */
>      QEMUTimer *transfer_timer;
> @@ -122,6 +123,8 @@ DECLARE_INSTANCE_CHECKER(SDHCIState, PCI_SDHCI,
>  DECLARE_INSTANCE_CHECKER(SDHCIState, SYSBUS_SDHCI,
>                           TYPE_SYSBUS_SDHCI)
>
> +#define TYPE_FSL_ESDHC "fsl-esdhc"
> +
>  #define TYPE_IMX_USDHC "imx-usdhc"
>
>  #define TYPE_S3C_SDHCI "s3c-sdhci"
> --

Regards,
Bin
Bernhard Beschow Sept. 16, 2022, 5:27 p.m. UTC | #2
Am 16. September 2022 15:15:03 UTC schrieb Bin Meng <bmeng.cn@gmail.com>:
>On Thu, Sep 15, 2022 at 11:30 PM Bernhard Beschow <shentey@gmail.com> wrote:
>>
>> Will allow e500 boards to access SD cards using just their own devices.
>>
>> Signed-off-by: Bernhard Beschow <shentey@gmail.com>
>> ---
>>  hw/sd/sdhci.c         | 147 +++++++++++++++++++++++++++++++++++++++++-
>>  include/hw/sd/sdhci.h |   3 +
>>  2 files changed, 149 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
>> index 7a5996caad..09285ccfa1 100644
>> --- a/hw/sd/sdhci.c
>> +++ b/hw/sd/sdhci.c
>> @@ -1369,6 +1369,7 @@ void sdhci_initfn(SDHCIState *s)
>>      s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
>>
>>      s->io_ops = &sdhci_mmio_ops;
>> +    s->io_registers_map_size = SDHC_REGISTERS_MAP_SIZE;
>>  }
>>
>>  void sdhci_uninitfn(SDHCIState *s)
>> @@ -1392,7 +1393,7 @@ void sdhci_common_realize(SDHCIState *s, Error **errp)
>>      s->fifo_buffer = g_malloc0(s->buf_maxsz);
>>
>>      memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
>> -                          SDHC_REGISTERS_MAP_SIZE);
>> +                          s->io_registers_map_size);
>>  }
>>
>>  void sdhci_common_unrealize(SDHCIState *s)
>> @@ -1575,6 +1576,149 @@ static const TypeInfo sdhci_bus_info = {
>>      .class_init = sdhci_bus_class_init,
>>  };
>>
>> +/* --- qdev Freescale eSDHC --- */
>> +
>> +/* Host Controller Capabilities Register 2 */
>> +#define ESDHC_CAPABILITIES_1        0x114
>> +
>> +/* Control Register for DMA transfer */
>> +#define ESDHC_DMA_SYSCTL            0x40c
>> +#define ESDHC_PERIPHERAL_CLK_SEL    0x00080000
>> +#define ESDHC_FLUSH_ASYNC_FIFO      0x00040000
>> +#define ESDHC_DMA_SNOOP             0x00000040
>
>It looks the above 3 bit fields are not used?

Yes, possibly. I'll check for more unused stuff.

>> +
>> +#define ESDHC_REGISTERS_MAP_SIZE    0x410
>> +
>> +static uint64_t esdhci_read(void *opaque, hwaddr offset, unsigned size)
>> +{
>> +    uint64_t ret;
>> +
>> +    if (size != 4) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
>> +                      " wrong size\n", size, offset);
>> +        return 0;
>> +    }
>> +
>> +    if (offset & 0x3) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
>> +                      " unaligned\n", size, offset);
>> +        return 0;
>> +    }
>> +
>> +    switch (offset) {
>> +    case SDHC_SYSAD:
>> +    case SDHC_BLKSIZE:
>> +    case SDHC_ARGUMENT:
>> +    case SDHC_TRNMOD:
>> +    case SDHC_RSPREG0:
>> +    case SDHC_RSPREG1:
>> +    case SDHC_RSPREG2:
>> +    case SDHC_RSPREG3:
>> +    case SDHC_BDATA:
>> +    case SDHC_PRNSTS:
>> +    case SDHC_HOSTCTL:
>> +    case SDHC_CLKCON:
>> +    case SDHC_NORINTSTS:
>> +    case SDHC_NORINTSTSEN:
>> +    case SDHC_NORINTSIGEN:
>> +    case SDHC_ACMD12ERRSTS:
>> +    case SDHC_CAPAB:
>> +    case SDHC_SLOT_INT_STATUS:
>> +        ret = sdhci_read(opaque, offset, size);
>> +        break;
>> +
>> +    case ESDHC_DMA_SYSCTL:
>> +    case 0x44:
>
>Can we define a macro for this offset?

Sure. Not sure why I didn't.

>> +        ret = 0;
>> +        qemu_log_mask(LOG_UNIMP, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
>> +                      " not implemented\n", size, offset);
>> +        break;
>> +
>> +    default:
>> +        ret = 0;
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
>> +                      " unknown offset\n", size, offset);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void esdhci_write(void *opaque, hwaddr offset, uint64_t val,
>> +                         unsigned size)
>> +{
>> +    if (size != 4) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
>> +                      " <- 0x%08lx wrong size\n", size, offset, val);
>> +        return;
>> +    }
>> +
>> +    if (offset & 0x3) {
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
>> +                      " <- 0x%08lx unaligned\n", size, offset, val);
>> +        return;
>> +    }
>> +
>> +    switch (offset) {
>> +    case SDHC_SYSAD:
>> +    case SDHC_BLKSIZE:
>> +    case SDHC_ARGUMENT:
>> +    case SDHC_TRNMOD:
>> +    case SDHC_BDATA:
>> +    case SDHC_HOSTCTL:
>> +    case SDHC_CLKCON:
>> +    case SDHC_NORINTSTS:
>> +    case SDHC_NORINTSTSEN:
>> +    case SDHC_NORINTSIGEN:
>> +    case SDHC_FEAER:
>> +        sdhci_write(opaque, offset, val, size);
>> +        break;
>> +
>> +    case ESDHC_DMA_SYSCTL:
>> +    case 0x44:
>
>ditto

Ack.

Best regards,
Bernhard
>
>> +        qemu_log_mask(LOG_UNIMP, "ESDHC wr_%ub @0x%02" HWADDR_PRIx " <- 0x%08lx "
>> +                      "not implemented\n", size, offset, val);
>> +        break;
>> +
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
>> +                      " <- 0x%08lx unknown offset\n", size, offset, val);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps esdhc_mmio_ops = {
>> +    .read = esdhci_read,
>> +    .write = esdhci_write,
>> +    .valid = {
>> +        .min_access_size = 1,
>> +        .max_access_size = 4,
>> +        .unaligned = false
>> +    },
>> +    .endianness = DEVICE_BIG_ENDIAN,
>> +};
>> +
>> +static void esdhci_init(Object *obj)
>> +{
>> +    DeviceState *dev = DEVICE(obj);
>> +    SDHCIState *s = SYSBUS_SDHCI(obj);
>> +
>> +    s->io_ops = &esdhc_mmio_ops;
>> +    s->io_registers_map_size = ESDHC_REGISTERS_MAP_SIZE;
>> +
>> +    /*
>> +     * Compatible with:
>> +     * - SD Host Controller Specification Version 2.0 Part A2
>> +     */
>> +    qdev_prop_set_uint8(dev, "sd-spec-version", 2);
>> +}
>> +
>> +static const TypeInfo esdhc_info = {
>> +    .name = TYPE_FSL_ESDHC,
>> +    .parent = TYPE_SYSBUS_SDHCI,
>> +    .instance_init = esdhci_init,
>> +};
>> +
>>  /* --- qdev i.MX eSDHC --- */
>>
>>  #define USDHC_MIX_CTRL                  0x48
>> @@ -1907,6 +2051,7 @@ static void sdhci_register_types(void)
>>  {
>>      type_register_static(&sdhci_sysbus_info);
>>      type_register_static(&sdhci_bus_info);
>> +    type_register_static(&esdhc_info);
>>      type_register_static(&imx_usdhc_info);
>>      type_register_static(&sdhci_s3c_info);
>>  }
>> diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
>> index 01a64c5442..5b32e83eee 100644
>> --- a/include/hw/sd/sdhci.h
>> +++ b/include/hw/sd/sdhci.h
>> @@ -45,6 +45,7 @@ struct SDHCIState {
>>      AddressSpace *dma_as;
>>      MemoryRegion *dma_mr;
>>      const MemoryRegionOps *io_ops;
>> +    uint64_t io_registers_map_size;
>>
>>      QEMUTimer *insert_timer;       /* timer for 'changing' sd card. */
>>      QEMUTimer *transfer_timer;
>> @@ -122,6 +123,8 @@ DECLARE_INSTANCE_CHECKER(SDHCIState, PCI_SDHCI,
>>  DECLARE_INSTANCE_CHECKER(SDHCIState, SYSBUS_SDHCI,
>>                           TYPE_SYSBUS_SDHCI)
>>
>> +#define TYPE_FSL_ESDHC "fsl-esdhc"
>> +
>>  #define TYPE_IMX_USDHC "imx-usdhc"
>>
>>  #define TYPE_S3C_SDHCI "s3c-sdhci"
>> --
>
>Regards,
>Bin
diff mbox series

Patch

diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 7a5996caad..09285ccfa1 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -1369,6 +1369,7 @@  void sdhci_initfn(SDHCIState *s)
     s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
 
     s->io_ops = &sdhci_mmio_ops;
+    s->io_registers_map_size = SDHC_REGISTERS_MAP_SIZE;
 }
 
 void sdhci_uninitfn(SDHCIState *s)
@@ -1392,7 +1393,7 @@  void sdhci_common_realize(SDHCIState *s, Error **errp)
     s->fifo_buffer = g_malloc0(s->buf_maxsz);
 
     memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
-                          SDHC_REGISTERS_MAP_SIZE);
+                          s->io_registers_map_size);
 }
 
 void sdhci_common_unrealize(SDHCIState *s)
@@ -1575,6 +1576,149 @@  static const TypeInfo sdhci_bus_info = {
     .class_init = sdhci_bus_class_init,
 };
 
+/* --- qdev Freescale eSDHC --- */
+
+/* Host Controller Capabilities Register 2 */
+#define ESDHC_CAPABILITIES_1        0x114
+
+/* Control Register for DMA transfer */
+#define ESDHC_DMA_SYSCTL            0x40c
+#define ESDHC_PERIPHERAL_CLK_SEL    0x00080000
+#define ESDHC_FLUSH_ASYNC_FIFO      0x00040000
+#define ESDHC_DMA_SNOOP             0x00000040
+
+#define ESDHC_REGISTERS_MAP_SIZE    0x410
+
+static uint64_t esdhci_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint64_t ret;
+
+    if (size != 4) {
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
+                      " wrong size\n", size, offset);
+        return 0;
+    }
+
+    if (offset & 0x3) {
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
+                      " unaligned\n", size, offset);
+        return 0;
+    }
+
+    switch (offset) {
+    case SDHC_SYSAD:
+    case SDHC_BLKSIZE:
+    case SDHC_ARGUMENT:
+    case SDHC_TRNMOD:
+    case SDHC_RSPREG0:
+    case SDHC_RSPREG1:
+    case SDHC_RSPREG2:
+    case SDHC_RSPREG3:
+    case SDHC_BDATA:
+    case SDHC_PRNSTS:
+    case SDHC_HOSTCTL:
+    case SDHC_CLKCON:
+    case SDHC_NORINTSTS:
+    case SDHC_NORINTSTSEN:
+    case SDHC_NORINTSIGEN:
+    case SDHC_ACMD12ERRSTS:
+    case SDHC_CAPAB:
+    case SDHC_SLOT_INT_STATUS:
+        ret = sdhci_read(opaque, offset, size);
+        break;
+
+    case ESDHC_DMA_SYSCTL:
+    case 0x44:
+        ret = 0;
+        qemu_log_mask(LOG_UNIMP, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
+                      " not implemented\n", size, offset);
+        break;
+
+    default:
+        ret = 0;
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC rd_%ub @0x%02" HWADDR_PRIx
+                      " unknown offset\n", size, offset);
+        break;
+    }
+
+    return ret;
+}
+
+static void esdhci_write(void *opaque, hwaddr offset, uint64_t val,
+                         unsigned size)
+{
+    if (size != 4) {
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
+                      " <- 0x%08lx wrong size\n", size, offset, val);
+        return;
+    }
+
+    if (offset & 0x3) {
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
+                      " <- 0x%08lx unaligned\n", size, offset, val);
+        return;
+    }
+
+    switch (offset) {
+    case SDHC_SYSAD:
+    case SDHC_BLKSIZE:
+    case SDHC_ARGUMENT:
+    case SDHC_TRNMOD:
+    case SDHC_BDATA:
+    case SDHC_HOSTCTL:
+    case SDHC_CLKCON:
+    case SDHC_NORINTSTS:
+    case SDHC_NORINTSTSEN:
+    case SDHC_NORINTSIGEN:
+    case SDHC_FEAER:
+        sdhci_write(opaque, offset, val, size);
+        break;
+
+    case ESDHC_DMA_SYSCTL:
+    case 0x44:
+        qemu_log_mask(LOG_UNIMP, "ESDHC wr_%ub @0x%02" HWADDR_PRIx " <- 0x%08lx "
+                      "not implemented\n", size, offset, val);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "ESDHC wr_%ub @0x%02" HWADDR_PRIx
+                      " <- 0x%08lx unknown offset\n", size, offset, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps esdhc_mmio_ops = {
+    .read = esdhci_read,
+    .write = esdhci_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+        .unaligned = false
+    },
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void esdhci_init(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    SDHCIState *s = SYSBUS_SDHCI(obj);
+
+    s->io_ops = &esdhc_mmio_ops;
+    s->io_registers_map_size = ESDHC_REGISTERS_MAP_SIZE;
+
+    /*
+     * Compatible with:
+     * - SD Host Controller Specification Version 2.0 Part A2
+     */
+    qdev_prop_set_uint8(dev, "sd-spec-version", 2);
+}
+
+static const TypeInfo esdhc_info = {
+    .name = TYPE_FSL_ESDHC,
+    .parent = TYPE_SYSBUS_SDHCI,
+    .instance_init = esdhci_init,
+};
+
 /* --- qdev i.MX eSDHC --- */
 
 #define USDHC_MIX_CTRL                  0x48
@@ -1907,6 +2051,7 @@  static void sdhci_register_types(void)
 {
     type_register_static(&sdhci_sysbus_info);
     type_register_static(&sdhci_bus_info);
+    type_register_static(&esdhc_info);
     type_register_static(&imx_usdhc_info);
     type_register_static(&sdhci_s3c_info);
 }
diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h
index 01a64c5442..5b32e83eee 100644
--- a/include/hw/sd/sdhci.h
+++ b/include/hw/sd/sdhci.h
@@ -45,6 +45,7 @@  struct SDHCIState {
     AddressSpace *dma_as;
     MemoryRegion *dma_mr;
     const MemoryRegionOps *io_ops;
+    uint64_t io_registers_map_size;
 
     QEMUTimer *insert_timer;       /* timer for 'changing' sd card. */
     QEMUTimer *transfer_timer;
@@ -122,6 +123,8 @@  DECLARE_INSTANCE_CHECKER(SDHCIState, PCI_SDHCI,
 DECLARE_INSTANCE_CHECKER(SDHCIState, SYSBUS_SDHCI,
                          TYPE_SYSBUS_SDHCI)
 
+#define TYPE_FSL_ESDHC "fsl-esdhc"
+
 #define TYPE_IMX_USDHC "imx-usdhc"
 
 #define TYPE_S3C_SDHCI "s3c-sdhci"