Re: [PATCH RFC 3/5] xHCI: bus power management implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Mar 03, 2010 at 06:07:55PM +0800, Libin wrote:
> >From d3055401927f70e4de7717138b4c8ecad8b8223a Mon Sep 17 00:00:00 2001
> From: Libin Yang <libin.yang@xxxxxxx>
> Date: Wed, 3 Mar 2010 13:50:41 +0800
> Subject: [PATCH 3/5] xHCI: bus power management implementation
> 
> This patch implements xHCI bus suspend/resume function hook.
> 
> In the patch it goes through all the ports and suspend/resume
> the ports if needed.

I thought the USB core ensured that all devices were suspended before
suspending the root hub.  Alan, can you confirm?

Sarah Sharp

> Signed-off-by: Crane Cai <crane.cai@xxxxxxx>
> Signed-off-by: Libin Yang <libin.yang@xxxxxxx>
> ---
>  drivers/usb/host/xhci-hcd.c |    2 +-
>  drivers/usb/host/xhci-hub.c |  148 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/host/xhci-pci.c |    2 +
>  drivers/usb/host/xhci.h     |   10 +++
>  4 files changed, 161 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
> index 4cb69e0..fe6a113 100644
> --- a/drivers/usb/host/xhci-hcd.c
> +++ b/drivers/usb/host/xhci-hcd.c
> @@ -223,7 +223,7 @@ int xhci_init(struct usb_hcd *hcd)
>   *
>   * xhci->lock must be held by caller.
>   */
> -static void xhci_work(struct xhci_hcd *xhci)
> +void xhci_work(struct xhci_hcd *xhci)
>  {
>  	u32 temp;
>  	u64 temp_64;
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index ffc2635..db048ad 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -24,6 +24,10 @@
>  
>  #include "xhci.h"
>  
> +#define	PORT_WAKE_BITS	(PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
> +#define	PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
> +			 PORT_RC | PORT_PLC | PORT_PE)
> +
>  static void xhci_hub_descriptor(struct xhci_hcd *xhci,
>  		struct usb_hub_descriptor *desc)
>  {
> @@ -508,3 +512,147 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
>  	spin_unlock_irqrestore(&xhci->lock, flags);
>  	return status ? retval : 0;
>  }
> +
> +#ifdef CONFIG_PM
> +
> +int xhci_bus_suspend(struct usb_hcd *hcd)
> +{
> +	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
> +	int port;
> +
> +	xhci_dbg(xhci, "suspend root hub\n");
> +
> +	port = HCS_MAX_PORTS(xhci->hcs_params1);
> +	spin_lock_irq(&xhci->lock);
> +	xhci->bus_suspended = 0;
> +	while (port--) {
> +		/* suspend the port if the port is not suspended */
> +		u32 __iomem *addr;
> +		u32 t1, t2;
> +
> +		addr = &xhci->op_regs->port_status_base +
> +			NUM_PORT_REGS * (port & 0xff);
> +		t1 = xhci_readl(xhci, addr);
> +		if (DEV_SUPERSPEED(t1))
> +			t1 &= ~(PORT_RWC_BITS | PORT_CEC);
> +		else
> +			t1 &= ~PORT_RWC_BITS;
> +		t2 = t1;
> +		if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
> +			xhci_dbg(xhci, "port %d not suspended\n", port);
> +			t2 = xhci_port_state_to_neutral(t2);
> +			t2 &= ~PORT_PLS_MASK;
> +			t2 |= PORT_LINK_STROBE | XDEV_U3;
> +			set_bit(port, &xhci->bus_suspended);
> +		}
> +		if (hcd->self.root_hub->do_remote_wakeup) {
> +			if (t1 & PORT_CONNECT) {
> +				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
> +				t2 &= ~PORT_WKCONN_E;
> +			} else {
> +				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
> +				t2 &= ~PORT_WKDISC_E;
> +			}
> +		} else
> +			t2 &= ~PORT_WAKE_BITS;
> +
> +		if (t1 != t2)
> +			xhci_writel(xhci, t2, addr);
> +
> +		if (DEV_HIGHSPEED(t1)) {
> +			/* enable remote wake up for USB 2.0 */
> +			u32 __iomem *addr;
> +			u32 tmp;
> +
> +			addr = &xhci->op_regs->port_power_base +
> +				NUM_PORT_REGS * (port & 0xff);
> +			tmp = xhci_readl(xhci, addr);
> +			tmp |= PORT_RWE;
> +			xhci_writel(xhci, tmp, addr);
> +		}
> +	}
> +	hcd->state = HC_STATE_SUSPENDED;
> +	xhci->next_statechange = jiffies + msecs_to_jiffies(10);
> +	spin_unlock_irq(&xhci->lock);
> +	return 0;
> +}
> +
> +int xhci_bus_resume(struct usb_hcd *hcd)
> +{
> +	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
> +	int port;
> +	u32 temp;
> +	u8 resume_needed;
> +
> +	if (time_before(jiffies, xhci->next_statechange))
> +		msleep(5);
> +
> +	spin_lock_irq(&xhci->lock);
> +	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
> +		spin_unlock_irq(&xhci->lock);
> +		return -ESHUTDOWN;
> +	}
> +
> +	/* delay the irqs */
> +	temp = xhci_readl(xhci, &xhci->op_regs->command);
> +	temp &= ~CMD_EIE;
> +	xhci_writel(xhci, temp, &xhci->op_regs->command);
> +
> +	port = HCS_MAX_PORTS(xhci->hcs_params1);
> +	while (port--) {
> +		/* Check whether need resume ports. If needed
> +		   resume port and disable remote wakeup */
> +		u32 __iomem *addr;
> +		u32	temp;
> +		addr = &xhci->op_regs->port_status_base +
> +			NUM_PORT_REGS * (port & 0xff);
> +		temp = xhci_readl(xhci, addr);
> +		if (DEV_SUPERSPEED(temp))
> +			temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
> +		else
> +			temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
> +		if (test_bit(port, &xhci->bus_suspended) &&
> +			(temp & PORT_PLS_MASK)) {
> +			temp &= ~PORT_PLS_MASK;
> +			resume_needed = 1;
> +		}
> +		xhci_writel(xhci, temp, addr);
> +		if (DEV_HIGHSPEED(temp)) {
> +			/* disable remote wake up for USB 2.0 */
> +			u32 __iomem *addr;
> +			u32 tmp;
> +
> +			addr = &xhci->op_regs->port_power_base +
> +				NUM_PORT_REGS * (port & 0xff);
> +			tmp = xhci_readl(xhci, addr);
> +			tmp &= ~PORT_RWE;
> +			xhci_writel(xhci, tmp, addr);
> +		}
> +	}
> +
> +	if (resume_needed) {
> +		spin_unlock_irq(&xhci->lock);
> +		msleep(20);
> +		spin_lock_irq(&xhci->lock);
> +	}
> +
> +	(void) xhci_readl(xhci, &xhci->op_regs->command);
> +
> +	xhci->next_statechange = jiffies + msecs_to_jiffies(5);
> +	hcd->state = HC_STATE_RUNNING;
> +	/* re-enable irqs */
> +	temp = xhci_readl(xhci, &xhci->op_regs->command);
> +	temp |= CMD_EIE;
> +	xhci_writel(xhci, temp, &xhci->op_regs->command);
> +	temp = xhci_readl(xhci, &xhci->op_regs->command);
> +
> +	spin_unlock_irq(&xhci->lock);
> +	return 0;
> +}
> +
> +#else
> +
> +#define	xhci_bus_suspend	NULL
> +#define	xhci_bus_resume		NULL
> +
> +#endif
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index 21030a9..b071d9b 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -147,6 +147,8 @@ static const struct hc_driver xhci_pci_hc_driver = {
>  	/* Root hub support */
>  	.hub_control =		xhci_hub_control,
>  	.hub_status_data =	xhci_hub_status_data,
> +	.bus_suspend =		xhci_bus_suspend,
> +	.bus_resume =		xhci_bus_resume,
>  };
>  
>  /*-------------------------------------------------------------------------*/
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 4c65d1e..fd092ca 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -357,6 +357,8 @@ struct xhci_op_regs {
>  #define PORT_U2_TIMEOUT(p)	(((p) & 0xff) << 8)
>  /* Bits 24:31 for port testing */
>  
> +/* USB2 Protocol PORTSPMSC */
> +#define PORT_RWE	(1 << 0x3)
>  
>  /**
>   * struct xhci_intr_reg - Interrupt Register Set
> @@ -1096,6 +1098,11 @@ struct xhci_hcd {
>  #endif
>  	/* Host controller watchdog timer structures */
>  	unsigned int		xhc_state;
> +
> +	unsigned long		bus_suspended;
> +	unsigned long		next_statechange;
> +
> +	u32			command;
>  /* Host controller is dying - not responding to commands. "I'm not dead yet!"
>   *
>   * xHC interrupts have been disabled and a watchdog timer will (or has already)
> @@ -1263,6 +1270,7 @@ void xhci_quiesce(struct xhci_hcd *xhci);
>  int xhci_halt(struct xhci_hcd *xhci);
>  int xhci_reset(struct xhci_hcd *xhci);
>  int xhci_init(struct usb_hcd *hcd);
> +void xhci_work(struct xhci_hcd *xhci);
>  int xhci_run(struct usb_hcd *hcd);
>  void xhci_stop(struct usb_hcd *hcd);
>  void xhci_shutdown(struct usb_hcd *hcd);
> @@ -1329,6 +1337,8 @@ void ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
>  int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
>  		char *buf, u16 wLength);
>  int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
> +int xhci_bus_suspend(struct usb_hcd *hcd);
> +int xhci_bus_resume(struct usb_hcd *hcd);
>  u32 xhci_port_state_to_neutral(u32 state);
>  u32 xhci_stop_endpoints(struct xhci_hcd *xhci, u16 port, int stop,
>  		unsigned long *flags);
> -- 
> 1.6.0.4
> 
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux