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