commit f68adda658590c264b6cb72ca1059a92d796fa58 Author: Mike Westerhof Date: Wed May 13 20:11:06 2009 -0500 This patch adds the option to poll vbus status to the s3c24xx udc driver. Machines that do not route the USB vbus signal to a GPIO can use this mechanism as an alternate way for the udc driver to detect the usb connect/disconnect state, which is useful (for example) to enable the link up/down state to be determined for the g_ether gadget. Signed-off-by: Mike Westerhof diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h index 546bb40..763aeba 100644 --- a/arch/arm/plat-s3c24xx/include/plat/udc.h +++ b/arch/arm/plat-s3c24xx/include/plat/udc.h @@ -27,6 +27,7 @@ enum s3c2410_udc_cmd_e { struct s3c2410_udc_mach_info { void (*udc_command)(enum s3c2410_udc_cmd_e); void (*vbus_draw)(unsigned int ma); + int (*get_vbus_status)(void); unsigned int vbus_pin; unsigned char vbus_pin_inverted; }; diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 891bab7..c8dca97 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -74,6 +74,7 @@ static void __iomem *base_addr; static u64 rsrc_start; static u64 rsrc_len; static struct dentry *s3c2410_udc_debugfs_root; +static struct timer_list vbus_poll_timer; static inline u32 udc_read(u32 reg) { @@ -1526,6 +1527,20 @@ static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) return IRQ_HANDLED; } +static void s3c2410_udc_vbus_poll(unsigned long _data) +{ + struct s3c2410_udc *data = (struct s3c2410_udc *)_data; + int v; + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + if (udc_info && udc_info->get_vbus_status) { + v = udc_info->get_vbus_status(); + if ((v > -1) && (v != data->vbus)) + s3c2410_udc_vbus_session(&data->gadget, v); + mod_timer(&vbus_poll_timer, jiffies + msecs_to_jiffies(900)); + } +} + static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) { dprintk(DEBUG_NORMAL, "%s()\n", __func__); @@ -1683,6 +1698,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) goto register_error; } + if (udc_info && udc_info->get_vbus_status && !udc_info->vbus_pin) { + mod_timer(&vbus_poll_timer, jiffies + msecs_to_jiffies(50)); + return 0; /* just return, vbus change will enable udc */ + } + /* Enable udc */ s3c2410_udc_enable(udc); @@ -1900,6 +1920,11 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } dev_dbg(dev, "got irq %i\n", irq); + } else if (udc_info && udc_info->get_vbus_status) { + udc->vbus = 0; + init_timer(&vbus_poll_timer); + vbus_poll_timer.function = s3c2410_udc_vbus_poll; + vbus_poll_timer.data = (unsigned long) udc; } else { udc->vbus = 1; } @@ -1948,6 +1973,8 @@ static int s3c2410_udc_remove(struct platform_device *pdev) if (udc_info && udc_info->vbus_pin > 0) { irq = gpio_to_irq(udc_info->vbus_pin); free_irq(irq, udc); + } else if (udc_info && udc_info->get_vbus_status) { + del_timer_sync(&vbus_poll_timer); } free_irq(IRQ_USBD, udc);