Subject: Re: USB driver

From: James Hicks <[email-masked]>
Date: Fri, 16 Jun 2000 17:04:04 -0400

Here is a patch to usb-ohci.[ch] that fixes the cache coherence problem
using pci_alloc_consistent.  We have tested this on the Compaq Personal
Server (SA110/footbridge/Opti USB OHCI) with 2.4.0-test1-ac7-rmk1.

Thanks to my summer intern, Brandon Sanders, for testing the latest version
we have working.

We haven't tested it against x86 yet, but Brandon is working on it.

There still seems to be a problem when unplugging devices -- causing the
kernel to crash.  I assume this doesn't happen on x86.

-Jamey

Common subdirectories: linux-2.4.0-test1-ac7/drivers/usb/serial and linux-2.4.0-test1-ac7-crl1/drivers/usb/serial
diff -u linux-2.4.0-test1-ac7/drivers/usb/usb-ohci.c linux-2.4.0-test1-ac7-crl1/drivers/usb/usb-ohci.c
--- linux-2.4.0-test1-ac7/drivers/usb/usb-ohci.c	Fri Jun 16 13:29:47 2000
+++ linux-2.4.0-test1-ac7-crl1/drivers/usb/usb-ohci.c	Fri Jun 16 16:54:42 2000
@@ -70,6 +70,135 @@
 static DECLARE_WAIT_QUEUE_HEAD (op_wakeup); 
 static LIST_HEAD (ohci_hcd_list);
 static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t usb_td_lock = SPIN_LOCK_UNLOCKED;
