diff mbox

[RFC,3/4] ahci: Add allwinner AHCI

Message ID eda3f569d6f605bc985a88fc3d9be7fca380a780.1444448892.git.crosthwaite.peter@gmail.com
State New
Headers show

Commit Message

Peter Crosthwaite Oct. 11, 2015, 4:21 p.m. UTC
Add a Sysbus AHCI subclass for the Allwinner AHCI. It has a few extra
vendor specific registers that are used for phy and power init.

Signed-off-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
---
 hw/ide/ahci.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ide/ahci.h | 16 ++++++++++
 2 files changed, 114 insertions(+)

Comments

John Snow Oct. 12, 2015, 11:09 p.m. UTC | #1
Is there any spec or documentation I can cross-reference this against?

I gather this exists within the vendor-specific reserved region from
0xA0 to 0xFF just prior to the port registers, so this all /looks/ like
it's right, I just don't have any way to verify it.

On 10/11/2015 12:21 PM, Peter Crosthwaite wrote:
> Add a Sysbus AHCI subclass for the Allwinner AHCI. It has a few extra
> vendor specific registers that are used for phy and power init.
> 
> Signed-off-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
> ---
>  hw/ide/ahci.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ide/ahci.h | 16 ++++++++++
>  2 files changed, 114 insertions(+)
> 
> diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
> index eff01b2..a7fa147 100644
> --- a/hw/ide/ahci.c
> +++ b/hw/ide/ahci.c
> @@ -1692,9 +1692,107 @@ static const TypeInfo sysbus_ahci_info = {
>      .class_init    = sysbus_ahci_class_init,
>  };
>  
> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80
> +
> +#define ALLWINNER_AHCI_BISTAFR    ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_BISTCR     ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_BISTFCTR   ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_BISTSR     ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_BISTDECR   ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_DIAGNR0    ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_DIAGNR1    ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_OOBR       ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_PHYCS0R    ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_PHYCS1R    ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_PHYCS2R    ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_TIMER1MS   ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_GPARAM1R   ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_GPARAM2R   ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_PPARAMR    ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_TESTR      ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_VERSIONR   ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_IDR        ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +#define ALLWINNER_AHCI_RWCR       ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
> +
> +static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
> +                                        unsigned size)
> +{
> +    AllwinnerAHCIState *a = opaque;
> +    uint64_t val = a->regs[addr/4];
> +
> +    switch (addr / 4) {
> +    case ALLWINNER_AHCI_PHYCS0R:
> +        val |= 0x2 << 28;
> +        break;
> +    case ALLWINNER_AHCI_PHYCS2R:
> +        val &= ~(0x1 << 24);
> +        break;
> +    }
> +    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
> +            addr, val, size);
> +    return  val;
> +}
> +
> +static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
> +                                     uint64_t val, unsigned size)
> +{
> +    AllwinnerAHCIState *a = opaque;
> +
> +    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
> +            addr, val, size);
> +    a->regs[addr/4] = val;
> +}
> +
> +static const MemoryRegionOps allwinner_ahci_mem_ops = {
> +    .read = allwinner_ahci_mem_read,
> +    .write = allwinner_ahci_mem_write,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 4,

Are you sure devices won't try to read individual bytes for error codes
out of these vendor registers?

> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void allwinner_ahci_init(Object *obj)
> +{
> +    SysbusAHCIState *s = SYSBUS_AHCI(obj);
> +    AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
> +
> +    memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
> +                          "allwinner_ahci", ALLWINNER_AHCI_MMIO_SIZE);
> +    memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
> +                                &a->mmio);
> +}
> +
> +static const VMStateDescription vmstate_allwinner_ahci = {
> +    .name = "a10.pic",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
> +                             ALLWINNER_AHCI_MMIO_SIZE/4),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &vmstate_allwinner_ahci;
> +}
> +
> +static const TypeInfo allwinner_ahci_info = {
> +    .name          = TYPE_ALLWINNER_AHCI,
> +    .parent        = TYPE_SYSBUS_AHCI,
> +    .instance_size = sizeof(AllwinnerAHCIState),
> +    .instance_init = allwinner_ahci_init,
> +    .class_init    = allwinner_ahci_class_init,
> +};
> +
>  static void sysbus_ahci_register_types(void)
>  {
>      type_register_static(&sysbus_ahci_info);
> +    type_register_static(&allwinner_ahci_info);
>  }
>  
>  type_init(sysbus_ahci_register_types)
> diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
> index 4ccaf5d..8973249 100644
> --- a/hw/ide/ahci.h
> +++ b/hw/ide/ahci.h
> @@ -386,4 +386,20 @@ typedef struct SysbusAHCIState {
>      uint32_t num_ports;
>  } SysbusAHCIState;
>  
> +#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
> +#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
> +                       TYPE_ALLWINNER_AHCI)
> +
> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80
> +
> +typedef struct AllwinnerAHCIState {
> +    /*< private >*/
> +    SysbusAHCIState parent_obj;
> +    /*< public >*/
> +
> +    MemoryRegion mmio;
> +    uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
> +} AllwinnerAHCIState;
> +
>  #endif /* HW_IDE_AHCI_H */
>
Peter Crosthwaite Oct. 13, 2015, 4:58 a.m. UTC | #2
On Mon, Oct 12, 2015 at 4:09 PM, John Snow <jsnow@redhat.com> wrote:
> Is there any spec or documentation I can cross-reference this against?
>

