11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2d49d431744007cec0ee1a3ade96f9e0f100c7907David Brownell * Copyright (C) 2001-2004 by David Brownell
353bd6a601a87bb6d0df844872bc15fd4e8d127ceDavid Brownell *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License as published by the
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Free Software Foundation; either version 2 of the License, or (at your
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * option) any later version.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for more details.
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software Foundation,
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* this file is part of ehci-hcd.c */
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * EHCI Root Hub ... the nonsharable stuff
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Registers don't need cpu_to_le32, that happens transparently
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
3083722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin#include <linux/usb/otg.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3258a97ffeb2297f154659f339d77eb3f32c4d8b3eAlan Stern#define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
3358a97ffeb2297f154659f339d77eb3f32c4d8b3eAlan Stern
34aff6d18f95bb81b2d07994372c8edcc2c2b41180Alan Stern#ifdef	CONFIG_PM
35aff6d18f95bb81b2d07994372c8edcc2c2b41180Alan Stern
36383975d765523a56dc43a6cd6d52e9b376800cf2Alan Sternstatic int ehci_hub_control(
37383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	struct usb_hcd	*hcd,
38383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u16		typeReq,
39383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u16		wValue,
40383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u16		wIndex,
41383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	char		*buf,
42383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u16		wLength
43383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern);
44383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
45383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern/* After a power loss, ports that were owned by the companion must be
46383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern * reset so that the companion can still own them.
47383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern */
48383975d765523a56dc43a6cd6d52e9b376800cf2Alan Sternstatic void ehci_handover_companion_ports(struct ehci_hcd *ehci)
49383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern{
50383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u32 __iomem	*reg;
51383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u32		status;
52383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	int		port;
53383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	__le32		buf;
54383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	struct usb_hcd	*hcd = ehci_to_hcd(ehci);
55383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
56383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	if (!ehci->owned_ports)
57383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		return;
58383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
59383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	/* Give the connections some time to appear */
60383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	msleep(20);
61383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
62383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	port = HCS_N_PORTS(ehci->hcs_params);
63383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	while (port--) {
64383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		if (test_bit(port, &ehci->owned_ports)) {
65383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			reg = &ehci->regs->port_status[port];
663c519b846c4d5edf7c94d1eede42445a815bf65cAlan Stern			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
67383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
68383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			/* Port already owned by companion? */
69383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			if (status & PORT_OWNER)
70383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				clear_bit(port, &ehci->owned_ports);
713c519b846c4d5edf7c94d1eede42445a815bf65cAlan Stern			else if (test_bit(port, &ehci->companion_ports))
723c519b846c4d5edf7c94d1eede42445a815bf65cAlan Stern				ehci_writel(ehci, status & ~PORT_PE, reg);
73383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			else
74383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				ehci_hub_control(hcd, SetPortFeature,
75383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern						USB_PORT_FEAT_RESET, port + 1,
76383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern						NULL, 0);
77383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		}
78383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	}
79383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
80383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	if (!ehci->owned_ports)
81383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		return;
82383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	msleep(90);		/* Wait for resets to complete */
83383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
84383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	port = HCS_N_PORTS(ehci->hcs_params);
85383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	while (port--) {
86383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		if (test_bit(port, &ehci->owned_ports)) {
87383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			ehci_hub_control(hcd, GetPortStatus,
88383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern					0, port + 1,
89383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern					(char *) &buf, sizeof(buf));
90383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
91383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			/* The companion should now own the port,
92383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			 * but if something went wrong the port must not
93383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			 * remain enabled.
94383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			 */
95383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			reg = &ehci->regs->port_status[port];
96383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
97383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			if (status & PORT_OWNER)
98383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				ehci_writel(ehci, status | PORT_CSC, reg);
99383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			else {
100383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				ehci_dbg(ehci, "failed handover port %d: %x\n",
101383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern						port + 1, status);
102383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				ehci_writel(ehci, status & ~PORT_PE, reg);
103383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			}
104383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		}
105383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	}
106383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
107383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	ehci->owned_ports = 0;
108383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern}
109383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern
1105407a3c3d942e75d4d123d213fd692bce5acc961Felipe Balbistatic int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
111294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett{
112294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	int i = HCS_N_PORTS(ehci->hcs_params);
113294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
114294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	/* First check if the controller indicates a change event */
115294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
116294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)
117294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett		return 1;
118294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
119294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	/*
120294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	 * Not all controllers appear to update this while going from D3 to D0,
121294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	 * so check the individual port status registers as well
122294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	 */
123294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
124294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	while (i--)
125294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett		if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC)
126294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett			return 1;
127294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
128294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	return 0;
129294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett}
130294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett
1310b0cd6c81defc4e4fccb9e9103666547fefdb9c0Fabio Estevamstatic __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
1324147200d25c423e627ab4487530b3d9f2ef829c8Alan Stern		bool suspending, bool do_wakeup)
13316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern{
13416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	int		port;
13516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	u32		temp;
136148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai	unsigned long	flags;
13716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
13816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	/* If remote wakeup is enabled for the root hub but disabled
13916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	 * for the controller, we must adjust all the port wakeup flags
14016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	 * when the controller is suspended or resumed.  In all other
14116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	 * cases they don't need to be changed.
14216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	 */
1434147200d25c423e627ab4487530b3d9f2ef829c8Alan Stern	if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
14416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		return;
14516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
146148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai	spin_lock_irqsave(&ehci->lock, flags);
147148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai
14816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	/* clear phy low-power mode before changing wakeup flags */
14916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	if (ehci->has_hostpc) {
15016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		port = HCS_N_PORTS(ehci->hcs_params);
15116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		while (port--) {
15216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			u32 __iomem	*hostpc_reg;
15316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
15416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
15516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern					+ HOSTPC0 + 4 * port);
15616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			temp = ehci_readl(ehci, hostpc_reg);
15716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);
15816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
159148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai		spin_unlock_irqrestore(&ehci->lock, flags);
16016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		msleep(5);
161148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai		spin_lock_irqsave(&ehci->lock, flags);
16216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	}
16316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
16416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	port = HCS_N_PORTS(ehci->hcs_params);
16516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	while (port--) {
16616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		u32 __iomem	*reg = &ehci->regs->port_status[port];
16716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		u32		t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
16816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		u32		t2 = t1 & ~PORT_WAKE_BITS;
16916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
17016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		/* If we are suspending the controller, clear the flags.
17116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		 * If we are resuming the controller, set the wakeup flags.
17216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		 */
17316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		if (!suspending) {
17416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if (t1 & PORT_CONNECT)
17516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
17616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			else
17716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
17816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
17916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		ehci_vdbg(ehci, "port %d, %08x -> %08x\n",
18016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				port + 1, t1, t2);
18116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		ehci_writel(ehci, t2, reg);
18216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	}
18316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
18416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	/* enter phy low-power mode again */
18516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	if (ehci->has_hostpc) {
18616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		port = HCS_N_PORTS(ehci->hcs_params);
18716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		while (port--) {
18816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			u32 __iomem	*hostpc_reg;
18916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
19016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
19116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern					+ HOSTPC0 + 4 * port);
19216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			temp = ehci_readl(ehci, hostpc_reg);
19316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
19416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
19516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	}
196ee0b9be829803e3ff5adec7456bd59a08425ffa1Alan Stern
197ee0b9be829803e3ff5adec7456bd59a08425ffa1Alan Stern	/* Does the root hub have a port wakeup pending? */
198294d95f2cbc2aef5346258f216cd9df570e271a5Matthew Garrett	if (!suspending && ehci_port_change(ehci))
199ee0b9be829803e3ff5adec7456bd59a08425ffa1Alan Stern		usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
200148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai
201148fc55fd0449683a1d15bf219ad8d8b6fa17545Yin Kangkai	spin_unlock_irqrestore(&ehci->lock, flags);
20216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern}
20316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
2040c0382e32d46f606951010b202382be14d180a17Alan Sternstatic int ehci_bus_suspend (struct usb_hcd *hcd)
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			port;
2088c03356a559ced6fa78931f498193f776d67e445Alan Stern	int			mask;
20916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	int			changed;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2118c774fe8a0284aff9e4c7ea43f5154fd46da325cAlan Stern	ehci_dbg(ehci, "suspend root hub\n");
2128c774fe8a0284aff9e4c7ea43f5154fd46da325cAlan Stern
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (time_before (jiffies, ehci->next_statechange))
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msleep(5);
215f8fa7571a928d6d0e1b7444b0ea69ec7dc7db3b6Alan Stern	del_timer_sync(&ehci->watchdog);
216f8fa7571a928d6d0e1b7444b0ea69ec7dc7db3b6Alan Stern	del_timer_sync(&ehci->iaa_watchdog);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irq (&ehci->lock);
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
220cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	/* Once the controller is stopped, port resumes that are already
221cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	 * in progress won't complete.  Hence if remote wakeup is enabled
222cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	 * for the root hub and any ports are in the middle of a resume or
223cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	 * remote wakeup, we must fail the suspend.
224cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	 */
225cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	if (hcd->self.root_hub->do_remote_wakeup) {
226a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern		if (ehci->resuming_ports) {
227a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			spin_unlock_irq(&ehci->lock);
228a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			ehci_dbg(ehci, "suspend failed because a port is resuming\n");
229a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			return -EBUSY;
230cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern		}
231cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	}
232cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* stop schedules, clean any completed work */
234e8799906045302776b35b66b16495c575db3b69cAlan Stern	if (ehci->rh_state == EHCI_RH_RUNNING)
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ehci_quiesce (ehci);
236083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci->command = ehci_readl(ehci, &ehci->regs->command);
2377d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells	ehci_work(ehci);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2398c03356a559ced6fa78931f498193f776d67e445Alan Stern	/* Unlike other USB host controller types, EHCI doesn't have
2408c03356a559ced6fa78931f498193f776d67e445Alan Stern	 * any notion of "global" or bus-wide suspend.  The driver has
2418c03356a559ced6fa78931f498193f776d67e445Alan Stern	 * to manually suspend all the active unsuspended ports, and
2428c03356a559ced6fa78931f498193f776d67e445Alan Stern	 * then manually resume them in the bus_resume() routine.
2438c03356a559ced6fa78931f498193f776d67e445Alan Stern	 */
2448c03356a559ced6fa78931f498193f776d67e445Alan Stern	ehci->bus_suspended = 0;
245383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	ehci->owned_ports = 0;
24616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	changed = 0;
247cec3a53c7fe794237b582e8e77fc0e48465e65eeAlan Stern	port = HCS_N_PORTS(ehci->hcs_params);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (port--) {
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u32 __iomem	*reg = &ehci->regs->port_status [port];
250083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		u32		t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
25116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		u32		t2 = t1 & ~PORT_WAKE_BITS;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2538c03356a559ced6fa78931f498193f776d67e445Alan Stern		/* keep track of which ports we suspend */
254383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		if (t1 & PORT_OWNER)
255383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			set_bit(port, &ehci->owned_ports);
256383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			t2 |= PORT_SUSPEND;
2588c03356a559ced6fa78931f498193f776d67e445Alan Stern			set_bit(port, &ehci->bus_suspended);
2598c03356a559ced6fa78931f498193f776d67e445Alan Stern		}
2608c03356a559ced6fa78931f498193f776d67e445Alan Stern
26116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		/* enable remote wakeup on all ports, if told to do so */
262331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du		if (hcd->self.root_hub->do_remote_wakeup) {
263331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			/* only enable appropriate wake bits, otherwise the
264331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 * hardware can not go phy low power mode. If a race
265331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 * condition happens here(connection change during bits
266331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 * set), the port change detection will finally fix it.
267331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 */
26816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if (t1 & PORT_CONNECT)
269331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
27016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			else
271331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
27216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (t1 != t2) {
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				port + 1, t1, t2);
277083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, t2, reg);
27816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			changed = 1;
27916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
28016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	}
281331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du
28216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	if (changed && ehci->has_hostpc) {
28316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		spin_unlock_irq(&ehci->lock);
28416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		msleep(5);	/* 5 ms for HCD to enter low-power mode */
28516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		spin_lock_irq(&ehci->lock);
28616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
28716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		port = HCS_N_PORTS(ehci->hcs_params);
28816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		while (port--) {
28916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			u32 __iomem	*hostpc_reg;
29016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			u32		t3;
29116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
29216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
29316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern					+ HOSTPC0 + 4 * port);
29416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			t3 = ehci_readl(ehci, hostpc_reg);
29516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
29616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			t3 = ehci_readl(ehci, hostpc_reg);
29716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci_dbg(ehci, "Port %d phy low-power mode %s\n",
298331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du					port, (t3 & HOSTPC_PHCD) ?
299331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du					"succeeded" : "failed");
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
303cd930c931417295ac6b4d9fea62accc67192ac34Alan Stern	/* Apparently some devices need a >= 1-uframe delay here */
304cd930c931417295ac6b4d9fea62accc67192ac34Alan Stern	if (ehci->bus_suspended)
305cd930c931417295ac6b4d9fea62accc67192ac34Alan Stern		udelay(150);
306cd930c931417295ac6b4d9fea62accc67192ac34Alan Stern
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* turn off now-idle HC */
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ehci_halt (ehci);
309e8799906045302776b35b66b16495c575db3b69cAlan Stern	ehci->rh_state = EHCI_RH_SUSPENDED;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311cdc647a9b75741659bfc6acc44a6b3a646ad53bfDavid Brownell	if (ehci->reclaim)
312cdc647a9b75741659bfc6acc44a6b3a646ad53bfDavid Brownell		end_unlink_async(ehci);
313cdc647a9b75741659bfc6acc44a6b3a646ad53bfDavid Brownell
3148c03356a559ced6fa78931f498193f776d67e445Alan Stern	/* allow remote wakeup */
3158c03356a559ced6fa78931f498193f776d67e445Alan Stern	mask = INTR_MASK;
31658a97ffeb2297f154659f339d77eb3f32c4d8b3eAlan Stern	if (!hcd->self.root_hub->do_remote_wakeup)
3178c03356a559ced6fa78931f498193f776d67e445Alan Stern		mask &= ~STS_PCD;
318083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, mask, &ehci->regs->intr_enable);
319083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_readl(ehci, &ehci->regs->intr_enable);
3208c03356a559ced6fa78931f498193f776d67e445Alan Stern
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ehci->next_statechange = jiffies + msecs_to_jiffies(10);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irq (&ehci->lock);
323015798b2f166725b1dae2b07b5ffb127ab187be0Jon Hunter
324015798b2f166725b1dae2b07b5ffb127ab187be0Jon Hunter	/* ehci_work() may have re-enabled the watchdog timer, which we do not
325015798b2f166725b1dae2b07b5ffb127ab187be0Jon Hunter	 * want, and so we must delete any pending watchdog timer events.
326015798b2f166725b1dae2b07b5ffb127ab187be0Jon Hunter	 */
327015798b2f166725b1dae2b07b5ffb127ab187be0Jon Hunter	del_timer_sync(&ehci->watchdog);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller has locked the root hub, and should reset/reinit on error */
3330c0382e32d46f606951010b202382be14d180a17Alan Sternstatic int ehci_bus_resume (struct usb_hcd *hcd)
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32			temp;
337383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u32			power_okay;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			i;
339d0f2fb2500b1c5fe4967eb45d8c9bc758d7aef80Wang Zhi	unsigned long		resume_needed = 0;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (time_before (jiffies, ehci->next_statechange))
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msleep(5);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irq (&ehci->lock);
344541c7d432f76771079e7c295d596ea47cc6a3030Alan Stern	if (!HCD_HW_ACCESSIBLE(hcd)) {
345cfa59dab27d1b282886e7772a8f9548236883892Alan Stern		spin_unlock_irq(&ehci->lock);
346cfa59dab27d1b282886e7772a8f9548236883892Alan Stern		return -ESHUTDOWN;
347cfa59dab27d1b282886e7772a8f9548236883892Alan Stern	}
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
349ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel	if (unlikely(ehci->debug)) {
350872d3599622702b59a00521c0a5b8ff3822e1803Jason Wessel		if (!dbgp_reset_prep())
351ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel			ehci->debug = NULL;
352ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel		else
353ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel			dbgp_external_startup();
354ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel	}
355ad45f1dc836cb175e9aeea927837dd48039d652cJason Wessel
356f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	/* Ideally and we've got a real resume here, and no port's power
357f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	 * was lost.  (For PCI, that means Vaux was maintained.)  But we
358f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	 * could instead be restoring a swsusp snapshot -- so that BIOS was
359f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	 * the last user of the controller, not reset/pm hardware keeping
360f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	 * state we gave to it.
361f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell	 */
362383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
363383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	ehci_dbg(ehci, "resume root hub%s\n",
364383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern			power_okay ? "" : " after power loss");
365f03c17fc9abe8582d6ad830290b3093fdf1eea61David Brownell
3668c03356a559ced6fa78931f498193f776d67e445Alan Stern	/* at least some APM implementations will try to deliver
3678c03356a559ced6fa78931f498193f776d67e445Alan Stern	 * IRQs right away, so delay them until we're ready.
3688c03356a559ced6fa78931f498193f776d67e445Alan Stern	 */
369083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
3708c03356a559ced6fa78931f498193f776d67e445Alan Stern
3718c03356a559ced6fa78931f498193f776d67e445Alan Stern	/* re-init operational registers */
372083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, 0, &ehci->regs->segment);
373083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
374083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* restore CMD_RUN, framelist size, and irq threshold */
377083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, ehci->command, &ehci->regs->command);
378e8799906045302776b35b66b16495c575db3b69cAlan Stern	ehci->rh_state = EHCI_RH_RUNNING;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
380e198a31489146bb723fef179e5d1d18c8225f246Alan Stern	/* Some controller/firmware combinations need a delay during which
381e198a31489146bb723fef179e5d1d18c8225f246Alan Stern	 * they set up the port statuses.  See Bugzilla #8190. */
3823a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	spin_unlock_irq(&ehci->lock);
3833a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	msleep(8);
3843a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	spin_lock_irq(&ehci->lock);
385e198a31489146bb723fef179e5d1d18c8225f246Alan Stern
38616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	/* clear phy low-power mode before resume */
38716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	if (ehci->bus_suspended && ehci->has_hostpc) {
38816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		i = HCS_N_PORTS(ehci->hcs_params);
38916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		while (i--) {
39016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if (test_bit(i, &ehci->bus_suspended)) {
39116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				u32 __iomem	*hostpc_reg;
39216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
39316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs
39416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern						+ HOSTPC0 + 4 * i);
39516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				temp = ehci_readl(ehci, hostpc_reg);
39616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				ehci_writel(ehci, temp & ~HOSTPC_PHCD,
39716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern						hostpc_reg);
39816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			}
39916032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		}
40016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		spin_unlock_irq(&ehci->lock);
40116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		msleep(5);
40216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern		spin_lock_irq(&ehci->lock);
40316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern	}
40416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
4058c03356a559ced6fa78931f498193f776d67e445Alan Stern	/* manually resume the ports we suspended during bus_suspend() */
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = HCS_N_PORTS (ehci->hcs_params);
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i--) {
408083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
40958a97ffeb2297f154659f339d77eb3f32c4d8b3eAlan Stern		temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
4108c03356a559ced6fa78931f498193f776d67e445Alan Stern		if (test_bit(i, &ehci->bus_suspended) &&
4113a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita				(temp & PORT_SUSPEND)) {
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			temp |= PORT_RESUME;
413d0f2fb2500b1c5fe4967eb45d8c9bc758d7aef80Wang Zhi			set_bit(i, &resume_needed);
4143a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita		}
415083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4173a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita
4183a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	/* msleep for 20ms only if code is trying to resume port */
4193a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	if (resume_needed) {
4203a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita		spin_unlock_irq(&ehci->lock);
4213a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita		msleep(20);
4223a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita		spin_lock_irq(&ehci->lock);
4233a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita	}
4243a4e72cbf2ac4435630a2b03bd25e60ef5967e99Vikram Pandita
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = HCS_N_PORTS (ehci->hcs_params);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i--) {
427083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
428d0f2fb2500b1c5fe4967eb45d8c9bc758d7aef80Wang Zhi		if (test_bit(i, &resume_needed)) {
4298c03356a559ced6fa78931f498193f776d67e445Alan Stern			temp &= ~(PORT_RWC_BITS | PORT_RESUME);
430083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
4318c03356a559ced6fa78931f498193f776d67e445Alan Stern			ehci_vdbg (ehci, "resumed port %d\n", i + 1);
4328c03356a559ced6fa78931f498193f776d67e445Alan Stern		}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
434083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	(void) ehci_readl(ehci, &ehci->regs->command);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* maybe re-activate the schedule(s) */
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	temp = 0;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ehci->async->qh_next.qh)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp |= CMD_ASE;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ehci->periodic_sched)
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp |= CMD_PSE;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (temp) {
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ehci->command |= temp;
444083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		ehci_writel(ehci, ehci->command, &ehci->regs->command);
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ehci->next_statechange = jiffies + msecs_to_jiffies(5);
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now we can safely re-enable irqs */
450083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irq (&ehci->lock);
4533bb1af5243d41af9518728445e9c9bd30dd47237Alan Stern	ehci_handover_companion_ports(ehci);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4590c0382e32d46f606951010b202382be14d180a17Alan Stern#define ehci_bus_suspend	NULL
4600c0382e32d46f606951010b202382be14d180a17Alan Stern#define ehci_bus_resume		NULL
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif	/* CONFIG_PM */
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46657e06c11372eccf5acebdd4664eb025fee76c561Alan Stern/*
46790da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao * Sets the owner of a port
46857e06c11372eccf5acebdd4664eb025fee76c561Alan Stern */
46990da096ee46b682011b7d549e52b81cf9742e60bBalaji Raostatic void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
47057e06c11372eccf5acebdd4664eb025fee76c561Alan Stern{
47157e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	u32 __iomem		*status_reg;
47257e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	u32			port_status;
47390da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao	int 			try;
47457e06c11372eccf5acebdd4664eb025fee76c561Alan Stern
47590da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao	status_reg = &ehci->regs->port_status[portnum];
47657e06c11372eccf5acebdd4664eb025fee76c561Alan Stern
47757e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	/*
47857e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	 * The controller won't set the OWNER bit if the port is
47957e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	 * enabled, so this loop will sometimes require at least two
48057e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	 * iterations: one to disable the port and one to set OWNER.
48157e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	 */
48257e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	for (try = 4; try > 0; --try) {
48357e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		spin_lock_irq(&ehci->lock);
48457e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		port_status = ehci_readl(ehci, status_reg);
48557e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		if ((port_status & PORT_OWNER) == new_owner
48657e06c11372eccf5acebdd4664eb025fee76c561Alan Stern				|| (port_status & (PORT_OWNER | PORT_CONNECT))
48757e06c11372eccf5acebdd4664eb025fee76c561Alan Stern					== 0)
48857e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			try = 0;
48957e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		else {
49057e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			port_status ^= PORT_OWNER;
49157e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			port_status &= ~(PORT_PE | PORT_RWC_BITS);
49257e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			ehci_writel(ehci, port_status, status_reg);
49357e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		}
49457e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		spin_unlock_irq(&ehci->lock);
49557e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		if (try > 1)
49657e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			msleep(5);
49757e06c11372eccf5acebdd4664eb025fee76c561Alan Stern	}
49890da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao}
49990da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao
50057e06c11372eccf5acebdd4664eb025fee76c561Alan Stern/*-------------------------------------------------------------------------*/
50157e06c11372eccf5acebdd4664eb025fee76c561Alan Stern
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int check_reset_complete (
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd	*ehci,
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		index,
505e6316565e568b3b5733be10cfca3c27259bef499Alan Stern	u32 __iomem	*status_reg,
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		port_status
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
508cd4cdc93ce04b562412df6c0cf25c564ab2522acDavid Brownell	if (!(port_status & PORT_CONNECT))
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return port_status;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if reset finished and it's still not enabled -- handoff */
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(port_status & PORT_PE)) {
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* with integrated TT, there's nobody to hand it to! */
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ehci_is_TDI(ehci)) {
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ehci_dbg (ehci,
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"Failed to enable port %d on root hub TT\n",
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				index+1);
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return port_status;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ehci_dbg (ehci, "port %d full speed --> companion\n",
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			index + 1);
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// what happens if HCS_N_CC(params) == 0 ?
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port_status |= PORT_OWNER;
52710f6524a8ef1413a8cbd952673997013183fe2a9David Brownell		port_status &= ~PORT_RWC_BITS;
528e6316565e568b3b5733be10cfca3c27259bef499Alan Stern		ehci_writel(ehci, port_status, status_reg);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
530796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug		/* ensure 440EPX ohci controller state is operational */
531796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug		if (ehci->has_amcc_usb23)
532796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug			set_ohci_hcfs(ehci, 1);
533796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug	} else {
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ehci_dbg (ehci, "port %d high speed\n", index + 1);
535796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug		/* ensure 440EPx ohci controller state is suspended */
536796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug		if (ehci->has_amcc_usb23)
537796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug			set_ohci_hcfs(ehci, 0);
538796bcae7361c28cf825780f6f1aac9dd3411394eVitaly Bordug	}
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return port_status;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* build "status change" packet (one or two bytes) from HC registers */
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsehci_hub_status_data (struct usb_hcd *hcd, char *buf)
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
552a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern	u32		temp, status;
55393f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	u32		mask;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		ports, i, retval = 1;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
5565a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du	u32		ppcd = 0;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* init status to no-changes */
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf [0] = 0;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ports = HCS_N_PORTS (ehci->hcs_params);
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ports > 7) {
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf [1] = 0;
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval++;
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
56553bd6a601a87bb6d0df844872bc15fd4e8d127ceDavid Brownell
566a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern	/* Inform the core about resumes-in-progress by returning
567a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern	 * a non-zero value even if there are no status changes.
568a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern	 */
569a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern	status = ehci->resuming_ports;
570a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern
57193f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	/* Some boards (mostly VIA?) report bogus overcurrent indications,
57293f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	 * causing massive log spam unless we completely ignore them.  It
5733a4fa0a25da81600ea0bcd75692ae8ca6050d165Robert P. J. Day	 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
57493f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	 * always set, seem to clear PORT_OCC and PORT_CSC when writing to
57593f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	 * PORT_POWER; that's surprising, but maybe within-spec.
57693f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	 */
57793f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	if (!ignore_oc)
57893f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell		mask = PORT_CSC | PORT_PEC | PORT_OCC;
57993f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	else
58093f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell		mask = PORT_CSC | PORT_PEC;
58193f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell	// PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND
58293f1a47c4af34c4ee014b3d2aae70089b3b69f72David Brownell
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* no hub change reports (bit 0) for now (power, ...) */
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* port N changes (bit N)? */
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&ehci->lock, flags);
5875a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du
5885a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du	/* get per-port change detect bits */
5895a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du	if (ehci->has_ppcd)
5905a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du		ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
5915a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ports; i++) {
5935a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du		/* leverage per-port change bits feature */
5945a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du		if (ehci->has_ppcd && !(ppcd & (1 << i)))
5955a9cdf332eae724b11906cb1712e3a662eba32b2Alek Du			continue;
596083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
597625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern
598625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		/*
599625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * Return status information even for ports with OWNER set.
600625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * Otherwise khubd wouldn't see the disconnect event when a
601625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * high-speed device is switched over to the companion
602625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * controller by the user.
603625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 */
604625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern
605eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern		if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
606eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern				|| (ehci->reset_done[i] && time_after_eq(
607eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern					jiffies, ehci->reset_done[i]))) {
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (i < 7)
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    buf [0] |= 1 << (i + 1);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    buf [1] |= 1 << (i - 7);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = STS_PCD;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME autosuspend idle root hubs */
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&ehci->lock, flags);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status ? retval : 0;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsehci_hub_descriptor (
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd			*ehci,
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hub_descriptor	*desc
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		ports = HCS_N_PORTS (ehci->hcs_params);
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		temp;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bDescriptorType = 0x29;
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bPwrOn2PwrGood = 10;	/* ehci 1.0, 2.3.9 says 20ms max */
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bHubContrCurrent = 0;
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bNbrPorts = ports;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	temp = 1 + (ports / 8);
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bDescLength = 7 + 2 * temp;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
639dbe79bbe9dcb22cb3651c46f18943477141ca452John Youn	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
640dbe79bbe9dcb22cb3651c46f18943477141ca452John Youn	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	temp = 0x0008;			/* per-port overcurrent reporting */
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (HCS_PPC (ehci->hcs_params))
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp |= 0x0001;		/* per-port power control */
64556c1e26d75008d39f1067f453719857a81109d9fDavid Brownell	else
64656c1e26d75008d39f1067f453719857a81109d9fDavid Brownell		temp |= 0x0002;		/* no power switching */
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// re-enable when we support USB_PORT_FEAT_INDICATOR below.
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (HCS_INDICATOR (ehci->hcs_params))
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp |= 0x0080;		/* per-port indicators (LEDs) */
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
652fd05e720099e8eeddb378305d1a41c1445344b91Al Viro	desc->wHubCharacteristics = cpu_to_le16(temp);
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ehci_hub_control (
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd	*hcd,
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		typeReq,
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wValue,
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wIndex,
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char		*buf,
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wLength
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		ports = HCS_N_PORTS (ehci->hcs_params);
667383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern	u32 __iomem	*status_reg = &ehci->regs->port_status[
668383975d765523a56dc43a6cd6d52e9b376800cf2Alan Stern				(wIndex & 0xff) - 1];
669331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du	u32 __iomem	*hostpc_reg = NULL;
670331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du	u32		temp, temp1, status;
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		retval = 0;
673f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell	unsigned	selector;
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * (track current state ourselves) ... blink for diagnostics,
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * power, "this is the one", etc.  EHCI spec supports this.
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
682331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du	if (ehci->has_hostpc)
683331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du		hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
684331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				+ HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&ehci->lock, flags);
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (typeReq) {
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ClearHubFeature:
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case C_HUB_LOCAL_POWER:
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case C_HUB_OVER_CURRENT:
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* no hub-wide feature/status flags */
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ClearPortFeature:
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!wIndex || wIndex > ports)
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wIndex--;
701e6316565e568b3b5733be10cfca3c27259bef499Alan Stern		temp = ehci_readl(ehci, status_reg);
702625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern
703625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		/*
704625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * Even if OWNER is set, so the port is owned by the
705625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * companion controller, khubd needs to be able to clear
706625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * the port-change status bits (especially
707749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern		 * USB_PORT_STAT_C_CONNECTION).
708625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 */
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_ENABLE:
712e6316565e568b3b5733be10cfca3c27259bef499Alan Stern			ehci_writel(ehci, temp & ~PORT_PE, status_reg);
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_C_ENABLE:
715083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC,
716e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					status_reg);
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_SUSPEND:
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (temp & PORT_RESET)
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto error;
721f8aeb3bb8657b207895aa10f75e63f2c48d08985David Brownell			if (ehci->no_selective_suspend)
722f8aeb3bb8657b207895aa10f75e63f2c48d08985David Brownell				break;
72383722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin#ifdef CONFIG_USB_OTG
72483722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin			if ((hcd->self.otg_port == (wIndex + 1))
72583722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin			    && hcd->self.b_hnp_enable) {
7266e13c6505cdff9766d5268ffb8c972c1a2f996e6Heikki Krogerus				otg_start_hnp(ehci->transceiver->otg);
72783722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin				break;
72883722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin			}
72983722bc9430424de1614ff31696f73a40b3d81a9Anatolij Gustschin#endif
73016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if (!(temp & PORT_SUSPEND))
73116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				break;
73216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if ((temp & PORT_PE) == 0)
73316032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				goto error;
73416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern
73516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			/* clear phy low-power mode before resume */
73616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			if (hostpc_reg) {
73716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				temp1 = ehci_readl(ehci, hostpc_reg);
73816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,
739eab80de01cb398419ef3305f35abcb367c647c8bAlek Du						hostpc_reg);
74016032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				spin_unlock_irqrestore(&ehci->lock, flags);
74116032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				msleep(5);/* wait to leave low-power mode */
74216032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern				spin_lock_irqsave(&ehci->lock, flags);
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
74416032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			/* resume signaling for 20 msec */
74516032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
74616032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci_writel(ehci, temp | PORT_RESUME, status_reg);
74716032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern			ehci->reset_done[wIndex] = jiffies
74816032c4f5b291af541e9114a09ea20ff5a0dc474Alan Stern					+ msecs_to_jiffies(20);
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_C_SUSPEND:
751d1f114d12bb4db3147e1b1342ae31083c5a79c84Alan Stern			clear_bit(wIndex, &ehci->port_c_suspend);
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_POWER:
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (HCS_PPC (ehci->hcs_params))
755083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt				ehci_writel(ehci,
756083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt					  temp & ~(PORT_RWC_BITS | PORT_POWER),
757e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					  status_reg);
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_C_CONNECTION:
76048f24970144479c29b8cee6d2e1dbedf6dcf9cfbAlek Du			if (ehci->has_lpm) {
76148f24970144479c29b8cee6d2e1dbedf6dcf9cfbAlek Du				/* clear PORTSC bits on disconnect */
76248f24970144479c29b8cee6d2e1dbedf6dcf9cfbAlek Du				temp &= ~PORT_LPM;
76348f24970144479c29b8cee6d2e1dbedf6dcf9cfbAlek Du				temp &= ~PORT_DEV_ADDR;
76448f24970144479c29b8cee6d2e1dbedf6dcf9cfbAlek Du			}
765083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
766e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					status_reg);
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_C_OVER_CURRENT:
769083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC,
770e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					status_reg);
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_C_RESET:
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* GetPortStatus clears reset */
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
778083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted write */
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetHubDescriptor:
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buf);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetHubStatus:
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* no hub-wide feature/status flags */
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset (buf, 0, 4);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		//cpu_to_le32s ((u32 *) buf);
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetPortStatus:
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!wIndex || wIndex > ports)
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wIndex--;
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = 0;
794e6316565e568b3b5733be10cfca3c27259bef499Alan Stern		temp = ehci_readl(ehci, status_reg);
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// wPortChange bits
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (temp & PORT_CSC)
798749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_C_CONNECTION << 16;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (temp & PORT_PEC)
800749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_C_ENABLE << 16;
801756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer
802756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer		if ((temp & PORT_OCC) && !ignore_oc){
803749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_C_OVERCURRENT << 16;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
805756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			/*
806756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 * Hubs should disable port power on over-current.
807756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 * However, not all EHCI implementations do this
808756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 * automatically, even if they _do_ support per-port
809756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 * power switching; they're allowed to just limit the
810756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 * current.  khubd will turn the power back on.
811756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			 */
81281463c1d707186adbbe534016cd1249edeab0dacSergei Shtylyov			if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) {
813756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer				ehci_writel(ehci,
814756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer					temp & ~(PORT_RWC_BITS | PORT_POWER),
815756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer					status_reg);
81681463c1d707186adbbe534016cd1249edeab0dacSergei Shtylyov				temp = ehci_readl(ehci, status_reg);
817756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer			}
818756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer		}
819756aa6b3d536afe85e151138cb03a293998887b3Christian Engelmayer
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* whoever resumes must GetPortStatus to complete it!! */
821629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern		if (temp & PORT_RESUME) {
822629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern
823629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern			/* Remote Wakeup received? */
824629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern			if (!ehci->reset_done[wIndex]) {
825629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				/* resume signaling for 20 msec */
826629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				ehci->reset_done[wIndex] = jiffies
827629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern						+ msecs_to_jiffies(20);
828629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				/* check the port again */
829629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				mod_timer(&ehci_to_hcd(ehci)->rh_timer,
830629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern						ehci->reset_done[wIndex]);
831629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern			}
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
833629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern			/* resume completed? */
834629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern			else if (time_after_eq(jiffies,
835629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern					ehci->reset_done[wIndex])) {
836eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern				clear_bit(wIndex, &ehci->suspended_ports);
837d1f114d12bb4db3147e1b1342ae31083c5a79c84Alan Stern				set_bit(wIndex, &ehci->port_c_suspend);
838629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				ehci->reset_done[wIndex] = 0;
839629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern
840629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				/* stop resume signaling */
841629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				temp = ehci_readl(ehci, status_reg);
842629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				ehci_writel(ehci,
843e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					temp & ~(PORT_RWC_BITS | PORT_RESUME),
844e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					status_reg);
845a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern				clear_bit(wIndex, &ehci->resuming_ports);
846629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				retval = handshake(ehci, status_reg,
847083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt					   PORT_RESUME, 0, 2000 /* 2msec */);
848629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				if (retval != 0) {
849629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern					ehci_err(ehci,
850629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern						"port %d resume error %d\n",
851629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern						wIndex + 1, retval);
852629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern					goto error;
853629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				}
854629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* whoever resets must GetPortStatus to complete it!! */
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((temp & PORT_RESET)
860629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern				&& time_after_eq(jiffies,
861629e4427aa817d5c9f11885420abf54b8f5967dcAlan Stern					ehci->reset_done[wIndex])) {
862749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_C_RESET << 16;
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ehci->reset_done [wIndex] = 0;
864a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			clear_bit(wIndex, &ehci->resuming_ports);
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* force reset to complete */
867083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt			ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
868e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					status_reg);
869c22fa3acbc2ef79ea57217643f6cd6d226963069David Brownell			/* REVISIT:  some hardware needs 550+ usec to clear
870c22fa3acbc2ef79ea57217643f6cd6d226963069David Brownell			 * this bit; seems too long to spin routinely...
871c22fa3acbc2ef79ea57217643f6cd6d226963069David Brownell			 */
872e6316565e568b3b5733be10cfca3c27259bef499Alan Stern			retval = handshake(ehci, status_reg,
8736307e0961205c50a8a9b6e8e3e4dfd178a944ba9Dinh Nguyen					PORT_RESET, 0, 1000);
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (retval != 0) {
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ehci_err (ehci, "port %d reset error %d\n",
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					wIndex + 1, retval);
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto error;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* see what we found out */
881e6316565e568b3b5733be10cfca3c27259bef499Alan Stern			temp = check_reset_complete (ehci, wIndex, status_reg,
882e6316565e568b3b5733be10cfca3c27259bef499Alan Stern					ehci_readl(ehci, status_reg));
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
885a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern		if (!(temp & (PORT_RESUME|PORT_RESET))) {
886eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern			ehci->reset_done[wIndex] = 0;
887a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			clear_bit(wIndex, &ehci->resuming_ports);
888a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern		}
889eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern
89057e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		/* transfer dedicated ports to the companion hc */
89157e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		if ((temp & PORT_CONNECT) &&
89257e06c11372eccf5acebdd4664eb025fee76c561Alan Stern				test_bit(wIndex, &ehci->companion_ports)) {
89357e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			temp &= ~PORT_RWC_BITS;
89457e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			temp |= PORT_OWNER;
89557e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			ehci_writel(ehci, temp, status_reg);
89657e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
89757e06c11372eccf5acebdd4664eb025fee76c561Alan Stern			temp = ehci_readl(ehci, status_reg);
89857e06c11372eccf5acebdd4664eb025fee76c561Alan Stern		}
89957e06c11372eccf5acebdd4664eb025fee76c561Alan Stern
900625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		/*
901625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * Even if OWNER is set, there's no harm letting khubd
902625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * see the wPortStatus values (they should all be 0 except
903625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 * for PORT_POWER anyway).
904625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		 */
905625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern
906625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		if (temp & PORT_CONNECT) {
907749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_CONNECTION;
908625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern			// status may be from integrated TT
909331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			if (ehci->has_hostpc) {
910331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				temp1 = ehci_readl(ehci, hostpc_reg);
911331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				status |= ehci_port_speed(ehci, temp1);
912331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			} else
913331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				status |= ehci_port_speed(ehci, temp);
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
915625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		if (temp & PORT_PE)
916749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_ENABLE;
917eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern
918eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern		/* maybe the port was unsuspended without our knowledge */
919eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern		if (temp & (PORT_SUSPEND|PORT_RESUME)) {
920749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_SUSPEND;
921eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern		} else if (test_bit(wIndex, &ehci->suspended_ports)) {
922eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern			clear_bit(wIndex, &ehci->suspended_ports);
923a448e4dc25303fe551e4dafe16c8c7c34f1b9d82Alan Stern			clear_bit(wIndex, &ehci->resuming_ports);
924eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern			ehci->reset_done[wIndex] = 0;
925eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern			if (temp & PORT_PE)
926eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern				set_bit(wIndex, &ehci->port_c_suspend);
927eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern		}
928eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern
929625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		if (temp & PORT_OC)
930749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_OVERCURRENT;
931625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		if (temp & PORT_RESET)
932749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_RESET;
933625b5c9a0069ef1b61feb3ce599b39f1b04b5666Alan Stern		if (temp & PORT_POWER)
934749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_POWER;
935d1f114d12bb4db3147e1b1342ae31083c5a79c84Alan Stern		if (test_bit(wIndex, &ehci->port_c_suspend))
936749da5f82fe33ff68dd4aa1a5e35cd9aa6246dabAlan Stern			status |= USB_PORT_STAT_C_SUSPEND << 16;
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9389776afc8b3dc487557f3f576002520f59be334e6David Brownell#ifndef	VERBOSE_DEBUG
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status & ~0xffff)	/* only if wPortChange is interesting */
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
942a5abdeafedf722b0f3f357f4a23089a686b1b80dHarvey Harrison		put_unaligned_le32(status, buf);
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SetHubFeature:
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case C_HUB_LOCAL_POWER:
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case C_HUB_OVER_CURRENT:
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* no hub-wide feature/status flags */
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SetPortFeature:
955f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		selector = wIndex >> 8;
956f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		wIndex &= 0xff;
9578d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel		if (unlikely(ehci->debug)) {
9588d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel			/* If the debug port is active any port
9598d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel			 * feature requests should get denied */
9608d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel			if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) &&
9618d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel			    (readl(&ehci->debug->control) & DBGP_ENABLED)) {
9628d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel				retval = -ENODEV;
9638d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel				goto error_exit;
9648d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel			}
9658d053c79f22462f55c02c8083580730b922cf7b4Jason Wessel		}
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!wIndex || wIndex > ports)
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wIndex--;
969e6316565e568b3b5733be10cfca3c27259bef499Alan Stern		temp = ehci_readl(ehci, status_reg);
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (temp & PORT_OWNER)
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
97310f6524a8ef1413a8cbd952673997013183fe2a9David Brownell		temp &= ~PORT_RWC_BITS;
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_SUSPEND:
976f8aeb3bb8657b207895aa10f75e63f2c48d08985David Brownell			if (ehci->no_selective_suspend)
977f8aeb3bb8657b207895aa10f75e63f2c48d08985David Brownell				break;
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((temp & PORT_PE) == 0
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					|| (temp & PORT_RESET) != 0)
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto error;
981b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du
982331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			/* After above check the port must be connected.
983331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 * Set appropriate bit thus could put phy into low power
984331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 * mode if we have hostpc feature
985331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			 */
986b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du			temp &= ~PORT_WKCONN_E;
987b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du			temp |= PORT_WKDISC_E | PORT_WKOC_E;
988b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
989331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			if (hostpc_reg) {
990b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du				spin_unlock_irqrestore(&ehci->lock, flags);
991331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				msleep(5);/* 5ms for HCD enter low pwr mode */
992b9df794258de24d10b0616634d4c30d8b6e9d381Alek Du				spin_lock_irqsave(&ehci->lock, flags);
993331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				temp1 = ehci_readl(ehci, hostpc_reg);
994331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				ehci_writel(ehci, temp1 | HOSTPC_PHCD,
995331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du					hostpc_reg);
996331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				temp1 = ehci_readl(ehci, hostpc_reg);
997331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du				ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
998331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du					wIndex, (temp1 & HOSTPC_PHCD) ?
999331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du					"succeeded" : "failed");
1000331ac6b288d9f3689514ced1878041fb0df7e13cAlek Du			}
1001eafe5b99f2135488b21cf17a262c54997c44f784Alan Stern			set_bit(wIndex, &ehci->suspended_ports);
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_POWER:
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (HCS_PPC (ehci->hcs_params))
1005083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt				ehci_writel(ehci, temp | PORT_POWER,
1006e6316565e568b3b5733be10cfca3c27259bef499Alan Stern						status_reg);
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_RESET:
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (temp & PORT_RESUME)
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto error;
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* line status bits may report this as low speed,
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * which can be fine if this root hub has a
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * transaction translator built in.
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					&& !ehci_is_TDI(ehci)
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					&& PORT_USB11 (temp)) {
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ehci_dbg (ehci,
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					"port %d low speed --> companion\n",
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					wIndex + 1);
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				temp |= PORT_OWNER;
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				temp |= PORT_RESET;
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				temp &= ~PORT_PE;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/*
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 * caller must wait, then call GetPortStatus
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 * usb 2.0 spec says 50 ms resets on root
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 */
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ehci->reset_done [wIndex] = jiffies
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						+ msecs_to_jiffies (50);
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1034e6316565e568b3b5733be10cfca3c27259bef499Alan Stern			ehci_writel(ehci, temp, status_reg);
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1036f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell
1037f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		/* For downstream facing ports (these):  one hub port is put
1038f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		 * into test mode according to USB2 11.24.2.13, then the hub
1039f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		 * must be reset (which for root hub now means rmmod+modprobe,
1040f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		 * or else system reboot).  See EHCI 2.3.9 and 4.14 for info
1041f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		 * about the EHCI-specific stuff.
1042f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		 */
1043f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell		case USB_PORT_FEAT_TEST:
1044f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell			if (!selector || selector > 5)
1045f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell				goto error;
1046f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell			ehci_quiesce(ehci);
104777636c86a600b83de01719efad83567e46d7e8ceBoris Todorov
104877636c86a600b83de01719efad83567e46d7e8ceBoris Todorov			/* Put all enabled ports into suspend */
104977636c86a600b83de01719efad83567e46d7e8ceBoris Todorov			while (ports--) {
105077636c86a600b83de01719efad83567e46d7e8ceBoris Todorov				u32 __iomem *sreg =
105177636c86a600b83de01719efad83567e46d7e8ceBoris Todorov						&ehci->regs->port_status[ports];
105277636c86a600b83de01719efad83567e46d7e8ceBoris Todorov
105377636c86a600b83de01719efad83567e46d7e8ceBoris Todorov				temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS;
105477636c86a600b83de01719efad83567e46d7e8ceBoris Todorov				if (temp & PORT_PE)
105577636c86a600b83de01719efad83567e46d7e8ceBoris Todorov					ehci_writel(ehci, temp | PORT_SUSPEND,
105677636c86a600b83de01719efad83567e46d7e8ceBoris Todorov							sreg);
105777636c86a600b83de01719efad83567e46d7e8ceBoris Todorov			}
1058f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell			ehci_halt(ehci);
105977636c86a600b83de01719efad83567e46d7e8ceBoris Todorov			temp = ehci_readl(ehci, status_reg);
1060f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell			temp |= selector << 16;
1061e6316565e568b3b5733be10cfca3c27259bef499Alan Stern			ehci_writel(ehci, temp, status_reg);
1062f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell			break;
1063f0d7f27351058284f62ab4848909373c2d1f5ce8David Brownell
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto error;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1067083522d76662cda71328df1f3d75e5a9057c7c9fBenjamin Herrenschmidt		ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* "stall" on error */
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -EPIPE;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10758d053c79f22462f55c02c8083580730b922cf7b4Jason Wesselerror_exit:
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&ehci->lock, flags);
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
107990da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao
10805407a3c3d942e75d4d123d213fd692bce5acc961Felipe Balbistatic void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd,
10815407a3c3d942e75d4d123d213fd692bce5acc961Felipe Balbi		int portnum)
108290da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao{
108390da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
108490da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao
108590da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao	if (ehci_is_TDI(ehci))
108690da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao		return;
108790da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao	set_owner(ehci, --portnum, PORT_OWNER);
108890da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao}
108990da096ee46b682011b7d549e52b81cf9742e60bBalaji Rao
10905407a3c3d942e75d4d123d213fd692bce5acc961Felipe Balbistatic int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd,
10915407a3c3d942e75d4d123d213fd692bce5acc961Felipe Balbi		int portnum)
10923a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern{
10933a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
10943a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern	u32 __iomem		*reg;
10953a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern
10963a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern	if (ehci_is_TDI(ehci))
10973a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern		return 0;
10983a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern	reg = &ehci->regs->port_status[portnum - 1];
10993a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern	return ehci_readl(ehci, reg) & PORT_OWNER;
11003a31155cfff0935e4b178f3dca733d2d60d2eb8dAlan Stern}
1101