Without that patch a USB host won't find the QE UDC device if a USB
cable plugged before the QE UDC probe. A user have to re-plug the
USB cable so that the host would reenumerate the device.

To solve the issues the QE UDC should reset the port at bind time.

Signed-off-by: Anton Vorontsov <[EMAIL PROTECTED]>
---

Please do not apply this patch now. It depends on few patches that
should be merged into powerpc-next tree. I'll repost this patch when
everything will be ready for the merge.

 drivers/usb/gadget/fsl_qe_udc.c |   91 ++++++++++++++++++++++++++++++++++++++-
 drivers/usb/gadget/fsl_qe_udc.h |   19 ++++++++
 2 files changed, 109 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 94c38e4..91ad856 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -34,6 +34,9 @@
 #include <linux/moduleparam.h>
 #include <linux/of_platform.h>
 #include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
@@ -2285,6 +2288,26 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc)
        return status;
 }
 
+static void qe_udc_port_generate_reset(struct qe_udc *udc)
+{
+       if (!udc->can_reset_port)
+               return;
+
+       qe_pin_set_gpio(udc->pins[PIN_USBOE]);
+       qe_pin_set_gpio(udc->pins[PIN_USBTP]);
+       qe_pin_set_gpio(udc->pins[PIN_USBTN]);
+
+       gpio_direction_output(udc->gpios[GPIO_USBOE], 0);
+       gpio_direction_output(udc->gpios[GPIO_USBTP], 0);
+       gpio_direction_output(udc->gpios[GPIO_USBTN], 0);
+
+       msleep(5);
+
+       qe_pin_set_dedicated(udc->pins[PIN_USBOE]);
+       qe_pin_set_dedicated(udc->pins[PIN_USBTP]);
+       qe_pin_set_dedicated(udc->pins[PIN_USBTN]);
+}
+
 /*-------------------------------------------------------------------------
        Gadget driver register and unregister.
  --------------------------------------------------------------------------*/
@@ -2335,6 +2358,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver 
*driver)
        udc_controller->ep0_dir = USB_DIR_OUT;
        dev_info(udc_controller->dev, "%s bind to driver %s \n",
                udc_controller->gadget.name, driver->driver.name);
+
+       qe_udc_port_generate_reset(udc_controller);
        return 0;
 }
 EXPORT_SYMBOL(usb_gadget_register_driver);
@@ -2504,9 +2529,11 @@ static int __devinit qe_udc_probe(struct of_device 
*ofdev,
                        const struct of_device_id *match)
 {
        struct device_node *np = ofdev->node;
+       struct device *dev = &ofdev->dev;
        struct qe_ep *ep;
        unsigned int ret = 0;
-       unsigned int i;
+       int i;
+       int j;
        const void *prop;
 
        prop = of_get_property(np, "mode", NULL);
@@ -2610,6 +2637,45 @@ static int __devinit qe_udc_probe(struct of_device 
*ofdev,
                goto err5;
        }
 
+       /*
+        * Get GPIOs and pins. Bail out only if something really failed,
+        * that is, silently accept `not implemented' errors. For CPM we
+        * don't (yet) support Pinmux API, so CPM gadgets will unable to
+        * do the port reset.
+        */
+       for (i = 0; i < NUM_GPIOS; i++) {
+               int gpio = of_get_gpio(np, i, NULL);
+
+               udc_controller->gpios[i] = gpio;
+
+               ret = gpio_request(gpio, dev->bus_id);
+               if (!ret)
+                       continue;
+
+               udc_controller->gpios[i] = ret;
+               if (ret == -ENOSYS)
+                       continue;
+
+               dev_err(dev, "failed to request gpio #%d", i);
+               goto err_gpios;
+       }
+
+       for (j = 0; j < NUM_PINS; j++) {
+               udc_controller->pins[j] = qe_pin_request(ofdev->node, j);
+               if (!IS_ERR(udc_controller->pins[j]))
+                       continue;
+
+               ret = PTR_ERR(udc_controller->pins[j]);
+               if (ret == -ENOSYS)
+                       continue;
+
+               dev_err(dev, "can't get pin #%d: %d\n", j, ret);
+               goto err_pins;
+       }
+
+       if (i == NUM_GPIOS && j == NUM_PINS)
+               udc_controller->can_reset_port = true;
+
        ret = device_add(&udc_controller->gadget.dev);
        if (ret)
                goto err6;
@@ -2620,6 +2686,14 @@ static int __devinit qe_udc_probe(struct of_device 
*ofdev,
        return 0;
 
 err6:
+err_pins:
+       while (--j >= 0)
+               qe_pin_free(udc_controller->pins[j]);
+err_gpios:
+       while (--i >= 0) {
+               if (gpio_is_valid(udc_controller->gpios[i]))
+                       gpio_free(udc_controller->gpios[i]);
+       }
        free_irq(udc_controller->usb_irq, udc_controller);
 err5:
        if (udc_controller->nullmap) {
@@ -2665,6 +2739,7 @@ static int __devexit qe_udc_remove(struct of_device 
*ofdev)
 {
        struct qe_ep *ep;
        unsigned int size;
+       int i;
 
        DECLARE_COMPLETION(done);
 
@@ -2706,6 +2781,20 @@ static int __devexit qe_udc_remove(struct of_device 
*ofdev)
        kfree(ep->rxbuffer);
        kfree(ep->txframe);
 
+       for (i = 0; i < NUM_GPIOS; i++) {
+               if (gpio_is_valid(udc_controller->gpios[i]))
+                       continue;
+               gpio_free(udc_controller->gpios[i]);
+       }
+
+       for (i = 0; i < NUM_PINS; i++) {
+               struct qe_pin *pin = udc_controller->pins[i];
+
+               if (!pin || IS_ERR(pin))
+                       continue;
+               qe_pin_free(pin);
+       }
+
        free_irq(udc_controller->usb_irq, udc_controller);
 
        tasklet_kill(&udc_controller->rx_tasklet);
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index 31b2710..c1226c3 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -317,6 +317,22 @@ struct qe_ep {
        struct timer_list timer;
 };
 
+enum qe_udc_gpios {
+       GPIO_USBOE = 0,
+       GPIO_USBTP,
+       GPIO_USBTN,
+       NUM_GPIOS,
+};
+
+enum qe_udc_pins {
+       PIN_USBOE = 0,
+       PIN_USBTP,
+       PIN_USBTN,
+       NUM_PINS,
+};
+
+struct qe_pin;
+
 struct qe_udc {
        struct usb_gadget gadget;
        struct usb_gadget_driver *driver;
@@ -357,6 +373,9 @@ struct qe_udc {
        unsigned int usb_clock;
        unsigned int usb_irq;
        struct usb_ctlr __iomem *usb_regs;
+       struct qe_pin *pins[NUM_PINS];
+       int gpios[NUM_GPIOS];
+       bool can_reset_port;
 
        struct tasklet_struct rx_tasklet;
 
-- 
1.5.6.3
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to