xref: /linux/drivers/usb/host/uhci-hub.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Universal Host Controller Interface driver for USB.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Maintainer: Alan Stern <stern@rowland.harvard.edu>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * (C) Copyright 1999 Linus Torvalds
81da177e4SLinus Torvalds  * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
91da177e4SLinus Torvalds  * (C) Copyright 1999 Randy Dunlap
101da177e4SLinus Torvalds  * (C) Copyright 1999 Georg Acher, acher@in.tum.de
111da177e4SLinus Torvalds  * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
121da177e4SLinus Torvalds  * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
131da177e4SLinus Torvalds  * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
16bef4665aSMing Lei static const __u8 root_hub_hub_des[] =
171da177e4SLinus Torvalds {
181da177e4SLinus Torvalds 	0x09,			/*  __u8  bLength; */
193e5dd4c3SSergei Shtylyov 	USB_DT_HUB,		/*  __u8  bDescriptorType; Hub-descriptor */
201da177e4SLinus Torvalds 	0x02,			/*  __u8  bNbrPorts; */
21673016d9SSergei Shtylyov 	HUB_CHAR_NO_LPSM |	/* __u16  wHubCharacteristics; */
22673016d9SSergei Shtylyov 		HUB_CHAR_INDV_PORT_OCPM, /* (per-port OC, no power switching) */
23673016d9SSergei Shtylyov 	0x00,
241da177e4SLinus Torvalds 	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
251da177e4SLinus Torvalds 	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
263171fcabSChen Gang 	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max */
273171fcabSChen Gang 	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max */
281da177e4SLinus Torvalds };
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #define	UHCI_RH_MAXCHILD	7
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /* must write as zeroes */
331da177e4SLinus Torvalds #define WZ_BITS		(USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds /* status change bits:  nonzero writes will clear */
361da177e4SLinus Torvalds #define RWC_BITS	(USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
371da177e4SLinus Torvalds 
3888018158SAlan Stern /* suspend/resume bits: port suspended or port resuming */
3988018158SAlan Stern #define SUSPEND_BITS	(USBPORTSC_SUSP | USBPORTSC_RD)
4088018158SAlan Stern 
41f5946f82SAlan Stern /* A port that either is connected or has a changed-bit set will prevent
42f5946f82SAlan Stern  * us from AUTO_STOPPING.
43f5946f82SAlan Stern  */
any_ports_active(struct uhci_hcd * uhci)44f5946f82SAlan Stern static int any_ports_active(struct uhci_hcd *uhci)
45f5946f82SAlan Stern {
46f5946f82SAlan Stern 	int port;
47f5946f82SAlan Stern 
48f5946f82SAlan Stern 	for (port = 0; port < uhci->rh_numports; ++port) {
499faa091aSJan Andersson 		if ((uhci_readw(uhci, USBPORTSC1 + port * 2) &
50f5946f82SAlan Stern 				(USBPORTSC_CCS | RWC_BITS)) ||
51f5946f82SAlan Stern 				test_bit(port, &uhci->port_c_suspend))
52f5946f82SAlan Stern 			return 1;
53f5946f82SAlan Stern 	}
54f5946f82SAlan Stern 	return 0;
55f5946f82SAlan Stern }
56f5946f82SAlan Stern 
get_hub_status_data(struct uhci_hcd * uhci,char * buf)576c1b445cSAlan Stern static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	int port;
605f8364b7SAlan Stern 	int mask = RWC_BITS;
615f8364b7SAlan Stern 
625f8364b7SAlan Stern 	/* Some boards (both VIA and Intel apparently) report bogus
635f8364b7SAlan Stern 	 * overcurrent indications, causing massive log spam unless
645f8364b7SAlan Stern 	 * we completely ignore them.  This doesn't seem to be a problem
655f8364b7SAlan Stern 	 * with the chipset so much as with the way it is connected on
665f8364b7SAlan Stern 	 * the motherboard; if the overcurrent input is left to float
675f8364b7SAlan Stern 	 * then it may constantly register false positives. */
685f8364b7SAlan Stern 	if (ignore_oc)
695f8364b7SAlan Stern 		mask &= ~USBPORTSC_OCC;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	*buf = 0;
721da177e4SLinus Torvalds 	for (port = 0; port < uhci->rh_numports; ++port) {
739faa091aSJan Andersson 		if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) ||
741da177e4SLinus Torvalds 				test_bit(port, &uhci->port_c_suspend))
751da177e4SLinus Torvalds 			*buf |= (1 << (port + 1));
761da177e4SLinus Torvalds 	}
771da177e4SLinus Torvalds 	return !!*buf;
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds #define CLR_RH_PORTSTAT(x) \
819faa091aSJan Andersson 	status = uhci_readw(uhci, port_addr);	\
821da177e4SLinus Torvalds 	status &= ~(RWC_BITS|WZ_BITS); \
831da177e4SLinus Torvalds 	status &= ~(x); \
841da177e4SLinus Torvalds 	status |= RWC_BITS & (x); \
859faa091aSJan Andersson 	uhci_writew(uhci, status, port_addr)
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds #define SET_RH_PORTSTAT(x) \
889faa091aSJan Andersson 	status = uhci_readw(uhci, port_addr);	\
891da177e4SLinus Torvalds 	status |= (x); \
901da177e4SLinus Torvalds 	status &= ~(RWC_BITS|WZ_BITS); \
919faa091aSJan Andersson 	uhci_writew(uhci, status, port_addr)
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds /* UHCI controllers don't automatically stop resume signalling after 20 msec,
941da177e4SLinus Torvalds  * so we have to poll and check timeouts in order to take care of it.
951da177e4SLinus Torvalds  */
uhci_finish_suspend(struct uhci_hcd * uhci,int port,unsigned long port_addr)961da177e4SLinus Torvalds static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
971da177e4SLinus Torvalds 		unsigned long port_addr)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	int status;
100de06a3b8SAlan Stern 	int i;
1011da177e4SLinus Torvalds 
1029faa091aSJan Andersson 	if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) {
10388018158SAlan Stern 		CLR_RH_PORTSTAT(SUSPEND_BITS);
1048e326406SAlan Stern 		if (test_bit(port, &uhci->resuming_ports))
1051da177e4SLinus Torvalds 			set_bit(port, &uhci->port_c_suspend);
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 		/* The controller won't actually turn off the RD bit until
1081da177e4SLinus Torvalds 		 * it has had a chance to send a low-speed EOP sequence,
109de06a3b8SAlan Stern 		 * which is supposed to take 3 bit times (= 2 microseconds).
110de06a3b8SAlan Stern 		 * Experiments show that some controllers take longer, so
111de06a3b8SAlan Stern 		 * we'll poll for completion. */
112de06a3b8SAlan Stern 		for (i = 0; i < 10; ++i) {
1139faa091aSJan Andersson 			if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS))
114de06a3b8SAlan Stern 				break;
115de06a3b8SAlan Stern 			udelay(1);
116de06a3b8SAlan Stern 		}
1171da177e4SLinus Torvalds 	}
1188e326406SAlan Stern 	clear_bit(port, &uhci->resuming_ports);
119840008bbSAlan Stern 	usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
122ae557175SAlan Stern /* Wait for the UHCI controller in HP's iLO2 server management chip.
123ae557175SAlan Stern  * It can take up to 250 us to finish a reset and set the CSC bit.
124ae557175SAlan Stern  */
wait_for_HP(struct uhci_hcd * uhci,unsigned long port_addr)1259faa091aSJan Andersson static void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr)
126ae557175SAlan Stern {
127ae557175SAlan Stern 	int i;
128ae557175SAlan Stern 
129ae557175SAlan Stern 	for (i = 10; i < 250; i += 10) {
1309faa091aSJan Andersson 		if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC)
131ae557175SAlan Stern 			return;
132ae557175SAlan Stern 		udelay(10);
133ae557175SAlan Stern 	}
134ae557175SAlan Stern 	/* Log a warning? */
135ae557175SAlan Stern }
136ae557175SAlan Stern 
uhci_check_ports(struct uhci_hcd * uhci)1371da177e4SLinus Torvalds static void uhci_check_ports(struct uhci_hcd *uhci)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	unsigned int port;
1401da177e4SLinus Torvalds 	unsigned long port_addr;
1411da177e4SLinus Torvalds 	int status;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	for (port = 0; port < uhci->rh_numports; ++port) {
1449faa091aSJan Andersson 		port_addr = USBPORTSC1 + 2 * port;
1459faa091aSJan Andersson 		status = uhci_readw(uhci, port_addr);
1461da177e4SLinus Torvalds 		if (unlikely(status & USBPORTSC_PR)) {
1471da177e4SLinus Torvalds 			if (time_after_eq(jiffies, uhci->ports_timeout)) {
1481da177e4SLinus Torvalds 				CLR_RH_PORTSTAT(USBPORTSC_PR);
1491da177e4SLinus Torvalds 				udelay(10);
1501da177e4SLinus Torvalds 
151ae557175SAlan Stern 				/* HP's server management chip requires
152ae557175SAlan Stern 				 * a longer delay. */
153dfeca7a8SJan Andersson 				if (uhci->wait_for_hp)
1549faa091aSJan Andersson 					wait_for_HP(uhci, port_addr);
155ae557175SAlan Stern 
1561da177e4SLinus Torvalds 				/* If the port was enabled before, turning
1571da177e4SLinus Torvalds 				 * reset on caused a port enable change.
1581da177e4SLinus Torvalds 				 * Turning reset off causes a port connect
1591da177e4SLinus Torvalds 				 * status change.  Clear these changes. */
1601da177e4SLinus Torvalds 				CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
1611da177e4SLinus Torvalds 				SET_RH_PORTSTAT(USBPORTSC_PE);
1621da177e4SLinus Torvalds 			}
1631da177e4SLinus Torvalds 		}
1641da177e4SLinus Torvalds 		if (unlikely(status & USBPORTSC_RD)) {
1651da177e4SLinus Torvalds 			if (!test_bit(port, &uhci->resuming_ports)) {
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 				/* Port received a wakeup request */
1681da177e4SLinus Torvalds 				set_bit(port, &uhci->resuming_ports);
1691da177e4SLinus Torvalds 				uhci->ports_timeout = jiffies +
170b8fb6f79SFelipe Balbi 					msecs_to_jiffies(USB_RESUME_TIMEOUT);
171840008bbSAlan Stern 				usb_hcd_start_port_resume(
172840008bbSAlan Stern 						&uhci_to_hcd(uhci)->self, port);
1736c1b445cSAlan Stern 
1746c1b445cSAlan Stern 				/* Make sure we see the port again
1756c1b445cSAlan Stern 				 * after the resuming period is over. */
1766c1b445cSAlan Stern 				mod_timer(&uhci_to_hcd(uhci)->rh_timer,
1776c1b445cSAlan Stern 						uhci->ports_timeout);
1781da177e4SLinus Torvalds 			} else if (time_after_eq(jiffies,
1791da177e4SLinus Torvalds 						uhci->ports_timeout)) {
1801da177e4SLinus Torvalds 				uhci_finish_suspend(uhci, port, port_addr);
1811da177e4SLinus Torvalds 			}
1821da177e4SLinus Torvalds 		}
1831da177e4SLinus Torvalds 	}
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
uhci_hub_status_data(struct usb_hcd * hcd,char * buf)1866c1b445cSAlan Stern static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
1876c1b445cSAlan Stern {
1886c1b445cSAlan Stern 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
1896c1b445cSAlan Stern 	unsigned long flags;
1901f09df8bSAlan Stern 	int status = 0;
1916c1b445cSAlan Stern 
1926c1b445cSAlan Stern 	spin_lock_irqsave(&uhci->lock, flags);
1936c1b445cSAlan Stern 
1947d12e780SDavid Howells 	uhci_scan_schedule(uhci);
195541c7d43SAlan Stern 	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
1961f09df8bSAlan Stern 		goto done;
1976c1b445cSAlan Stern 	uhci_check_ports(uhci);
1981f09df8bSAlan Stern 
1996c1b445cSAlan Stern 	status = get_hub_status_data(uhci, buf);
2006c1b445cSAlan Stern 
2016c1b445cSAlan Stern 	switch (uhci->rh_state) {
2026c1b445cSAlan Stern 	    case UHCI_RH_SUSPENDED:
2036c1b445cSAlan Stern 		/* if port change, ask to be resumed */
204b446b96fSAlan Stern 		if (status || uhci->resuming_ports) {
205b446b96fSAlan Stern 			status = 1;
2066c1b445cSAlan Stern 			usb_hcd_resume_root_hub(hcd);
207b446b96fSAlan Stern 		}
2086c1b445cSAlan Stern 		break;
2096c1b445cSAlan Stern 
2106c1b445cSAlan Stern 	    case UHCI_RH_AUTO_STOPPED:
2116c1b445cSAlan Stern 		/* if port change, auto start */
2126c1b445cSAlan Stern 		if (status)
2136c1b445cSAlan Stern 			wakeup_rh(uhci);
2146c1b445cSAlan Stern 		break;
2156c1b445cSAlan Stern 
2166c1b445cSAlan Stern 	    case UHCI_RH_RUNNING:
2176c1b445cSAlan Stern 		/* are any devices attached? */
2186c1b445cSAlan Stern 		if (!any_ports_active(uhci)) {
2196c1b445cSAlan Stern 			uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
2206c1b445cSAlan Stern 			uhci->auto_stop_time = jiffies + HZ;
2216c1b445cSAlan Stern 		}
2226c1b445cSAlan Stern 		break;
2236c1b445cSAlan Stern 
2246c1b445cSAlan Stern 	    case UHCI_RH_RUNNING_NODEVS:
2256c1b445cSAlan Stern 		/* auto-stop if nothing connected for 1 second */
2266c1b445cSAlan Stern 		if (any_ports_active(uhci))
2276c1b445cSAlan Stern 			uhci->rh_state = UHCI_RH_RUNNING;
228997ff893SAlan Stern 		else if (time_after_eq(jiffies, uhci->auto_stop_time) &&
229997ff893SAlan Stern 				!uhci->wait_for_hp)
2306c1b445cSAlan Stern 			suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
2316c1b445cSAlan Stern 		break;
2326c1b445cSAlan Stern 
2336c1b445cSAlan Stern 	    default:
2346c1b445cSAlan Stern 		break;
2356c1b445cSAlan Stern 	}
2366c1b445cSAlan Stern 
2376c1b445cSAlan Stern done:
2386c1b445cSAlan Stern 	spin_unlock_irqrestore(&uhci->lock, flags);
2396c1b445cSAlan Stern 	return status;
2406c1b445cSAlan Stern }
2416c1b445cSAlan Stern 
2421da177e4SLinus Torvalds /* size of returned buffer is part of USB spec */
uhci_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)2431da177e4SLinus Torvalds static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
2441da177e4SLinus Torvalds 			u16 wIndex, char *buf, u16 wLength)
2451da177e4SLinus Torvalds {
2461da177e4SLinus Torvalds 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
2475a3e2055SDeng-Cheng Zhu 	int status, lstatus, retval = 0;
2481da177e4SLinus Torvalds 	unsigned int port = wIndex - 1;
2499faa091aSJan Andersson 	unsigned long port_addr = USBPORTSC1 + 2 * port;
2501da177e4SLinus Torvalds 	u16 wPortChange, wPortStatus;
2511da177e4SLinus Torvalds 	unsigned long flags;
2521da177e4SLinus Torvalds 
253541c7d43SAlan Stern 	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
254a8bed8b6SAlan Stern 		return -ETIMEDOUT;
255a8bed8b6SAlan Stern 
2561da177e4SLinus Torvalds 	spin_lock_irqsave(&uhci->lock, flags);
2571da177e4SLinus Torvalds 	switch (typeReq) {
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	case GetHubStatus:
2601da177e4SLinus Torvalds 		*(__le32 *)buf = cpu_to_le32(0);
2615a3e2055SDeng-Cheng Zhu 		retval = 4; /* hub power */
2625a3e2055SDeng-Cheng Zhu 		break;
2631da177e4SLinus Torvalds 	case GetPortStatus:
2641da177e4SLinus Torvalds 		if (port >= uhci->rh_numports)
2651da177e4SLinus Torvalds 			goto err;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 		uhci_check_ports(uhci);
2689faa091aSJan Andersson 		status = uhci_readw(uhci, port_addr);
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 		/* Intel controllers report the OverCurrent bit active on.
2711da177e4SLinus Torvalds 		 * VIA controllers report it active off, so we'll adjust the
2721da177e4SLinus Torvalds 		 * bit value.  (It's not standardized in the UHCI spec.)
2731da177e4SLinus Torvalds 		 */
274dfeca7a8SJan Andersson 		if (uhci->oc_low)
2751da177e4SLinus Torvalds 			status ^= USBPORTSC_OC;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 		/* UHCI doesn't support C_RESET (always false) */
2781da177e4SLinus Torvalds 		wPortChange = lstatus = 0;
2791da177e4SLinus Torvalds 		if (status & USBPORTSC_CSC)
2801da177e4SLinus Torvalds 			wPortChange |= USB_PORT_STAT_C_CONNECTION;
2811da177e4SLinus Torvalds 		if (status & USBPORTSC_PEC)
2821da177e4SLinus Torvalds 			wPortChange |= USB_PORT_STAT_C_ENABLE;
2835f8364b7SAlan Stern 		if ((status & USBPORTSC_OCC) && !ignore_oc)
2841da177e4SLinus Torvalds 			wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 		if (test_bit(port, &uhci->port_c_suspend)) {
2871da177e4SLinus Torvalds 			wPortChange |= USB_PORT_STAT_C_SUSPEND;
2881da177e4SLinus Torvalds 			lstatus |= 1;
2891da177e4SLinus Torvalds 		}
2901da177e4SLinus Torvalds 		if (test_bit(port, &uhci->resuming_ports))
2911da177e4SLinus Torvalds 			lstatus |= 4;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 		/* UHCI has no power switching (always on) */
2941da177e4SLinus Torvalds 		wPortStatus = USB_PORT_STAT_POWER;
2951da177e4SLinus Torvalds 		if (status & USBPORTSC_CCS)
2961da177e4SLinus Torvalds 			wPortStatus |= USB_PORT_STAT_CONNECTION;
2971da177e4SLinus Torvalds 		if (status & USBPORTSC_PE) {
2981da177e4SLinus Torvalds 			wPortStatus |= USB_PORT_STAT_ENABLE;
29988018158SAlan Stern 			if (status & SUSPEND_BITS)
3001da177e4SLinus Torvalds 				wPortStatus |= USB_PORT_STAT_SUSPEND;
3011da177e4SLinus Torvalds 		}
3021da177e4SLinus Torvalds 		if (status & USBPORTSC_OC)
3031da177e4SLinus Torvalds 			wPortStatus |= USB_PORT_STAT_OVERCURRENT;
3041da177e4SLinus Torvalds 		if (status & USBPORTSC_PR)
3051da177e4SLinus Torvalds 			wPortStatus |= USB_PORT_STAT_RESET;
3061da177e4SLinus Torvalds 		if (status & USBPORTSC_LSDA)
3071da177e4SLinus Torvalds 			wPortStatus |= USB_PORT_STAT_LOW_SPEED;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 		if (wPortChange)
3101da177e4SLinus Torvalds 			dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
3111da177e4SLinus Torvalds 					wIndex, status, lstatus);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 		*(__le16 *)buf = cpu_to_le16(wPortStatus);
3141da177e4SLinus Torvalds 		*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
3155a3e2055SDeng-Cheng Zhu 		retval = 4;
3165a3e2055SDeng-Cheng Zhu 		break;
3171da177e4SLinus Torvalds 	case SetHubFeature:		/* We don't implement these */
3181da177e4SLinus Torvalds 	case ClearHubFeature:
3191da177e4SLinus Torvalds 		switch (wValue) {
3201da177e4SLinus Torvalds 		case C_HUB_OVER_CURRENT:
3211da177e4SLinus Torvalds 		case C_HUB_LOCAL_POWER:
3225a3e2055SDeng-Cheng Zhu 			break;
3231da177e4SLinus Torvalds 		default:
3241da177e4SLinus Torvalds 			goto err;
3251da177e4SLinus Torvalds 		}
3261da177e4SLinus Torvalds 		break;
3271da177e4SLinus Torvalds 	case SetPortFeature:
3281da177e4SLinus Torvalds 		if (port >= uhci->rh_numports)
3291da177e4SLinus Torvalds 			goto err;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 		switch (wValue) {
3321da177e4SLinus Torvalds 		case USB_PORT_FEAT_SUSPEND:
3331da177e4SLinus Torvalds 			SET_RH_PORTSTAT(USBPORTSC_SUSP);
3345a3e2055SDeng-Cheng Zhu 			break;
3351da177e4SLinus Torvalds 		case USB_PORT_FEAT_RESET:
3361da177e4SLinus Torvalds 			SET_RH_PORTSTAT(USBPORTSC_PR);
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 			/* Reset terminates Resume signalling */
3391da177e4SLinus Torvalds 			uhci_finish_suspend(uhci, port, port_addr);
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 			/* USB v2.0 7.1.7.5 */
342b8fb6f79SFelipe Balbi 			uhci->ports_timeout = jiffies +
343b8fb6f79SFelipe Balbi 				msecs_to_jiffies(USB_RESUME_TIMEOUT);
3445a3e2055SDeng-Cheng Zhu 			break;
3451da177e4SLinus Torvalds 		case USB_PORT_FEAT_POWER:
3461da177e4SLinus Torvalds 			/* UHCI has no power switching */
3475a3e2055SDeng-Cheng Zhu 			break;
3481da177e4SLinus Torvalds 		default:
3491da177e4SLinus Torvalds 			goto err;
3501da177e4SLinus Torvalds 		}
3511da177e4SLinus Torvalds 		break;
3521da177e4SLinus Torvalds 	case ClearPortFeature:
3531da177e4SLinus Torvalds 		if (port >= uhci->rh_numports)
3541da177e4SLinus Torvalds 			goto err;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 		switch (wValue) {
3571da177e4SLinus Torvalds 		case USB_PORT_FEAT_ENABLE:
3581da177e4SLinus Torvalds 			CLR_RH_PORTSTAT(USBPORTSC_PE);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 			/* Disable terminates Resume signalling */
3611da177e4SLinus Torvalds 			uhci_finish_suspend(uhci, port, port_addr);
3625a3e2055SDeng-Cheng Zhu 			break;
3631da177e4SLinus Torvalds 		case USB_PORT_FEAT_C_ENABLE:
3641da177e4SLinus Torvalds 			CLR_RH_PORTSTAT(USBPORTSC_PEC);
3655a3e2055SDeng-Cheng Zhu 			break;
3661da177e4SLinus Torvalds 		case USB_PORT_FEAT_SUSPEND:
3679faa091aSJan Andersson 			if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) {
3688e326406SAlan Stern 
3698e326406SAlan Stern 				/* Make certain the port isn't suspended */
3708e326406SAlan Stern 				uhci_finish_suspend(uhci, port, port_addr);
3718e326406SAlan Stern 			} else if (!test_and_set_bit(port,
3721da177e4SLinus Torvalds 						&uhci->resuming_ports)) {
3731da177e4SLinus Torvalds 				SET_RH_PORTSTAT(USBPORTSC_RD);
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds 				/* The controller won't allow RD to be set
3761da177e4SLinus Torvalds 				 * if the port is disabled.  When this happens
3771da177e4SLinus Torvalds 				 * just skip the Resume signalling.
3781da177e4SLinus Torvalds 				 */
3799faa091aSJan Andersson 				if (!(uhci_readw(uhci, port_addr) &
3809faa091aSJan Andersson 						USBPORTSC_RD))
3811da177e4SLinus Torvalds 					uhci_finish_suspend(uhci, port,
3821da177e4SLinus Torvalds 							port_addr);
3831da177e4SLinus Torvalds 				else
3841da177e4SLinus Torvalds 					/* USB v2.0 7.1.7.7 */
3851da177e4SLinus Torvalds 					uhci->ports_timeout = jiffies +
3861da177e4SLinus Torvalds 						msecs_to_jiffies(20);
3871da177e4SLinus Torvalds 			}
3885a3e2055SDeng-Cheng Zhu 			break;
3891da177e4SLinus Torvalds 		case USB_PORT_FEAT_C_SUSPEND:
3901da177e4SLinus Torvalds 			clear_bit(port, &uhci->port_c_suspend);
3915a3e2055SDeng-Cheng Zhu 			break;
3921da177e4SLinus Torvalds 		case USB_PORT_FEAT_POWER:
3931da177e4SLinus Torvalds 			/* UHCI has no power switching */
3941da177e4SLinus Torvalds 			goto err;
3951da177e4SLinus Torvalds 		case USB_PORT_FEAT_C_CONNECTION:
3961da177e4SLinus Torvalds 			CLR_RH_PORTSTAT(USBPORTSC_CSC);
3975a3e2055SDeng-Cheng Zhu 			break;
3981da177e4SLinus Torvalds 		case USB_PORT_FEAT_C_OVER_CURRENT:
3991da177e4SLinus Torvalds 			CLR_RH_PORTSTAT(USBPORTSC_OCC);
4005a3e2055SDeng-Cheng Zhu 			break;
4011da177e4SLinus Torvalds 		case USB_PORT_FEAT_C_RESET:
4021da177e4SLinus Torvalds 			/* this driver won't report these */
4035a3e2055SDeng-Cheng Zhu 			break;
4041da177e4SLinus Torvalds 		default:
4051da177e4SLinus Torvalds 			goto err;
4061da177e4SLinus Torvalds 		}
4071da177e4SLinus Torvalds 		break;
4081da177e4SLinus Torvalds 	case GetHubDescriptor:
4095a3e2055SDeng-Cheng Zhu 		retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
4105a3e2055SDeng-Cheng Zhu 		memcpy(buf, root_hub_hub_des, retval);
4115a3e2055SDeng-Cheng Zhu 		if (retval > 2)
4121da177e4SLinus Torvalds 			buf[2] = uhci->rh_numports;
4135a3e2055SDeng-Cheng Zhu 		break;
4141da177e4SLinus Torvalds 	default:
4151da177e4SLinus Torvalds err:
4161da177e4SLinus Torvalds 		retval = -EPIPE;
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 	spin_unlock_irqrestore(&uhci->lock, flags);
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	return retval;
4211da177e4SLinus Torvalds }
422