Пример #1
0
/**
 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
 * @dev: USB Host Controller being suspended
 * @state: state that the controller is going into
 *
 * Store this function in the HCD's struct pci_driver as suspend().
 */
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
	struct usb_hcd		*hcd;
	int			retval = 0;
	int			has_pci_pm;

	hcd = pci_get_drvdata(dev);

	/* 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.
	 */
	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
	if (has_pci_pm)
		dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
			dev->current_state, state);

	switch (hcd->state) {
	case USB_STATE_HALT:
		dev_dbg (hcd->self.controller, "halted; hcd not suspended\n");
		break;
	case HCD_STATE_SUSPENDED:
		dev_dbg (hcd->self.controller, "hcd already suspended\n");
		break;
	default:
		retval = hcd->driver->suspend (hcd, state);
		if (retval)
			dev_dbg (hcd->self.controller, 
					"suspend fail, retval %d\n",
					retval);
		else {
			hcd->state = HCD_STATE_SUSPENDED;
			pci_save_state (dev, hcd->pci_state);
#ifdef	CONFIG_USB_SUSPEND
			pci_enable_wake (dev, state, hcd->remote_wakeup);
			pci_enable_wake (dev, 4, hcd->remote_wakeup);
#endif
			/* no DMA or IRQs except in D0 */
			pci_disable_device (dev);
			free_irq (hcd->irq, hcd);
			
			if (has_pci_pm)
				retval = pci_set_power_state (dev, state);
			dev->dev.power.power_state = state;
			if (retval < 0) {
				dev_dbg (&dev->dev,
						"PCI suspend fail, %d\n",
						retval);
				(void) usb_hcd_pci_resume (dev);
			}
		}
	}
	return retval;
}
Пример #2
0
Файл: tusb.c Проект: 1587/ltp
/*
 * test_hcd_resume
 *	make call to resume device for power management
 *	so that device will be active and able to use
 *	again
 */
static int test_hcd_resume()
{
	int rc;
	struct pci_dev *pdev = NULL;

	/* check that pdev is set */
	if (!(pdev = ltp_usb.pdev)) {
		printk("tusb: Cant find host controller pci_dev pointer\n");
		return 1;
	}

	/* make call and check return value */
	rc = usb_hcd_pci_resume(pdev);
	if (rc)
		printk("tusb: Resume got retval, failure\n");
	else
		printk("tusb: Resume success\n");

	return rc;
}
Пример #3
0
/**
 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
 * @dev: USB Host Controller being suspended
 * @state: state that the controller is going into
 *
 * Store this function in the HCD's struct pci_driver as suspend().
 */
