162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Universal Host Controller Interface driver for USB.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Maintainer: Alan Stern <stern@rowland.harvard.edu>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * (C) Copyright 1999 Linus Torvalds
862306a36Sopenharmony_ci * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
962306a36Sopenharmony_ci * (C) Copyright 1999 Randy Dunlap
1062306a36Sopenharmony_ci * (C) Copyright 1999 Georg Acher, acher@in.tum.de
1162306a36Sopenharmony_ci * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
1262306a36Sopenharmony_ci * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
1362306a36Sopenharmony_ci * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic const __u8 root_hub_hub_des[] =
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	0x09,			/*  __u8  bLength; */
1962306a36Sopenharmony_ci	USB_DT_HUB,		/*  __u8  bDescriptorType; Hub-descriptor */
2062306a36Sopenharmony_ci	0x02,			/*  __u8  bNbrPorts; */
2162306a36Sopenharmony_ci	HUB_CHAR_NO_LPSM |	/* __u16  wHubCharacteristics; */
2262306a36Sopenharmony_ci		HUB_CHAR_INDV_PORT_OCPM, /* (per-port OC, no power switching) */
2362306a36Sopenharmony_ci	0x00,
2462306a36Sopenharmony_ci	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
2562306a36Sopenharmony_ci	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
2662306a36Sopenharmony_ci	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max */
2762306a36Sopenharmony_ci	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max */
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define	UHCI_RH_MAXCHILD	7
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* must write as zeroes */
3362306a36Sopenharmony_ci#define WZ_BITS		(USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* status change bits:  nonzero writes will clear */
3662306a36Sopenharmony_ci#define RWC_BITS	(USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* suspend/resume bits: port suspended or port resuming */
3962306a36Sopenharmony_ci#define SUSPEND_BITS	(USBPORTSC_SUSP | USBPORTSC_RD)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* A port that either is connected or has a changed-bit set will prevent
4262306a36Sopenharmony_ci * us from AUTO_STOPPING.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistatic int any_ports_active(struct uhci_hcd *uhci)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int port;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	for (port = 0; port < uhci->rh_numports; ++port) {
4962306a36Sopenharmony_ci		if ((uhci_readw(uhci, USBPORTSC1 + port * 2) &
5062306a36Sopenharmony_ci				(USBPORTSC_CCS | RWC_BITS)) ||
5162306a36Sopenharmony_ci				test_bit(port, &uhci->port_c_suspend))
5262306a36Sopenharmony_ci			return 1;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	int port;
6062306a36Sopenharmony_ci	int mask = RWC_BITS;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Some boards (both VIA and Intel apparently) report bogus
6362306a36Sopenharmony_ci	 * overcurrent indications, causing massive log spam unless
6462306a36Sopenharmony_ci	 * we completely ignore them.  This doesn't seem to be a problem
6562306a36Sopenharmony_ci	 * with the chipset so much as with the way it is connected on
6662306a36Sopenharmony_ci	 * the motherboard; if the overcurrent input is left to float
6762306a36Sopenharmony_ci	 * then it may constantly register false positives. */
6862306a36Sopenharmony_ci	if (ignore_oc)
6962306a36Sopenharmony_ci		mask &= ~USBPORTSC_OCC;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	*buf = 0;
7262306a36Sopenharmony_ci	for (port = 0; port < uhci->rh_numports; ++port) {
7362306a36Sopenharmony_ci		if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) ||
7462306a36Sopenharmony_ci				test_bit(port, &uhci->port_c_suspend))
7562306a36Sopenharmony_ci			*buf |= (1 << (port + 1));
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	return !!*buf;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define CLR_RH_PORTSTAT(x) \
8162306a36Sopenharmony_ci	status = uhci_readw(uhci, port_addr);	\
8262306a36Sopenharmony_ci	status &= ~(RWC_BITS|WZ_BITS); \
8362306a36Sopenharmony_ci	status &= ~(x); \
8462306a36Sopenharmony_ci	status |= RWC_BITS & (x); \
8562306a36Sopenharmony_ci	uhci_writew(uhci, status, port_addr)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define SET_RH_PORTSTAT(x) \
8862306a36Sopenharmony_ci	status = uhci_readw(uhci, port_addr);	\
8962306a36Sopenharmony_ci	status |= (x); \
9062306a36Sopenharmony_ci	status &= ~(RWC_BITS|WZ_BITS); \
9162306a36Sopenharmony_ci	uhci_writew(uhci, status, port_addr)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* UHCI controllers don't automatically stop resume signalling after 20 msec,
9462306a36Sopenharmony_ci * so we have to poll and check timeouts in order to take care of it.
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_cistatic void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
9762306a36Sopenharmony_ci		unsigned long port_addr)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	int status;
10062306a36Sopenharmony_ci	int i;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) {
10362306a36Sopenharmony_ci		CLR_RH_PORTSTAT(SUSPEND_BITS);
10462306a36Sopenharmony_ci		if (test_bit(port, &uhci->resuming_ports))
10562306a36Sopenharmony_ci			set_bit(port, &uhci->port_c_suspend);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		/* The controller won't actually turn off the RD bit until
10862306a36Sopenharmony_ci		 * it has had a chance to send a low-speed EOP sequence,
10962306a36Sopenharmony_ci		 * which is supposed to take 3 bit times (= 2 microseconds).
11062306a36Sopenharmony_ci		 * Experiments show that some controllers take longer, so
11162306a36Sopenharmony_ci		 * we'll poll for completion. */
11262306a36Sopenharmony_ci		for (i = 0; i < 10; ++i) {
11362306a36Sopenharmony_ci			if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS))
11462306a36Sopenharmony_ci				break;
11562306a36Sopenharmony_ci			udelay(1);
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	clear_bit(port, &uhci->resuming_ports);
11962306a36Sopenharmony_ci	usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* Wait for the UHCI controller in HP's iLO2 server management chip.
12362306a36Sopenharmony_ci * It can take up to 250 us to finish a reset and set the CSC bit.
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int i;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	for (i = 10; i < 250; i += 10) {
13062306a36Sopenharmony_ci		if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC)
13162306a36Sopenharmony_ci			return;
13262306a36Sopenharmony_ci		udelay(10);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	/* Log a warning? */
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void uhci_check_ports(struct uhci_hcd *uhci)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	unsigned int port;
14062306a36Sopenharmony_ci	unsigned long port_addr;
14162306a36Sopenharmony_ci	int status;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	for (port = 0; port < uhci->rh_numports; ++port) {
14462306a36Sopenharmony_ci		port_addr = USBPORTSC1 + 2 * port;
14562306a36Sopenharmony_ci		status = uhci_readw(uhci, port_addr);
14662306a36Sopenharmony_ci		if (unlikely(status & USBPORTSC_PR)) {
14762306a36Sopenharmony_ci			if (time_after_eq(jiffies, uhci->ports_timeout)) {
14862306a36Sopenharmony_ci				CLR_RH_PORTSTAT(USBPORTSC_PR);
14962306a36Sopenharmony_ci				udelay(10);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci				/* HP's server management chip requires
15262306a36Sopenharmony_ci				 * a longer delay. */
15362306a36Sopenharmony_ci				if (uhci->wait_for_hp)
15462306a36Sopenharmony_ci					wait_for_HP(uhci, port_addr);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci				/* If the port was enabled before, turning
15762306a36Sopenharmony_ci				 * reset on caused a port enable change.
15862306a36Sopenharmony_ci				 * Turning reset off causes a port connect
15962306a36Sopenharmony_ci				 * status change.  Clear these changes. */
16062306a36Sopenharmony_ci				CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
16162306a36Sopenharmony_ci				SET_RH_PORTSTAT(USBPORTSC_PE);
16262306a36Sopenharmony_ci			}
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		if (unlikely(status & USBPORTSC_RD)) {
16562306a36Sopenharmony_ci			if (!test_bit(port, &uhci->resuming_ports)) {
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci				/* Port received a wakeup request */
16862306a36Sopenharmony_ci				set_bit(port, &uhci->resuming_ports);
16962306a36Sopenharmony_ci				uhci->ports_timeout = jiffies +
17062306a36Sopenharmony_ci					msecs_to_jiffies(USB_RESUME_TIMEOUT);
17162306a36Sopenharmony_ci				usb_hcd_start_port_resume(
17262306a36Sopenharmony_ci						&uhci_to_hcd(uhci)->self, port);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci				/* Make sure we see the port again
17562306a36Sopenharmony_ci				 * after the resuming period is over. */
17662306a36Sopenharmony_ci				mod_timer(&uhci_to_hcd(uhci)->rh_timer,
17762306a36Sopenharmony_ci						uhci->ports_timeout);
17862306a36Sopenharmony_ci			} else if (time_after_eq(jiffies,
17962306a36Sopenharmony_ci						uhci->ports_timeout)) {
18062306a36Sopenharmony_ci				uhci_finish_suspend(uhci, port, port_addr);
18162306a36Sopenharmony_ci			}
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
18962306a36Sopenharmony_ci	unsigned long flags;
19062306a36Sopenharmony_ci	int status = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	spin_lock_irqsave(&uhci->lock, flags);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	uhci_scan_schedule(uhci);
19562306a36Sopenharmony_ci	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
19662306a36Sopenharmony_ci		goto done;
19762306a36Sopenharmony_ci	uhci_check_ports(uhci);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	status = get_hub_status_data(uhci, buf);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	switch (uhci->rh_state) {
20262306a36Sopenharmony_ci	    case UHCI_RH_SUSPENDED:
20362306a36Sopenharmony_ci		/* if port change, ask to be resumed */
20462306a36Sopenharmony_ci		if (status || uhci->resuming_ports) {
20562306a36Sopenharmony_ci			status = 1;
20662306a36Sopenharmony_ci			usb_hcd_resume_root_hub(hcd);
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	    case UHCI_RH_AUTO_STOPPED:
21162306a36Sopenharmony_ci		/* if port change, auto start */
21262306a36Sopenharmony_ci		if (status)
21362306a36Sopenharmony_ci			wakeup_rh(uhci);
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	    case UHCI_RH_RUNNING:
21762306a36Sopenharmony_ci		/* are any devices attached? */
21862306a36Sopenharmony_ci		if (!any_ports_active(uhci)) {
21962306a36Sopenharmony_ci			uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
22062306a36Sopenharmony_ci			uhci->auto_stop_time = jiffies + HZ;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	    case UHCI_RH_RUNNING_NODEVS:
22562306a36Sopenharmony_ci		/* auto-stop if nothing connected for 1 second */
22662306a36Sopenharmony_ci		if (any_ports_active(uhci))
22762306a36Sopenharmony_ci			uhci->rh_state = UHCI_RH_RUNNING;
22862306a36Sopenharmony_ci		else if (time_after_eq(jiffies, uhci->auto_stop_time) &&
22962306a36Sopenharmony_ci				!uhci->wait_for_hp)
23062306a36Sopenharmony_ci			suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
23162306a36Sopenharmony_ci		break;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	    default:
23462306a36Sopenharmony_ci		break;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cidone:
23862306a36Sopenharmony_ci	spin_unlock_irqrestore(&uhci->lock, flags);
23962306a36Sopenharmony_ci	return status;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/* size of returned buffer is part of USB spec */
24362306a36Sopenharmony_cistatic int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
24462306a36Sopenharmony_ci			u16 wIndex, char *buf, u16 wLength)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
24762306a36Sopenharmony_ci	int status, lstatus, retval = 0;
24862306a36Sopenharmony_ci	unsigned int port = wIndex - 1;
24962306a36Sopenharmony_ci	unsigned long port_addr = USBPORTSC1 + 2 * port;
25062306a36Sopenharmony_ci	u16 wPortChange, wPortStatus;
25162306a36Sopenharmony_ci	unsigned long flags;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
25462306a36Sopenharmony_ci		return -ETIMEDOUT;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	spin_lock_irqsave(&uhci->lock, flags);
25762306a36Sopenharmony_ci	switch (typeReq) {
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	case GetHubStatus:
26062306a36Sopenharmony_ci		*(__le32 *)buf = cpu_to_le32(0);
26162306a36Sopenharmony_ci		retval = 4; /* hub power */
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci	case GetPortStatus:
26462306a36Sopenharmony_ci		if (port >= uhci->rh_numports)
26562306a36Sopenharmony_ci			goto err;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		uhci_check_ports(uhci);
26862306a36Sopenharmony_ci		status = uhci_readw(uhci, port_addr);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		/* Intel controllers report the OverCurrent bit active on.
27162306a36Sopenharmony_ci		 * VIA controllers report it active off, so we'll adjust the
27262306a36Sopenharmony_ci		 * bit value.  (It's not standardized in the UHCI spec.)
27362306a36Sopenharmony_ci		 */
27462306a36Sopenharmony_ci		if (uhci->oc_low)
27562306a36Sopenharmony_ci			status ^= USBPORTSC_OC;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		/* UHCI doesn't support C_RESET (always false) */
27862306a36Sopenharmony_ci		wPortChange = lstatus = 0;
27962306a36Sopenharmony_ci		if (status & USBPORTSC_CSC)
28062306a36Sopenharmony_ci			wPortChange |= USB_PORT_STAT_C_CONNECTION;
28162306a36Sopenharmony_ci		if (status & USBPORTSC_PEC)
28262306a36Sopenharmony_ci			wPortChange |= USB_PORT_STAT_C_ENABLE;
28362306a36Sopenharmony_ci		if ((status & USBPORTSC_OCC) && !ignore_oc)
28462306a36Sopenharmony_ci			wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		if (test_bit(port, &uhci->port_c_suspend)) {
28762306a36Sopenharmony_ci			wPortChange |= USB_PORT_STAT_C_SUSPEND;
28862306a36Sopenharmony_ci			lstatus |= 1;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci		if (test_bit(port, &uhci->resuming_ports))
29162306a36Sopenharmony_ci			lstatus |= 4;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* UHCI has no power switching (always on) */
29462306a36Sopenharmony_ci		wPortStatus = USB_PORT_STAT_POWER;
29562306a36Sopenharmony_ci		if (status & USBPORTSC_CCS)
29662306a36Sopenharmony_ci			wPortStatus |= USB_PORT_STAT_CONNECTION;
29762306a36Sopenharmony_ci		if (status & USBPORTSC_PE) {
29862306a36Sopenharmony_ci			wPortStatus |= USB_PORT_STAT_ENABLE;
29962306a36Sopenharmony_ci			if (status & SUSPEND_BITS)
30062306a36Sopenharmony_ci				wPortStatus |= USB_PORT_STAT_SUSPEND;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci		if (status & USBPORTSC_OC)
30362306a36Sopenharmony_ci			wPortStatus |= USB_PORT_STAT_OVERCURRENT;
30462306a36Sopenharmony_ci		if (status & USBPORTSC_PR)
30562306a36Sopenharmony_ci			wPortStatus |= USB_PORT_STAT_RESET;
30662306a36Sopenharmony_ci		if (status & USBPORTSC_LSDA)
30762306a36Sopenharmony_ci			wPortStatus |= USB_PORT_STAT_LOW_SPEED;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (wPortChange)
31062306a36Sopenharmony_ci			dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
31162306a36Sopenharmony_ci					wIndex, status, lstatus);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		*(__le16 *)buf = cpu_to_le16(wPortStatus);
31462306a36Sopenharmony_ci		*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
31562306a36Sopenharmony_ci		retval = 4;
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	case SetHubFeature:		/* We don't implement these */
31862306a36Sopenharmony_ci	case ClearHubFeature:
31962306a36Sopenharmony_ci		switch (wValue) {
32062306a36Sopenharmony_ci		case C_HUB_OVER_CURRENT:
32162306a36Sopenharmony_ci		case C_HUB_LOCAL_POWER:
32262306a36Sopenharmony_ci			break;
32362306a36Sopenharmony_ci		default:
32462306a36Sopenharmony_ci			goto err;
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case SetPortFeature:
32862306a36Sopenharmony_ci		if (port >= uhci->rh_numports)
32962306a36Sopenharmony_ci			goto err;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		switch (wValue) {
33262306a36Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
33362306a36Sopenharmony_ci			SET_RH_PORTSTAT(USBPORTSC_SUSP);
33462306a36Sopenharmony_ci			break;
33562306a36Sopenharmony_ci		case USB_PORT_FEAT_RESET:
33662306a36Sopenharmony_ci			SET_RH_PORTSTAT(USBPORTSC_PR);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci			/* Reset terminates Resume signalling */
33962306a36Sopenharmony_ci			uhci_finish_suspend(uhci, port, port_addr);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			/* USB v2.0 7.1.7.5 */
34262306a36Sopenharmony_ci			uhci->ports_timeout = jiffies +
34362306a36Sopenharmony_ci				msecs_to_jiffies(USB_RESUME_TIMEOUT);
34462306a36Sopenharmony_ci			break;
34562306a36Sopenharmony_ci		case USB_PORT_FEAT_POWER:
34662306a36Sopenharmony_ci			/* UHCI has no power switching */
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci		default:
34962306a36Sopenharmony_ci			goto err;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci	case ClearPortFeature:
35362306a36Sopenharmony_ci		if (port >= uhci->rh_numports)
35462306a36Sopenharmony_ci			goto err;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		switch (wValue) {
35762306a36Sopenharmony_ci		case USB_PORT_FEAT_ENABLE:
35862306a36Sopenharmony_ci			CLR_RH_PORTSTAT(USBPORTSC_PE);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci			/* Disable terminates Resume signalling */
36162306a36Sopenharmony_ci			uhci_finish_suspend(uhci, port, port_addr);
36262306a36Sopenharmony_ci			break;
36362306a36Sopenharmony_ci		case USB_PORT_FEAT_C_ENABLE:
36462306a36Sopenharmony_ci			CLR_RH_PORTSTAT(USBPORTSC_PEC);
36562306a36Sopenharmony_ci			break;
36662306a36Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
36762306a36Sopenharmony_ci			if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) {
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci				/* Make certain the port isn't suspended */
37062306a36Sopenharmony_ci				uhci_finish_suspend(uhci, port, port_addr);
37162306a36Sopenharmony_ci			} else if (!test_and_set_bit(port,
37262306a36Sopenharmony_ci						&uhci->resuming_ports)) {
37362306a36Sopenharmony_ci				SET_RH_PORTSTAT(USBPORTSC_RD);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci				/* The controller won't allow RD to be set
37662306a36Sopenharmony_ci				 * if the port is disabled.  When this happens
37762306a36Sopenharmony_ci				 * just skip the Resume signalling.
37862306a36Sopenharmony_ci				 */
37962306a36Sopenharmony_ci				if (!(uhci_readw(uhci, port_addr) &
38062306a36Sopenharmony_ci						USBPORTSC_RD))
38162306a36Sopenharmony_ci					uhci_finish_suspend(uhci, port,
38262306a36Sopenharmony_ci							port_addr);
38362306a36Sopenharmony_ci				else
38462306a36Sopenharmony_ci					/* USB v2.0 7.1.7.7 */
38562306a36Sopenharmony_ci					uhci->ports_timeout = jiffies +
38662306a36Sopenharmony_ci						msecs_to_jiffies(20);
38762306a36Sopenharmony_ci			}
38862306a36Sopenharmony_ci			break;
38962306a36Sopenharmony_ci		case USB_PORT_FEAT_C_SUSPEND:
39062306a36Sopenharmony_ci			clear_bit(port, &uhci->port_c_suspend);
39162306a36Sopenharmony_ci			break;
39262306a36Sopenharmony_ci		case USB_PORT_FEAT_POWER:
39362306a36Sopenharmony_ci			/* UHCI has no power switching */
39462306a36Sopenharmony_ci			goto err;
39562306a36Sopenharmony_ci		case USB_PORT_FEAT_C_CONNECTION:
39662306a36Sopenharmony_ci			CLR_RH_PORTSTAT(USBPORTSC_CSC);
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci		case USB_PORT_FEAT_C_OVER_CURRENT:
39962306a36Sopenharmony_ci			CLR_RH_PORTSTAT(USBPORTSC_OCC);
40062306a36Sopenharmony_ci			break;
40162306a36Sopenharmony_ci		case USB_PORT_FEAT_C_RESET:
40262306a36Sopenharmony_ci			/* this driver won't report these */
40362306a36Sopenharmony_ci			break;
40462306a36Sopenharmony_ci		default:
40562306a36Sopenharmony_ci			goto err;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case GetHubDescriptor:
40962306a36Sopenharmony_ci		retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
41062306a36Sopenharmony_ci		memcpy(buf, root_hub_hub_des, retval);
41162306a36Sopenharmony_ci		if (retval > 2)
41262306a36Sopenharmony_ci			buf[2] = uhci->rh_numports;
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	default:
41562306a36Sopenharmony_cierr:
41662306a36Sopenharmony_ci		retval = -EPIPE;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	spin_unlock_irqrestore(&uhci->lock, flags);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return retval;
42162306a36Sopenharmony_ci}
422