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:
Sai Pavan Boddu 2020-09-24 19:50:52 +05:30 committed by Gerd Hoffmann
parent 755fba11fb
commit 8ddab8dd3d
5 changed files with 303 additions and 193 deletions

View File

@ -25,17 +25,17 @@
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "hcd-xhci.h" #include "hcd-xhci-pci.h"
static Property nec_xhci_properties[] = { static Property nec_xhci_properties[] = {
DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
DEFINE_PROP_BIT("superspeed-ports-first", DEFINE_PROP_BIT("superspeed-ports-first", XHCIPciState,
XHCIState, flags, XHCI_FLAG_SS_FIRST, true), xhci.flags, XHCI_FLAG_SS_FIRST, true),
DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, DEFINE_PROP_BIT("force-pcie-endcap", XHCIPciState, xhci.flags,
XHCI_FLAG_FORCE_PCIE_ENDCAP, false), XHCI_FLAG_FORCE_PCIE_ENDCAP, false),
DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), DEFINE_PROP_UINT32("intrs", XHCIPciState, xhci.numintrs, MAXINTRS),
DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), DEFINE_PROP_UINT32("slots", XHCIPciState, xhci.numslots, MAXSLOTS),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };
@ -52,7 +52,7 @@ static void nec_xhci_class_init(ObjectClass *klass, void *data)
static const TypeInfo nec_xhci_info = { static const TypeInfo nec_xhci_info = {
.name = TYPE_NEC_XHCI, .name = TYPE_NEC_XHCI,
.parent = TYPE_XHCI, .parent = TYPE_XHCI_PCI,
.class_init = nec_xhci_class_init, .class_init = nec_xhci_class_init,
}; };

View File

