qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] xilinx_zynq: merged support for ULPI PHY and ULPI v


From: jbenz
Subject: [Qemu-devel] [PATCH] xilinx_zynq: merged support for ULPI PHY and ULPI viewport from xilinx/qemu
Date: Tue, 29 Mar 2016 21:51:24 +0200

Signed-off-by: Joscha Benz <address@hidden>
---
 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

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 @@
 
 #include "qemu/osdep.h"
 #include "hw/usb/hcd-ehci.h"
+#include "hw/register.h"
 
 static const VMStateDescription vmstate_ehci_sysbus = {
     .name        = "ehci-sysbus",
@@ -43,15 +44,6 @@ static void usb_ehci_sysbus_realize(DeviceState *dev, Error 
**errp)
     sysbus_init_irq(d, &s->irq);
 }
 
-static void usb_ehci_sysbus_reset(DeviceState *dev)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(dev);
-    EHCISysBusState *i = SYS_BUS_EHCI(d);
-    EHCIState *s = &i->ehci;
-
-    ehci_reset(s);
-}
-
 static void ehci_sysbus_init(Object *obj)
 {
     SysBusDevice *d = SYS_BUS_DEVICE(obj);
@@ -80,7 +72,6 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void 
*data)
     dc->realize = usb_ehci_sysbus_realize;
     dc->vmsd = &vmstate_ehci_sysbus;
     dc->props = ehci_sysbus_properties;
-    dc->reset = usb_ehci_sysbus_reset;
     set_bit(DEVICE_CATEGORY_USB, dc->categories);
 }
 
@@ -94,20 +85,182 @@ static const TypeInfo ehci_type_info = {
     .class_size    = sizeof(SysBusEHCIClass),
 };
 
+enum PS7USBRegs {
+    XLNX_ID = 0x0,
+    XLNX_HWGENERAL = 0x4,
+    XLNX_HWHOST = 0x8,
+    XLNX_HWTXBUF = 0x10,
+    XLNX_HWRXBUF = 0x14,
+    XLNX_DCIVERSION = 0x120,
+    XLNX_DCCPARAMS  = 0x124,
+};
+
+/* FIXME: Add the functionality of remaining phy registers */
+enum ULPIRegs {
+    VENDOR_ID_L = 0x0,
+    VENDOR_ID_H = 0x1,
+    PRODUCT_ID_L = 0x2,
+    PRODUCT_ID_H = 0x3,
+    SCRATCH_REG_0 = 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 = XLNX_PS7_USB(dev);
+
+    /* Show phy in normal functioning state after init */
+    s->ulpi_viewport = 0x8000000;
+    /* Vendor and product ID are as per micron ulpi phy specifications */
+    s->ulpireg[VENDOR_ID_L] = 0x24;
+    s->ulpireg[VENDOR_ID_H] = 0x04;
+    s->ulpireg[PRODUCT_ID_L] = 0x4;
+    s->ulpireg[PRODUCT_ID_H] = 0x0;
+
+}
+
 static void ehci_xlnx_class_init(ObjectClass *oc, void *data)
 {
     SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
     DeviceClass *dc = DEVICE_CLASS(oc);
 
+    dc->reset = ehci_xlnx_reset;
     set_bit(DEVICE_CATEGORY_USB, dc->categories);
     sec->capsbase = 0x100;
     sec->opregbase = 0x140;
 }
 
+static uint64_t xlnx_devreg_read(void *opaque, hwaddr addr, unsigned size)
+{
+    EHCIState *s = opaque;
+        /* DCIVERSION and DCCPARAMS are mapped at 0x20 words distance from
+         * end of capacity registers
+         */
+    hwaddr offset = 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 = opaque;
+
+    return s->ulpi_viewport;
+}
+
+static void xlnx_ulpi_write(void *opaque, hwaddr addr, uint64_t data,
+                            unsigned size)
+{
+    PS7USBState *s = opaque;
+    uint8_t ulpiaddr;
+    /* Clear RW feilds before writes */
+    s->ulpi_viewport &= ~ULPIREG_RWBITS_MASK;
+    s->ulpi_viewport |= 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 = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIWU, 0);
+    }
+
+    if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRUN)) {
+        ulpiaddr = F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIADDR);
+
+        if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRW)) {
+            s->ulpireg[ulpiaddr] = F_EX32(s->ulpi_viewport, ULPI_VIEWPORT,
+                                          ULPIDATWR);
+        } else {
+            s->ulpi_viewport = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT,
+                                      ULPIDATRD, s->ulpireg[ulpiaddr]);
+        }
+
+        s->ulpi_viewport = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRUN, 0);
+    }
+}
+
+static const MemoryRegionOps ps7usb_devreg_ops = {
+    .read = xlnx_devreg_read,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ps7usb_hwreg_ops = {
+    .read = xlnx_hwreg_read,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ps7usb_ulpi_ops = {
+    .read = xlnx_ulpi_read,
+    .write = xlnx_ulpi_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ehci_xlnx_init(Object *Obj)
+{
+    EHCISysBusState *p = SYS_BUS_EHCI(Obj);
+    PS7USBState *s = XLNX_PS7_USB(Obj);
+    EHCIState *pp = &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_hwreg);
+
+    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 = {
-    .name          = "xlnx,ps7-usb",
+    .name          = TYPE_XLNX_PS7_USB,
     .parent        = TYPE_SYS_BUS_EHCI,
     .class_init    = ehci_xlnx_class_init,
+    .instance_size = sizeof(PS7USBState),
+    .instance_init = ehci_xlnx_init,
 };
 
 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 *dev, 
Error **errp)
     s->async_bh = qemu_bh_new(ehci_frame_timer, s);
     s->device = dev;
 
+    qemu_register_reset(ehci_reset, s);
     s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s);
 }
 
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"
 
 #define SYS_BUS_EHCI(obj) \
     OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI)
@@ -380,4 +381,34 @@ typedef struct FUSBH200EHCIState {
     MemoryRegion mem_vendor;
 } FUSBH200EHCIState;
 
+#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 <address@hidden>
+ *
+ * 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 device
+ * 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 feature
+ * @ui0: Bits that when written 0 indicate use of an unimplemented feature
+ *
+ * @pre_write: Pre write callback. Passed the value that's to be written,
+ * immediately before the actual write. The returned value is what is written,
+ * giving the handler a chance to modify the written value.
+ * @post_write: Post write callback. Passed the written value. Most write 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 client.
+ */
+
+#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 device.
+ * @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 current
+ * 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 size);
+uint64_t register_read_memory_le(void *opaque, hwaddr addr, unsigned size);
+
+/* Define constants for a 32 bit register */
+
+#define REG32(reg, addr) \
+enum { A_ ## reg = (addr) }; \
+enum { R_ ## reg = (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 = (shift)}; \
+enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \
+enum { R_ ## reg ## _ ## field ## _MASK = (((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 = { .v = val };                                                   \
+    uint32_t d;                                                           \
+    d = 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] = F_DP32((regs)[R_ ## reg], reg, field, val);
+#endif

reply via email to

[Prev in Thread] Current Thread [Next in Thread]