Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 127288
b: refs/heads/master
c: a0d4922
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jan 7, 2009
1 parent 1181e31 commit b2c3fa0
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 96 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a81a81a25d3ecdab777abca87c5ddf484056103d
refs/heads/master: a0d4922da2e4ccb0973095d8d29f36f6b1b5f703
200 changes: 106 additions & 94 deletions trunk/drivers/usb/core/hcd-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
* @message: semantics in flux
* @message: Power Management message describing this state transition
*
* Store this function in the HCD's struct pci_driver as suspend().
* Store this function in the HCD's struct pci_driver as .suspend.
*/
int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
{
struct usb_hcd *hcd;
struct usb_hcd *hcd = pci_get_drvdata(dev);
int retval = 0;
int has_pci_pm;

hcd = pci_get_drvdata(dev);
int wake, w;

/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
Expand All @@ -212,58 +210,76 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
* otherwise the swsusp will save (and restore) garbage state.
*/
if (!(hcd->state == HC_STATE_SUSPENDED ||
hcd->state == HC_STATE_HALT))
return -EBUSY;
hcd->state == HC_STATE_HALT)) {
dev_warn(&dev->dev, "Root hub is not suspended\n");
retval = -EBUSY;
goto done;
}

/* We might already be suspended (runtime PM -- not yet written) */
if (dev->current_state != PCI_D0)
goto done;

if (hcd->driver->pci_suspend) {
retval = hcd->driver->pci_suspend(hcd, message);
suspend_report_result(hcd->driver->pci_suspend, retval);
if (retval)
goto done;
}
synchronize_irq(dev->irq);

/* FIXME until the generic PM interfaces change a lot more, this
* can't use PCI D1 and D2 states. For example, the confusion
* between messages and states will need to vanish, and messages
* will need to provide a target system state again.
*
* It'll be important to learn characteristics of the target state,
* especially on embedded hardware where the HCD will often be in
* charge of an external VBUS power supply and one or more clocks.
* Some target system states will leave them active; others won't.
* (With PCI, that's often handled by platform BIOS code.)
*/
synchronize_irq(dev->irq);

/* even when the PCI layer rejects some of the PCI calls
* below, HCs can try global suspend and reduce DMA traffic.
* PM-sensitive HCDs may already have done this.
/* Don't fail on error to enable wakeup. We rely on pci code
* to reject requests the hardware can't implement, rather
* than coding the same thing.
*/
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
wake = (hcd->state == HC_STATE_SUSPENDED &&
device_may_wakeup(&dev->dev));
w = pci_wake_from_d3(dev, wake);
if (w < 0)
wake = w;
dev_dbg(&dev->dev, "wakeup: %d\n", wake);

/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
* link (except maybe for PME# resume signaling) and enter some PCI
* low power state, if the hardware allows.
*/
if (hcd->state == HC_STATE_SUSPENDED) {
pci_disable_device(dev);
done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);

/* no DMA or IRQs except when HC is active */
if (dev->current_state == PCI_D0) {
pci_save_state(dev);
pci_disable_device(dev);
}
/**
* usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled
* @dev: USB Host Controller being suspended
* @message: Power Management message describing this state transition
*
* Store this function in the HCD's struct pci_driver as .suspend_late.
*/
int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
{
int retval = 0;
int has_pci_pm;

if (message.event == PM_EVENT_FREEZE ||
message.event == PM_EVENT_PRETHAW) {
dev_dbg(hcd->self.controller, "--> no state change\n");
goto done;
}
/* We might already be suspended (runtime PM -- not yet written) */
if (dev->current_state != PCI_D0)
goto done;

if (!has_pci_pm) {
dev_dbg(hcd->self.controller, "--> PCI D0/legacy\n");
goto done;
}
pci_save_state(dev);

/* Don't change state if we don't need to */
if (message.event == PM_EVENT_FREEZE ||
message.event == PM_EVENT_PRETHAW) {
dev_dbg(&dev->dev, "--> no state change\n");
goto done;
}

has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (!has_pci_pm) {
dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
} else {

/* NOTE: dev->current_state becomes nonzero only here, and
* only for devices that support PCI PM. Also, exiting
Expand All @@ -273,35 +289,16 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
retval = pci_set_power_state(dev, PCI_D3hot);
suspend_report_result(pci_set_power_state, retval);
if (retval == 0) {
int wake = device_can_wakeup(&hcd->self.root_hub->dev);

wake = wake && device_may_wakeup(hcd->self.controller);

dev_dbg(hcd->self.controller, "--> PCI D3%s\n",
wake ? "/wakeup" : "");

/* Ignore these return values. We rely on pci code to
* reject requests the hardware can't implement, rather
* than coding the same thing.
*/
(void) pci_enable_wake(dev, PCI_D3hot, wake);
(void) pci_enable_wake(dev, PCI_D3cold, wake);
dev_dbg(&dev->dev, "--> PCI D3\n");
} else {
dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
retval);
(void) usb_hcd_pci_resume(dev);
pci_restore_state(dev);
}

} else if (hcd->state != HC_STATE_HALT) {
dev_dbg(hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
WARN_ON(1);
retval = -EINVAL;
}

done:
if (retval == 0) {
#ifdef CONFIG_PPC_PMAC
if (retval == 0) {
/* Disable ASIC clocks for USB */
if (machine_is(powermac)) {
struct device_node *of_node;
Expand All @@ -311,30 +308,24 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
pmac_call_feature(PMAC_FTR_USB_ENABLE,
of_node, 0, 0);
}
#endif
}
#endif

done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);

/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
* Store this function in the HCD's struct pci_driver as .resume_early.
*/
int usb_hcd_pci_resume(struct pci_dev *dev)
int usb_hcd_pci_resume_early(struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;

hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg(hcd->self.controller,
"can't resume, not suspended!\n");
return 0;
}
int retval = 0;
pci_power_t state = dev->current_state;

#ifdef CONFIG_PPC_PMAC
/* Reenable ASIC clocks for USB */
Expand All @@ -352,7 +343,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
* calls "standby", "suspend to RAM", and so on). There are also
* dirty cases when swsusp fakes a suspend in "shutdown" mode.
*/
if (dev->current_state != PCI_D0) {
if (state != PCI_D0) {
#ifdef DEBUG
int pci_pm;
u16 pmcr;
Expand All @@ -364,8 +355,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
/* Clean case: power to USB and to HC registers was
* maintained; remote wakeup is easy.
*/
dev_dbg(hcd->self.controller, "resume from PCI D%d\n",
pmcr);
dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr);
} else {
/* Clean: HC lost Vcc power, D0 uninitialized
* + Vaux may have preserved port and transceiver
Expand All @@ -376,32 +366,55 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
* + after BIOS init
* + after Linux init (HCD statically linked)
*/
dev_dbg(hcd->self.controller,
"PCI D0, from previous PCI D%d\n",
dev->current_state);
dev_dbg(&dev->dev, "resume from previous PCI D%d\n",
state);
}
#endif
/* yes, ignore these results too... */
(void) pci_enable_wake(dev, dev->current_state, 0);
(void) pci_enable_wake(dev, PCI_D3cold, 0);

retval = pci_set_power_state(dev, PCI_D0);
} else {
/* Same basic cases: clean (powered/not), dirty */
dev_dbg(hcd->self.controller, "PCI legacy resume\n");
dev_dbg(&dev->dev, "PCI legacy resume\n");
}

if (retval < 0)
dev_err(&dev->dev, "can't resume: %d\n", retval);
else
pci_restore_state(dev);

return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early);

/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as .resume.
*/
int usb_hcd_pci_resume(struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;

hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg(hcd->self.controller,
"can't resume, not suspended!\n");
return 0;
}

/* NOTE: the PCI API itself is asymmetric here. We don't need to
* pci_set_power_state(PCI_D0) since that's part of re-enabling;
* but that won't re-enable bus mastering. Yet pci_disable_device()
* explicitly disables bus mastering...
*/
retval = pci_enable_device(dev);
if (retval < 0) {
dev_err(hcd->self.controller,
"can't re-enable after resume, %d!\n", retval);
dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
retval);
return retval;
}

pci_set_master(dev);
pci_restore_state(dev);

/* yes, ignore this result too... */
(void) pci_wake_from_d3(dev, 0);

clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);

Expand All @@ -413,7 +426,6 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
usb_hc_died(hcd);
}
}

return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
Expand Down
4 changes: 3 additions & 1 deletion trunk/drivers/usb/core/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
extern void usb_hcd_pci_remove(struct pci_dev *dev);

#ifdef CONFIG_PM
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t state);
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_resume_early(struct pci_dev *dev);
extern int usb_hcd_pci_resume(struct pci_dev *dev);
#endif /* CONFIG_PM */

Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/host/ehci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ static struct pci_driver ehci_pci_driver = {

#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif
.shutdown = usb_hcd_pci_shutdown,
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/host/ohci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ static struct pci_driver ohci_pci_driver = {

#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif

Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/host/uhci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,8 @@ static struct pci_driver uhci_pci_driver = {

#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif /* PM */
};
Expand Down

0 comments on commit b2c3fa0

Please sign in to comment.