diff mbox

[2/2] usb-ehci: add Faraday FUSBH200 support

Message ID 1359438234-5337-3-git-send-email-dantesu@gmail.com
State New
Headers show

Commit Message

Kuo-Jung Su Jan. 29, 2013, 5:43 a.m. UTC
From: Kuo-Jung Su <dantesu@faraday-tech.com>

Add Faraday FUSBH200 support, which is slightly different from EHCI spec.
(Or maybe simply a bad/wrong implementation...)

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Andreas <afaerber@suse.de>
Cc: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---
 hw/usb/hcd-ehci-sysbus.c |   66 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb/hcd-ehci.h        |    5 ++++
 2 files changed, 71 insertions(+)

Comments

Andreas Färber Jan. 29, 2013, 9:50 a.m. UTC | #1
Am 29.01.2013 06:43, schrieb Kuo-Jung Su:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
> 
> Add Faraday FUSBH200 support, which is slightly different from EHCI spec.
> (Or maybe simply a bad/wrong implementation...)
> 
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Andreas <afaerber@suse.de>
> Cc: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
> ---
>  hw/usb/hcd-ehci-sysbus.c |   66 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/usb/hcd-ehci.h        |    5 ++++
>  2 files changed, 71 insertions(+)
> 
> diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
> index ae2db1a..404a227 100644
> --- a/hw/usb/hcd-ehci-sysbus.c
> +++ b/hw/usb/hcd-ehci-sysbus.c
> @@ -17,6 +17,49 @@
>  
>  #include "hw/usb/hcd-ehci.h"
>  
> +/*
> + * Faraday FUSBH200 USB 2.0 EHCI
> + */
> +
> +static uint64_t
> +ehci_fusbh200_read(void *ptr, hwaddr addr, unsigned size)
> +{
> +    hwaddr off = 0x34 + addr;
> +
> +    switch (off) {
> +    case 0x34:  /* fusbh200: EOF/Async. Sleep Timer Register */
> +        return 0x00000041;
> +    case 0x40:  /* fusbh200: Bus Monitor Control/Status Register */
> +        /* High-Speed, VBUS valid, interrupt level-high active */
> +        return (2 << 9) | (1 << 8) | (1 << 3);
> +    }
> +
> +    return 0;
> +}
> +
> +static void
> +ehci_fusbh200_write(void *ptr, hwaddr addr, uint64_t val, unsigned size)
> +{
> +}
> +
> +static const MemoryRegionOps ehci_mmio_fusbh200_ops = {
> +    .read = ehci_fusbh200_read,
> +    .write = ehci_fusbh200_write,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 4,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};

This part looks okay now, except that I'd suggest to name the ops
ehci_fusbh200_mmio_ops (ehci_fusbh200_ being a consistent prefix).

> +
> +static void
> +usb_ehci_fusbh200_initfn(EHCIState *s, DeviceState *dev)

Bad naming, the inconsistent use of "initfn" is what my patch tried to
clean up for you.
And this signature is only necessary in common EHCI code because it
needs to access EHCIState from both PCIDevice and SysBusDevice. This
change is in SysBus-only code though, so you can access EHCIState
through the device state.

> +{
> +    memory_region_init_io(&s->mem_vendor, &ehci_mmio_fusbh200_ops, s,
> +                          "fusbh200", 0x4c);

This has no dependencies on other fields, so it should go into a
void ehci_fusbh200_initfn(Object *obj) hooked to your .instance_init.

> +    memory_region_add_subregion(&s->mem,
> +                                s->opregbase + s->portscbase + 4 * s->portnr,
> +                                &s->mem_vendor);
> +}
> +
>  static const VMStateDescription vmstate_ehci_sysbus = {
>      .name        = "ehci-sysbus",
>      .version_id  = 2,
> @@ -46,6 +89,9 @@ static void usb_ehci_sysbus_realizefn(DeviceState *dev, Error **errp)
>      s->dma = &dma_context_memory;
>  
>      usb_ehci_initfn(s, dev);

FWIW I notice that I should've renamed this in my patch, too (outside
the scope of your patch).

> +    if (sec->vendor_init) {
> +        sec->vendor_init(s, DEVICE(dev));
> +    }
>      sysbus_init_irq(d, &s->irq);
>      sysbus_init_mmio(d, &s->mem);
>  }

This is not what I had in mind. For one thing your initialization does
not need to go before sysbus_init_{irq,mmio}.