+
+
+
+#ifdef CONFIG_ARCH_PERSONAL_SERVER
+/*-------------------------------------------------------------------------*
+ * Code to check addresses are in range
+ *-------------------------------------------------------------------------*/ 
+
+static int __verify_virt(volatile void *virt, int line)
+{
+  unsigned long addr = (unsigned long)virt;
+  int ok = ((addr & 0xF0000000) == 0xC0000000);
+  if (!ok)
+    printk(KERN_CRIT __FILE__ ":%d: Expected virtual address, got %p\n", line, virt);
+  return ok;
+}
+static int __verify_dma(dma_addr_t dma, int line)
+{
+  unsigned long addr = (unsigned long)dma;
+  int ok = ((addr & 0xF0000000) == 0xE0000000);
+  if (!ok)
+    printk(KERN_CRIT __FILE__ ":%d: Expected DMA address, got %#08x\n", line, dma);
+  return ok;
+}
+#define verify_virt(virt) __verify_virt((virt), __LINE__)
+#define verify_dma(dma) __verify_dma((dma), __LINE__)
+
+#else /* !CONFIG_ARCH_PERSONAL_SERVER, address range macros are empty */
+
+#define verify_virt(virt)  
+#define verify_dma(dma)  
+
+#endif /* !CONFIG_ARCH_PERSONAL_SERVER */
+
+/*-------------------------------------------------------------------------*
+ * Consistent allocation functions 
+ *-------------------------------------------------------------------------*/ 
+
+static int ohci_consistent_storage_create(struct pci_dev *pdev, struct ohci_consistent_storage *ocsp)
+{
+   int num_eds = NUM_EDS * 256;
+   int num_tds = 2048;
+   int i;
+   char *storage = NULL;
+   if (ocsp == NULL)
+      return -EINVAL;
+   storage = ocsp->virt = pci_alloc_consistent(pdev, 
+                                               sizeof(ed_t) * num_eds + sizeof(td_t) * num_tds,
+                                               &ocsp->dma_addr);
+   if (storage == NULL)
+      return -ENOMEM;
+   ocsp->edlist = NULL;
+   for (i = 0; i < num_eds; i++) {
+      ed_t *ed = (ed_t *)(storage + i * sizeof(ed_t));
+      *(ed_t **)ed = ocsp->edlist;
+      ocsp->edlist = ed;
+      ed->state = ED_NEW;
+   }
+   ocsp->tdlist = NULL;
+   for (i = 0; i < num_tds; i++) {
+      td_t *td = (td_t *)(storage + i * sizeof(td_t));
+      *(td_t **)td = ocsp->tdlist;
+      ocsp->tdlist = td;
+   }
+
+   ocsp->virt_to_dma = (long)ocsp->dma_addr - (long)ocsp->virt;
+   return 0;
+}
+
+static ed_t *ohci_alloc_consistent_ed(struct ohci *ohci) 
+{
+   int flags = 0;
+   ed_t *ed;
+   if (!in_interrupt()) spin_lock_irqsave (&usb_ed_lock, flags);	
+   ed = ohci->consistent_storage.edlist;
+   ohci->consistent_storage.edlist = *(ed_t **)ed;
+   if (!in_interrupt()) spin_unlock_irqrestore (&usb_ed_lock, flags);
+   if (0) printk(KERN_CRIT "alloc_consistent_ed: ed=%p\n", ed);
+   return ed;
+}
+
+static void ohci_free_consistent_ed(struct ohci *ohci, ed_t *ed) 
+{
+   int flags = 0;
+   if (!in_interrupt()) spin_lock_irqsave (&usb_ed_lock, flags);	
+   *(ed_t **)ed = ohci->consistent_storage.edlist;
+   ohci->consistent_storage.edlist = ed;
+   if (!in_interrupt()) spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+static td_t *ohci_alloc_consistent_td(struct ohci *ohci)
+{
+   int flags = 0;
+   td_t *td;
+   if (!in_interrupt()) spin_lock_irqsave (&usb_td_lock, flags);	
+   td = ohci->consistent_storage.tdlist;
+   ohci->consistent_storage.tdlist = *(td_t **)td;
+   if (!in_interrupt()) spin_unlock_irqrestore (&usb_td_lock, flags);
+   verify_virt(td);
+   if (0) printk(KERN_CRIT "alloc_consistent_td: td=%p\n", td);
+   return td;
+}
+
+static void ohci_free_consistent_td(struct ohci *ohci, td_t *td)
+{
+   int flags = 0;
+   verify_virt(td);
+   if (!in_interrupt()) spin_lock_irqsave (&usb_td_lock, flags);	
+   *(td_t **)td = ohci->consistent_storage.tdlist;
+   ohci->consistent_storage.tdlist = td;
+   if (!in_interrupt()) spin_unlock_irqrestore (&usb_td_lock, flags);
+}
+
+static dma_addr_t ohci_virt_to_bus(struct ohci *ohci, volatile void *obp)
+{
+   dma_addr_t dma = (long)(obp + ohci->consistent_storage.virt_to_dma);
+   verify_virt(obp);
+   verify_dma(dma);
+   return dma;
+}
+
+static void *ohci_bus_to_virt(struct ohci *ohci, dma_addr_t dma_addr)
+{
+   void *virt = (void *)(dma_addr - ohci->consistent_storage.virt_to_dma);
+   verify_virt(virt);
+   verify_dma(dma_addr);
+   return virt;
+}
 
 /*-------------------------------------------------------------------------*
  * URB support functions 
@@ -79,6 +208,7 @@
  
 static void urb_rm_priv (urb_t * urb) 
 {
+	struct ohci *ohci = (ohci_t *) urb->dev->bus->hcpriv;
 	urb_priv_t * urb_priv = urb->hcpriv;
 	int i;
 	
@@ -86,7 +216,7 @@
 	
 	for (i = 0; i < urb_priv->length; i++) {
 		if (urb_priv->td [i]) {
-			OHCI_FREE (urb_priv->td [i]);
+			OHCI_FREE_TD (ohci, urb_priv->td [i]);
 		}
 	}
 	kfree (urb->hcpriv);
@@ -150,12 +280,12 @@
 	 __u32 * ed_p;
 	for (i= 0; i < 32; i++) {
 		j = 5;
-		ed_p = &(ohci->hcca.int_table [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));
+			ed_t *ed = (ed_t *) ohci_bus_to_virt(ohci, le32_to_cpup(ed_p));
 			printk (" ed: %4x;", ed->hwINFO);
 			ed_p = &ed->hwNextED;
 		}
@@ -313,7 +443,7 @@
 	ohci_dump_status (controller);
 	if (verbose)
 		ep_print_int_eds (controller, "hcca");
-	dbg ("hcca frame #%04x", controller->hcca.frame_no);
+	dbg ("hcca frame #%04x", controller->hcca->frame_no);
 	ohci_dump_roothub (controller, 1);
 }
 
@@ -472,7 +602,7 @@
 	
 	/* allocate the TDs */
 	for (i = 0; i < size; i++) { 
-		OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
+		OHCI_ALLOC_TD (ohci, urb_priv->td[i]);
 		if (!urb_priv->td[i]) {
 			usb_dec_dev_use (urb->dev);	
 			urb_rm_priv (urb);
@@ -490,7 +620,7 @@
 	/* for ISOC transfers calculate start frame index */
 	if (urb->transfer_flags & USB_ISO_ASAP) { 
 		urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): 
-								(le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
+								(le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
 	}	
 	urb->status = USB_ST_URB_PENDING;
 	urb->actual_length = 0;
@@ -574,6 +704,8 @@
 
 static int sohci_alloc_dev (struct usb_device *usb_dev)
 {
+        int i;
+        struct ohci *ohci = usb_dev->bus->hcpriv;
 	struct ohci_device * dev;
 
 	dev = kmalloc (sizeof (*dev), GFP_KERNEL);
@@ -582,6 +714,12 @@
 		
 	memset (dev, 0, sizeof (*dev));
 
+        for (i = 0; i < NUM_EDS; i++) {
+           dev->ed[i] = ohci_alloc_consistent_ed(ohci);
+           if (dev->ed[i] == NULL)
+              return -ENOMEM;
+        }
+
 	usb_dev->hcpriv = dev;
 
 	return 0;
@@ -607,13 +745,14 @@
 		/* delete all TDs of all EDs */
 		spin_lock_irqsave (&usb_ed_lock, flags);	
 		for(i = 0; i < NUM_EDS; i++) {
-  			ed = &(dev->ed[i]);
+  			ed = dev->ed[i];
   			if (ed->state != ED_NEW) {
   				if (ed->state == ED_OPER) ep_unlink (ohci, ed);
   				ep_rm_ed (usb_dev, ed);
   				ed->state = ED_DEL;
   				cnt++;
   			}
+                        ohci_free_consistent_ed(ohci, ed);
   		}
   		spin_unlock_irqrestore (&usb_ed_lock, flags);
   		
@@ -637,7 +776,7 @@
 {
 	ohci_t * ohci = usb_dev->bus->hcpriv;
 	
-	return le16_to_cpu (ohci->hcca.frame_no);
+	return le16_to_cpu (ohci->hcca->frame_no);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -718,9 +857,9 @@
 	case CTRL:
 		ed->hwNextED = 0;
 		if (ohci->ed_controltail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+			writel (ohci_virt_to_bus (ohci, ed), &ohci->regs->ed_controlhead);
 		} else {
-			ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (ohci_virt_to_bus (ohci, ed));
 		}
 		ed->ed_prev = ohci->ed_controltail;
 		ohci->ed_controltail = edi;	  
@@ -729,9 +868,9 @@
 	case BULK:  
 		ed->hwNextED = 0;
 		if (ohci->ed_bulktail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+			writel (ohci_virt_to_bus (ohci, ed), &ohci->regs->ed_bulkhead);
 		} else {
-			ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ohci_virt_to_bus (ohci, ed));
 		}
 		ed->ed_prev = ohci->ed_bulktail;
 		ohci->ed_bulktail = edi;	  
@@ -746,12 +885,12 @@
 		
 		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);
+			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); 
+				(*ed_p != 0) && (((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
+				ed_p = &(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt (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 (ohci_virt_to_bus (ohci, ed));
 		}
 #ifdef DEBUG
 		ep_print_int_eds (ohci, "LINK_INT");
@@ -762,16 +901,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 (ohci_virt_to_bus (ohci, ed));
 			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)]); 
+			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 = &(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval);
+			*ed_p = cpu_to_le32 (ohci_virt_to_bus (ohci, ed));	
 			}	
 			ed->ed_prev = NULL;
 		}	
@@ -810,7 +949,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;
+			((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 		}
 		break;
       
@@ -823,7 +962,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;
+			((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 		}
 		break;
       
@@ -832,11 +971,11 @@
 		interval = ed->int_interval;
 
 		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; 
+			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 = &(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->hwNextED), 
+				inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval)) {				
+					if(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p))) == ed) {
 			  			*ed_p = ed->hwNextED;		
 			  			break;
 			  		}
