11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Universal Host Controller Interface driver for USB. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Maintainer: Alan Stern <stern@rowland.harvard.edu> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * (C) Copyright 1999 Linus Torvalds 71da177e4SLinus Torvalds * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com 81da177e4SLinus Torvalds * (C) Copyright 1999 Randy Dunlap 91da177e4SLinus Torvalds * (C) Copyright 1999 Georg Acher, acher@in.tum.de 101da177e4SLinus Torvalds * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de 111da177e4SLinus Torvalds * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch 121da177e4SLinus Torvalds * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds 15bef4665aSMing Lei static const __u8 root_hub_hub_des[] = 161da177e4SLinus Torvalds { 171da177e4SLinus Torvalds 0x09, /* __u8 bLength; */ 181da177e4SLinus Torvalds 0x29, /* __u8 bDescriptorType; Hub-descriptor */ 191da177e4SLinus Torvalds 0x02, /* __u8 bNbrPorts; */ 20673016d9SSergei Shtylyov HUB_CHAR_NO_LPSM | /* __u16 wHubCharacteristics; */ 21673016d9SSergei Shtylyov HUB_CHAR_INDV_PORT_OCPM, /* (per-port OC, no power switching) */ 22673016d9SSergei Shtylyov 0x00, 231da177e4SLinus Torvalds 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ 241da177e4SLinus Torvalds 0x00, /* __u8 bHubContrCurrent; 0 mA */ 253171fcabSChen Gang 0x00, /* __u8 DeviceRemovable; *** 7 Ports max */ 263171fcabSChen Gang 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max */ 271da177e4SLinus Torvalds }; 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #define UHCI_RH_MAXCHILD 7 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds /* must write as zeroes */ 321da177e4SLinus Torvalds #define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4) 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds /* status change bits: nonzero writes will clear */ 351da177e4SLinus Torvalds #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) 361da177e4SLinus Torvalds 3788018158SAlan Stern /* suspend/resume bits: port suspended or port resuming */ 3888018158SAlan Stern #define SUSPEND_BITS (USBPORTSC_SUSP | USBPORTSC_RD) 3988018158SAlan Stern 40f5946f82SAlan Stern /* A port that either is connected or has a changed-bit set will prevent 41f5946f82SAlan Stern * us from AUTO_STOPPING. 42f5946f82SAlan Stern */ 43f5946f82SAlan Stern static int any_ports_active(struct uhci_hcd *uhci) 44f5946f82SAlan Stern { 45f5946f82SAlan Stern int port; 46f5946f82SAlan Stern 47f5946f82SAlan Stern for (port = 0; port < uhci->rh_numports; ++port) { 489faa091aSJan Andersson if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & 49f5946f82SAlan Stern (USBPORTSC_CCS | RWC_BITS)) || 50f5946f82SAlan Stern test_bit(port, &uhci->port_c_suspend)) 51f5946f82SAlan Stern return 1; 52f5946f82SAlan Stern } 53f5946f82SAlan Stern return 0; 54f5946f82SAlan Stern } 55f5946f82SAlan Stern 566c1b445cSAlan Stern static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) 571da177e4SLinus Torvalds { 581da177e4SLinus Torvalds int port; 595f8364b7SAlan Stern int mask = RWC_BITS; 605f8364b7SAlan Stern 615f8364b7SAlan Stern /* Some boards (both VIA and Intel apparently) report bogus 625f8364b7SAlan Stern * overcurrent indications, causing massive log spam unless 635f8364b7SAlan Stern * we completely ignore them. This doesn't seem to be a problem 645f8364b7SAlan Stern * with the chipset so much as with the way it is connected on 655f8364b7SAlan Stern * the motherboard; if the overcurrent input is left to float 665f8364b7SAlan Stern * then it may constantly register false positives. */ 675f8364b7SAlan Stern if (ignore_oc) 685f8364b7SAlan Stern mask &= ~USBPORTSC_OCC; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds *buf = 0; 711da177e4SLinus Torvalds for (port = 0; port < uhci->rh_numports; ++port) { 729faa091aSJan Andersson if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) || 731da177e4SLinus Torvalds test_bit(port, &uhci->port_c_suspend)) 741da177e4SLinus Torvalds *buf |= (1 << (port + 1)); 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds return !!*buf; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds #define CLR_RH_PORTSTAT(x) \ 809faa091aSJan Andersson status = uhci_readw(uhci, port_addr); \ 811da177e4SLinus Torvalds status &= ~(RWC_BITS|WZ_BITS); \ 821da177e4SLinus Torvalds status &= ~(x); \ 831da177e4SLinus Torvalds status |= RWC_BITS & (x); \ 849faa091aSJan Andersson uhci_writew(uhci, status, port_addr) 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds #define SET_RH_PORTSTAT(x) \ 879faa091aSJan Andersson status = uhci_readw(uhci, port_addr); \ 881da177e4SLinus Torvalds status |= (x); \ 891da177e4SLinus Torvalds status &= ~(RWC_BITS|WZ_BITS); \ 909faa091aSJan Andersson uhci_writew(uhci, status, port_addr) 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* UHCI controllers don't automatically stop resume signalling after 20 msec, 931da177e4SLinus Torvalds * so we have to poll and check timeouts in order to take care of it. 941da177e4SLinus Torvalds */ 951da177e4SLinus Torvalds static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, 961da177e4SLinus Torvalds unsigned long port_addr) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds int status; 99de06a3b8SAlan Stern int i; 1001da177e4SLinus Torvalds 1019faa091aSJan Andersson if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) { 10288018158SAlan Stern CLR_RH_PORTSTAT(SUSPEND_BITS); 1038e326406SAlan Stern if (test_bit(port, &uhci->resuming_ports)) 1041da177e4SLinus Torvalds set_bit(port, &uhci->port_c_suspend); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* The controller won't actually turn off the RD bit until 1071da177e4SLinus Torvalds * it has had a chance to send a low-speed EOP sequence, 108de06a3b8SAlan Stern * which is supposed to take 3 bit times (= 2 microseconds). 109de06a3b8SAlan Stern * Experiments show that some controllers take longer, so 110de06a3b8SAlan Stern * we'll poll for completion. */ 111de06a3b8SAlan Stern for (i = 0; i < 10; ++i) { 1129faa091aSJan Andersson if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS)) 113de06a3b8SAlan Stern break; 114de06a3b8SAlan Stern udelay(1); 115de06a3b8SAlan Stern } 1161da177e4SLinus Torvalds } 1178e326406SAlan Stern clear_bit(port, &uhci->resuming_ports); 118840008bbSAlan Stern usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 121ae557175SAlan Stern /* Wait for the UHCI controller in HP's iLO2 server management chip. 122ae557175SAlan Stern * It can take up to 250 us to finish a reset and set the CSC bit. 123ae557175SAlan Stern */ 1249faa091aSJan Andersson static void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr) 125ae557175SAlan Stern { 126ae557175SAlan Stern int i; 127ae557175SAlan Stern 128ae557175SAlan Stern for (i = 10; i < 250; i += 10) { 1299faa091aSJan Andersson if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC) 130ae557175SAlan Stern return; 131ae557175SAlan Stern udelay(10); 132ae557175SAlan Stern } 133ae557175SAlan Stern /* Log a warning? */ 134ae557175SAlan Stern } 135ae557175SAlan Stern 1361da177e4SLinus Torvalds static void uhci_check_ports(struct uhci_hcd *uhci) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds unsigned int port; 1391da177e4SLinus Torvalds unsigned long port_addr; 1401da177e4SLinus Torvalds int status; 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds for (port = 0; port < uhci->rh_numports; ++port) { 1439faa091aSJan Andersson port_addr = USBPORTSC1 + 2 * port; 1449faa091aSJan Andersson status = uhci_readw(uhci, port_addr); 1451da177e4SLinus Torvalds if (unlikely(status & USBPORTSC_PR)) { 1461da177e4SLinus Torvalds if (time_after_eq(jiffies, uhci->ports_timeout)) { 1471da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_PR); 1481da177e4SLinus Torvalds udelay(10); 1491da177e4SLinus Torvalds 150ae557175SAlan Stern /* HP's server management chip requires 151ae557175SAlan Stern * a longer delay. */ 152dfeca7a8SJan Andersson if (uhci->wait_for_hp) 1539faa091aSJan Andersson wait_for_HP(uhci, port_addr); 154ae557175SAlan Stern 1551da177e4SLinus Torvalds /* If the port was enabled before, turning 1561da177e4SLinus Torvalds * reset on caused a port enable change. 1571da177e4SLinus Torvalds * Turning reset off causes a port connect 1581da177e4SLinus Torvalds * status change. Clear these changes. */ 1591da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC); 1601da177e4SLinus Torvalds SET_RH_PORTSTAT(USBPORTSC_PE); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds if (unlikely(status & USBPORTSC_RD)) { 1641da177e4SLinus Torvalds if (!test_bit(port, &uhci->resuming_ports)) { 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds /* Port received a wakeup request */ 1671da177e4SLinus Torvalds set_bit(port, &uhci->resuming_ports); 1681da177e4SLinus Torvalds uhci->ports_timeout = jiffies + 169*b8fb6f79SFelipe Balbi msecs_to_jiffies(USB_RESUME_TIMEOUT); 170840008bbSAlan Stern usb_hcd_start_port_resume( 171840008bbSAlan Stern &uhci_to_hcd(uhci)->self, port); 1726c1b445cSAlan Stern 1736c1b445cSAlan Stern /* Make sure we see the port again 1746c1b445cSAlan Stern * after the resuming period is over. */ 1756c1b445cSAlan Stern mod_timer(&uhci_to_hcd(uhci)->rh_timer, 1766c1b445cSAlan Stern uhci->ports_timeout); 1771da177e4SLinus Torvalds } else if (time_after_eq(jiffies, 1781da177e4SLinus Torvalds uhci->ports_timeout)) { 1791da177e4SLinus Torvalds uhci_finish_suspend(uhci, port, port_addr); 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 1856c1b445cSAlan Stern static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) 1866c1b445cSAlan Stern { 1876c1b445cSAlan Stern struct uhci_hcd *uhci = hcd_to_uhci(hcd); 1886c1b445cSAlan Stern unsigned long flags; 1891f09df8bSAlan Stern int status = 0; 1906c1b445cSAlan Stern 1916c1b445cSAlan Stern spin_lock_irqsave(&uhci->lock, flags); 1926c1b445cSAlan Stern 1937d12e780SDavid Howells uhci_scan_schedule(uhci); 194541c7d43SAlan Stern if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) 1951f09df8bSAlan Stern goto done; 1966c1b445cSAlan Stern uhci_check_ports(uhci); 1971f09df8bSAlan Stern 1986c1b445cSAlan Stern status = get_hub_status_data(uhci, buf); 1996c1b445cSAlan Stern 2006c1b445cSAlan Stern switch (uhci->rh_state) { 2016c1b445cSAlan Stern case UHCI_RH_SUSPENDED: 2026c1b445cSAlan Stern /* if port change, ask to be resumed */ 203b446b96fSAlan Stern if (status || uhci->resuming_ports) { 204b446b96fSAlan Stern status = 1; 2056c1b445cSAlan Stern usb_hcd_resume_root_hub(hcd); 206b446b96fSAlan Stern } 2076c1b445cSAlan Stern break; 2086c1b445cSAlan Stern 2096c1b445cSAlan Stern case UHCI_RH_AUTO_STOPPED: 2106c1b445cSAlan Stern /* if port change, auto start */ 2116c1b445cSAlan Stern if (status) 2126c1b445cSAlan Stern wakeup_rh(uhci); 2136c1b445cSAlan Stern break; 2146c1b445cSAlan Stern 2156c1b445cSAlan Stern case UHCI_RH_RUNNING: 2166c1b445cSAlan Stern /* are any devices attached? */ 2176c1b445cSAlan Stern if (!any_ports_active(uhci)) { 2186c1b445cSAlan Stern uhci->rh_state = UHCI_RH_RUNNING_NODEVS; 2196c1b445cSAlan Stern uhci->auto_stop_time = jiffies + HZ; 2206c1b445cSAlan Stern } 2216c1b445cSAlan Stern break; 2226c1b445cSAlan Stern 2236c1b445cSAlan Stern case UHCI_RH_RUNNING_NODEVS: 2246c1b445cSAlan Stern /* auto-stop if nothing connected for 1 second */ 2256c1b445cSAlan Stern if (any_ports_active(uhci)) 2266c1b445cSAlan Stern uhci->rh_state = UHCI_RH_RUNNING; 227997ff893SAlan Stern else if (time_after_eq(jiffies, uhci->auto_stop_time) && 228997ff893SAlan Stern !uhci->wait_for_hp) 2296c1b445cSAlan Stern suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); 2306c1b445cSAlan Stern break; 2316c1b445cSAlan Stern 2326c1b445cSAlan Stern default: 2336c1b445cSAlan Stern break; 2346c1b445cSAlan Stern } 2356c1b445cSAlan Stern 2366c1b445cSAlan Stern done: 2376c1b445cSAlan Stern spin_unlock_irqrestore(&uhci->lock, flags); 2386c1b445cSAlan Stern return status; 2396c1b445cSAlan Stern } 2406c1b445cSAlan Stern 2411da177e4SLinus Torvalds /* size of returned buffer is part of USB spec */ 2421da177e4SLinus Torvalds static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 2431da177e4SLinus Torvalds u16 wIndex, char *buf, u16 wLength) 2441da177e4SLinus Torvalds { 2451da177e4SLinus Torvalds struct uhci_hcd *uhci = hcd_to_uhci(hcd); 2465a3e2055SDeng-Cheng Zhu int status, lstatus, retval = 0; 2471da177e4SLinus Torvalds unsigned int port = wIndex - 1; 2489faa091aSJan Andersson unsigned long port_addr = USBPORTSC1 + 2 * port; 2491da177e4SLinus Torvalds u16 wPortChange, wPortStatus; 2501da177e4SLinus Torvalds unsigned long flags; 2511da177e4SLinus Torvalds 252541c7d43SAlan Stern if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) 253a8bed8b6SAlan Stern return -ETIMEDOUT; 254a8bed8b6SAlan Stern 2551da177e4SLinus Torvalds spin_lock_irqsave(&uhci->lock, flags); 2561da177e4SLinus Torvalds switch (typeReq) { 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds case GetHubStatus: 2591da177e4SLinus Torvalds *(__le32 *)buf = cpu_to_le32(0); 2605a3e2055SDeng-Cheng Zhu retval = 4; /* hub power */ 2615a3e2055SDeng-Cheng Zhu break; 2621da177e4SLinus Torvalds case GetPortStatus: 2631da177e4SLinus Torvalds if (port >= uhci->rh_numports) 2641da177e4SLinus Torvalds goto err; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds uhci_check_ports(uhci); 2679faa091aSJan Andersson status = uhci_readw(uhci, port_addr); 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds /* Intel controllers report the OverCurrent bit active on. 2701da177e4SLinus Torvalds * VIA controllers report it active off, so we'll adjust the 2711da177e4SLinus Torvalds * bit value. (It's not standardized in the UHCI spec.) 2721da177e4SLinus Torvalds */ 273dfeca7a8SJan Andersson if (uhci->oc_low) 2741da177e4SLinus Torvalds status ^= USBPORTSC_OC; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds /* UHCI doesn't support C_RESET (always false) */ 2771da177e4SLinus Torvalds wPortChange = lstatus = 0; 2781da177e4SLinus Torvalds if (status & USBPORTSC_CSC) 2791da177e4SLinus Torvalds wPortChange |= USB_PORT_STAT_C_CONNECTION; 2801da177e4SLinus Torvalds if (status & USBPORTSC_PEC) 2811da177e4SLinus Torvalds wPortChange |= USB_PORT_STAT_C_ENABLE; 2825f8364b7SAlan Stern if ((status & USBPORTSC_OCC) && !ignore_oc) 2831da177e4SLinus Torvalds wPortChange |= USB_PORT_STAT_C_OVERCURRENT; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds if (test_bit(port, &uhci->port_c_suspend)) { 2861da177e4SLinus Torvalds wPortChange |= USB_PORT_STAT_C_SUSPEND; 2871da177e4SLinus Torvalds lstatus |= 1; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds if (test_bit(port, &uhci->resuming_ports)) 2901da177e4SLinus Torvalds lstatus |= 4; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds /* UHCI has no power switching (always on) */ 2931da177e4SLinus Torvalds wPortStatus = USB_PORT_STAT_POWER; 2941da177e4SLinus Torvalds if (status & USBPORTSC_CCS) 2951da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_CONNECTION; 2961da177e4SLinus Torvalds if (status & USBPORTSC_PE) { 2971da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_ENABLE; 29888018158SAlan Stern if (status & SUSPEND_BITS) 2991da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_SUSPEND; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds if (status & USBPORTSC_OC) 3021da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_OVERCURRENT; 3031da177e4SLinus Torvalds if (status & USBPORTSC_PR) 3041da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_RESET; 3051da177e4SLinus Torvalds if (status & USBPORTSC_LSDA) 3061da177e4SLinus Torvalds wPortStatus |= USB_PORT_STAT_LOW_SPEED; 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds if (wPortChange) 3091da177e4SLinus Torvalds dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n", 3101da177e4SLinus Torvalds wIndex, status, lstatus); 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds *(__le16 *)buf = cpu_to_le16(wPortStatus); 3131da177e4SLinus Torvalds *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); 3145a3e2055SDeng-Cheng Zhu retval = 4; 3155a3e2055SDeng-Cheng Zhu break; 3161da177e4SLinus Torvalds case SetHubFeature: /* We don't implement these */ 3171da177e4SLinus Torvalds case ClearHubFeature: 3181da177e4SLinus Torvalds switch (wValue) { 3191da177e4SLinus Torvalds case C_HUB_OVER_CURRENT: 3201da177e4SLinus Torvalds case C_HUB_LOCAL_POWER: 3215a3e2055SDeng-Cheng Zhu break; 3221da177e4SLinus Torvalds default: 3231da177e4SLinus Torvalds goto err; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds break; 3261da177e4SLinus Torvalds case SetPortFeature: 3271da177e4SLinus Torvalds if (port >= uhci->rh_numports) 3281da177e4SLinus Torvalds goto err; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds switch (wValue) { 3311da177e4SLinus Torvalds case USB_PORT_FEAT_SUSPEND: 3321da177e4SLinus Torvalds SET_RH_PORTSTAT(USBPORTSC_SUSP); 3335a3e2055SDeng-Cheng Zhu break; 3341da177e4SLinus Torvalds case USB_PORT_FEAT_RESET: 3351da177e4SLinus Torvalds SET_RH_PORTSTAT(USBPORTSC_PR); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /* Reset terminates Resume signalling */ 3381da177e4SLinus Torvalds uhci_finish_suspend(uhci, port, port_addr); 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds /* USB v2.0 7.1.7.5 */ 341*b8fb6f79SFelipe Balbi uhci->ports_timeout = jiffies + 342*b8fb6f79SFelipe Balbi msecs_to_jiffies(USB_RESUME_TIMEOUT); 3435a3e2055SDeng-Cheng Zhu break; 3441da177e4SLinus Torvalds case USB_PORT_FEAT_POWER: 3451da177e4SLinus Torvalds /* UHCI has no power switching */ 3465a3e2055SDeng-Cheng Zhu break; 3471da177e4SLinus Torvalds default: 3481da177e4SLinus Torvalds goto err; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds break; 3511da177e4SLinus Torvalds case ClearPortFeature: 3521da177e4SLinus Torvalds if (port >= uhci->rh_numports) 3531da177e4SLinus Torvalds goto err; 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds switch (wValue) { 3561da177e4SLinus Torvalds case USB_PORT_FEAT_ENABLE: 3571da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_PE); 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds /* Disable terminates Resume signalling */ 3601da177e4SLinus Torvalds uhci_finish_suspend(uhci, port, port_addr); 3615a3e2055SDeng-Cheng Zhu break; 3621da177e4SLinus Torvalds case USB_PORT_FEAT_C_ENABLE: 3631da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_PEC); 3645a3e2055SDeng-Cheng Zhu break; 3651da177e4SLinus Torvalds case USB_PORT_FEAT_SUSPEND: 3669faa091aSJan Andersson if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) { 3678e326406SAlan Stern 3688e326406SAlan Stern /* Make certain the port isn't suspended */ 3698e326406SAlan Stern uhci_finish_suspend(uhci, port, port_addr); 3708e326406SAlan Stern } else if (!test_and_set_bit(port, 3711da177e4SLinus Torvalds &uhci->resuming_ports)) { 3721da177e4SLinus Torvalds SET_RH_PORTSTAT(USBPORTSC_RD); 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds /* The controller won't allow RD to be set 3751da177e4SLinus Torvalds * if the port is disabled. When this happens 3761da177e4SLinus Torvalds * just skip the Resume signalling. 3771da177e4SLinus Torvalds */ 3789faa091aSJan Andersson if (!(uhci_readw(uhci, port_addr) & 3799faa091aSJan Andersson USBPORTSC_RD)) 3801da177e4SLinus Torvalds uhci_finish_suspend(uhci, port, 3811da177e4SLinus Torvalds port_addr); 3821da177e4SLinus Torvalds else 3831da177e4SLinus Torvalds /* USB v2.0 7.1.7.7 */ 3841da177e4SLinus Torvalds uhci->ports_timeout = jiffies + 3851da177e4SLinus Torvalds msecs_to_jiffies(20); 3861da177e4SLinus Torvalds } 3875a3e2055SDeng-Cheng Zhu break; 3881da177e4SLinus Torvalds case USB_PORT_FEAT_C_SUSPEND: 3891da177e4SLinus Torvalds clear_bit(port, &uhci->port_c_suspend); 3905a3e2055SDeng-Cheng Zhu break; 3911da177e4SLinus Torvalds case USB_PORT_FEAT_POWER: 3921da177e4SLinus Torvalds /* UHCI has no power switching */ 3931da177e4SLinus Torvalds goto err; 3941da177e4SLinus Torvalds case USB_PORT_FEAT_C_CONNECTION: 3951da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_CSC); 3965a3e2055SDeng-Cheng Zhu break; 3971da177e4SLinus Torvalds case USB_PORT_FEAT_C_OVER_CURRENT: 3981da177e4SLinus Torvalds CLR_RH_PORTSTAT(USBPORTSC_OCC); 3995a3e2055SDeng-Cheng Zhu break; 4001da177e4SLinus Torvalds case USB_PORT_FEAT_C_RESET: 4011da177e4SLinus Torvalds /* this driver won't report these */ 4025a3e2055SDeng-Cheng Zhu break; 4031da177e4SLinus Torvalds default: 4041da177e4SLinus Torvalds goto err; 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds break; 4071da177e4SLinus Torvalds case GetHubDescriptor: 4085a3e2055SDeng-Cheng Zhu retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); 4095a3e2055SDeng-Cheng Zhu memcpy(buf, root_hub_hub_des, retval); 4105a3e2055SDeng-Cheng Zhu if (retval > 2) 4111da177e4SLinus Torvalds buf[2] = uhci->rh_numports; 4125a3e2055SDeng-Cheng Zhu break; 4131da177e4SLinus Torvalds default: 4141da177e4SLinus Torvalds err: 4151da177e4SLinus Torvalds retval = -EPIPE; 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds spin_unlock_irqrestore(&uhci->lock, flags); 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds return retval; 4201da177e4SLinus Torvalds } 421