X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fgadget%2Fpxa2xx_udc.c;h=fff027d30a09d2e564f842baa4eba11a90a80ad2;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=710f7a435f9eccb6c2fdefac6d8035a11e108228;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 710f7a435..fff027d30 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -1,6 +1,6 @@ /* * linux/drivers/usb/gadget/pxa2xx_udc.c - * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers + * Intel PXA25x and IXP4xx on-chip full speed USB device controllers * * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) * Copyright (C) 2003 Robert Schwebel, Pengutronix @@ -27,12 +27,10 @@ #undef DEBUG // #define VERBOSE DBG_VERBOSE -#include #include #include #include #include -#include #include #include #include @@ -43,7 +41,7 @@ #include #include #include -#include +#include #include #include @@ -54,7 +52,9 @@ #include #include #include +#ifdef CONFIG_ARCH_PXA #include +#endif #include #include @@ -63,7 +63,7 @@ /* - * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx + * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x * series processors. The UDC for the IXP 4xx series is very similar. * There are fifteen endpoints, in addition to ep0. * @@ -79,8 +79,8 @@ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. */ -#define DRIVER_VERSION "14-Dec-2003" -#define DRIVER_DESC "PXA 2xx USB Device Controller driver" +#define DRIVER_VERSION "4-May-2005" +#define DRIVER_DESC "PXA 25x USB Device Controller driver" static const char driver_name [] = "pxa2xx_udc"; @@ -290,6 +290,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, static int pxa2xx_ep_disable (struct usb_ep *_ep) { struct pxa2xx_ep *ep; + unsigned long flags; ep = container_of (_ep, struct pxa2xx_ep, ep); if (!_ep || !ep->desc) { @@ -297,6 +298,8 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) _ep ? ep->ep.name : NULL); return -EINVAL; } + local_irq_save(flags); + nuke (ep, -ESHUTDOWN); #ifdef USE_DMA @@ -310,9 +313,10 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) /* flush fifo (mostly for IN buffers) */ pxa2xx_ep_fifo_flush (_ep); - ep->desc = 0; + ep->desc = NULL; ep->stopped = 1; + local_irq_restore(flags); DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); return 0; } @@ -328,15 +332,14 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) * pxa2xx_ep_alloc_request - allocate a request data structure */ static struct usb_request * -pxa2xx_ep_alloc_request (struct usb_ep *_ep, int gfp_flags) +pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) { struct pxa2xx_request *req; - req = kmalloc (sizeof *req, gfp_flags); + req = kzalloc(sizeof(*req), gfp_flags); if (!req) - return 0; + return NULL; - memset (req, 0, sizeof *req); INIT_LIST_HEAD (&req->queue); return &req->req; } @@ -363,13 +366,17 @@ pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) */ static void * pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, int gfp_flags) + dma_addr_t *dma, gfp_t gfp_flags) { char *retval; retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); if (retval) +#ifdef USE_DMA *dma = virt_to_bus (retval); +#else + *dma = (dma_addr_t)~0; +#endif return retval; } @@ -411,7 +418,6 @@ static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status) static inline void ep0_idle (struct pxa2xx_udc *dev) { dev->ep0state = EP0_IDLE; - LED_EP0_OFF; } static int @@ -540,6 +546,7 @@ write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) count = req->req.length; done (ep, req, 0); ep0_idle(ep->dev); +#ifndef CONFIG_ARCH_IXP4XX #if 1 /* This seems to get rid of lost status irqs in some cases: * host responds quickly, or next request involves config @@ -559,6 +566,7 @@ write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) udelay(1); } while (count); } +#endif #endif } else if (ep->dev->req_pending) ep0start(ep->dev, 0, "IN"); @@ -867,7 +875,7 @@ done: /*-------------------------------------------------------------------------*/ static int -pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) +pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct pxa2xx_request *req; struct pxa2xx_ep *ep; @@ -930,7 +938,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) case EP0_IN_DATA_PHASE: dev->stats.write.ops++; if (write_ep0_fifo(ep, req)) - req = 0; + req = NULL; break; case EP0_OUT_DATA_PHASE: @@ -940,9 +948,11 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) DBG(DBG_VERBOSE, "ep0 config ack%s\n", dev->has_cfr ? "" : " raced"); if (dev->has_cfr) - UDCCFR = UDCCFR_AREN|UDCCFR_ACM; + UDCCFR = UDCCFR_AREN|UDCCFR_ACM + |UDCCFR_MB1; done(ep, req, 0); dev->ep0state = EP0_END_XFER; + local_irq_restore (flags); return 0; } if (dev->req_pending) @@ -951,7 +961,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) && read_ep0_fifo(ep, req))) { ep0_idle(dev); done(ep, req, 0); - req = 0; + req = NULL; } break; @@ -966,13 +976,13 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) kick_dma(ep, req); #endif /* can the FIFO can satisfy the request immediately? */ - } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 - && (*ep->reg_udccs & UDCCS_BI_TFS) != 0 - && write_fifo(ep, req)) { - req = 0; + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 + && write_fifo(ep, req)) + req = NULL; } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 && read_fifo(ep, req)) { - req = 0; + req = NULL; } if (likely (req && ep->desc) && ep->dma < 0) @@ -1093,7 +1103,6 @@ static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value) start_watchdog(ep->dev); ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; - LED_EP0_OFF; /* and bulk/intr endpoints like dropping stalls too */ } else { @@ -1193,13 +1202,71 @@ static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget) return 0; } +static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *); +static void udc_enable (struct pxa2xx_udc *); +static void udc_disable(struct pxa2xx_udc *); + +/* We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +static int pullup(struct pxa2xx_udc *udc, int is_active) +{ + is_active = is_active && udc->vbus && udc->pullup; + DMSG("%s\n", is_active ? "active" : "inactive"); + if (is_active) + udc_enable(udc); + else { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + DMSG("disconnect %s\n", udc->driver + ? udc->driver->driver.name + : "(no driver)"); + stop_activity(udc, udc->driver); + } + udc_disable(udc); + } + return 0; +} + +/* VBUS reporting logically comes from a transceiver */ +static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct pxa2xx_udc *udc; + + udc = container_of(_gadget, struct pxa2xx_udc, gadget); + udc->vbus = is_active = (is_active != 0); + DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); + pullup(udc, is_active); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct pxa2xx_udc *udc; + + udc = container_of(_gadget, struct pxa2xx_udc, gadget); + + /* not all boards support pullup control */ + if (!udc->mach->udc_command) + return -EOPNOTSUPP; + + is_active = (is_active != 0); + udc->pullup = is_active; + pullup(udc, is_active); + return 0; +} + static const struct usb_gadget_ops pxa2xx_udc_ops = { - .get_frame = pxa2xx_udc_get_frame, - .wakeup = pxa2xx_udc_wakeup, - // current versions must always be self-powered + .get_frame = pxa2xx_udc_get_frame, + .wakeup = pxa2xx_udc_wakeup, + .vbus_session = pxa2xx_udc_vbus_session, + .pullup = pxa2xx_udc_pullup, + + // .vbus_draw ... boards may consume current from VBUS, up to + // 100-500mA based on config. the 500uA suspend ceiling means + // that exclusively vbus-powered PXA designs violate USB specs. }; - /*-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -1228,7 +1295,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, "%s version: %s\nGadget driver: %s\nHost %s\n\n", driver_name, DRIVER_VERSION SIZE_STR DMASTR, dev->driver ? dev->driver->driver.name : "(none)", - is_usb_connected() ? "full speed" : "disconnected"); + is_vbus_present() ? "full speed" : "disconnected"); size -= t; next += t; @@ -1277,7 +1344,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, next += t; } - if (!is_usb_connected() || !dev->driver) + if (!is_vbus_present() || !dev->driver) goto done; t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", @@ -1367,7 +1434,7 @@ done: /* "function" sysfs attribute */ static ssize_t -show_function (struct device *_dev, char *buf) +show_function (struct device *_dev, struct device_attribute *attr, char *buf) { struct pxa2xx_udc *dev = dev_get_drvdata (_dev); @@ -1392,7 +1459,7 @@ static void udc_disable(struct pxa2xx_udc *dev) UFNRH = UFNRH_SIM; /* if hardware supports it, disconnect from usb */ - make_usb_disappear(); + pullup_off(); udc_clear_mask_UDCCR(UDCCR_UDE); @@ -1426,7 +1493,7 @@ static void udc_reinit(struct pxa2xx_udc *dev) if (i != 0) list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); - ep->desc = 0; + ep->desc = NULL; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = ep->dma_irqs = 0; @@ -1445,6 +1512,7 @@ static void udc_enable (struct pxa2xx_udc *dev) #ifdef CONFIG_ARCH_PXA /* Enable clock for USB device */ pxa_set_cken(CKEN11_USB, 1); + udelay(5); #endif /* try to clear these bits before we enable the udc */ @@ -1468,7 +1536,7 @@ static void udc_enable (struct pxa2xx_udc *dev) /* pxa255 (a0+) can avoid a set_config race that could * prevent gadget drivers from configuring correctly */ - UDCCFR = UDCCFR_ACM; + UDCCFR = UDCCFR_ACM | UDCCFR_MB1; } else { /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) * which could result in missing packets and interrupts. @@ -1497,19 +1565,14 @@ static void udc_enable (struct pxa2xx_udc *dev) } #endif - /* caller must be able to sleep in order to cope - * with startup transients. - */ - msleep(100); - /* enable suspend/resume and reset irqs */ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); /* enable ep0 irqs */ UICR0 &= ~UICR0_IM0; - /* if hardware supports it, connect to usb and wait for host */ - let_usb_appear(); + /* if hardware supports it, pullup D+ and wait for reset */ + pullup_on(); } @@ -1525,7 +1588,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) int retval; if (!driver - || driver->speed != USB_SPEED_FULL + || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->unbind || !driver->disconnect @@ -1539,6 +1602,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) /* first hook up the driver ... */ dev->driver = driver; dev->gadget.dev.driver = &driver->driver; + dev->pullup = 1; device_add (&dev->gadget.dev); retval = driver->bind(&dev->gadget); @@ -1547,18 +1611,17 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) driver->driver.name, retval); device_del (&dev->gadget.dev); - dev->driver = 0; - dev->gadget.dev.driver = 0; + dev->driver = NULL; + dev->gadget.dev.driver = NULL; return retval; } device_create_file(dev->dev, &dev_attr_function); /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. - * NOTE: this shouldn't power up until later. */ DMSG("registered gadget driver '%s'\n", driver->driver.name); - udc_enable(dev); + pullup(dev, 1); dump_state(dev); return 0; } @@ -1571,7 +1634,7 @@ stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) /* don't disconnect drivers more than once */ if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = 0; + driver = NULL; dev->gadget.speed = USB_SPEED_UNKNOWN; /* prevent new request submissions, kill any outstanding requests */ @@ -1602,12 +1665,12 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return -EINVAL; local_irq_disable(); - udc_disable(dev); + pullup(dev, 0); stop_activity(dev, driver); local_irq_enable(); driver->unbind(&dev->gadget); - dev->driver = 0; + dev->driver = NULL; device_del (&dev->gadget.dev); device_remove_file(dev->dev, &dev_attr_function); @@ -1623,61 +1686,41 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); #ifdef CONFIG_ARCH_LUBBOCK -/* Lubbock can report connect or disconnect irqs. Likely more hardware - * could support it as a timer callback. - * - * FIXME for better power management, keep the hardware powered down - * until a host is powering the link. means scheduling work later - * in some task that can udc_enable(). +/* Lubbock has separate connect and disconnect irqs. More typical designs + * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. */ -#define enable_disconnect_irq() \ - if (machine_is_lubbock()) { enable_irq(LUBBOCK_USB_DISC_IRQ); } -#define disable_disconnect_irq() \ - if (machine_is_lubbock()) { disable_irq(LUBBOCK_USB_DISC_IRQ); } - static irqreturn_t -usb_connection_irq(int irq, void *_dev, struct pt_regs *r) +lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r) { struct pxa2xx_udc *dev = _dev; + int vbus; dev->stats.irqs++; HEX_DISPLAY(dev->stats.irqs); - - if (!is_usb_connected()) { - LED_CONNECTED_OFF; - disable_disconnect_irq(); - /* report disconnect just once */ - if (dev->gadget.speed != USB_SPEED_UNKNOWN) { - DMSG("disconnect %s\n", - dev->driver ? dev->driver->driver.name : 0); - stop_activity(dev, dev->driver); - - // udc_disable (dev); - // no more udc irqs - // maybe "ACTION=disconnect /sbin/hotplug gadget". - } - } else if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + switch (irq) { + case LUBBOCK_USB_IRQ: LED_CONNECTED_ON; - - DMSG("?? connect irq ??\n"); - - // if there's no driver bound, ignore; else - // udc_enable (dev); - // UDC irqs drive the rest. - // maybe "ACTION=connect /sbin/hotplug gadget". + vbus = 1; + disable_irq(LUBBOCK_USB_IRQ); + enable_irq(LUBBOCK_USB_DISC_IRQ); + break; + case LUBBOCK_USB_DISC_IRQ: + LED_CONNECTED_OFF; + vbus = 0; + disable_irq(LUBBOCK_USB_DISC_IRQ); + enable_irq(LUBBOCK_USB_IRQ); + break; + default: + return IRQ_NONE; } + + pxa2xx_udc_vbus_session(&dev->gadget, vbus); return IRQ_HANDLED; } #endif -#ifndef enable_disconnect_irq -#warning USB disconnect() is not yet reported. -#define enable_disconnect_irq() do {} while (0) -#define disable_disconnect_irq() do {} while (0) -#endif - /*-------------------------------------------------------------------------*/ @@ -1719,7 +1762,7 @@ static void handle_ep0 (struct pxa2xx_udc *dev) } u; if (list_empty(&ep->queue)) - req = 0; + req = NULL; else req = list_entry(ep->queue.next, struct pxa2xx_request, queue); @@ -1763,14 +1806,11 @@ bad_setup: goto bad_setup; got_setup: - le16_to_cpus (&u.r.wValue); - le16_to_cpus (&u.r.wIndex); - le16_to_cpus (&u.r.wLength); - - LED_EP0_ON; DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, u.r.wLength); + le16_to_cpu(u.r.wValue), + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wLength)); /* cope with automagic for some standard requests. */ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) @@ -1802,7 +1842,8 @@ config_change: * - ep reset doesn't include halt(?). */ DMSG("broken set_interface (%d/%d)\n", - u.r.wIndex, u.r.wValue); + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wValue)); goto config_change; } break; @@ -1846,7 +1887,6 @@ stall: ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); start_watchdog(dev); dev->ep0state = EP0_STALL; - LED_EP0_OFF; /* deferred i/o == no response yet */ } else if (dev->req_pending) { @@ -1947,7 +1987,7 @@ static void handle_ep(struct pxa2xx_ep *ep) req = list_entry(ep->queue.next, struct pxa2xx_request, queue); else - req = 0; + req = NULL; // TODO check FST handling @@ -2017,10 +2057,10 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) if (unlikely(udccr & UDCCR_SUSIR)) { udc_ack_int_UDCCR(UDCCR_SUSIR); handled = 1; - DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() + DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() ? "" : "+disconnect"); - if (!is_usb_connected()) + if (!is_vbus_present()) stop_activity(dev, dev->driver); else if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver @@ -2038,7 +2078,7 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume - && is_usb_connected()) + && is_vbus_present()) dev->driver->resume(&dev->gadget); } @@ -2049,8 +2089,6 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) if ((UDCCR & UDCCR_UDA) == 0) { DBG(DBG_VERBOSE, "USB reset start\n"); - if (dev->gadget.speed != USB_SPEED_UNKNOWN) - disable_disconnect_irq(); /* reset driver and endpoints, * in case that's not yet done @@ -2058,12 +2096,11 @@ pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) stop_activity (dev, dev->driver); } else { - INFO("USB reset\n"); + DBG(DBG_VERBOSE, "USB reset end\n"); dev->gadget.speed = USB_SPEED_FULL; LED_CONNECTED_ON; memset(&dev->stats, 0, sizeof dev->stats); /* driver and endpoints are still reset */ - enable_disconnect_irq(); } } else { @@ -2393,11 +2430,12 @@ static struct pxa2xx_udc memory = { #define PXA210_B1 0x00000123 #define PXA210_B0 0x00000122 #define IXP425_A0 0x000001c1 +#define IXP465_AD 0x00000200 /* * probe - binds to the platform device */ -static int __init pxa2xx_udc_probe(struct device *_dev) +static int __init pxa2xx_udc_probe(struct platform_device *pdev) { struct pxa2xx_udc *dev = &memory; int retval, out_dma = 1; @@ -2429,6 +2467,8 @@ static int __init pxa2xx_udc_probe(struct device *_dev) break; #elif defined(CONFIG_ARCH_IXP4XX) case IXP425_A0: + case IXP465_AD: + dev->has_cfr = 1; out_dma = 0; break; #endif @@ -2460,26 +2500,28 @@ static int __init pxa2xx_udc_probe(struct device *_dev) #endif /* other non-static parts of init */ - dev->dev = _dev; - dev->mach = _dev->platform_data; + dev->dev = &pdev->dev; + dev->mach = pdev->dev.platform_data; init_timer(&dev->timer); dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev; device_initialize(&dev->gadget.dev); - dev->gadget.dev.parent = _dev; - dev->gadget.dev.dma_mask = _dev->dma_mask; + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; the_controller = dev; - dev_set_drvdata(_dev, dev); + platform_set_drvdata(pdev, dev); udc_disable(dev); udc_reinit(dev); + dev->vbus = is_vbus_present(); + /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USB, pxa2xx_udc_irq, - SA_INTERRUPT, driver_name, dev); + IRQF_DISABLED, driver_name, dev); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, IRQ_USB, retval); @@ -2489,27 +2531,47 @@ static int __init pxa2xx_udc_probe(struct device *_dev) #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - disable_irq(LUBBOCK_USB_DISC_IRQ); retval = request_irq(LUBBOCK_USB_DISC_IRQ, - usb_connection_irq, - SA_INTERRUPT /* OOPSING | SA_SAMPLE_RANDOM */, + lubbock_vbus_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { - enable_irq(LUBBOCK_USB_DISC_IRQ); printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); +lubbock_fail0: + free_irq(IRQ_USB, dev); return -EBUSY; } - dev->got_disc = 1; + retval = request_irq(LUBBOCK_USB_IRQ, + lubbock_vbus_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + driver_name, dev); + if (retval != 0) { + printk(KERN_ERR "%s: can't get irq %i, err %d\n", + driver_name, LUBBOCK_USB_IRQ, retval); + free_irq(LUBBOCK_USB_DISC_IRQ, dev); + goto lubbock_fail0; + } +#ifdef DEBUG + /* with U-Boot (but not BLOB), hex is off by default */ + HEX_DISPLAY(dev->stats.irqs); + LUB_DISC_BLNK_LED &= 0xff; +#endif } #endif create_proc_files(); return 0; } -static int __exit pxa2xx_udc_remove(struct device *_dev) + +static void pxa2xx_udc_shutdown(struct platform_device *_dev) { - struct pxa2xx_udc *dev = _dev->driver_data; + pullup_off(); +} + +static int __exit pxa2xx_udc_remove(struct platform_device *pdev) +{ + struct pxa2xx_udc *dev = platform_get_drvdata(pdev); udc_disable(dev); remove_proc_files(); @@ -2519,38 +2581,82 @@ static int __exit pxa2xx_udc_remove(struct device *_dev) free_irq(IRQ_USB, dev); dev->got_irq = 0; } - if (machine_is_lubbock() && dev->got_disc) { +#ifdef CONFIG_ARCH_LUBBOCK + if (machine_is_lubbock()) { free_irq(LUBBOCK_USB_DISC_IRQ, dev); - dev->got_disc = 0; + free_irq(LUBBOCK_USB_IRQ, dev); } - dev_set_drvdata(_dev, 0); - the_controller = 0; +#endif + platform_set_drvdata(pdev, NULL); + the_controller = NULL; return 0; } /*-------------------------------------------------------------------------*/ -static struct device_driver udc_driver = { - .name = "pxa2xx-udc", - .bus = &platform_bus_type, +#ifdef CONFIG_PM + +/* USB suspend (controlled by the host) and system suspend (controlled + * by the PXA) don't necessarily work well together. If USB is active, + * the 48 MHz clock is required; so the system can't enter 33 MHz idle + * mode, or any deeper PM saving state. + * + * For now, we punt and forcibly disconnect from the USB host when PXA + * enters any suspend state. While we're disconnected, we always disable + * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. + * Boards without software pullup control shouldn't use those states. + * VBUS IRQs should probably be ignored so that the PXA device just acts + * "dead" to USB hosts until system resume. + */ +static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct pxa2xx_udc *udc = platform_get_drvdata(dev); + + if (!udc->mach->udc_command) + WARN("USB host won't detect disconnect!\n"); + pullup(udc, 0); + + return 0; +} + +static int pxa2xx_udc_resume(struct platform_device *dev) +{ + struct pxa2xx_udc *udc = platform_get_drvdata(dev); + + pullup(udc, 1); + + return 0; +} + +#else +#define pxa2xx_udc_suspend NULL +#define pxa2xx_udc_resume NULL +#endif + +/*-------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { .probe = pxa2xx_udc_probe, + .shutdown = pxa2xx_udc_shutdown, .remove = __exit_p(pxa2xx_udc_remove), - - // FIXME power management support - // .suspend = ... disable UDC - // .resume = ... re-enable UDC + .suspend = pxa2xx_udc_suspend, + .resume = pxa2xx_udc_resume, + .driver = { + .owner = THIS_MODULE, + .name = "pxa2xx-udc", + }, }; static int __init udc_init(void) { printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); - return driver_register(&udc_driver); + return platform_driver_register(&udc_driver); } module_init(udc_init); static void __exit udc_exit(void) { - driver_unregister(&udc_driver); + platform_driver_unregister(&udc_driver); } module_exit(udc_exit);