18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) Copyright David Brownell 2000-2002 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/pci.h> 98c2ecf20Sopenharmony_ci#include <linux/usb.h> 108c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/io.h> 138c2ecf20Sopenharmony_ci#include <asm/irq.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 168c2ecf20Sopenharmony_ci#include <asm/machdep.h> 178c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 188c2ecf20Sopenharmony_ci#include <asm/prom.h> 198c2ecf20Sopenharmony_ci#endif 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "usb.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Coordinate handoffs between EHCI and companion controllers 288c2ecf20Sopenharmony_ci * during EHCI probing and system resume. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(companions_rwsem); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI 348c2ecf20Sopenharmony_ci#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI 358c2ecf20Sopenharmony_ci#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline int is_ohci_or_uhci(struct pci_dev *pdev) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return pdev->class == CL_OHCI || pdev->class == CL_UHCI; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_citypedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd, 438c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Iterate over PCI devices in the same slot as pdev and call fn for each */ 468c2ecf20Sopenharmony_cistatic void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd, 478c2ecf20Sopenharmony_ci companion_fn fn) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct pci_dev *companion; 508c2ecf20Sopenharmony_ci struct usb_hcd *companion_hcd; 518c2ecf20Sopenharmony_ci unsigned int slot = PCI_SLOT(pdev->devfn); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Iterate through other PCI functions in the same slot. 558c2ecf20Sopenharmony_ci * If the function's drvdata isn't set then it isn't bound to 568c2ecf20Sopenharmony_ci * a USB host controller driver, so skip it. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci companion = NULL; 598c2ecf20Sopenharmony_ci for_each_pci_dev(companion) { 608c2ecf20Sopenharmony_ci if (companion->bus != pdev->bus || 618c2ecf20Sopenharmony_ci PCI_SLOT(companion->devfn) != slot) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Companion device should be either UHCI,OHCI or EHCI host 668c2ecf20Sopenharmony_ci * controller, otherwise skip. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci if (companion->class != CL_UHCI && companion->class != CL_OHCI && 698c2ecf20Sopenharmony_ci companion->class != CL_EHCI) 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci companion_hcd = pci_get_drvdata(companion); 738c2ecf20Sopenharmony_ci if (!companion_hcd || !companion_hcd->self.root_hub) 748c2ecf20Sopenharmony_ci continue; 758c2ecf20Sopenharmony_ci fn(pdev, hcd, companion, companion_hcd); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * We're about to add an EHCI controller, which will unceremoniously grab 818c2ecf20Sopenharmony_ci * all the port connections away from its companions. To prevent annoying 828c2ecf20Sopenharmony_ci * error messages, lock the companion's root hub and gracefully unconfigure 838c2ecf20Sopenharmony_ci * it beforehand. Leave it locked until the EHCI controller is all set. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd, 868c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct usb_device *udev; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (is_ohci_or_uhci(companion)) { 918c2ecf20Sopenharmony_ci udev = companion_hcd->self.root_hub; 928c2ecf20Sopenharmony_ci usb_lock_device(udev); 938c2ecf20Sopenharmony_ci usb_set_configuration(udev, 0); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Adding the EHCI controller has either succeeded or failed. Set the 998c2ecf20Sopenharmony_ci * companion pointer accordingly, and in either case, reconfigure and 1008c2ecf20Sopenharmony_ci * unlock the root hub. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd, 1038c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct usb_device *udev; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (is_ohci_or_uhci(companion)) { 1088c2ecf20Sopenharmony_ci if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */ 1098c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "HS companion for %s\n", 1108c2ecf20Sopenharmony_ci dev_name(&companion->dev)); 1118c2ecf20Sopenharmony_ci companion_hcd->self.hs_companion = &hcd->self; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci udev = companion_hcd->self.root_hub; 1148c2ecf20Sopenharmony_ci usb_set_configuration(udev, 1); 1158c2ecf20Sopenharmony_ci usb_unlock_device(udev); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * We just added a non-EHCI controller. Find the EHCI controller to 1218c2ecf20Sopenharmony_ci * which it is a companion, and store a pointer to the bus structure. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_cistatic void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd, 1248c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) { 1278c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "FS/LS companion for %s\n", 1288c2ecf20Sopenharmony_ci dev_name(&companion->dev)); 1298c2ecf20Sopenharmony_ci hcd->self.hs_companion = &companion_hcd->self; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* We are removing an EHCI controller. Clear the companions' pointers. */ 1348c2ecf20Sopenharmony_cistatic void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd, 1358c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (is_ohci_or_uhci(companion)) 1388c2ecf20Sopenharmony_ci companion_hcd->self.hs_companion = NULL; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* An EHCI controller must wait for its companions before resuming. */ 1448c2ecf20Sopenharmony_cistatic void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, 1458c2ecf20Sopenharmony_ci struct pci_dev *companion, struct usb_hcd *companion_hcd) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (is_ohci_or_uhci(companion)) 1488c2ecf20Sopenharmony_ci device_pm_wait_for_dev(&pdev->dev, &companion->dev); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* configure so an HC device and id are always provided */ 1568c2ecf20Sopenharmony_ci/* always called with process context; sleeping is OK */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * usb_hcd_pci_probe - initialize PCI-based HCDs 1608c2ecf20Sopenharmony_ci * @dev: USB Host Controller being probed 1618c2ecf20Sopenharmony_ci * @id: pci hotplug id connecting controller to HCD framework 1628c2ecf20Sopenharmony_ci * @driver: USB HC driver handle 1638c2ecf20Sopenharmony_ci * Context: !in_interrupt() 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Allocates basic PCI resources for this USB host controller, and 1668c2ecf20Sopenharmony_ci * then invokes the start() method for the HCD associated with it 1678c2ecf20Sopenharmony_ci * through the hotplug entry's driver_data. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Store this function in the HCD's struct pci_driver as probe(). 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * Return: 0 if successful. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ciint usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id, 1748c2ecf20Sopenharmony_ci const struct hc_driver *driver) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct usb_hcd *hcd; 1778c2ecf20Sopenharmony_ci int retval; 1788c2ecf20Sopenharmony_ci int hcd_irq = 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (usb_disabled()) 1818c2ecf20Sopenharmony_ci return -ENODEV; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (!id) 1848c2ecf20Sopenharmony_ci return -EINVAL; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!driver) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (pci_enable_device(dev) < 0) 1908c2ecf20Sopenharmony_ci return -ENODEV; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * The xHCI driver has its own irq management 1948c2ecf20Sopenharmony_ci * make sure irq setup is not touched for xhci in generic hcd code 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci if ((driver->flags & HCD_MASK) < HCD_USB3) { 1978c2ecf20Sopenharmony_ci retval = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSI); 1988c2ecf20Sopenharmony_ci if (retval < 0) { 1998c2ecf20Sopenharmony_ci dev_err(&dev->dev, 2008c2ecf20Sopenharmony_ci "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", 2018c2ecf20Sopenharmony_ci pci_name(dev)); 2028c2ecf20Sopenharmony_ci retval = -ENODEV; 2038c2ecf20Sopenharmony_ci goto disable_pci; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci hcd_irq = pci_irq_vector(dev, 0); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); 2098c2ecf20Sopenharmony_ci if (!hcd) { 2108c2ecf20Sopenharmony_ci retval = -ENOMEM; 2118c2ecf20Sopenharmony_ci goto free_irq_vectors; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) && 2158c2ecf20Sopenharmony_ci driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (driver->flags & HCD_MEMORY) { 2188c2ecf20Sopenharmony_ci /* EHCI, OHCI */ 2198c2ecf20Sopenharmony_ci hcd->rsrc_start = pci_resource_start(dev, 0); 2208c2ecf20Sopenharmony_ci hcd->rsrc_len = pci_resource_len(dev, 0); 2218c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start, 2228c2ecf20Sopenharmony_ci hcd->rsrc_len, driver->description)) { 2238c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "controller already in use\n"); 2248c2ecf20Sopenharmony_ci retval = -EBUSY; 2258c2ecf20Sopenharmony_ci goto put_hcd; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci hcd->regs = devm_ioremap(&dev->dev, hcd->rsrc_start, 2288c2ecf20Sopenharmony_ci hcd->rsrc_len); 2298c2ecf20Sopenharmony_ci if (hcd->regs == NULL) { 2308c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "error mapping memory\n"); 2318c2ecf20Sopenharmony_ci retval = -EFAULT; 2328c2ecf20Sopenharmony_ci goto put_hcd; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci /* UHCI */ 2378c2ecf20Sopenharmony_ci int region; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci for (region = 0; region < PCI_STD_NUM_BARS; region++) { 2408c2ecf20Sopenharmony_ci if (!(pci_resource_flags(dev, region) & 2418c2ecf20Sopenharmony_ci IORESOURCE_IO)) 2428c2ecf20Sopenharmony_ci continue; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci hcd->rsrc_start = pci_resource_start(dev, region); 2458c2ecf20Sopenharmony_ci hcd->rsrc_len = pci_resource_len(dev, region); 2468c2ecf20Sopenharmony_ci if (devm_request_region(&dev->dev, hcd->rsrc_start, 2478c2ecf20Sopenharmony_ci hcd->rsrc_len, driver->description)) 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci if (region == PCI_ROM_RESOURCE) { 2518c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "no i/o regions available\n"); 2528c2ecf20Sopenharmony_ci retval = -EBUSY; 2538c2ecf20Sopenharmony_ci goto put_hcd; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci pci_set_master(dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Note: dev_set_drvdata must be called while holding the rwsem */ 2608c2ecf20Sopenharmony_ci if (dev->class == CL_EHCI) { 2618c2ecf20Sopenharmony_ci down_write(&companions_rwsem); 2628c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, hcd); 2638c2ecf20Sopenharmony_ci for_each_companion(dev, hcd, ehci_pre_add); 2648c2ecf20Sopenharmony_ci retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); 2658c2ecf20Sopenharmony_ci if (retval != 0) 2668c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 2678c2ecf20Sopenharmony_ci for_each_companion(dev, hcd, ehci_post_add); 2688c2ecf20Sopenharmony_ci up_write(&companions_rwsem); 2698c2ecf20Sopenharmony_ci } else { 2708c2ecf20Sopenharmony_ci down_read(&companions_rwsem); 2718c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, hcd); 2728c2ecf20Sopenharmony_ci retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); 2738c2ecf20Sopenharmony_ci if (retval != 0) 2748c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci for_each_companion(dev, hcd, non_ehci_add); 2778c2ecf20Sopenharmony_ci up_read(&companions_rwsem); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (retval != 0) 2818c2ecf20Sopenharmony_ci goto put_hcd; 2828c2ecf20Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (pci_dev_run_wake(dev)) 2858c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&dev->dev); 2868c2ecf20Sopenharmony_ci return retval; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciput_hcd: 2898c2ecf20Sopenharmony_ci usb_put_hcd(hcd); 2908c2ecf20Sopenharmony_cifree_irq_vectors: 2918c2ecf20Sopenharmony_ci if ((driver->flags & HCD_MASK) < HCD_USB3) 2928c2ecf20Sopenharmony_ci pci_free_irq_vectors(dev); 2938c2ecf20Sopenharmony_cidisable_pci: 2948c2ecf20Sopenharmony_ci pci_disable_device(dev); 2958c2ecf20Sopenharmony_ci dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); 2968c2ecf20Sopenharmony_ci return retval; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_probe); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* may be called without controller electrically present */ 3028c2ecf20Sopenharmony_ci/* may be called with controller, bus, and devices active */ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs 3068c2ecf20Sopenharmony_ci * @dev: USB Host Controller being removed 3078c2ecf20Sopenharmony_ci * Context: !in_interrupt() 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * Reverses the effect of usb_hcd_pci_probe(), first invoking 3108c2ecf20Sopenharmony_ci * the HCD's stop() method. It is always called from a thread 3118c2ecf20Sopenharmony_ci * context, normally "rmmod", "apmd", or something similar. 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * Store this function in the HCD's struct pci_driver as remove(). 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_civoid usb_hcd_pci_remove(struct pci_dev *dev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct usb_hcd *hcd; 3188c2ecf20Sopenharmony_ci int hcd_driver_flags; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci hcd = pci_get_drvdata(dev); 3218c2ecf20Sopenharmony_ci if (!hcd) 3228c2ecf20Sopenharmony_ci return; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci hcd_driver_flags = hcd->driver->flags; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (pci_dev_run_wake(dev)) 3278c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&dev->dev); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Fake an interrupt request in order to give the driver a chance 3308c2ecf20Sopenharmony_ci * to test whether the controller hardware has been removed (e.g., 3318c2ecf20Sopenharmony_ci * cardbus physical eject). 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci local_irq_disable(); 3348c2ecf20Sopenharmony_ci usb_hcd_irq(0, hcd); 3358c2ecf20Sopenharmony_ci local_irq_enable(); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Note: dev_set_drvdata must be called while holding the rwsem */ 3388c2ecf20Sopenharmony_ci if (dev->class == CL_EHCI) { 3398c2ecf20Sopenharmony_ci down_write(&companions_rwsem); 3408c2ecf20Sopenharmony_ci for_each_companion(dev, hcd, ehci_remove); 3418c2ecf20Sopenharmony_ci usb_remove_hcd(hcd); 3428c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 3438c2ecf20Sopenharmony_ci up_write(&companions_rwsem); 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci /* Not EHCI; just clear the companion pointer */ 3468c2ecf20Sopenharmony_ci down_read(&companions_rwsem); 3478c2ecf20Sopenharmony_ci hcd->self.hs_companion = NULL; 3488c2ecf20Sopenharmony_ci usb_remove_hcd(hcd); 3498c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 3508c2ecf20Sopenharmony_ci up_read(&companions_rwsem); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci usb_put_hcd(hcd); 3538c2ecf20Sopenharmony_ci if ((hcd_driver_flags & HCD_MASK) < HCD_USB3) 3548c2ecf20Sopenharmony_ci pci_free_irq_vectors(dev); 3558c2ecf20Sopenharmony_ci pci_disable_device(dev); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_remove); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * usb_hcd_pci_shutdown - shutdown host controller 3618c2ecf20Sopenharmony_ci * @dev: USB Host Controller being shutdown 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_civoid usb_hcd_pci_shutdown(struct pci_dev *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct usb_hcd *hcd; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci hcd = pci_get_drvdata(dev); 3688c2ecf20Sopenharmony_ci if (!hcd) 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && 3728c2ecf20Sopenharmony_ci hcd->driver->shutdown) { 3738c2ecf20Sopenharmony_ci hcd->driver->shutdown(hcd); 3748c2ecf20Sopenharmony_ci if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) 3758c2ecf20Sopenharmony_ci free_irq(hcd->irq, hcd); 3768c2ecf20Sopenharmony_ci pci_disable_device(dev); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 3848c2ecf20Sopenharmony_cistatic void powermac_set_asic(struct pci_dev *pci_dev, int enable) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci /* Enanble or disable ASIC clocks for USB */ 3878c2ecf20Sopenharmony_ci if (machine_is(powermac)) { 3888c2ecf20Sopenharmony_ci struct device_node *of_node; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci of_node = pci_device_to_OF_node(pci_dev); 3918c2ecf20Sopenharmony_ci if (of_node) 3928c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_USB_ENABLE, 3938c2ecf20Sopenharmony_ci of_node, 0, enable); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#else 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic inline void powermac_set_asic(struct pci_dev *pci_dev, int enable) 4008c2ecf20Sopenharmony_ci{} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int check_root_hub_suspended(struct device *dev) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (HCD_RH_RUNNING(hcd)) { 4098c2ecf20Sopenharmony_ci dev_warn(dev, "Root hub is not suspended\n"); 4108c2ecf20Sopenharmony_ci return -EBUSY; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci if (hcd->shared_hcd) { 4138c2ecf20Sopenharmony_ci hcd = hcd->shared_hcd; 4148c2ecf20Sopenharmony_ci if (HCD_RH_RUNNING(hcd)) { 4158c2ecf20Sopenharmony_ci dev_warn(dev, "Secondary root hub is not suspended\n"); 4168c2ecf20Sopenharmony_ci return -EBUSY; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int suspend_common(struct device *dev, bool do_wakeup) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 4258c2ecf20Sopenharmony_ci struct usb_hcd *hcd = pci_get_drvdata(pci_dev); 4268c2ecf20Sopenharmony_ci int retval; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Root hub suspend should have stopped all downstream traffic, 4298c2ecf20Sopenharmony_ci * and all bus master traffic. And done so for both the interface 4308c2ecf20Sopenharmony_ci * and the stub usb_device (which we check here). But maybe it 4318c2ecf20Sopenharmony_ci * didn't; writing sysfs power/state files ignores such rules... 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci retval = check_root_hub_suspended(dev); 4348c2ecf20Sopenharmony_ci if (retval) 4358c2ecf20Sopenharmony_ci return retval; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { 4388c2ecf20Sopenharmony_ci /* Optimization: Don't suspend if a root-hub wakeup is 4398c2ecf20Sopenharmony_ci * pending and it would cause the HCD to wake up anyway. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) 4428c2ecf20Sopenharmony_ci return -EBUSY; 4438c2ecf20Sopenharmony_ci if (do_wakeup && hcd->shared_hcd && 4448c2ecf20Sopenharmony_ci HCD_WAKEUP_PENDING(hcd->shared_hcd)) 4458c2ecf20Sopenharmony_ci return -EBUSY; 4468c2ecf20Sopenharmony_ci retval = hcd->driver->pci_suspend(hcd, do_wakeup); 4478c2ecf20Sopenharmony_ci suspend_report_result(hcd->driver->pci_suspend, retval); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Check again in case wakeup raced with pci_suspend */ 4508c2ecf20Sopenharmony_ci if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) || 4518c2ecf20Sopenharmony_ci (retval == 0 && do_wakeup && hcd->shared_hcd && 4528c2ecf20Sopenharmony_ci HCD_WAKEUP_PENDING(hcd->shared_hcd))) { 4538c2ecf20Sopenharmony_ci if (hcd->driver->pci_resume) 4548c2ecf20Sopenharmony_ci hcd->driver->pci_resume(hcd, false); 4558c2ecf20Sopenharmony_ci retval = -EBUSY; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci if (retval) 4588c2ecf20Sopenharmony_ci return retval; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* If MSI-X is enabled, the driver will have synchronized all vectors 4628c2ecf20Sopenharmony_ci * in pci_suspend(). If MSI or legacy PCI is enabled, that will be 4638c2ecf20Sopenharmony_ci * synchronized here. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci if (!hcd->msix_enabled) 4668c2ecf20Sopenharmony_ci synchronize_irq(pci_irq_vector(pci_dev, 0)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Downstream ports from this root hub should already be quiesced, so 4698c2ecf20Sopenharmony_ci * there will be no DMA activity. Now we can shut down the upstream 4708c2ecf20Sopenharmony_ci * link (except maybe for PME# resume signaling). We'll enter a 4718c2ecf20Sopenharmony_ci * low power state during suspend_noirq, if the hardware allows. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 4748c2ecf20Sopenharmony_ci return retval; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int resume_common(struct device *dev, int event) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 4808c2ecf20Sopenharmony_ci struct usb_hcd *hcd = pci_get_drvdata(pci_dev); 4818c2ecf20Sopenharmony_ci int retval; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (HCD_RH_RUNNING(hcd) || 4848c2ecf20Sopenharmony_ci (hcd->shared_hcd && 4858c2ecf20Sopenharmony_ci HCD_RH_RUNNING(hcd->shared_hcd))) { 4868c2ecf20Sopenharmony_ci dev_dbg(dev, "can't resume, not suspended!\n"); 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci retval = pci_enable_device(pci_dev); 4918c2ecf20Sopenharmony_ci if (retval < 0) { 4928c2ecf20Sopenharmony_ci dev_err(dev, "can't re-enable after resume, %d!\n", retval); 4938c2ecf20Sopenharmony_ci return retval; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci pci_set_master(pci_dev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* 5018c2ecf20Sopenharmony_ci * Only EHCI controllers have to wait for their companions. 5028c2ecf20Sopenharmony_ci * No locking is needed because PCI controller drivers do not 5038c2ecf20Sopenharmony_ci * get unbound during system resume. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) 5068c2ecf20Sopenharmony_ci for_each_companion(pci_dev, hcd, 5078c2ecf20Sopenharmony_ci ehci_wait_for_companions); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci retval = hcd->driver->pci_resume(hcd, 5108c2ecf20Sopenharmony_ci event == PM_EVENT_RESTORE); 5118c2ecf20Sopenharmony_ci if (retval) { 5128c2ecf20Sopenharmony_ci dev_err(dev, "PCI post-resume error %d!\n", retval); 5138c2ecf20Sopenharmony_ci usb_hc_died(hcd); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci return retval; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int hcd_pci_suspend(struct device *dev) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci return suspend_common(dev, device_may_wakeup(dev)); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int hcd_pci_suspend_noirq(struct device *dev) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 5298c2ecf20Sopenharmony_ci struct usb_hcd *hcd = pci_get_drvdata(pci_dev); 5308c2ecf20Sopenharmony_ci int retval; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci retval = check_root_hub_suspended(dev); 5338c2ecf20Sopenharmony_ci if (retval) 5348c2ecf20Sopenharmony_ci return retval; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci pci_save_state(pci_dev); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* If the root hub is dead rather than suspended, disallow remote 5398c2ecf20Sopenharmony_ci * wakeup. usb_hc_died() should ensure that both hosts are marked as 5408c2ecf20Sopenharmony_ci * dying, so we only need to check the primary roothub. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci if (HCD_DEAD(hcd)) 5438c2ecf20Sopenharmony_ci device_set_wakeup_enable(dev, 0); 5448c2ecf20Sopenharmony_ci dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Possibly enable remote wakeup, 5478c2ecf20Sopenharmony_ci * choose the appropriate low-power state, and go to that state. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci retval = pci_prepare_to_sleep(pci_dev); 5508c2ecf20Sopenharmony_ci if (retval == -EIO) { /* Low-power not supported */ 5518c2ecf20Sopenharmony_ci dev_dbg(dev, "--> PCI D0 legacy\n"); 5528c2ecf20Sopenharmony_ci retval = 0; 5538c2ecf20Sopenharmony_ci } else if (retval == 0) { 5548c2ecf20Sopenharmony_ci dev_dbg(dev, "--> PCI %s\n", 5558c2ecf20Sopenharmony_ci pci_power_name(pci_dev->current_state)); 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci suspend_report_result(pci_prepare_to_sleep, retval); 5588c2ecf20Sopenharmony_ci return retval; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci powermac_set_asic(pci_dev, 0); 5628c2ecf20Sopenharmony_ci return retval; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int hcd_pci_resume_noirq(struct device *dev) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci powermac_set_asic(to_pci_dev(dev), 1); 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int hcd_pci_resume(struct device *dev) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci return resume_common(dev, PM_EVENT_RESUME); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int hcd_pci_restore(struct device *dev) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci return resume_common(dev, PM_EVENT_RESTORE); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci#else 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci#define hcd_pci_suspend NULL 5848c2ecf20Sopenharmony_ci#define hcd_pci_suspend_noirq NULL 5858c2ecf20Sopenharmony_ci#define hcd_pci_resume_noirq NULL 5868c2ecf20Sopenharmony_ci#define hcd_pci_resume NULL 5878c2ecf20Sopenharmony_ci#define hcd_pci_restore NULL 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int hcd_pci_runtime_suspend(struct device *dev) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci int retval; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci retval = suspend_common(dev, true); 5968c2ecf20Sopenharmony_ci if (retval == 0) 5978c2ecf20Sopenharmony_ci powermac_set_asic(to_pci_dev(dev), 0); 5988c2ecf20Sopenharmony_ci dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); 5998c2ecf20Sopenharmony_ci return retval; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int hcd_pci_runtime_resume(struct device *dev) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci int retval; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci powermac_set_asic(to_pci_dev(dev), 1); 6078c2ecf20Sopenharmony_ci retval = resume_common(dev, PM_EVENT_AUTO_RESUME); 6088c2ecf20Sopenharmony_ci dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); 6098c2ecf20Sopenharmony_ci return retval; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciconst struct dev_pm_ops usb_hcd_pci_pm_ops = { 6138c2ecf20Sopenharmony_ci .suspend = hcd_pci_suspend, 6148c2ecf20Sopenharmony_ci .suspend_noirq = hcd_pci_suspend_noirq, 6158c2ecf20Sopenharmony_ci .resume_noirq = hcd_pci_resume_noirq, 6168c2ecf20Sopenharmony_ci .resume = hcd_pci_resume, 6178c2ecf20Sopenharmony_ci .freeze = hcd_pci_suspend, 6188c2ecf20Sopenharmony_ci .freeze_noirq = check_root_hub_suspended, 6198c2ecf20Sopenharmony_ci .thaw_noirq = NULL, 6208c2ecf20Sopenharmony_ci .thaw = hcd_pci_resume, 6218c2ecf20Sopenharmony_ci .poweroff = hcd_pci_suspend, 6228c2ecf20Sopenharmony_ci .poweroff_noirq = hcd_pci_suspend_noirq, 6238c2ecf20Sopenharmony_ci .restore_noirq = hcd_pci_resume_noirq, 6248c2ecf20Sopenharmony_ci .restore = hcd_pci_restore, 6258c2ecf20Sopenharmony_ci .runtime_suspend = hcd_pci_runtime_suspend, 6268c2ecf20Sopenharmony_ci .runtime_resume = hcd_pci_runtime_resume, 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 631