[PATCH] usb/fsl_qe_udc: Implement port reset
Anton Vorontsov
avorontsov at ru.mvista.com
Sun Nov 9 04:56:59 EST 2008
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 <avorontsov at ru.mvista.com>
---
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
More information about the Linuxppc-dev
mailing list