/*
* 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
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/types.h>
-#include <linux/version.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
/*
- * 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.
*
* 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";
// #define USE_OUT_DMA
// #define DISABLE_TEST_MODE
-#ifdef CONFIG_PROC_FS
-#define UDC_PROC_FILE
-#endif
-
#ifdef CONFIG_ARCH_IXP4XX
#undef USE_DMA
#include "pxa2xx_udc.h"
-#ifdef CONFIG_EMBEDDED
-/* few strings, and little code to use them */
-#undef DEBUG
-#undef UDC_PROC_FILE
-#endif
-
#ifdef USE_DMA
static int use_dma = 1;
module_param(use_dma, bool, 0);
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) {
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
+ local_irq_save(flags);
+
nuke (ep, -ESHUTDOWN);
#ifdef USE_DMA
/* 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;
}
* 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;
}
*/
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;
}
static inline void ep0_idle (struct pxa2xx_udc *dev)
{
dev->ep0state = EP0_IDLE;
- LED_EP0_OFF;
}
static int
/*-------------------------------------------------------------------------*/
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;
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:
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)
&& read_ep0_fifo(ep, req))) {
ep0_idle(dev);
done(ep, req, 0);
- req = 0;
+ req = NULL;
}
break;
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)
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 {
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 UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
"%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;
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",
#define remove_proc_files() \
remove_proc_entry(proc_node_name, NULL)
-#else /* !UDC_PROC_FILE */
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* "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);
UFNRH = UFNRH_SIM;
/* if hardware supports it, disconnect from usb */
- make_usb_disappear();
+ pullup_off();
udc_clear_mask_UDCCR(UDCCR_UDE);
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;
#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 */
/* 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.
}
#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();
}
/* 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);
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;
}
/* 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 */
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);
#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
-
/*-------------------------------------------------------------------------*/
} u;
if (list_empty(&ep->queue))
- req = 0;
+ req = NULL;
else
req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
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)
* - 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;
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) {
req = list_entry(ep->queue.next,
struct pxa2xx_request, queue);
else
- req = 0;
+ req = NULL;
// TODO check FST handling
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
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume
- && is_usb_connected())
+ && is_vbus_present())
dev->driver->resume(&dev->gadget);
}
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
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 {
/*
* 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;
#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);
#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,
+ SA_INTERRUPT | SA_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,
+ SA_INTERRUPT | SA_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();
free_irq(IRQ_USB, dev);
dev->got_irq = 0;
}
- if (machine_is_lubbock() && dev->got_disc) {
+ 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;
+ 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);