@@ -853,18 +992,18 @@
     	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;
+				((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
 				
 		if (ed->ed_prev != NULL) {
 			ed->ed_prev->hwNextED = ed->hwNextED;
 		} else {
 			for (i = 0; i < 32; i += inter) {
 				inter = 1;
-				for (ed_p = &(ohci->hcca.int_table[ep_rev (5, 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 = &(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
+						inter = ep_rev (6, ((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p)))->int_interval);
+						if(((ed_t *) ohci_bus_to_virt (ohci, le32_to_cpup (ed_p))) == ed) {
 			  				*ed_p = ed->hwNextED;		
 			  				break;
 			  			}
@@ -899,17 +1038,16 @@
  	
 	spin_lock (&usb_ed_lock);
 
-	ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 
-			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+	ed = ed_ret = (usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
 
 	if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) 
 		return NULL; /* pending delete request */
 	
 	if (ed->state == ED_NEW) {
 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
-  		OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
+  		OHCI_ALLOC_TD (ohci, td); /* dummy td; end of td list for ed */
   		if(!td) return NULL; /* out of memory */
-		ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+		ed->hwTailP = cpu_to_le32 (ohci_virt_to_bus (ohci, td));
 		ed->hwHeadP = ed->hwTailP;	
 		ed->state = ED_UNLINK;
 		ed->type = usb_pipetype (pipe);
@@ -952,7 +1090,7 @@
 	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
 	writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
 
-	frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
+	frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
 	ed->ed_rm_list = ohci->ed_rm_list[frame];
 	ohci->ed_rm_list[frame] = ed;
 
@@ -972,33 +1110,45 @@
 
 /* prepare a TD */
 
-static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index)
+ static void td_fill (struct ohci *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 (urb_priv == NULL) return;
+
 	if (index >= urb_priv->length) {
 		err("internal OHCI error: TD index > length");
 		return;
 	}
 	
 	td_pt = urb_priv->td [index];
+ if (td_pt == NULL) return;
+ 
+ 
 	/* 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] = (td_t *) ohci_bus_to_virt (ohci, le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
+         if (td == NULL) return;
+ 
 	td->ed = urb_priv->ed;
+ 
+         if (td->ed == NULL) return;
+ 
 	td->index = index;
 	td->urb = urb; 
 	td->hwINFO = cpu_to_le32 (info);
+
 	if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
 		td->hwCBP = cpu_to_le32 (((!data || !len)? 
-								0 : virt_to_bus (data)) & 0xFFFFF000);
+                                           0 : 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 || !len)? 0 : 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->hwBE = cpu_to_le32 ((!data || !len )? 0: (data + len - 1));
+ 	td->hwNextTD = cpu_to_le32 (ohci_virt_to_bus (ohci, td_pt));
+ 	td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+ 
 	td_pt->hwNextTD = 0;
 	td->ed->hwTailP = td->hwNextTD;
    
@@ -1016,10 +1166,15 @@
 	void * ctrl = urb->setup_packet;
 	void * data = urb->transfer_buffer;
 	int data_len = urb->transfer_buffer_length;
+        dma_addr_t dma_data = 
+           pci_map_single(ohci->consistent_storage.pci_dev,
+                          data, data_len, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
 	int cnt = 0; 
 	__u32 info = 0;
   	unsigned int toggle = 0;
 
+        urb_priv->transfer_buffer_dma_addr = dma_data;
+
 	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
   	if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
   		toggle = TD_T_TOGGLE;
@@ -1035,12 +1190,12 @@
 			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);
-				data += 4096; data_len -= 4096; cnt++;
+ 				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), dma_data, 4096, urb, cnt);
+ 				dma_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), dma_data, data_len, urb, cnt);
 			cnt++;
 			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
 			break;
@@ -1048,27 +1203,29 @@
 		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, dma_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->consistent_storage.pci_dev, ctrl, 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++);  
+ 				td_fill (ohci, info, dma_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, (dma_addr_t)NULL, 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), 
+  					 dma_data + urb->iso_frame_desc[cnt].offset, 
 					urb->iso_frame_desc[cnt].length, urb, cnt); 
 			}
 			break;
@@ -1119,9 +1276,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 - urb_priv->transfer_buffer_dma_addr + 1;
   				else
-					urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer;
+                                                 urb->actual_length = tdCBP - urb_priv->transfer_buffer_dma_addr;
 			}
   		}
   	}
@@ -1142,11 +1299,11 @@
   	
   	spin_lock_irqsave (&usb_ed_lock, flags);
   	
-	td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;
-	ohci->hcca.done_head = 0;
+	td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+	ohci->hcca->done_head = 0;
 	
 	while (td_list_hc) {		
-		td_list = (td_t *) bus_to_virt (td_list_hc);
+		td_list = (td_t *) ohci_bus_to_virt (ohci, td_list_hc);
 
 		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
 			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
@@ -1190,8 +1347,8 @@
 	spin_lock_irqsave (&usb_ed_lock, flags);
 	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 = ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+		tdHeadP = ohci_bus_to_virt (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
 		edINFO = le32_to_cpup (&ed->hwINFO);
 		td_p = &ed->hwHeadP;
 
@@ -1199,7 +1356,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 = ohci_bus_to_virt (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) dl_transfer_length (td);
@@ -1220,7 +1377,7 @@
 		}
 		if (ed->state & ED_DEL) { /* set by sohci_free_dev */
 			struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
-   	 		OHCI_FREE (tdTailP); /* free dummy td */
+   	 		OHCI_FREE_TD (ohci, tdTailP); /* free dummy td */
    	 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
    	 		ed->state = ED_NEW; 
    	 		/* if all eds are removed wake up sohci_free_dev */
@@ -1735,7 +1892,7 @@
 	writel (0, &ohci->regs->ed_controlhead);
 	writel (0, &ohci->regs->ed_bulkhead);
 	
-	writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */
+	writel (ohci->hcca_dma_addr, &ohci->regs->hcca); /* a reset clears this */
    
   	fminterval = 0x2edf;
 	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
@@ -1787,14 +1944,14 @@
 	struct ohci_regs * regs = ohci->regs;
  	int ints; 
 
-	if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) {
+	if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
 		ints =  OHCI_INTR_WDH;
 	} else { 
  		if ((ints = (readl (&regs->intrstatus) & readl (&regs->intrenable))) == 0)
 			return;
 	} 
 
-	// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
+	// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
 
 	if (ints & OHCI_INTR_UE) {
 		ohci->disabled++;
@@ -1814,7 +1971,7 @@
 	}
 
 	if (ints & OHCI_INTR_SF) { 
-		unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1;
+		unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
 		writel (OHCI_INTR_SF, &regs->intrdisable);	
 		if (ohci->ed_rm_list[!frame] != NULL) {
 			dl_del_list (ohci, !frame);
@@ -1835,7 +1992,7 @@
 
 	/* for load balancing of the interrupt branches */
 	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
-	for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0;
+ 	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
 	
 	ohci->ed_rm_list [0] = NULL;
 	ohci->ed_rm_list [1] = NULL;
@@ -1848,20 +2005,31 @@
 
 /* allocate OHCI */
 
-static ohci_t * hc_alloc_ohci (void * mem_base)
+ static ohci_t * hc_alloc_ohci (struct pci_dev *pdev, void * mem_base)
 {
 	ohci_t * ohci;
 	struct usb_bus * bus;
+         int err;
 
 	ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1);
 	if (!ohci)
 		return NULL;
 		
 	memset (ohci, 0, sizeof (ohci_t));
+ 	ohci->hcca = (struct ohci_hcca *)pci_alloc_consistent(pdev, sizeof(struct ohci_hcca), &ohci->hcca_dma_addr);
+         if (!ohci->hcca)
+                 return NULL;
 	
 	ohci->irq = -1;
 	ohci->regs = mem_base;   
 
+	hc_reinit_ohci( ohci );
+
+         if ((err = ohci_consistent_storage_create(pdev, &ohci->consistent_storage))) {
+            printk(KERN_ERR "hc_alloc_ohci: failed to allocate consistent storage: err=%d\n", err);
+            return NULL;
+         }
+ 
 	bus = usb_alloc_bus (&sohci_device_operations);
 	if (!bus) {
 		free_pages ((unsigned long) ohci, 1);
@@ -1924,7 +2092,7 @@
 		(unsigned long)	mem_base, bufp);
 	printk(KERN_INFO __FILE__ ": %s\n", dev->name);
     
-	ohci = hc_alloc_ohci (mem_base);
+	ohci = hc_alloc_ohci (dev, mem_base);
 	if (!ohci) {
 		return -ENOMEM;
 	}
diff -u linux-2.4.0-test1-ac7/drivers/usb/usb-ohci.h linux-2.4.0-test1-ac7-crl1/drivers/usb/usb-ohci.h
--- linux-2.4.0-test1-ac7/drivers/usb/usb-ohci.h	Fri Jun 16 13:27:42 2000
+++ linux-2.4.0-test1-ac7-crl1/drivers/usb/usb-ohci.h	Fri Jun 16 16:54:42 2000
@@ -374,11 +374,29 @@
 	__u16 td_cnt;	// number of tds already serviced
 	int   state;
 	void * wait;
+        dma_addr_t transfer_buffer_dma_addr;
 	td_t * td[0];	// list pointer to all corresponding TDs associated with this request
 
 } urb_priv_t;
 #define URB_DEL 1
 
+
+/*
+ * In order to use the pci_alloc_consistent interface to allocate ED's and
+ * TD's, we need to keep a mapping between the virtual addresses and the
+ * dma addresses.  We do this using a kmem_cache, allowing it a single slab
+ * only.
+ */
+
+struct ohci_consistent_storage {
+        char *virt;
+        dma_addr_t dma_addr;
+        long virt_to_dma;
+        ed_t *edlist; /* freelist of eds */
+        td_t *tdlist; /* freelist of tds */
+        struct pci_dev *pci_dev;
+};
+
 /*
  * This is the full ohci controller description
  *
@@ -386,9 +404,9 @@
  * a subset of what the full implementation needs. (Linus)
  */
 
-
 typedef struct ohci {
-	struct ohci_hcca hcca;		/* hcca */
+	struct ohci_hcca *hcca;		/* hcca */
+        dma_addr_t  hcca_dma_addr;
 
 	int irq;
 	int disabled;			/* e.g. got a UE, we're hung */
@@ -410,6 +428,7 @@
 	struct usb_bus * bus;    
 	struct usb_device * dev[128];
 	struct virt_root_hub rh;
+        struct ohci_consistent_storage consistent_storage;
 } ohci_t;

@@ -417,7 +436,7 @@
 #define NUM_EDS 32		/* num of preallocated endpoint descriptors */
 
 struct ohci_device {
-	ed_t 	ed[NUM_EDS];
+	ed_t 	*ed[NUM_EDS];
 	int ed_cnt;
 	void  * wait;
 };
@@ -432,7 +451,7 @@
 static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load);
 static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
 /* td */
-static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index);
+static void td_fill(struct ohci *, unsigned int info, dma_addr_t data, int len, urb_t * urb, int index);
 static void td_submit_urb(urb_t * urb);
 /* root hub */
 static int rh_submit_urb(urb_t * urb);
@@ -440,11 +459,11 @@
 static int rh_init_int_timer(urb_t * urb);
 
 #ifdef DEBUG
-#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x)
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
+#define OHCI_FREE_TD(ohci,x) ohci_free_consistent_td(ohci, x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x)
+#define OHCI_ALLOC_TD(ohci,x) (x) = ohci_alloc_consistent_td(ohci); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x)
 static int __ohci_free_cnt = 0;
 #else
-#define OHCI_FREE(x) kfree(x) 
-#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) 
+#define OHCI_FREE_TD(ohci, x) ohci_free_consistent_td(ohci, x) 
+#define OHCI_ALLOC_TD(ohci, x) (x) = ohci_alloc_consistent_td(ohci) 
 #endif

unsubscribe: body of `unsubscribe linux-arm-kernel' to [email-masked]

---

From: Kunihiko IMAI <[email-masked]>
Date: Tue, 20 Jun 2000 13:48:42 +0900

Hello,

I've just caught up the latest version of linux kernel.  And I've
ported James's version of usb-ohci.c driver for SA1111.

It's a quite a quick hack, but I believe that there remains only
political or formal problems.

Note (or known bug):

o This patch requires a setup for SA1111 DMA.
	I'll post it later.

o Not consider co-existence with a PCI-connected OHCI USB device.

o mis-used bus_to_virt() and virt_to_bus() instead of phys_to_virt()
  and virt_to_phys().

o CONFIG_SA1100_XP860 is a machine arch. definition.
	XP860 has a SA1110 and a SA1111.  So it is similar to Intel
	assabet + neponset.
	I believe that it is easy to port this patch to them.

o Too dirty :-)

Thanks.
_._. __._  _.. .  .___ ._. _____ _... ._ _._  _.._. .____  .__. ... .  _._

                                                             Kunihiko IMAI
--- usb-ohci.c.JAMES	Mon Jun 19 21:24:21 2000
+++ usb-ohci.c	Tue Jun 20 13:44:02 2000
@@ -61,6 +61,21 @@
 #include <linux/pmu.h>
 #endif
 
+#ifdef CONFIG_SA1111
+
+#define SA1111_OHCI_BASE        SA1111_p2v(_SA1111(0x0400))
+#define SA1111_OHCI_EXTENT      512
+#define SA1111_OHCI_IRQ         NIRQHCIM
+
+#ifdef CONFIG_SA1100_XP860
+#define PwrSensePolLow  1
+#define PwrCtrlPolLow   1
+#else
+#define PwrSensePolLow  0
+#define PwrCtrlPolLow   0
+#endif
+
+#endif
 
 /* For initializing controller (mask in an HCFS mode too) */
 #define	OHCI_CONTROL_INIT \
@@ -90,7 +105,11 @@
 static int __verify_dma(dma_addr_t dma, int line)
 {
   unsigned long addr = (unsigned long)dma;
+#ifndef CONFIG_SA1100_XP860
   int ok = ((addr & 0xF0000000) == 0xE0000000);
+#else
+  int ok = ((addr & 0xF0000000) == 0xC0000000);
+#endif
   if (!ok)
     printk(KERN_CRIT __FILE__ ":%d: Expected DMA address, got %#08x\n", line, dma);
   return ok;
@@ -2071,8 +2090,12 @@
 	usb_deregister_bus (ohci->bus);
 	usb_free_bus (ohci->bus);
     
+#ifndef CONFIG_SA1111
 	/* unmap the IO address space */
 	iounmap (ohci->regs);
+#else
+	release_region ( SA1111_OHCI_BASE, SA1111_OHCI_EXTENT );
+#endif
        
@@ -2119,9 +2142,16 @@
 		ohci->irq = irq;     
 		hc_start (ohci);
 
+#ifndef CONFIG_SA1111
 		pmdev = pm_register (PM_PCI_DEV,
 				     PM_PCI_ID(dev),
 				     handle_pm_event);
+#else
+		/* FIX ME: this is quite dummy */
+		pmdev = pm_register (9999,
+				     9999,
+				     handle_pm_event);
+#endif
 		if (pmdev)
 			pmdev->data = ohci;
 
@@ -2140,6 +2170,7 @@
  
 static int hc_start_ohci (struct pci_dev * dev)
 {
+#ifndef CONFIG_SA1111
 	unsigned long mem_base;
 
 	if (pci_enable_device(dev) < 0)
@@ -2154,6 +2185,10 @@
 		return -EFAULT;
 	}
 	return hc_found_ohci (dev, dev->irq, (void *) mem_base);
+#else
+	request_region ( SA1111_OHCI_BASE, SA1111_OHCI_EXTENT, "usb-ohci" );
+	return hc_found_ohci (dev, SA1111_OHCI_IRQ, SA1111_OHCI_BASE );
+#endif
 } 
 
@@ -2257,6 +2292,7 @@
  
 int ohci_hcd_init (void) 
 {
+#ifndef CONFIG_SA1111
 	int ret = -ENODEV;
 	struct pci_dev * dev = NULL;
  
@@ -2268,6 +2304,20 @@
 	pmu_register_sleep_notifier (&ohci_sleep_notifier);
 #endif  
     return ret;
+#else
+	struct pci_dev * dev = NULL;
+	volatile unsigned long *Reset = SA1111_p2v(_SA1111(0x051c));
+
+	/* turn the USB clock power on */
+	SKPCR |= 0x81;
+	mdelay(1);
+
+	/* reset USB */
+        *Reset = ( PwrSensePolLow << 6 )
+		| ( PwrCtrlPolLow << 7 );
+ 
+	return hc_start_ohci(dev);
+#endif /*CONFIG_SA1111*/
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2296,6 +2346,10 @@
 		INIT_LIST_HEAD (&ohci->ohci_hcd_list);
 		hc_release_ohci (ohci);
 	}		
+#ifdef CONFIG_SA1111
+	/* turn the USB clock off */
+	SKPCR &= ~0x1;
+#endif
 }
 #endif //MODULE

---

From: Kunihiko IMAI <[email-masked]>
Date: Fri, 30 Jun 2000 21:10:52 +0900

Hello,

At Fri, 16 Jun 2000 17:04:04 -0400,

> kernel to crash.  I assume this doesn't happen on x86.

I found that where this happens.  But I haven't fixed it yet.  The
data structure is too complicated for me to trace completely.

Would someone give me some advice?

The crashed place is:

On dl_del_list() in usb-ohci.c (with James patch + my patch),

		OHCI_FREE_TD (ohci, tdTailP); /* free dummy td */
		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
		ed->state = ED_NEW; 

		if (!--dev->ed_cnt)
			wake_up (&op_wakeup);

I observed it occurred `dev' was NULL.  In this case, `--dev->ed_cnt'
is trapped and got Oops dump.

Thanks.

                                                             Kunihiko IMAI