X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fsl811-hcd.c;h=a92343052751c06b57cd99dbc50ed607e17b5eae;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=c932c53ac01db402374794381ecdfecfeade10e8;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index c932c53ac..a92343052 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -2,8 +2,8 @@ * SL811HS HCD (Host Controller Driver) for USB. * * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) - * Copyright (C) 2004 David Brownell - * + * Copyright (C) 2004-2005 David Brownell + * * Periodic scheduling is based on Roman's OHCI code * Copyright (C) 1999 Roman Weissgaerber * @@ -15,7 +15,7 @@ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" * document (providing significant pieces missing from that spec); plus * the SL811S spec if you want peripheral side info. - */ + */ /* * Status: Passed basic stress testing, works with hubs, mice, keyboards, @@ -32,13 +32,6 @@ #undef PACKET_TRACE #include - -#ifdef CONFIG_USB_DEBUG -# define DEBUG -#else -# undef DEBUG -#endif - #include #include #include @@ -54,6 +47,7 @@ #include #include #include +#include #include #include @@ -67,7 +61,7 @@ MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION "15 Dec 2004" +#define DRIVER_VERSION "19 May 2005" #ifndef DEBUG @@ -90,8 +84,6 @@ static const char hcd_name[] = "sl811-hcd"; /*-------------------------------------------------------------------------*/ -static irqreturn_t sl811h_irq(int irq, void *_hcd, struct pt_regs *regs); - static void port_power(struct sl811 *sl811, int is_on) { struct usb_hcd *hcd = sl811_to_hcd(sl811); @@ -103,12 +95,12 @@ static void port_power(struct sl811 *sl811, int is_on) sl811->port1 = (1 << USB_PORT_FEAT_POWER); sl811->irq_enable = SL11H_INTMASK_INSRMV; - hcd->self.controller->power.power_state = PM_SUSPEND_ON; + hcd->self.controller->power.power_state = PMSG_ON; } else { sl811->port1 = 0; sl811->irq_enable = 0; - hcd->state = USB_STATE_HALT; - hcd->self.controller->power.power_state = PM_SUSPEND_DISK; + hcd->state = HC_STATE_HALT; + hcd->self.controller->power.power_state = PMSG_SUSPEND; } sl811->ctrl1 = 0; sl811_write(sl811, SL11H_IRQ_ENABLE, 0); @@ -123,6 +115,10 @@ static void port_power(struct sl811 *sl811, int is_on) /* reset as thoroughly as we can */ if (sl811->board && sl811->board->reset) sl811->board->reset(hcd->self.controller); + else { + sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0); + mdelay(20); + } sl811_write(sl811, SL11H_IRQ_ENABLE, 0); sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); @@ -136,7 +132,7 @@ static void port_power(struct sl811 *sl811, int is_on) /* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, * and may start I/O. Endpoint queues are scanned during completion irq - * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation. + * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation. * * Using an external DMA engine to copy a packet at a time could work, * though setup/teardown costs may be too big to make it worthwhile. @@ -445,6 +441,7 @@ static void finish_request( spin_lock(&urb->lock); if (urb->status == -EINPROGRESS) urb->status = status; + urb->hcpriv = NULL; spin_unlock(&urb->lock); spin_unlock(&sl811->lock); @@ -474,7 +471,7 @@ static void finish_request( if (*prev) *prev = ep->next; sl811->load[i] -= ep->load; - } + } ep->branch = PERIODIC_SIZE; sl811->periodic_count--; sl811_to_hcd(sl811)->self.bandwidth_allocated @@ -645,9 +642,8 @@ static inline u8 checkdone(struct sl811 *sl811) return irqstat; } -static irqreturn_t sl811h_irq(int irq, void *_hcd, struct pt_regs *regs) +static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs) { - struct usb_hcd *hcd = _hcd; struct sl811 *sl811 = hcd_to_sl811(hcd); u8 irqstat; irqreturn_t ret = IRQ_NONE; @@ -664,9 +660,9 @@ retry: #ifdef QUIRK2 /* this may no longer be necessary ... */ - if (irqstat == 0 && ret == IRQ_NONE) { + if (irqstat == 0) { irqstat = checkdone(sl811); - if (irqstat && irq != ~0) + if (irqstat) sl811->stat_lost++; } #endif @@ -725,7 +721,8 @@ retry: if (sl811->active_a) { sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); finish_request(sl811, sl811->active_a, - container_of(sl811->active_a->hep->urb_list.next, + container_of(sl811->active_a + ->hep->urb_list.next, struct urb, urb_list), NULL, -ESHUTDOWN); sl811->active_a = NULL; @@ -734,14 +731,15 @@ retry: if (sl811->active_b) { sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); finish_request(sl811, sl811->active_b, - container_of(sl811->active_b->hep->urb_list.next, + container_of(sl811->active_b + ->hep->urb_list.next, struct urb, urb_list), NULL, -ESHUTDOWN); sl811->active_b = NULL; } #endif - /* port status seems wierd until after reset, so + /* port status seems weird until after reset, so * force the reset and make khubd clean up later. */ sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) @@ -760,12 +758,11 @@ retry: if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) start_transfer(sl811); ret = IRQ_HANDLED; - hcd->saw_irq = 1; if (retries--) goto retry; } - if (sl811->periodic_count == 0 && list_empty(&sl811->async)) + if (sl811->periodic_count == 0 && list_empty(&sl811->async)) sofirq_off(sl811); sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); @@ -779,6 +776,9 @@ retry: /* usb 1.1 says max 90% of a frame is available for periodic transfers. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time. + * + * NOTE: the periodic schedule is a sparse tree, with the load for + * each branch minimized. see fig 3.5 in the OHCI spec for example. */ #define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ @@ -800,7 +800,7 @@ static int balance(struct sl811 *sl811, u16 period, u16 load) } if (j < PERIODIC_SIZE) continue; - branch = i; + branch = i; } } return branch; @@ -812,7 +812,7 @@ static int sl811h_urb_enqueue( struct usb_hcd *hcd, struct usb_host_endpoint *hep, struct urb *urb, - int mem_flags + gfp_t mem_flags ) { struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_device *udev = urb->dev; @@ -832,14 +832,15 @@ static int sl811h_urb_enqueue( /* avoid all allocations within spinlocks */ if (!hep->hcpriv) - ep = kcalloc(1, sizeof *ep, mem_flags); + ep = kzalloc(sizeof *ep, mem_flags); spin_lock_irqsave(&sl811->lock, flags); /* don't submit to a dead or disabled port */ if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) - || !HCD_IS_RUNNING(hcd->state)) { + || !HC_IS_RUNNING(hcd->state)) { retval = -ENODEV; + kfree(ep); goto fail; } @@ -852,7 +853,7 @@ static int sl811h_urb_enqueue( } else { INIT_LIST_HEAD(&ep->schedule); - ep->udev = usb_get_dev(udev); + ep->udev = udev; ep->epnum = epnum; ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; @@ -894,6 +895,7 @@ static int sl811h_urb_enqueue( break; } + ep->hep = hep; hep->hcpriv = ep; } @@ -907,8 +909,16 @@ static int sl811h_urb_enqueue( case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: urb->interval = ep->period; - if (ep->branch < PERIODIC_SIZE) + if (ep->branch < PERIODIC_SIZE) { + /* NOTE: the phase is correct here, but the value + * needs offsetting by the transfer queue depth. + * All current drivers ignore start_frame, so this + * is unlikely to ever matter... + */ + urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + + ep->branch; break; + } retval = balance(sl811, ep->period, ep->load); if (retval < 0) @@ -965,15 +975,16 @@ fail: static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) { struct sl811 *sl811 = hcd_to_sl811(hcd); - struct usb_host_endpoint *hep = urb->hcpriv; + struct usb_host_endpoint *hep; unsigned long flags; struct sl811h_ep *ep; int retval = 0; + spin_lock_irqsave(&sl811->lock, flags); + hep = urb->hcpriv; if (!hep) - return -EINVAL; + goto fail; - spin_lock_irqsave(&sl811->lock, flags); ep = hep->hcpriv; if (ep) { /* finish right away if this urb can't be active ... @@ -1021,6 +1032,7 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) VDBG("dequeue, urb %p active %s; wait4irq\n", urb, (sl811->active_a == ep) ? "A" : "B"); } else +fail: retval = -EINVAL; spin_unlock_irqrestore(&sl811->lock, flags); return retval; @@ -1040,7 +1052,6 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) WARN("ep %p not empty?\n", ep); - usb_put_dev(ep->udev); kfree(ep); hep->hcpriv = NULL; } @@ -1073,7 +1084,7 @@ sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) */ local_irq_save(flags); if (!timer_pending(&sl811->timer)) { - if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE) + if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE) sl811->stat_lost++; } local_irq_restore(flags); @@ -1116,7 +1127,7 @@ sl811h_hub_descriptor ( desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = 1 << 1; + desc->bitmap[0] = 0 << 1; desc->bitmap[1] = ~0; } @@ -1345,7 +1356,7 @@ error: #ifdef CONFIG_PM static int -sl811h_hub_suspend(struct usb_hcd *hcd) +sl811h_bus_suspend(struct usb_hcd *hcd) { // SOFs off DBG("%s\n", __FUNCTION__); @@ -1353,7 +1364,7 @@ sl811h_hub_suspend(struct usb_hcd *hcd) } static int -sl811h_hub_resume(struct usb_hcd *hcd) +sl811h_bus_resume(struct usb_hcd *hcd) { // SOFs on DBG("%s\n", __FUNCTION__); @@ -1362,8 +1373,8 @@ sl811h_hub_resume(struct usb_hcd *hcd) #else -#define sl811h_hub_suspend NULL -#define sl811h_hub_resume NULL +#define sl811h_bus_suspend NULL +#define sl811h_bus_resume NULL #endif @@ -1557,28 +1568,19 @@ static int sl811h_start(struct usb_hcd *hcd) { struct sl811 *sl811 = hcd_to_sl811(hcd); - struct usb_device *udev; /* chip has been reset, VBUS power is off */ + hcd->state = HC_STATE_RUNNING; - udev = usb_alloc_dev(NULL, &hcd->self, 0); - if (!udev) - return -ENOMEM; - - udev->speed = USB_SPEED_FULL; - hcd->state = USB_STATE_RUNNING; - - if (sl811->board) - hcd->can_wakeup = sl811->board->can_wakeup; - - if (hcd_register_root(udev, hcd) != 0) { - usb_put_dev(udev); - sl811h_stop(hcd); - return -ENODEV; + if (sl811->board) { + if (!device_can_wakeup(hcd->self.controller)) + device_init_wakeup(hcd->self.controller, + sl811->board->can_wakeup); + hcd->power_budget = sl811->board->power * 2; } - if (sl811->board && sl811->board->power) - hub_set_power_budget(udev, sl811->board->power * 2); + /* enable power and interupts */ + port_power(sl811, 1); return 0; } @@ -1592,7 +1594,12 @@ static struct hc_driver sl811h_hc_driver = { /* * generic hardware linkage */ - .flags = HCD_USB11, + .irq = sl811h_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* Basic lifecycle operations */ + .start = sl811h_start, + .stop = sl811h_stop, /* * managing i/o requests and associated device resources @@ -1611,118 +1618,101 @@ static struct hc_driver sl811h_hc_driver = { */ .hub_status_data = sl811h_hub_status_data, .hub_control = sl811h_hub_control, - .hub_suspend = sl811h_hub_suspend, - .hub_resume = sl811h_hub_resume, + .bus_suspend = sl811h_bus_suspend, + .bus_resume = sl811h_bus_resume, }; /*-------------------------------------------------------------------------*/ -static int __init_or_module -sl811h_remove(struct device *dev) +static int __devexit +sl811h_remove(struct platform_device *dev) { - struct sl811 *sl811 = dev_get_drvdata(dev); - struct usb_hcd *hcd = sl811_to_hcd(sl811); - struct platform_device *pdev; + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct sl811 *sl811 = hcd_to_sl811(hcd); struct resource *res; - pdev = container_of(dev, struct platform_device, dev); - - if (HCD_IS_RUNNING(hcd->state)) - hcd->state = USB_STATE_QUIESCING; - - usb_disconnect(&hcd->self.root_hub); remove_debug_file(sl811); - sl811h_stop(hcd); - - usb_deregister_bus(&hcd->self); - - free_irq(hcd->irq, hcd); + usb_remove_hcd(hcd); - iounmap(sl811->data_reg); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - release_mem_region(res->start, 1); + /* some platforms may use IORESOURCE_IO */ + res = platform_get_resource(dev, IORESOURCE_MEM, 1); + if (res) + iounmap(sl811->data_reg); - iounmap(sl811->addr_reg); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, 1); + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res) + iounmap(sl811->addr_reg); usb_put_hcd(hcd); return 0; } -#define resource_len(r) (((r)->end - (r)->start) + 1) - -static int __init -sl811h_probe(struct device *dev) +static int __devinit +sl811h_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct sl811 *sl811; - struct platform_device *pdev; struct resource *addr, *data; int irq; void __iomem *addr_reg; void __iomem *data_reg; int retval; - u8 tmp; + u8 tmp, ioaddr = 0; /* basic sanity checks first. board-specific init logic should * have initialized these three resources and probably board * specific platform_data. we don't probe for IRQs, and do only * minimal sanity checking. */ - pdev = container_of(dev, struct platform_device, dev); - if (pdev->num_resources < 3) - return -ENODEV; - - addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq = platform_get_irq(pdev, 0); - if (!addr || !data || irq < 0) + irq = platform_get_irq(dev, 0); + if (dev->num_resources < 3 || irq < 0) return -ENODEV; /* refuse to confuse usbcore */ - if (dev->dma_mask) { + if (dev->dev.dma_mask) { DBG("no we won't dma\n"); return -EINVAL; } - if (!request_mem_region(addr->start, 1, hcd_name)) { - retval = -EBUSY; - goto err1; - } - addr_reg = ioremap(addr->start, resource_len(addr)); - if (addr_reg == NULL) { - retval = -ENOMEM; - goto err2; - } + /* the chip may be wired for either kind of addressing */ + addr = platform_get_resource(dev, IORESOURCE_MEM, 0); + data = platform_get_resource(dev, IORESOURCE_MEM, 1); + retval = -EBUSY; + if (!addr || !data) { + addr = platform_get_resource(dev, IORESOURCE_IO, 0); + data = platform_get_resource(dev, IORESOURCE_IO, 1); + if (!addr || !data) + return -ENODEV; + ioaddr = 1; + + addr_reg = (void __iomem *) addr->start; + data_reg = (void __iomem *) data->start; + } else { + addr_reg = ioremap(addr->start, 1); + if (addr_reg == NULL) { + retval = -ENOMEM; + goto err2; + } - if (!request_mem_region(data->start, 1, hcd_name)) { - retval = -EBUSY; - goto err3; - } - data_reg = ioremap(data->start, resource_len(addr)); - if (data_reg == NULL) { - retval = -ENOMEM; - goto err4; + data_reg = ioremap(data->start, 1); + if (data_reg == NULL) { + retval = -ENOMEM; + goto err4; + } } /* allocate and initialize hcd */ - hcd = usb_create_hcd(&sl811h_hc_driver); + hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev->dev.bus_id); if (!hcd) { - retval = 0; + retval = -ENOMEM; goto err5; } + hcd->rsrc_start = addr->start; sl811 = hcd_to_sl811(hcd); - dev_set_drvdata(dev, sl811); - - hcd->self.controller = dev; - hcd->self.bus_name = dev->bus_id; - hcd->irq = irq; - hcd->regs = addr_reg; spin_lock_init(&sl811->lock); INIT_LIST_HEAD(&sl811->async); - sl811->board = dev->platform_data; + sl811->board = dev->dev.platform_data; init_timer(&sl811->timer); sl811->timer.function = sl811h_timer; sl811->timer.data = (unsigned long) sl811; @@ -1749,44 +1739,29 @@ sl811h_probe(struct device *dev) goto err6; } - /* sl811s would need a different handler for this irq */ -#ifdef CONFIG_ARM - /* Cypress docs say the IRQ is IRQT_HIGH ... */ - set_irq_type(irq, IRQT_RISING); -#endif - retval = request_irq(irq, sl811h_irq, SA_INTERRUPT, - hcd->driver->description, hcd); + /* The chip's IRQ is level triggered, active high. A requirement + * for platform device setup is to cope with things like signal + * inverters (e.g. CF is active low) or working only with edge + * triggers (e.g. most ARM CPUs). Initial driver stress testing + * was on a system with single edge triggering, so most sorts of + * triggering arrangement should work. + */ + retval = usb_add_hcd(hcd, irq, SA_INTERRUPT | SA_SHIRQ); if (retval != 0) goto err6; - INFO("%s, irq %d\n", hcd->product_desc, irq); - - retval = usb_register_bus(&hcd->self); - if (retval < 0) - goto err7; - - retval = sl811h_start(hcd); - if (retval < 0) - goto err8; - create_debug_file(sl811); - return 0; + return retval; - err8: - usb_deregister_bus(&hcd->self); - err7: - free_irq(hcd->irq, hcd); err6: usb_put_hcd(hcd); err5: - iounmap(data_reg); + if (!ioaddr) + iounmap(data_reg); err4: - release_mem_region(data->start, 1); - err3: - iounmap(addr_reg); + if (!ioaddr) + iounmap(addr_reg); err2: - release_mem_region(addr->start, 1); - err1: DBG("init error, %d\n", retval); return retval; } @@ -1794,48 +1769,45 @@ sl811h_probe(struct device *dev) #ifdef CONFIG_PM /* for this device there's no useful distinction between the controller - * and its root hub, except that the root hub only gets direct PM calls + * and its root hub, except that the root hub only gets direct PM calls * when CONFIG_USB_SUSPEND is enabled. */ static int -sl811h_suspend(struct device *dev, u32 state, u32 phase) +sl811h_suspend(struct platform_device *dev, pm_message_t state) { - struct sl811 *sl811 = dev_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct sl811 *sl811 = hcd_to_sl811(hcd); int retval = 0; - if (phase != SUSPEND_POWER_DOWN) - return retval; - - if (state <= PM_SUSPEND_MEM) - retval = sl811h_hub_suspend(sl811_to_hcd(sl811)); - else + if (state.event == PM_EVENT_FREEZE) + retval = sl811h_bus_suspend(hcd); + else if (state.event == PM_EVENT_SUSPEND) port_power(sl811, 0); if (retval == 0) - dev->power.power_state = state; + dev->dev.power.power_state = state; return retval; } static int -sl811h_resume(struct device *dev, u32 phase) +sl811h_resume(struct platform_device *dev) { - struct sl811 *sl811 = dev_get_drvdata(dev); - - if (phase != RESUME_POWER_ON) - return 0; + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct sl811 *sl811 = hcd_to_sl811(hcd); /* with no "check to see if VBUS is still powered" board hook, * let's assume it'd only be powered to enable remote wakeup. */ - if (dev->power.power_state > PM_SUSPEND_MEM - || !sl811_to_hcd(sl811)->can_wakeup) { + if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND + || !device_can_wakeup(&hcd->self.root_hub->dev)) { sl811->port1 = 0; port_power(sl811, 1); + usb_root_hub_lost_power(hcd->self.root_hub); return 0; } - dev->power.power_state = PM_SUSPEND_ON; - return sl811h_hub_resume(sl811_to_hcd(sl811)); + dev->dev.power.power_state = PMSG_ON; + return sl811h_bus_resume(hcd); } #else @@ -1846,31 +1818,34 @@ sl811h_resume(struct device *dev, u32 phase) #endif -static struct device_driver sl811h_driver = { - .name = (char *) hcd_name, - .bus = &platform_bus_type, - +/* this driver is exported so sl811_cs can depend on it */ +struct platform_driver sl811h_driver = { .probe = sl811h_probe, - .remove = sl811h_remove, + .remove = __devexit_p(sl811h_remove), .suspend = sl811h_suspend, .resume = sl811h_resume, + .driver = { + .name = (char *) hcd_name, + .owner = THIS_MODULE, + }, }; +EXPORT_SYMBOL(sl811h_driver); /*-------------------------------------------------------------------------*/ - -static int __init sl811h_init(void) + +static int __init sl811h_init(void) { if (usb_disabled()) return -ENODEV; INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION); - return driver_register(&sl811h_driver); + return platform_driver_register(&sl811h_driver); } module_init(sl811h_init); -static void __exit sl811h_cleanup(void) -{ - driver_unregister(&sl811h_driver); +static void __exit sl811h_cleanup(void) +{ + platform_driver_unregister(&sl811h_driver); } module_exit(sl811h_cleanup);