diff options
Diffstat (limited to 'uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c')
-rw-r--r-- | uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c b/uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c index b7c89c1..fc8911f 100644 --- a/uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c +++ b/uClinux-2.4.20-uc1/drivers/usb/usb-ohci.c @@ -53,7 +53,9 @@ #include <linux/config.h> #include <linux/module.h> +#ifndef CONFIG_BOARD_W90N745 #include <linux/pci.h> +#endif #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ioport.h> @@ -78,12 +80,16 @@ #include "usb-ohci.h" +#ifndef CONFIG_BOARD_W90N745 #include "hcd.h" +#endif #ifdef CONFIG_PMAC_PBOOK #include <asm/machdep.h> #include <asm/pmac_feature.h> +#ifndef CONFIG_BOARD_W90N745 #include <asm/pci-bridge.h> +#endif #ifndef CONFIG_PM #define CONFIG_PM #endif @@ -129,7 +135,253 @@ static inline u32 roothub_status (struct ohci *hc) static u32 roothub_portstatus (struct ohci *hc, int i) { return read_roothub (hc, portstatus [i], 0xffe0fce0); } +#ifdef CONFIG_BOARD_W90N745 + + +extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, + int bustime, int isoc); +extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, + int isoc); +extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); + + /*pool support functions*/ +struct ohci_pool * +ohci_pool_create (const char *name, + size_t size, size_t align, size_t allocation, int flags) +{ + struct ohci_pool *retval; + + if (align == 0) + align = 1; + if (size == 0) + return 0; + else if (size < align) + size = align; + else if ((size % align) != 0) { + size += align + 1; + size &= ~(align - 1); + } + + if (allocation == 0) { + if (PAGE_SIZE < size) + allocation = size; + else + allocation = PAGE_SIZE; + // FIXME: round up for less fragmentation + } else if (allocation < size) + return 0; + + if (!(retval = kmalloc (sizeof *retval, flags))) + return retval; + +#ifdef CONFIG_PCIPOOL_DEBUG + flags |= SLAB_POISON; +#endif + + strncpy (retval->name, name, sizeof retval->name); + retval->name [sizeof retval->name - 1] = 0; + + INIT_LIST_HEAD (&retval->page_list); + spin_lock_init (&retval->lock); + retval->size = size; + retval->flags = flags; + retval->allocation = allocation; + retval->blocks_per_page = allocation / size; + init_waitqueue_head (&retval->waitq); + +#ifdef CONFIG_PCIPOOL_DEBUG + printk (KERN_DEBUG "pcipool create%s size %d, %d/page (%d alloc)\n", + retval->name, size, + retval->blocks_per_page, allocation); +#endif + + return retval; +} + +void * +ohci_pool_alloc (struct ohci_pool *pool, int mem_flags, dma_addr_t *handle) +{ + unsigned long flags; + struct list_head *entry; + struct pci_page *page; + int map, block; + size_t offset; + void *retval; + +restart: + spin_lock_irqsave (&pool->lock, flags); + list_for_each (entry, &pool->page_list) { + int i; + page = list_entry (entry, struct pci_page, page_list); + /* only cachable accesses here ... */ + for (map = 0, i = 0; + i < pool->blocks_per_page; + i += BITS_PER_LONG, map++) { + if (page->bitmap[map] == 0) + continue; + block = ffz (~ page->bitmap [map]); + if ((i + block) < pool->blocks_per_page) { + clear_bit (block, &page->bitmap [map]); + offset = (BITS_PER_LONG * map) + block; + offset *= pool->size; + goto ready; + } + } + } + if (!(page = pool_alloc_page (pool, mem_flags))) { + if (mem_flags == SLAB_KERNEL) { + DECLARE_WAITQUEUE (wait, current); + + current->state = TASK_INTERRUPTIBLE; + add_wait_queue (&pool->waitq, &wait); + spin_unlock_irqrestore (&pool->lock, flags); + + schedule_timeout (POOL_TIMEOUT_JIFFIES); + + current->state = TASK_RUNNING; + remove_wait_queue (&pool->waitq, &wait); + goto restart; + } + retval = 0; + goto done; + } + + clear_bit (0, &page->bitmap [0]); + offset = 0; +ready: + retval = offset + page->vaddr; + *handle = offset + page->dma; +done: + spin_unlock_irqrestore (&pool->lock, flags); + return retval; +} + +void +ohci_pool_free (struct ohci_pool *pool, void *vaddr, dma_addr_t dma) +{ + struct pci_page *page; + unsigned long flags; + int map, block; + + if ((page = pool_find_page (pool, dma)) == 0) { + printk (KERN_ERR "pci_pool_free %s, %p/%x (bad dma)\n", + pool->name, vaddr, (int) (dma & 0xffffffff)); + return; + } + + block = dma - page->dma; + block /= pool->size; + map = block / BITS_PER_LONG; + block %= BITS_PER_LONG; + + if (pool->flags & SLAB_POISON) + memset (vaddr, POOL_POISON_BYTE, pool->size); + + spin_lock_irqsave (&pool->lock, flags); + set_bit (block, &page->bitmap [map]); + if (waitqueue_active (&pool->waitq)) + wake_up (&pool->waitq); + /* + * Resist a temptation to do + * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page); + * it is not interrupt safe. Better have empty pages hang around. + */ + spin_unlock_irqrestore (&pool->lock, flags); +} + +static struct pci_page * +pool_find_page (struct ohci_pool *pool, dma_addr_t dma) +{ + unsigned long flags; + struct list_head *entry; + struct pci_page *page; + + spin_lock_irqsave (&pool->lock, flags); + list_for_each (entry, &pool->page_list) { + page = list_entry (entry, struct pci_page, page_list); + if (dma < page->dma) + continue; + if (dma < (page->dma + pool->allocation)) + goto done; + } + page = 0; +done: + spin_unlock_irqrestore (&pool->lock, flags); + return page; +} + +void +ohci_pool_destroy (struct ohci_pool *pool) +{ + unsigned long flags; + + spin_lock_irqsave (&pool->lock, flags); + while (!list_empty (&pool->page_list)) { + struct pci_page *page; + page = list_entry (pool->page_list.next, + struct pci_page, page_list); + if (is_page_busy (pool->blocks_per_page, page->bitmap)) { + /* leak the still-in-use consistent memory */ + list_del (&page->page_list); + kfree ((void*)page); + } else + pool_free_page (pool, page); + } + spin_unlock_irqrestore (&pool->lock, flags); + kfree ((void*)pool); +} + +static struct pci_page * +pool_alloc_page (struct ohci_pool *pool, int mem_flags) +{ + struct pci_page *page; + int mapsize; + + mapsize = pool->blocks_per_page; + mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG; + mapsize *= sizeof (long); + + page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags); + if (!page) + return 0; + page->vaddr = ohci_alloc_consistent (pool->allocation, + &page->dma); + if (page->vaddr) { + memset (page->bitmap, 0xff, mapsize); // bit set == free + if (pool->flags & SLAB_POISON) + memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); + list_add (&page->page_list, &pool->page_list); + } else { + kfree ((void*)page); + page = 0; + } + return page; +} + +static void +pool_free_page (struct ohci_pool *pool, struct pci_page *page) +{ + dma_addr_t dma = page->dma; + + if (pool->flags & SLAB_POISON) + memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); + ohci_free_consistent (pool->allocation, page->vaddr, dma); + list_del (&page->page_list); + kfree ((void*)page); +} + +static inline int +is_page_busy (int blocks, unsigned long *bitmap) +{ + while (blocks > 0) { + if (*bitmap++ != ~0UL) + return 1; + blocks -= BITS_PER_LONG; + } + return 0; +} +#endif /*-------------------------------------------------------------------------* * URB support functions *-------------------------------------------------------------------------*/ @@ -157,17 +409,26 @@ static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) /* unmap CTRL URB setup */ if (usb_pipecontrol (td->urb->pipe)) { +#ifndef CONFIG_BOARD_W90N745 pci_unmap_single (hc->ohci_dev, td->data_dma, 8, PCI_DMA_TODEVICE); +#else + ohci_unmap_single(td->data_dma, 8, PCI_DMA_TODEVICE); +#endif /* CTRL data buffer starts at td 1 if len > 0 */ if (len && last > 0) td = urb_priv->td [1]; } +#ifndef CONFIG_BOARD_W90N745 /* unmap data buffer */ if (len && td->data_dma) pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir); +#else + if (len && td->data_dma) + ohci_unmap_single(td->data_dma, 8, PCI_DMA_TODEVICE); +#endif for (i = 0; i <= last; i++) { td = urb_priv->td [i]; @@ -442,7 +703,9 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose) static void ohci_dump (ohci_t *controller, int verbose) { +#ifndef CONFIG_BOARD_W90N745 dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); +#endif // dumps some of the state we know about ohci_dump_status (controller); @@ -483,12 +746,20 @@ static int sohci_return_urb (struct ohci *hc, struct urb * urb) switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: +#ifdef CONFIG_BOARD_W90N745 + ohci_unmap_single(urb_priv->td [0]->data_dma, + urb->transfer_buffer_length, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); +#else 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); +#endif urb->complete (urb); /* implicitly requeued */ @@ -500,12 +771,20 @@ static int sohci_return_urb (struct ohci *hc, struct urb * urb) case PIPE_ISOCHRONOUS: for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); if (urbt) { /* send the reply and requeue URB */ +#ifdef CONFIG_BOARD_W90N745 + ohci_unmap_single(urb_priv->td [0]->data_dma, + urb->transfer_buffer_length, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); +#else 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); +#endif urb->complete (urb); spin_lock_irqsave (&usb_ed_lock, flags); urb->actual_length = 0; @@ -855,8 +1134,10 @@ static int sohci_free_dev (struct usb_device * usb_dev) if (ed->state != ED_NEW) { if (ed->state == ED_OPER) { /* driver on that interface didn't unlink an urb */ +#ifndef CONFIG_BOARD_W90N745 dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", ohci->ohci_dev->slot_name, usb_dev->devnum, i); +#endif ep_unlink (ohci, ed); } ep_rm_ed (usb_dev, ed); @@ -900,8 +1181,10 @@ static int sohci_free_dev (struct usb_device * usb_dev) } } else { /* likely some interface's driver has a refcount bug */ +#ifndef CONFIG_BOARD_W90N745 err ("bus %s devnum %d deletion in interrupt", ohci->ohci_dev->slot_name, usb_dev->devnum); +#endif BUG (); } } @@ -1369,12 +1652,20 @@ static void td_submit_urb (struct urb * urb) urb_priv->td_cnt = 0; if (data_len) { +#ifdef CONFIG_BOARD_W90N745 + data = ohci_map_single(urb->transfer_buffer, data_len, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE + ); +#else data = pci_map_single (ohci->ohci_dev, urb->transfer_buffer, data_len, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE ); +#endif } else data = 0; @@ -1417,11 +1708,18 @@ static void td_submit_urb (struct urb * urb) case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; +#ifdef CONFIG_BOARD_W90N745 + td_fill (ohci, info, + ohci_map_single(urb->setup_packet, 8, + PCI_DMA_TODEVICE), + 8, urb, cnt++); +#else td_fill (ohci, info, pci_map_single (ohci->ohci_dev, urb->setup_packet, 8, PCI_DMA_TODEVICE), 8, urb, cnt++); +#endif 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; @@ -1814,8 +2112,10 @@ static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) num_ports = roothub_a (ohci) & RH_A_NDP; if (num_ports > MAX_ROOT_PORTS) { +#ifndef CONFIG_BOARD_W90N745 err ("bogus NDP=%d for OHCI usb-%s", num_ports, ohci->ohci_dev->slot_name); +#endif err ("rereads as NDP=%d", readl (&ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ @@ -2162,9 +2462,11 @@ static int hc_reset (ohci_t * ohci) /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); +#ifndef CONFIG_BOARD_W90N745 dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", ohci->ohci_dev->slot_name, readl (&ohci->regs->control)); +#endif /* Reset USB (needed by some controllers) */ writel (0, &ohci->regs->control); @@ -2309,7 +2611,9 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) /* cardbus/... hardware gone before remove() */ } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { ohci->disabled++; +#ifndef CONFIG_BOARD_W90N745 err ("%s device removed!", ohci->ohci_dev->slot_name); +#endif return; /* interrupt for some other device? */ @@ -2321,8 +2625,10 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) if (ints & OHCI_INTR_UE) { ohci->disabled++; +#ifndef CONFIG_BOARD_W90N745 err ("OHCI Unrecoverable Error, controller usb-%s disabled", ohci->ohci_dev->slot_name); +#endif // e.g. due to PCI Master/Target Abort #ifdef DEBUG @@ -2371,10 +2677,43 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) writel (OHCI_INTR_MIE, ®s->intrenable); } +/*-------------alloc consistent not for pci-------------------------------------------*/ +#ifdef CONFIG_BOARD_W90N745 +void *ohci_alloc_consistent(size_t size, dma_addr_t *handle) +{ + void *__ret; + int __gfp = GFP_KERNEL; + + __ret = (void*)((unsigned long)consistent_alloc(__gfp, (size), (handle))|0x80000000); + return __ret; +} + +void ohci_free_consistent(size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + consistent_free(vaddr, size, dma_handle); +} + +inline dma_addr_t +ohci_map_single(void *ptr, size_t size, int direction) +{ + consistent_sync(ptr, size, direction); + return virt_to_bus(ptr); +} + +inline void +ohci_unmap_single(dma_addr_t dma_addr, size_t size, int direction) +{ + /* nothing to do */ +} +#endif + + /*-------------------------------------------------------------------------*/ /* allocate OHCI */ +#ifndef CONFIG_BOARD_W90N745 static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) { ohci_t * ohci; @@ -2419,7 +2758,51 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) return ohci; } +#else +static ohci_t * __devinit hc_alloc_ohci (void * mem_base) +{ + ohci_t * ohci; + + ohci = (ohci_t *)kmalloc (sizeof *ohci, GFP_KERNEL); + if (!ohci) + return NULL; + + memset (ohci, 0, sizeof (ohci_t)); + + ohci->hcca = ohci_alloc_consistent(sizeof *ohci->hcca, + &ohci->hcca_dma); + + if (!ohci->hcca) { + kfree (ohci); + return NULL; + } + + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + + ohci->disabled = 1; + ohci->sleeping = 0; + ohci->irq = -1; + ohci->regs = mem_base; + + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); + + INIT_LIST_HEAD (&ohci->timeout_list); + + ohci->bus = usb_alloc_bus (&sohci_device_operations); + + + if (!ohci->bus) { + ohci_free_consistent (sizeof *ohci->hcca, + ohci->hcca, ohci->hcca_dma); + kfree (ohci); + return NULL; + } + ohci->bus->hcpriv = (void *) ohci; + return ohci; +} +#endif /*-------------------------------------------------------------------------*/ @@ -2427,7 +2810,9 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) static void hc_release_ohci (ohci_t * ohci) { +#ifndef CONFIG_BOARD_W90N745 dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); +#endif /* disconnect all devices */ if (ohci->bus->root_hub) @@ -2440,7 +2825,9 @@ static void hc_release_ohci (ohci_t * ohci) free_irq (ohci->irq, ohci); ohci->irq = -1; } +#ifndef CONFIG_BOARD_W90N745 pci_set_drvdata(ohci->ohci_dev, NULL); +#endif if (ohci->bus) { if (ohci->bus->busnum != -1) usb_deregister_bus (ohci->bus); @@ -2456,8 +2843,13 @@ static void hc_release_ohci (ohci_t * ohci) /* unmap the IO address space */ iounmap (ohci->regs); +#ifndef CONFIG_BOARD_W90N745 pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, ohci->hcca, ohci->hcca_dma); +#else + ohci_free_consistent(sizeof*ohci->hcca, + ohci->hcca, ohci->hcca_dma); +#endif kfree (ohci); } @@ -2468,6 +2860,7 @@ static void hc_release_ohci (ohci_t * ohci) static struct pci_driver ohci_pci_driver; +#ifndef CONFIG_BOARD_W90N745 static int __devinit hc_found_ohci (struct pci_dev *dev, int irq, void *mem_base, const struct pci_device_id *id) @@ -2542,7 +2935,69 @@ hc_found_ohci (struct pci_dev *dev, int irq, #endif return 0; } +#else +static int __devinit +hc_found_ohci (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); +#else + bufp = __irq_itoa(irq); +#endif + printk(": USB OHCI at membase 0x%lx, IRQ %s\n", + (unsigned long) mem_base, bufp); + + ohci = hc_alloc_ohci (mem_base); + if (!ohci) { + return -ENOMEM; + } + printk("hc_alloc_ohci\n"); + 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"); + + printk("hc_reset\n"); + if (hc_reset (ohci) < 0) { + hc_release_ohci (ohci); + return -ENODEV; + } + //printk("writel\n"); + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + wait_ms (10); + + usb_register_bus (ohci->bus); + //CSR_WRITE (AIC_SCR9, 0x41); /* high-level sensitive, priority level 1 */ + if (request_irq (irq, hc_interrupt, SA_SHIRQ, + ohci_pci_driver.name, ohci) != 0) { + err ("request interrupt %s failed", bufp); + hc_release_ohci (ohci); + return -EBUSY; + } + ohci->irq = irq; + //printk("irq:%d\n",ohci->irq); + if (hc_start (ohci) < 0) { + err ("can't start usb"); + hc_release_ohci (ohci); + return -EBUSY; + } +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + return 0; +} +#endif /*-------------------------------------------------------------------------*/ #ifdef CONFIG_PM @@ -2589,6 +3044,7 @@ static void hc_restart (ohci_t *ohci) /* configured so that an OHCI device is always provided */ /* always called with process context; sleeping is OK */ +#ifndef CONFIG_BOARD_W90N745 static int __devinit ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) { @@ -2671,8 +3127,43 @@ ohci_pci_remove (struct pci_dev *dev) release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); pci_disable_device (dev); } +#else +static int __devinit +ohci_pci_probe (const struct pci_device_id *id) +{ + unsigned long mem_resource, mem_len; + void *mem_base; + int status; + + mem_resource = USB_HOST; + mem_len = 0x5C; + + mem_base = ioremap_nocache (mem_resource, mem_len); + + if (!mem_base) { + err("Error mapping OHCI memory"); + release_mem_region (mem_resource, mem_len); + return -EFAULT; + } + /* controller writes into our memory */ + /*dev->resource[0].start=mem_resource; + dev->resource[0].end=mem_resource + mem_len; + dev->irq = 9;*/ + + status = hc_found_ohci(INT_USBINT0, mem_base, id);//lsshi 2005-4-20 15:38 + if (status < 0) { + iounmap (mem_base); + release_mem_region (mem_resource, mem_len); + } + return status; +} +static void __devexit +ohci_pci_remove (struct pci_dev *dev) +{ +} +#endif #ifdef CONFIG_PM /*-------------------------------------------------------------------------*/ @@ -2917,14 +3408,22 @@ static struct pci_driver ohci_pci_driver = { static int __init ohci_hcd_init (void) { +#ifdef CONFIG_BOARD_W90N745 + printk("add a static ohci host controller device\n"); + ohci_pci_probe (ohci_pci_ids); + return 0; +#else return pci_module_init (&ohci_pci_driver); +#endif } /*-------------------------------------------------------------------------*/ static void __exit ohci_hcd_cleanup (void) { +#ifndef CONFIG_BOARD_W90N745 pci_unregister_driver (&ohci_pci_driver); +#endif } module_init (ohci_hcd_init); |