mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 01:33:56 -06:00
usb/hcd-xhci: Split pci wrapper for xhci base model
This patch sets the base to use xhci as sysbus model, for which pci specific hooks are moved to hcd-xhci-pci.c. As a part of this requirment msi/msix interrupts handling is moved under XHCIPCIState. Made required changes for qemu-xhci-nec. Signed-off-by: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com> Message-id: 1600957256-6494-4-git-send-email-sai.pavan.boddu@xilinx.com Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
755fba11fb
commit
8ddab8dd3d
5 changed files with 303 additions and 193 deletions
|
@ -24,10 +24,7 @@
|
|||
#include "qemu/module.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
|
@ -56,8 +53,6 @@
|
|||
#define OFF_OPER LEN_CAP
|
||||
#define OFF_RUNTIME 0x1000
|
||||
#define OFF_DOORBELL 0x2000
|
||||
#define OFF_MSIX_TABLE 0x3000
|
||||
#define OFF_MSIX_PBA 0x3800
|
||||
/* must be power of 2 */
|
||||
#define LEN_REGS 0x4000
|
||||
|
||||
|
@ -547,54 +542,28 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
|
|||
return &xhci->ports[index];
|
||||
}
|
||||
|
||||
static void xhci_intx_update(XHCIState *xhci)
|
||||
static void xhci_intr_update(XHCIState *xhci, int v)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
int level = 0;
|
||||
|
||||
if (msix_enabled(pci_dev) ||
|
||||
msi_enabled(pci_dev)) {
|
||||
return;
|
||||
if (v == 0) {
|
||||
if (xhci->intr[0].iman & IMAN_IP &&
|
||||
xhci->intr[0].iman & IMAN_IE &&
|
||||
xhci->usbcmd & USBCMD_INTE) {
|
||||
level = 1;
|
||||
}
|
||||
if (xhci->intr_raise) {
|
||||
xhci->intr_raise(xhci, 0, level);
|
||||
}
|
||||
}
|
||||
|
||||
if (xhci->intr[0].iman & IMAN_IP &&
|
||||
xhci->intr[0].iman & IMAN_IE &&
|
||||
xhci->usbcmd & USBCMD_INTE) {
|
||||
level = 1;
|
||||
}
|
||||
|
||||
trace_usb_xhci_irq_intx(level);
|
||||
pci_set_irq(pci_dev, level);
|
||||
}
|
||||
|
||||
static void xhci_msix_update(XHCIState *xhci, int v)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
bool enabled;
|
||||
|
||||
if (!msix_enabled(pci_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
enabled = xhci->intr[v].iman & IMAN_IE;
|
||||
if (enabled == xhci->intr[v].msix_used) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
trace_usb_xhci_irq_msix_use(v);
|
||||
msix_vector_use(pci_dev, v);
|
||||
xhci->intr[v].msix_used = true;
|
||||
} else {
|
||||
trace_usb_xhci_irq_msix_unuse(v);
|
||||
msix_vector_unuse(pci_dev, v);
|
||||
xhci->intr[v].msix_used = false;
|
||||
if (xhci->intr_update) {
|
||||
xhci->intr_update(xhci, v,
|
||||
xhci->intr[v].iman & IMAN_IE);
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_intr_raise(XHCIState *xhci, int v)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
bool pending = (xhci->intr[v].erdp_low & ERDP_EHB);
|
||||
|
||||
xhci->intr[v].erdp_low |= ERDP_EHB;
|
||||
|
@ -611,22 +580,8 @@ static void xhci_intr_raise(XHCIState *xhci, int v)
|
|||
if (!(xhci->usbcmd & USBCMD_INTE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msix_enabled(pci_dev)) {
|
||||
trace_usb_xhci_irq_msix(v);
|
||||
msix_notify(pci_dev, v);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msi_enabled(pci_dev)) {
|
||||
trace_usb_xhci_irq_msi(v);
|
||||
msi_notify(pci_dev, v);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v == 0) {
|
||||
trace_usb_xhci_irq_intx(1);
|
||||
pci_irq_assert(pci_dev);
|
||||
if (xhci->intr_raise) {
|
||||
xhci->intr_raise(xhci, v, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2717,7 +2672,6 @@ static void xhci_reset(DeviceState *dev)
|
|||
xhci->intr[i].erstba_high = 0;
|
||||
xhci->intr[i].erdp_low = 0;
|
||||
xhci->intr[i].erdp_high = 0;
|
||||
xhci->intr[i].msix_used = 0;
|
||||
|
||||
xhci->intr[i].er_ep_idx = 0;
|
||||
xhci->intr[i].er_pcs = 1;
|
||||
|
@ -2939,8 +2893,7 @@ static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size)
|
|||
static void xhci_oper_write(void *ptr, hwaddr reg,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
XHCIState *xhci = ptr;
|
||||
DeviceState *d = DEVICE(ptr);
|
||||
XHCIState *xhci = XHCI(ptr);
|
||||
|
||||
trace_usb_xhci_oper_write(reg, val);
|
||||
|
||||
|
@ -2962,15 +2915,15 @@ static void xhci_oper_write(void *ptr, hwaddr reg,
|
|||
xhci->usbcmd = val & 0xc0f;
|
||||
xhci_mfwrap_update(xhci);
|
||||
if (val & USBCMD_HCRST) {
|
||||
xhci_reset(d);
|
||||
xhci_reset(DEVICE(xhci));
|
||||
}
|
||||
xhci_intx_update(xhci);
|
||||
xhci_intr_update(xhci, 0);
|
||||
break;
|
||||
|
||||
case 0x04: /* USBSTS */
|
||||
/* these bits are write-1-to-clear */
|
||||
xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
|
||||
xhci_intx_update(xhci);
|
||||
xhci_intr_update(xhci, 0);
|
||||
break;
|
||||
|
||||
case 0x14: /* DNCTRL */
|
||||
|
@ -3073,10 +3026,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
|||
}
|
||||
intr->iman &= ~IMAN_IE;
|
||||
intr->iman |= val & IMAN_IE;
|
||||
if (v == 0) {
|
||||
xhci_intx_update(xhci);
|
||||
}
|
||||
xhci_msix_update(xhci, v);
|
||||
xhci_intr_update(xhci, v);
|
||||
break;
|
||||
case 0x04: /* IMOD */
|
||||
intr->imod = val;
|
||||
|
@ -3321,7 +3271,6 @@ static USBBusOps xhci_bus_ops = {
|
|||
|
||||
static void usb_xhci_init(XHCIState *xhci)
|
||||
{
|
||||
DeviceState *dev = DEVICE(xhci);
|
||||
XHCIPort *port;
|
||||
unsigned int i, usbports, speedmask;
|
||||
|
||||
|
@ -3336,7 +3285,7 @@ static void usb_xhci_init(XHCIState *xhci)
|
|||
usbports = MAX(xhci->numports_2, xhci->numports_3);
|
||||
xhci->numports = xhci->numports_2 + xhci->numports_3;
|
||||
|
||||
usb_bus_new(&xhci->bus, sizeof(xhci->bus), &xhci_bus_ops, dev);
|
||||
usb_bus_new(&xhci->bus, sizeof(xhci->bus), &xhci_bus_ops, xhci->hostOpaque);
|
||||
|
||||
for (i = 0; i < usbports; i++) {
|
||||
speedmask = 0;
|
||||
|
@ -3376,21 +3325,12 @@ static void usb_xhci_init(XHCIState *xhci)
|
|||
}
|
||||
}
|
||||
|
||||
static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
|
||||
static void usb_xhci_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
int i, ret;
|
||||
Error *err = NULL;
|
||||
int i;
|
||||
|
||||
XHCIState *xhci = XHCI(dev);
|
||||
|
||||
dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */
|
||||
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
|
||||
dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
|
||||
dev->config[0x60] = 0x30; /* release number */
|
||||
|
||||
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
|
||||
xhci->nec_quirks = true;
|
||||
}
|
||||
if (xhci->numintrs > MAXINTRS) {
|
||||
xhci->numintrs = MAXINTRS;
|
||||
}
|
||||
|
@ -3412,36 +3352,18 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
|
|||
xhci->max_pstreams_mask = 0;
|
||||
}
|
||||
|
||||
if (xhci->msi != ON_OFF_AUTO_OFF) {
|
||||
ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
|
||||
/* Any error other than -ENOTSUP(board's MSI support is broken)
|
||||
* is a programming error */
|
||||
assert(!ret || ret == -ENOTSUP);
|
||||
if (ret && xhci->msi == ON_OFF_AUTO_ON) {
|
||||
/* Can't satisfy user's explicit msi=on request, fail */
|
||||
error_append_hint(&err, "You have to use msi=auto (default) or "
|
||||
"msi=off with this machine type.\n");
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
|
||||
/* With msi=auto, we fall back to MSI off silently */
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
usb_xhci_init(xhci);
|
||||
xhci->as = pci_get_address_space(dev);
|
||||
xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci);
|
||||
|
||||
memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS);
|
||||
memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci,
|
||||
memory_region_init(&xhci->mem, OBJECT(dev), "xhci", LEN_REGS);
|
||||
memory_region_init_io(&xhci->mem_cap, OBJECT(dev), &xhci_cap_ops, xhci,
|
||||
"capabilities", LEN_CAP);
|
||||
memory_region_init_io(&xhci->mem_oper, OBJECT(xhci), &xhci_oper_ops, xhci,
|
||||
memory_region_init_io(&xhci->mem_oper, OBJECT(dev), &xhci_oper_ops, xhci,
|
||||
"operational", 0x400);
|
||||
memory_region_init_io(&xhci->mem_runtime, OBJECT(xhci), &xhci_runtime_ops, xhci,
|
||||
"runtime", LEN_RUNTIME);
|
||||
memory_region_init_io(&xhci->mem_doorbell, OBJECT(xhci), &xhci_doorbell_ops, xhci,
|
||||
"doorbell", LEN_DOORBELL);
|
||||
memory_region_init_io(&xhci->mem_runtime, OBJECT(dev), &xhci_runtime_ops,
|
||||
xhci, "runtime", LEN_RUNTIME);
|
||||
memory_region_init_io(&xhci->mem_doorbell, OBJECT(dev), &xhci_doorbell_ops,
|
||||
xhci, "doorbell", LEN_DOORBELL);
|
||||
|
||||
memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap);
|
||||
memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper);
|
||||
|
@ -3452,31 +3374,13 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
|
|||
XHCIPort *port = &xhci->ports[i];
|
||||
uint32_t offset = OFF_OPER + 0x400 + 0x10 * i;
|
||||
port->xhci = xhci;
|
||||
memory_region_init_io(&port->mem, OBJECT(xhci), &xhci_port_ops, port,
|
||||
memory_region_init_io(&port->mem, OBJECT(dev), &xhci_port_ops, port,
|
||||
port->name, 0x10);
|
||||
memory_region_add_subregion(&xhci->mem, offset, &port->mem);
|
||||
}
|
||||
|
||||
pci_register_bar(dev, 0,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
&xhci->mem);
|
||||
|
||||
if (pci_bus_is_express(pci_get_bus(dev)) ||
|
||||
xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) {
|
||||
ret = pcie_endpoint_cap_init(dev, 0xa0);
|
||||
assert(ret > 0);
|
||||
}
|
||||
|
||||
if (xhci->msix != ON_OFF_AUTO_OFF) {
|
||||
/* TODO check for errors, and should fail when msix=on */
|
||||
msix_init(dev, xhci->numintrs,
|
||||
&xhci->mem, 0, OFF_MSIX_TABLE,
|
||||
&xhci->mem, 0, OFF_MSIX_PBA,
|
||||
0x90, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_xhci_exit(PCIDevice *dev)
|
||||
static void usb_xhci_unrealize(DeviceState *dev)
|
||||
{
|
||||
int i;
|
||||
XHCIState *xhci = XHCI(dev);
|
||||
|
@ -3503,25 +3407,18 @@ static void usb_xhci_exit(PCIDevice *dev)
|
|||
memory_region_del_subregion(&xhci->mem, &port->mem);
|
||||
}
|
||||
|
||||
/* destroy msix memory region */
|
||||
if (dev->msix_table && dev->msix_pba
|
||||
&& dev->msix_entry_used) {
|
||||
msix_uninit(dev, &xhci->mem, &xhci->mem);
|
||||
}
|
||||
|
||||
usb_bus_release(&xhci->bus);
|
||||
}
|
||||
|
||||
static int usb_xhci_post_load(void *opaque, int version_id)
|
||||
{
|
||||
XHCIState *xhci = opaque;
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
XHCISlot *slot;
|
||||
XHCIEPContext *epctx;
|
||||
dma_addr_t dcbaap, pctx;
|
||||
uint32_t slot_ctx[4];
|
||||
uint32_t ep_ctx[5];
|
||||
int slotid, epid, state, intr;
|
||||
int slotid, epid, state;
|
||||
|
||||
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
|
||||
|
||||
|
@ -3559,15 +3456,6 @@ static int usb_xhci_post_load(void *opaque, int version_id)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (intr = 0; intr < xhci->numintrs; intr++) {
|
||||
if (xhci->intr[intr].msix_used) {
|
||||
msix_vector_use(pci_dev, intr);
|
||||
} else {
|
||||
msix_vector_unuse(pci_dev, intr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3652,14 +3540,11 @@ static const VMStateDescription vmstate_xhci_intr = {
|
|||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_xhci = {
|
||||
.name = "xhci",
|
||||
const VMStateDescription vmstate_xhci = {
|
||||
.name = "xhci-core",
|
||||
.version_id = 1,
|
||||
.post_load = usb_xhci_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, XHCIState),
|
||||
VMSTATE_MSIX(parent_obj, XHCIState),
|
||||
|
||||
VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1,
|
||||
vmstate_xhci_port, XHCIPort),
|
||||
VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1,
|
||||
|
@ -3691,42 +3576,27 @@ static Property xhci_properties[] = {
|
|||
XHCI_FLAG_ENABLE_STREAMS, true),
|
||||
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
|
||||
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
|
||||
DEFINE_PROP_LINK("host", XHCIState, hostOpaque, TYPE_DEVICE,
|
||||
DeviceState *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void xhci_instance_init(Object *obj)
|
||||
{
|
||||
/* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
|
||||
* line, therefore, no need to wait to realize like other devices */
|
||||
PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
|
||||
}
|
||||
|
||||
static void xhci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_xhci;
|
||||
device_class_set_props(dc, xhci_properties);
|
||||
dc->realize = usb_xhci_realize;
|
||||
dc->unrealize = usb_xhci_unrealize;
|
||||
dc->reset = xhci_reset;
|
||||
set_bit(DEVICE_CATEGORY_USB, dc->categories);
|
||||
k->realize = usb_xhci_realize;
|
||||
k->exit = usb_xhci_exit;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
device_class_set_props(dc, xhci_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo xhci_info = {
|
||||
.name = TYPE_XHCI,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(XHCIState),
|
||||
.class_init = xhci_class_init,
|
||||
.instance_init = xhci_instance_init,
|
||||
.abstract = true,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void xhci_register_types(void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue