patch-2.4.4 linux/drivers/usb/usb-ohci.c

Next file: linux/drivers/usb/usb-ohci.h
Previous file: linux/drivers/usb/uhci.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
@@ -12,11 +12,15 @@
  * 
  * History:
  * 
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ 	pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
  * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
  * 2000/09/26 fixed races in removing the private portion of the urb
  * 2000/09/07 disable bulk and control lists when unlinking the last
  *	endpoint descriptor in order to avoid unrecoverable errors on
- *	the Lucent chips.
+ *	the Lucent chips. (rwc@sgi)
  * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
  *	urb unlink probs, indentation fixes
  * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
@@ -65,8 +69,6 @@
 
 #define OHCI_USE_NPS		// force NoPowerSwitching mode
 // #define OHCI_VERBOSE_DEBUG	/* not always helpful */
-// #define OHCI_MEM_SLAB
-// #define OHCI_MEM_FLAGS	SLAB_POISON	/* no redzones; see mm/slab.c */
 
 #include "usb-ohci.h"
 
@@ -92,6 +94,30 @@
 static LIST_HEAD (ohci_hcd_list);
 static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
 
+
+/*-------------------------------------------------------------------------*/
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect.  AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+	u32 temp = readl (&hc->regs->roothub.register); \
+	if (hc->flags & OHCI_QUIRK_AMD756) \
+		while (temp & mask) \
+			temp = readl (&hc->regs->roothub.register); \
+	temp; })
+
+static u32 roothub_a (struct ohci *hc)
+	{ return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci *hc, int i)
+	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+
 /*-------------------------------------------------------------------------*
  * URB support functions 
  *-------------------------------------------------------------------------*/ 
@@ -100,10 +126,29 @@
 
 static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
 {
-	int i;
-
-	for (i = 0; i < urb_priv->length; i++) {
-		if (urb_priv->td [i]) {
+	int		i;
+	int		last = urb_priv->length - 1;
+	struct td	*td;
+
+	for (i = 0; i <= last; i++) {
+		td = urb_priv->td [i];
+		if (td) {
+			int		len;
+			int		dir;
+
+			if ((td->hwINFO & cpu_to_le32 (TD_DP)) == TD_DP_SETUP) {
+				len = 8;
+				dir = PCI_DMA_TODEVICE;
+			} else if (i == last) {
+				len = td->urb->transfer_buffer_length,
+				dir = usb_pipeout (td->urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE;
+			} else
+				len = dir = 0;
+			if (len && td->data_dma)
+				pci_unmap_single (hc->ohci_dev,
+					td->data_dma, len, dir);
 			td_free (hc, urb_priv->td [i]);
 		}
 	}
@@ -118,6 +163,13 @@
 	if (urb_priv) {
 		urb->hcpriv = NULL;
 
+#ifdef	DO_TIMEOUTS
+		if (urb->timeout) {
+			list_del (&urb->urb_list);
+			urb->timeout -= jiffies;
+		}
+#endif
+
 		/* Release int/iso bandwidth */
 		if (urb->bandwidth) {
 			switch (usb_pipetype(urb->pipe)) {
@@ -132,7 +184,7 @@
 			}
 		}
 
-		urb_free_priv ((struct ohci *)urb->dev->bus, urb_priv);
+		urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
 		usb_dec_dev_use (urb->dev);
 		urb->dev = NULL;
 	}
@@ -214,7 +266,7 @@
 		    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));
+			ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
 			printk (" ed: %4x;", ed->hwINFO);
 			ed_p = &ed->hwNextED;
 		}
@@ -307,10 +359,9 @@
 
 static void ohci_dump_roothub (ohci_t *controller, int verbose)
 {
-	struct ohci_regs	*regs = controller->regs;
 	__u32			temp, ndp, i;
 
-	temp = readl (&regs->roothub.a);
+	temp = roothub_a (controller);
 	ndp = (temp & RH_A_NDP);
 
 	if (verbose) {
@@ -323,13 +374,13 @@
 			(temp & RH_A_PSM) ? " PSM" : "",
 			ndp
 			);
-		temp = readl (&regs->roothub.b);
+		temp = roothub_b (controller);
 		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
 			temp,
 			(temp & RH_B_PPCM) >> 16,
 			(temp & RH_B_DR)
 			);
-		temp = readl (&regs->roothub.status);
+		temp = roothub_status (controller);
 		dbg ("roothub.status: %08x%s%s%s%s%s%s",
 			temp,
 			(temp & RH_HS_CRWE) ? " CRWE" : "",
@@ -342,7 +393,7 @@
 	}
 	
 	for (i = 0; i < ndp; i++) {
-		temp = readl (&regs->roothub.portstatus [i]);
+		temp = roothub_portstatus (controller, i);
 		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
 			i,
 			temp,
@@ -385,7 +436,7 @@
 
 /* return a request to the completion handler */
  
-static int sohci_return_urb (urb_t * urb)
+static int sohci_return_urb (struct ohci *hc, urb_t * urb)
 {
 	urb_priv_t * urb_priv = urb->hcpriv;
 	urb_t * urbt;
@@ -407,7 +458,15 @@
 
 	switch (usb_pipetype (urb->pipe)) {
   		case PIPE_INTERRUPT:
-			urb->complete (urb); /* call complete and requeue URB */	
+			pci_unmap_single (hc->ohci_dev,
+				urb_priv->td [0]->data_dma,
+				urb->transfer_buffer_length,
+				usb_pipeout (urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE);
+			urb->complete (urb);
+
+			/* implicitly requeued */
   			urb->actual_length = 0;
   			urb->status = USB_ST_URB_PENDING;
   			if (urb_priv->state != URB_DEL)
@@ -417,8 +476,13 @@
 		case PIPE_ISOCHRONOUS:
 			for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
 			if (urbt) { /* send the reply and requeue URB */	
+				pci_unmap_single (hc->ohci_dev,
+					urb_priv->td [0]->data_dma,
+					urb->transfer_buffer_length,
+					usb_pipeout (urb->pipe)
+						? PCI_DMA_TODEVICE
+						: PCI_DMA_FROMDEVICE);
 				urb->complete (urb);
-				
 				spin_lock_irqsave (&usb_ed_lock, flags);
 				urb->actual_length = 0;
   				urb->status = USB_ST_URB_PENDING;
@@ -460,6 +524,7 @@
 	int i, size = 0;
 	unsigned long flags;
 	int bustime = 0;
+	int mem_flags = ALLOC_FLAGS;
 	
 	if (!urb->dev || !urb->dev->bus)
 		return -ENODEV;
@@ -489,7 +554,7 @@
 	}
 
 	/* every endpoint has a ed, locate and fill it */
-	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
+	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
 		usb_dec_dev_use (urb->dev);	
 		return -ENOMEM;
 	}
@@ -534,8 +599,9 @@
 
 	/* allocate the TDs */
 	for (i = 0; i < size; i++) { 
-		urb_priv->td[i] = td_alloc (ohci);
+		urb_priv->td[i] = td_alloc (ohci, mem_flags);
 		if (!urb_priv->td[i]) {
+			urb_priv->length = i;
 			urb_free_priv (ohci, urb_priv);
 			usb_dec_dev_use (urb->dev);	
 			return -ENOMEM;
@@ -569,6 +635,9 @@
 				return bustime;
 			}
 			usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
+#ifdef	DO_TIMEOUTS
+			urb->timeout = 0;
+#endif
 	}
 
 	spin_lock_irqsave (&usb_ed_lock, flags);
@@ -582,6 +651,31 @@
 
 	/* fill the TDs and link it to the ed */
 	td_submit_urb (urb);
+
+#ifdef	DO_TIMEOUTS
+	/* maybe add to ordered list of timeouts */
+	if (urb->timeout) {
+		struct list_head	*entry;
+
+		// FIXME:  usb-uhci uses relative timeouts (like this),
+		// while uhci uses absolute ones (probably better).
+		// Pick one solution and change the affected drivers.
+		urb->timeout += jiffies;
+
+		list_for_each (entry, &ohci->timeout_list) {
+			struct urb	*next_urb;
+
+			next_urb = list_entry (entry, struct urb, urb_list);
+			if (time_after_eq (urb->timeout, next_urb->timeout))
+				break;
+		}
+		list_add (&urb->urb_list, entry);
+
+		/* drive timeouts by SF (messy, but works) */
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);	
+	}
+#endif
+
 	spin_unlock_irqrestore (&usb_ed_lock, flags);
 
 	return 0;	
@@ -687,18 +781,11 @@
 {
 	struct ohci_device * dev;
 
-	/* FIXME:  ED allocation with pci_consistent memory
-	 * must know the controller ... either pass it in here,
-	 * or decouple ED allocation from dev allocation.
-	 */
-	dev = dev_alloc (NULL);
+	dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
 	if (!dev)
 		return -ENOMEM;
-		
-	memset (dev, 0, sizeof (*dev));
 
 	usb_dev->hcpriv = dev;
-
 	return 0;
 }
 
@@ -783,7 +870,7 @@
 	}
 
 	/* free device, and associated EDs */
-	dev_free (dev);
+	dev_free (ohci, dev);
 
 	return 0;
 }
@@ -877,9 +964,9 @@
 	case PIPE_CONTROL:
 		ed->hwNextED = 0;
 		if (ohci->ed_controltail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+			writel (ed->dma, &ohci->regs->ed_controlhead);
 		} else {
-			ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_controltail;
 		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
@@ -893,9 +980,9 @@
 	case PIPE_BULK:
 		ed->hwNextED = 0;
 		if (ohci->ed_bulktail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+			writel (ed->dma, &ohci->regs->ed_bulkhead);
 		} else {
-			ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_bulktail;
 		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
@@ -916,11 +1003,11 @@
 		for (i = 0; i < ep_rev (6, interval); i += inter) {
 			inter = 1;
 			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); 
-				(*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval); 
-				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
-					inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
+				(*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
+				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
 			ed->hwNextED = *ed_p; 
-			*ed_p = cpu_to_le32 (virt_to_bus (ed));
+			*ed_p = cpu_to_le32 (ed->dma);
 		}
 #ifdef DEBUG
 		ep_print_int_eds (ohci, "LINK_INT");
@@ -931,16 +1018,16 @@
 		ed->hwNextED = 0;
 		ed->int_interval = 1;
 		if (ohci->ed_isotail != NULL) {
-			ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
 			ed->ed_prev = ohci->ed_isotail;
 		} else {
 			for ( i = 0; i < 32; i += inter) {
 				inter = 1;
 				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
 					*ed_p != 0; 
-					ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
-						inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
-				*ed_p = cpu_to_le32 (virt_to_bus (ed));	
+					ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+				*ed_p = cpu_to_le32 (ed->dma);	
 			}	
 			ed->ed_prev = NULL;
 		}	
@@ -984,7 +1071,7 @@
 		if (ohci->ed_controltail == ed) {
 			ohci->ed_controltail = ed->ed_prev;
 		} else {
-			((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 		}
 		break;
       
@@ -1001,7 +1088,7 @@
 		if (ohci->ed_bulktail == ed) {
 			ohci->ed_bulktail = ed->ed_prev;
 		} else {
-			((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 		}
 		break;
       
@@ -1012,9 +1099,9 @@
 		for (i = 0; i < ep_rev (6, interval); i += inter) {
 			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]), inter = 1; 
 				(*ed_p != 0) && (*ed_p != ed->hwNextED); 
-				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED), 
-				inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) {				
-					if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), 
+				inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {				
+					if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
 			  			*ed_p = ed->hwNextED;		
 			  			break;
 			  		}
@@ -1031,7 +1118,7 @@
 		if (ohci->ed_isotail == ed)
 			ohci->ed_isotail = ed->ed_prev;
 		if (ed->hwNextED != 0) 
-		    ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		    (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 				    
 		if (ed->ed_prev != NULL) {
 			ed->ed_prev->hwNextED = ed->hwNextED;
@@ -1039,9 +1126,9 @@
 			for (i = 0; i < 32; i++) {
 				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
 						*ed_p != 0; 
-						ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) {
-					// inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
-					if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {
+						ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
+					// inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+					if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
 						*ed_p = ed->hwNextED;		
 						break;
 					}
@@ -1066,7 +1153,13 @@
  * in all other cases the state is left unchanged
  * the ed info fields are setted anyway even though most of them should not change */
  
-static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load)
+static ed_t * ep_add_ed (
+	struct usb_device * usb_dev,
+	unsigned int pipe,
+	int interval,
+	int load,
+	int mem_flags
+)
 {
    	ohci_t * ohci = usb_dev->bus->hcpriv;
 	td_t * td;
@@ -1089,13 +1182,16 @@
 	if (ed->state == ED_NEW) {
 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
   		/* dummy td; end of td list for ed */
-		td = td_alloc (ohci);
-  		if (!td) {
+		td = td_alloc (ohci, mem_flags);
+		/* hash the ed for later reverse mapping */
+ 		if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
 			/* out of memory */
+		        if (td)
+		            td_free(ohci, td);
 			spin_unlock_irqrestore (&usb_ed_lock, flags);
 			return NULL;
 		}
-		ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+		ed->hwTailP = cpu_to_le32 (td->td_dma);
 		ed->hwHeadP = ed->hwTailP;	
 		ed->state = ED_UNLINK;
 		ed->type = usb_pipetype (pipe);
@@ -1164,43 +1260,52 @@
  * TD handling functions
  *-------------------------------------------------------------------------*/
 
-/* prepare a TD */
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
 
-static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index)
+static void
+td_fill (ohci_t * ohci, unsigned int info,
+	dma_addr_t data, int len,
+	urb_t * urb, int index)
 {
 	volatile td_t  * td, * td_pt;
 	urb_priv_t * urb_priv = urb->hcpriv;
-
+	
 	if (index >= urb_priv->length) {
 		err("internal OHCI error: TD index > length");
 		return;
 	}
 	
+	/* use this td as the next dummy */
 	td_pt = urb_priv->td [index];
+	td_pt->hwNextTD = 0;
+
 	/* fill the old dummy TD */
-	td = urb_priv->td [index] = (td_t *)
-		bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
+	td = urb_priv->td [index] = dma_to_td (ohci,
+			le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
+
 	td->ed = urb_priv->ed;
 	td->next_dl_td = NULL;
 	td->index = index;
 	td->urb = urb; 
+	td->data_dma = data;
+	if (!len)
+		data = 0;
+
 	td->hwINFO = cpu_to_le32 (info);
 	if ((td->ed->type) == PIPE_ISOCHRONOUS) {
-		td->hwCBP = cpu_to_le32 (((!data || !len)
-			? 0
-			: virt_to_bus (data)) & 0xFFFFF000);
+		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
 		td->ed->last_iso = info & 0xffff;
 	} else {
-		td->hwCBP = cpu_to_le32 (((!data || !len)
-			? 0
-			: virt_to_bus (data))); 
+		td->hwCBP = cpu_to_le32 (data); 
 	}			
-	td->hwBE = cpu_to_le32 ((!data || !len )
-		? 0
-		: virt_to_bus (data + len - 1));
-	td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
-	td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
-	td_pt->hwNextTD = 0;
+	if (data)
+		td->hwBE = cpu_to_le32 (data + len - 1);
+	else
+		td->hwBE = 0;
+	td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+	td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+
+	/* append to queue */
 	td->ed->hwTailP = td->hwNextTD;
 }
 
@@ -1212,8 +1317,7 @@
 { 
 	urb_priv_t * urb_priv = urb->hcpriv;
 	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
-	void * ctrl = urb->setup_packet;
-	void * data = urb->transfer_buffer;
+	dma_addr_t data;
 	int data_len = urb->transfer_buffer_length;
 	int cnt = 0; 
 	__u32 info = 0;
@@ -1228,18 +1332,28 @@
 	}
 	
 	urb_priv->td_cnt = 0;
+
+	if (data_len) {
+		data = pci_map_single (ohci->ohci_dev,
+			urb->transfer_buffer, data_len,
+			usb_pipeout (urb->pipe)
+				? PCI_DMA_TODEVICE
+				: PCI_DMA_FROMDEVICE
+			);
+	} else
+		data = 0;
 	
 	switch (usb_pipetype (urb->pipe)) {
 		case PIPE_BULK:
 			info = usb_pipeout (urb->pipe)? 
 				TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
 			while(data_len > 4096) {		
-				td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
+				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
 				data += 4096; data_len -= 4096; cnt++;
 			}
 			info = usb_pipeout (urb->pipe)?
 				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
-			td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
+			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
 			cnt++;
 			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
 			break;
@@ -1247,27 +1361,32 @@
 		case PIPE_INTERRUPT:
 			info = usb_pipeout (urb->pipe)? 
 				TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
-			td_fill (info, data, data_len, urb, cnt++);
+			td_fill (ohci, info, data, data_len, urb, cnt++);
 			break;
 
 		case PIPE_CONTROL:
 			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
-			td_fill (info, ctrl, 8, urb, cnt++); 
+			td_fill (ohci, info,
+				pci_map_single (ohci->ohci_dev,
+					urb->setup_packet, 8,
+					PCI_DMA_TODEVICE),
+				8, urb, cnt++); 
 			if (data_len > 0) {  
 				info = usb_pipeout (urb->pipe)? 
 					TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
-				td_fill (info, data, data_len, urb, cnt++);  
+				/* NOTE:  mishandles transfers >8K, some >4K */
+				td_fill (ohci, info, data, data_len, urb, cnt++);  
 			} 
 			info = usb_pipeout (urb->pipe)? 
  				TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
-			td_fill (info, NULL, 0, urb, cnt++);
+			td_fill (ohci, info, data, 0, urb, cnt++);
 			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
 			break;
 
 		case PIPE_ISOCHRONOUS:
 			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
-				td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), 
- 					(__u8 *) data + urb->iso_frame_desc[cnt].offset, 
+				td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), 
+					data + urb->iso_frame_desc[cnt].offset, 
 					urb->iso_frame_desc[cnt].length, urb, cnt); 
 			}
 			break;
@@ -1318,9 +1437,9 @@
 				((td->index == 0) || (td->index == urb_priv->length - 1)))) {
  			if (tdBE != 0) {
  				if (td->hwCBP == 0)
-					urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1;
+					urb->actual_length += tdBE - td->data_dma + 1;
   				else
-					urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer;
+					urb->actual_length += tdCBP - td->data_dma;
 			}
   		}
   	}
@@ -1366,7 +1485,7 @@
 	ohci->hcca->done_head = 0;
 	
 	while (td_list_hc) {		
-		td_list = (td_t *) bus_to_virt (td_list_hc);
+		td_list = dma_to_td (ohci, td_list_hc);
 
 		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
 			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
@@ -1411,8 +1530,8 @@
 
 	for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
 
-   	 	tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
-		tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+		tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+		tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
 		edINFO = le32_to_cpup (&ed->hwINFO);
 		td_p = &ed->hwHeadP;
 
@@ -1420,7 +1539,7 @@
 			urb_t * urb = td->urb;
 			urb_priv_t * urb_priv = td->urb->hcpriv;
 			
-			td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+			td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
 			if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
 				tdINFO = le32_to_cpup (&td->hwINFO);
 				if (TD_CC_GET (tdINFO) < 0xE)
@@ -1439,7 +1558,8 @@
 			struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
 			td_free (ohci, tdTailP); /* free dummy td */
    	 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
-   	 		ed->state = ED_NEW; 
+			ed->state = ED_NEW;
+			hash_free_ed(ohci, ed);
    	 		/* if all eds are removed wake up sohci_free_dev */
    	 		if (!--dev->ed_cnt) {
 				wait_queue_head_t *wait_head = dev->wait;
@@ -1450,7 +1570,7 @@
 			}
    	 	} else {
    	 		ed->state &= ~ED_URB_DEL;
-			tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+			tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
 
 			if (tdHeadP == tdTailP) {
 				if (ed->state == ED_OPER)
@@ -1458,6 +1578,7 @@
 				td_free (ohci, tdTailP);
 				ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
 				ed->state = ED_NEW;
+				hash_free_ed(ohci, ed);
 				--(usb_to_ohci (ohci->dev[edINFO & 0x7F]))->ed_cnt;
 			} else
    	 			ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
@@ -1535,7 +1656,7 @@
 			if ((ed->state & (ED_OPER | ED_UNLINK))
 					&& (urb_priv->state != URB_DEL)) {
   				urb->status = cc_to_error[cc];
-  				sohci_return_urb (urb);
+  				sohci_return_urb (ohci, urb);
   			} else {
 				spin_lock_irqsave (&usb_ed_lock, flags);
   				dl_del_urb (urb);
@@ -1640,7 +1761,7 @@
 
 	__u8 data[8];
 
-	num_ports = readl (&ohci->regs->roothub.a) & RH_A_NDP; 
+	num_ports = roothub_a (ohci) & RH_A_NDP; 
 	if (num_ports > MAX_ROOT_PORTS) {
 		err ("bogus NDP=%d for OHCI usb-%s", num_ports,
 			ohci->ohci_dev->slot_name);
@@ -1649,13 +1770,13 @@
 		/* retry later; "should not happen" */
 		return 0;
 	}
-	*(__u8 *) data = (readl (&ohci->regs->roothub.status) & (RH_HS_LPSC | RH_HS_OCIC))
+	*(__u8 *) data = (roothub_status (ohci) & (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]) &
+			((roothub_portstatus (ohci, 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);
@@ -1726,8 +1847,8 @@
 #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_PORTSTAT		readl(&ohci->regs->roothub.portstatus[wIndex-1])
+#define RD_RH_STAT		roothub_status(ohci)
+#define RD_RH_PORTSTAT		roothub_portstatus(ohci,wIndex-1)
 
 /* request to virtual root hub */
 
@@ -1765,9 +1886,6 @@
 	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, 
@@ -1869,7 +1987,7 @@
 		
 		case RH_GET_DESCRIPTOR | RH_CLASS:
 		    {
-			    __u32 temp = readl (&ohci->regs->roothub.a);
+			    __u32 temp = roothub_a (ohci);
 
 			    data_buf [0] = 9;		// min length;
 			    data_buf [1] = 0x29;
@@ -1884,7 +2002,7 @@
 
 			    datab [1] = 0;
 			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
-			    temp = readl (&ohci->regs->roothub.b);
+			    temp = roothub_b (ohci);
 			    data_buf [7] = temp & RH_B_DR;
 			    if (data_buf [2] < 7) {
 				data_buf [8] = 0xff;
@@ -2038,13 +2156,14 @@
 	writel (mask, &ohci->regs->intrstatus);
 
 #ifdef	OHCI_USE_NPS
-	writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM,
+	/* required for AMD-756 and some Mac platforms */
+	writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
 		&ohci->regs->roothub.a);
 	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
 #endif	/* OHCI_USE_NPS */
 
 	// POTPGT delay is bits 24-31, in 2 ms units.
-	mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe);
+	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
  
 	/* connect the virtual root hub */
 	ohci->rh.devnum = 0;
@@ -2068,6 +2187,37 @@
 
 /*-------------------------------------------------------------------------*/
 
+/* called only from interrupt handler */
+
+static void check_timeouts (struct ohci *ohci)
+{
+	spin_lock (&usb_ed_lock);
+	while (!list_empty (&ohci->timeout_list)) {
+		struct urb	*urb;
+
+		urb = list_entry (ohci->timeout_list.next, struct urb, urb_list);
+		if (time_after (jiffies, urb->timeout))
+			break;
+
+		list_del_init (&urb->urb_list);
+		if (urb->status != -EINPROGRESS)
+			continue;
+
+		urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+		spin_unlock (&usb_ed_lock);
+
+		// outside the interrupt handler (in a timer...)
+		// this reference would race interrupts
+		sohci_unlink_urb (urb);
+
+		spin_lock (&usb_ed_lock);
+	}
+	spin_unlock (&usb_ed_lock);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
 /* an interrupt happens */
 
 static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
@@ -2120,10 +2270,18 @@
 		if (ohci->ed_rm_list[frame] != NULL)
 			writel (OHCI_INTR_SF, &regs->intrenable);	
 	}
+
+	if (!list_empty (&ohci->timeout_list)) {
+		check_timeouts (ohci);
+// FIXME:  enable SF as needed in a timer;
+// don't make lots of 1ms interrupts
+// On unloaded USB, think 4k ~= 4-5msec
+		if (!list_empty (&ohci->timeout_list))
+			writel (OHCI_INTR_SF, &regs->intrenable);	
+	}
+
 	writel (ints, &regs->intrstatus);
 	writel (OHCI_INTR_MIE, &regs->intrenable);	
-
-	/* FIXME:  check URB timeouts */
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2159,6 +2317,8 @@
 	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
 	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
 
+	INIT_LIST_HEAD (&ohci->timeout_list);
+
 	bus = usb_alloc_bus (&sohci_device_operations);
 	if (!bus) {
 		kfree (ohci);
@@ -2198,6 +2358,8 @@
 
 	list_del (&ohci->ohci_hcd_list);
 	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+
+	ohci_mem_cleanup (ohci);
     
 	/* unmap the IO address space */
 	iounmap (ohci->regs);
@@ -2215,11 +2377,13 @@
 static struct pci_driver ohci_pci_driver;
  
 static int __devinit
-hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base)
+hc_found_ohci (struct pci_dev *dev, int irq,
+	void *mem_base, const struct pci_device_id *id)
 {
 	ohci_t * ohci;
 	u8 latency, limit;
 	char buf[8], *bufp = buf;
+	int ret;
 
 #ifndef __sparc__
 	sprintf(buf, "%d", irq);
@@ -2234,6 +2398,13 @@
 	if (!ohci) {
 		return -ENOMEM;
 	}
+	if ((ret = ohci_mem_init (ohci)) < 0) {
+		hc_release_ohci (ohci);
+		return ret;
+	}
+	ohci->flags = id->driver_data;
+	if (ohci->flags & OHCI_QUIRK_AMD756)
+		printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
 
 	/* bad pci latencies can contribute to overruns */ 
 	pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
@@ -2331,13 +2502,6 @@
 	unsigned long mem_resource, mem_len;
 	void *mem_base;
 
-	/* blacklisted hardware? */
-	if (id->driver_data) {
-		info ("%s (%s): %s", dev->slot_name,
-			dev->name, (char *) id->driver_data);
-		return -ENODEV;
-	}
-
 	if (pci_enable_device(dev) < 0)
 		return -ENODEV;
 	
@@ -2358,7 +2522,7 @@
 	/* controller writes into our memory */
 	pci_set_master (dev);
 
-	return hc_found_ohci (dev, dev->irq, mem_base);
+	return hc_found_ohci (dev, dev->irq, mem_base, id);
 } 
 
 /*-------------------------------------------------------------------------*/
@@ -2505,15 +2669,14 @@
 
 	/*
 	 * AMD-756 [Viper] USB has a serious erratum when used with
-	 * lowspeed devices like mice; oopses have been seen.  The
-	 * vendor workaround needs an NDA ... for now, blacklist it.
+	 * lowspeed devices like mice.
 	 */
 	vendor:		0x1022,
 	device:		0x740c,
 	subvendor:	PCI_ANY_ID,
 	subdevice:	PCI_ANY_ID,
 
-	driver_data:	(unsigned long) "blacklisted, erratum #4",
+	driver_data:	OHCI_QUIRK_AMD756,
 
 } , {
 
@@ -2586,11 +2749,7 @@
 {
 	int ret;
 
-	if ((ret = ohci_mem_init ()) < 0)
-		return ret;
-
 	if ((ret = pci_module_init (&ohci_pci_driver)) < 0) {
-		ohci_mem_cleanup ();
 		return ret;
 	}
 
@@ -2608,7 +2767,6 @@
 	pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
 #endif  
 	pci_unregister_driver (&ohci_pci_driver);
-	ohci_mem_cleanup ();
 }
 
 module_init (ohci_hcd_init);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)