From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53778) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1akzgn-0000Bq-3Z for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1akzgi-0000kD-H3 for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:33 -0400 Received: from mx1.mailbox.org ([80.241.60.212]:38935) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1akzgi-0000j0-4Y for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:28 -0400 From: jbenz@mailbox.org Date: Tue, 29 Mar 2016 21:51:24 +0200 Message-Id: <1459281084-13346-1-git-send-email-jbenz@mailbox.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------2.7.4" Subject: [Qemu-devel] [PATCH] xilinx_zynq: merged support for ULPI PHY and ULPI viewport from xilinx/qemu List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Joscha Benz , Peter Crosthwaite , Alistair Francis From: Joscha Benz This is a multi-part message in MIME format. --------------2.7.4 Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: quoted-printable Signed-off-by: Joscha Benz --- hw/usb/hcd-ehci-sysbus.c | 175 ++++++++++++++++++++++++++++++--- hw/usb/hcd-ehci.c | 1 + hw/usb/hcd-ehci.h | 31 ++++++ include/hw/register.h | 245 +++++++++++++++++++++++++++++++++++++++++= ++++++ 4 files changed, 441 insertions(+), 11 deletions(-) create mode 100644 include/hw/register.h --------------2.7.4 Content-Type: text/x-patch; name="0001-xilinx_zynq-merged-support-for-ULPI-PHY-and-ULPI-vie.patch" Content-Disposition: inline; filename="0001-xilinx_zynq-merged-support-for-ULPI-PHY-and-ULPI-vie.patch" Content-Transfer-Encoding: quoted-printable diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 6c20604..4866450 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -17,6 +17,7 @@ =20 #include "qemu/osdep.h" #include "hw/usb/hcd-ehci.h" +#include "hw/register.h" =20 static const VMStateDescription vmstate_ehci_sysbus =3D { .name =3D "ehci-sysbus", @@ -43,15 +44,6 @@ static void usb_ehci_sysbus_realize(DeviceState *dev, = Error **errp) sysbus_init_irq(d, &s->irq); } =20 -static void usb_ehci_sysbus_reset(DeviceState *dev) -{ - SysBusDevice *d =3D SYS_BUS_DEVICE(dev); - EHCISysBusState *i =3D SYS_BUS_EHCI(d); - EHCIState *s =3D &i->ehci; - - ehci_reset(s); -} - static void ehci_sysbus_init(Object *obj) { SysBusDevice *d =3D SYS_BUS_DEVICE(obj); @@ -80,7 +72,6 @@ static void ehci_sysbus_class_init(ObjectClass *klass, = void *data) dc->realize =3D usb_ehci_sysbus_realize; dc->vmsd =3D &vmstate_ehci_sysbus; dc->props =3D ehci_sysbus_properties; - dc->reset =3D usb_ehci_sysbus_reset; set_bit(DEVICE_CATEGORY_USB, dc->categories); } =20 @@ -94,20 +85,182 @@ static const TypeInfo ehci_type_info =3D { .class_size =3D sizeof(SysBusEHCIClass), }; =20 +enum PS7USBRegs { + XLNX_ID =3D 0x0, + XLNX_HWGENERAL =3D 0x4, + XLNX_HWHOST =3D 0x8, + XLNX_HWTXBUF =3D 0x10, + XLNX_HWRXBUF =3D 0x14, + XLNX_DCIVERSION =3D 0x120, + XLNX_DCCPARAMS =3D 0x124, +}; + +/* FIXME: Add the functionality of remaining phy registers */ +enum ULPIRegs { + VENDOR_ID_L =3D 0x0, + VENDOR_ID_H =3D 0x1, + PRODUCT_ID_L =3D 0x2, + PRODUCT_ID_H =3D 0x3, + SCRATCH_REG_0 =3D 0x16, +}; + +REG32(ULPI_VIEWPORT, PS7USB_ULPIVP_OFFSET) + FIELD(ULPI_VIEWPORT, ULPIDATWR, 8, 0) + FIELD(ULPI_VIEWPORT, ULPIDATRD, 8, 8) + FIELD(ULPI_VIEWPORT, ULPIADDR, 8, 16) + FIELD(ULPI_VIEWPORT, ULPIPORT, 3, 24) + FIELD(ULPI_VIEWPORT, ULPISS, 1, 27) + FIELD(ULPI_VIEWPORT, ULPIRW, 1, 29) + FIELD(ULPI_VIEWPORT, ULPIRUN, 1, 30) + FIELD(ULPI_VIEWPORT, ULPIWU, 1, 31) + +static void ehci_xlnx_reset(DeviceState *dev) +{ + PS7USBState *s =3D XLNX_PS7_USB(dev); + + /* Show phy in normal functioning state after init */ + s->ulpi_viewport =3D 0x8000000; + /* Vendor and product ID are as per micron ulpi phy specifications *= / + s->ulpireg[VENDOR_ID_L] =3D 0x24; + s->ulpireg[VENDOR_ID_H] =3D 0x04; + s->ulpireg[PRODUCT_ID_L] =3D 0x4; + s->ulpireg[PRODUCT_ID_H] =3D 0x0; + +} + static void ehci_xlnx_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec =3D SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc =3D DEVICE_CLASS(oc); =20 + dc->reset =3D ehci_xlnx_reset; set_bit(DEVICE_CATEGORY_USB, dc->categories); sec->capsbase =3D 0x100; sec->opregbase =3D 0x140; } =20 +static uint64_t xlnx_devreg_read(void *opaque, hwaddr addr, unsigned siz= e) +{ + EHCIState *s =3D opaque; + /* DCIVERSION and DCCPARAMS are mapped at 0x20 words distance fr= om + * end of capacity registers + */ + hwaddr offset =3D s->capsbase + 0x20 + addr; + + switch (offset) { + case XLNX_DCIVERSION: + return 0x00000001; + case XLNX_DCCPARAMS: + /* Host mode enabled + * Number of endpoints fixed to 12 as per zynq-7000 + */ + return 0x0000010C; + } + return 0; +} + +static uint64_t xlnx_hwreg_read(void *opaque, hwaddr addr, unsigned size= ) +{ + /* All the following registers will just read out default values as = per + * dwc_usb2_hs_device_controller spec + */ + switch (addr) { + case XLNX_ID: + return XLNX_ID_DEFVAL; + case XLNX_HWGENERAL: + return XLNX_HWGENERAL_DEFVAL; + case XLNX_HWHOST: + return XLNX_HWHOST_DEFVAL; + case XLNX_HWTXBUF: + return XLNX_HWTXBUF_DEFVAL; + case XLNX_HWRXBUF: + return XLNX_HWRXBUF_DEFVAL; + } + return 0; +} + +static uint64_t xlnx_ulpi_read(void *opaque, hwaddr addr, unsigned size) +{ + PS7USBState *s =3D opaque; + + return s->ulpi_viewport; +} + +static void xlnx_ulpi_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + PS7USBState *s =3D opaque; + uint8_t ulpiaddr; + /* Clear RW feilds before writes */ + s->ulpi_viewport &=3D ~ULPIREG_RWBITS_MASK; + s->ulpi_viewport |=3D data & ULPIREG_RWBITS_MASK; + + /* ULPI Wake Up call : Clear the bit when set */ + if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIWU)) { + s->ulpi_viewport =3D F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULP= IWU, 0); + } + + if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRUN)) { + ulpiaddr =3D F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIADDR); + + if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRW)) { + s->ulpireg[ulpiaddr] =3D F_EX32(s->ulpi_viewport, ULPI_VIEWP= ORT, + ULPIDATWR); + } else { + s->ulpi_viewport =3D F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, + ULPIDATRD, s->ulpireg[ulpiaddr]); + } + + s->ulpi_viewport =3D F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULP= IRUN, 0); + } +} + +static const MemoryRegionOps ps7usb_devreg_ops =3D { + .read =3D xlnx_devreg_read, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .endianness =3D DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps ps7usb_hwreg_ops =3D { + .read =3D xlnx_hwreg_read, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .endianness =3D DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps ps7usb_ulpi_ops =3D { + .read =3D xlnx_ulpi_read, + .write =3D xlnx_ulpi_write, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .endianness =3D DEVICE_LITTLE_ENDIAN, +}; + +static void ehci_xlnx_init(Object *Obj) +{ + EHCISysBusState *p =3D SYS_BUS_EHCI(Obj); + PS7USBState *s =3D XLNX_PS7_USB(Obj); + EHCIState *pp =3D &p->ehci; + memory_region_init_io(&s->mem_hwreg, Obj, &ps7usb_hwreg_ops, pp, + "ps7usb_hwreg", PS7USB_HWREG_SIZE); + memory_region_add_subregion(&pp->mem, PS7USB_HWREG_OFFSET, &s->mem_h= wreg); + + memory_region_init_io(&s->mem_devreg, Obj, &ps7usb_devreg_ops, pp, + "ps7usb_devicemode", PS7USB_DEVREG_SIZE); + memory_region_add_subregion(&pp->mem, PS7USB_DEVREG_OFFSET, &s->mem_= devreg); + + memory_region_init_io(&s->mem_ulpi, Obj, &ps7usb_ulpi_ops, s, + "ps7usb_ulpi_viewport", PS7USB_ULPIVP_SIZE); + memory_region_add_subregion(&pp->mem, PS7USB_ULPIVP_OFFSET, &s->mem_= ulpi); +} + static const TypeInfo ehci_xlnx_type_info =3D { - .name =3D "xlnx,ps7-usb", + .name =3D TYPE_XLNX_PS7_USB, .parent =3D TYPE_SYS_BUS_EHCI, .class_init =3D ehci_xlnx_class_init, + .instance_size =3D sizeof(PS7USBState), + .instance_init =3D ehci_xlnx_init, }; =20 static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 159f58d..bbda633 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2478,6 +2478,7 @@ void usb_ehci_realize(EHCIState *s, DeviceState *de= v, Error **errp) s->async_bh =3D qemu_bh_new(ehci_frame_timer, s); s->device =3D dev; =20 + qemu_register_reset(ehci_reset, s); s->vmstate =3D qemu_add_vm_change_state_handler(usb_ehci_vm_state_ch= ange, s); } =20 diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 3021842..4cf0a29 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -342,6 +342,7 @@ typedef struct EHCIPCIState { #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" +#define TYPE_XLNX_PS7_USB "xlnx,ps7-usb" =20 #define SYS_BUS_EHCI(obj) \ OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI) @@ -380,4 +381,34 @@ typedef struct FUSBH200EHCIState { MemoryRegion mem_vendor; } FUSBH200EHCIState; =20 +#define XLNX_PS7_USB(obj) \ + OBJECT_CHECK(PS7USBState, (obj), TYPE_XLNX_PS7_USB) + +#define PS7USB_DEVREG_OFFSET 0x120 +#define PS7USB_DEVREG_SIZE 0x8 +#define PS7USB_HWREG_OFFSET 0x0 +#define PS7USB_HWREG_SIZE 0x18 +#define PS7USB_ULPIVP_OFFSET 0x170 +#define PS7USB_ULPIVP_SIZE 0x4 + +#define XLNX_ID_DEFVAL 0xE441FA05 +#define XLNX_HWGENERAL_DEFVAL 0x83 +#define XLNX_HWHOST_DEFVAL 0x10020001 +#define XLNX_HWTXBUF_DEFVAL 0x80060A10 +#define XLNX_HWRXBUF_DEFVAL 0xA10 + +#define ULPIREG_RWBITS_MASK 0xE0FF00FF + +typedef struct PS7USBState { + EHCISysBusState parent_obj; + + uint32_t ulpi_viewport; + uint8_t ulpireg[0x19]; + + MemoryRegion mem_devreg; + MemoryRegion mem_hwreg; + MemoryRegion mem_ulpi; +} PS7USBState; + + #endif diff --git a/include/hw/register.h b/include/hw/register.h new file mode 100644 index 0000000..1213135 --- /dev/null +++ b/include/hw/register.h @@ -0,0 +1,245 @@ +/* + * Register Definition API + * + * Copyright (c) 2013 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef REGISTER_H +#define REGISTER_H + +#include "hw/qdev-core.h" +#include "exec/memory.h" +#include "hw/irq.h" + +typedef struct RegisterInfo RegisterInfo; +typedef struct RegisterAccessInfo RegisterAccessInfo; +typedef struct RegisterDecodeInfo RegisterDecodeInfo; + +/** + * A register access error message + * @mask: Bits in the register the error applies to + * @reason: Reason why this access is an error + */ + +typedef struct RegisterAccessError { + uint64_t mask; + const char *reason; +} RegisterAccessError; + +#define REG_GPIO_POL_HIGH 0 +#define REG_GPIO_POL_LOW 1 +typedef struct RegisterGPIOMapping { + const char *name; + uint8_t bit_pos; + bool input; + bool polarity; + uint8_t num; + uint8_t width; +} RegisterGPIOMapping; + +/** + * Access description for a register that is part of guest accessible de= vice + * state. + * + * @name: String name of the register + * @ro: whether or not the bit is read-only + * @w1c: bits with the common write 1 to clear semantic. + * @reset: reset value. + * @cor: Bits that are clear on read + * @rsvd: Bits that are reserved and should not be changed + * + * @ge1: Bits that when written 1 indicate a guest error + * @ge0: Bits that when written 0 indicate a guest error + * @ui1: Bits that when written 1 indicate use of an unimplemented featu= re + * @ui0: Bits that when written 0 indicate use of an unimplemented featu= re + * + * @pre_write: Pre write callback. Passed the value that's to be written= , + * immediately before the actual write. The returned value is what is wr= itten, + * giving the handler a chance to modify the written value. + * @post_write: Post write callback. Passed the written value. Most writ= e side + * effects should be implemented here. + * + * @post_read: Post read callback. Passes the value that is about to be = returned + * for a read. The return value from this function is what is ultimately= read, + * allowing this function to modify the value before return to the clien= t. + */ + +#define REG_DECODE_READ (1 << 0) +#define REG_DECODE_WRITE (1 << 1) +#define REG_DECODE_EXECUTE (1 << 2) +#define REG_DECODE_RW (REG_DECODE_READ | REG_DECODE_WRITE) + +struct RegisterAccessInfo { + const char *name; + uint64_t ro; + uint64_t w1c; + uint64_t reset; + uint64_t cor; + uint64_t rsvd; + /* HACK - get rid of me */ + uint64_t inhibit_reset; + + const RegisterAccessError *ge0; + const RegisterAccessError *ge1; + const RegisterAccessError *ui0; + const RegisterAccessError *ui1; + + uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val); + void (*post_write)(RegisterInfo *reg, uint64_t val); + + uint64_t (*post_read)(RegisterInfo *reg, uint64_t val); + + const RegisterGPIOMapping *gpios; + + size_t storage; + int data_size; + + struct { + hwaddr addr; + uint8_t flags; + } decode; + + void *opaque; +}; + +/** + * A register that is part of guest accessible state + * @data: pointer to the register data. Will be cast + * to the relevant uint type depending on data_size. + * @data_size: Size of the register in bytes. Must be + * 1, 2, 4 or 8 + * @data_big_endian: Define endianess of data register + * + * @access: Access desciption of this register + * + * @debug: Whether or not verbose debug is enabled + * @prefix: String prefix for log and debug messages + * + * @opaque: Opaque data for the register + * + * @mem: optional Memory region for the register + */ + +struct RegisterInfo { + DeviceState parent_obj; + + void *data; + int data_size; + + const RegisterAccessInfo *access; + + bool debug; + const char *prefix; + + void *opaque; + /* private */ + bool read_lite; + bool write_lite; + + MemoryRegion mem; +}; + +#define TYPE_REGISTER "qemu,register" +#define REGISTER(obj) OBJECT_CHECK(RegisterInfo, (obj), TYPE_REGISTER) + +struct RegisterDecodeInfo { + RegisterInfo *reg; + hwaddr addr; + unsigned len; +}; + +/** + * write a value to a register, subject to its restrictions + * @reg: register to write to + * @val: value to write + * @we: write enable mask + */ + +void register_write(RegisterInfo *reg, uint64_t val, uint64_t we); + +/** + * read a value from a register, subject to its restrictions + * @reg: register to read from + * returns: value read + */ + +uint64_t register_read(RegisterInfo *reg); + +/** + * reset a register + * @reg: register to reset + */ + +void register_reset(RegisterInfo *reg); + +/** + * initialize a register. Gpio's are setup as IOs to the specified devic= e. + * @reg: Register to initialize + */ + +void register_init(RegisterInfo *reg); + +/** + * Refresh GPIO outputs based on diff between old value register current= value. + * GPIOs are refreshed for fields where the old value differs to the cur= rent + * value. + * + * @reg: Register to refresh GPIO outs + * @old_value: previous value of register + */ + +void register_refresh_gpios(RegisterInfo *reg, uint64_t old_value); + +void register_write_memory_be(void *opaque, hwaddr addr, uint64_t value, + unsigned size); +void register_write_memory_le(void *opaque, hwaddr addr, uint64_t value, + unsigned size); + +uint64_t register_read_memory_be(void *opaque, hwaddr addr, unsigned siz= e); +uint64_t register_read_memory_le(void *opaque, hwaddr addr, unsigned siz= e); + +/* Define constants for a 32 bit register */ + +#define REG32(reg, addr) \ +enum { A_ ## reg =3D (addr) }; \ +enum { R_ ## reg =3D (addr) / 4 }; + +/* Define SHIFT, LEGTH and MASK constants for a field within a register = */ + +#define FIELD(reg, field, length, shift) \ +enum { R_ ## reg ## _ ## field ## _SHIFT =3D (shift)}; \ +enum { R_ ## reg ## _ ## field ## _LENGTH =3D (length)}; \ +enum { R_ ## reg ## _ ## field ## _MASK =3D (((1ULL << (length)) - 1) \ + << (shift)) }; \ + +/* Extract a field from a register */ + +#define F_EX32(storage, reg, field) \ + extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + +/* Extract a field from an array of registers */ + +#define AF_EX32(regs, reg, field) \ + F_EX32((regs)[R_ ## reg], reg, field) + +/* Deposit a register field. */ + +#define F_DP32(storage, reg, field, val) ({ = \ + struct { = \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; = \ + } v =3D { .v =3D val }; = \ + uint32_t d; = \ + d =3D deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, = \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); = \ + d; }) + +/* Deposit a field to array of registers. */ + +#define AF_DP32(regs, reg, field, val) \ + (regs)[R_ ## reg] =3D F_DP32((regs)[R_ ## reg], reg, field, val); +#endif --------------2.7.4--