What I am not too sure about without digging out SysBus and PCI EHCI
sources is at which point the fields may get modified. In 1/2 your new
fields are only ever initialized in class_init and then transferred to
instance state in the realizefn.

There's two better options:

1) Change 1/2 to set the default values s->... in an instance_init
initfn directly, then your instance_init can execute "on top" (with QOM
infrastructure taking care of calling the parent's initfn before yours)
and overwrite its values.
You won't need sec->portscbase and sec->portnr then and we avoid
duplicating "sec->portscbase = 0x44; sec->portnr = NB_PORTS;".

2) Keep 1/2 as is and override the realizefn in 2/2 via dc->realize =
your_realizefn. This involves saving the parent's realizefn in a new
FUSHBH200SysBusEHCIClass::parent_realize and explicitly invoking that
parent_realize function before adding your MemoryRegion as subregion.

The big underlying question is whether other future devices may want to
override the values via qdev static properties. Then the assignment
needs to remain in realizefn, otherwise initfn makes our life easier.

> @@ -76,6 +122,7 @@ static void ehci_xlnx_class_init(ObjectClass *oc, void *data)
>      sec->opregbase = 0x140;
>      sec->portscbase = 0x44;
>      sec->portnr = NB_PORTS;
> +    sec->vendor_init = NULL;
>  }
>  
>  static const TypeInfo ehci_xlnx_type_info = {
> @@ -92,6 +139,7 @@ static void ehci_exynos4210_class_init(ObjectClass *oc, void *data)
>      sec->opregbase = 0x10;
>      sec->portscbase = 0x44;
>      sec->portnr = NB_PORTS;
> +    sec->vendor_init = NULL;
>  }
>  
>  static const TypeInfo ehci_exynos4210_type_info = {

You don't need to zero-initialize fields in class_init or initfn.

> @@ -100,11 +148,29 @@ static const TypeInfo ehci_exynos4210_type_info = {
>      .class_init    = ehci_exynos4210_class_init,
>  };
>  
> +static void ehci_fusbh200_class_init(ObjectClass *oc, void *data)
> +{
> +    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
> +
> +    sec->capsbase = 0x0;
> +    sec->opregbase = 0x10;
> +    sec->portscbase = 0x20;
> +    sec->portnr = 1;
> +    sec->vendor_init = usb_ehci_fusbh200_initfn;
> +}
> +
> +static const TypeInfo ehci_fusbh200_type_info = {
> +    .name          = TYPE_FUSBH200_EHCI,
> +    .parent        = TYPE_SYS_BUS_EHCI,
> +    .class_init    = ehci_fusbh200_class_init,
> +};
> +
>  static void ehci_sysbus_register_types(void)
>  {
>      type_register_static(&ehci_type_info);
>      type_register_static(&ehci_xlnx_type_info);
>      type_register_static(&ehci_exynos4210_type_info);
> +    type_register_static(&ehci_fusbh200_type_info);
>  }
>  
>  type_init(ehci_sysbus_register_types)
> diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
> index e587b67..3ca9c8f 100644
> --- a/hw/usb/hcd-ehci.h
> +++ b/hw/usb/hcd-ehci.h
> @@ -261,6 +261,7 @@ struct EHCIState {
>      MemoryRegion mem_caps;
>      MemoryRegion mem_opreg;
>      MemoryRegion mem_ports;
> +    MemoryRegion mem_vendor;
>      int companion_count;
>      uint16_t capsbase;
>      uint16_t opregbase;

Similar question here: Do we expect other models to expose a single
vendor MemoryRegion as well?

I would prefer to place this a) in the SysBus-specific state or if only
for this device b) in a new FUSBH200-specific state derived from it.

> @@ -336,6 +337,7 @@ typedef struct EHCIPCIState {
>  
>  #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
>  #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
> +#define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
>  
>  #define SYS_BUS_EHCI(obj) \
>      OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI)
> @@ -361,6 +363,9 @@ typedef struct SysBusEHCIClass {
>      uint16_t opregbase;
>      uint16_t portscbase;
>      uint16_t portnr;
> +
> +    /* vendor specific init function */
> +    void (*vendor_init)(EHCIState *s, DeviceState *dev);
>  } SysBusEHCIClass;
>  
>  #endif

Gerd, what are your thoughts? If Kuo-Jung doesn't mind, I would offer to
send a v2 implementing the changes I suggested in the way you prefer.

