fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / gadget / pxa2xx_udc.c
index bb028c5..b78de96 100644 (file)
@@ -27,7 +27,6 @@
 #undef DEBUG
 // #define     VERBOSE DBG_VERBOSE
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/mm.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/irq.h>
 
 #include <asm/byteorder.h>
 #include <asm/dma.h>
 #include <asm/io.h>
-#include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/mach-types.h>
 #include <asm/unaligned.h>
 #include <asm/hardware.h>
+#ifdef CONFIG_ARCH_PXA
 #include <asm/arch/pxa-regs.h>
+#endif
 
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
@@ -109,7 +110,7 @@ static int use_dma = 1;
 module_param(use_dma, bool, 0);
 MODULE_PARM_DESC (use_dma, "true to use dma");
 
-static void dma_nodesc_handler (int dmach, void *_ep, struct pt_regs *r);
+static void dma_nodesc_handler (int dmach, void *_ep);
 static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req);
 
 #ifdef USE_OUT_DMA
@@ -149,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
 static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
 static void nuke (struct pxa2xx_ep *, int status);
 
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_vbus)
+               return pxa_gpio_get(mach->gpio_vbus);
+       if (mach->udc_is_connected)
+               return mach->udc_is_connected();
+       return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_pullup)
+               pxa_gpio_set(mach->gpio_pullup, 0);
+       else if (mach->udc_command)
+               mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+       struct pxa2xx_udc_mach_info             *mach = the_controller->mach;
+
+       if (mach->gpio_pullup)
+               pxa_gpio_set(mach->gpio_pullup, 1);
+       else if (mach->udc_command)
+               mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
 static void pio_irq_enable(int bEndpointAddress)
 {
         bEndpointAddress &= 0xf;
@@ -335,11 +369,10 @@ 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 NULL;
 
-       memset (req, 0, sizeof *req);
        INIT_LIST_HEAD (&req->queue);
        return &req->req;
 }
@@ -546,6 +579,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
@@ -565,6 +599,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");
@@ -793,7 +828,7 @@ static void cancel_dma(struct pxa2xx_ep *ep)
 }
 
 /* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */
-static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r)
+static void dma_nodesc_handler(int dmach, void *_ep)
 {
        struct pxa2xx_ep        *ep = _ep;
        struct pxa2xx_request   *req;
@@ -1586,9 +1621,8 @@ 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
                        || !driver->setup)
                return -EINVAL;
@@ -1659,7 +1693,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 
        if (!dev)
                return -ENODEV;
-       if (!driver || driver != dev->driver)
+       if (!driver || driver != dev->driver || !driver->unbind)
                return -EINVAL;
 
        local_irq_disable();
@@ -1689,7 +1723,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
  */
 
 static irqreturn_t
-lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+lubbock_vbus_irq(int irq, void *_dev)
 {
        struct pxa2xx_udc       *dev = _dev;
        int                     vbus;
@@ -1719,6 +1753,15 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
 
 #endif
 
+static irqreturn_t udc_vbus_irq(int irq, void *_dev)
+{
+       struct pxa2xx_udc       *dev = _dev;
+       int                     vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+       pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+       return IRQ_HANDLED;
+}
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -2039,7 +2082,7 @@ static void handle_ep(struct pxa2xx_ep *ep)
  * could cause usb protocol errors.
  */
 static irqreturn_t
-pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r)
+pxa2xx_udc_irq(int irq, void *_dev)
 {
        struct pxa2xx_udc       *dev = _dev;
        int                     handled;
@@ -2428,6 +2471,8 @@ static struct pxa2xx_udc memory = {
 #define PXA210_B1              0x00000123
 #define PXA210_B0              0x00000122
 #define IXP425_A0              0x000001c1
+#define IXP425_B0              0x000001f1
+#define IXP465_AD              0x00000200
 
 /*
  *     probe - binds to the platform device
@@ -2435,7 +2480,7 @@ static struct pxa2xx_udc memory = {
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
        struct pxa2xx_udc *dev = &memory;
-       int retval, out_dma = 1;
+       int retval, out_dma = 1, vbus_irq;
        u32 chiprev;
 
        /* insist on Intel/ARM/XScale */
@@ -2464,6 +2509,9 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
                break;
 #elif  defined(CONFIG_ARCH_IXP4XX)
        case IXP425_A0:
+       case IXP425_B0:
+       case IXP465_AD:
+               dev->has_cfr = 1;
                out_dma = 0;
                break;
 #endif
@@ -2497,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
        /* other non-static parts of init */
        dev->dev = &pdev->dev;
        dev->mach = pdev->dev.platform_data;
+       if (dev->mach->gpio_vbus) {
+               vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+               pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+                               | GPIO_IN);
+               set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+       } else
+               vbus_irq = 0;
+       if (dev->mach->gpio_pullup)
+               pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+                               | GPIO_OUT | GPIO_DFLT_LOW);
 
        init_timer(&dev->timer);
        dev->timer.function = udc_watchdog;
@@ -2516,7 +2574,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 
        /* 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);
@@ -2528,7 +2586,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
        if (machine_is_lubbock()) {
                retval = request_irq(LUBBOCK_USB_DISC_IRQ,
                                lubbock_vbus_irq,
-                               SA_INTERRUPT | SA_SAMPLE_RANDOM,
+                               IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
                                driver_name, dev);
                if (retval != 0) {
                        printk(KERN_ERR "%s: can't get irq %i, err %d\n",
@@ -2539,7 +2597,7 @@ lubbock_fail0:
                }
                retval = request_irq(LUBBOCK_USB_IRQ,
                                lubbock_vbus_irq,
-                               SA_INTERRUPT | SA_SAMPLE_RANDOM,
+                               IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
                                driver_name, dev);
                if (retval != 0) {
                        printk(KERN_ERR "%s: can't get irq %i, err %d\n",
@@ -2552,8 +2610,19 @@ lubbock_fail0:
                HEX_DISPLAY(dev->stats.irqs);
                LUB_DISC_BLNK_LED &= 0xff;
 #endif
-       }
+       } else
 #endif
+       if (vbus_irq) {
+               retval = request_irq(vbus_irq, udc_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, vbus_irq, retval);
+                       free_irq(IRQ_USB, dev);
+                       return -EBUSY;
+               }
+       }
        create_proc_files();
 
        return 0;
@@ -2568,18 +2637,24 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
 {
        struct pxa2xx_udc *dev = platform_get_drvdata(pdev);
 
+       if (dev->driver)
+               return -EBUSY;
+
        udc_disable(dev);
        remove_proc_files();
-       usb_gadget_unregister_driver(dev->driver);
 
        if (dev->got_irq) {
                free_irq(IRQ_USB, dev);
                dev->got_irq = 0;
        }
+#ifdef CONFIG_ARCH_LUBBOCK
        if (machine_is_lubbock()) {
                free_irq(LUBBOCK_USB_DISC_IRQ, dev);
                free_irq(LUBBOCK_USB_IRQ, dev);
        }
+#endif
+       if (dev->mach->gpio_vbus)
+               free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
        platform_set_drvdata(pdev, NULL);
        the_controller = NULL;
        return 0;