This is the 2.3.99 pre4-2 version of the rest of a patch I sent
by a few weeks ago (then marked as "experimental").
It reduces use of magic numbers when accessing OHCI registers,
and provides basic facilities to interpret their fields using
debug-only printing routines.
- Dave
--- linux/drivers/usb.1/usb-ohci.c Fri Mar 31 11:15:35 2000
+++ linux/drivers/usb/usb-ohci.c Sat Apr 1 12:20:08 2000
@@ -49,7 +49,7 @@
#include <asm/system.h>
#include <asm/unaligned.h>
-#define OHCI_USE_NPS
+#define OHCI_USE_NPS // force NoPowerSwitching mode
#include "usb-ohci.h"
@@ -61,6 +61,12 @@
#include <linux/pmu.h>
#endif
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+ (OHCI_CTRL_CBSR & 0x3) \
+ | OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
static DECLARE_WAIT_QUEUE_HEAD (op_wakeup);
static LIST_HEAD (ohci_hcd_list);
static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
@@ -145,14 +151,16 @@
}
}
-/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso
+eds*/
void ep_print_int_eds (ohci_t * ohci, char * str) {
int i, j;
__u32 * ed_p;
for (i= 0; i < 32; i++) {
j = 5;
- printk (KERN_DEBUG __FILE__ " %s branch int %2d(%2x):", str, i, i);
ed_p = &(ohci->hcca.int_table [i]);
+ if (*ed_p == 0)
+ continue;
+ printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
while (*ed_p != 0 && j--) {
ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p));
printk (" ed: %4x;", ed->hwINFO);
@@ -161,7 +169,161 @@
printk ("\n");
}
}
-
+
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+ if (value)
+ dbg ("%s %08x", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+// dump control and status registers
+static void ohci_dump_status (ohci_t *controller)
+{
+ struct ohci_regs *regs = controller->regs;
+ __u32 temp;
+
+ temp = readl (®s->revision) & 0xff;
+ if (temp != 0x10)
+ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+ temp = readl (®s->control);
+ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl (®s->cmdstatus);
+ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus));
+ ohci_dump_intr_mask ("intrenable", readl (®s->intrenable));
+ // intrdisable always same as intrenable
+ // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable));
+
+ maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent));
+
+ maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead));
+ maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent));
+
+ maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead));
+ maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent));
+
+ maybe_print_eds ("donehead", readl (®s->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+ struct ohci_regs *regs = controller->regs;
+ __u32 temp, ndp, i;
+
+ temp = readl (®s->roothub.a);
+ ndp = (temp & RH_A_NDP);
+
+ if (verbose) {
+ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = readl (®s->roothub.b);
+ dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = readl (®s->roothub.status);
+ dbg ("roothub.status: %08x%s%s%s%s%s%s",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = readl (®s->roothub.portstatus [i]);
+ dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+ i,
+ temp,
+ (temp & RH_PS_PRSC) ? " PRSC" : "",
+ (temp & RH_PS_OCIC) ? " OCIC" : "",
+ (temp & RH_PS_PSSC) ? " PSSC" : "",
+ (temp & RH_PS_PESC) ? " PESC" : "",
+ (temp & RH_PS_CSC) ? " CSC" : "",
+
+ (temp & RH_PS_LSDA) ? " LSDA" : "",
+ (temp & RH_PS_PPS) ? " PPS" : "",
+ (temp & RH_PS_PRS) ? " PRS" : "",
+ (temp & RH_PS_POCI) ? " POCI" : "",
+ (temp & RH_PS_PSS) ? " PSS" : "",
+
+ (temp & RH_PS_PES) ? " PES" : "",
+ (temp & RH_PS_CCS) ? " CCS" : ""
+ );
+ }
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+ dbg ("OHCI controller %p state", controller->regs);
+
+ // dumps some of the state we know about
+ ohci_dump_status (controller);
+ if (verbose)
+ ep_print_int_eds (controller, "hcca");
+ dbg ("hcca frame #%04x", controller->hcca.frame_no);
+ ohci_dump_roothub (controller, 1);
+}
+
#endif
@@ -365,12 +527,12 @@
if (!urb) /* just to be sure */
return -EINVAL;
+ ohci = (ohci_t *) urb->dev->bus->hcpriv;
+
#ifdef DEBUG
urb_print (urb, "UNLINK", 1);
#endif
- ohci = (ohci_t *) urb->dev->bus->hcpriv;
-
if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
return rh_unlink_urb (urb); /* a request to the virtual root hub */
@@ -787,10 +949,10 @@
switch (ed->type) {
case CTRL: /* stop CTRL list */
- writel (ohci->hc_control &= ~(0x01 << 4),
&ohci->regs->control);
+ writel (ohci->hc_control &= ~OHCI_CTRL_CLE,
+&ohci->regs->control);
break;
case BULK: /* stop BULK list */
- writel (ohci->hc_control &= ~(0x01 << 5),
&ohci->regs->control);
+ writel (ohci->hc_control &= ~OHCI_CTRL_BLE,
+&ohci->regs->control);
break;
}
}
@@ -1012,8 +1174,10 @@
if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
if (bulk) writel (0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
- if (!ohci->ed_rm_list[!frame]) /* start CTRL u. BULK list */
- writel (ohci->hc_control |= (0x03<<4), &ohci->regs->control);
+ if (!ohci->ed_rm_list[!frame]) { /* enable CTRL and BULK lists
+*/
+ ohci->hc_control |= OHCI_CTRL_CLE | OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
ohci->ed_rm_list[frame] = NULL;
spin_unlock_irqrestore (&usb_ed_lock, flags);
@@ -1112,6 +1276,7 @@
* Virtual Root Hub
*-------------------------------------------------------------------------*/
+/* Device descriptor */
static __u8 root_hub_dev_des[] =
{
0x12, /* __u8 bLength; */
@@ -1170,23 +1335,8 @@
0xff /* __u8 ep_bInterval; 255 ms */
};
-/*
-For OHCI we need just the 2nd Byte, so we
-don't need this constant byte-array
+/* Hub class-specific descriptor is constructed dynamically */
-static __u8 root_hub_hub_des[] =
-{
- 0x00, * __u8 bLength; *
- 0x29, * __u8 bDescriptorType; Hub-descriptor *
- 0x02, * __u8 bNbrPorts; *
- 0x00, * __u16 wHubCharacteristics; *
- 0x00,
- 0x01, * __u8 bPwrOn2pwrGood; 2ms *
- 0x00, * __u8 bHubContrCurrent; 0 mA *
- 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** *
- 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** *
-};
-*/
/*-------------------------------------------------------------------------*/
@@ -1201,13 +1351,16 @@
__u8 data[8];
- num_ports = readl (&ohci->regs->roothub.a) & 0xff;
- *(__u8 *) data = (readl (&ohci->regs->roothub.status) & 0x00030000) > 0? 1: 0;
+ num_ports = readl (&ohci->regs->roothub.a) & RH_A_NDP;
+ *(__u8 *) data = (readl (&ohci->regs->roothub.status) & (RH_HS_LPSC |
+RH_HS_OCIC))
+ ? 1: 0;
ret = *(__u8 *) data;
for ( i = 0; i < num_ports; i++) {
*(__u8 *) (data + (i + 1) / 8) |=
- ((readl (&ohci->regs->roothub.portstatus[i]) & 0x001f0000) >
0? 1: 0) << ((i + 1) % 8);
+ ((readl (&ohci->regs->roothub.portstatus[i]) &
+ (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+RH_PS_PRSC))
+ ? 1: 0) << ((i + 1) % 8);
ret += *(__u8 *) (data + (i + 1) / 8);
}
len = i/8 + 1;
@@ -1221,7 +1374,7 @@
/*-------------------------------------------------------------------------*/
-/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
static void rh_int_timer_do (unsigned long ptr)
{
@@ -1267,10 +1420,10 @@
/*-------------------------------------------------------------------------*/
-#define OK(x) len = (x); break
+#define OK(x) len = (x); break
#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status)
#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
-#define RD_RH_STAT readl(&ohci->regs->roothub.status)
+#define RD_RH_STAT readl(&ohci->regs->roothub.status)
#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1])
/* request to virtual root hub */
@@ -1309,6 +1462,10 @@
wValue = le16_to_cpu (cmd->value);
wIndex = le16_to_cpu (cmd->index);
wLength = le16_to_cpu (cmd->length);
+
+ dbg ("rh_submit_urb, req = %d(%x) len=%d", bmRType_bReq,
+ bmRType_bReq, wLength);
+
switch (bmRType_bReq) {
/* Request Destination:
without flags: Device,
@@ -1325,7 +1482,9 @@
case RH_GET_STATUS | RH_ENDPOINT:
*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
case RH_GET_STATUS | RH_CLASS:
- *(__u32 *) data_buf = cpu_to_le32 (RD_RH_STAT &
0x7fff7fff); OK (4);
+ *(__u32 *) data_buf = cpu_to_le32 (
+ RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+ OK (4);
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK
(4);
@@ -1370,11 +1529,15 @@
case (RH_PORT_SUSPEND):
WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
- if((RD_RH_PORTSTAT &1) != 0)
WR_RH_PORTSTAT (RH_PS_PRS ); OK (0);
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PRS);
+ OK (0);
case (RH_PORT_POWER):
WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
- if((RD_RH_PORTSTAT &1) != 0)
WR_RH_PORTSTAT (RH_PS_PES ); OK (0);
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PES );
+ OK (0);
}
break;
@@ -1441,8 +1604,9 @@
status = TD_CC_STALL;
}
- dbg("USB HC roothubstat1: %x", readl ( &(ohci->regs->roothub.portstatus[0]) ));
- dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
+#ifdef DEBUG
+ ohci_dump_roothub (ohci, 0);
+#endif
len = min(len, leni);
if (data != data_buf)
@@ -1480,10 +1644,10 @@
int timeout = 30;
int smm_timeout = 50; /* 0,5 sec */
- if (readl (&ohci->regs->control) & 0x100) { /* SMM owns the HC */
- writel (0x08, &ohci->regs->cmdstatus); /* request ownership */
+ if (readl (&ohci->regs->control) & OHCI_CTRL_RWC) { /* SMM owns the HC */
+ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
dbg("USB HC TakeOver from SMM");
- while (readl (&ohci->regs->control) & 0x100) {
+ while (readl (&ohci->regs->control) & OHCI_CTRL_RWC) {
wait_ms (10);
if (--smm_timeout == 0) {
err("USB HC TakeOver failed!");
@@ -1492,13 +1656,17 @@
}
}
- writel ((1 << 31), &ohci->regs->intrdisable); /* Disable HC interrupts */
+ /* Disable HC interrupts */
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control));
- /* this seems to be needed for the lucent controller on powerbooks.. */
- writel (0, &ohci->regs->control); /* Move USB to reset state */
+
+ /* Reset USB (needed by some controllers) */
+ writel (0, &ohci->regs->control);
- writel (1, &ohci->regs->cmdstatus); /* HC Reset */
- while ((readl (&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */
+ /* HC Reset requires max 10 ms delay */
+ writel (OHCI_HCR, &ohci->regs->cmdstatus);
+ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--timeout == 0) {
err("USB HC reset timed out!");
return -1;
@@ -1539,14 +1707,18 @@
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
- writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
+ /* start controller operations */
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ writel (ohci->hc_control, &ohci->regs->control);
+
writel (mask, &ohci->regs->intrenable);
writel (mask, &ohci->regs->intrstatus);
#ifdef OHCI_USE_NPS
- writel ((readl(&ohci->regs->roothub.a) | 0x200) & ~0x100,
+ writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM,
&ohci->regs->roothub.a);
- writel (0x10000, &ohci->regs->roothub.status);
+ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+ // POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);
#endif /* OHCI_USE_NPS */
@@ -1584,10 +1756,11 @@
}
dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
-
+
if (ints & OHCI_INTR_UE) {
ohci->disabled++;
err ("OHCI Unrecoverable Error, controller disabled");
+ // e.g. due to PCI Master/Target Abort
}
if (ints & OHCI_INTR_WDH) {
@@ -1729,6 +1902,10 @@
if (pmdev)
pmdev->data = ohci;
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif
+
return 0;
}
err("request interrupt %d failed", irq);
@@ -1792,7 +1969,8 @@
case PBOOK_WAKE:
writel (ohci->hc_control = OHCI_USB_RESUME,
&ohci->regs->control);
wait_ms (20);
- writel (ohci->hc_control = 0xBF, &ohci->regs->control);
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ writel (ohci->hc_control, &ohci->regs->control);
enable_irq (ohci->irq);
break;
}
@@ -1812,16 +1990,30 @@
ohci_t * ohci = (ohci_t*) dev->data;
if (ohci) {
switch (rqst) {
+ /*
+ * We have to disable the OHCI irq. This sucks because we'd rather
+ * not disable a potentially shared IRQ since the other user might
+ * want an IRQ event during suspend. Unfortunately at least some
+ * machines come back with the IRQ floating and if we don't do
+ * this we are dead, defunct, pushing up the silicon daisies.
+ */
case PM_SUSPEND:
dbg("USB-Bus suspend: %p", ohci);
- writel (ohci->hc_control = 0xFF, &ohci->regs->control);
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_SUSPEND;
+ writel (ohci->hc_control, &ohci->regs->control);
wait_ms (10);
+ if(ohci->irq > 0)
+ disable_irq(ohci->irq);
break;
case PM_RESUME:
dbg("USB-Bus resume: %p", ohci);
- writel (ohci->hc_control = 0x7F, &ohci->regs->control);
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_RESUME;
+ writel (ohci->hc_control, &ohci->regs->control);
wait_ms (20);
- writel (ohci->hc_control = 0xBF, &ohci->regs->control);
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ writel (ohci->hc_control, &ohci->regs->control);
+ if(ohci->irq > 0)
+ enable_irq(ohci->irq);
break;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]