Regards,
Andreas
Gerd Hoffmann Jan. 29, 2013, 10:05 a.m. UTC | #2
> This is not what I had in mind. For one thing your initialization does
> not need to go before sysbus_init_{irq,mmio}.
> 
> What I am not too sure about without digging out SysBus and PCI EHCI
> sources is at which point the fields may get modified. In 1/2 your new
> fields are only ever initialized in class_init and then transferred to
> instance state in the realizefn.

The offsets must be initialized before calling usb_ehci_initfn as they
are used to setup the memory regions.

Adding the FUSBH200 specific memory region must be done after calling
usb_ehci_initfn because that creates the parent memory region where you
hook it in.

> There's two better options:
> 
> 1) Change 1/2 to set the default values s->... in an instance_init
> initfn directly, then your instance_init can execute "on top" (with QOM
> infrastructure taking care of calling the parent's initfn before yours)
> and overwrite its values.
> You won't need sec->portscbase and sec->portnr then and we avoid
> duplicating "sec->portscbase = 0x44; sec->portnr = NB_PORTS;".
> 
> 2) Keep 1/2 as is and override the realizefn in 2/2 via dc->realize =
> your_realizefn. This involves saving the parent's realizefn in a new
> FUSHBH200SysBusEHCIClass::parent_realize and explicitly invoking that
> parent_realize function before adding your MemoryRegion as subregion.

Given the ordering constrains outlined above I think (2) will work
better.  You know QOM better than me though ...

> The big underlying question is whether other future devices may want to
> override the values via qdev static properties.

I don't think so.  The values are hardware-specific and should not be
user-configurable.

>> +    MemoryRegion mem_vendor;
> 
> Similar question here: Do we expect other models to expose a single
> vendor MemoryRegion as well?

Unlikely (or at least uncommon).

cheers,
  Gerd
Kuo-Jung Su Jan. 30, 2013, 12:44 a.m. UTC | #3
2013/1/29 Andreas Färber <afaerber@suse.de>