int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
    struct usb_hcd		*hcd;
    int			retval = 0;
    int			has_pci_pm;

    hcd = pci_get_drvdata(dev);

    /* 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.
     */
    has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
    if (state > 4)
        state = 4;

    switch (hcd->state) {

    /* entry if root hub wasn't yet suspended ... from sysfs,
     * without autosuspend, or if USB_SUSPEND isn't configured.
     */
    case USB_STATE_RUNNING:
        hcd->state = USB_STATE_QUIESCING;
        retval = hcd->driver->suspend (hcd, state);
        if (retval) {
            dev_dbg (hcd->self.controller,
                     "suspend fail, retval %d\n",
                     retval);
            break;
        }
        hcd->state = HCD_STATE_SUSPENDED;
    /* FALLTHROUGH */

    /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
     * controller and/or root hub will already have been suspended,
     * but it won't be ready for a PCI resume call.
     *
     * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
     * have been called, otherwise root hub timers still run ...
     */
    case HCD_STATE_SUSPENDED:
        if (state <= dev->current_state)
            break;

        /* no DMA or IRQs except in D0 */
        if (!dev->current_state) {
            pci_save_state (dev);
            pci_disable_device (dev);
            free_irq (hcd->irq, hcd);
        }

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

        /* POLICY: ignore D1/D2/D3hot differences;
         * we know D3hot will always work.
         */
        retval = pci_set_power_state (dev, state);
        if (retval < 0 && state < 3) {
            retval = pci_set_power_state (dev, 3);
            if (retval == 0)
                state = 3;
        }
        if (retval == 0) {
            dev_dbg (hcd->self.controller, "--> PCI %s\n",
                     pci_state(dev->current_state));
#ifdef	CONFIG_USB_SUSPEND
            pci_enable_wake (dev, state, hcd->remote_wakeup);
            pci_enable_wake (dev, 4, hcd->remote_wakeup);
#endif
        } else if (retval < 0) {
            dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n",
                     pci_state(state), retval);
            (void) usb_hcd_pci_resume (dev);
            break;
        }
        break;
    default:
        dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
                 hcd->state);
        retval = -EINVAL;
        break;
    }

    /* update power_state **ONLY** to make sysfs happier */
    if (retval == 0)
        dev->dev.power.power_state = state;
    return retval;
}
Пример #4
0
/**
 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
 * @dev: USB Host Controller being suspended
 * @message: semantics in flux
 *
 * 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;
	int			retval = 0;
	int			has_pci_pm;

	hcd = pci_get_drvdata(dev);

	/* 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.)
	 */

	/* 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.
	 */
	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);

	switch (hcd->state) {

	/* entry if root hub wasn't yet suspended ... from sysfs,
	 * without autosuspend, or if USB_SUSPEND isn't configured.
	 */
	case HC_STATE_RUNNING:
		hcd->state = HC_STATE_QUIESCING;
		retval = hcd->driver->suspend (hcd, message);
		if (retval) {
			dev_dbg (hcd->self.controller, 
					"suspend fail, retval %d\n",
					retval);
			break;
		}
		hcd->state = HC_STATE_SUSPENDED;
		/* FALLTHROUGH */

	/* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
	 * controller and/or root hub will already have been suspended,
	 * but it won't be ready for a PCI resume call.
	 *
	 * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
	 * have been called, otherwise root hub timers still run ...
	 */
	case HC_STATE_SUSPENDED:
		/* no DMA or IRQs except when HC is active */
		if (dev->current_state == PCI_D0) {
			free_irq (hcd->irq, hcd);
			pci_save_state (dev);
			pci_disable_device (dev);
		}

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

		/* NOTE:  dev->current_state becomes nonzero only here, and
		 * only for devices that support PCI PM.  Also, exiting
		 * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
		 * some device state (e.g. as part of clock reinit).
		 */
		retval = pci_set_power_state (dev, PCI_D3hot);
		if (retval == 0) {
			dev_dbg (hcd->self.controller, "--> PCI D3\n");
			pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
			pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
		} else if (retval < 0) {
			dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
					retval);
			(void) usb_hcd_pci_resume (dev);
			break;
		}
		break;
	default:
		dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
			hcd->state);
		WARN_ON(1);
		retval = -EINVAL;
		break;
	}

	/* update power_state **ONLY** to make sysfs happier */
	if (retval == 0)
		dev->dev.power.power_state = message;
	return retval;
}
Пример #5
0
/**
 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
 * @dev: USB Host Controller being suspended
 * @message: semantics in flux
 *
 * 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;
	int			retval = 0;
	int			has_pci_pm;

	hcd = pci_get_drvdata(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
	 * didn't; writing sysfs power/state files ignores such rules...
	 *
	 * We must ignore the FREEZE vs SUSPEND distinction here, because
	 * otherwise the swsusp will save (and restore) garbage state.
	 */
	if (!(hcd->state == HC_STATE_SUSPENDED ||
			hcd->state == HC_STATE_HALT))
		return -EBUSY;

	if (hcd->driver->suspend) {
		retval = hcd->driver->suspend(hcd, message);
		suspend_report_result(hcd->driver->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.)
	 */

	/* 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.
	 */
	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);

	/* 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) {

		/* no DMA or IRQs except when HC is active */
		if (dev->current_state == PCI_D0) {
			pci_save_state (dev);
			pci_disable_device (dev);
		}

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

		/* NOTE:  dev->current_state becomes nonzero only here, and
		 * only for devices that support PCI PM.  Also, exiting
		 * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
		 * some device state (e.g. as part of clock reinit).
		 */
		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);
		} else {
			dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
					retval);
			(void) usb_hcd_pci_resume (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) {
		dev->dev.power.power_state = PMSG_SUSPEND;

#ifdef CONFIG_PPC_PMAC
		/* Disable ASIC clocks for USB */
		if (machine_is(powermac)) {
			struct device_node	*of_node;

			of_node = pci_device_to_OF_node (dev);
			if (of_node)
				pmac_call_feature(PMAC_FTR_USB_ENABLE,
							of_node, 0, 0);
		}
#endif
	}

	return retval;
}