ChangeSet 1.1557.49.25, 2004/02/18 15:05:01-08:00, stern@rowland.harvard.edu [PATCH] USB: Improve UHCI root hub code: descriptor, OC bits, etc. This patch is from David Brownell. UHCI root hub updates ... minor bugfixes and cleanups, improving conformance with the USB hub specification. - UHCI doesn't support any kind of power switching; so modify the hub descriptor to stop claiming it does! Likewise fail attempts to disable power on any port. - Intel defined both overcurrent status overcurrent-change bits, but the current code only knows about one. Modify hub descriptor to report per-port overcurrent protection; and use both bits. - Modify the port status set/clear macros to know about the bits that must always be written as zero, and the write-to-clear bits. Update callers which wrote "set" instead of "clear". - Rewrote code returning port status; magic numbers are gone. - Driver can't really support 8 root hub ports; don't try. Also moves the #define DEBUG earlier so that it can kick in any of the various debug macros ... like pr_debug() and dev_dbg(). drivers/usb/host/uhci-hcd.c | 12 ++--- drivers/usb/host/uhci-hcd.h | 9 +++- drivers/usb/host/uhci-hub.c | 94 +++++++++++++++++++++++++++++--------------- 3 files changed, 76 insertions(+), 39 deletions(-) diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c Thu Feb 19 17:21:02 2004 +++ b/drivers/usb/host/uhci-hcd.c Thu Feb 19 17:21:02 2004 @@ -27,6 +27,11 @@ */ #include +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif #include #include #include @@ -43,11 +48,6 @@ #include #include #include -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif #include #include @@ -2276,7 +2276,7 @@ /* This is experimental so anything less than 2 or greater than 8 is */ /* something weird and we'll ignore it */ - if (port < 2 || port > 8) { + if (port < 2 || port > UHCI_RH_MAXCHILD) { info("port count misdetected? forcing to 2 ports"); port = 2; } diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h --- a/drivers/usb/host/uhci-hcd.h Thu Feb 19 17:21:02 2004 +++ b/drivers/usb/host/uhci-hcd.h Thu Feb 19 17:21:02 2004 @@ -49,12 +49,19 @@ #define USBPORTSC_CSC 0x0002 /* Connect Status Change */ #define USBPORTSC_PE 0x0004 /* Port Enable */ #define USBPORTSC_PEC 0x0008 /* Port Enable Change */ -#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */ +#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */ #define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */ #define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ #define USBPORTSC_PR 0x0200 /* Port Reset */ +/* OC and OCC from Intel 430TX and later (not UHCI 1.1d spec) */ #define USBPORTSC_OC 0x0400 /* Over Current condition */ +#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */ #define USBPORTSC_SUSP 0x1000 /* Suspend */ +#define USBPORTSC_RES2 0x2000 /* reserved, write zeroes */ +#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */ +#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */ /* Legacy support register */ #define USBLEGSUP 0xc0 diff -Nru a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c --- a/drivers/usb/host/uhci-hub.c Thu Feb 19 17:21:02 2004 +++ b/drivers/usb/host/uhci-hub.c Thu Feb 19 17:21:02 2004 @@ -16,14 +16,22 @@ 0x09, /* __u8 bLength; */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, + 0x0a, /* __u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ 0x00, /* __u8 bHubContrCurrent; 0 mA */ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ }; +#define UHCI_RH_MAXCHILD 7 + +/* must write as zeroes */ +#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4) + +/* status change bits: nonzero writes will clear */ +#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) + static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -32,7 +40,9 @@ *buf = 0; for (i = 0; i < uhci->rh_numports; i++) { - *buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + *buf |= (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS) != 0 + ? (1 << (i + 1)) + : 0; len = (i + 1) / 8 + 1; } @@ -43,12 +53,15 @@ #define CLR_RH_PORTSTAT(x) \ status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ - status = (status & 0xfff5) & ~(x); \ + status &= ~(RWC_BITS|WZ_BITS); \ + status &= ~(x); \ + status |= RWC_BITS & (x); \ outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) #define SET_RH_PORTSTAT(x) \ status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ - status = (status & 0xfff5) | (x); \ + status |= (x); \ + status &= ~(RWC_BITS|WZ_BITS); \ outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) @@ -57,13 +70,9 @@ u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int i, status, retval = 0, len = 0; + int status, retval = 0, len = 0; unsigned int io_addr = uhci->io_addr; - __u16 cstatus; - char c_p_r[8]; - - for (i = 0; i < 8; i++) - c_p_r[i] = 0; + u16 wPortChange, wPortStatus; switch (typeReq) { /* Request Destination: @@ -79,18 +88,39 @@ OK(4); /* hub power */ case GetPortStatus: status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); - cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | - ((status & USBPORTSC_PEC) >> (3 - 1)) | - (c_p_r[wIndex - 1] << (0 + 4)); - status = (status & USBPORTSC_CCS) | - ((status & USBPORTSC_PE) >> (2 - 1)) | - ((status & USBPORTSC_SUSP) >> (12 - 2)) | - ((status & USBPORTSC_PR) >> (9 - 4)) | - (1 << 8) | /* power on */ - ((status & USBPORTSC_LSDA) << (-8 + 9)); - *(__u16 *)buf = cpu_to_le16(status); - *(__u16 *)(buf + 2) = cpu_to_le16(cstatus); + /* C_SUSPEND and C_RESET are always false */ + wPortChange = 0; + if (status & USBPORTSC_CSC) + wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16); + if (status & USBPORTSC_PEC) + wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16); + if (status & USBPORTSC_OCC) + wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16); + + /* UHCI has no power switching (always on) */ + wPortStatus = 1 << USB_PORT_FEAT_POWER; + if (status & USBPORTSC_CCS) + wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION; + if (status & USBPORTSC_PE) { + wPortStatus |= 1 << USB_PORT_FEAT_ENABLE; + if (status & (USBPORTSC_SUSP | USBPORTSC_RD)) + wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND; + } + if (status & USBPORTSC_OC) + wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT; + if (status & USBPORTSC_PR) + wPortStatus |= 1 << USB_PORT_FEAT_RESET; + if (status & USBPORTSC_LSDA) + wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED; + + if (wPortChange) + dev_dbg (uhci->hcd.self.controller, + "port %d portsc %04x\n", + wIndex, status); + + *(__u16 *)buf = cpu_to_le16(wPortStatus); + *(__u16 *)(buf + 2) = cpu_to_le16(wPortChange); OK(4); case SetHubFeature: switch (wValue) { @@ -104,6 +134,7 @@ case ClearHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: OK(0); /* hub power over current */ default: goto err; @@ -120,18 +151,15 @@ case USB_PORT_FEAT_RESET: SET_RH_PORTSTAT(USBPORTSC_PR); mdelay(50); /* USB v1.1 7.1.7.3 */ - c_p_r[wIndex - 1] = 1; CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); SET_RH_PORTSTAT(USBPORTSC_PE); mdelay(10); - SET_RH_PORTSTAT(0xa); + CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC); OK(0); case USB_PORT_FEAT_POWER: + /* UHCI has no power switching */ OK(0); /* port power ** */ - case USB_PORT_FEAT_ENABLE: - SET_RH_PORTSTAT(USBPORTSC_PE); - OK(0); default: goto err; } @@ -145,23 +173,25 @@ CLR_RH_PORTSTAT(USBPORTSC_PE); OK(0); case USB_PORT_FEAT_C_ENABLE: - SET_RH_PORTSTAT(USBPORTSC_PEC); + CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: CLR_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_C_SUSPEND: - /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + /* this driver won't report these */ OK(0); case USB_PORT_FEAT_POWER: - OK(0); /* port power */ + /* UHCI has no power switching */ + goto err; case USB_PORT_FEAT_C_CONNECTION: - SET_RH_PORTSTAT(USBPORTSC_CSC); + CLR_RH_PORTSTAT(USBPORTSC_CSC); OK(0); case USB_PORT_FEAT_C_OVER_CURRENT: + CLR_RH_PORTSTAT(USBPORTSC_OCC); OK(0); /* port power over current */ case USB_PORT_FEAT_C_RESET: - c_p_r[wIndex - 1] = 0; + /* this driver won't report these */ OK(0); default: goto err;