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:
		
				
					committed by
					
						 Gerd Hoffmann
						Gerd Hoffmann
					
				
			
			
				
	
			
			
			
						parent
						
							755fba11fb
						
					
				
				
					commit
					8ddab8dd3d
				
			| @@ -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, | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -25,12 +25,205 @@ | ||||
| #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.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); | ||||
| @@ -42,10 +235,11 @@ static void qemu_xhci_class_init(ObjectClass *klass, void *data) | ||||
|  | ||||
| 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; | ||||
|     xhci->msix     = ON_OFF_AUTO_AUTO; | ||||
|     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); | ||||
| @@ -53,13 +247,14 @@ static void qemu_xhci_instance_init(Object *obj) | ||||
|  | ||||
| static const TypeInfo qemu_xhci_info = { | ||||
|     .name          = TYPE_QEMU_XHCI, | ||||
|     .parent        = TYPE_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); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										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 | ||||
| @@ -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) | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "qom/object.h" | ||||
|  | ||||
| #include "hw/usb.h" | ||||
| #include "sysemu/dma.h" | ||||
|  | ||||
| #define TYPE_XHCI "base-xhci" | ||||
| #define TYPE_NEC_XHCI "nec-usb-xhci" | ||||
| @@ -183,10 +184,8 @@ typedef struct XHCIInterrupter { | ||||
|  | ||||
| } XHCIInterrupter; | ||||
|  | ||||
| struct XHCIState { | ||||
|     /*< private >*/ | ||||
|     PCIDevice parent_obj; | ||||
|     /*< public >*/ | ||||
| typedef struct XHCIState { | ||||
|     DeviceState parent; | ||||
|  | ||||
|     USBBus bus; | ||||
|     MemoryRegion mem; | ||||
| @@ -203,8 +202,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; | ||||
| @@ -229,8 +229,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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user