Hi Philippe,

On Mon, Apr 20, 2020 at 12:16 AM Philippe Mathieu-Daudé <f4...@amsat.org>
wrote:

> Hi Paul,
>
> On 3/29/20 1:17 AM, Paul Zimmerman wrote:
> > Add the dwc-hsotg (dwc2) USB host controller emulation code.
> > Based on hw/usb/hcd-ehci.c and hw/usb/hcd-ohci.c.
> >
> > Note that to use this with the dwc-otg driver in the Raspbian
> > kernel, you must pass the option "dwc_otg.fiq_fsm_enable=0" on
> > the kernel command line.
> >
> > Emulation of slave mode and of descriptor-DMA mode has not been
> > implemented yet. These modes are seldom used.
> >
> > I have used some on-line sources of information while developing
> > this emulation, including:
> >
> > http://www.capital-micro.com/PDF/CME-M7_Family_User_Guide_EN.pdf
> > has a pretty complete description of the controller starting on
> > page 370.
> >
> >
> https://sourceforge.net/p/wive-ng/wive-ng-mt/ci/master/tree/docs/DataSheets/RT3050_5x_V2.0_081408_0902.pdf
> > has a description of the controller registers starting on page
> > 130.
> >
> > Signed-off-by: Paul Zimmerman <pauld...@gmail.com>
> > ---
> >  hw/usb/hcd-dwc2.c   | 1301 +++++++++++++++++++++++++++++++++++++++++++
> >  hw/usb/trace-events |   47 ++
> >  2 files changed, 1348 insertions(+)
> >  create mode 100644 hw/usb/hcd-dwc2.c
> >
> > diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c
> [...]
> > +static void dwc2_init(DWC2State *s, DeviceState *dev)
> > +{
> > +    s->usb_frame_time = NANOSECONDS_PER_SECOND / 1000;          /*
> 1000000 */
> > +    if (NANOSECONDS_PER_SECOND >= USB_HZ_FS) {
> > +        s->usb_bit_time = NANOSECONDS_PER_SECOND / USB_HZ_FS;   /* 83.3
> */
> > +    } else {
> > +        s->usb_bit_time = 1;
> > +    }
> > +
> > +    s->fi = 11999;
>
> What is this magic number?
>

This is the USB frame interval, in bit times I believe. It's actually
12000-1. Not sure of the reason for the -1, I lifted this code directly
from the hcd-ohci driver and it seems to work ;) The hcd-ohci driver
actually has this value in hex (0x2edf), I changed it to decimal because
I thought it was clearer. I can make a #define for this if you think
that would be better.

> +
> > +    memory_region_init(&s->mem, OBJECT(dev), "dwc2", DWC2_MMIO_SIZE);
> > +    memory_region_init_io(&s->mem_glbreg, OBJECT(dev),
> &dwc2_mmio_glbreg_ops, s,
> > +                          "global", 0x70);
> > +    memory_region_init_io(&s->mem_fszreg, OBJECT(dev),
> &dwc2_mmio_fszreg_ops, s,
> > +                          "hptxfsiz", 0x4);
> > +    memory_region_init_io(&s->mem_hreg0, OBJECT(dev),
> &dwc2_mmio_hreg0_ops, s,
> > +                          "host", 0x44);
> > +    memory_region_init_io(&s->mem_hreg1, OBJECT(dev),
> &dwc2_mmio_hreg1_ops, s,
> > +                          "host channels", 0x20 * NB_CHAN);
> > +    memory_region_init_io(&s->mem_pcgreg, OBJECT(dev),
> &dwc2_mmio_pcgreg_ops, s,
> > +                          "power/clock", 0x8);
> > +    memory_region_init_io(&s->mem_hreg2, OBJECT(dev),
> &dwc2_mmio_hreg2_ops, s,
> > +                          "host fifos", NB_CHAN * 0x1000);
> > +
> > +    memory_region_add_subregion(&s->mem, s->glbregbase, &s->mem_glbreg);
> > +    memory_region_add_subregion(&s->mem, s->fszregbase, &s->mem_fszreg);
> > +    memory_region_add_subregion(&s->mem, s->hreg0base, &s->mem_hreg0);
> > +    memory_region_add_subregion(&s->mem, s->hreg1base, &s->mem_hreg1);
> > +    memory_region_add_subregion(&s->mem, s->pcgregbase, &s->mem_pcgreg);
> > +    memory_region_add_subregion(&s->mem, s->hreg2base, &s->mem_hreg2);
> > +
> > +    s->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> > +                                dwc2_frame_boundary, s);
> > +}
> > +
> > +static void dwc2_sysbus_reset(DeviceState *dev)
> > +{
> > +    SysBusDevice *d = SYS_BUS_DEVICE(dev);
> > +    DWC2State *s = DWC2_USB(d);
> > +
> > +    dwc2_reset(s);
> > +}
> > +
> > +static void dwc2_sysbus_realize(DeviceState *dev, Error **errp)
> > +{
> > +    SysBusDevice *d = SYS_BUS_DEVICE(dev);
> > +    DWC2State *s = DWC2_USB(dev);
> > +
> > +    s->glbregbase = 0;
> > +    s->fszregbase = 0x0100;
> > +    s->hreg0base = 0x0400;
> > +    s->hreg1base = 0x0500;
> > +    s->pcgregbase = 0x0e00;
> > +    s->hreg2base = 0x1000;
>
> No need to use variable for the constant base addresses, use them
> directly in as argument to memory_region_add_subregion().
>

These values are also used in the register read/write functions,
that's why they are in variables.

Since you don't reuse each block, and blocks cover very few registers,
> have you considered using a pair of MRs instead? One of 4KB and the
> other 64KB. hreg2 is the only one particularly different.
>

Because this controller has so many registers, I use arrays
in the DWC2State struct to hold them instead of addressing
them individually. If I only used two MRs, those arrays would
either need be a lot bigger, or the read/write functions would
need some special logic to address the correct array. I thought
the current method was the most straightforward.

Thanks,
Paul

Reply via email to