--- 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) { @@ -134,6 +135,8 @@ static int dprintk(int level, const char return 0; } #endif + +#ifdef CONFIG_USB_GADGET_DEBUG_FS static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg; @@ -197,6 +200,7 @@ static const struct file_operations s3c2 .release = single_release, .owner = THIS_MODULE, }; +#endif /* io macros */ @@ -843,6 +847,7 @@ static void s3c2410_udc_handle_ep(struct u32 ep_csr1; u32 idx; +handle_ep_again: if (likely (!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct s3c2410_request, queue); @@ -882,6 +887,8 @@ static void s3c2410_udc_handle_ep(struct if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { s3c2410_udc_read_fifo(ep,req); + if (s3c2410_udc_fifo_count_out()) + goto handle_ep_again; } } } @@ -1520,6 +1527,20 @@ static irqreturn_t s3c2410_udc_vbus_irq( 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__); @@ -1677,6 +1698,11 @@ int usb_gadget_register_driver(struct us 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); @@ -1707,6 +1733,7 @@ int usb_gadget_unregister_driver(struct if (driver->disconnect) driver->disconnect(&udc->gadget); + driver->unbind(&udc->gadget); device_del(&udc->gadget.dev); udc->driver = NULL; @@ -1893,10 +1920,16 @@ static int s3c2410_udc_probe(struct plat } 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; } +#ifdef CONFIG_USB_GADGET_DEBUG_FS if (s3c2410_udc_debugfs_root) { udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root, @@ -1904,6 +1937,7 @@ static int s3c2410_udc_probe(struct plat if (!udc->regs_info) dev_warn(dev, "debugfs file creation failed\n"); } +#endif dev_dbg(dev, "probe ok\n"); @@ -1939,6 +1973,8 @@ static int s3c2410_udc_remove(struct pla 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); @@ -2013,12 +2049,14 @@ static int __init udc_init(void) dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION); +#ifdef CONFIG_USB_GADGET_DEBUG_FS s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); if (IS_ERR(s3c2410_udc_debugfs_root)) { printk(KERN_ERR "%s: debugfs dir creation failed %ld\n", gadget_name, PTR_ERR(s3c2410_udc_debugfs_root)); s3c2410_udc_debugfs_root = NULL; } +#endif retval = platform_driver_register(&udc_driver_2410); if (retval) --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -21,6 +21,8 @@ #include <linux/platform_device.h> #include <linux/clk.h> +#include <mach/hardware.h> +#include <mach/regs-gpio.h> #include <plat/usb-control.h> #define valid_port(idx) ((idx) == 1 || (idx) == 2) @@ -306,6 +308,42 @@ static void s3c2410_hcd_oc(struct s3c241 local_irq_restore(flags); } +/* switching of USB pads */ +static ssize_t show_usb_mode(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (__raw_readl(S3C24XX_MISCCR) & S3C2410_MISCCR_USBHOST) + return sprintf(buf, "host\n"); + + return sprintf(buf, "device\n"); +} + +static ssize_t set_usb_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!strncmp(buf, "host", 4)) { + printk(KERN_WARNING "s3c2410: changing usb to host\n"); + s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, + S3C2410_MISCCR_USBHOST); + /* FIXME: + * - call machine-specific disable-pullup function i + * - enable +Vbus (if hardware supports it) + */ + s3c2410_gpio_setpin(S3C2410_GPB9, 0); + } else if (!strncmp(buf, "device", 6)) { + printk(KERN_WARNING "s3c2410: changing usb to device\n"); + s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, 0); + s3c2410_gpio_setpin(S3C2410_GPB9, 1); + } else { + printk(KERN_WARNING "s3c2410: unknown mode\n"); + return -EINVAL; + } + + return count; +} + +static DEVICE_ATTR(usb_mode, S_IRUGO | S_IWUSR, show_usb_mode, set_usb_mode); + /* may be called without controller electrically present */ /* may be called with controller, bus, and devices active */ @@ -486,15 +524,23 @@ static int ohci_hcd_s3c2410_drv_remove(s return 0; } +static int ohci_hcd_s3c2410_drv_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + ohci_finish_controller_resume(hcd); + return 0; +} + static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe, .remove = ohci_hcd_s3c2410_drv_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ - /*.resume = ohci_hcd_s3c2410_drv_resume, */ + .resume = ohci_hcd_s3c2410_drv_resume, .driver = { .owner = THIS_MODULE, - .name = "s3c2410-ohci", + .name = "s3c-ohci", }, }; --- 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; };