162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (C) Copyright David Brownell 2000-2002
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/pci.h>
962306a36Sopenharmony_ci#include <linux/usb.h>
1062306a36Sopenharmony_ci#include <linux/usb/hcd.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/io.h>
1362306a36Sopenharmony_ci#include <asm/irq.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
1662306a36Sopenharmony_ci#include <asm/machdep.h>
1762306a36Sopenharmony_ci#include <asm/pmac_feature.h>
1862306a36Sopenharmony_ci#endif
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "usb.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Coordinate handoffs between EHCI and companion controllers
2762306a36Sopenharmony_ci * during EHCI probing and system resume.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic DECLARE_RWSEM(companions_rwsem);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define CL_UHCI		PCI_CLASS_SERIAL_USB_UHCI
3362306a36Sopenharmony_ci#define CL_OHCI		PCI_CLASS_SERIAL_USB_OHCI
3462306a36Sopenharmony_ci#define CL_EHCI		PCI_CLASS_SERIAL_USB_EHCI
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic inline int is_ohci_or_uhci(struct pci_dev *pdev)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return pdev->class == CL_OHCI || pdev->class == CL_UHCI;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_citypedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd,
4262306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Iterate over PCI devices in the same slot as pdev and call fn for each */
4562306a36Sopenharmony_cistatic void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
4662306a36Sopenharmony_ci		companion_fn fn)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct pci_dev		*companion;
4962306a36Sopenharmony_ci	struct usb_hcd		*companion_hcd;
5062306a36Sopenharmony_ci	unsigned int		slot = PCI_SLOT(pdev->devfn);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * Iterate through other PCI functions in the same slot.
5462306a36Sopenharmony_ci	 * If the function's drvdata isn't set then it isn't bound to
5562306a36Sopenharmony_ci	 * a USB host controller driver, so skip it.
5662306a36Sopenharmony_ci	 */
5762306a36Sopenharmony_ci	companion = NULL;
5862306a36Sopenharmony_ci	for_each_pci_dev(companion) {
5962306a36Sopenharmony_ci		if (companion->bus != pdev->bus ||
6062306a36Sopenharmony_ci				PCI_SLOT(companion->devfn) != slot)
6162306a36Sopenharmony_ci			continue;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/*
6462306a36Sopenharmony_ci		 * Companion device should be either UHCI,OHCI or EHCI host
6562306a36Sopenharmony_ci		 * controller, otherwise skip.
6662306a36Sopenharmony_ci		 */
6762306a36Sopenharmony_ci		if (companion->class != CL_UHCI && companion->class != CL_OHCI &&
6862306a36Sopenharmony_ci				companion->class != CL_EHCI)
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		companion_hcd = pci_get_drvdata(companion);
7262306a36Sopenharmony_ci		if (!companion_hcd || !companion_hcd->self.root_hub)
7362306a36Sopenharmony_ci			continue;
7462306a36Sopenharmony_ci		fn(pdev, hcd, companion, companion_hcd);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * We're about to add an EHCI controller, which will unceremoniously grab
8062306a36Sopenharmony_ci * all the port connections away from its companions.  To prevent annoying
8162306a36Sopenharmony_ci * error messages, lock the companion's root hub and gracefully unconfigure
8262306a36Sopenharmony_ci * it beforehand.  Leave it locked until the EHCI controller is all set.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd,
8562306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct usb_device *udev;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (is_ohci_or_uhci(companion)) {
9062306a36Sopenharmony_ci		udev = companion_hcd->self.root_hub;
9162306a36Sopenharmony_ci		usb_lock_device(udev);
9262306a36Sopenharmony_ci		usb_set_configuration(udev, 0);
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * Adding the EHCI controller has either succeeded or failed.  Set the
9862306a36Sopenharmony_ci * companion pointer accordingly, and in either case, reconfigure and
9962306a36Sopenharmony_ci * unlock the root hub.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd,
10262306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct usb_device *udev;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (is_ohci_or_uhci(companion)) {
10762306a36Sopenharmony_ci		if (dev_get_drvdata(&pdev->dev)) {	/* Succeeded */
10862306a36Sopenharmony_ci			dev_dbg(&pdev->dev, "HS companion for %s\n",
10962306a36Sopenharmony_ci					dev_name(&companion->dev));
11062306a36Sopenharmony_ci			companion_hcd->self.hs_companion = &hcd->self;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		udev = companion_hcd->self.root_hub;
11362306a36Sopenharmony_ci		usb_set_configuration(udev, 1);
11462306a36Sopenharmony_ci		usb_unlock_device(udev);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * We just added a non-EHCI controller.  Find the EHCI controller to
12062306a36Sopenharmony_ci * which it is a companion, and store a pointer to the bus structure.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd,
12362306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) {
12662306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "FS/LS companion for %s\n",
12762306a36Sopenharmony_ci				dev_name(&companion->dev));
12862306a36Sopenharmony_ci		hcd->self.hs_companion = &companion_hcd->self;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* We are removing an EHCI controller.  Clear the companions' pointers. */
13362306a36Sopenharmony_cistatic void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd,
13462306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	if (is_ohci_or_uhci(companion))
13762306a36Sopenharmony_ci		companion_hcd->self.hs_companion = NULL;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#ifdef	CONFIG_PM
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* An EHCI controller must wait for its companions before resuming. */
14362306a36Sopenharmony_cistatic void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
14462306a36Sopenharmony_ci		struct pci_dev *companion, struct usb_hcd *companion_hcd)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	if (is_ohci_or_uhci(companion))
14762306a36Sopenharmony_ci		device_pm_wait_for_dev(&pdev->dev, &companion->dev);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#endif	/* CONFIG_PM */
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/* configure so an HC device and id are always provided */
15562306a36Sopenharmony_ci/* always called with process context; sleeping is OK */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/**
15862306a36Sopenharmony_ci * usb_hcd_pci_probe - initialize PCI-based HCDs
15962306a36Sopenharmony_ci * @dev: USB Host Controller being probed
16062306a36Sopenharmony_ci * @driver: USB HC driver handle
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * Context: task context, might sleep
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * Allocates basic PCI resources for this USB host controller, and
16562306a36Sopenharmony_ci * then invokes the start() method for the HCD associated with it
16662306a36Sopenharmony_ci * through the hotplug entry's driver_data.
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * Store this function in the HCD's struct pci_driver as probe().
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Return: 0 if successful.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_ciint usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct usb_hcd		*hcd;
17562306a36Sopenharmony_ci	int			retval;
17662306a36Sopenharmony_ci	int			hcd_irq = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (usb_disabled())
17962306a36Sopenharmony_ci		return -ENODEV;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (!driver)
18262306a36Sopenharmony_ci		return -EINVAL;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (pci_enable_device(dev) < 0)
18562306a36Sopenharmony_ci		return -ENODEV;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * The xHCI driver has its own irq management
18962306a36Sopenharmony_ci	 * make sure irq setup is not touched for xhci in generic hcd code
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	if ((driver->flags & HCD_MASK) < HCD_USB3) {
19262306a36Sopenharmony_ci		retval = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSI);
19362306a36Sopenharmony_ci		if (retval < 0) {
19462306a36Sopenharmony_ci			dev_err(&dev->dev,
19562306a36Sopenharmony_ci			"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
19662306a36Sopenharmony_ci				pci_name(dev));
19762306a36Sopenharmony_ci			retval = -ENODEV;
19862306a36Sopenharmony_ci			goto disable_pci;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci		hcd_irq = pci_irq_vector(dev, 0);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
20462306a36Sopenharmony_ci	if (!hcd) {
20562306a36Sopenharmony_ci		retval = -ENOMEM;
20662306a36Sopenharmony_ci		goto free_irq_vectors;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
21062306a36Sopenharmony_ci			driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (driver->flags & HCD_MEMORY) {
21362306a36Sopenharmony_ci		/* EHCI, OHCI */
21462306a36Sopenharmony_ci		hcd->rsrc_start = pci_resource_start(dev, 0);
21562306a36Sopenharmony_ci		hcd->rsrc_len = pci_resource_len(dev, 0);
21662306a36Sopenharmony_ci		if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
21762306a36Sopenharmony_ci				hcd->rsrc_len, driver->description)) {
21862306a36Sopenharmony_ci			dev_dbg(&dev->dev, "controller already in use\n");
21962306a36Sopenharmony_ci			retval = -EBUSY;
22062306a36Sopenharmony_ci			goto put_hcd;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		hcd->regs = devm_ioremap(&dev->dev, hcd->rsrc_start,
22362306a36Sopenharmony_ci				hcd->rsrc_len);
22462306a36Sopenharmony_ci		if (hcd->regs == NULL) {
22562306a36Sopenharmony_ci			dev_dbg(&dev->dev, "error mapping memory\n");
22662306a36Sopenharmony_ci			retval = -EFAULT;
22762306a36Sopenharmony_ci			goto put_hcd;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	} else {
23162306a36Sopenharmony_ci		/* UHCI */
23262306a36Sopenharmony_ci		int	region;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		for (region = 0; region < PCI_STD_NUM_BARS; region++) {
23562306a36Sopenharmony_ci			if (!(pci_resource_flags(dev, region) &
23662306a36Sopenharmony_ci					IORESOURCE_IO))
23762306a36Sopenharmony_ci				continue;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci			hcd->rsrc_start = pci_resource_start(dev, region);
24062306a36Sopenharmony_ci			hcd->rsrc_len = pci_resource_len(dev, region);
24162306a36Sopenharmony_ci			if (devm_request_region(&dev->dev, hcd->rsrc_start,
24262306a36Sopenharmony_ci					hcd->rsrc_len, driver->description))
24362306a36Sopenharmony_ci				break;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci		if (region == PCI_STD_NUM_BARS) {
24662306a36Sopenharmony_ci			dev_dbg(&dev->dev, "no i/o regions available\n");
24762306a36Sopenharmony_ci			retval = -EBUSY;
24862306a36Sopenharmony_ci			goto put_hcd;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	pci_set_master(dev);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Note: dev_set_drvdata must be called while holding the rwsem */
25562306a36Sopenharmony_ci	if (dev->class == CL_EHCI) {
25662306a36Sopenharmony_ci		down_write(&companions_rwsem);
25762306a36Sopenharmony_ci		dev_set_drvdata(&dev->dev, hcd);
25862306a36Sopenharmony_ci		for_each_companion(dev, hcd, ehci_pre_add);
25962306a36Sopenharmony_ci		retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
26062306a36Sopenharmony_ci		if (retval != 0)
26162306a36Sopenharmony_ci			dev_set_drvdata(&dev->dev, NULL);
26262306a36Sopenharmony_ci		for_each_companion(dev, hcd, ehci_post_add);
26362306a36Sopenharmony_ci		up_write(&companions_rwsem);
26462306a36Sopenharmony_ci	} else {
26562306a36Sopenharmony_ci		down_read(&companions_rwsem);
26662306a36Sopenharmony_ci		dev_set_drvdata(&dev->dev, hcd);
26762306a36Sopenharmony_ci		retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
26862306a36Sopenharmony_ci		if (retval != 0)
26962306a36Sopenharmony_ci			dev_set_drvdata(&dev->dev, NULL);
27062306a36Sopenharmony_ci		else
27162306a36Sopenharmony_ci			for_each_companion(dev, hcd, non_ehci_add);
27262306a36Sopenharmony_ci		up_read(&companions_rwsem);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (retval != 0)
27662306a36Sopenharmony_ci		goto put_hcd;
27762306a36Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (pci_dev_run_wake(dev))
28062306a36Sopenharmony_ci		pm_runtime_put_noidle(&dev->dev);
28162306a36Sopenharmony_ci	return retval;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciput_hcd:
28462306a36Sopenharmony_ci	usb_put_hcd(hcd);
28562306a36Sopenharmony_cifree_irq_vectors:
28662306a36Sopenharmony_ci	if ((driver->flags & HCD_MASK) < HCD_USB3)
28762306a36Sopenharmony_ci		pci_free_irq_vectors(dev);
28862306a36Sopenharmony_cidisable_pci:
28962306a36Sopenharmony_ci	pci_disable_device(dev);
29062306a36Sopenharmony_ci	dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
29162306a36Sopenharmony_ci	return retval;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* may be called without controller electrically present */
29762306a36Sopenharmony_ci/* may be called with controller, bus, and devices active */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/**
30062306a36Sopenharmony_ci * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
30162306a36Sopenharmony_ci * @dev: USB Host Controller being removed
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci * Context: task context, might sleep
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci * Reverses the effect of usb_hcd_pci_probe(), first invoking
30662306a36Sopenharmony_ci * the HCD's stop() method.  It is always called from a thread
30762306a36Sopenharmony_ci * context, normally "rmmod", "apmd", or something similar.
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Store this function in the HCD's struct pci_driver as remove().
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_civoid usb_hcd_pci_remove(struct pci_dev *dev)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct usb_hcd		*hcd;
31462306a36Sopenharmony_ci	int			hcd_driver_flags;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	hcd = pci_get_drvdata(dev);
31762306a36Sopenharmony_ci	if (!hcd)
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	hcd_driver_flags = hcd->driver->flags;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (pci_dev_run_wake(dev))
32362306a36Sopenharmony_ci		pm_runtime_get_noresume(&dev->dev);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Fake an interrupt request in order to give the driver a chance
32662306a36Sopenharmony_ci	 * to test whether the controller hardware has been removed (e.g.,
32762306a36Sopenharmony_ci	 * cardbus physical eject).
32862306a36Sopenharmony_ci	 */
32962306a36Sopenharmony_ci	local_irq_disable();
33062306a36Sopenharmony_ci	usb_hcd_irq(0, hcd);
33162306a36Sopenharmony_ci	local_irq_enable();
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* Note: dev_set_drvdata must be called while holding the rwsem */
33462306a36Sopenharmony_ci	if (dev->class == CL_EHCI) {
33562306a36Sopenharmony_ci		down_write(&companions_rwsem);
33662306a36Sopenharmony_ci		for_each_companion(dev, hcd, ehci_remove);
33762306a36Sopenharmony_ci		usb_remove_hcd(hcd);
33862306a36Sopenharmony_ci		dev_set_drvdata(&dev->dev, NULL);
33962306a36Sopenharmony_ci		up_write(&companions_rwsem);
34062306a36Sopenharmony_ci	} else {
34162306a36Sopenharmony_ci		/* Not EHCI; just clear the companion pointer */
34262306a36Sopenharmony_ci		down_read(&companions_rwsem);
34362306a36Sopenharmony_ci		hcd->self.hs_companion = NULL;
34462306a36Sopenharmony_ci		usb_remove_hcd(hcd);
34562306a36Sopenharmony_ci		dev_set_drvdata(&dev->dev, NULL);
34662306a36Sopenharmony_ci		up_read(&companions_rwsem);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	usb_put_hcd(hcd);
34962306a36Sopenharmony_ci	if ((hcd_driver_flags & HCD_MASK) < HCD_USB3)
35062306a36Sopenharmony_ci		pci_free_irq_vectors(dev);
35162306a36Sopenharmony_ci	pci_disable_device(dev);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/**
35662306a36Sopenharmony_ci * usb_hcd_pci_shutdown - shutdown host controller
35762306a36Sopenharmony_ci * @dev: USB Host Controller being shutdown
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_civoid usb_hcd_pci_shutdown(struct pci_dev *dev)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct usb_hcd		*hcd;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	hcd = pci_get_drvdata(dev);
36462306a36Sopenharmony_ci	if (!hcd)
36562306a36Sopenharmony_ci		return;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
36862306a36Sopenharmony_ci			hcd->driver->shutdown) {
36962306a36Sopenharmony_ci		hcd->driver->shutdown(hcd);
37062306a36Sopenharmony_ci		if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
37162306a36Sopenharmony_ci			free_irq(hcd->irq, hcd);
37262306a36Sopenharmony_ci		pci_disable_device(dev);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci#ifdef	CONFIG_PM
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci#ifdef	CONFIG_PPC_PMAC
38062306a36Sopenharmony_cistatic void powermac_set_asic(struct pci_dev *pci_dev, int enable)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	/* Enanble or disable ASIC clocks for USB */
38362306a36Sopenharmony_ci	if (machine_is(powermac)) {
38462306a36Sopenharmony_ci		struct device_node	*of_node;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		of_node = pci_device_to_OF_node(pci_dev);
38762306a36Sopenharmony_ci		if (of_node)
38862306a36Sopenharmony_ci			pmac_call_feature(PMAC_FTR_USB_ENABLE,
38962306a36Sopenharmony_ci					of_node, 0, enable);
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci#else
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
39662306a36Sopenharmony_ci{}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci#endif	/* CONFIG_PPC_PMAC */
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int check_root_hub_suspended(struct device *dev)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct usb_hcd		*hcd = dev_get_drvdata(dev);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (HCD_RH_RUNNING(hcd)) {
40562306a36Sopenharmony_ci		dev_warn(dev, "Root hub is not suspended\n");
40662306a36Sopenharmony_ci		return -EBUSY;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	if (hcd->shared_hcd) {
40962306a36Sopenharmony_ci		hcd = hcd->shared_hcd;
41062306a36Sopenharmony_ci		if (HCD_RH_RUNNING(hcd)) {
41162306a36Sopenharmony_ci			dev_warn(dev, "Secondary root hub is not suspended\n");
41262306a36Sopenharmony_ci			return -EBUSY;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int suspend_common(struct device *dev, pm_message_t msg)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct pci_dev		*pci_dev = to_pci_dev(dev);
42162306a36Sopenharmony_ci	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
42262306a36Sopenharmony_ci	bool			do_wakeup;
42362306a36Sopenharmony_ci	int			retval;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Root hub suspend should have stopped all downstream traffic,
42862306a36Sopenharmony_ci	 * and all bus master traffic.  And done so for both the interface
42962306a36Sopenharmony_ci	 * and the stub usb_device (which we check here).  But maybe it
43062306a36Sopenharmony_ci	 * didn't; writing sysfs power/state files ignores such rules...
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	retval = check_root_hub_suspended(dev);
43362306a36Sopenharmony_ci	if (retval)
43462306a36Sopenharmony_ci		return retval;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) {
43762306a36Sopenharmony_ci		/* Optimization: Don't suspend if a root-hub wakeup is
43862306a36Sopenharmony_ci		 * pending and it would cause the HCD to wake up anyway.
43962306a36Sopenharmony_ci		 */
44062306a36Sopenharmony_ci		if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
44162306a36Sopenharmony_ci			return -EBUSY;
44262306a36Sopenharmony_ci		if (do_wakeup && hcd->shared_hcd &&
44362306a36Sopenharmony_ci				HCD_WAKEUP_PENDING(hcd->shared_hcd))
44462306a36Sopenharmony_ci			return -EBUSY;
44562306a36Sopenharmony_ci		retval = hcd->driver->pci_suspend(hcd, do_wakeup);
44662306a36Sopenharmony_ci		suspend_report_result(dev, hcd->driver->pci_suspend, retval);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		/* Check again in case wakeup raced with pci_suspend */
44962306a36Sopenharmony_ci		if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) ||
45062306a36Sopenharmony_ci				(retval == 0 && do_wakeup && hcd->shared_hcd &&
45162306a36Sopenharmony_ci				 HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
45262306a36Sopenharmony_ci			if (hcd->driver->pci_resume)
45362306a36Sopenharmony_ci				hcd->driver->pci_resume(hcd, msg);
45462306a36Sopenharmony_ci			retval = -EBUSY;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		if (retval)
45762306a36Sopenharmony_ci			return retval;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* If MSI-X is enabled, the driver will have synchronized all vectors
46162306a36Sopenharmony_ci	 * in pci_suspend(). If MSI or legacy PCI is enabled, that will be
46262306a36Sopenharmony_ci	 * synchronized here.
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	if (!hcd->msix_enabled)
46562306a36Sopenharmony_ci		synchronize_irq(pci_irq_vector(pci_dev, 0));
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* Downstream ports from this root hub should already be quiesced, so
46862306a36Sopenharmony_ci	 * there will be no DMA activity.  Now we can shut down the upstream
46962306a36Sopenharmony_ci	 * link (except maybe for PME# resume signaling).  We'll enter a
47062306a36Sopenharmony_ci	 * low power state during suspend_noirq, if the hardware allows.
47162306a36Sopenharmony_ci	 */
47262306a36Sopenharmony_ci	pci_disable_device(pci_dev);
47362306a36Sopenharmony_ci	return retval;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int resume_common(struct device *dev, pm_message_t msg)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct pci_dev		*pci_dev = to_pci_dev(dev);
47962306a36Sopenharmony_ci	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
48062306a36Sopenharmony_ci	int			retval;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (HCD_RH_RUNNING(hcd) ||
48362306a36Sopenharmony_ci			(hcd->shared_hcd &&
48462306a36Sopenharmony_ci			 HCD_RH_RUNNING(hcd->shared_hcd))) {
48562306a36Sopenharmony_ci		dev_dbg(dev, "can't resume, not suspended!\n");
48662306a36Sopenharmony_ci		return 0;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	retval = pci_enable_device(pci_dev);
49062306a36Sopenharmony_ci	if (retval < 0) {
49162306a36Sopenharmony_ci		dev_err(dev, "can't re-enable after resume, %d!\n", retval);
49262306a36Sopenharmony_ci		return retval;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	pci_set_master(pci_dev);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		/*
50062306a36Sopenharmony_ci		 * Only EHCI controllers have to wait for their companions.
50162306a36Sopenharmony_ci		 * No locking is needed because PCI controller drivers do not
50262306a36Sopenharmony_ci		 * get unbound during system resume.
50362306a36Sopenharmony_ci		 */
50462306a36Sopenharmony_ci		if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME)
50562306a36Sopenharmony_ci			for_each_companion(pci_dev, hcd,
50662306a36Sopenharmony_ci					ehci_wait_for_companions);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		retval = hcd->driver->pci_resume(hcd, msg);
50962306a36Sopenharmony_ci		if (retval) {
51062306a36Sopenharmony_ci			dev_err(dev, "PCI post-resume error %d!\n", retval);
51162306a36Sopenharmony_ci			usb_hc_died(hcd);
51262306a36Sopenharmony_ci		}
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	return retval;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#ifdef	CONFIG_PM_SLEEP
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int hcd_pci_suspend(struct device *dev)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	return suspend_common(dev, PMSG_SUSPEND);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int hcd_pci_suspend_noirq(struct device *dev)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct pci_dev		*pci_dev = to_pci_dev(dev);
52762306a36Sopenharmony_ci	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
52862306a36Sopenharmony_ci	int			retval;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	retval = check_root_hub_suspended(dev);
53162306a36Sopenharmony_ci	if (retval)
53262306a36Sopenharmony_ci		return retval;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	pci_save_state(pci_dev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* If the root hub is dead rather than suspended, disallow remote
53762306a36Sopenharmony_ci	 * wakeup.  usb_hc_died() should ensure that both hosts are marked as
53862306a36Sopenharmony_ci	 * dying, so we only need to check the primary roothub.
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci	if (HCD_DEAD(hcd))
54162306a36Sopenharmony_ci		device_set_wakeup_enable(dev, 0);
54262306a36Sopenharmony_ci	dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* Possibly enable remote wakeup,
54562306a36Sopenharmony_ci	 * choose the appropriate low-power state, and go to that state.
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci	retval = pci_prepare_to_sleep(pci_dev);
54862306a36Sopenharmony_ci	if (retval == -EIO) {		/* Low-power not supported */
54962306a36Sopenharmony_ci		dev_dbg(dev, "--> PCI D0 legacy\n");
55062306a36Sopenharmony_ci		retval = 0;
55162306a36Sopenharmony_ci	} else if (retval == 0) {
55262306a36Sopenharmony_ci		dev_dbg(dev, "--> PCI %s\n",
55362306a36Sopenharmony_ci				pci_power_name(pci_dev->current_state));
55462306a36Sopenharmony_ci	} else {
55562306a36Sopenharmony_ci		suspend_report_result(dev, pci_prepare_to_sleep, retval);
55662306a36Sopenharmony_ci		return retval;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	powermac_set_asic(pci_dev, 0);
56062306a36Sopenharmony_ci	return retval;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int hcd_pci_poweroff_late(struct device *dev)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct pci_dev		*pci_dev = to_pci_dev(dev);
56662306a36Sopenharmony_ci	struct usb_hcd		*hcd = pci_get_drvdata(pci_dev);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (hcd->driver->pci_poweroff_late && !HCD_DEAD(hcd))
56962306a36Sopenharmony_ci		return hcd->driver->pci_poweroff_late(hcd, device_may_wakeup(dev));
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic int hcd_pci_resume_noirq(struct device *dev)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	powermac_set_asic(to_pci_dev(dev), 1);
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int hcd_pci_resume(struct device *dev)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	return resume_common(dev, PMSG_RESUME);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int hcd_pci_restore(struct device *dev)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	return resume_common(dev, PMSG_RESTORE);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci#else
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci#define hcd_pci_suspend		NULL
59362306a36Sopenharmony_ci#define hcd_pci_suspend_noirq	NULL
59462306a36Sopenharmony_ci#define hcd_pci_poweroff_late	NULL
59562306a36Sopenharmony_ci#define hcd_pci_resume_noirq	NULL
59662306a36Sopenharmony_ci#define hcd_pci_resume		NULL
59762306a36Sopenharmony_ci#define hcd_pci_restore		NULL
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci#endif	/* CONFIG_PM_SLEEP */
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int hcd_pci_runtime_suspend(struct device *dev)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	int	retval;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	retval = suspend_common(dev, PMSG_AUTO_SUSPEND);
60662306a36Sopenharmony_ci	if (retval == 0)
60762306a36Sopenharmony_ci		powermac_set_asic(to_pci_dev(dev), 0);
60862306a36Sopenharmony_ci	dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
60962306a36Sopenharmony_ci	return retval;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int hcd_pci_runtime_resume(struct device *dev)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	int	retval;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	powermac_set_asic(to_pci_dev(dev), 1);
61762306a36Sopenharmony_ci	retval = resume_common(dev, PMSG_AUTO_RESUME);
61862306a36Sopenharmony_ci	dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
61962306a36Sopenharmony_ci	return retval;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ciconst struct dev_pm_ops usb_hcd_pci_pm_ops = {
62362306a36Sopenharmony_ci	.suspend	= hcd_pci_suspend,
62462306a36Sopenharmony_ci	.suspend_noirq	= hcd_pci_suspend_noirq,
62562306a36Sopenharmony_ci	.resume_noirq	= hcd_pci_resume_noirq,
62662306a36Sopenharmony_ci	.resume		= hcd_pci_resume,
62762306a36Sopenharmony_ci	.freeze		= hcd_pci_suspend,
62862306a36Sopenharmony_ci	.freeze_noirq	= check_root_hub_suspended,
62962306a36Sopenharmony_ci	.thaw_noirq	= NULL,
63062306a36Sopenharmony_ci	.thaw		= hcd_pci_resume,
63162306a36Sopenharmony_ci	.poweroff	= hcd_pci_suspend,
63262306a36Sopenharmony_ci	.poweroff_late	= hcd_pci_poweroff_late,
63362306a36Sopenharmony_ci	.poweroff_noirq	= hcd_pci_suspend_noirq,
63462306a36Sopenharmony_ci	.restore_noirq	= hcd_pci_resume_noirq,
63562306a36Sopenharmony_ci	.restore	= hcd_pci_restore,
63662306a36Sopenharmony_ci	.runtime_suspend = hcd_pci_runtime_suspend,
63762306a36Sopenharmony_ci	.runtime_resume	= hcd_pci_runtime_resume,
63862306a36Sopenharmony_ci};
63962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci#endif	/* CONFIG_PM */
642