@@ -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)
@@ -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);
}
@@ -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
new file mode 100644
@@ -0,0 +1,245 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2013 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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
Signed-off-by: Joscha Benz <jbenz@mailbox.org> --- 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