Not that I know of. I am running off a combination of experiments
(looking at messages from P1) and the Linux driver source.

> I gather this exists within the vendor-specific reserved region from
> 0xA0 to 0xFF just prior to the port registers, so this all /looks/ like
> it's right, I just don't have any way to verify it.
>
> On 10/11/2015 12:21 PM, Peter Crosthwaite wrote:
>> Add a Sysbus AHCI subclass for the Allwinner AHCI. It has a few extra
>> vendor specific registers that are used for phy and power init.
>>
>> Signed-off-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
>> ---
>>  hw/ide/ahci.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/ide/ahci.h | 16 ++++++++++
>>  2 files changed, 114 insertions(+)
>>
>> diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
>> index eff01b2..a7fa147 100644
>> --- a/hw/ide/ahci.c
>> +++ b/hw/ide/ahci.c
>> @@ -1692,9 +1692,107 @@ static const TypeInfo sysbus_ahci_info = {
>>      .class_init    = sysbus_ahci_class_init,
>>  };
>>
>> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
>> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80
>> +
>> +#define ALLWINNER_AHCI_BISTAFR    ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_BISTCR     ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_BISTFCTR   ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_BISTSR     ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_BISTDECR   ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_DIAGNR0    ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_DIAGNR1    ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_OOBR       ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_PHYCS0R    ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_PHYCS1R    ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_PHYCS2R    ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_TIMER1MS   ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_GPARAM1R   ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_GPARAM2R   ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_PPARAMR    ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_TESTR      ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_VERSIONR   ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_IDR        ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +#define ALLWINNER_AHCI_RWCR       ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
>> +
>> +static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
>> +                                        unsigned size)
>> +{
>> +    AllwinnerAHCIState *a = opaque;
>> +    uint64_t val = a->regs[addr/4];
>> +
>> +    switch (addr / 4) {
>> +    case ALLWINNER_AHCI_PHYCS0R:
>> +        val |= 0x2 << 28;
>> +        break;
>> +    case ALLWINNER_AHCI_PHYCS2R:
>> +        val &= ~(0x1 << 24);
>> +        break;
>> +    }
>> +    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
>> +            addr, val, size);
>> +    return  val;
>> +}
>> +
>> +static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
>> +                                     uint64_t val, unsigned size)
>> +{
>> +    AllwinnerAHCIState *a = opaque;
>> +
>> +    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
>> +            addr, val, size);
>> +    a->regs[addr/4] = val;
>> +}
>> +
>> +static const MemoryRegionOps allwinner_ahci_mem_ops = {
>> +    .read = allwinner_ahci_mem_read,
>> +    .write = allwinner_ahci_mem_write,
>> +    .valid.min_access_size = 4,
>> +    .valid.max_access_size = 4,
>
> Are you sure devices won't try to read individual bytes for error codes
> out of these vendor registers?
>

No idea.

Regards,
Peter

>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +};
>> +
>> +static void allwinner_ahci_init(Object *obj)
>> +{
>> +    SysbusAHCIState *s = SYSBUS_AHCI(obj);
>> +    AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
>> +
>> +    memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
>> +                          "allwinner_ahci", ALLWINNER_AHCI_MMIO_SIZE);
>> +    memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
>> +                                &a->mmio);
>> +}
>> +
>> +static const VMStateDescription vmstate_allwinner_ahci = {
>> +    .name = "a10.pic",
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
>> +                             ALLWINNER_AHCI_MMIO_SIZE/4),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->vmsd = &vmstate_allwinner_ahci;
>> +}
>> +
>> +static const TypeInfo allwinner_ahci_info = {
>> +    .name          = TYPE_ALLWINNER_AHCI,
>> +    .parent        = TYPE_SYSBUS_AHCI,
>> +    .instance_size = sizeof(AllwinnerAHCIState),
>> +    .instance_init = allwinner_ahci_init,
>> +    .class_init    = allwinner_ahci_class_init,
>> +};
>> +
>>  static void sysbus_ahci_register_types(void)
>>  {
>>      type_register_static(&sysbus_ahci_info);
>> +    type_register_static(&allwinner_ahci_info);
>>  }
>>
>>  type_init(sysbus_ahci_register_types)
>> diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
>> index 4ccaf5d..8973249 100644
>> --- a/hw/ide/ahci.h
>> +++ b/hw/ide/ahci.h
>> @@ -386,4 +386,20 @@ typedef struct SysbusAHCIState {
>>      uint32_t num_ports;
>>  } SysbusAHCIState;
>>
>> +#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
>> +#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
>> +                       TYPE_ALLWINNER_AHCI)
>> +
>> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
>> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80
>> +
>> +typedef struct AllwinnerAHCIState {
>> +    /*< private >*/
>> +    SysbusAHCIState parent_obj;
>> +    /*< public >*/
>> +
>> +    MemoryRegion mmio;
>> +    uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
>> +} AllwinnerAHCIState;
>> +
>>  #endif /* HW_IDE_AHCI_H */
>>
Beniamino Galvani Oct. 13, 2015, 6:28 p.m. UTC | #3
On Sun, Oct 11, 2015 at 09:21:35AM -0700, Peter Crosthwaite wrote:
> --- a/hw/ide/ahci.c
> +++ b/hw/ide/ahci.c
> @@ -1692,9 +1692,107 @@ static const TypeInfo sysbus_ahci_info = {
>      .class_init    = sysbus_ahci_class_init,
>  };
>  
> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80

