Commit 1345ad81 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[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().
parent 8b290385
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -43,11 +48,6 @@ ...@@ -43,11 +48,6 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/dmapool.h> #include <linux/dmapool.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/bitops.h> #include <asm/bitops.h>
...@@ -2276,7 +2276,7 @@ static int uhci_start(struct usb_hcd *hcd) ...@@ -2276,7 +2276,7 @@ static int uhci_start(struct usb_hcd *hcd)
/* This is experimental so anything less than 2 or greater than 8 is */ /* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */ /* 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"); info("port count misdetected? forcing to 2 ports");
port = 2; port = 2;
} }
......
...@@ -49,12 +49,19 @@ ...@@ -49,12 +49,19 @@
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ #define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */ #define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ #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_RD 0x0040 /* Resume Detect */
#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ #define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */ #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_OC 0x0400 /* Over Current condition */
#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */
#define USBPORTSC_SUSP 0x1000 /* Suspend */ #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 */ /* Legacy support register */
#define USBLEGSUP 0xc0 #define USBLEGSUP 0xc0
......
...@@ -16,14 +16,22 @@ static __u8 root_hub_hub_des[] = ...@@ -16,14 +16,22 @@ static __u8 root_hub_hub_des[] =
0x09, /* __u8 bLength; */ 0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */ 0x02, /* __u8 bNbrPorts; */
0x00, /* __u16 wHubCharacteristics; */ 0x0a, /* __u16 wHubCharacteristics; */
0x00, 0x00, /* (per-port OC, no power switching) */
0x01, /* __u8 bPwrOn2pwrGood; 2ms */ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */ 0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 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) static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
...@@ -32,7 +40,9 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) ...@@ -32,7 +40,9 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
*buf = 0; *buf = 0;
for (i = 0; i < uhci->rh_numports; i++) { 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; len = (i + 1) / 8 + 1;
} }
...@@ -43,12 +53,15 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) ...@@ -43,12 +53,15 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
#define CLR_RH_PORTSTAT(x) \ #define CLR_RH_PORTSTAT(x) \
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ 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)) outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
#define SET_RH_PORTSTAT(x) \ #define SET_RH_PORTSTAT(x) \
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ 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)) outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
...@@ -57,13 +70,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -57,13 +70,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength) u16 wIndex, char *buf, u16 wLength)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); 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; unsigned int io_addr = uhci->io_addr;
__u16 cstatus; u16 wPortChange, wPortStatus;
char c_p_r[8];
for (i = 0; i < 8; i++)
c_p_r[i] = 0;
switch (typeReq) { switch (typeReq) {
/* Request Destination: /* Request Destination:
...@@ -79,18 +88,39 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -79,18 +88,39 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
OK(4); /* hub power */ OK(4); /* hub power */
case GetPortStatus: case GetPortStatus:
status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
((status & USBPORTSC_PEC) >> (3 - 1)) | /* C_SUSPEND and C_RESET are always false */
(c_p_r[wIndex - 1] << (0 + 4)); wPortChange = 0;
status = (status & USBPORTSC_CCS) | if (status & USBPORTSC_CSC)
((status & USBPORTSC_PE) >> (2 - 1)) | wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
((status & USBPORTSC_SUSP) >> (12 - 2)) | if (status & USBPORTSC_PEC)
((status & USBPORTSC_PR) >> (9 - 4)) | wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
(1 << 8) | /* power on */ if (status & USBPORTSC_OCC)
((status & USBPORTSC_LSDA) << (-8 + 9)); wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
*(__u16 *)buf = cpu_to_le16(status); /* UHCI has no power switching (always on) */
*(__u16 *)(buf + 2) = cpu_to_le16(cstatus); 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); OK(4);
case SetHubFeature: case SetHubFeature:
switch (wValue) { switch (wValue) {
...@@ -104,6 +134,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -104,6 +134,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case ClearHubFeature: case ClearHubFeature:
switch (wValue) { switch (wValue) {
case C_HUB_OVER_CURRENT: case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
OK(0); /* hub power over current */ OK(0); /* hub power over current */
default: default:
goto err; goto err;
...@@ -120,18 +151,15 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -120,18 +151,15 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR); SET_RH_PORTSTAT(USBPORTSC_PR);
mdelay(50); /* USB v1.1 7.1.7.3 */ mdelay(50); /* USB v1.1 7.1.7.3 */
c_p_r[wIndex - 1] = 1;
CLR_RH_PORTSTAT(USBPORTSC_PR); CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10); udelay(10);
SET_RH_PORTSTAT(USBPORTSC_PE); SET_RH_PORTSTAT(USBPORTSC_PE);
mdelay(10); mdelay(10);
SET_RH_PORTSTAT(0xa); CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
OK(0); OK(0);
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
OK(0); /* port power ** */ OK(0); /* port power ** */
case USB_PORT_FEAT_ENABLE:
SET_RH_PORTSTAT(USBPORTSC_PE);
OK(0);
default: default:
goto err; goto err;
} }
...@@ -145,23 +173,25 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -145,23 +173,25 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
CLR_RH_PORTSTAT(USBPORTSC_PE); CLR_RH_PORTSTAT(USBPORTSC_PE);
OK(0); OK(0);
case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_ENABLE:
SET_RH_PORTSTAT(USBPORTSC_PEC); CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0); OK(0);
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
CLR_RH_PORTSTAT(USBPORTSC_SUSP); CLR_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0); OK(0);
case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_SUSPEND:
/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ /* this driver won't report these */
OK(0); OK(0);
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
OK(0); /* port power */ /* UHCI has no power switching */
goto err;
case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_CONNECTION:
SET_RH_PORTSTAT(USBPORTSC_CSC); CLR_RH_PORTSTAT(USBPORTSC_CSC);
OK(0); OK(0);
case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_OVER_CURRENT:
CLR_RH_PORTSTAT(USBPORTSC_OCC);
OK(0); /* port power over current */ OK(0); /* port power over current */
case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_RESET:
c_p_r[wIndex - 1] = 0; /* this driver won't report these */
OK(0); OK(0);
default: default:
goto err; goto err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment