USB: s3c2410_udc: Add common implementation for GPIO controlled pullups
authorLars-Peter Clausen <lars@metafoo.de>
Mon, 7 Mar 2011 07:41:59 +0000 (08:41 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 7 Mar 2011 20:23:22 +0000 (12:23 -0800)
Currently all boards using the s3c2410_udc driver use a GPIO to control the
state of the pullup, as a result the same code is reimplemented in each board
file.
This patch adds support for using a GPIO to control the pullup state to the udc
driver, so the boards can use a common implementation.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
arch/arm/plat-s3c24xx/include/plat/udc.h
drivers/usb/gadget/s3c2410_udc.c

index 546bb4008f49cf240fc21351c7d25a4f06fbe5a4..80457c6414aa2a7f3c8689ac7e180f8859dd3d47 100644 (file)
@@ -27,6 +27,10 @@ enum s3c2410_udc_cmd_e {
 struct s3c2410_udc_mach_info {
        void    (*udc_command)(enum s3c2410_udc_cmd_e);
        void    (*vbus_draw)(unsigned int ma);
+
+       unsigned int pullup_pin;
+       unsigned int pullup_pin_inverted;
+
        unsigned int vbus_pin;
        unsigned char vbus_pin_inverted;
 };
index 2b025200a69a690fe176e51d02e7981cea5c72dd..6d8b04061d5d6772f076c9792315a8ee8c42499d 100644 (file)
@@ -1481,7 +1481,9 @@ static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
 {
        dprintk(DEBUG_NORMAL, "%s()\n", __func__);
 
-       if (udc_info && udc_info->udc_command) {
+       if (udc_info && (udc_info->udc_command ||
+               gpio_is_valid(udc_info->pullup_pin))) {
+
                if (is_on)
                        s3c2410_udc_enable(udc);
                else {
@@ -1558,6 +1560,32 @@ static const struct usb_gadget_ops s3c2410_ops = {
        .vbus_draw              = s3c2410_vbus_draw,
 };
 
+static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
+{
+       if (!udc_info)
+               return;
+
+       if (udc_info->udc_command) {
+               udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+       } else if (gpio_is_valid(udc_info->pullup_pin)) {
+               int value;
+
+               switch (cmd) {
+               case S3C2410_UDC_P_ENABLE:
+                       value = 1;
+                       break;
+               case S3C2410_UDC_P_DISABLE:
+                       value = 0;
+                       break;
+               default:
+                       return;
+               }
+               value ^= udc_info->pullup_pin_inverted;
+
+               gpio_set_value(udc_info->pullup_pin, value);
+       }
+}
+
 /*------------------------- gadget driver handling---------------------------*/
 /*
  * s3c2410_udc_disable
@@ -1579,8 +1607,7 @@ static void s3c2410_udc_disable(struct s3c2410_udc *dev)
        udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
 
        /* Good bye, cruel world */
-       if (udc_info && udc_info->udc_command)
-               udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+       s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
 
        /* Set speed to unknown */
        dev->gadget.speed = USB_SPEED_UNKNOWN;
@@ -1641,8 +1668,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
        udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
 
        /* time to say "hello, world" */
-       if (udc_info && udc_info->udc_command)
-               udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+       s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
 }
 
 /*
@@ -1917,6 +1943,17 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
                udc->vbus = 1;
        }
 
+       if (udc_info && !udc_info->udc_command &&
+               gpio_is_valid(udc_info->pullup_pin)) {
+
+               retval = gpio_request_one(udc_info->pullup_pin,
+                               udc_info->vbus_pin_inverted ?
+                               GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
+                               "udc pullup");
+               if (retval)
+                       goto err_vbus_irq;
+       }
+
        if (s3c2410_udc_debugfs_root) {
                udc->regs_info = debugfs_create_file("registers", S_IRUGO,
                                s3c2410_udc_debugfs_root,
@@ -1929,6 +1966,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
 
        return 0;
 
+err_vbus_irq:
+       if (udc_info && udc_info->vbus_pin > 0)
+               free_irq(gpio_to_irq(udc_info->vbus_pin), udc);
 err_gpio_claim:
        if (udc_info && udc_info->vbus_pin > 0)
                gpio_free(udc_info->vbus_pin);
@@ -1956,6 +1996,10 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
 
        debugfs_remove(udc->regs_info);
 
+       if (udc_info && !udc_info->udc_command &&
+               gpio_is_valid(udc_info->pullup_pin))
+               gpio_free(udc_info->pullup_pin);
+
        if (udc_info && udc_info->vbus_pin > 0) {
                irq = gpio_to_irq(udc_info->vbus_pin);
                free_irq(irq, udc);
@@ -1987,16 +2031,14 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
 {
-       if (udc_info && udc_info->udc_command)
-               udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+       s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
 
        return 0;
 }
 
 static int s3c2410_udc_resume(struct platform_device *pdev)
 {
-       if (udc_info && udc_info->udc_command)
-               udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+       s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
 
        return 0;
 }