> Am 29.01.2013 06:43, schrieb Kuo-Jung Su:
> > From: Kuo-Jung Su <dantesu@faraday-tech.com>
> >
> > Add Faraday FUSBH200 support, which is slightly different from EHCI spec.
> > (Or maybe simply a bad/wrong implementation...)
> >
> > Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > Cc: Andreas <afaerber@suse.de>
> > Cc: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
> > ---
> >  hw/usb/hcd-ehci-sysbus.c |   66
> ++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/usb/hcd-ehci.h        |    5 ++++
> >  2 files changed, 71 insertions(+)
> >
> > diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
> > index ae2db1a..404a227 100644
> > --- a/hw/usb/hcd-ehci-sysbus.c
> > +++ b/hw/usb/hcd-ehci-sysbus.c
> > @@ -17,6 +17,49 @@
> >
> >  #include "hw/usb/hcd-ehci.h"
> >
> > +/*
> > + * Faraday FUSBH200 USB 2.0 EHCI
> > + */
> > +
> > +static uint64_t
> > +ehci_fusbh200_read(void *ptr, hwaddr addr, unsigned size)
> > +{
> > +    hwaddr off = 0x34 + addr;
> > +
> > +    switch (off) {
> > +    case 0x34:  /* fusbh200: EOF/Async. Sleep Timer Register */
> > +        return 0x00000041;
> > +    case 0x40:  /* fusbh200: Bus Monitor Control/Status Register */
> > +        /* High-Speed, VBUS valid, interrupt level-high active */
> > +        return (2 << 9) | (1 << 8) | (1 << 3);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static void
> > +ehci_fusbh200_write(void *ptr, hwaddr addr, uint64_t val, unsigned size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ehci_mmio_fusbh200_ops = {
> > +    .read = ehci_fusbh200_read,
> > +    .write = ehci_fusbh200_write,
> > +    .valid.min_access_size = 4,
> > +    .valid.max_access_size = 4,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +};
>
> This part looks okay now, except that I'd suggest to name the ops
> ehci_fusbh200_mmio_ops (ehci_fusbh200_ being a consistent prefix).
>
>
Sure, consider it done.


> > +
> > +static void
> > +usb_ehci_fusbh200_initfn(EHCIState *s, DeviceState *dev)
>
> Bad naming, the inconsistent use of "initfn" is what my patch tried to
> clean up for you.
> And this signature is only necessary in common EHCI code because it
> needs to access EHCIState from both PCIDevice and SysBusDevice. This
> change is in SysBus-only code though, so you can access EHCIState
> through the device state.
>
>
My bad, because I didn't know what's the difference between realizefn and
initfn,
so I did it in a wrong way.


> > +{
> > +    memory_region_init_io(&s->mem_vendor, &ehci_mmio_fusbh200_ops, s,
> > +                          "fusbh200", 0x4c);
>
> This has no dependencies on other fields, so it should go into a
> void ehci_fusbh200_initfn(Object *obj) hooked to your .instance_init.
>
> > +    memory_region_add_subregion(&s->mem,
> > +                                s->opregbase + s->portscbase + 4 *
> s->portnr,
> > +                                &s->mem_vendor);
> > +}
> > +
> >  static const VMStateDescription vmstate_ehci_sysbus = {
> >      .name        = "ehci-sysbus",
> >      .version_id  = 2,
> > @@ -46,6 +89,9 @@ static void usb_ehci_sysbus_realizefn(DeviceState
> *dev, Error **errp)
> >      s->dma = &dma_context_memory;
> >
> >      usb_ehci_initfn(s, dev);
>
> FWIW I notice that I should've renamed this in my patch, too (outside
> the scope of your patch).
>
> > +    if (sec->vendor_init) {
> > +        sec->vendor_init(s, DEVICE(dev));
> > +    }
> >      sysbus_init_irq(d, &s->irq);
> >      sysbus_init_mmio(d, &s->mem);
> >  }
>
> This is not what I had in mind. For one thing your initialization does
> not need to go before sysbus_init_{irq,mmio}.
>
> What I am not too sure about without digging out SysBus and PCI EHCI
> sources is at which point the fields may get modified. In 1/2 your new
> fields are only ever initialized in class_init and then transferred to
> instance state in the realizefn.
>
> There's two better options:
>
> 1) Change 1/2 to set the default values s->... in an instance_init
> initfn directly, then your instance_init can execute "on top" (with QOM
> infrastructure taking care of calling the parent's initfn before yours)
> and overwrite its values.
> You won't need sec->portscbase and sec->portnr then and we avoid
> duplicating "sec->portscbase = 0x44; sec->portnr = NB_PORTS;".
>
> 2) Keep 1/2 as is and override the realizefn in 2/2 via dc->realize =
> your_realizefn. This involves saving the parent's realizefn in a new
> FUSHBH200SysBusEHCIClass::parent_realize and explicitly invoking that
> parent_realize function before adding your MemoryRegion as subregion.
>
> The big underlying question is whether other future devices may want to
> override the values via qdev static properties. Then the assignment
> needs to remain in realizefn, otherwise initfn makes our life easier.
>

No problem,  consider it done.


> > @@ -76,6 +122,7 @@ static void ehci_xlnx_class_init(ObjectClass *oc,
> void *data)
> >      sec->opregbase = 0x140;
> >      sec->portscbase = 0x44;
> >      sec->portnr = NB_PORTS;
> > +    sec->vendor_init = NULL;
> >  }
> >
> >  static const TypeInfo ehci_xlnx_type_info = {
> > @@ -92,6 +139,7 @@ static void ehci_exynos4210_class_init(ObjectClass
> *oc, void *data)
> >      sec->opregbase = 0x10;
> >      sec->portscbase = 0x44;
> >      sec->portnr = NB_PORTS;
> > +    sec->vendor_init = NULL;
> >  }
> >
> >  static const TypeInfo ehci_exynos4210_type_info = {
>
> You don't need to zero-initialize fields in class_init or initfn.
>
>
got you, thanks


> > @@ -100,11 +148,29 @@ static const TypeInfo ehci_exynos4210_type_info = {
> >      .class_init    = ehci_exynos4210_class_init,
> >  };
> >
> > +static void ehci_fusbh200_class_init(ObjectClass *oc, void *data)
> > +{
> > +    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
> > +
> > +    sec->capsbase = 0x0;
> > +    sec->opregbase = 0x10;
> > +    sec->portscbase = 0x20;
> > +    sec->portnr = 1;
> > +    sec->vendor_init = usb_ehci_fusbh200_initfn;
> > +}
> > +
> > +static const TypeInfo ehci_fusbh200_type_info = {
> > +    .name          = TYPE_FUSBH200_EHCI,
> > +    .parent        = TYPE_SYS_BUS_EHCI,
> > +    .class_init    = ehci_fusbh200_class_init,
> > +};
> > +
> >  static void ehci_sysbus_register_types(void)
> >  {
> >      type_register_static(&ehci_type_info);
> >      type_register_static(&ehci_xlnx_type_info);
> >      type_register_static(&ehci_exynos4210_type_info);
> > +    type_register_static(&ehci_fusbh200_type_info);
> >  }
> >
> >  type_init(ehci_sysbus_register_types)
> > diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
> > index e587b67..3ca9c8f 100644
> > --- a/hw/usb/hcd-ehci.h
> > +++ b/hw/usb/hcd-ehci.h
> > @@ -261,6 +261,7 @@ struct EHCIState {
> >      MemoryRegion mem_caps;
> >      MemoryRegion mem_opreg;
> >      MemoryRegion mem_ports;
> > +    MemoryRegion mem_vendor;
> >      int companion_count;
> >      uint16_t capsbase;
> >      uint16_t opregbase;
>
> Similar question here: Do we expect other models to expose a single
> vendor MemoryRegion as well?
>
> I would prefer to place this a) in the SysBus-specific state or if only
> for this device b) in a new FUSBH200-specific state derived from it.
>
>
ok, no problem


