usb: xhci sysbus support.
usb: use lock guards. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABCgAGBQJfceImAAoJEEy22O7T6HE4aW8QAL5YWzVvPiuTaCXv2v9Ol+W4 P+hQff+YRmQr6uf8Jh2UQc88NRNGs4opPQ4Sl/aFNHb3f79sL1mEb/7LafshFZnf PODH7DhOWYHWNXX+/t5K0DXbX/nEvsMjNTeMRoZk0xOeIw3THiU4pohZuMZojvK8 ih/OUyCs88+pc8Zzp2MeqGe9rDbb7YGeVHR6DZQdZw7nEBcs/f8SITEaNhoczcl0 CSpY2bEdNegM9IDyhJsnFVuE+hLBhgPxCjSs9byhfOb5I15kpw2GqZS9I6iL7E6T QpUfnH3UVUgiR+820/3h1jO9mJgsF0ORrOMtVqTPSShBgqcvhfCcIjL0RDSTXpFe HyATsM+wo9Vlo8Rsmf7sIskojG4eC88PuMsMo7Fi4YSCSzlulLRY9O01SswC/GOG KrGPoNuO83QYkpO9k+25t5obSr5jnP2HGCXjnu3qnuiMNajUf3oSB/eVo8QJg0pO QFMuYcOyzYXFhAy4iQyKVRnq1vY44bSdkwR3tVPCPQhz2DBk7rvJAJaM+RtVRpiE rQdeD8xL01FfjaFoBjAWbJPH7Ql0QqpUC0/zvoRS14L9Jc/OW8ywezlT7qQSumo1 6HsDOwaKp/isx4rnDP7vr/T1/ZXVZpogDgl9aF61J0FXhasWiOTvV/jkrNWhKj0T 71r67RObAhdCPZaKi95b =L9cN -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/usb-20200928-pull-request' into staging usb: xhci sysbus support. usb: use lock guards. # gpg: Signature made Mon 28 Sep 2020 14:16:22 BST # gpg: using RSA key 4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/usb-20200928-pull-request: hw/usb: Use lock guard macros usb: hcd-xhci-sysbus: Attach xhci to sysbus device usb/hcd-xhci: Split pci wrapper for xhci base model usb/hcd-xhci: Move qemu-xhci device to hcd-xhci-pci.c usb/hcd-xhci: Make dma read/writes hooks pci free Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1553d543ff
@ -36,12 +36,23 @@ config USB_XHCI
|
||||
depends on PCI
|
||||
select USB
|
||||
|
||||
config USB_XHCI_PCI
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI
|
||||
select USB_XHCI
|
||||
|
||||
config USB_XHCI_NEC
|
||||
bool
|
||||
default y if PCI_DEVICES
|
||||
depends on PCI
|
||||
select USB_XHCI
|
||||
|
||||
config USB_XHCI_SYSBUS
|
||||
bool
|
||||
default y if USB_XHCI
|
||||
select USB
|
||||
|
||||
config USB_MUSB
|
||||
bool
|
||||
select USB
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <libcacard.h>
|
||||
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "ccid.h"
|
||||
@ -244,7 +245,7 @@ static void *handle_apdu_thread(void* arg)
|
||||
card->quit_apdu_thread = 0; /* debugging */
|
||||
break;
|
||||
}
|
||||
qemu_mutex_lock(&card->vreader_mutex);
|
||||
WITH_QEMU_LOCK_GUARD(&card->vreader_mutex) {
|
||||
while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
|
||||
event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
|
||||
assert((unsigned long)event > 1000);
|
||||
@ -271,7 +272,7 @@ static void *handle_apdu_thread(void* arg)
|
||||
}
|
||||
g_free(event);
|
||||
}
|
||||
qemu_mutex_unlock(&card->vreader_mutex);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -365,7 +366,7 @@ static void card_event_handler(EventNotifier *notifier)
|
||||
EmulEvent *event, *next;
|
||||
|
||||
event_notifier_test_and_clear(&card->notifier);
|
||||
qemu_mutex_lock(&card->event_list_mutex);
|
||||
QEMU_LOCK_GUARD(&card->event_list_mutex);
|
||||
QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
|
||||
DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
|
||||
switch (event->p.gen.type) {
|
||||
@ -398,7 +399,6 @@ static void card_event_handler(EventNotifier *notifier)
|
||||
g_free(event);
|
||||
}
|
||||
QSIMPLEQ_INIT(&card->event_list);
|
||||
qemu_mutex_unlock(&card->event_list_mutex);
|
||||
}
|
||||
|
||||
static int init_event_notifier(EmulatedState *card, Error **errp)
|
||||
|
@ -25,17 +25,17 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
#include "hcd-xhci.h"
|
||||
#include "hcd-xhci-pci.h"
|
||||
|
||||
static Property nec_xhci_properties[] = {
|
||||
DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_BIT("superspeed-ports-first",
|
||||
XHCIState, flags, XHCI_FLAG_SS_FIRST, true),
|
||||
DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags,
|
||||
DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_BIT("superspeed-ports-first", XHCIPciState,
|
||||
xhci.flags, XHCI_FLAG_SS_FIRST, true),
|
||||
DEFINE_PROP_BIT("force-pcie-endcap", XHCIPciState, xhci.flags,
|
||||
XHCI_FLAG_FORCE_PCIE_ENDCAP, false),
|
||||
DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS),
|
||||
DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS),
|
||||
DEFINE_PROP_UINT32("intrs", XHCIPciState, xhci.numintrs, MAXINTRS),
|
||||
DEFINE_PROP_UINT32("slots", XHCIPciState, xhci.numslots, MAXSLOTS),
|
||||
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 = {
|
||||
.name = TYPE_NEC_XHCI,
|
||||
.parent = TYPE_XHCI,
|
||||
.parent = TYPE_XHCI_PCI,
|
||||
.class_init = nec_xhci_class_init,
|
||||
};
|
||||
|
||||
|
261
hw/usb/hcd-xhci-pci.c
Normal file
261
hw/usb/hcd-xhci-pci.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* USB xHCI controller with PCI bus emulation
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2011 Securiforest
|
||||
* SPDX-FileContributor: Hector Martin <hector@marcansoft.com>
|
||||
* SPDX-sourceInfo: Based on usb-ohci.c, emulates Renesas NEC USB 3.0
|
||||
* SPDX-FileCopyrightText: 2020 Xilinx
|
||||
* SPDX-FileContributor: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
|
||||
* SPDX-sourceInfo: Moved the pci specific content for hcd-xhci.c to
|
||||
* hcd-xhci-pci.c
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hcd-xhci-pci.h"
|
||||
#include "trace.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)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_XHCI;
|
||||
k->revision = 0x01;
|
||||
}
|
||||
|
||||
static void qemu_xhci_instance_init(Object *obj)
|
||||
{
|
||||
XHCIPciState *s = XHCI_PCI(obj);
|
||||
XHCIState *xhci = &s->xhci;
|
||||
|
||||
s->msi = ON_OFF_AUTO_OFF;
|
||||
s->msix = ON_OFF_AUTO_AUTO;
|
||||
xhci->numintrs = MAXINTRS;
|
||||
xhci->numslots = MAXSLOTS;
|
||||
xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST);
|
||||
}
|
||||
|
||||
static const TypeInfo qemu_xhci_info = {
|
||||
.name = TYPE_QEMU_XHCI,
|
||||
.parent = TYPE_XHCI_PCI,
|
||||
.class_init = qemu_xhci_class_init,
|
||||
.instance_init = qemu_xhci_instance_init,
|
||||
};
|
||||
|
||||
static void xhci_register_types(void)
|
||||
{
|
||||
type_register_static(&xhci_pci_info);
|
||||
type_register_static(&qemu_xhci_info);
|
||||
}
|
||||
|
||||
type_init(xhci_register_types)
|
44
hw/usb/hcd-xhci-pci.h
Normal file
44
hw/usb/hcd-xhci-pci.h
Normal 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
|
109
hw/usb/hcd-xhci-sysbus.c
Normal file
109
hw/usb/hcd-xhci-sysbus.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* USB xHCI controller for system-bus interface
|
||||
* Based on hcd-echi-sysbus.c
|
||||
|
||||
* SPDX-FileCopyrightText: 2020 Xilinx
|
||||
* SPDX-FileContributor: Author: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hcd-xhci-sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
static void xhci_sysbus_intr_raise(XHCIState *xhci, int n, bool level)
|
||||
{
|
||||
XHCISysbusState *s = container_of(xhci, XHCISysbusState, xhci);
|
||||
|
||||
qemu_set_irq(s->irq[n], level);
|
||||
}
|
||||
|
||||
void xhci_sysbus_reset(DeviceState *dev)
|
||||
{
|
||||
XHCISysbusState *s = XHCI_SYSBUS(dev);
|
||||
|
||||
device_legacy_reset(DEVICE(&s->xhci));
|
||||
}
|
||||
|
||||
static void xhci_sysbus_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XHCISysbusState *s = XHCI_SYSBUS(dev);
|
||||
Error *err = NULL;
|
||||
|
||||
object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL);
|
||||
object_property_set_bool(OBJECT(&s->xhci), "realized", true, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
s->irq = g_new0(qemu_irq, s->xhci.numintrs);
|
||||
qdev_init_gpio_out_named(dev, s->irq, SYSBUS_DEVICE_GPIO_IRQ,
|
||||
s->xhci.numintrs);
|
||||
if (s->xhci.dma_mr) {
|
||||
s->xhci.as = g_malloc0(sizeof(AddressSpace));
|
||||
address_space_init(s->xhci.as, s->xhci.dma_mr, NULL);
|
||||
} else {
|
||||
s->xhci.as = &address_space_memory;
|
||||
}
|
||||
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->xhci.mem);
|
||||
}
|
||||
|
||||
static void xhci_sysbus_instance_init(Object *obj)
|
||||
{
|
||||
XHCISysbusState *s = XHCI_SYSBUS(obj);
|
||||
|
||||
object_initialize_child(obj, "xhci-core", &s->xhci, TYPE_XHCI);
|
||||
qdev_alias_all_properties(DEVICE(&s->xhci), obj);
|
||||
|
||||
object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
|
||||
(Object **)&s->xhci.dma_mr,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
OBJ_PROP_LINK_STRONG);
|
||||
s->xhci.intr_update = NULL;
|
||||
s->xhci.intr_raise = xhci_sysbus_intr_raise;
|
||||
}
|
||||
|
||||
static Property xhci_sysbus_props[] = {
|
||||
DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, MAXINTRS),
|
||||
DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, MAXSLOTS),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_xhci_sysbus = {
|
||||
.name = "xhci-sysbus",
|
||||
.version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(xhci, XHCISysbusState, 1, vmstate_xhci, XHCIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void xhci_sysbus_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = xhci_sysbus_reset;
|
||||
dc->realize = xhci_sysbus_realize;
|
||||
dc->vmsd = &vmstate_xhci_sysbus;
|
||||
device_class_set_props(dc, xhci_sysbus_props);
|
||||
}
|
||||
|
||||
static const TypeInfo xhci_sysbus_info = {
|
||||
.name = TYPE_XHCI_SYSBUS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(XHCISysbusState),
|
||||
.class_init = xhci_sysbus_class_init,
|
||||
.instance_init = xhci_sysbus_instance_init
|
||||
};
|
||||
|
||||
static void xhci_sysbus_register_types(void)
|
||||
{
|
||||
type_register_static(&xhci_sysbus_info);
|
||||
}
|
||||
|
||||
type_init(xhci_sysbus_register_types);
|
32
hw/usb/hcd-xhci-sysbus.h
Normal file
32
hw/usb/hcd-xhci-sysbus.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* USB xHCI controller for system-bus interface
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Xilinx
|
||||
* SPDX-FileContributor: Author: Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
|
||||
* SPDX-sourceInfo: Based on hcd-echi-sysbus
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_USB_HCD_XHCI_SYSBUS_H
|
||||
#define HW_USB_HCD_XHCI_SYSBUS_H
|
||||
|
||||
#include "hw/usb.h"
|
||||
#include "hcd-xhci.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_XHCI_SYSBUS "sysbus-xhci"
|
||||
#define XHCI_SYSBUS(obj) \
|
||||
OBJECT_CHECK(XHCISysbusState, (obj), TYPE_XHCI_SYSBUS)
|
||||
|
||||
|
||||
typedef struct XHCISysbusState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
XHCIState xhci;
|
||||
qemu_irq *irq;
|
||||
} XHCISysbusState;
|
||||
|
||||
void xhci_sysbus_reset(DeviceState *dev);
|
||||
#endif
|
@ -23,12 +23,8 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "hw/usb.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"
|
||||
|
||||
@ -57,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
|
||||
|
||||
@ -429,12 +423,12 @@ static const char *ep_state_name(uint32_t state)
|
||||
ARRAY_SIZE(ep_state_names));
|
||||
}
|
||||
|
||||
static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit)
|
||||
bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit)
|
||||
{
|
||||
return xhci->flags & (1 << bit);
|
||||
}
|
||||
|
||||
static void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit)
|
||||
void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit)
|
||||
{
|
||||
xhci->flags |= (1 << bit);
|
||||
}
|
||||
@ -495,7 +489,7 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
|
||||
assert((len % sizeof(uint32_t)) == 0);
|
||||
|
||||
pci_dma_read(PCI_DEVICE(xhci), addr, buf, len);
|
||||
dma_memory_read(xhci->as, addr, buf, len);
|
||||
|
||||
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
|
||||
buf[i] = le32_to_cpu(buf[i]);
|
||||
@ -515,7 +509,7 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
for (i = 0; i < n; i++) {
|
||||
tmp[i] = cpu_to_le32(buf[i]);
|
||||
}
|
||||
pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len);
|
||||
dma_memory_write(xhci->as, addr, tmp, len);
|
||||
}
|
||||
|
||||
static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
|
||||
@ -548,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;
|
||||
}
|
||||
|
||||
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;
|
||||
if (xhci->intr_raise) {
|
||||
xhci->intr_raise(xhci, 0, level);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -612,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,7 +598,6 @@ static void xhci_die(XHCIState *xhci)
|
||||
|
||||
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
XHCIInterrupter *intr = &xhci->intr[v];
|
||||
XHCITRB ev_trb;
|
||||
dma_addr_t addr;
|
||||
@ -663,7 +616,7 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
ev_trb.status, ev_trb.control);
|
||||
|
||||
addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
|
||||
pci_dma_write(pci_dev, addr, &ev_trb, TRB_SIZE);
|
||||
dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE);
|
||||
|
||||
intr->er_ep_idx++;
|
||||
if (intr->er_ep_idx >= intr->er_size) {
|
||||
@ -720,12 +673,11 @@ static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
|
||||
static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
|
||||
dma_addr_t *addr)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
uint32_t link_cnt = 0;
|
||||
|
||||
while (1) {
|
||||
TRBType type;
|
||||
pci_dma_read(pci_dev, ring->dequeue, trb, TRB_SIZE);
|
||||
dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE);
|
||||
trb->addr = ring->dequeue;
|
||||
trb->ccs = ring->ccs;
|
||||
le64_to_cpus(&trb->parameter);
|
||||
@ -762,7 +714,6 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
|
||||
|
||||
static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
|
||||
{
|
||||
PCIDevice *pci_dev = PCI_DEVICE(xhci);
|
||||
XHCITRB trb;
|
||||
int length = 0;
|
||||
dma_addr_t dequeue = ring->dequeue;
|
||||
@ -773,7 +724,7 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
|
||||
|
||||
while (1) {
|
||||
TRBType type;
|
||||
pci_dma_read(pci_dev, dequeue, &trb, TRB_SIZE);
|
||||
dma_memory_read(xhci->as, dequeue, &trb, TRB_SIZE);
|
||||
le64_to_cpus(&trb.parameter);
|
||||
le32_to_cpus(&trb.status);
|
||||
le32_to_cpus(&trb.control);
|
||||
@ -828,7 +779,7 @@ static void xhci_er_reset(XHCIState *xhci, int v)
|
||||
xhci_die(xhci);
|
||||
return;
|
||||
}
|
||||
pci_dma_read(PCI_DEVICE(xhci), erstba, &seg, sizeof(seg));
|
||||
dma_memory_read(xhci->as, erstba, &seg, sizeof(seg));
|
||||
le32_to_cpus(&seg.addr_low);
|
||||
le32_to_cpus(&seg.addr_high);
|
||||
le32_to_cpus(&seg.size);
|
||||
@ -1440,7 +1391,7 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer)
|
||||
int i;
|
||||
|
||||
xfer->int_req = false;
|
||||
pci_dma_sglist_init(&xfer->sgl, PCI_DEVICE(xhci), xfer->trb_count);
|
||||
qemu_sglist_init(&xfer->sgl, DEVICE(xhci), xfer->trb_count, xhci->as);
|
||||
for (i = 0; i < xfer->trb_count; i++) {
|
||||
XHCITRB *trb = &xfer->trbs[i];
|
||||
dma_addr_t addr;
|
||||
@ -2104,7 +2055,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
|
||||
poctx = ldq_le_pci_dma(PCI_DEVICE(xhci), dcbaap + 8 * slotid);
|
||||
poctx = ldq_le_dma(xhci->as, dcbaap + 8 * slotid);
|
||||
ictx = xhci_mask64(pictx);
|
||||
octx = xhci_mask64(poctx);
|
||||
|
||||
@ -2442,7 +2393,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
|
||||
/* TODO: actually implement real values here */
|
||||
bw_ctx[0] = 0;
|
||||
memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
|
||||
pci_dma_write(PCI_DEVICE(xhci), ctx, bw_ctx, sizeof(bw_ctx));
|
||||
dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx));
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
@ -2721,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;
|
||||
@ -2943,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);
|
||||
|
||||
@ -2966,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 */
|
||||
@ -3077,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;
|
||||
@ -3325,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;
|
||||
|
||||
@ -3340,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;
|
||||
@ -3380,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;
|
||||
}
|
||||
@ -3416,35 +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->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);
|
||||
@ -3455,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);
|
||||
@ -3506,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);
|
||||
|
||||
@ -3534,7 +3428,7 @@ static int usb_xhci_post_load(void *opaque, int version_id)
|
||||
continue;
|
||||
}
|
||||
slot->ctx =
|
||||
xhci_mask64(ldq_le_pci_dma(pci_dev, dcbaap + 8 * slotid));
|
||||
xhci_mask64(ldq_le_dma(xhci->as, dcbaap + 8 * slotid));
|
||||
xhci_dma_read_u32s(xhci, slot->ctx, slot_ctx, sizeof(slot_ctx));
|
||||
slot->uport = xhci_lookup_uport(xhci, slot_ctx);
|
||||
if (!slot->uport) {
|
||||
@ -3562,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;
|
||||
}
|
||||
|
||||
@ -3655,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,
|
||||
@ -3694,75 +3576,32 @@ 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 qemu_xhci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_XHCI;
|
||||
k->revision = 0x01;
|
||||
}
|
||||
|
||||
static void qemu_xhci_instance_init(Object *obj)
|
||||
{
|
||||
XHCIState *xhci = XHCI(obj);
|
||||
|
||||
xhci->msi = ON_OFF_AUTO_OFF;
|
||||
xhci->msix = ON_OFF_AUTO_AUTO;
|
||||
xhci->numintrs = MAXINTRS;
|
||||
xhci->numslots = MAXSLOTS;
|
||||
xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST);
|
||||
}
|
||||
|
||||
static const TypeInfo qemu_xhci_info = {
|
||||
.name = TYPE_QEMU_XHCI,
|
||||
.parent = TYPE_XHCI,
|
||||
.class_init = qemu_xhci_class_init,
|
||||
.instance_init = qemu_xhci_instance_init,
|
||||
};
|
||||
|
||||
static void xhci_register_types(void)
|
||||
{
|
||||
type_register_static(&xhci_info);
|
||||
type_register_static(&qemu_xhci_info);
|
||||
}
|
||||
|
||||
type_init(xhci_register_types)
|
||||
|
@ -23,6 +23,9 @@
|
||||
#define HW_USB_HCD_XHCI_H
|
||||
#include "qom/object.h"
|
||||
|
||||
#include "hw/usb.h"
|
||||
#include "sysemu/dma.h"
|
||||
|
||||
#define TYPE_XHCI "base-xhci"
|
||||
#define TYPE_NEC_XHCI "nec-usb-xhci"
|
||||
#define TYPE_QEMU_XHCI "qemu-xhci"
|
||||
@ -181,13 +184,13 @@ typedef struct XHCIInterrupter {
|
||||
|
||||
} XHCIInterrupter;
|
||||
|
||||
struct XHCIState {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
/*< public >*/
|
||||
typedef struct XHCIState {
|
||||
DeviceState parent;
|
||||
|
||||
USBBus bus;
|
||||
MemoryRegion mem;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace *as;
|
||||
MemoryRegion mem_cap;
|
||||
MemoryRegion mem_oper;
|
||||
MemoryRegion mem_runtime;
|
||||
@ -200,8 +203,9 @@ struct XHCIState {
|
||||
uint32_t numslots;
|
||||
uint32_t flags;
|
||||
uint32_t max_pstreams_mask;
|
||||
OnOffAuto msi;
|
||||
OnOffAuto msix;
|
||||
void (*intr_update)(XHCIState *s, int n, bool enable);
|
||||
void (*intr_raise)(XHCIState *s, int n, bool level);
|
||||
DeviceState *hostOpaque;
|
||||
|
||||
/* Operational Registers */
|
||||
uint32_t usbcmd;
|
||||
@ -226,6 +230,9 @@ struct XHCIState {
|
||||
XHCIRing cmd_ring;
|
||||
|
||||
bool nec_quirks;
|
||||
};
|
||||
} XHCIState;
|
||||
|
||||
extern const VMStateDescription vmstate_xhci;
|
||||
bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit);
|
||||
void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit);
|
||||
#endif
|
||||
|
@ -21,6 +21,8 @@ softmmu_ss.add(when: 'CONFIG_USB_EHCI', if_true: files('hcd-ehci.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_EHCI_PCI', if_true: files('hcd-ehci-pci.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci.c', 'hcd-ehci-sysbus.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c'))
|
||||
|
Loading…
Reference in New Issue
Block a user