These are already defined in the header file.

> +static const VMStateDescription vmstate_allwinner_ahci = {
> +    .name = "a10.pic",

.name = "allwinner-ahci" ?

Beniamino
Peter Crosthwaite Oct. 26, 2015, 3:48 p.m. UTC | #4
On Tue, Oct 13, 2015 at 11:28 AM, Beniamino Galvani <b.galvani@gmail.com> wrote:
> On Sun, Oct 11, 2015 at 09:21:35AM -0700, Peter Crosthwaite wrote:
>> --- a/hw/ide/ahci.c
>> +++ b/hw/ide/ahci.c
>> @@ -1692,9 +1692,107 @@ static const TypeInfo sysbus_ahci_info = {
>>      .class_init    = sysbus_ahci_class_init,
>>  };
>>
>> +#define ALLWINNER_AHCI_MMIO_OFF  0x80
>> +#define ALLWINNER_AHCI_MMIO_SIZE 0x80
>
> These are already defined in the header file.
>

Dropped.

>> +static const VMStateDescription vmstate_allwinner_ahci = {
>> +    .name = "a10.pic",
>
> .name = "allwinner-ahci" ?
>

Fixed. I also tweaked the name of the memory region to use - instead
of _ to match this.

Thanks.

Regards,
Peter

> Beniamino
diff mbox

Patch

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index eff01b2..a7fa147 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1692,9 +1692,107 @@  static const TypeInfo sysbus_ahci_info = {
     .class_init    = sysbus_ahci_class_init,
 };
 
+#define ALLWINNER_AHCI_MMIO_OFF  0x80
+#define ALLWINNER_AHCI_MMIO_SIZE 0x80
+
+#define ALLWINNER_AHCI_BISTAFR    ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTCR     ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTFCTR   ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTSR     ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTDECR   ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR0    ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR1    ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_OOBR       ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS0R    ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS1R    ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS2R    ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TIMER1MS   ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM1R   ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM2R   ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PPARAMR    ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TESTR      ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_VERSIONR   ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_IDR        ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_RWCR       ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+
+static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
+                                        unsigned size)
+{
+    AllwinnerAHCIState *a = opaque;
+    uint64_t val = a->regs[addr/4];
+
+    switch (addr / 4) {
+    case ALLWINNER_AHCI_PHYCS0R:
+        val |= 0x2 << 28;
+        break;
+    case ALLWINNER_AHCI_PHYCS2R:
+        val &= ~(0x1 << 24);
+        break;
+    }
+    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+            addr, val, size);
+    return  val;
+}
+
+static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
+                                     uint64_t val, unsigned size)
+{
+    AllwinnerAHCIState *a = opaque;
+
+    DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+            addr, val, size);
+    a->regs[addr/4] = val;
+}
+
+static const MemoryRegionOps allwinner_ahci_mem_ops = {
+    .read = allwinner_ahci_mem_read,
+    .write = allwinner_ahci_mem_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void allwinner_ahci_init(Object *obj)
+{
+    SysbusAHCIState *s = SYSBUS_AHCI(obj);
+    AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
+
+    memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
+                          "allwinner_ahci", ALLWINNER_AHCI_MMIO_SIZE);
+    memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
+                                &a->mmio);
+}
+
+static const VMStateDescription vmstate_allwinner_ahci = {
+    .name = "a10.pic",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
+                             ALLWINNER_AHCI_MMIO_SIZE/4),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_allwinner_ahci;
+}
+
+static const TypeInfo allwinner_ahci_info = {
+    .name          = TYPE_ALLWINNER_AHCI,
+    .parent        = TYPE_SYSBUS_AHCI,
+    .instance_size = sizeof(AllwinnerAHCIState),
+    .instance_init = allwinner_ahci_init,
+    .class_init    = allwinner_ahci_class_init,
+};
+
 static void sysbus_ahci_register_types(void)
 {
     type_register_static(&sysbus_ahci_info);
+    type_register_static(&allwinner_ahci_info);
 }
 
 type_init(sysbus_ahci_register_types)
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index 4ccaf5d..8973249 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -386,4 +386,20 @@  typedef struct SysbusAHCIState {
     uint32_t num_ports;
 } SysbusAHCIState;
 
+#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
+#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
+                       TYPE_ALLWINNER_AHCI)
+
+#define ALLWINNER_AHCI_MMIO_OFF  0x80
+#define ALLWINNER_AHCI_MMIO_SIZE 0x80
+
+typedef struct AllwinnerAHCIState {
+    /*< private >*/
+    SysbusAHCIState parent_obj;
+    /*< public >*/
+
+    MemoryRegion mmio;
+    uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
+} AllwinnerAHCIState;
+
 #endif /* HW_IDE_AHCI_H */