> > @@ -336,6 +337,7 @@ typedef struct EHCIPCIState {
> >
> >  #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
> >  #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
> > +#define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
> >
> >  #define SYS_BUS_EHCI(obj) \
> >      OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI)
> > @@ -361,6 +363,9 @@ typedef struct SysBusEHCIClass {
> >      uint16_t opregbase;
> >      uint16_t portscbase;
> >      uint16_t portnr;
> > +
> > +    /* vendor specific init function */
> > +    void (*vendor_init)(EHCIState *s, DeviceState *dev);
> >  } SysBusEHCIClass;
> >
> >  #endif
>
> Gerd, what are your thoughts? If Kuo-Jung doesn't mind, I would offer to
> send a v2 implementing the changes I suggested in the way you prefer.
>

Don't worry about me, I'm O.K to any changes. It does not bother me at all,
what's really killing me is only the patch rules....


>
> Regards,
> Andreas
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
>
Andreas Färber Jan. 31, 2013, 3:35 p.m. UTC | #4
Am 30.01.2013 01:44, schrieb Kuo-Jung Su:
> 
> 2013/1/29 Andreas Färber <afaerber@suse.de <mailto:afaerber@suse.de>>
> 
>     Gerd, what are your thoughts? If Kuo-Jung doesn't mind, I would offer to
>     send a v2 implementing the changes I suggested in the way you prefer.
> 
> 
> Don't worry about me, I'm O.K to any changes. It does not bother me at all,
> what's really killing me is only the patch rules....

I started looking into this but I feel we should defer both my realizefn
patch and the fusbh200 addition to 1.5, so that it can get sufficient
review. It turned out less trivial than I thought. ;)
Will send an RFC the next days.

Regards,
Andreas

P.S. Kuo-Jung, please try to avoid HTML-formatted replies to the list,
the quoting breaks then. Thanks.
Kuo-Jung Su Feb. 1, 2013, 1:41 a.m. UTC | #5
2013/1/31 Andreas Färber <afaerber@suse.de>:
> Am 30.01.2013 01:44, schrieb Kuo-Jung Su:
>>
>> 2013/1/29 Andreas Färber <afaerber@suse.de <mailto:afaerber@suse.de>>
>>
>>     Gerd, what are your thoughts? If Kuo-Jung doesn't mind, I would offer to
>>     send a v2 implementing the changes I suggested in the way you prefer.
>>
>>
>> Don't worry about me, I'm O.K to any changes. It does not bother me at all,
>> what's really killing me is only the patch rules....
>
> I started looking into this but I feel we should defer both my realizefn
> patch and the fusbh200 addition to 1.5, so that it can get sufficient
> review. It turned out less trivial than I thought. ;)
> Will send an RFC the next days.
>
> Regards,
> Andreas
>
> P.S. Kuo-Jung, please try to avoid HTML-formatted replies to the list,
> the quoting breaks then. Thanks.

Sure, it's my bad, sorry.

>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg



--
Best wishes,
Kuo-Jung Su
diff mbox

Patch

diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
index ae2db1a..404a227 100644
--- a/hw/usb/hcd-ehci-sysbus.c
+++ b/hw/usb/hcd-ehci-sysbus.c
@@ -17,6 +17,49 @@ 
 
 #include "hw/usb/hcd-ehci.h"
 
