Index: linux-2.6.18/drivers/usb/host/ohci-tmio.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18/drivers/usb/host/ohci-tmio.c 2006-09-20 16:18:08.000000000 +0200 @@ -0,0 +1,894 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus glue for Toshiba Mobile IO (TMIO) Controller's OHCI core + * (C) Copyright 2005 Chris Humbert + * + * This is known to work with the following variants: + * TC6393XB revision 3 (32kB SRAM) + * + * The TMIO's OHCI core DMAs through a small internal buffer that + * is directly addressable by the CPU. dma_declare_coherent_memory + * and DMA bounce buffers allow the higher-level OHCI host driver to + * work. However, the dma API doesn't handle dma mapping failures + * well (dma_sg_map() is a prime example), so it is unusable. + * + * This HC pretends be a PIO-ish controller and uses the kernel's + * generic allocator for the entire SRAM. Using the USB core's + * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's + * ohci_ops, we provide memory management for OHCI's TDs and EDs. We + * internally queue a URB's TDs until enough dma memory is available + * to enqueue them with the HC. + * + * Written from sparse documentation from Toshiba and Sharp's driver + * for the 2.4 kernel, + * usb-ohci-tc6393.c (C) Copyright 2004 Lineo Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include /* for consistent_sync() */ +#include + +/*-------------------------------------------------------------------------*/ + +/* + * USB Host Controller Configuration Register + */ +struct tmio_uhccr { + u8 x00[8]; + u8 revid; /* 0x08 Revision ID */ + u8 x01[7]; + u16 basel; /* 0x10 USB Control Register Base Address Low */ + u16 baseh; /* 0x12 USB Control Register Base Address High */ + u8 x02[0x2c]; + u8 ilme; /* 0x40 Internal Local Memory Enable */ + u8 x03[0x0b]; + u16 pm; /* 0x4c Power Management */ + u8 x04[2]; + u8 intc; /* 0x50 INT Control */ + u8 x05[3]; + u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */ + u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */ + u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */ + u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */ + u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */ + u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */ + u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */ + u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */ + u8 x06[0x98]; + u8 misc; /* 0xFC MISC */ + u8 x07[3]; +} __attribute__ ((packed)); + +union tmio_uhccr_pm { + u16 raw; +struct { + unsigned gcken:1; /* D0 */ + unsigned ckrnen:1; /* D1 */ + unsigned uspw1:1; /* D2 USB Port 1 Power Disable */ + unsigned uspw2:1; /* D3 USB Port 2 Power Disable */ + unsigned x00:4; + unsigned pmee:1; /* D8 */ + unsigned x01:6; + unsigned pmes:1; /* D15 */ +} __attribute__ ((packed)); +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +struct tmio_dma_pool { + struct device* dev; + unsigned int size; +}; + +struct tmio_hcd { + struct gen_pool* poolp; + struct usb_operations ops; + struct tmio_dma_pool td_pool; + struct tmio_dma_pool ed_pool; + + struct tmio_uhccr __iomem *ccr; + void __iomem * sram; + size_t sram_len; +}; + +#define hcd_to_tmio(hcd) ((struct tmio_hcd*)(hcd_to_ohci (hcd) + 1)) + +struct tmio_td { + void* data; /* td's data buffer */ + void __iomem * bounce; /* dma bounce buffer */ + dma_addr_t dma; /* bounce buffer dma address */ + size_t len; /* bounce buffer length */ + u32 info; /* parameter for td_fill */ +}; + +struct tmio_urb { + int td_add; /* next index to be added */ + int td_queue; /* next index to be HC enqueued */ + + struct tmio_td td [0]; /* private td data */ +}; + +static inline struct tmio_urb *urb_to_turb (struct urb *urb) +{ + urb_priv_t* urb_priv = urb->hcpriv; + return (struct tmio_urb*)(urb_priv->td + urb_priv->length); +} + +/*-------------------------------------------------------------------------*/ + +/* gen_pool_alloc page allocator callback */ +static unsigned long tmio_pool_callback(struct gen_pool *poolp) +{ + return 0; +} + +static inline void tmio_pool_destroy(struct tmio_hcd *tmio) +{ + struct gen_pool *poolp = tmio->poolp; + + if (!poolp) + return; + if (poolp->h) + kfree(poolp->h); + kfree(poolp); + tmio->poolp = NULL; +} + +/*-------------------------------------------------------------------------*/ + +#define BOUNDED_XYL(x,y,ylen) (((y) <= (x)) && ((x) < ((y)+(ylen)))) +#define BOUNDED_XYY(x,y1,y2) (((y1) <= (x)) && ((x) < (y2))) + +static inline dma_addr_t tmio_virt_to_dma (struct usb_hcd *hcd, void *vaddr) +{ + struct resource* sram = tmio_resource_mem (hcd->self.controller); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + + return BOUNDED_XYL (vaddr, tmio->sram, tmio->sram_len) + ? sram->start + (vaddr - tmio->sram) + : ~0; +} + +static inline void* tmio_dma_to_virt (struct usb_hcd *hcd, dma_addr_t handle) +{ + struct resource* sram = tmio_resource_mem (hcd->self.controller); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + + return BOUNDED_XYY (handle, sram->start, sram->end + 1) + ? tmio->sram + handle - sram->start + : NULL; +} + +/* + * allocate dma-able memory in the device's internal sram + * + * The generic pool allocator's minimum chunk size is 32 bytes, + * which is the cache line size on the PXA255, so we don't need + * to do anything special for smaller requests. + */ +static inline void *tmio_dma_alloc (struct device *dev, size_t size, + dma_addr_t *handle, gfp_t mem_flags) +{ + struct usb_hcd* hcd = dev_get_drvdata (dev); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + void* virt = (void*) gen_pool_alloc (tmio->poolp, size); + + return (*handle = tmio_virt_to_dma (hcd, virt)) == ~0 ? NULL : virt; +} + +static inline void tmio_dma_free (struct device *dev, size_t size, + void *cpu_addr, dma_addr_t handle) +{ + struct usb_hcd* hcd = dev_get_drvdata (dev); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + dma_addr_t dma = tmio_virt_to_dma (hcd, cpu_addr); + + if (unlikely (dma == ~0)) { + dev_err (dev, "trying to free bad address 0x%p\n", cpu_addr); + return; + } + + if (unlikely (handle != dma)) + dev_err (dev, "dma address mismatch for 0x%p: %08x != %08x\n", + cpu_addr, handle, dma); + + gen_pool_free (tmio->poolp, (unsigned long) cpu_addr, size); +} + +/*-------------------------------------------------------------------------*/ + +static void *tmio_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, + dma_addr_t *handle) +{ + struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; + return tmio_dma_alloc (tdp->dev, tdp->size, handle, mem_flags); +} + +static void +tmio_dma_pool_free (struct dma_pool *pool, void *vaddr, dma_addr_t addr) +{ + struct tmio_dma_pool *tdp = (struct tmio_dma_pool*) pool; + return tmio_dma_free (tdp->dev, tdp->size, vaddr, addr); +} + +static void *tmio_buffer_alloc (struct usb_bus *bus, size_t size, + gfp_t mem_flags, dma_addr_t *dma) +{ + return tmio_dma_alloc (bus->controller, size, dma, mem_flags); +} + +static void tmio_buffer_free (struct usb_bus *bus, size_t size, + void *addr, dma_addr_t dma) +{ + tmio_dma_free (bus->controller, size, addr, dma); +} + +/*-------------------------------------------------------------------------*/ + +static void tmio_hc_stop (struct usb_hcd *hcd) +{ + struct device* dev = hcd->self.controller; + struct tmio_device* tdev = dev_to_tdev (dev); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + struct tmio_uhccr __iomem* ccr = tmio->ccr; + union tmio_uhccr_pm pm = {0}; + + pm.gcken = 1; + pm.ckrnen = 1; + pm.uspw1 = 1; + pm.uspw2 = 1; + + iowrite8 (0, &ccr->intc); + iowrite8 (0, &ccr->ilme); + iowrite16(0, &ccr->basel); + iowrite16(0, &ccr->baseh); + iowrite16(pm.raw, &ccr->pm); + + tdev->ops->function (dev, 0); + tdev->ops->clock (dev, 0); +} + +static void tmio_hc_start (struct usb_hcd *hcd) +{ + struct device* dev = hcd->self.controller; + struct tmio_device* tdev = dev_to_tdev (dev); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + struct tmio_uhccr __iomem* ccr = tmio->ccr; + union tmio_uhccr_pm pm = {0}; + + pm.pmes = 1; + pm.pmee = 1; + pm.ckrnen = 1; + pm.gcken = 1; + + tdev->ops->clock (dev, 1); + tdev->ops->function (dev, 1); + + iowrite16(pm.raw, &ccr->pm); + iowrite16(hcd->rsrc_start, &ccr->basel); + iowrite16(hcd->rsrc_start >> 16, &ccr->baseh); + iowrite8 (1, &ccr->ilme); + iowrite8 (2, &ccr->intc); + + consistent_sync (tmio->sram, tmio->sram_len, DMA_BIDIRECTIONAL); + + dev_info (dev, "revision %d @ 0x%08llx, irq %d\n", + ioread8 (&ccr->revid), hcd->rsrc_start, hcd->irq); +} + +static void tmio_stop (struct usb_hcd *hcd) +{ + struct ohci_hcd* ohci = hcd_to_ohci (hcd); + + /* NULL these so ohci_stop() doesn't try to free them */ + ohci->hcca = NULL; + ohci->td_cache = NULL; + ohci->ed_cache = NULL; + + ohci_stop (hcd); + tmio_hc_stop (hcd); + tmio_pool_destroy (hcd_to_tmio (hcd)); + + /* We don't free the hcca because tmio_hc_stop() turns off + * the sram and the memory allocation data is destroyed. */ +} + +static int tmio_start (struct usb_hcd *hcd) +{ + struct device* dev = hcd->self.controller; + struct ohci_hcd* ohci = hcd_to_ohci (hcd); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + int retval; + + tmio_hc_start (hcd); + + tmio->poolp = gen_pool_create(0, fls(tmio->sram_len) - 1, + tmio_pool_callback, 0); + if (!tmio->poolp) { + retval = -ENOMEM; + goto err_gen_pool_create; + } + + gen_pool_free (tmio->poolp, (unsigned long)(tmio->sram), + tmio->sram_len); + + ohci->hcca = tmio_dma_alloc (dev, sizeof *ohci->hcca, + &ohci->hcca_dma, GFP_KERNEL); + if (!ohci->hcca) { + retval = -ENOMEM; + goto err_hcca_alloc; + } + + /* for our dma_pool_alloc/free hooks */ + ohci->td_cache = (struct dma_pool*) &tmio->td_pool; + ohci->ed_cache = (struct dma_pool*) &tmio->ed_pool; + + if ((retval = ohci_init (ohci)) < 0) + goto err_ohci_init; + + if ((retval = ohci_run (ohci)) < 0) + goto err_ohci_run; + + return 0; + +err_ohci_run: + err ("can't start %s", hcd->self.bus_name); +err_ohci_init: +err_hcca_alloc: +err_gen_pool_create: + tmio_stop (hcd); + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static inline void *tmio_urb_dma_to_virt(struct urb *urb, dma_addr_t dma) +{ + if (BOUNDED_XYL(dma, urb->transfer_dma, urb->transfer_buffer_length)) + return urb->transfer_buffer + dma - urb->transfer_dma; + + if (BOUNDED_XYL(dma, urb->setup_dma, sizeof (struct usb_ctrlrequest))) + return urb->setup_packet + dma - urb->setup_dma; + + return NULL; +} + +static struct tmio_td* tmio_td_find (struct td *td, int *index) +{ + struct urb* urb = td->urb; + urb_priv_t* urb_priv = urb->hcpriv; + struct tmio_urb* turb = urb_to_turb (urb); + int i; + + for (i=0; i < urb_priv->length; i++) + if (urb_priv->td[i] == td) { + *index = i; + return turb->td + i; + } + + return NULL; +} + +/* + * map the td's data to dma-able memory + * + * if this td transfers data, + * sets tmtd->data to the urb's data buffer + * sets tmtd->dma to dma-able memory + * sets tmtd->bounce to non-NULL if a bounce buffer is allocated + * copies the urb's data buffer to the bounce buffer if necessary + */ +static int tmio_td_dma_map (struct ohci_hcd *ohci, struct urb *urb, + struct tmio_td *tmtd, int idx) +{ + struct usb_hcd* hcd = ohci_to_hcd (ohci); + struct device* dev = hcd->self.controller; + dma_addr_t dma; + + if (!tmtd->len) + return 0; + + if (tmio_dma_to_virt (hcd, tmtd->dma)) + return 0; + + tmtd->data = tmio_urb_dma_to_virt (urb, tmtd->dma); + if (unlikely (!tmtd->data)) { + dev_err (dev, "TD has bad dma address 0x%08x\n", tmtd->dma); + return 0; + } + + tmtd->bounce = tmio_dma_alloc (dev, tmtd->len, &dma, GFP_ATOMIC); + if (!tmtd->bounce) + return -ENOMEM; + + if ((usb_pipecontrol (urb->pipe) && !idx) || usb_pipeout (urb->pipe)) { + consistent_sync (tmtd->bounce, tmtd->len, DMA_TO_DEVICE); + memcpy (tmtd->bounce, tmtd->data, tmtd->len); + } else + consistent_sync (tmtd->bounce, tmtd->len, DMA_FROM_DEVICE); + + tmtd->dma = dma; + return 0; +} + +/* + * unmaps the td's data from dma-able memory + * + * if a bounce buffer has been allocated, + * copy the bounce buffer to the urb's data buffer if necessary + * free the bounce buffer + */ +static void tmio_td_dma_unmap (struct ohci_hcd *ohci, struct td *td) +{ + struct device* dev = (ohci_to_hcd (ohci))->self.controller; + struct urb* urb = td->urb; + int idx; + struct tmio_td* tmtd = tmio_td_find (td, &idx); + + if (!tmtd->bounce) + return; + + if (usb_pipein (urb->pipe) && (usb_pipecontrol (urb->pipe) ? idx : 1)) { + memcpy (tmtd->data, tmtd->bounce, tmtd->len); + consistent_sync (tmtd->data, tmtd->len, DMA_TO_DEVICE); + } + + tmio_dma_free (dev, tmtd->len, tmtd->bounce, tmtd->dma); +} + +static int tmio_urb_runqueue (struct ohci_hcd *ohci, struct urb *urb) +{ + struct tmio_urb* turb = urb_to_turb (urb); + urb_priv_t* urb_priv= urb->hcpriv; + int start = turb->td_queue; + int retval = 0; + int i; + + for (i = start; i < turb->td_add; i = ++turb->td_queue) { + struct tmio_td *tmtd = turb->td + i; + + if ((retval = tmio_td_dma_map (ohci, urb, tmtd, i))) + break; + + td_fill (ohci, tmtd->info, tmtd->dma, tmtd->len, urb, i); + } + + if (i <= start) + return retval; + + /* kickstart the appropriate list */ + wmb (); + switch (urb_priv->ed->type) { + case PIPE_BULK: + ohci_writel (ohci, OHCI_BLF, &ohci->regs->cmdstatus); + break; + case PIPE_CONTROL: + ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus); + break; + } + + return retval; +} + +/* + * This needs to be called with ohci->lock held so the pending urb list + * isn't modified. + */ +static int tmio_ohci_runqueue (struct ohci_hcd *ohci) +{ + urb_priv_t* priv; + int retval = 0; + + list_for_each_entry_reverse (priv, &ohci->pending, pending) + if ((retval = tmio_urb_runqueue (ohci, priv->td[0]->urb))) + return retval; + + return retval; +} + +static void tmio_td_fill (struct ohci_hcd *ohci, u32 info, + dma_addr_t data, int len, struct urb *urb, int index) +{ + struct tmio_urb* turb = urb_to_turb (urb); + struct tmio_td* tmtd = turb->td + index; + + tmtd->data = NULL; + tmtd->bounce = NULL; + tmtd->dma = data; + tmtd->len = len; + tmtd->info = info; + turb->td_add = index + 1; +} + +static void +tmio_td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) +{ + tmio_td_dma_unmap (ohci, td); + td_done (ohci, urb, td); +} + +const static struct ohci_ops tmio_ops = { + .dma_pool_alloc = tmio_dma_pool_alloc, + .dma_pool_free = tmio_dma_pool_free, + .td_fill = tmio_td_fill, + .td_done = tmio_td_done, +}; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t tmio_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) +{ + irqreturn_t retval = ohci_irq (hcd, ptregs); + + if (retval == IRQ_HANDLED) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + tmio_ohci_runqueue (ohci); + spin_unlock_irqrestore (&ohci->lock, flags); + } + + return retval; +} + +/* + * This is ohci_urb_enqueue with: + * dma address sanitization for tmio_urb_dma_to_virt() + * allocate extra space in urb_priv for our private data + * initialize urb_priv->td[0]->urb for tmio_ohci_runqueue() + * call tmio_ohci_runqueue() after submitting TDs + */ +static int tmio_urb_enqueue ( + struct usb_hcd *hcd, + struct usb_host_endpoint *ep, + struct urb *urb, + gfp_t mem_flags +) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct ed *ed; + urb_priv_t *urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + unsigned long flags; + int retval = 0; + +#ifdef OHCI_VERBOSE_DEBUG + urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + /* make sure we can convert dma offsets back to virtual addresses */ + if (!tmio_dma_to_virt (hcd, urb->setup_dma)) + urb->setup_dma = 0; + + if (!tmio_dma_to_virt (hcd, urb->transfer_dma)) + urb->transfer_dma = sizeof (struct usb_ctrlrequest); + + /* every endpoint has a ed, locate and maybe (re)initialize it */ + if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval))) + return -ENOMEM; + + /* for the private part of the URB we need the number of TDs (size) */ + switch (ed->type) { + case PIPE_CONTROL: + /* td_submit_urb() doesn't yet handle these */ + if (urb->transfer_buffer_length > 4096) + return -EMSGSIZE; + + /* 1 TD for setup, 1 for ACK, plus ... */ + size = 2; + /* FALLTHROUGH */ + // case PIPE_INTERRUPT: + // case PIPE_BULK: + default: + /* one TD for every 4096 Bytes (can be upto 8K) */ + size += urb->transfer_buffer_length / 4096; + /* ... and for any remaining bytes ... */ + if ((urb->transfer_buffer_length % 4096) != 0) + size++; + /* ... and maybe a zero length packet to wrap it up */ + if (size == 0) + size++; + else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 + && (urb->transfer_buffer_length + % usb_maxpacket (urb->dev, pipe, + usb_pipeout (pipe))) == 0) + size++; + break; + case PIPE_ISOCHRONOUS: /* number of packets from URB */ + size = urb->number_of_packets; + break; + } + + /* allocate the private part of the URB */ + urb_priv = kzalloc (sizeof (urb_priv_t) + + size * sizeof (struct td*) + + sizeof (struct tmio_urb) + + size * sizeof (struct tmio_td), + mem_flags); + if (!urb_priv) + return -ENOMEM; + INIT_LIST_HEAD (&urb_priv->pending); + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (deferring hash chain updates) */ + for (i = 0; i < size; i++) { + urb_priv->td [i] = td_alloc (ohci, mem_flags); + if (!urb_priv->td [i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + return -ENOMEM; + } + urb_priv->td [i]->urb = urb; + } + + spin_lock_irqsave (&ohci->lock, flags); + + /* don't submit to a dead HC */ + if (!HC_IS_RUNNING(hcd->state)) { + retval = -ENODEV; + goto fail; + } + + /* in case of unlink-during-submit */ + spin_lock (&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock (&urb->lock); + urb->hcpriv = urb_priv; + finish_urb (ohci, urb, NULL); + retval = 0; + goto fail; + } + + /* schedule the ed if needed */ + if (ed->state == ED_IDLE) { + retval = ed_schedule (ohci, ed); + if (retval < 0) + goto fail0; + if (ed->type == PIPE_ISOCHRONOUS) { + u16 frame = ohci_frame_no(ohci); + + /* delay a few frames before the first TD */ + frame += max_t (u16, 8, ed->interval); + frame &= ~(ed->interval - 1); + frame |= ed->branch; + urb->start_frame = frame; + + /* yes, only URB_ISO_ASAP is supported, and + * urb->start_frame is never used as input. + */ + } + } else if (ed->type == PIPE_ISOCHRONOUS) + urb->start_frame = ed->last_iso + ed->interval; + + /* fill the TDs and link them to the ed; and + * enable that part of the schedule, if needed + * and update count of queued periodic urbs + */ + urb->hcpriv = urb_priv; + td_submit_urb (ohci, urb); + tmio_ohci_runqueue (ohci); + +fail0: + spin_unlock (&urb->lock); +fail: + if (retval) + urb_free_priv (ohci, urb_priv); + spin_unlock_irqrestore (&ohci->lock, flags); + return retval; +} + +static const struct hc_driver tmio_hc_driver = { + .description = hcd_name, + .product_desc = "TMIO OHCI USB Host Controller", + .hcd_priv_size = sizeof (struct ohci_hcd) + + sizeof (struct tmio_hcd), + + /* generic hardware linkage */ + .irq = tmio_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* basic lifecycle operations */ + .start = tmio_start, + .stop = tmio_stop, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = tmio_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = ohci_get_frame, + + /* root hub support */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * tmio_probe - initialize TMIO-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int tmio_probe (struct device *dev) +{ + struct tmio_device* tdev = dev_to_tdev (dev); + struct resource* config = tmio_resource_config (dev); + struct resource* regs = tmio_resource_control (dev); + struct resource* sram = tmio_resource_mem (dev); + struct resource* irq = tmio_resource_irq (dev); + struct usb_operations* ops; + struct tmio_hcd* tmio; + struct ohci_hcd* ohci; + struct usb_hcd* hcd; + int retval; + + if (usb_disabled ()) + return -ENODEV; + + if (dev->dma_mask || dev->coherent_dma_mask) { + dev_err (dev, "DMA not supported\n"); + return -ENODEV; + } + + hcd = usb_create_hcd (&tmio_hc_driver, dev, dev->bus_id); + if (!hcd) { + retval = -ENOMEM; + goto err_create_hcd; + } + + tmio = hcd_to_tmio (hcd); + tmio->td_pool.dev = dev; + tmio->ed_pool.dev = dev; + tmio->td_pool.size = sizeof (struct td); + tmio->ed_pool.size = sizeof (struct ed); + ohci = hcd_to_ohci (hcd); + ohci_hcd_init (ohci); + ohci->ops = &tmio_ops; + + retval = request_resource (tdev->iomem, config); + if (retval) + goto err_request_config_resource; + + retval = request_resource (tdev->iomem, regs); + if (retval) + goto err_request_regs_resource; + + retval = request_resource (tdev->iomem, sram); + if (retval) + goto err_request_sram_resource; + + hcd->rsrc_start = regs->start; + hcd->rsrc_len = regs->end - regs->start + 1; + tmio->sram_len = sram->end - sram->start + 1; + + tmio->ccr = ioremap (config->start, config->end - config->start + 1); + if (!tmio->ccr) { + retval = -ENOMEM; + goto err_ioremap_ccr; + } + + hcd->regs = ioremap (hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + retval = -ENOMEM; + goto err_ioremap_regs; + } + + tmio->sram = ioremap (sram->start, tmio->sram_len); + if (!tmio->sram) { + retval = -ENOMEM; + goto err_ioremap_sram; + } + + /* drivers should use our coherent buffer allocator */ + ops = &tmio->ops; + memcpy (ops, hcd->self.op, sizeof *ops); + ops->buffer_alloc = tmio_buffer_alloc; + ops->buffer_free = tmio_buffer_free; + hcd->self.op = ops; + + retval = usb_add_hcd (hcd, irq->start, SA_INTERRUPT); + if (retval) + goto err_usb_add_hcd; + + return 0; + +err_usb_add_hcd: + iounmap (tmio->sram); +err_ioremap_sram: + iounmap (hcd->regs); +err_ioremap_regs: + iounmap (tmio->ccr); +err_ioremap_ccr: + release_resource (sram); +err_request_sram_resource: + release_resource (regs); +err_request_regs_resource: + release_resource (config); +err_request_config_resource: + usb_put_hcd (hcd); +err_create_hcd: + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * tmio_remove - shutdown processing for TMIO-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of tmio_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static int tmio_remove (struct device *dev) +{ + struct usb_hcd* hcd = dev_get_drvdata (dev); + struct tmio_hcd* tmio = hcd_to_tmio (hcd); + + usb_remove_hcd (hcd); + iounmap (tmio->sram); + iounmap (hcd->regs); + iounmap (tmio->ccr); + release_resource (tmio_resource_mem (dev)); + release_resource (tmio_resource_control (dev)); + release_resource (tmio_resource_config (dev)); + usb_put_hcd (hcd); + return 0; +} + +static struct device_driver tmio_ohci = { + .name = TMIO_NAME_OHCI, + .bus = &tmio_bus_type, + .probe = tmio_probe, + .remove = tmio_remove, +}; + +static int __init tmio_init (void) +{ + dbg (DRIVER_INFO " (%s)", TMIO_SOC_NAME); + dbg ("block sizes: ed %d td %d", + sizeof (struct ed), sizeof (struct td)); + + return driver_register (&tmio_ohci); +} + +static void __exit tmio_exit (void) +{ + driver_unregister (&tmio_ohci); +} + +module_init (tmio_init); +module_exit (tmio_exit); Index: linux-2.6.18/drivers/usb/host/Kconfig =================================================================== --- linux-2.6.18.orig/drivers/usb/host/Kconfig 2006-09-20 05:42:06.000000000 +0200 +++ linux-2.6.18/drivers/usb/host/Kconfig 2006-09-20 16:18:08.000000000 +0200 @@ -83,6 +83,7 @@ tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select GENERIC_ALLOCATOR if TOSHIBA_TC6393XB ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's Index: linux-2.6.18/drivers/usb/host/ohci-hcd.c =================================================================== --- linux-2.6.18.orig/drivers/usb/host/ohci-hcd.c 2006-09-20 05:42:06.000000000 +0200 +++ linux-2.6.18/drivers/usb/host/ohci-hcd.c 2006-09-20 16:18:54.000000000 +0200 @@ -923,6 +923,7 @@ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ || defined (CONFIG_PXA27x) \ + || defined (CONFIG_TOSHIBA_TC6393XB) \ || defined (CONFIG_ARCH_EP93XX) \ || defined (CONFIG_SOC_AU1X00) \ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \