123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /*
- * 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.1 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 bool 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) && level) {
- msix_notify(pci_dev, n);
- return true;
- }
- if (msi_enabled(pci_dev) && level) {
- n %= msi_nr_vectors_allocated(pci_dev);
- msi_notify(pci_dev, n);
- return true;
- }
- return false;
- }
- static bool xhci_pci_intr_mapping_conditional(XHCIState *xhci)
- {
- XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
- PCIDevice *pci_dev = PCI_DEVICE(s);
- /*
- * Implementation of the "conditional-intr-mapping" property, which only
- * enables interrupter mapping if MSI or MSI-X is available and active.
- * Forces all events onto interrupter/event ring 0 in pin-based IRQ mode.
- * Provides compatibility with macOS guests on machine types where MSI(-X)
- * is not available.
- */
- return msix_enabled(pci_dev) || msi_enabled(pci_dev);
- }
- static void xhci_pci_reset(DeviceState *dev)
- {
- XHCIPciState *s = XHCI_PCI(dev);
- device_cold_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;
- if (s->conditional_intr_mapping) {
- s->xhci.intr_mapping_supported = xhci_pci_intr_mapping_conditional;
- }
- if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) {
- 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))) {
- 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 = (const 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 const Property xhci_pci_properties[] = {
- 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_BOOL("conditional-intr-mapping", XHCIPciState,
- conditional_intr_mapping, false),
- };
- static void xhci_class_init(ObjectClass *klass, void *data)
- {
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
- device_class_set_legacy_reset(dc, 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;
- device_class_set_props(dc, xhci_pci_properties);
- object_class_property_set_description(klass, "conditional-intr-mapping",
- "When true, disables interrupter mapping for pin-based IRQ mode. "
- "Intended to be used with guest drivers with questionable behaviour, "
- "such as macOS's.");
- }
- 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 = XHCI_MAXINTRS;
- xhci->numslots = XHCI_MAXSLOTS;
- }
- 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)
|