+/*
+ * Faraday FUSBH200 USB 2.0 EHCI
+ */
+
+static uint64_t
+ehci_fusbh200_read(void *ptr, hwaddr addr, unsigned size)
+{
+    hwaddr off = 0x34 + addr;
+
+    switch (off) {
+    case 0x34:  /* fusbh200: EOF/Async. Sleep Timer Register */
+        return 0x00000041;
+    case 0x40:  /* fusbh200: Bus Monitor Control/Status Register */
+        /* High-Speed, VBUS valid, interrupt level-high active */
+        return (2 << 9) | (1 << 8) | (1 << 3);
+    }
+
+    return 0;
+}
+
+static void
+ehci_fusbh200_write(void *ptr, hwaddr addr, uint64_t val, unsigned size)
+{
+}
+
+static const MemoryRegionOps ehci_mmio_fusbh200_ops = {
+    .read = ehci_fusbh200_read,
+    .write = ehci_fusbh200_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void
+usb_ehci_fusbh200_initfn(EHCIState *s, DeviceState *dev)
+{
+    memory_region_init_io(&s->mem_vendor, &ehci_mmio_fusbh200_ops, s,
+                          "fusbh200", 0x4c);
+    memory_region_add_subregion(&s->mem,
+                                s->opregbase + s->portscbase + 4 * s->portnr,
+                                &s->mem_vendor);
+}
+
 static const VMStateDescription vmstate_ehci_sysbus = {
     .name        = "ehci-sysbus",
     .version_id  = 2,
@@ -46,6 +89,9 @@  static void usb_ehci_sysbus_realizefn(DeviceState *dev, Error **errp)
     s->dma = &dma_context_memory;
 
     usb_ehci_initfn(s, dev);
+    if (sec->vendor_init) {
+        sec->vendor_init(s, DEVICE(dev));
+    }
     sysbus_init_irq(d, &s->irq);
     sysbus_init_mmio(d, &s->mem);
 }
@@ -76,6 +122,7 @@  static void ehci_xlnx_class_init(ObjectClass *oc, void *data)
     sec->opregbase = 0x140;
     sec->portscbase = 0x44;
     sec->portnr = NB_PORTS;
+    sec->vendor_init = NULL;
 }
 
 static const TypeInfo ehci_xlnx_type_info = {
@@ -92,6 +139,7 @@  static void ehci_exynos4210_class_init(ObjectClass *oc, void *data)
     sec->opregbase = 0x10;
     sec->portscbase = 0x44;
     sec->portnr = NB_PORTS;
+    sec->vendor_init = NULL;
 }
 
 static const TypeInfo ehci_exynos4210_type_info = {
@@ -100,11 +148,29 @@  static const TypeInfo ehci_exynos4210_type_info = {
     .class_init    = ehci_exynos4210_class_init,
 };
 
+static void ehci_fusbh200_class_init(ObjectClass *oc, void *data)
+{
+    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
+
+    sec->capsbase = 0x0;
+    sec->opregbase = 0x10;
+    sec->portscbase = 0x20;
+    sec->portnr = 1;
+    sec->vendor_init = usb_ehci_fusbh200_initfn;
+}
+
+static const TypeInfo ehci_fusbh200_type_info = {
+    .name          = TYPE_FUSBH200_EHCI,
+    .parent        = TYPE_SYS_BUS_EHCI,
+    .class_init    = ehci_fusbh200_class_init,
+};
+
 static void ehci_sysbus_register_types(void)
 {
     type_register_static(&ehci_type_info);
     type_register_static(&ehci_xlnx_type_info);
     type_register_static(&ehci_exynos4210_type_info);
+    type_register_static(&ehci_fusbh200_type_info);
 }
 
 type_init(ehci_sysbus_register_types)
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index e587b67..3ca9c8f 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -261,6 +261,7 @@  struct EHCIState {
     MemoryRegion mem_caps;
     MemoryRegion mem_opreg;
     MemoryRegion mem_ports;
+    MemoryRegion mem_vendor;
     int companion_count;
     uint16_t capsbase;
     uint16_t opregbase;
@@ -336,6 +337,7 @@  typedef struct EHCIPCIState {
 
 #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb"
 #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
+#define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
 
 #define SYS_BUS_EHCI(obj) \
     OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI)
@@ -361,6 +363,9 @@  typedef struct SysBusEHCIClass {
     uint16_t opregbase;
     uint16_t portscbase;
     uint16_t portnr;
+
+    /* vendor specific init function */
+    void (*vendor_init)(EHCIState *s, DeviceState *dev);
 } SysBusEHCIClass;
 
 #endif