summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hcd-pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hcd-pci.c')
-rw-r--r--drivers/usb/core/hcd-pci.c148
1 files changed, 75 insertions, 73 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index ea829ad798c0..cd223475917e 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright David Brownell 2000-2002
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program 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 General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
@@ -28,7 +15,6 @@
#ifdef CONFIG_PPC_PMAC
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
-#include <asm/prom.h>
#endif
#include "usb.h"
@@ -171,8 +157,9 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
/**
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
- * @id: pci hotplug id connecting controller to HCD framework
- * Context: !in_interrupt()
+ * @driver: USB HC driver handle
+ *
+ * Context: task context, might sleep
*
* Allocates basic PCI resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
@@ -182,9 +169,8 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
*
* Return: 0 if successful.
*/
-int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
{
- struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
int hcd_irq = 0;
@@ -192,9 +178,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (usb_disabled())
return -ENODEV;
- if (!id)
- return -EINVAL;
- driver = (struct hc_driver *)id->driver_data;
if (!driver)
return -EINVAL;
@@ -206,58 +189,60 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
* make sure irq setup is not touched for xhci in generic hcd code
*/
if ((driver->flags & HCD_MASK) < HCD_USB3) {
- if (!dev->irq) {
+ retval = pci_alloc_irq_vectors(dev, 1, 1,
+ PCI_IRQ_INTX | PCI_IRQ_MSI);
+ if (retval < 0) {
dev_err(&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
retval = -ENODEV;
goto disable_pci;
}
- hcd_irq = dev->irq;
+ hcd_irq = pci_irq_vector(dev, 0);
}
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
- goto disable_pci;
+ goto free_irq_vectors;
}
- hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
- driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
+ hcd->amd_resume_bug = usb_hcd_amd_resume_bug(dev, driver);
if (driver->flags & HCD_MEMORY) {
- /* EHCI, OHCI */
+ /* XHCI, EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
hcd->rsrc_len = pci_resource_len(dev, 0);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
+ if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
+ hcd->rsrc_len, driver->description)) {
dev_dbg(&dev->dev, "controller already in use\n");
retval = -EBUSY;
goto put_hcd;
}
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_ioremap(&dev->dev, hcd->rsrc_start,
+ hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&dev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto release_mem_region;
+ goto put_hcd;
}
} else {
/* UHCI */
int region;
- for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+ for (region = 0; region < PCI_STD_NUM_BARS; region++) {
if (!(pci_resource_flags(dev, region) &
IORESOURCE_IO))
continue;
hcd->rsrc_start = pci_resource_start(dev, region);
hcd->rsrc_len = pci_resource_len(dev, region);
- if (request_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description))
+ if (devm_request_region(&dev->dev, hcd->rsrc_start,
+ hcd->rsrc_len, driver->description))
break;
}
- if (region == PCI_ROM_RESOURCE) {
+ if (region == PCI_STD_NUM_BARS) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto put_hcd;
@@ -288,22 +273,18 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (retval != 0)
- goto unmap_registers;
+ goto put_hcd;
device_wakeup_enable(hcd->self.controller);
if (pci_dev_run_wake(dev))
pm_runtime_put_noidle(&dev->dev);
return retval;
-unmap_registers:
- if (driver->flags & HCD_MEMORY) {
- iounmap(hcd->regs);
-release_mem_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- } else
- release_region(hcd->rsrc_start, hcd->rsrc_len);
put_hcd:
usb_put_hcd(hcd);
+free_irq_vectors:
+ if ((driver->flags & HCD_MASK) < HCD_USB3)
+ pci_free_irq_vectors(dev);
disable_pci:
pci_disable_device(dev);
dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
@@ -318,7 +299,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
/**
* usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
* @dev: USB Host Controller being removed
- * Context: !in_interrupt()
+ *
+ * Context: task context, might sleep
*
* Reverses the effect of usb_hcd_pci_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
@@ -329,11 +311,14 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
void usb_hcd_pci_remove(struct pci_dev *dev)
{
struct usb_hcd *hcd;
+ int hcd_driver_flags;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
+ hcd_driver_flags = hcd->driver->flags;
+
if (pci_dev_run_wake(dev))
pm_runtime_get_noresume(&dev->dev);
@@ -360,15 +345,9 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
dev_set_drvdata(&dev->dev, NULL);
up_read(&companions_rwsem);
}
-
- if (hcd->driver->flags & HCD_MEMORY) {
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- } else {
- release_region(hcd->rsrc_start, hcd->rsrc_len);
- }
-
usb_put_hcd(hcd);
+ if ((hcd_driver_flags & HCD_MASK) < HCD_USB3)
+ pci_free_irq_vectors(dev);
pci_disable_device(dev);
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
@@ -420,8 +399,7 @@ static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
static int check_root_hub_suspended(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
if (HCD_RH_RUNNING(hcd)) {
dev_warn(dev, "Root hub is not suspended\n");
@@ -437,12 +415,20 @@ static int check_root_hub_suspended(struct device *dev)
return 0;
}
-static int suspend_common(struct device *dev, bool do_wakeup)
+static int suspend_common(struct device *dev, pm_message_t msg)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ bool do_wakeup;
int retval;
+ if (PMSG_IS_AUTO(msg))
+ do_wakeup = true;
+ else if (PMSG_NO_WAKEUP(msg))
+ do_wakeup = false;
+ else
+ do_wakeup = device_may_wakeup(dev);
+
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
* and the stub usb_device (which we check here). But maybe it
@@ -462,14 +448,14 @@ static int suspend_common(struct device *dev, bool do_wakeup)
HCD_WAKEUP_PENDING(hcd->shared_hcd))
return -EBUSY;
retval = hcd->driver->pci_suspend(hcd, do_wakeup);
- suspend_report_result(hcd->driver->pci_suspend, retval);
+ suspend_report_result(dev, hcd->driver->pci_suspend, retval);
/* Check again in case wakeup raced with pci_suspend */
if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) ||
(retval == 0 && do_wakeup && hcd->shared_hcd &&
HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
if (hcd->driver->pci_resume)
- hcd->driver->pci_resume(hcd, false);
+ hcd->driver->pci_resume(hcd, msg);
retval = -EBUSY;
}
if (retval)
@@ -481,7 +467,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
* synchronized here.
*/
if (!hcd->msix_enabled)
- synchronize_irq(pci_dev->irq);
+ synchronize_irq(pci_irq_vector(pci_dev, 0));
/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
@@ -492,7 +478,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
return retval;
}
-static int resume_common(struct device *dev, int event)
+static int resume_common(struct device *dev, pm_message_t msg)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
@@ -520,16 +506,13 @@ static int resume_common(struct device *dev, int event)
* No locking is needed because PCI controller drivers do not
* get unbound during system resume.
*/
- if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
+ if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME)
for_each_companion(pci_dev, hcd,
ehci_wait_for_companions);
- retval = hcd->driver->pci_resume(hcd,
- event == PM_EVENT_RESTORE);
+ retval = hcd->driver->pci_resume(hcd, msg);
if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval);
- if (hcd->shared_hcd)
- usb_hc_died(hcd->shared_hcd);
usb_hc_died(hcd);
}
}
@@ -540,7 +523,12 @@ static int resume_common(struct device *dev, int event)
static int hcd_pci_suspend(struct device *dev)
{
- return suspend_common(dev, device_may_wakeup(dev));
+ return suspend_common(dev, PMSG_SUSPEND);
+}
+
+static int hcd_pci_freeze(struct device *dev)
+{
+ return suspend_common(dev, PMSG_FREEZE);
}
static int hcd_pci_suspend_noirq(struct device *dev)
@@ -574,7 +562,7 @@ static int hcd_pci_suspend_noirq(struct device *dev)
dev_dbg(dev, "--> PCI %s\n",
pci_power_name(pci_dev->current_state));
} else {
- suspend_report_result(pci_prepare_to_sleep, retval);
+ suspend_report_result(dev, pci_prepare_to_sleep, retval);
return retval;
}
@@ -582,6 +570,17 @@ static int hcd_pci_suspend_noirq(struct device *dev)
return retval;
}
+static int hcd_pci_poweroff_late(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+
+ if (hcd->driver->pci_poweroff_late && !HCD_DEAD(hcd))
+ return hcd->driver->pci_poweroff_late(hcd, device_may_wakeup(dev));
+
+ return 0;
+}
+
static int hcd_pci_resume_noirq(struct device *dev)
{
powermac_set_asic(to_pci_dev(dev), 1);
@@ -590,18 +589,20 @@ static int hcd_pci_resume_noirq(struct device *dev)
static int hcd_pci_resume(struct device *dev)
{
- return resume_common(dev, PM_EVENT_RESUME);
+ return resume_common(dev, PMSG_RESUME);
}
static int hcd_pci_restore(struct device *dev)
{
- return resume_common(dev, PM_EVENT_RESTORE);
+ return resume_common(dev, PMSG_RESTORE);
}
#else
#define hcd_pci_suspend NULL
+#define hcd_pci_freeze NULL
#define hcd_pci_suspend_noirq NULL
+#define hcd_pci_poweroff_late NULL
#define hcd_pci_resume_noirq NULL
#define hcd_pci_resume NULL
#define hcd_pci_restore NULL
@@ -612,7 +613,7 @@ static int hcd_pci_runtime_suspend(struct device *dev)
{
int retval;
- retval = suspend_common(dev, true);
+ retval = suspend_common(dev, PMSG_AUTO_SUSPEND);
if (retval == 0)
powermac_set_asic(to_pci_dev(dev), 0);
dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
@@ -624,7 +625,7 @@ static int hcd_pci_runtime_resume(struct device *dev)
int retval;
powermac_set_asic(to_pci_dev(dev), 1);
- retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
+ retval = resume_common(dev, PMSG_AUTO_RESUME);
dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
return retval;
}
@@ -634,11 +635,12 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend_noirq = hcd_pci_suspend_noirq,
.resume_noirq = hcd_pci_resume_noirq,
.resume = hcd_pci_resume,
- .freeze = check_root_hub_suspended,
+ .freeze = hcd_pci_freeze,
.freeze_noirq = check_root_hub_suspended,
.thaw_noirq = NULL,
- .thaw = NULL,
+ .thaw = hcd_pci_resume,
.poweroff = hcd_pci_suspend,
+ .poweroff_late = hcd_pci_poweroff_late,
.poweroff_noirq = hcd_pci_suspend_noirq,
.restore_noirq = hcd_pci_resume_noirq,
.restore = hcd_pci_restore,