usb/fsl_qe_udc: Implement port reset
PATCH 1/1 usb/fsl_qe_udc: Implement port reset
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(-)
drivers/usb/gadget/fsl_qe_udc.c +90 -1
@@ -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);
drivers/usb/gadget/fsl_qe_udc.h +19 -0
@@ -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;
Signature · 1 line
1.5.6.3