@ -25,12 +25,205 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "hw/pci/msi.h" #include "hw/pci/msi.h"
#include "hw/pci/msix.h" #include "hw/pci/msix.h"
#include "hcd-xhci.h" #include "hcd-xhci-pci.h"
#include "trace.h" #include "trace.h"
#include "qapi/error.h" #include "qapi/error.h"
#define OFF_MSIX_TABLE 0x3000
#define OFF_MSIX_PBA 0x3800
static void xhci_pci_intr_update(XHCIState *xhci, int n, bool enable)
{
XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
PCIDevice *pci_dev = PCI_DEVICE(s);
if (!msix_enabled(pci_dev)) {
return;
}
if (enable == !!xhci->intr[n].msix_used) {
return;
}
if (enable) {
trace_usb_xhci_irq_msix_use(n);
msix_vector_use(pci_dev, n);
xhci->intr[n].msix_used = true;
} else {
trace_usb_xhci_irq_msix_unuse(n);
msix_vector_unuse(pci_dev, n);
xhci->intr[n].msix_used = false;
}
}
static void xhci_pci_intr_raise(XHCIState *xhci, int n, bool level)
{
XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
PCIDevice *pci_dev = PCI_DEVICE(s);
if (n == 0 &&
!(msix_enabled(pci_dev) ||
msi_enabled(pci_dev))) {
pci_set_irq(pci_dev, level);
}
if (msix_enabled(pci_dev)) {
msix_notify(pci_dev, n);
return;
}
if (msi_enabled(pci_dev)) {
msi_notify(pci_dev, n);
return;
}
}
static void xhci_pci_reset(DeviceState *dev)
{
XHCIPciState *s = XHCI_PCI(dev);
device_legacy_reset(DEVICE(&s->xhci));
}
static int xhci_pci_vmstate_post_load(void *opaque, int version_id)
{
XHCIPciState *s = XHCI_PCI(opaque);
PCIDevice *pci_dev = PCI_DEVICE(s);
int intr;
for (intr = 0; intr < s->xhci.numintrs; intr++) {
if (s->xhci.intr[intr].msix_used) {
msix_vector_use(pci_dev, intr);
} else {
msix_vector_unuse(pci_dev, intr);
}
}
return 0;
}
static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
{
int ret;
Error *err = NULL;
XHCIPciState *s = XHCI_PCI(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 */
object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL);
s->xhci.intr_update = xhci_pci_intr_update;
s->xhci.intr_raise = xhci_pci_intr_raise;
object_property_set_bool(OBJECT(&s->xhci), "realized", true, &err);
if (err) {
error_propagate(errp, err);
return;
}
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
s->xhci.nec_quirks = true;
}
if (s->msi != ON_OFF_AUTO_OFF) {
ret = msi_init(dev, 0x70, s->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 && s->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 || s->msi == ON_OFF_AUTO_AUTO);
/* With msi=auto, we fall back to MSI off silently */
error_free(err);
}
pci_register_bar(dev, 0,
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&s->xhci.mem);
if (pci_bus_is_express(pci_get_bus(dev)) ||
xhci_get_flag(&s->xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) {
ret = pcie_endpoint_cap_init(dev, 0xa0);
assert(ret > 0);
}
if (s->msix != ON_OFF_AUTO_OFF) {
/* TODO check for errors, and should fail when msix=on */
msix_init(dev, s->xhci.numintrs,
&s->xhci.mem, 0, OFF_MSIX_TABLE,
&s->xhci.mem, 0, OFF_MSIX_PBA,
0x90, NULL);
}
s->xhci.as = pci_get_address_space(dev);
}
static void usb_xhci_pci_exit(PCIDevice *dev)
{
XHCIPciState *s = XHCI_PCI(dev);
/* destroy msix memory region */
if (dev->msix_table && dev->msix_pba
&& dev->msix_entry_used) {
msix_uninit(dev, &s->xhci.mem, &s->xhci.mem);
}
}
static const VMStateDescription vmstate_xhci_pci = {
.name = "xhci",
.version_id = 1,
.post_load = xhci_pci_vmstate_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, XHCIPciState),
VMSTATE_MSIX(parent_obj, XHCIPciState),
VMSTATE_STRUCT(xhci, XHCIPciState, 1, vmstate_xhci, XHCIState),
VMSTATE_END_OF_LIST()
}
};
static void xhci_instance_init(Object *obj)
{
XHCIPciState *s = XHCI_PCI(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;
object_initialize_child(obj, "xhci-core", &s->xhci, TYPE_XHCI);
qdev_alias_all_properties(DEVICE(&s->xhci), obj);
}
static void xhci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = xhci_pci_reset;
dc->vmsd = &vmstate_xhci_pci;
set_bit(DEVICE_CATEGORY_USB, dc->categories);
k->realize = usb_xhci_pci_realize;
k->exit = usb_xhci_pci_exit;
k->class_id = PCI_CLASS_SERIAL_USB;
}
static const TypeInfo xhci_pci_info = {
.name = TYPE_XHCI_PCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XHCIPciState),
.class_init = xhci_class_init,
.instance_init = xhci_instance_init,
.abstract = true,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_PCIE_DEVICE },
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ }
},
};
static void qemu_xhci_class_init(ObjectClass *klass, void *data) static void qemu_xhci_class_init(ObjectClass *klass, void *data)
{ {
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@ -42,10 +235,11 @@ static void qemu_xhci_class_init(ObjectClass *klass, void *data)
static void qemu_xhci_instance_init(Object *obj) static void qemu_xhci_instance_init(Object *obj)
{ {
XHCIState *xhci = XHCI(obj); XHCIPciState *s = XHCI_PCI(obj);
XHCIState *xhci = &s->xhci;
xhci->msi = ON_OFF_AUTO_OFF; s->msi = ON_OFF_AUTO_OFF;
xhci->msix = ON_OFF_AUTO_AUTO; s->msix = ON_OFF_AUTO_AUTO;
xhci->numintrs = MAXINTRS; xhci->numintrs = MAXINTRS;
xhci->numslots = MAXSLOTS; xhci->numslots = MAXSLOTS;
xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST); xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST);
@ -53,13 +247,14 @@ static void qemu_xhci_instance_init(Object *obj)
static const TypeInfo qemu_xhci_info = { static const TypeInfo qemu_xhci_info = {
.name = TYPE_QEMU_XHCI, .name = TYPE_QEMU_XHCI,
.parent = TYPE_XHCI, .parent = TYPE_XHCI_PCI,
.class_init = qemu_xhci_class_init, .class_init = qemu_xhci_class_init,
.instance_init = qemu_xhci_instance_init, .instance_init = qemu_xhci_instance_init,
}; };
static void xhci_register_types(void) static void xhci_register_types(void)
{ {
type_register_static(&xhci_pci_info);
type_register_static(&qemu_xhci_info); type_register_static(&qemu_xhci_info);
} }

44
hw/usb/hcd-xhci-pci.h Normal file
View File

@ -0,0 +1,44 @@
/*
* USB xHCI controller emulation
*
* Copyright (c) 2011 Securiforest
* Date: 2011-05-11 ; Author: Hector Martin <hector@marcansoft.com>
* Based on usb-ohci.c, emulates Renesas NEC USB 3.0
* Date: 2020-01-1; Author: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
* PCI hooks are moved from XHCIState to XHCIPciState
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_USB_HCD_XHCI_PCI_H
#define HW_USB_HCD_XHCI_PCI_H
#include "hw/usb.h"
#include "hcd-xhci.h"
#define TYPE_XHCI_PCI "pci-xhci"
#define XHCI_PCI(obj) \
OBJECT_CHECK(XHCIPciState, (obj), TYPE_XHCI_PCI)
typedef struct XHCIPciState {
/*< private >*/
PCIDevice parent_obj;
/*< public >*/
XHCIState xhci;
OnOffAuto msi;
OnOffAuto msix;
} XHCIPciState;
#endif

View File

@ -24,10 +24,7 @@
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/queue.h" #include "qemu/queue.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "trace.h" #include "trace.h"
#include "qapi/error.h" #include "qapi/error.h"
@ -56,8 +53,6 @@
#define OFF_OPER LEN_CAP #define OFF_OPER LEN_CAP
#define OFF_RUNTIME 0x1000 #define OFF_RUNTIME 0x1000
#define OFF_DOORBELL 0x2000 #define OFF_DOORBELL 0x2000
#define OFF_MSIX_TABLE 0x3000
#define OFF_MSIX_PBA 0x3800
/* must be power of 2 */ /* must be power of 2 */
#define LEN_REGS 0x4000 #define LEN_REGS 0x4000
@ -547,54 +542,28 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
return &xhci->ports[index]; 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; int level = 0;
if (msix_enabled(pci_dev) || if (v == 0) {
msi_enabled(pci_dev)) {
return;
}
if (xhci->intr[0].iman & IMAN_IP && if (xhci->intr[0].iman & IMAN_IP &&
xhci->intr[0].iman & IMAN_IE && xhci->intr[0].iman & IMAN_IE &&
xhci->usbcmd & USBCMD_INTE) { xhci->usbcmd & USBCMD_INTE) {
level = 1; level = 1;
} }
if (xhci->intr_raise) {
trace_usb_xhci_irq_intx(level); xhci->intr_raise(xhci, 0, 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 (xhci->intr_update) {
if (enabled) { xhci->intr_update(xhci, v,
trace_usb_xhci_irq_msix_use(v); xhci->intr[v].iman & IMAN_IE);
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;
} }
} }
static void xhci_intr_raise(XHCIState *xhci, int v) static void xhci_intr_raise(XHCIState *xhci, int v)
{ {
PCIDevice *pci_dev = PCI_DEVICE(xhci);
bool pending = (xhci->intr[v].erdp_low & ERDP_EHB); bool pending = (xhci->intr[v].erdp_low & ERDP_EHB);
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)) { if (!(xhci->usbcmd & USBCMD_INTE)) {
return; return;
} }
if (xhci->intr_raise) {
if (msix_enabled(pci_dev)) { xhci->intr_raise(xhci, v, true);
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);
} }
} }
@ -2717,7 +2672,6 @@ static void xhci_reset(DeviceState *dev)
xhci->intr[i].erstba_high = 0; xhci->intr[i].erstba_high = 0;
xhci->intr[i].erdp_low = 0; xhci->intr[i].erdp_low = 0;
xhci->intr[i].erdp_high = 0; xhci->intr[i].erdp_high = 0;
xhci->intr[i].msix_used = 0;
xhci->intr[i].er_ep_idx = 0; xhci->intr[i].er_ep_idx = 0;
xhci->intr[i].er_pcs = 1; 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, static void xhci_oper_write(void *ptr, hwaddr reg,
uint64_t val, unsigned size) uint64_t val, unsigned size)
{ {
XHCIState *xhci = ptr; XHCIState *xhci = XHCI(ptr);
DeviceState *d = DEVICE(ptr);
trace_usb_xhci_oper_write(reg, val); 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->usbcmd = val & 0xc0f;
xhci_mfwrap_update(xhci); xhci_mfwrap_update(xhci);
if (val & USBCMD_HCRST) { if (val & USBCMD_HCRST) {
xhci_reset(d); xhci_reset(DEVICE(xhci));
} }
xhci_intx_update(xhci); xhci_intr_update(xhci, 0);
break; break;
case 0x04: /* USBSTS */ case 0x04: /* USBSTS */
/* these bits are write-1-to-clear */ /* these bits are write-1-to-clear */
xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE)); xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
xhci_intx_update(xhci); xhci_intr_update(xhci, 0);
break; break;
case 0x14: /* DNCTRL */ case 0x14: /* DNCTRL */
@ -3073,10 +3026,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
} }
intr->iman &= ~IMAN_IE; intr->iman &= ~IMAN_IE;
intr->iman |= val & IMAN_IE; intr->iman |= val & IMAN_IE;
if (v == 0) { xhci_intr_update(xhci, v);
xhci_intx_update(xhci);
}
xhci_msix_update(xhci, v);
break; break;
case 0x04: /* IMOD */ case 0x04: /* IMOD */
intr->imod = val; intr->imod = val;
@ -3321,7 +3271,6 @@ static USBBusOps xhci_bus_ops = {
static void usb_xhci_init(XHCIState *xhci) static void usb_xhci_init(XHCIState *xhci)
{ {
DeviceState *dev = DEVICE(xhci);
XHCIPort *port; XHCIPort *port;
unsigned int i, usbports, speedmask; unsigned int i, usbports, speedmask;
@ -3336,7 +3285,7 @@ static void usb_xhci_init(XHCIState *xhci)
usbports = MAX(xhci->numports_2, xhci->numports_3); usbports = MAX(xhci->numports_2, xhci->numports_3);
xhci->numports = 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++) { for (i = 0; i < usbports; i++) {
speedmask = 0; 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; int i;
Error *err = NULL;
XHCIState *xhci = XHCI(dev); 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) { if (xhci->numintrs > MAXINTRS) {
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; 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); usb_xhci_init(xhci);
xhci->as = pci_get_address_space(dev);
xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); 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(&xhci->mem, OBJECT(dev), "xhci", LEN_REGS);
memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci, memory_region_init_io(&xhci->mem_cap, OBJECT(dev), &xhci_cap_ops, xhci,
"capabilities", LEN_CAP); "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); "operational", 0x400);
memory_region_init_io(&xhci->mem_runtime, OBJECT(xhci), &xhci_runtime_ops, xhci, memory_region_init_io(&xhci->mem_runtime, OBJECT(dev), &xhci_runtime_ops,
"runtime", LEN_RUNTIME); xhci, "runtime", LEN_RUNTIME);
memory_region_init_io(&xhci->mem_doorbell, OBJECT(xhci), &xhci_doorbell_ops, xhci, memory_region_init_io(&xhci->mem_doorbell, OBJECT(dev), &xhci_doorbell_ops,
"doorbell", LEN_DOORBELL); xhci, "doorbell", LEN_DOORBELL);
memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap); memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap);
memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper); 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]; XHCIPort *port = &xhci->ports[i];
uint32_t offset = OFF_OPER + 0x400 + 0x10 * i; uint32_t offset = OFF_OPER + 0x400 + 0x10 * i;
port->xhci = xhci; 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); port->name, 0x10);
memory_region_add_subregion(&xhci->mem, offset, &port->mem); 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; int i;
XHCIState *xhci = XHCI(dev); XHCIState *xhci = XHCI(dev);
@ -3503,25 +3407,18 @@ static void usb_xhci_exit(PCIDevice *dev)
memory_region_del_subregion(&xhci->mem, &port->mem); 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); usb_bus_release(&xhci->bus);
} }
static int usb_xhci_post_load(void *opaque, int version_id) static int usb_xhci_post_load(void *opaque, int version_id)
{ {
XHCIState *xhci = opaque; XHCIState *xhci = opaque;
PCIDevice *pci_dev = PCI_DEVICE(xhci);
XHCISlot *slot; XHCISlot *slot;
XHCIEPContext *epctx; XHCIEPContext *epctx;
dma_addr_t dcbaap, pctx; dma_addr_t dcbaap, pctx;
uint32_t slot_ctx[4]; uint32_t slot_ctx[4];
uint32_t ep_ctx[5]; uint32_t ep_ctx[5];
int slotid, epid, state, intr; int slotid, epid, state;
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); 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; return 0;
} }
@ -3652,14 +3540,11 @@ static const VMStateDescription vmstate_xhci_intr = {
} }
}; };
static const VMStateDescription vmstate_xhci = { const VMStateDescription vmstate_xhci = {
.name = "xhci", .name = "xhci-core",
.version_id = 1, .version_id = 1,
.post_load = usb_xhci_post_load, .post_load = usb_xhci_post_load,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, XHCIState),
VMSTATE_MSIX(parent_obj, XHCIState),
VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1, VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1,
vmstate_xhci_port, XHCIPort), vmstate_xhci_port, XHCIPort),
VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1, VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1,
@ -3691,42 +3576,27 @@ static Property xhci_properties[] = {
XHCI_FLAG_ENABLE_STREAMS, true), XHCI_FLAG_ENABLE_STREAMS, true),
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
DEFINE_PROP_LINK("host", XHCIState, hostOpaque, TYPE_DEVICE,
DeviceState *),
DEFINE_PROP_END_OF_LIST(), 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) static void xhci_class_init(ObjectClass *klass, void *data)
{ {
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_xhci; dc->realize = usb_xhci_realize;
device_class_set_props(dc, xhci_properties); dc->unrealize = usb_xhci_unrealize;
dc->reset = xhci_reset; dc->reset = xhci_reset;
set_bit(DEVICE_CATEGORY_USB, dc->categories); device_class_set_props(dc, xhci_properties);
k->realize = usb_xhci_realize; dc->user_creatable = false;
k->exit = usb_xhci_exit;
k->class_id = PCI_CLASS_SERIAL_USB;
} }
static const TypeInfo xhci_info = { static const TypeInfo xhci_info = {
.name = TYPE_XHCI, .name = TYPE_XHCI,
.parent = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE,
.instance_size = sizeof(XHCIState), .instance_size = sizeof(XHCIState),
.class_init = xhci_class_init, .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) static void xhci_register_types(void)

View File

@ -24,6 +24,7 @@
#include "qom/object.h" #include "qom/object.h"
#include "hw/usb.h" #include "hw/usb.h"
#include "sysemu/dma.h"
#define TYPE_XHCI "base-xhci" #define TYPE_XHCI "base-xhci"
#define TYPE_NEC_XHCI "nec-usb-xhci" #define TYPE_NEC_XHCI "nec-usb-xhci"
@ -183,10 +184,8 @@ typedef struct XHCIInterrupter {
} XHCIInterrupter; } XHCIInterrupter;
struct XHCIState { typedef struct XHCIState {
/*< private >*/ DeviceState parent;
PCIDevice parent_obj;
/*< public >*/
USBBus bus; USBBus bus;
MemoryRegion mem; MemoryRegion mem;
@ -203,8 +202,9 @@ struct XHCIState {
uint32_t numslots; uint32_t numslots;
uint32_t flags; uint32_t flags;
uint32_t max_pstreams_mask; uint32_t max_pstreams_mask;
OnOffAuto msi; void (*intr_update)(XHCIState *s, int n, bool enable);
OnOffAuto msix; void (*intr_raise)(XHCIState *s, int n, bool level);
DeviceState *hostOpaque;
/* Operational Registers */ /* Operational Registers */
uint32_t usbcmd; uint32_t usbcmd;
@ -229,8 +229,9 @@ struct XHCIState {
XHCIRing cmd_ring; XHCIRing cmd_ring;
bool nec_quirks; bool nec_quirks;
}; } XHCIState;
extern const VMStateDescription vmstate_xhci;
bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit); bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit);
void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit); void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit);
#endif #endif