From 901cb2ab2c9aaeb16304ff99ebc4af968893c8ec Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 Mar 2013 15:22:40 -0700 Subject: Staging: ccg: remove it from the build This driver has been nothing but trouble, and no one shipping a new Android device uses it, so let's just drop it, making the USB Gadget driver authors lives a whole lot easier as they do their rework. Cc: John Stultz Cc: Paul Bolle Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 093f10c..659855c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -116,8 +116,6 @@ source "drivers/staging/android/Kconfig" source "drivers/staging/ozwpan/Kconfig" -source "drivers/staging/ccg/Kconfig" - source "drivers/staging/gdm72xx/Kconfig" source "drivers/staging/csr/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index fa41b04..b367ea8 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ -obj-$(CONFIG_USB_G_CCG) += ccg/ obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/ obj-$(CONFIG_CSR_WIFI) += csr/ obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal/ -- cgit v0.10.2 From 302beda9830f656c98afb25e26e94602b7a83fea Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 20 Feb 2013 12:25:14 -0300 Subject: usb: chipidea: usbmisc_imx6q: Staticize usbmisc_imx6q_drv_init/exit() Staticize usbmisc_imx6q_drv_init/exit() to fix the following sparse warnings: drivers/usb/chipidea/usbmisc_imx6q.c:147:12: warning: symbol 'usbmisc_imx6q_drv_init' was not declared. Should it be static? drivers/usb/chipidea/usbmisc_imx6q.c:153:13: warning: symbol 'usbmisc_imx6q_drv_exit' was not declared. Should it be static? Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/usbmisc_imx6q.c b/drivers/usb/chipidea/usbmisc_imx6q.c index a1bce39..113fcea 100644 --- a/drivers/usb/chipidea/usbmisc_imx6q.c +++ b/drivers/usb/chipidea/usbmisc_imx6q.c @@ -144,13 +144,13 @@ static struct platform_driver usbmisc_imx6q_driver = { }, }; -int __init usbmisc_imx6q_drv_init(void) +static int __init usbmisc_imx6q_drv_init(void) { return platform_driver_register(&usbmisc_imx6q_driver); } subsys_initcall(usbmisc_imx6q_drv_init); -void __exit usbmisc_imx6q_drv_exit(void) +static void __exit usbmisc_imx6q_drv_exit(void) { platform_driver_unregister(&usbmisc_imx6q_driver); } -- cgit v0.10.2 From 9143b771f1ec2d677e536cdcdb732ef96d649d21 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 20 Feb 2013 16:52:25 -0300 Subject: usb: host: ehci-mxc: Remove unneeded header file Since commit c0304996b (USB: ehci-mxc: remove Efika MX-specific CHRGVBUS hack) there is no need to include , so remove it. Signed-off-by: Fabio Estevam Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index e9301fb..18c30af 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -28,11 +28,7 @@ #include #include #include - #include - -#include - #include "ehci.h" #define DRIVER_DESC "Freescale On-Chip EHCI Host driver" -- cgit v0.10.2 From f1bffc8ca61853dad54da592aadfd28882c00a9e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 20 Feb 2013 16:52:26 -0300 Subject: usb: host: ehci-mxc: Remove dev_info on probe It is not very useful to indicate the the driver is about to be probed. Quoting Alan Stern [1]: "Plenty of drivers don't include any message like this at all. You might as well get rid of it entirely." Remove such dev_info(). [1] http://marc.info/?l=linux-usb&m=136138896132433&w=2 Signed-off-by: Fabio Estevam Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 18c30af..b5b7be4 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -57,8 +57,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ehci_hcd *ehci; - dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); - if (!pdata) { dev_err(dev, "No platform data given, bailing out.\n"); return -EINVAL; -- cgit v0.10.2 From 8a7298d361827a1f244415dde62b1b07688d6a3a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 23 Feb 2013 10:11:35 -0500 Subject: usb/serial: Remove unnecessary check for console The tty port ops shutdown() routine is not called for console ports; remove extra check. Signed-off-by: Peter Hurley Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a19ed74..8424478 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -256,22 +256,18 @@ static int serial_open(struct tty_struct *tty, struct file *filp) * serial_down - shut down hardware * @tport: tty port to shut down * - * Shut down a USB serial port unless it is the console. We never - * shut down the console hardware as it will always be in use. Serialized - * against activate by the tport mutex and kept to matching open/close pairs + * Shut down a USB serial port. Serialized against activate by the + * tport mutex and kept to matching open/close pairs * of calls by the ASYNCB_INITIALIZED flag. + * + * Not called if tty is console. */ static void serial_down(struct tty_port *tport) { struct usb_serial_port *port = container_of(tport, struct usb_serial_port, port); struct usb_serial_driver *drv = port->serial->type; - /* - * The console is magical. Do not hang up the console hardware - * or there will be tears. - */ - if (port->port.console) - return; + if (drv->close) drv->close(port); } -- cgit v0.10.2 From 39d35681d5380b403855202dcd75575a8d5b0ec1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 24 Feb 2013 00:55:07 -0800 Subject: USB: remove incorrect __exit markups Even if bus is not hot-pluggable, the devices can be unbound from the driver via sysfs, so we should not be using __exit annotations on remove() methods. The only exception is drivers registered with platform_driver_probe() which specifically disables sysfs bind/unbind attributes. Signed-off-by: Dmitry Torokhov Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index b5b7be4..a38c8c8 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -172,7 +172,7 @@ err_alloc: return ret; } -static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) +static int ehci_mxc_drv_remove(struct platform_device *pdev) { struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; struct usb_hcd *hcd = platform_get_drvdata(pdev); diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 914a3ec..38c45fb 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -305,7 +305,7 @@ err1: return err; } -static int __exit ehci_orion_drv_remove(struct platform_device *pdev) +static int ehci_orion_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct clk *clk; @@ -333,7 +333,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, - .remove = __exit_p(ehci_orion_drv_remove), + .remove = ehci_orion_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "orion-ehci", diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 3565a30..e30e396 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -170,7 +170,7 @@ fail_create_hcd: return ret; } -static int __exit ehci_hcd_sh_remove(struct platform_device *pdev) +static int ehci_hcd_sh_remove(struct platform_device *pdev) { struct ehci_sh_priv *priv = platform_get_drvdata(pdev); struct usb_hcd *hcd = priv->hcd; @@ -196,7 +196,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove = __exit_p(ehci_hcd_sh_remove), + .remove = ehci_hcd_sh_remove .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index af9cb11..8b9de95 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1212,7 +1212,7 @@ static void isp1301_release(struct device *dev) static struct isp1301 *the_transceiver; -static int __exit isp1301_remove(struct i2c_client *i2c) +static int isp1301_remove(struct i2c_client *i2c) { struct isp1301 *isp; @@ -1634,7 +1634,7 @@ static struct i2c_driver isp1301_driver = { .name = "isp1301_omap", }, .probe = isp1301_probe, - .remove = __exit_p(isp1301_remove), + .remove = isp1301_remove, .id_table = isp1301_id, }; diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index a994715..24d573a 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -658,7 +658,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) return 0; } -static int __exit twl4030_usb_remove(struct platform_device *pdev) +static int twl4030_usb_remove(struct platform_device *pdev) { struct twl4030_usb *twl = platform_get_drvdata(pdev); int val; @@ -702,7 +702,7 @@ MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); static struct platform_driver twl4030_usb_driver = { .probe = twl4030_usb_probe, - .remove = __exit_p(twl4030_usb_remove), + .remove = twl4030_usb_remove, .driver = { .name = "twl4030_usb", .owner = THIS_MODULE, diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 8cd6cf4..7f3c5b0 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -393,7 +393,7 @@ static int twl6030_usb_probe(struct platform_device *pdev) return 0; } -static int __exit twl6030_usb_remove(struct platform_device *pdev) +static int twl6030_usb_remove(struct platform_device *pdev) { struct twl6030_usb *twl = platform_get_drvdata(pdev); @@ -420,7 +420,7 @@ MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); static struct platform_driver twl6030_usb_driver = { .probe = twl6030_usb_probe, - .remove = __exit_p(twl6030_usb_remove), + .remove = twl6030_usb_remove, .driver = { .name = "twl6030_usb", .owner = THIS_MODULE, diff --git a/drivers/usb/phy/mv_u3d_phy.c b/drivers/usb/phy/mv_u3d_phy.c index 9d85991..bafd67f 100644 --- a/drivers/usb/phy/mv_u3d_phy.c +++ b/drivers/usb/phy/mv_u3d_phy.c @@ -313,7 +313,7 @@ err: return ret; } -static int __exit mv_u3d_phy_remove(struct platform_device *pdev) +static int mv_u3d_phy_remove(struct platform_device *pdev) { struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev); -- cgit v0.10.2 From bba90aedb00906a2f0d34325610729a1ee016f43 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 1 Mar 2013 08:14:19 +0300 Subject: usb: storage: onetouch: tighten a range check Smatch complains because we only allocate ONETOUCH_PKT_LEN (2) bytes but later when we call usb_fill_int_urb() we assume maxp can be up to 8 bytes. I talked to the maintainer and maxp should be capped at ONETOUCH_PKT_LEN. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index cb79de6..2696489 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -195,6 +195,7 @@ static int onetouch_connect_input(struct us_data *ss) pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = min(maxp, ONETOUCH_PKT_LEN); onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); input_dev = input_allocate_device(); @@ -245,8 +246,7 @@ static int onetouch_connect_input(struct us_data *ss) input_dev->open = usb_onetouch_open; input_dev->close = usb_onetouch_close; - usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, - (maxp > 8 ? 8 : maxp), + usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, maxp, usb_onetouch_irq, onetouch, endpoint->bInterval); onetouch->irq->transfer_dma = onetouch->data_dma; onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -- cgit v0.10.2 From dad3cab3e063110b3ae3dc82a00e7aacd09b91ec Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 4 Mar 2013 16:52:49 -0600 Subject: USB: fix trivial usb_device kernel-doc errors Fix trivial kernel-doc warnings: Warning(include/linux/usb.h:574): No description found for parameter 'usb3_lpm_enabled' Warning(include/linux/usb.h:574): Excess struct/union/enum/typedef member 'usb_classdev' description in 'usb_device' Warning(include/linux/usb.h:574): Excess struct/union/enum/typedef member 'usbfs_dentry' description in 'usb_device' Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Jiri Kosina Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb.h b/include/linux/usb.h index 4d22d0f..52464fb 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -469,14 +469,12 @@ struct usb3_lpm_parameters { * @lpm_capable: device supports LPM * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled + * @usb3_lpm_enabled: USB3 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) * @serial: iSerialNumber string, if present (static) * @filelist: usbfs files that are open to this device - * @usb_classdev: USB class device that was created for usbfs device - * access from userspace - * @usbfs_dentry: usbfs dentry entry for the device * @maxchild: number of ports if hub * @quirks: quirks of the whole device * @urbnum: number of URBs submitted for the whole device -- cgit v0.10.2 From ae8d4879667949fb49f0862b11ba680f671b2185 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Thu, 7 Mar 2013 01:51:12 +0530 Subject: usb: serial: Remove redundant NULL check before kfree kfree on NULL pointer is a no-op. Signed-off-by: Syam Sidhardhan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 809fb32..107ff9e 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1252,8 +1252,7 @@ static void mos7840_close(struct usb_serial_port *port) if (mos7840_port->write_urb) { /* if this urb had a transfer buffer already (old tx) free it */ - if (mos7840_port->write_urb->transfer_buffer != NULL) - kfree(mos7840_port->write_urb->transfer_buffer); + kfree(mos7840_port->write_urb->transfer_buffer); usb_free_urb(mos7840_port->write_urb); } -- cgit v0.10.2 From a4dee9c9fdda7a3ebcb76f5e6280508530f67b76 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 7 Mar 2013 12:57:58 +0100 Subject: USB: cdc-acm: Remove obsolete predefined speeds array Modern speed handling has been introduced in 2009 by commit 9b80fee149a875a6292b2556ab2c64dc7ab7d6f5 (cdc_acm: Fix to use modern speed interfaces) and the acm_tty_speed array has been unused since. Signed-off-by: Samuel Tardieu Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8ac25ad..d003c8c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -840,14 +840,6 @@ static int acm_tty_ioctl(struct tty_struct *tty, return rv; } -static const __u32 acm_tty_speed[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, - 1200, 1800, 2400, 4800, 9600, 19200, 38400, - 57600, 115200, 230400, 460800, 500000, 576000, - 921600, 1000000, 1152000, 1500000, 2000000, - 2500000, 3000000, 3500000, 4000000 -}; - static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { -- cgit v0.10.2 From 07cd29d76532acc6a9148074c3915c02cdd709d9 Mon Sep 17 00:00:00 2001 From: Paul Vlase Date: Sun, 10 Mar 2013 15:52:02 +0200 Subject: usb: Use resource_size function Signed-off-by: Paul Vlase Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 3065809..5cd9f96 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -225,7 +225,7 @@ static int mv_ehci_probe(struct platform_device *pdev) (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); hcd->rsrc_start = r->start; - hcd->rsrc_len = r->end - r->start + 1; + hcd->rsrc_len = resource_size(r); hcd->regs = ehci_mv->op_regs; hcd->irq = platform_get_irq(pdev, 0); -- cgit v0.10.2 From 8244ac043c5334012006d542d1cd5c1e3fe2f32a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 11 Mar 2013 23:23:23 +0800 Subject: USB: misc: usb3503: use module_i2c_driver to simplify the code Use the module_i2c_driver() macro to make the code smaller and a bit simpler. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index f713f6a..d3a1cce 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -307,18 +307,7 @@ static struct i2c_driver usb3503_driver = { .id_table = usb3503_id, }; -static int __init usb3503_init(void) -{ - return i2c_add_driver(&usb3503_driver); -} - -static void __exit usb3503_exit(void) -{ - i2c_del_driver(&usb3503_driver); -} - -module_init(usb3503_init); -module_exit(usb3503_exit); +module_i2c_driver(usb3503_driver); MODULE_AUTHOR("Dongjin Kim "); MODULE_DESCRIPTION("USB3503 USB HUB driver"); -- cgit v0.10.2 From 5b2750d5b5a25c9c8b842e22fb2a7015dc798455 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Tue, 12 Mar 2013 13:33:27 +0800 Subject: driver: usb: storage: remove cast for kmalloc return value remove cast for kmalloc return value. Signed-off-by: Zhang Yanfei Cc: Matthew Dharm Cc: Greg Kroah-Hartman Cc: Andrew Morton Cc: linux-usb@vger.kernel.org Cc: usb-storage@lists.one-eyed-alien.net Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index ecea478..06a3d22 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1457,8 +1457,7 @@ static int isd200_init_info(struct us_data *us) retStatus = ISD200_ERROR; else { info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL); - info->RegsBuf = (unsigned char *) - kmalloc(sizeof(info->ATARegs), GFP_KERNEL); + info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL); info->srb.sense_buffer = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) { -- cgit v0.10.2 From a1645eefc8358d0a9dbeba254e51aa89de4d80a4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Mar 2013 17:14:57 +0000 Subject: usb: misc: sisusbvga: Avoid NULL pointer dereference from sisusb A failed kzalloc() is reported with a dev_err that dereferences the null sisusb, this will cause a NULL pointer deference error. Instead, pass dev->dev to the dev_err() rather than &sisusb->sisusb_dev->dev Smatch analysis: drivers/usb/misc/sisusbvga/sisusb.c:3087 sisusb_probe() error: potential null dereference 'sisusb'. (kzalloc returns null) Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index dd573ab..c21386e 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3084,7 +3084,7 @@ static int sisusb_probe(struct usb_interface *intf, /* Allocate memory for our private */ if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for private data\n"); + dev_err(&dev->dev, "Failed to allocate memory for private data\n"); return -ENOMEM; } kref_init(&sisusb->kref); -- cgit v0.10.2 From 54a419668b0f27b7982807fb2376d237e0a0ce05 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 12 Mar 2013 12:44:39 +0200 Subject: USB: EHCI: split ehci-omap out to a separate driver This patch (as1645) converts ehci-omap over to the new "ehci-hcd is a library" approach, so that it can coexist peacefully with other EHCI platform drivers and can make use of the private area allocated at the end of struct ehci_hcd. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c59a112..62f4e9a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -155,7 +155,7 @@ config USB_EHCI_MXC Variation of ARC USB block used in some Freescale chips. config USB_EHCI_HCD_OMAP - bool "EHCI support for OMAP3 and later chips" + tristate "EHCI support for OMAP3 and later chips" depends on USB_EHCI_HCD && ARCH_OMAP default y ---help--- diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 001fbff..56de410 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o +obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b416a3f..303b022 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1252,11 +1252,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_sh_driver #endif -#ifdef CONFIG_USB_EHCI_HCD_OMAP -#include "ehci-omap.c" -#define PLATFORM_DRIVER ehci_hcd_omap_driver -#endif - #ifdef CONFIG_PPC_PS3 #include "ehci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver @@ -1346,6 +1341,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \ !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \ !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ + !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 0555ee4..fa66757 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -36,6 +36,9 @@ * - convert to use hwmod and runtime PM */ +#include +#include +#include #include #include #include @@ -43,6 +46,10 @@ #include #include #include +#include +#include + +#include "ehci.h" #include @@ -57,9 +64,11 @@ #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 -/*-------------------------------------------------------------------------*/ +#define DRIVER_DESC "OMAP-EHCI Host Controller driver" -static const struct hc_driver ehci_omap_hc_driver; +static const char hcd_name[] = "ehci-omap"; + +/*-------------------------------------------------------------------------*/ static inline void ehci_write(void __iomem *base, u32 reg, u32 val) @@ -166,6 +175,12 @@ static void disable_put_regulator( /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ +static struct hc_driver __read_mostly ehci_omap_hc_driver; + +static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { + .reset = omap_ehci_init, +}; + /** * ehci_hcd_omap_probe - initialize TI-based HCDs * @@ -315,56 +330,33 @@ static struct platform_driver ehci_hcd_omap_driver = { /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ .driver = { - .name = "ehci-omap", + .name = hcd_name, } }; /*-------------------------------------------------------------------------*/ -static const struct hc_driver ehci_omap_hc_driver = { - .description = hcd_name, - .product_desc = "OMAP-EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .reset = omap_ehci_init, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, +static int __init ehci_omap_init(void) +{ + if (usb_disabled()) + return -ENODEV; - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, + pr_info("%s: " DRIVER_DESC "\n", hcd_name); - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, + ehci_init_driver(&ehci_omap_hc_driver, &ehci_omap_overrides); + return platform_driver_register(&ehci_hcd_omap_driver); +} +module_init(ehci_omap_init); - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; +static void __exit ehci_omap_cleanup(void) +{ + platform_driver_unregister(&ehci_hcd_omap_driver); +} +module_exit(ehci_omap_cleanup); MODULE_ALIAS("platform:ehci-omap"); MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_AUTHOR("Felipe Balbi "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 18c2bb1b8c1571f4c1fa33cc1f4525b282059455 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:40 +0200 Subject: USB: ehci-omap: Use devm_ioremap_resource() Make use of devm_ioremap_resource() and correct comment. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index fa66757..70e8e6f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -216,23 +216,15 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); - if (!res) { - dev_err(dev, "UHH EHCI get resource failed\n"); - return -ENODEV; - } - - regs = ioremap(res->start, resource_size(res)); - if (!regs) { - dev_err(dev, "UHH EHCI ioremap failed\n"); - return -ENOMEM; - } + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, dev_name(dev)); if (!hcd) { - dev_err(dev, "failed to create hcd with err %d\n", ret); - ret = -ENOMEM; - goto err_io; + dev_err(dev, "Failed to create HCD\n"); + return -ENOMEM; } hcd->rsrc_start = res->start; @@ -285,8 +277,6 @@ err_pm_runtime: pm_runtime_put_sync(dev); usb_put_hcd(hcd); -err_io: - iounmap(regs); return ret; } @@ -306,7 +296,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) usb_remove_hcd(hcd); disable_put_regulator(dev->platform_data); - iounmap(hcd->regs); usb_put_hcd(hcd); pm_runtime_put_sync(dev); -- cgit v0.10.2 From dcd64063fd917b5c79f99cae218e1df3ed1b62a2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:41 +0200 Subject: USB: ehci-omap: Use PHY APIs to get the PHY device and put it out of suspend For each port that is in PHY mode we obtain a PHY device using the USB PHY library and put it out of suspend. It is up to platform code to associate the PHY to the controller's port and it is up to the PHY driver to manage the PHY's resources. Also remove weird spacing around declarations we come across. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 70e8e6f..6b8b7e5 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -4,10 +4,11 @@ * Bus Glue for the EHCI controllers in OMAP3/4 * Tested on several OMAP3 boards, and OMAP4 Pandaboard * - * Copyright (C) 2007-2011 Texas Instruments, Inc. + * Copyright (C) 2007-2013 Texas Instruments, Inc. * Author: Vikram Pandita * Author: Anand Gadiyar * Author: Keshava Munegowda + * Author: Roger Quadros * * Copyright (C) 2009 Nokia Corporation * Contact: Felipe Balbi @@ -70,6 +71,10 @@ static const char hcd_name[] = "ehci-omap"; /*-------------------------------------------------------------------------*/ +struct omap_hcd { + struct usb_phy *phy[OMAP3_HS_USB_PORTS]; /* one PHY for each port */ + int nports; +}; static inline void ehci_write(void __iomem *base, u32 reg, u32 val) { @@ -178,7 +183,8 @@ static void disable_put_regulator( static struct hc_driver __read_mostly ehci_omap_hc_driver; static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { - .reset = omap_ehci_init, + .reset = omap_ehci_init, + .extra_priv_size = sizeof(struct omap_hcd), }; /** @@ -190,15 +196,16 @@ static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { */ static int ehci_hcd_omap_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct usbhs_omap_platform_data *pdata = dev->platform_data; - struct resource *res; - struct usb_hcd *hcd; - void __iomem *regs; - int ret = -ENODEV; - int irq; - int i; - char supply[7]; + struct device *dev = &pdev->dev; + struct usbhs_omap_platform_data *pdata = dev->platform_data; + struct resource *res; + struct usb_hcd *hcd; + void __iomem *regs; + int ret = -ENODEV; + int irq; + int i; + char supply[7]; + struct omap_hcd *omap; if (usb_disabled()) return -ENODEV; @@ -231,6 +238,33 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = regs; + omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv; + omap->nports = pdata->nports; + + platform_set_drvdata(pdev, hcd); + + /* get the PHY devices if needed */ + for (i = 0 ; i < omap->nports ; i++) { + struct usb_phy *phy; + + if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) + continue; + + /* get the PHY device */ + phy = devm_usb_get_phy_dev(dev, i); + if (IS_ERR(phy) || !phy) { + ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; + dev_err(dev, "Can't get PHY device for port %d: %d\n", + i, ret); + goto err_phy; + } + + omap->phy[i] = phy; + usb_phy_init(omap->phy[i]); + /* bring PHY out of suspend */ + usb_phy_set_suspend(omap->phy[i], 0); + } + /* get ehci regulator and enable */ for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) { @@ -275,6 +309,13 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) err_pm_runtime: disable_put_regulator(pdata); pm_runtime_put_sync(dev); + +err_phy: + for (i = 0; i < omap->nports; i++) { + if (omap->phy[i]) + usb_phy_shutdown(omap->phy[i]); + } + usb_put_hcd(hcd); return ret; @@ -291,13 +332,20 @@ err_pm_runtime: */ static int ehci_hcd_omap_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd = dev_get_drvdata(dev); + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct omap_hcd *omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv; + int i; usb_remove_hcd(hcd); disable_put_regulator(dev->platform_data); - usb_put_hcd(hcd); + for (i = 0; i < omap->nports; i++) { + if (omap->phy[i]) + usb_phy_shutdown(omap->phy[i]); + } + + usb_put_hcd(hcd); pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- cgit v0.10.2 From 87425ad36330e4ee2806f19c7d14524c43a02210 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:42 +0200 Subject: USB: ehci-omap: Remove PHY reset handling code Reset GPIO handling for the PHY must be done in the PHY driver. We use the PHY helpers instead to reset the PHY. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6b8b7e5..0bbfdc1 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -86,79 +86,27 @@ static inline u32 ehci_read(void __iomem *base, u32 reg) return __raw_readl(base + reg); } - -static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - unsigned reg = 0; - - reg = ULPI_FUNC_CTRL_RESET - /* FUNCTION_CTRL_SET register */ - | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT) - /* Write */ - | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) - /* PORTn */ - | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) - /* start ULPI access*/ - | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT); - - ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg); - - /* Wait for ULPI access completion */ - while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI) - & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - dev_dbg(hcd->self.controller, - "phy reset operation timed out\n"); - break; - } - } -} - static int omap_ehci_init(struct usb_hcd *hcd) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int rc; - struct usbhs_omap_platform_data *pdata; - - pdata = hcd->self.controller->platform_data; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct omap_hcd *omap = (struct omap_hcd *)ehci->priv; + int rc, i; /* Hold PHYs in reset while initializing EHCI controller */ - if (pdata->phy_reset) { - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0); - - /* Hold the PHY in RESET for enough time till DIR is high */ - udelay(10); + for (i = 0; i < omap->nports; i++) { + if (omap->phy[i]) + usb_phy_shutdown(omap->phy[i]); } - /* Soft reset the PHY using PHY reset command over ULPI */ - if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(hcd, 0); - if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) - omap_ehci_soft_phy_reset(hcd, 1); - /* we know this is the memory we want, no need to ioremap again */ ehci->caps = hcd->regs; rc = ehci_setup(hcd); - if (pdata->phy_reset) { - /* Hold the PHY in RESET for enough time till - * PHY is settled and ready - */ - udelay(10); - - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_set_value_cansleep(pdata->reset_gpio_port[0], 1); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_set_value_cansleep(pdata->reset_gpio_port[1], 1); + /* Bring PHYs out of reset */ + for (i = 0; i < omap->nports; i++) { + if (omap->phy[i]) + usb_phy_init(omap->phy[i]); } return rc; -- cgit v0.10.2 From 218a214723d75f5692660d4f4eb4f524b0dfabec Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:43 +0200 Subject: USB: ehci-omap: Remove PHY regulator handling code PHY regulator handling must be done in the PHY driver Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 0bbfdc1..57fe985 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -112,19 +111,6 @@ static int omap_ehci_init(struct usb_hcd *hcd) return rc; } -static void disable_put_regulator( - struct usbhs_omap_platform_data *pdata) -{ - int i; - - for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { - if (pdata->regulator[i]) { - regulator_disable(pdata->regulator[i]); - regulator_put(pdata->regulator[i]); - } - } -} - /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -152,7 +138,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) int ret = -ENODEV; int irq; int i; - char supply[7]; struct omap_hcd *omap; if (usb_disabled()) @@ -213,23 +198,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) usb_phy_set_suspend(omap->phy[i], 0); } - /* get ehci regulator and enable */ - for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { - if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) { - pdata->regulator[i] = NULL; - continue; - } - snprintf(supply, sizeof(supply), "hsusb%d", i); - pdata->regulator[i] = regulator_get(dev, supply); - if (IS_ERR(pdata->regulator[i])) { - pdata->regulator[i] = NULL; - dev_dbg(dev, - "failed to get ehci port%d regulator\n", i); - } else { - regulator_enable(pdata->regulator[i]); - } - } - pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -255,7 +223,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return 0; err_pm_runtime: - disable_put_regulator(pdata); pm_runtime_put_sync(dev); err_phy: @@ -286,7 +253,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) int i; usb_remove_hcd(hcd); - disable_put_regulator(dev->platform_data); for (i = 0; i < omap->nports; i++) { if (omap->phy[i]) -- cgit v0.10.2 From dfdf9ad92aa90d2a5de732dbd5e23bb17501ac67 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:44 +0200 Subject: USB: ehci-omap: Select NOP USB transceiver driver In PHY mode we need to have the nop-usb-xceiv transceiver driver to operate, so select it in Kconfig. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 62f4e9a..2f68221 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -157,6 +157,7 @@ config USB_EHCI_MXC config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on USB_EHCI_HCD && ARCH_OMAP + select NOP_USB_XCEIV default y ---help--- Enables support for the on-chip EHCI controller on -- cgit v0.10.2 From 3414211b914464113ba3cd19726e44b5e416087d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:45 +0200 Subject: USB: ehci-omap: Get platform resources by index rather than by name Since there is only one resource per type we don't really need to use resource name to obtain it. This also also makes it easier for device tree adaptation. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 57fe985..7d05cce 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -148,14 +148,13 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return -ENODEV; } - irq = platform_get_irq_byname(pdev, "ehci-irq"); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "EHCI irq failed\n"); return -ENODEV; } - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "ehci"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit v0.10.2 From 8c3ec38550a6b880738c8b1982f9207d0fd5a339 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:46 +0200 Subject: USB: ohci-omap3: Get platform resources by index rather than by name Since there is only one resource per type we don't really need to use resource name to obtain it. This also also makes it easier for device tree adaptation. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index eb35d96..5ed28c5 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -141,14 +141,13 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev) return -ENODEV; } - irq = platform_get_irq_byname(pdev, "ohci-irq"); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "OHCI irq failed\n"); return -ENODEV; } - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "ohci"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "UHH OHCI get resource failed\n"); return -ENOMEM; -- cgit v0.10.2 From 5867320dec346c2dc26f224f876d780111ca149d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:47 +0200 Subject: USB: ohci-omap3: Add device tree support and binding information Allows the OHCI controller found in OMAP3 and later chips to be specified via device tree. Signed-off-by: Roger Quadros Reviewed-by: Mark Rutland Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/usb/ohci-omap3.txt b/Documentation/devicetree/bindings/usb/ohci-omap3.txt new file mode 100644 index 0000000..14ab428 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ohci-omap3.txt @@ -0,0 +1,15 @@ +OMAP HS USB OHCI controller (OMAP3 and later) + +Required properties: + +- compatible: should be "ti,ohci-omap3" +- reg: should contain one register range i.e. start and length +- interrupts: description of the interrupt line + +Example for OMAP4: + +usbhsohci: ohci@4a064800 { + compatible = "ti,ohci-omap3", "usb-ohci"; + reg = <0x4a064800 0x400>; + interrupts = <0 76 0x4>; +}; diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 5ed28c5..ddfc314 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -31,6 +31,8 @@ #include #include +#include +#include /*-------------------------------------------------------------------------*/ @@ -112,6 +114,8 @@ static const struct hc_driver ohci_omap3_hc_driver = { /*-------------------------------------------------------------------------*/ +static u64 omap_ohci_dma_mask = DMA_BIT_MASK(32); + /* * configure so an HC device and id are always provided * always called with process context; sleeping is OK @@ -159,6 +163,13 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev) return -ENOMEM; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &omap_ohci_dma_mask; hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev, dev_name(dev)); @@ -228,12 +239,20 @@ static void ohci_hcd_omap3_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +static const struct of_device_id omap_ohci_dt_ids[] = { + { .compatible = "ti,ohci-omap3" }, + { } +}; + +MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids); + static struct platform_driver ohci_hcd_omap3_driver = { .probe = ohci_hcd_omap3_probe, .remove = ohci_hcd_omap3_remove, .shutdown = ohci_hcd_omap3_shutdown, .driver = { .name = "ohci-omap3", + .of_match_table = of_match_ptr(omap_ohci_dt_ids), }, }; -- cgit v0.10.2 From a1ae0affee119e6deb937d157aa8b43319c1d6f3 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:48 +0200 Subject: USB: ehci-omap: Add device tree support and binding information Allows the OMAP EHCI controller to be specified via device tree. Signed-off-by: Roger Quadros Reviewed-by: Mark Rutland Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/usb/ehci-omap.txt b/Documentation/devicetree/bindings/usb/ehci-omap.txt new file mode 100644 index 0000000..485a9a1 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ehci-omap.txt @@ -0,0 +1,32 @@ +OMAP HS USB EHCI controller + +This device is usually the child of the omap-usb-host +Documentation/devicetree/bindings/mfd/omap-usb-host.txt + +Required properties: + +- compatible: should be "ti,ehci-omap" +- reg: should contain one register range i.e. start and length +- interrupts: description of the interrupt line + +Optional properties: + +- phys: list of phandles to PHY nodes. + This property is required if at least one of the ports are in + PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY + +To specify the port mode, see +Documentation/devicetree/bindings/mfd/omap-usb-host.txt + +Example for OMAP4: + +usbhsehci: ehci@4a064c00 { + compatible = "ti,ehci-omap", "usb-ehci"; + reg = <0x4a064c00 0x400>; + interrupts = <0 77 0x4>; +}; + +&usbhsehci { + phys = <&hsusb1_phy 0 &hsusb3_phy>; +}; + diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7d05cce..45cd01e 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include "ehci.h" @@ -121,6 +123,8 @@ static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { .extra_priv_size = sizeof(struct omap_hcd), }; +static u64 omap_ehci_dma_mask = DMA_BIT_MASK(32); + /** * ehci_hcd_omap_probe - initialize TI-based HCDs * @@ -148,6 +152,17 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return -ENODEV; } + /* For DT boot, get platform data from parent. i.e. usbhshost */ + if (dev->of_node) { + pdata = dev->parent->platform_data; + dev->platform_data = pdata; + } + + if (!pdata) { + dev_err(dev, "Missing platform data\n"); + return -ENODEV; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "EHCI irq failed\n"); @@ -159,6 +174,14 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &omap_ehci_dma_mask; + hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, dev_name(dev)); if (!hcd) { @@ -183,7 +206,10 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) continue; /* get the PHY device */ - phy = devm_usb_get_phy_dev(dev, i); + if (dev->of_node) + phy = devm_usb_get_phy_by_phandle(dev, "phys", i); + else + phy = devm_usb_get_phy_dev(dev, i); if (IS_ERR(phy) || !phy) { ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; dev_err(dev, "Can't get PHY device for port %d: %d\n", @@ -273,6 +299,13 @@ static void ehci_hcd_omap_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +static const struct of_device_id omap_ehci_dt_ids[] = { + { .compatible = "ti,ehci-omap" }, + { } +}; + +MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); + static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, .remove = ehci_hcd_omap_remove, @@ -281,6 +314,7 @@ static struct platform_driver ehci_hcd_omap_driver = { /*.resume = ehci_hcd_omap_resume, */ .driver = { .name = hcd_name, + .of_match_table = of_match_ptr(omap_ehci_dt_ids), } }; @@ -307,6 +341,7 @@ module_exit(ehci_omap_cleanup); MODULE_ALIAS("platform:ehci-omap"); MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_AUTHOR("Felipe Balbi "); +MODULE_AUTHOR("Roger Quadros "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From a2f450ca88a394e282f09e5e16f9de60cd487f80 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:49 +0200 Subject: USB: ehci-omap: Try to get PHY even if not in PHY mode Even when not in PHY mode, the USB device on the port (e.g. HUB) might need resources like RESET which can be modelled as a PHY device. So try to get the PHY device in any case. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 45cd01e..1ba1df8 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -202,15 +202,16 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) for (i = 0 ; i < omap->nports ; i++) { struct usb_phy *phy; - if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) - continue; - /* get the PHY device */ if (dev->of_node) phy = devm_usb_get_phy_by_phandle(dev, "phys", i); else phy = devm_usb_get_phy_dev(dev, i); if (IS_ERR(phy) || !phy) { + /* Don't bail out if PHY is not absolutely necessary */ + if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) + continue; + ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; dev_err(dev, "Can't get PHY device for port %d: %d\n", i, ret); -- cgit v0.10.2 From 49f092198f4fd2c70847de7151d33df08929af51 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 13 Mar 2013 15:14:43 +0200 Subject: USB: ehci-omap: Fix detection in HSIC mode Move PHY initialization until after EHCI initialization is complete, instead of initializing the PHYs first, shutting them down again, and then initializing them a second time. This fixes HSIC device detection. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 1ba1df8..755b428 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -90,26 +90,13 @@ static inline u32 ehci_read(void __iomem *base, u32 reg) static int omap_ehci_init(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct omap_hcd *omap = (struct omap_hcd *)ehci->priv; - int rc, i; - - /* Hold PHYs in reset while initializing EHCI controller */ - for (i = 0; i < omap->nports; i++) { - if (omap->phy[i]) - usb_phy_shutdown(omap->phy[i]); - } + int rc; /* we know this is the memory we want, no need to ioremap again */ ehci->caps = hcd->regs; rc = ehci_setup(hcd); - /* Bring PHYs out of reset */ - for (i = 0; i < omap->nports; i++) { - if (omap->phy[i]) - usb_phy_init(omap->phy[i]); - } - return rc; } @@ -219,9 +206,6 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } omap->phy[i] = phy; - usb_phy_init(omap->phy[i]); - /* bring PHY out of suspend */ - usb_phy_set_suspend(omap->phy[i], 0); } pm_runtime_enable(dev); @@ -245,6 +229,20 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) goto err_pm_runtime; } + /* + * Bring PHYs out of reset. + * Even though HSIC mode is a PHY-less mode, the reset + * line exists between the chips and can be modelled + * as a PHY device for reset control. + */ + for (i = 0; i < omap->nports; i++) { + if (!omap->phy[i]) + continue; + + usb_phy_init(omap->phy[i]); + /* bring PHY out of suspend */ + usb_phy_set_suspend(omap->phy[i], 0); + } return 0; -- cgit v0.10.2 From 413fd1e9aa3e0441e64ed4703ce1bba164e135c0 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 13 Mar 2013 15:16:03 +0200 Subject: USB: ehci-omap: Get rid of omap_ehci_init() As it does almost nothing, get rid of omap_ehci_init() and move the ehci->caps initialization part into probe(). Also remove the outdated TODO list from header. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 755b428..5de3e43 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -29,12 +29,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * TODO (last updated Feb 27, 2010): - * - add kernel-doc - * - enable AUTOIDLE - * - add suspend/resume - * - add HSIC and TLL support - * - convert to use hwmod and runtime PM */ #include @@ -87,26 +81,12 @@ static inline u32 ehci_read(void __iomem *base, u32 reg) return __raw_readl(base + reg); } -static int omap_ehci_init(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int rc; - - /* we know this is the memory we want, no need to ioremap again */ - ehci->caps = hcd->regs; - - rc = ehci_setup(hcd); - - return rc; -} - /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ static struct hc_driver __read_mostly ehci_omap_hc_driver; static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { - .reset = omap_ehci_init, .extra_priv_size = sizeof(struct omap_hcd), }; @@ -179,6 +159,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = regs; + hcd_to_ehci(hcd)->caps = regs; omap = (struct omap_hcd *)hcd_to_ehci(hcd)->priv; omap->nports = pdata->nports; -- cgit v0.10.2 From 1f0972f5b05a674d73e4eb314fa1b6c78e37aef1 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:19 +0200 Subject: usb: phy: nop: Add some parameters to platform data Add clk_rate parameter to platform data. If supplied, the NOP phy driver will program the clock to that rate during probe. Also add 2 flags, needs_vcc and needs_reset. If the flag is set and the regulator couldn't be found then the driver will bail out with -EPROBE_DEFER. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h index 28884c7..148d351 100644 --- a/include/linux/usb/nop-usb-xceiv.h +++ b/include/linux/usb/nop-usb-xceiv.h @@ -5,6 +5,11 @@ struct nop_usb_xceiv_platform_data { enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; }; #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -- cgit v0.10.2 From e4d7dc6674efd798792adbd689986cde5422aa62 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:20 +0200 Subject: usb: phy: nop: use devm_kzalloc() Use resource managed kzalloc. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index a3ce24b..af52870 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -100,15 +100,14 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; - nop = kzalloc(sizeof *nop, GFP_KERNEL); + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); if (!nop) return -ENOMEM; - nop->phy.otg = kzalloc(sizeof *nop->phy.otg, GFP_KERNEL); - if (!nop->phy.otg) { - kfree(nop); + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) return -ENOMEM; - } if (pdata) type = pdata->type; @@ -127,7 +126,7 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - goto exit; + return err; } platform_set_drvdata(pdev, nop); @@ -135,10 +134,6 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); return 0; -exit: - kfree(nop->phy.otg); - kfree(nop); - return err; } static int nop_usb_xceiv_remove(struct platform_device *pdev) @@ -148,8 +143,6 @@ static int nop_usb_xceiv_remove(struct platform_device *pdev) usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); - kfree(nop->phy.otg); - kfree(nop); return 0; } -- cgit v0.10.2 From 2319fb88e16e56c64d4f3ab50af69ed6dadbc7b5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:21 +0200 Subject: usb: phy: nop: Manage PHY clock If the PHY has a clock associated to it then manage the clock. We just enable the clock in .init() and disable it in .shutdown(). Add clk_rate parameter in platform data and configure the clock rate during probe if supplied. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index af52870..17c174f 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -32,10 +32,12 @@ #include #include #include +#include struct nop_usb_xceiv { struct usb_phy phy; struct device *dev; + struct clk *clk; }; static struct platform_device *pd; @@ -64,6 +66,24 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) return 0; } +static int nop_init(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); +} + static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { if (!otg) @@ -112,10 +132,34 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (pdata) type = pdata->type; + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && pdata && pdata->clk_rate) { + err = clk_set_rate(nop->clk, pdata->clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + nop->dev = &pdev->dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; nop->phy.state = OTG_STATE_UNDEFINED; nop->phy.otg->phy = &nop->phy; @@ -126,7 +170,7 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - return err; + goto err_add; } platform_set_drvdata(pdev, nop); @@ -134,12 +178,20 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; } static int nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); -- cgit v0.10.2 From 58f735fe4778d34d9d1e37bcdd59325d66a8793e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:22 +0200 Subject: usb: phy: nop: Handle power supply regulator for the PHY We use "vcc" as the supply name for the PHY's power supply. The power supply will be enabled during .init() and disabled during .shutdown() Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 17c174f..fbdcfef 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -33,11 +33,13 @@ #include #include #include +#include struct nop_usb_xceiv { struct usb_phy phy; struct device *dev; struct clk *clk; + struct regulator *vcc; }; static struct platform_device *pd; @@ -70,6 +72,11 @@ static int nop_init(struct usb_phy *phy) { struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + if (!IS_ERR(nop->clk)) clk_enable(nop->clk); @@ -82,6 +89,11 @@ static void nop_shutdown(struct usb_phy *phy) if (!IS_ERR(nop->clk)) clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } } static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) @@ -154,6 +166,12 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) } } + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + } + nop->dev = &pdev->dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; -- cgit v0.10.2 From ad63ebfc3565bbdec87ee4e30e4d40d164c1d3b8 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:23 +0200 Subject: usb: phy: nop: Handle RESET for the PHY We expect the RESET line to be modeled as a regulator with supply name "reset". The regulator should be modeled such that enabling the regulator brings the PHY device out of RESET and disabling the regulator holds the device in RESET. They PHY will be held in RESET in .shutdown() and brought out of RESET in .init(). Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index fbdcfef..6efc9b7 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -40,6 +40,7 @@ struct nop_usb_xceiv { struct device *dev; struct clk *clk; struct regulator *vcc; + struct regulator *reset; }; static struct platform_device *pd; @@ -80,6 +81,12 @@ static int nop_init(struct usb_phy *phy) if (!IS_ERR(nop->clk)) clk_enable(nop->clk); + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + return 0; } @@ -87,6 +94,12 @@ static void nop_shutdown(struct usb_phy *phy) { struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + if (!IS_ERR(nop->clk)) clk_disable(nop->clk); @@ -172,6 +185,12 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) PTR_ERR(nop->vcc)); } + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + } + nop->dev = &pdev->dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; -- cgit v0.10.2 From 90f4232f31f087f86667da03b1a4f3c90a32cb4a Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:24 +0200 Subject: usb: phy: nop: use new PHY API to register PHY We would need to support multiple PHYs of the same type so use the new PHY API usb_add_phy_dev() to register the PHY. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 6efc9b7..fe7a460 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -198,12 +198,13 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.init = nop_init; nop->phy.shutdown = nop_shutdown; nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; nop->phy.otg->phy = &nop->phy; nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy, type); + err = usb_add_phy_dev(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); -- cgit v0.10.2 From 0eba387973f521e57f00584e5e840e5328a61dda Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:25 +0200 Subject: usb: phy: nop: Add device tree support and binding information The PHY clock, clock rate, VCC regulator and RESET regulator can now be provided via device tree. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt new file mode 100644 index 0000000..d7e2726 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -0,0 +1,34 @@ +USB NOP PHY + +Required properties: +- compatible: should be usb-nop-xceiv + +Optional properties: +- clocks: phandle to the PHY clock. Use as per Documentation/devicetree + /bindings/clock/clock-bindings.txt + This property is required if clock-frequency is specified. + +- clock-names: Should be "main_clk" + +- clock-frequency: the clock frequency (in Hz) that the PHY clock must + be configured to. + +- vcc-supply: phandle to the regulator that provides RESET to the PHY. + +- reset-supply: phandle to the regulator that provides power to the PHY. + +Example: + + hsusb1_phy { + compatible = "usb-nop-xceiv"; + clock-frequency = <19200000>; + clocks = <&osc 0>; + clock-names = "main_clk"; + vcc-supply = <&hsusb1_vcc_regulator>; + reset-supply = <&hsusb1_reset_regulator>; + }; + +hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator +and expects that clock to be configured to 19.2MHz by the NOP PHY driver. +hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator +controls RESET. diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index fe7a460..b26b1c2 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -34,13 +34,14 @@ #include #include #include +#include struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; }; static struct platform_device *pd; @@ -140,10 +141,12 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) static int nop_usb_xceiv_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; struct nop_usb_xceiv *nop; enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; + u32 clk_rate = 0; nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); if (!nop) @@ -154,8 +157,16 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (!nop->phy.otg) return -ENOMEM; - if (pdata) + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + } else if (pdata) { type = pdata->type; + clk_rate = pdata->clk_rate; + } nop->clk = devm_clk_get(&pdev->dev, "main_clk"); if (IS_ERR(nop->clk)) { @@ -163,8 +174,8 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) PTR_ERR(nop->clk)); } - if (!IS_ERR(nop->clk) && pdata && pdata->clk_rate) { - err = clk_set_rate(nop->clk, pdata->clk_rate); + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); if (err) { dev_err(&pdev->dev, "Error setting clock rate\n"); return err; @@ -237,12 +248,20 @@ static int nop_usb_xceiv_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + static struct platform_driver nop_usb_xceiv_driver = { .probe = nop_usb_xceiv_probe, .remove = nop_usb_xceiv_remove, .driver = { .name = "nop_usb_xceiv", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nop_xceiv_dt_ids), }, }; -- cgit v0.10.2 From b54b5f56531d9fcbb30908373ba842af4de6a26b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:26 +0200 Subject: USB: phy: nop: Defer probe if device needs VCC/RESET Add 2 flags, needs_vcc and needs_reset to platform data. If the flag is set and the regulator couldn't be found then we bail out with -EPROBE_DEFER. For device tree boot we depend on presensce of vcc-supply/ reset-supply properties to decide if we should bail out with -EPROBE_DEFER or just continue in case the regulator can't be found. This is required for proper functionality in cases where the regulator is needed but is probed later than the PHY device. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index b26b1c2..2b10cc9 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -147,6 +147,8 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); if (!nop) @@ -163,9 +165,14 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (of_property_read_u32(node, "clock-frequency", &clk_rate)) clk_rate = 0; + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + } else if (pdata) { type = pdata->type; clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; } nop->clk = devm_clk_get(&pdev->dev, "main_clk"); @@ -194,12 +201,16 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (IS_ERR(nop->vcc)) { dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; } nop->reset = devm_regulator_get(&pdev->dev, "reset"); if (IS_ERR(nop->reset)) { dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; } nop->dev = &pdev->dev; -- cgit v0.10.2 From e36a0c870f7dbbfa7ed13cd83b79be00bcd00380 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Feb 2013 20:03:27 +0530 Subject: usb: dwc3: omap: minor fixes to get dt working Includes few minor fixes in dwc3-omap like populating the compatible string in a correct way, extracting the utmi-mode property properly and changing the index of get_irq since irq of core is removed from hwmod entry. Also updated the documentation with dwc3-omap device tree binding information. Signed-off-by: Kishon Vijay Abraham I [ balbi@ti.com : fix a compile warning introduced by this commit ] Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 1ef0ce7..1b9f55f 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -41,6 +41,34 @@ Board specific device node entry power = <50>; }; +OMAP DWC3 GLUE + - compatible : Should be "ti,dwc3" + - ti,hwmods : Should be "usb_otg_ss" + - reg : Address and length of the register set for the device. + - interrupts : The irq number of this device that is used to interrupt the + MPU + - #address-cells, #size-cells : Must be present if the device has sub-nodes + - utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID. + It should be set to "1" for HW mode and "2" for SW mode. + - ranges: the child address space are mapped 1:1 onto the parent address space + +Sub-nodes: +The dwc3 core should be added as subnode to omap dwc3 glue. +- dwc3 : + The binding details of dwc3 can be found in: + Documentation/devicetree/bindings/usb/dwc3.txt + +omap_dwc3 { + compatible = "ti,dwc3"; + ti,hwmods = "usb_otg_ss"; + reg = <0x4a020000 0x1ff>; + interrupts = <0 93 4>; + #address-cells = <1>; + #size-cells = <1>; + utmi-mode = <2>; + ranges; +}; + OMAP CONTROL USB Required properties: diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index afa05e3..e1206b4 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -316,11 +316,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; - int size; int ret = -ENOMEM; int irq; - const u32 *utmi_mode; + int utmi_mode = 0; + u32 reg; void __iomem *base; @@ -334,13 +334,13 @@ static int dwc3_omap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, omap); - irq = platform_get_irq(pdev, 1); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "missing IRQ resource\n"); return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory base resource\n"); return -EINVAL; @@ -387,25 +387,22 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); - utmi_mode = of_get_property(node, "utmi-mode", &size); - if (utmi_mode && size == sizeof(*utmi_mode)) { - reg |= *utmi_mode; - } else { - if (!pdata) { - dev_dbg(dev, "missing platform data\n"); - } else { - switch (pdata->utmi_mode) { - case DWC3_OMAP_UTMI_MODE_SW: - reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - case DWC3_OMAP_UTMI_MODE_HW: - reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - default: - dev_dbg(dev, "UNKNOWN utmi mode %d\n", - pdata->utmi_mode); - } - } + if (node) + of_property_read_u32(node, "utmi-mode", &utmi_mode); + else if (pdata) + utmi_mode = pdata->utmi_mode; + else + dev_dbg(dev, "missing platform data\n"); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode); } dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); @@ -465,7 +462,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) static const struct of_device_id of_dwc3_match[] = { { - "ti,dwc3", + .compatible = "ti,dwc3" }, { }, }; -- cgit v0.10.2 From 4495afcf713adb5bdb16504052952bdd0d11f90a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Feb 2013 20:03:28 +0530 Subject: usb: dwc3: omap: remove platform data associated with dwc3-omap omap5 is not going to have support for non-dt boot making the platform data associated with dwc3 useless. Removed it here. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index e1206b4..43a2482 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -309,7 +309,6 @@ static int dwc3_omap_remove_core(struct device *dev, void *c) static int dwc3_omap_probe(struct platform_device *pdev) { - struct dwc3_omap_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; struct dwc3_omap *omap; @@ -326,6 +325,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) void __iomem *base; void *context; + if (!node) { + dev_err(dev, "device node not found\n"); + return -EINVAL; + } + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); if (!omap) { dev_err(dev, "not enough memory\n"); @@ -387,12 +391,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); - if (node) - of_property_read_u32(node, "utmi-mode", &utmi_mode); - else if (pdata) - utmi_mode = pdata->utmi_mode; - else - dev_dbg(dev, "missing platform data\n"); + of_property_read_u32(node, "utmi-mode", &utmi_mode); switch (utmi_mode) { case DWC3_OMAP_UTMI_MODE_SW: @@ -435,13 +434,10 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); - if (node) { - ret = of_platform_populate(node, NULL, NULL, dev); - if (ret) { - dev_err(&pdev->dev, - "failed to add create dwc3 core\n"); - return ret; - } + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(&pdev->dev, "failed to create dwc3 core\n"); + return ret; } return 0; diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h index ada4012..1d36ca8 100644 --- a/include/linux/platform_data/dwc3-omap.h +++ b/include/linux/platform_data/dwc3-omap.h @@ -41,7 +41,3 @@ enum dwc3_omap_utmi_mode { DWC3_OMAP_UTMI_MODE_HW, DWC3_OMAP_UTMI_MODE_SW, }; - -struct dwc3_omap_data { - enum dwc3_omap_utmi_mode utmi_mode; -}; -- cgit v0.10.2 From 7eaf8f2a7da6506df0e6edc4fdb22678f0eb3602 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Feb 2013 20:03:29 +0530 Subject: usb: dwc3: omap: stop using nop-usb-xceiv Now that we have drivers for omap-usb2 phy and omap-usb3 phy, stop using nop-usb-xceiv. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 43a2482..3d343d9 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -52,7 +52,6 @@ #include #include -#include /* * All these registers belong to OMAP's Wrapper around the @@ -117,8 +116,6 @@ struct dwc3_omap { /* device lock */ spinlock_t lock; - struct platform_device *usb2_phy; - struct platform_device *usb3_phy; struct device *dev; int irq; @@ -193,60 +190,6 @@ void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) } EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); -static int dwc3_omap_register_phys(struct dwc3_omap *omap) -{ - struct nop_usb_xceiv_platform_data pdata; - struct platform_device *pdev; - int ret; - - memset(&pdata, 0x00, sizeof(pdata)); - - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); - if (!pdev) - return -ENOMEM; - - omap->usb2_phy = pdev; - pdata.type = USB_PHY_TYPE_USB2; - - ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata)); - if (ret) - goto err1; - - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); - if (!pdev) { - ret = -ENOMEM; - goto err1; - } - - omap->usb3_phy = pdev; - pdata.type = USB_PHY_TYPE_USB3; - - ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata)); - if (ret) - goto err2; - - ret = platform_device_add(omap->usb2_phy); - if (ret) - goto err2; - - ret = platform_device_add(omap->usb3_phy); - if (ret) - goto err3; - - return 0; - -err3: - platform_device_del(omap->usb2_phy); - -err2: - platform_device_put(omap->usb3_phy); - -err1: - platform_device_put(omap->usb2_phy); - - return ret; -} - static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) { struct dwc3_omap *omap = _omap; @@ -356,12 +299,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) return -ENOMEM; } - ret = dwc3_omap_register_phys(omap); - if (ret) { - dev_err(dev, "couldn't register PHYs\n"); - return ret; - } - context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL); if (!context) { dev_err(dev, "couldn't allocate dwc3 context memory\n"); @@ -445,10 +382,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) static int dwc3_omap_remove(struct platform_device *pdev) { - struct dwc3_omap *omap = platform_get_drvdata(pdev); - - platform_device_unregister(omap->usb2_phy); - platform_device_unregister(omap->usb3_phy); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); -- cgit v0.10.2 From f07bd56bbdaa2340ebf46af9a37e7b2d1b4578e3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 14:52:24 +0200 Subject: usb: gadget: udc-core: allow udc class register gadget device Currently all UDC drivers are calling device_register() before calling usb_add_gadget_udc(). In order to avoid code duplication, we can allow udc-core.c register that device. However that would become a really large patch, so to cope with the meanwhile and allow us to write bite-sized patches, we're adding a flag which will be set by UDC driver once it removes the code for registering the gadget device. Once all are converted, the new flag will be removed. Reviewed-by: Tomasz Figa Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 2a9cd36..9195054 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -173,6 +173,14 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (!udc) goto err1; + if (gadget->register_my_device) { + dev_set_name(&gadget->dev, "gadget"); + + ret = device_register(&gadget->dev); + if (ret) + goto err2; + } + device_initialize(&udc->dev); udc->dev.release = usb_udc_release; udc->dev.class = udc_class; @@ -180,7 +188,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) udc->dev.parent = parent; ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); if (ret) - goto err2; + goto err3; udc->gadget = gadget; @@ -189,18 +197,22 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) ret = device_add(&udc->dev); if (ret) - goto err3; + goto err4; mutex_unlock(&udc_lock); return 0; -err3: + +err4: list_del(&udc->list); mutex_unlock(&udc_lock); -err2: +err3: put_device(&udc->dev); +err2: + if (gadget->register_my_device) + put_device(&gadget->dev); err1: return ret; } @@ -254,6 +266,9 @@ found: kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); device_unregister(&udc->dev); + + if (gadget->register_my_device) + device_unregister(&gadget->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2e297e8..fcd9ef8 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -494,6 +494,9 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. + * @register_my_device: Flag telling udc-core that UDC driver didn't + * register the gadget device to the driver model. Temporary until + * all UDC drivers are fixed up properly. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. @@ -531,6 +534,7 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; + unsigned register_my_device:1; const char *name; struct device dev; unsigned out_epnum; -- cgit v0.10.2 From 1e1930bd3d9c6174eba1e3ca4135fd25ea3ad59c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 14:56:26 +0200 Subject: usb: dwc3: gadget: let udc-core manage gadget->dev We don't need to register that device ourselves if we simply set gadget->register_my_device. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 82e160e..10bb161 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2488,8 +2488,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) goto err3; } - dev_set_name(&dwc->gadget.dev, "gadget"); - dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; @@ -2501,6 +2499,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; dwc->gadget.dev.release = dwc3_gadget_release; + dwc->gadget.register_my_device = true; dwc->gadget.name = "dwc3-gadget"; /* @@ -2544,24 +2543,14 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc3_gadget_usb3_phy_suspend(dwc, false); } - ret = device_register(&dwc->gadget.dev); - if (ret) { - dev_err(dwc->dev, "failed to register gadget device\n"); - put_device(&dwc->gadget.dev); - goto err6; - } - ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); - goto err7; + goto err6; } return 0; -err7: - device_unregister(&dwc->gadget.dev); - err6: dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); free_irq(irq, dwc); @@ -2610,6 +2599,4 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), dwc->ctrl_req, dwc->ctrl_req_addr); - - device_unregister(&dwc->gadget.dev); } -- cgit v0.10.2 From 5ed01c6400270dccb8c3574061ff3d163f0fe3fe Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 15:03:59 +0200 Subject: usb: musb: gadget: let udc-core manage gadget-dev By simply setting a flag, we can delete a little boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index be18537..cadb750 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1887,12 +1887,11 @@ int musb_gadget_setup(struct musb *musb) musb->g.speed = USB_SPEED_UNKNOWN; /* this "gadget" abstracts/virtualizes the controller */ - dev_set_name(&musb->g.dev, "gadget"); musb->g.dev.parent = musb->controller; musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; musb->g.name = musb_driver_name; - + musb->g.register_my_device = true; musb->g.is_otg = 1; musb_g_init_endpoints(musb); @@ -1900,11 +1899,6 @@ int musb_gadget_setup(struct musb *musb) musb->is_active = 0; musb_platform_try_idle(musb, 0); - status = device_register(&musb->g.dev); - if (status != 0) { - put_device(&musb->g.dev); - return status; - } status = usb_add_gadget_udc(musb->controller, &musb->g); if (status) goto err; @@ -1919,8 +1913,6 @@ err: void musb_gadget_cleanup(struct musb *musb) { usb_del_gadget_udc(&musb->g); - if (musb->g.dev.parent) - device_unregister(&musb->g.dev); } /* -- cgit v0.10.2 From 6dfc84fcb6eb32621c557e64f7520be27c0d636a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 15:07:29 +0200 Subject: usb: gadget: omap_udc: let udc-core manage gadget->dev By simply setting a flag, we drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index f844565..c979272 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2632,10 +2632,9 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.max_speed = USB_SPEED_FULL; udc->gadget.name = driver_name; - device_initialize(&udc->gadget.dev); - dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.release = omap_udc_release; udc->gadget.dev.parent = &odev->dev; + udc->gadget.register_my_device = true; if (use_dma) udc->gadget.dev.dma_mask = odev->dev.dma_mask; @@ -2912,14 +2911,12 @@ bad_on_1710: } create_proc_file(); - status = device_add(&udc->gadget.dev); + status = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (status) goto cleanup4; - status = usb_add_gadget_udc(&pdev->dev, &udc->gadget); - if (!status) - return status; - /* If fail, fall through */ + return 0; + cleanup4: remove_proc_file(); @@ -2990,7 +2987,6 @@ static int omap_udc_remove(struct platform_device *pdev) release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); - device_unregister(&udc->gadget.dev); wait_for_completion(&done); return 0; -- cgit v0.10.2 From 12ad0fcaf2fbf3f48dd2b4dbaff372830aada8a2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 15:10:10 +0200 Subject: usb: gadget: amd5536udc: let udc-core manage gadget->dev By simply setting a flag, we drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 75973f3..eee01ea 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3080,7 +3080,6 @@ static void udc_pci_remove(struct pci_dev *pdev) if (dev->active) pci_disable_device(pdev); - device_unregister(&dev->gadget.dev); pci_set_drvdata(pdev, NULL); udc_remove(dev); @@ -3276,6 +3275,7 @@ static int udc_probe(struct udc *dev) dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; + dev->gadget.register_my_device = true; /* init registers, interrupts, ... */ startup_registers(dev); @@ -3301,13 +3301,6 @@ static int udc_probe(struct udc *dev) if (retval) goto finished; - retval = device_register(&dev->gadget.dev); - if (retval) { - usb_del_gadget_udc(&dev->gadget); - put_device(&dev->gadget.dev); - goto finished; - } - /* timer init */ init_timer(&udc_timer); udc_timer.function = udc_timer_function; -- cgit v0.10.2 From 2533beea9025254215be65cd1fca8da65019fd04 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 15:15:30 +0200 Subject: usb: gadget: at91_udc: let udc-core manage gadget->dev By simply setting a flag, we can remove some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 45dd292..47b7e58 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1726,6 +1726,7 @@ static int at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; + udc->gadget.register_my_device = true; udc->gadget.dev.parent = dev; if (pdev->dev.of_node) at91udc_of_init(udc, pdev->dev.of_node); @@ -1780,13 +1781,7 @@ static int at91udc_probe(struct platform_device *pdev) DBG("clocks missing\n"); retval = -ENODEV; /* NOTE: we "know" here that refcounts on these are NOPs */ - goto fail0b; - } - - retval = device_register(&udc->gadget.dev); - if (retval < 0) { - put_device(&udc->gadget.dev); - goto fail0b; + goto fail1; } /* don't do anything until we have both gadget driver and VBUS */ @@ -1857,8 +1852,6 @@ fail3: fail2: free_irq(udc->udp_irq, udc); fail1: - device_unregister(&udc->gadget.dev); -fail0b: iounmap(udc->udp_baseaddr); fail0a: if (cpu_is_at91rm9200()) @@ -1892,8 +1885,6 @@ static int __exit at91udc_remove(struct platform_device *pdev) gpio_free(udc->board.vbus_pin); } free_irq(udc->udp_irq, udc); - device_unregister(&udc->gadget.dev); - iounmap(udc->udp_baseaddr); if (cpu_is_at91rm9200()) -- cgit v0.10.2 From 621c723eb71d2f02baafe20a3eaefc3a4dec7788 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 15:21:36 +0200 Subject: usb: gadget: atmel_usba_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index b66130c..2404d0c 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1900,9 +1900,9 @@ static int __init usba_udc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", (unsigned long)fifo->start, udc->fifo); - device_initialize(&udc->gadget.dev); udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; + udc->gadget.register_my_device = true; platform_set_drvdata(pdev, udc); @@ -1962,12 +1962,6 @@ static int __init usba_udc_probe(struct platform_device *pdev) } udc->irq = irq; - ret = device_add(&udc->gadget.dev); - if (ret) { - dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret); - goto err_device_add; - } - if (gpio_is_valid(pdata->vbus_pin)) { if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) { udc->vbus_pin = pdata->vbus_pin; @@ -2007,9 +2001,6 @@ err_add_udc: gpio_free(udc->vbus_pin); } - device_unregister(&udc->gadget.dev); - -err_device_add: free_irq(irq, udc); err_request_irq: kfree(usba_ep); @@ -2053,8 +2044,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev) clk_put(udc->hclk); clk_put(udc->pclk); - device_unregister(&udc->gadget.dev); - return 0; } -- cgit v0.10.2 From 9b4ead05e7a3abf350d4ca1f7e0b71dc44f07f57 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:09:06 +0200 Subject: usb: gadget: bcm63xx_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index 8cc8253..c020b87 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2368,13 +2368,13 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) spin_lock_init(&udc->lock); INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process); - dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.ops = &bcm63xx_udc_ops; udc->gadget.name = dev_name(dev); udc->gadget.dev.parent = dev; udc->gadget.dev.release = bcm63xx_udc_gadget_release; udc->gadget.dev.dma_mask = dev->dma_mask; + udc->gadget.register_my_device = true; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; @@ -2414,17 +2414,12 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) } } - rc = device_register(&udc->gadget.dev); - if (rc) - goto out_uninit; - bcm63xx_udc_init_debugfs(udc); rc = usb_add_gadget_udc(dev, &udc->gadget); if (!rc) return 0; bcm63xx_udc_cleanup_debugfs(udc); - device_unregister(&udc->gadget.dev); out_uninit: bcm63xx_uninit_udc_hw(udc); return rc; @@ -2440,7 +2435,6 @@ static int bcm63xx_udc_remove(struct platform_device *pdev) bcm63xx_udc_cleanup_debugfs(udc); usb_del_gadget_udc(&udc->gadget); - device_unregister(&udc->gadget.dev); BUG_ON(udc->driver); platform_set_drvdata(pdev, NULL); -- cgit v0.10.2 From 002cb13f4a6f3c28510575922a226a7a09ab6e91 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:10:16 +0200 Subject: usb: gadget: dummy_hcd: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 8cf0c0f..a6950aa 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -983,16 +983,10 @@ static int dummy_udc_probe(struct platform_device *pdev) dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; + dum->gadget.register_my_device = true; - dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; - rc = device_register(&dum->gadget.dev); - if (rc < 0) { - put_device(&dum->gadget.dev); - return rc; - } - init_dummy_udc_hw(dum); rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget); @@ -1008,7 +1002,6 @@ static int dummy_udc_probe(struct platform_device *pdev) err_dev: usb_del_gadget_udc(&dum->gadget); err_udc: - device_unregister(&dum->gadget.dev); return rc; } @@ -1019,7 +1012,6 @@ static int dummy_udc_remove(struct platform_device *pdev) usb_del_gadget_udc(&dum->gadget); platform_set_drvdata(pdev, NULL); device_remove_file(&dum->gadget.dev, &dev_attr_function); - device_unregister(&dum->gadget.dev); return 0; } -- cgit v0.10.2 From c07d1b63ac3c5f18f07739a8736633dd6998c944 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:11:38 +0200 Subject: usb: gadget: fsl_qe_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 034477c..0f78cd8 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2523,13 +2523,9 @@ static int qe_udc_probe(struct platform_device *ofdev) /* name: Identifies the controller hardware type. */ udc->gadget.name = driver_name; - - device_initialize(&udc->gadget.dev); - - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.release = qe_udc_release; udc->gadget.dev.parent = &ofdev->dev; + udc->gadget.register_my_device = true; /* initialize qe_ep struct */ for (i = 0; i < USB_MAX_ENDPOINTS ; i++) { @@ -2592,13 +2588,9 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err5; } - ret = device_add(&udc->gadget.dev); - if (ret) - goto err6; - ret = usb_add_gadget_udc(&ofdev->dev, &udc->gadget); if (ret) - goto err7; + goto err6; dev_set_drvdata(&ofdev->dev, udc); dev_info(udc->dev, @@ -2606,8 +2598,6 @@ static int qe_udc_probe(struct platform_device *ofdev) (udc->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; -err7: - device_unregister(&udc->gadget.dev); err6: free_irq(udc->usb_irq, udc); err5: @@ -2702,7 +2692,6 @@ static int qe_udc_remove(struct platform_device *ofdev) iounmap(udc->usb_regs); - device_unregister(&udc->gadget.dev); /* wait for release() of gadget.dev to free udc */ wait_for_completion(&done); -- cgit v0.10.2 From eab35c4e6d952c91a413b408635ba6a9ec5fcf41 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:13:20 +0200 Subject: usb: gadget: fsl_udc_core: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 04d5fef..9140a2d 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2524,9 +2524,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; udc_controller->gadget.dev.of_node = pdev->dev.of_node; - ret = device_register(&udc_controller->gadget.dev); - if (ret < 0) - goto err_free_irq; + udc_controller->gadget.register_my_device = true; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; @@ -2559,7 +2557,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) DTD_ALIGNMENT, UDC_DMA_BOUNDARY); if (udc_controller->td_pool == NULL) { ret = -ENOMEM; - goto err_unregister; + goto err_free_irq; } ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget); @@ -2571,8 +2569,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) err_del_udc: dma_pool_destroy(udc_controller->td_pool); -err_unregister: - device_unregister(&udc_controller->gadget.dev); err_free_irq: free_irq(udc_controller->irq, udc_controller); err_iounmap: @@ -2622,7 +2618,6 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_DEVICE) release_mem_region(res->start, resource_size(res)); - device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ wait_for_completion(&done); -- cgit v0.10.2 From fc84d2fbe776f5352c3a981a5250a3ad83789f72 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:14:45 +0200 Subject: usb: gadget: fusb300_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 066cb89..d290172 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1422,15 +1422,12 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.ops = &fusb300_gadget_ops; - device_initialize(&fusb300->gadget.dev); - - dev_set_name(&fusb300->gadget.dev, "gadget"); - fusb300->gadget.max_speed = USB_SPEED_HIGH; fusb300->gadget.dev.parent = &pdev->dev; fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask; fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; + fusb300->gadget.register_my_device = true; fusb300->reg = reg; ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, @@ -1478,19 +1475,10 @@ static int __init fusb300_probe(struct platform_device *pdev) if (ret) goto err_add_udc; - ret = device_add(&fusb300->gadget.dev); - if (ret) { - pr_err("device_add error (%d)\n", ret); - goto err_add_device; - } - dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; -err_add_device: - usb_del_gadget_udc(&fusb300->gadget); - err_add_udc: fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); -- cgit v0.10.2 From 5637bf5b7a91804fe0230dff70024df078786090 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:17:45 +0200 Subject: usb: gadget: goku_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 85742d4..b4ea2cf 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1716,8 +1716,6 @@ static void goku_remove(struct pci_dev *pdev) pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device(pdev); - if (dev->registered) - device_unregister(&dev->gadget.dev); pci_set_drvdata(pdev, NULL); dev->regs = NULL; @@ -1756,11 +1754,11 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_FULL; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; + dev->gadget.register_my_device = true; /* now all the pci goodies ... */ retval = pci_enable_device(pdev); @@ -1810,12 +1808,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); #endif - retval = device_register(&dev->gadget.dev); - if (retval) { - put_device(&dev->gadget.dev); - goto err; - } - dev->registered = 1; retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); if (retval) goto err; diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index b4470d2..86d2ada 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -250,8 +250,7 @@ struct goku_udc { got_region:1, req_config:1, configured:1, - enabled:1, - registered:1; + enabled:1; /* pci state used to access those endpoints */ struct pci_dev *pdev; -- cgit v0.10.2 From 0b3702c62e41f3707cb8ba68bf46561597a6f0af Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:22:57 +0200 Subject: usb: gadget: imx_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 5bd930d..435b203 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1461,15 +1461,10 @@ static int __init imx_udc_probe(struct platform_device *pdev) imx_usb->clk = clk; imx_usb->dev = &pdev->dev; - device_initialize(&imx_usb->gadget.dev); - + imx_usb->gadget.register_my_device = true; imx_usb->gadget.dev.parent = &pdev->dev; imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; - ret = device_add(&imx_usb->gadget.dev); - if (retval) - goto fail4; - platform_set_drvdata(pdev, imx_usb); usb_init_data(imx_usb); @@ -1481,11 +1476,9 @@ static int __init imx_udc_probe(struct platform_device *pdev) ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget); if (ret) - goto fail5; + goto fail4; return 0; -fail5: - device_unregister(&imx_usb->gadget.dev); fail4: for (i = 0; i < IMX_USB_NB_EP + 1; i++) free_irq(imx_usb->usbd_int[i], imx_usb); @@ -1509,7 +1502,6 @@ static int __exit imx_udc_remove(struct platform_device *pdev) int i; usb_del_gadget_udc(&imx_usb->gadget); - device_unregister(&imx_usb->gadget.dev); imx_udc_disable(imx_usb); del_timer(&imx_usb->timer); -- cgit v0.10.2 From e0c9e4739a6f9217f35f1a59807a2f19a4cd7cdd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:26:46 +0200 Subject: usb: gadget: lpc32xx_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index aa04089..329e1c5 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3090,6 +3090,7 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) /* init software state */ udc->gadget.dev.parent = dev; + udc->gadget.register_my_device = true; udc->pdev = pdev; udc->dev = &pdev->dev; udc->enabled = 0; @@ -3248,12 +3249,6 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) udc_disable(udc); udc_reinit(udc); - retval = device_register(&udc->gadget.dev); - if (retval < 0) { - dev_err(udc->dev, "Device registration failure\n"); - goto dev_register_fail; - } - /* Request IRQs - low and high priority USB device IRQs are routed to * the same handler, while the DMA interrupt is routed elsewhere */ retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq, @@ -3320,8 +3315,6 @@ irq_dev_fail: irq_hp_fail: free_irq(udc->udp_irq[IRQ_USB_LP], udc); irq_lp_fail: - device_unregister(&udc->gadget.dev); -dev_register_fail: dma_pool_destroy(udc->dd_cache); dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, @@ -3376,8 +3369,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev) free_irq(udc->udp_irq[IRQ_USB_HP], udc); free_irq(udc->udp_irq[IRQ_USB_LP], udc); - device_unregister(&udc->gadget.dev); - clk_disable(udc->usb_otg_clk); clk_put(udc->usb_otg_clk); clk_disable(udc->usb_slv_clk); -- cgit v0.10.2 From 96d20c32f68b058b4430a6f711124511a2d9b361 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:35:12 +0200 Subject: usb: gadget: m66592-udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index c1b8c2d..43ad70d 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1538,7 +1538,6 @@ static int __exit m66592_remove(struct platform_device *pdev) struct m66592 *m66592 = dev_get_drvdata(&pdev->dev); usb_del_gadget_udc(&m66592->gadget); - device_del(&m66592->gadget.dev); del_timer_sync(&m66592->timer); iounmap(m66592->reg); @@ -1608,13 +1607,12 @@ static int __init m66592_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, m66592); m66592->gadget.ops = &m66592_gadget_ops; - device_initialize(&m66592->gadget.dev); - dev_set_name(&m66592->gadget.dev, "gadget"); m66592->gadget.max_speed = USB_SPEED_HIGH; m66592->gadget.dev.parent = &pdev->dev; m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; + m66592->gadget.register_my_device = true; init_timer(&m66592->timer); m66592->timer.function = m66592_timer; @@ -1674,12 +1672,6 @@ static int __init m66592_probe(struct platform_device *pdev) init_controller(m66592); - ret = device_add(&m66592->gadget.dev); - if (ret) { - pr_err("device_add error (%d)\n", ret); - goto err_device_add; - } - ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget); if (ret) goto err_add_udc; @@ -1688,9 +1680,6 @@ static int __init m66592_probe(struct platform_device *pdev) return 0; err_add_udc: - device_del(&m66592->gadget.dev); - -err_device_add: m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); clean_up3: -- cgit v0.10.2 From 7a071890b467e3b820b6afb13f2a877a249ae8f9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:37:24 +0200 Subject: usb: gadget: mv_u3d_core: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index b5cea27..565addc 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1792,8 +1792,6 @@ static int mv_u3d_remove(struct platform_device *dev) clk_put(u3d->clk); - device_unregister(&u3d->gadget.dev); - platform_set_drvdata(dev, NULL); kfree(u3d); @@ -1957,15 +1955,11 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&u3d->gadget.dev, "gadget"); u3d->gadget.dev.parent = &dev->dev; u3d->gadget.dev.dma_mask = dev->dev.dma_mask; u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ - - retval = device_register(&u3d->gadget.dev); - if (retval) - goto err_register_gadget_device; + u3d->gadget.register_my_device = true; mv_u3d_eps_init(u3d); @@ -1991,8 +1985,6 @@ static int mv_u3d_probe(struct platform_device *dev) return 0; err_unregister: - device_unregister(&u3d->gadget.dev); -err_register_gadget_device: free_irq(u3d->irq, &dev->dev); err_request_irq: err_get_irq: -- cgit v0.10.2 From 2cd807e7a8ee18d13fd06e84ef9f485a5c28a521 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:38:19 +0200 Subject: usb: gadget: mv_u3d_core: fix a compile warning Fix the following compile warning: mv_u3d_core.c:1766:12: warning: 'mv_u3d_remove' \ defined but not used [-Wunused-function] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 565addc..734ade1 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -2072,7 +2072,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove = __exit_p(mv_u3d_remove), + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .owner = THIS_MODULE, -- cgit v0.10.2 From 5dc7b773657d9e217eaacd51f74e9cea81260088 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:43:47 +0200 Subject: usb: gadget: mv_udc_core: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index c8cf959..a7afdfb 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2138,8 +2138,6 @@ static int mv_udc_remove(struct platform_device *pdev) mv_udc_disable(udc); - device_unregister(&udc->gadget.dev); - /* free dev, wait for the release() finished */ wait_for_completion(udc->done); @@ -2311,15 +2309,11 @@ static int mv_udc_probe(struct platform_device *pdev) udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ - - retval = device_register(&udc->gadget.dev); - if (retval) - goto err_destroy_dma; + udc->gadget.register_my_device = true; eps_init(udc); @@ -2342,7 +2336,7 @@ static int mv_udc_probe(struct platform_device *pdev) if (!udc->qwork) { dev_err(&pdev->dev, "cannot create workqueue\n"); retval = -ENOMEM; - goto err_unregister; + goto err_destroy_dma; } INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); @@ -2370,8 +2364,6 @@ static int mv_udc_probe(struct platform_device *pdev) err_create_workqueue: destroy_workqueue(udc->qwork); -err_unregister: - device_unregister(&udc->gadget.dev); err_destroy_dma: dma_pool_destroy(udc->dtd_pool); err_free_dma: -- cgit v0.10.2 From c9f9c849ae873d592823d31c8e1dcade5c46fe14 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:48:12 +0200 Subject: usb: gadget: net2272: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index d226058..635248f 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2209,7 +2209,6 @@ net2272_remove(struct net2272 *dev) free_irq(dev->irq, dev); iounmap(dev->base_addr); - device_unregister(&dev->gadget.dev); device_remove_file(dev->dev, &dev_attr_registers); dev_info(dev->dev, "unbind\n"); @@ -2236,11 +2235,11 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&ret->gadget.dev, "gadget"); ret->gadget.dev.parent = dev; ret->gadget.dev.dma_mask = dev->dma_mask; ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; + ret->gadget.register_my_device = true; return ret; } @@ -2275,12 +2274,9 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) dma_mode_string()); dev_info(dev->dev, "version: %s\n", driver_vers); - ret = device_register(&dev->gadget.dev); - if (ret) - goto err_irq; ret = device_create_file(dev->dev, &dev_attr_registers); if (ret) - goto err_dev_reg; + goto err_irq; ret = usb_add_gadget_udc(dev->dev, &dev->gadget); if (ret) @@ -2290,8 +2286,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) err_add_udc: device_remove_file(dev->dev, &dev_attr_registers); - err_dev_reg: - device_unregister(&dev->gadget.dev); err_irq: free_irq(dev->irq, dev); err: -- cgit v0.10.2 From 654dfe4d2ab0d00573dcb0d7d7c957165dc4d9f2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:52:14 +0200 Subject: usb: gadget: net2280: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index a1b650e..c55af42 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2679,7 +2679,6 @@ static void net2280_remove (struct pci_dev *pdev) pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device (pdev); - device_unregister (&dev->gadget.dev); device_remove_file (&pdev->dev, &dev_attr_registers); pci_set_drvdata (pdev, NULL); @@ -2711,11 +2710,11 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; + dev->gadget.register_my_device = true; /* now all the pci goodies ... */ if (pci_enable_device (pdev) < 0) { @@ -2823,8 +2822,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - retval = device_register (&dev->gadget.dev); - if (retval) goto done; retval = device_create_file (&pdev->dev, &dev_attr_registers); if (retval) goto done; -- cgit v0.10.2 From 5a8a375714d007f556c81b739aa69e3c745d8015 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 16:56:35 +0200 Subject: usb: gadget: pch_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index a787a8e..7032145 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -358,7 +358,6 @@ struct pch_udc_dev { prot_stall:1, irq_registered:1, mem_region:1, - registered:1, suspended:1, connected:1, vbus_session:1, @@ -3078,8 +3077,6 @@ static void pch_udc_remove(struct pci_dev *pdev) pci_resource_len(pdev, PCH_UDC_PCI_BAR)); if (dev->active) pci_disable_device(pdev); - if (dev->registered) - device_unregister(&dev->gadget.dev); kfree(dev); pci_set_drvdata(pdev, NULL); } @@ -3196,17 +3193,12 @@ static int pch_udc_probe(struct pci_dev *pdev, if (retval) goto finished; - dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; - - retval = device_register(&dev->gadget.dev); - if (retval) - goto finished; - dev->registered = 1; + dev->gadget.register_my_device = true; /* Put the device in disconnected state till a driver is bound */ pch_udc_set_disconnect(dev); -- cgit v0.10.2 From 80bf5343decf65d9e4141143d49e6ed381565cbf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:01:28 +0200 Subject: usb: gadget: r8a66597-udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index f46a1b7..ae94c0e 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1837,7 +1837,6 @@ static int __exit r8a66597_remove(struct platform_device *pdev) clk_put(r8a66597->clk); } - device_unregister(&r8a66597->gadget.dev); kfree(r8a66597); return 0; } @@ -1915,17 +1914,12 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; - dev_set_name(&r8a66597->gadget.dev, "gadget"); r8a66597->gadget.max_speed = USB_SPEED_HIGH; r8a66597->gadget.dev.parent = &pdev->dev; r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; - ret = device_register(&r8a66597->gadget.dev); - if (ret < 0) { - dev_err(&pdev->dev, "device_register failed\n"); - goto clean_up; - } + r8a66597->gadget.register_my_device = true; init_timer(&r8a66597->timer); r8a66597->timer.function = r8a66597_timer; @@ -1939,7 +1933,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(r8a66597->clk); - goto clean_up_dev; + goto clean_up; } clk_enable(r8a66597->clk); } @@ -2007,8 +2001,6 @@ clean_up2: clk_disable(r8a66597->clk); clk_put(r8a66597->clk); } -clean_up_dev: - device_unregister(&r8a66597->gadget.dev); clean_up: if (r8a66597) { if (r8a66597->sudmac_reg) -- cgit v0.10.2 From b8d833a327aaed6637398446ec8a3e555b3ddd11 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:03:10 +0200 Subject: usb: gadget: s3c-hsotg: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Reviewed-by: Tomasz Figa Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index c26564f..5fbd233 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3567,17 +3567,13 @@ static int s3c_hsotg_probe(struct platform_device *pdev) dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); - device_initialize(&hsotg->gadget.dev); - - dev_set_name(&hsotg->gadget.dev, "gadget"); - hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); - hsotg->gadget.dev.parent = dev; hsotg->gadget.dev.dma_mask = dev->dma_mask; hsotg->gadget.dev.release = s3c_hsotg_release; + hsotg->gadget.register_my_device = true; /* reset the system */ @@ -3658,12 +3654,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) s3c_hsotg_phy_disable(hsotg); - ret = device_add(&hsotg->gadget.dev); - if (ret) { - put_device(&hsotg->gadget.dev); - goto err_ep_mem; - } - ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget); if (ret) goto err_ep_mem; @@ -3702,10 +3692,8 @@ static int s3c_hsotg_remove(struct platform_device *pdev) } s3c_hsotg_phy_disable(hsotg); - clk_disable_unprepare(hsotg->clk); - device_unregister(&hsotg->gadget.dev); return 0; } -- cgit v0.10.2 From 40ed30cff5957d15dcd2638c6f48fae839a3100a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:04:56 +0200 Subject: usb: gadget: s3c-hsudc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 458965a..c4ff747 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1303,18 +1303,16 @@ static int s3c_hsudc_probe(struct platform_device *pdev) spin_lock_init(&hsudc->lock); - dev_set_name(&hsudc->gadget.dev, "gadget"); - hsudc->gadget.max_speed = USB_SPEED_HIGH; hsudc->gadget.ops = &s3c_hsudc_gadget_ops; hsudc->gadget.name = dev_name(dev); hsudc->gadget.dev.parent = dev; hsudc->gadget.dev.dma_mask = dev->dma_mask; hsudc->gadget.ep0 = &hsudc->ep[0].ep; - hsudc->gadget.is_otg = 0; hsudc->gadget.is_a_peripheral = 0; hsudc->gadget.speed = USB_SPEED_UNKNOWN; + hsudc->gadget.register_my_device = true; s3c_hsudc_setup_ep(hsudc); @@ -1345,12 +1343,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) disable_irq(hsudc->irq); local_irq_enable(); - ret = device_register(&hsudc->gadget.dev); - if (ret) { - put_device(&hsudc->gadget.dev); - goto err_add_device; - } - ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); if (ret) goto err_add_udc; @@ -1359,7 +1351,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) return 0; err_add_udc: - device_unregister(&hsudc->gadget.dev); err_add_device: clk_disable(hsudc->uclk); err_res: -- cgit v0.10.2 From b1e1eaba2916427d25fbd411cd863052836560ff Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:06:42 +0200 Subject: usb: gadget: s3c2410_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 08f8965..c413494 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1824,16 +1824,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev) goto err_mem; } - device_initialize(&udc->gadget.dev); udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - - /* Bind the driver */ - retval = device_add(&udc->gadget.dev); - if (retval) { - dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval); - goto err_device_add; - } + udc->gadget.register_my_device = true; the_controller = udc; platform_set_drvdata(pdev, udc); @@ -1923,8 +1916,6 @@ err_gpio_claim: err_int: free_irq(IRQ_USBD, udc); err_map: - device_unregister(&udc->gadget.dev); -err_device_add: iounmap(base_addr); err_mem: release_mem_region(rsrc_start, rsrc_len); @@ -1946,7 +1937,6 @@ static int s3c2410_udc_remove(struct platform_device *pdev) return -EBUSY; usb_del_gadget_udc(&udc->gadget); - device_unregister(&udc->gadget.dev); debugfs_remove(udc->regs_info); if (udc_info && !udc_info->udc_command && -- cgit v0.10.2 From 0972ef71b4841a59fab6f998d6e6c2c684443583 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:08:01 +0200 Subject: usb: renesas_usbhs: gadget: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 78fca97..5d5fab0 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -976,15 +976,12 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) /* * init gadget */ - dev_set_name(&gpriv->gadget.dev, "gadget"); gpriv->gadget.dev.parent = dev; gpriv->gadget.dev.release = usbhs_mod_gadget_release; gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; - ret = device_register(&gpriv->gadget.dev); - if (ret < 0) - goto err_add_udc; + gpriv->gadget.register_my_device = true; INIT_LIST_HEAD(&gpriv->gadget.ep_list); @@ -1014,15 +1011,13 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ret = usb_add_gadget_udc(dev, &gpriv->gadget); if (ret) - goto err_register; + goto err_add_udc; dev_info(dev, "gadget probed\n"); return 0; -err_register: - device_unregister(&gpriv->gadget.dev); err_add_udc: kfree(gpriv->uep); @@ -1038,8 +1033,6 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv) usb_del_gadget_udc(&gpriv->gadget); - device_unregister(&gpriv->gadget.dev); - kfree(gpriv->uep); kfree(gpriv); } -- cgit v0.10.2 From b73f5a2a0a2b2ff10d941e35c2ff08fcc04a9862 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:28:30 +0200 Subject: usb: gadget: pxa25x_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d0f3748..8996fcb 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2138,16 +2138,9 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev; - device_initialize(&dev->gadget.dev); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - - retval = device_add(&dev->gadget.dev); - if (retval) { - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - goto err_device_add; - } + dev->gadget.register_my_device = true; the_controller = dev; platform_set_drvdata(pdev, dev); @@ -2199,8 +2192,6 @@ lubbock_fail0: free_irq(irq, dev); #endif err_irq1: - device_unregister(&dev->gadget.dev); - err_device_add: if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: @@ -2226,7 +2217,6 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) return -EBUSY; usb_del_gadget_udc(&dev->gadget); - device_unregister(&dev->gadget.dev); dev->pullup = 0; pullup(dev); -- cgit v0.10.2 From 45f596a6d776532f03135d48f10a1164006f9466 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:29:39 +0200 Subject: usb: gadget: pxa27x_udc: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Acked-by: Robert Jarzmik Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 2fc8676..1c5bfaa 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1871,7 +1871,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g, udc->driver = NULL; - if (!IS_ERR_OR_NULL(udc->transceiver)) return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; @@ -2456,9 +2455,9 @@ static int __init pxa_udc_probe(struct platform_device *pdev) goto err_map; } - device_initialize(&udc->gadget.dev); udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = NULL; + udc->gadget.register_my_device = true; udc->vbus_sensed = 0; the_controller = udc; @@ -2475,12 +2474,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev) goto err_irq; } - retval = device_add(&udc->gadget.dev); - if (retval) { - dev_err(udc->dev, "device_add error %d\n", retval); - goto err_dev_add; - } - retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (retval) goto err_add_udc; @@ -2490,8 +2483,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev) return 0; err_add_udc: - device_unregister(&udc->gadget.dev); -err_dev_add: free_irq(udc->irq, udc); err_irq: iounmap(udc->regs); @@ -2512,7 +2503,6 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) int gpio = udc->mach->gpio_pullup; usb_del_gadget_udc(&udc->gadget); - device_del(&udc->gadget.dev); usb_gadget_unregister_driver(udc->driver); free_irq(udc->irq, udc); pxa_cleanup_debugfs(udc); -- cgit v0.10.2 From dc9e2873b740331b186b8f315fd18bbc97108d2e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:36:39 +0200 Subject: usb: chipidea: let udc-core manage gadget->dev By simply setting a flag, we can drop some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index f64fbea..e95e8bb 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1717,11 +1717,11 @@ static int udc_start(struct ci13xxx *ci) INIT_LIST_HEAD(&ci->gadget.ep_list); - dev_set_name(&ci->gadget.dev, "gadget"); ci->gadget.dev.dma_mask = dev->dma_mask; ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; ci->gadget.dev.parent = dev; ci->gadget.dev.release = udc_release; + ci->gadget.register_my_device = true; /* alloc resources */ ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, @@ -1761,15 +1761,9 @@ static int udc_start(struct ci13xxx *ci) hw_enable_vbus_intr(ci); } - retval = device_register(&ci->gadget.dev); - if (retval) { - put_device(&ci->gadget.dev); - goto put_transceiver; - } - retval = dbg_create_files(ci->dev); if (retval) - goto unreg_device; + goto put_transceiver; if (!IS_ERR_OR_NULL(ci->transceiver)) { retval = otg_set_peripheral(ci->transceiver->otg, @@ -1797,8 +1791,6 @@ remove_trans: dev_err(dev, "error = %i\n", retval); remove_dbg: dbg_remove_files(ci->dev); -unreg_device: - device_unregister(&ci->gadget.dev); put_transceiver: if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy) usb_put_phy(ci->transceiver); @@ -1837,7 +1829,6 @@ static void udc_stop(struct ci13xxx *ci) usb_put_phy(ci->transceiver); } dbg_remove_files(ci->dev); - device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ memset(&ci->gadget, 0, sizeof(ci->gadget)); } -- cgit v0.10.2 From 7bce401cc6db5508ef2517e45bd8caf7ce0a15ee Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:41:00 +0200 Subject: usb: gadget: drop now unnecessary flag We don't need the ->register_my_device flag anymore because all UDC drivers have been properly converted. Let's remove every history of it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e95e8bb..1b65ac8 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1721,7 +1721,6 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; ci->gadget.dev.parent = dev; ci->gadget.dev.release = udc_release; - ci->gadget.register_my_device = true; /* alloc resources */ ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 10bb161..65493b6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2499,7 +2499,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; dwc->gadget.dev.release = dwc3_gadget_release; - dwc->gadget.register_my_device = true; dwc->gadget.name = "dwc3-gadget"; /* diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index eee01ea..eec4461 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3275,7 +3275,6 @@ static int udc_probe(struct udc *dev) dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; - dev->gadget.register_my_device = true; /* init registers, interrupts, ... */ startup_registers(dev); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 47b7e58..9936de9 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1726,7 +1726,6 @@ static int at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; - udc->gadget.register_my_device = true; udc->gadget.dev.parent = dev; if (pdev->dev.of_node) at91udc_of_init(udc, pdev->dev.of_node); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 2404d0c..41518e6 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1902,7 +1902,6 @@ static int __init usba_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - udc->gadget.register_my_device = true; platform_set_drvdata(pdev, udc); diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index c020b87..d4f73e1 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2374,7 +2374,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = dev; udc->gadget.dev.release = bcm63xx_udc_gadget_release; udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.register_my_device = true; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a6950aa..c4f27d5 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -983,7 +983,6 @@ static int dummy_udc_probe(struct platform_device *pdev) dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; - dum->gadget.register_my_device = true; dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 0f78cd8..0e7531b 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2525,7 +2525,6 @@ static int qe_udc_probe(struct platform_device *ofdev) udc->gadget.name = driver_name; udc->gadget.dev.release = qe_udc_release; udc->gadget.dev.parent = &ofdev->dev; - udc->gadget.register_my_device = true; /* initialize qe_ep struct */ for (i = 0; i < USB_MAX_ENDPOINTS ; i++) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 9140a2d..f33b900 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2524,7 +2524,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; udc_controller->gadget.dev.of_node = pdev->dev.of_node; - udc_controller->gadget.register_my_device = true; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index d290172..2d3c8b3 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1427,7 +1427,6 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask; fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; - fusb300->gadget.register_my_device = true; fusb300->reg = reg; ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index b4ea2cf..8a6c666 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1758,7 +1758,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; - dev->gadget.register_my_device = true; /* now all the pci goodies ... */ retval = pci_enable_device(pdev); diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 435b203..9c5b745 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1461,7 +1461,6 @@ static int __init imx_udc_probe(struct platform_device *pdev) imx_usb->clk = clk; imx_usb->dev = &pdev->dev; - imx_usb->gadget.register_my_device = true; imx_usb->gadget.dev.parent = &pdev->dev; imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 329e1c5..67c3ef9 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3090,7 +3090,6 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) /* init software state */ udc->gadget.dev.parent = dev; - udc->gadget.register_my_device = true; udc->pdev = pdev; udc->dev = &pdev->dev; udc->enabled = 0; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 43ad70d..eb61d0b 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1612,7 +1612,6 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; - m66592->gadget.register_my_device = true; init_timer(&m66592->timer); m66592->timer.function = m66592_timer; diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 734ade1..e5735fc 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1959,7 +1959,6 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.dev.dma_mask = dev->dev.dma_mask; u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ - u3d->gadget.register_my_device = true; mv_u3d_eps_init(u3d); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index a7afdfb..be35573 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2313,7 +2313,6 @@ static int mv_udc_probe(struct platform_device *pdev) udc->gadget.dev.dma_mask = pdev->dev.dma_mask; udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ - udc->gadget.register_my_device = true; eps_init(udc); diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 635248f..78c8bb5 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2239,7 +2239,6 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.dev.dma_mask = dev->dma_mask; ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; - ret->gadget.register_my_device = true; return ret; } diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index c55af42..2089d9b 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2714,7 +2714,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; - dev->gadget.register_my_device = true; /* now all the pci goodies ... */ if (pci_enable_device (pdev) < 0) { diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index c979272..b23c861 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2634,7 +2634,6 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.dev.release = omap_udc_release; udc->gadget.dev.parent = &odev->dev; - udc->gadget.register_my_device = true; if (use_dma) udc->gadget.dev.dma_mask = odev->dev.dma_mask; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 7032145..e8c9afd 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -3198,7 +3198,6 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; - dev->gadget.register_my_device = true; /* Put the device in disconnected state till a driver is bound */ pch_udc_set_disconnect(dev); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8996fcb..e29bb87 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2140,7 +2140,6 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - dev->gadget.register_my_device = true; the_controller = dev; platform_set_drvdata(pdev, dev); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 1c5bfaa..07ce147 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2457,7 +2457,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = NULL; - udc->gadget.register_my_device = true; udc->vbus_sensed = 0; the_controller = udc; diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index ae94c0e..a67d477 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1919,7 +1919,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; - r8a66597->gadget.register_my_device = true; init_timer(&r8a66597->timer); r8a66597->timer.function = r8a66597_timer; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 5fbd233..8ae0bd9 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3573,7 +3573,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->gadget.dev.parent = dev; hsotg->gadget.dev.dma_mask = dev->dma_mask; hsotg->gadget.dev.release = s3c_hsotg_release; - hsotg->gadget.register_my_device = true; /* reset the system */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index c4ff747..7fc3de5 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1312,7 +1312,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->gadget.is_otg = 0; hsudc->gadget.is_a_peripheral = 0; hsudc->gadget.speed = USB_SPEED_UNKNOWN; - hsudc->gadget.register_my_device = true; s3c_hsudc_setup_ep(hsudc); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index c413494..a669081 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1826,7 +1826,6 @@ static int s3c2410_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - udc->gadget.register_my_device = true; the_controller = udc; platform_set_drvdata(pdev, udc); diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 9195054..40b1d88 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -173,13 +173,11 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (!udc) goto err1; - if (gadget->register_my_device) { - dev_set_name(&gadget->dev, "gadget"); + dev_set_name(&gadget->dev, "gadget"); - ret = device_register(&gadget->dev); - if (ret) - goto err2; - } + ret = device_register(&gadget->dev); + if (ret) + goto err2; device_initialize(&udc->dev); udc->dev.release = usb_udc_release; @@ -211,8 +209,8 @@ err3: put_device(&udc->dev); err2: - if (gadget->register_my_device) - put_device(&gadget->dev); + put_device(&gadget->dev); + err1: return ret; } @@ -266,9 +264,7 @@ found: kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); device_unregister(&udc->dev); - - if (gadget->register_my_device) - device_unregister(&gadget->dev); + device_unregister(&gadget->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index cadb750..e363033 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1891,7 +1891,6 @@ int musb_gadget_setup(struct musb *musb) musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; musb->g.name = musb_driver_name; - musb->g.register_my_device = true; musb->g.is_otg = 1; musb_g_init_endpoints(musb); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 5d5fab0..6a3afa9 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -981,7 +981,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; - gpriv->gadget.register_my_device = true; INIT_LIST_HEAD(&gpriv->gadget.ep_list); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index fcd9ef8..2e297e8 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -494,9 +494,6 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. - * @register_my_device: Flag telling udc-core that UDC driver didn't - * register the gadget device to the driver model. Temporary until - * all UDC drivers are fixed up properly. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. @@ -534,7 +531,6 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; - unsigned register_my_device:1; const char *name; struct device dev; unsigned out_epnum; -- cgit v0.10.2 From e58ebcd14210d3def22ba44d9d14daffbe2eb8e0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 14:48:36 +0200 Subject: usb: gadget: s3c-hsotg: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 8ae0bd9..2812fa5 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -39,8 +39,6 @@ #include "s3c-hsotg.h" -#define DMA_ADDR_INVALID (~((dma_addr_t)0)) - static const char * const s3c_hsotg_supply_names[] = { "vusb_d", /* digital USB supply, 1.2V */ "vusb_a", /* analog USB supply, 1.1V */ @@ -405,7 +403,6 @@ static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep, INIT_LIST_HEAD(&req->queue); - req->req.dma = DMA_ADDR_INVALID; return &req->req; } @@ -435,24 +432,12 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg, struct s3c_hsotg_req *hs_req) { struct usb_request *req = &hs_req->req; - enum dma_data_direction dir; - - dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; /* ignore this if we're not moving any data */ if (hs_req->req.length == 0) return; - if (hs_req->mapped) { - /* we mapped this, so unmap and remove the dma */ - - dma_unmap_single(hsotg->dev, req->dma, req->length, dir); - - req->dma = DMA_ADDR_INVALID; - hs_req->mapped = 0; - } else { - dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir); - } + usb_gadget_unmap_request(&hsotg->gadget, hs_req, hs_ep->dir_in); } /** @@ -852,37 +837,16 @@ static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct usb_request *req) { - enum dma_data_direction dir; struct s3c_hsotg_req *hs_req = our_req(req); - - dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + int ret; /* if the length is zero, ignore the DMA data */ if (hs_req->req.length == 0) return 0; - if (req->dma == DMA_ADDR_INVALID) { - dma_addr_t dma; - - dma = dma_map_single(hsotg->dev, req->buf, req->length, dir); - - if (unlikely(dma_mapping_error(hsotg->dev, dma))) - goto dma_error; - - if (dma & 3) { - dev_err(hsotg->dev, "%s: unaligned dma buffer\n", - __func__); - - dma_unmap_single(hsotg->dev, dma, req->length, dir); - return -EINVAL; - } - - hs_req->mapped = 1; - req->dma = dma; - } else { - dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir); - hs_req->mapped = 0; - } + ret = usb_gadget_map_request(&hsotg->gadget, req, hs_ep->dir_in); + if (ret) + goto dma_error; return 0; -- cgit v0.10.2 From 3be49f38d38a1a2c1cffc61579290c9ba6404446 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 14:51:34 +0200 Subject: usb: gadget: amd5536udc: remove unused structure member that member isn't used anywhere in the driver and be removed with no mercy. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index f1bf32e..6744d3b 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -472,7 +472,6 @@ struct udc_request { /* flags */ unsigned dma_going : 1, - dma_mapping : 1, dma_done : 1; /* phys. address */ dma_addr_t td_phys; -- cgit v0.10.2 From 1bda9df8dd6d39ac369020594c82d3f70f3a4721 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 16:57:02 +0200 Subject: usb: gadget: atmel_usba_udc: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 41518e6..94aeba8 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -489,13 +489,8 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status) if (req->req.status == -EINPROGRESS) req->req.status = status; - if (req->mapped) { - dma_unmap_single( - &udc->pdev->dev, req->req.dma, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + if (req->using_dma) + usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", @@ -684,7 +679,6 @@ usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) return NULL; INIT_LIST_HEAD(&req->queue); - req->req.dma = DMA_ADDR_INVALID; return &req->req; } @@ -717,20 +711,11 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, return -EINVAL; } - req->using_dma = 1; - - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single( - &udc->pdev->dev, req->req.buf, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device( - &udc->pdev->dev, req->req.dma, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 0; - } + ret = usb_gadget_map_request(&udc->gadget, &req->req, ep->is_in); + if (ret) + return ret; + req->using_dma = 1; req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length) | USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE | USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE; -- cgit v0.10.2 From 5f6da778578de6b7c43b943cf9cfba12289e9ff3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 17:03:21 +0200 Subject: usb: gadget: fsl_udc_core: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index f33b900..c948241 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -185,20 +185,7 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); } - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); + usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); if (status && (status != -ESHUTDOWN)) VDBG("complete %s req %p stat %d len %u/%u", @@ -888,6 +875,7 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) struct fsl_req *req = container_of(_req, struct fsl_req, req); struct fsl_udc *udc; unsigned long flags; + int ret; /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf @@ -910,22 +898,9 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->ep = ep; - /* map virtual address to hardware */ - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + return ret; req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -1290,6 +1265,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) { struct fsl_req *req = udc->status_req; struct fsl_ep *ep; + int ret; if (direction == EP_DIR_IN) udc->ep0_dir = USB_DIR_IN; @@ -1307,10 +1283,9 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) req->req.complete = NULL; req->dtd_count = 0; - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, - ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + return ret; if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0) fsl_queue_td(ep, req); @@ -1353,6 +1328,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, u16 tmp = 0; /* Status, cpu endian */ struct fsl_req *req; struct fsl_ep *ep; + int ret; ep = &udc->eps[0]; @@ -1390,10 +1366,9 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->req.complete = NULL; req->dtd_count = 0; - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, - ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + goto stall; /* prime the data phase */ if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0)) -- cgit v0.10.2 From 0324f25fc66cd94273d0aa67637ed260ff70f01e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 17:08:28 +0200 Subject: usb: gadget: fusb300: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 2d3c8b3..5c9dd06 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -938,25 +938,22 @@ IDMA_RESET: static void fusb300_set_idma(struct fusb300_ep *ep, struct fusb300_request *req) { - dma_addr_t d; + int ret; - d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE); - - if (dma_mapping_error(NULL, d)) { - printk(KERN_DEBUG "dma_mapping_error\n"); + ret = usb_gadget_map_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); + if (ret) return; - } - - dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE); fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); - fusb300_fill_idma_prdtbl(ep, d, req->req.length); + fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length); /* check idma is done */ fusb300_wait_idma_finished(ep); - dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE); + usb_gadget_unmap_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); } static void in_ep_fifo_handler(struct fusb300_ep *ep) -- cgit v0.10.2 From 369ac9cb3e61ff9281dfec14e1e0ee4855c41352 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 17:13:27 +0200 Subject: usb: gadget: lpc32xx_udc: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 67c3ef9..1478327 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -1469,23 +1469,7 @@ static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) status = req->req.status; if (ep->lep) { - enum dma_data_direction direction; - - if (ep->is_in) - direction = DMA_TO_DEVICE; - else - direction = DMA_FROM_DEVICE; - - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - direction); - req->req.dma = 0; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - direction); + usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); /* Free DDs */ udc_dd_free(udc, req->dd_desc_ptr); @@ -1841,26 +1825,11 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, } if (ep->lep) { - enum dma_data_direction direction; struct lpc32xx_usbd_dd_gad *dd; - /* Map DMA pointer */ - if (ep->is_in) - direction = DMA_TO_DEVICE; - else - direction = DMA_FROM_DEVICE; - - if (req->req.dma == 0) { - req->req.dma = dma_map_single( - ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, direction); - req->mapped = 1; - } else { - dma_sync_single_for_device( - ep->udc->gadget.dev.parent, req->req.dma, - req->req.length, direction); - req->mapped = 0; - } + status = usb_gadget_map_request(&udc->gadget, _req, ep->is_in); + if (status) + return status; /* For the request, build a list of DDs */ dd = udc_dd_alloc(udc); -- cgit v0.10.2 From 4c0c6d0085337549e1b4fc97d9616eae732049d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 28 Jan 2013 17:19:34 +0200 Subject: usb: gadget: mv_udc_core: switch over to usb_gadget_map/unmap_request() we have generic implementations for a reason, let's use them Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index be35573..7f4d19d 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -237,18 +237,7 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status) dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma); } - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); + usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); if (status && (status != -ESHUTDOWN)) dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u", @@ -732,21 +721,9 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->ep = ep; /* map virtual address to hardware */ - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, ep_dir(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_dir(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } + retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep)); + if (retval) + return retval; req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -780,18 +757,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return 0; err_unmap_dma: - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); + usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep)); return retval; } @@ -1528,14 +1494,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) return 0; out: - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); return retval; } -- cgit v0.10.2 From f122d33e4b0045a42238b9a4c3943adf7e8313c1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Feb 2013 15:15:11 +0200 Subject: usb: dwc3: core: explicitly setup and cleanup event buffers Make the call to dwc3_event_buffers_setup() and dwc3_event_buffers_cleanup() explicit, so it's easier to implement PM. Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ffa6b00..4743508 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -337,12 +337,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err0; - } - return 0; err0: @@ -351,8 +345,6 @@ err0: static void dwc3_core_exit(struct dwc3 *dwc) { - dwc3_event_buffers_cleanup(dwc); - usb_phy_shutdown(dwc->usb2_phy); usb_phy_shutdown(dwc->usb3_phy); } @@ -480,6 +472,12 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err1; + } + mode = DWC3_MODE(dwc->hwparams.hwparams0); switch (mode) { @@ -488,7 +486,7 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_gadget_init(dwc); if (ret) { dev_err(dev, "failed to initialize gadget\n"); - goto err1; + goto err2; } break; case DWC3_MODE_HOST: @@ -496,7 +494,7 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_host_init(dwc); if (ret) { dev_err(dev, "failed to initialize host\n"); - goto err1; + goto err2; } break; case DWC3_MODE_DRD: @@ -504,32 +502,32 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_host_init(dwc); if (ret) { dev_err(dev, "failed to initialize host\n"); - goto err1; + goto err2; } ret = dwc3_gadget_init(dwc); if (ret) { dev_err(dev, "failed to initialize gadget\n"); - goto err1; + goto err2; } break; default: dev_err(dev, "Unsupported mode of operation %d\n", mode); - goto err1; + goto err2; } dwc->mode = mode; ret = dwc3_debugfs_init(dwc); if (ret) { dev_err(dev, "failed to initialize debugfs\n"); - goto err2; + goto err3; } pm_runtime_allow(dev); return 0; -err2: +err3: switch (mode) { case DWC3_MODE_DEVICE: dwc3_gadget_exit(dwc); @@ -546,6 +544,9 @@ err2: break; } +err2: + dwc3_event_buffers_cleanup(dwc); + err1: dwc3_core_exit(dwc); @@ -583,6 +584,7 @@ static int dwc3_remove(struct platform_device *pdev) break; } + dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); dwc3_core_exit(dwc); -- cgit v0.10.2 From 8698e2acf3a5e8d6f260ca7675f94e5087df5ae8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Feb 2013 15:24:04 +0200 Subject: usb: dwc3: gadget: introduce and use enable/disable irq methods we don't need to enable IRQs until we have a gadget driver loaded and ready to work, so let's delay IRQ enable to ->udc_start() and IRQ disable to ->udc_stop(). While at that, also move the related use of request_irq() and free_irq(). Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 65493b6..db20317 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1469,6 +1469,32 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) return ret; } +static void dwc3_gadget_enable_irq(struct dwc3 *dwc) +{ + u32 reg; + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); +} + +static void dwc3_gadget_disable_irq(struct dwc3 *dwc) +{ + /* mask all interrupts */ + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); +} + +static irqreturn_t dwc3_interrupt(int irq, void *_dwc); + static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { @@ -1476,6 +1502,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, struct dwc3_ep *dep; unsigned long flags; int ret = 0; + int irq; u32 reg; spin_lock_irqsave(&dwc->lock, flags); @@ -1536,6 +1563,17 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, + "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err1; + } + + dwc3_gadget_enable_irq(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; @@ -1554,9 +1592,14 @@ static int dwc3_gadget_stop(struct usb_gadget *g, { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; + int irq; spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_disable_irq(dwc); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + free_irq(irq, dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -2454,7 +2497,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) { u32 reg; int ret; - int irq; dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), &dwc->ctrl_req_addr, GFP_KERNEL); @@ -2510,33 +2552,11 @@ int dwc3_gadget_init(struct dwc3 *dwc) if (ret) goto err4; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - - ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, - "dwc3", dwc); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err5; - } - reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg |= DWC3_DCFG_LPM_CAP; dwc3_writel(dwc->regs, DWC3_DCFG, reg); - /* Enable all but Start and End of Frame IRQs */ - reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | - DWC3_DEVTEN_EVNTOVERFLOWEN | - DWC3_DEVTEN_CMDCMPLTEN | - DWC3_DEVTEN_ERRTICERREN | - DWC3_DEVTEN_WKUPEVTEN | - DWC3_DEVTEN_ULSTCNGEN | - DWC3_DEVTEN_CONNECTDONEEN | - DWC3_DEVTEN_USBRSTEN | - DWC3_DEVTEN_DISCONNEVTEN); - dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); - - /* automatic phy suspend only on recent versions */ + /* Enable USB2 LPM and automatic phy suspend only on recent versions */ if (dwc->revision >= DWC3_REVISION_194A) { dwc3_gadget_usb2_phy_suspend(dwc, false); dwc3_gadget_usb3_phy_suspend(dwc, false); @@ -2545,15 +2565,11 @@ int dwc3_gadget_init(struct dwc3 *dwc) ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); - goto err6; + goto err5; } return 0; -err6: - dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); - free_irq(irq, dwc); - err5: dwc3_gadget_free_endpoints(dwc); @@ -2578,13 +2594,7 @@ err0: void dwc3_gadget_exit(struct dwc3 *dwc) { - int irq; - usb_del_gadget_udc(&dwc->gadget); - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - - dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); - free_irq(irq, dwc); dwc3_gadget_free_endpoints(dwc); -- cgit v0.10.2 From 9fcb3bd8d12db29c101d3722d37ddef9d1915317 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Feb 2013 17:55:58 +0200 Subject: usb: dwc3: gadget: save state of pullups This will be used during resume to verify if we should reconnect our pullups or not. Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b417506..35e4d3c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -704,6 +704,7 @@ struct dwc3 { unsigned delayed_status:1; unsigned needs_fifo_resize:1; unsigned resize_fifos:1; + unsigned pullups_connected:1; enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index db20317..04e4812 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1425,8 +1425,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) if (dwc->revision >= DWC3_REVISION_194A) reg &= ~DWC3_DCTL_KEEP_CONNECT; reg |= DWC3_DCTL_RUN_STOP; + dwc->pullups_connected = true; } else { reg &= ~DWC3_DCTL_RUN_STOP; + dwc->pullups_connected = false; } dwc3_writel(dwc->regs, DWC3_DCTL, reg); -- cgit v0.10.2 From 7415f17c9560c923ba61cd330c8dfcd5c3630b80 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 Apr 2012 14:56:33 +0300 Subject: usb: dwc3: core: add power management support Add support for basic power management on the dwc3 driver. While there is still lots to improve for full PM support, this minimal patch will already make sure that we survive suspend-to-ram and suspend-to-disk without major issues. Cc: Vikas C Sajjan Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4743508..75a9f88 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -591,6 +591,123 @@ static int dwc3_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int dwc3_prepare(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + switch (dwc->mode) { + case DWC3_MODE_DEVICE: + case DWC3_MODE_DRD: + dwc3_gadget_prepare(dwc); + /* FALLTHROUGH */ + case DWC3_MODE_HOST: + default: + dwc3_event_buffers_cleanup(dwc); + break; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static void dwc3_complete(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + switch (dwc->mode) { + case DWC3_MODE_DEVICE: + case DWC3_MODE_DRD: + dwc3_gadget_complete(dwc); + /* FALLTHROUGH */ + case DWC3_MODE_HOST: + default: + dwc3_event_buffers_setup(dwc); + break; + } + + spin_unlock_irqrestore(&dwc->lock, flags); +} + +static int dwc3_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + switch (dwc->mode) { + case DWC3_MODE_DEVICE: + case DWC3_MODE_DRD: + dwc3_gadget_suspend(dwc); + /* FALLTHROUGH */ + case DWC3_MODE_HOST: + default: + /* do nothing */ + break; + } + + dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL); + spin_unlock_irqrestore(&dwc->lock, flags); + + usb_phy_shutdown(dwc->usb3_phy); + usb_phy_shutdown(dwc->usb2_phy); + + return 0; +} + +static int dwc3_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; + + usb_phy_init(dwc->usb3_phy); + usb_phy_init(dwc->usb2_phy); + msleep(100); + + spin_lock_irqsave(&dwc->lock, flags); + + dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); + + switch (dwc->mode) { + case DWC3_MODE_DEVICE: + case DWC3_MODE_DRD: + dwc3_gadget_resume(dwc); + /* FALLTHROUGH */ + case DWC3_MODE_HOST: + default: + /* do nothing */ + break; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static const struct dev_pm_ops dwc3_dev_pm_ops = { + .prepare = dwc3_prepare, + .complete = dwc3_complete, + + SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) +}; + +#define DWC3_PM_OPS &(dwc3_dev_pm_ops) +#else +#define DWC3_PM_OPS NULL +#endif + #ifdef CONFIG_OF static const struct of_device_id of_dwc3_match[] = { { @@ -607,6 +724,7 @@ static struct platform_driver dwc3_driver = { .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), + .pm = DWC3_PM_OPS, }, }; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 35e4d3c..52e48e2 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -626,6 +626,8 @@ struct dwc3_scratchpad_array { * @mode: mode of operation * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY + * @dcfg: saved contents of DCFG register + * @gctl: saved contents of GCTL register * @is_selfpowered: true when we are selfpowered * @three_stage_setup: set if we perform a three phase setup * @ep0_bounced: true when we used bounce buffer @@ -675,6 +677,10 @@ struct dwc3 { void __iomem *regs; size_t regs_size; + /* used for suspend/resume */ + u32 dcfg; + u32 gctl; + u32 num_event_buffers; u32 u1u2; u32 maximum_speed; @@ -885,4 +891,31 @@ static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } #endif +/* power management interface */ +#if !IS_ENABLED(CONFIG_USB_DWC3_HOST) +int dwc3_gadget_prepare(struct dwc3 *dwc); +void dwc3_gadget_complete(struct dwc3 *dwc); +int dwc3_gadget_suspend(struct dwc3 *dwc); +int dwc3_gadget_resume(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_prepare(struct dwc3 *dwc) +{ + return 0; +} + +static inline void dwc3_gadget_complete(struct dwc3 *dwc) +{ +} + +static inline int dwc3_gadget_suspend(struct dwc3 *dwc) +{ + return 0; +} + +static inline int dwc3_gadget_resume(struct dwc3 *dwc) +{ + return 0; +} +#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 04e4812..73b0b7f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2594,6 +2594,8 @@ err0: return ret; } +/* -------------------------------------------------------------------------- */ + void dwc3_gadget_exit(struct dwc3 *dwc) { usb_del_gadget_udc(&dwc->gadget); @@ -2611,3 +2613,62 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), dwc->ctrl_req, dwc->ctrl_req_addr); } + +int dwc3_gadget_prepare(struct dwc3 *dwc) +{ + if (dwc->pullups_connected) + dwc3_gadget_disable_irq(dwc); + + return 0; +} + +void dwc3_gadget_complete(struct dwc3 *dwc) +{ + if (dwc->pullups_connected) { + dwc3_gadget_enable_irq(dwc); + dwc3_gadget_run_stop(dwc, true); + } +} + +int dwc3_gadget_suspend(struct dwc3 *dwc) +{ + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG); + + return 0; +} + +int dwc3_gadget_resume(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + if (ret) + goto err0; + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + if (ret) + goto err1; + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); + + dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg); + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + return ret; +} -- cgit v0.10.2 From 9a4b5dab91a8bfae46bfa572660cf43e9ebdc6c3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 11 Feb 2013 11:03:59 +0200 Subject: usb: dwc3: omap: introduce enable/disable IRQ methods they will be re-used on suspend/resume implementation. Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 3d343d9..43d6205 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -250,6 +250,34 @@ static int dwc3_omap_remove_core(struct device *dev, void *c) return 0; } +static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) +{ + u32 reg; + + /* enable all IRQs */ + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); + + reg = (USBOTGSS_IRQ1_OEVT | + USBOTGSS_IRQ1_DRVVBUS_RISE | + USBOTGSS_IRQ1_CHRGVBUS_RISE | + USBOTGSS_IRQ1_DISCHRGVBUS_RISE | + USBOTGSS_IRQ1_IDPULLUP_RISE | + USBOTGSS_IRQ1_DRVVBUS_FALL | + USBOTGSS_IRQ1_CHRGVBUS_FALL | + USBOTGSS_IRQ1_DISCHRGVBUS_FALL | + USBOTGSS_IRQ1_IDPULLUP_FALL); + + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); +} + +static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) +{ + /* disable all IRQs */ + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, 0x00); + dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00); +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -355,21 +383,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) return ret; } - /* enable all IRQs */ - reg = USBOTGSS_IRQO_COREIRQ_ST; - dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); - - reg = (USBOTGSS_IRQ1_OEVT | - USBOTGSS_IRQ1_DRVVBUS_RISE | - USBOTGSS_IRQ1_CHRGVBUS_RISE | - USBOTGSS_IRQ1_DISCHRGVBUS_RISE | - USBOTGSS_IRQ1_IDPULLUP_RISE | - USBOTGSS_IRQ1_DRVVBUS_FALL | - USBOTGSS_IRQ1_CHRGVBUS_FALL | - USBOTGSS_IRQ1_DISCHRGVBUS_FALL | - USBOTGSS_IRQ1_IDPULLUP_FALL); - - dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); + dwc3_omap_enable_irqs(omap); ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { @@ -382,6 +396,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) static int dwc3_omap_remove(struct platform_device *pdev) { + struct dwc3_omap *omap = platform_get_drvdata(pdev); + + dwc3_omap_disable_irqs(omap); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); -- cgit v0.10.2 From 1d9a00eeca1deeebf001047aa5e5e9d00e5588cf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 11 Feb 2013 11:07:34 +0200 Subject: usb: dwc3: omap: remove unused fields from private structure we're not using those fields of the structure, might as well remove them. Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 43d6205..ed178c0 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -121,9 +121,6 @@ struct dwc3_omap { int irq; void __iomem *base; - void *context; - u32 resource_size; - u32 dma_status:1; }; @@ -294,7 +291,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) u32 reg; void __iomem *base; - void *context; if (!node) { dev_err(dev, "device node not found\n"); @@ -327,16 +323,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) return -ENOMEM; } - context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL); - if (!context) { - dev_err(dev, "couldn't allocate dwc3 context memory\n"); - return -ENOMEM; - } - spin_lock_init(&omap->lock); - omap->resource_size = resource_size(res); - omap->context = context; omap->dev = dev; omap->irq = irq; omap->base = base; -- cgit v0.10.2 From f3e117f4437af5a2d1b72ae0fa1890dbf9bca72f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 11 Feb 2013 11:12:02 +0200 Subject: usb: dwc3: omap: add basic suspend/resume support this patch implements basic suspend/resume functionality for the OMAP glue layer. Tested-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ed178c0..35b9673 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -121,6 +121,8 @@ struct dwc3_omap { int irq; void __iomem *base; + u32 utmi_otg_status; + u32 dma_status:1; }; @@ -402,12 +404,66 @@ static const struct of_device_id of_dwc3_match[] = { }; MODULE_DEVICE_TABLE(of, of_dwc3_match); +#ifdef CONFIG_PM +static int dwc3_omap_prepare(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + dwc3_omap_disable_irqs(omap); + + return 0; +} + +static void dwc3_omap_complete(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + dwc3_omap_enable_irqs(omap); +} + +static int dwc3_omap_suspend(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + omap->utmi_otg_status = dwc3_omap_readl(omap->base, + USBOTGSS_UTMI_OTG_STATUS); + + return 0; +} + +static int dwc3_omap_resume(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, + omap->utmi_otg_status); + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { + .prepare = dwc3_omap_prepare, + .complete = dwc3_omap_complete, + + SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume) +}; + +#define DEV_PM_OPS (&dwc3_omap_dev_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, .remove = dwc3_omap_remove, .driver = { .name = "omap-dwc3", .of_match_table = of_dwc3_match, + .pm = DEV_PM_OPS, }, }; -- cgit v0.10.2 From 0646caf754aa3ce55ef978d7f4a3e3d0aab7a187 Mon Sep 17 00:00:00 2001 From: Vikas Sajjan Date: Tue, 16 Oct 2012 15:15:38 +0530 Subject: usb: dwc3: exynos: add basic suspend/resume support Adds suspend and resume callbacks to exynos dwc3 driver as part of power management support. This change does gating of dwc3 clock during suspend/resume cycles. Signed-off-by: Abhilash Kesavan Signed-off-by: Vikas C Sajjan CC: Doug Anderson Tested-by: Vivek Gautam [ balbi@ti.com : refreshed to current linus/master ] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index b082bec..e12e452 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -187,12 +187,46 @@ static const struct of_device_id exynos_dwc3_match[] = { MODULE_DEVICE_TABLE(of, exynos_dwc3_match); #endif +#ifdef CONFIG_PM +static int dwc3_exynos_suspend(struct device *dev) +{ + struct dwc3_exynos *exynos = dev_get_drvdata(dev); + + clk_disable(exynos->clk); + + return 0; +} + +static int dwc3_exynos_resume(struct device *dev) +{ + struct dwc3_exynos *exynos = dev_get_drvdata(dev); + + clk_enable(exynos->clk); + + /* runtime set active to reflect active state. */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume) +}; + +#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, .remove = dwc3_exynos_remove, .driver = { .name = "exynos-dwc3", .of_match_table = of_match_ptr(exynos_dwc3_match), + .pm = DEV_PM_OPS, }, }; -- cgit v0.10.2 From 68907a77431f405c0e8e7031aa77aae03383c69d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 11 Feb 2013 11:36:22 +0200 Subject: usb: dwc3: pci: add basic suspend/resume support this patch adds basic PM support for the PCI glue layer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index e8d7768..227d4a7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -212,11 +212,49 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); +#ifdef CONFIG_PM +static int dwc3_pci_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + + pci_disable_device(pci); + + return 0; +} + +static int dwc3_pci_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + int ret; + + ret = pci_enable_device(pci); + if (ret) { + dev_err(dev, "can't re-enable device --> %d\n", ret); + return ret; + } + + pci_set_master(pci); + + return 0; +} + +static const struct dev_pm_ops dwc3_pci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume) +}; + +#define DEV_PM_OPS (&dwc3_pci_dev_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct pci_driver dwc3_pci_driver = { .name = "dwc3-pci", .id_table = dwc3_pci_id_table, .probe = dwc3_pci_probe, .remove = dwc3_pci_remove, + .driver = { + .pm = DEV_PM_OPS, + }, }; MODULE_AUTHOR("Felipe Balbi "); -- cgit v0.10.2 From 6110a7ebda18d103f28bec0eab65f7be01871233 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 1 Feb 2013 23:33:50 +0200 Subject: usb: musb: core: remove unnecessary pr_info() there's really no need for that message. It's been a while since it printed something useful. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index daec6e0..36b7a1e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2293,8 +2293,6 @@ static int __init musb_init(void) if (usb_disabled()) return 0; - pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n", - musb_driver_name); return platform_driver_register(&musb_driver); } module_init(musb_init); -- cgit v0.10.2 From b42f7f3091de06f25abf59a26a3446f7b2fd0a50 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 4 Feb 2013 19:04:45 +0200 Subject: usb: musb: switch over to devm_ioremap_resource() this will make sure that request_memory_region() will be called and that we don't need to manually call iounmap() on ->remove(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 36b7a1e..fad8571 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2008,7 +2008,6 @@ static int musb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int irq = platform_get_irq_byname(pdev, "mc"); - int status; struct resource *iomem; void __iomem *base; @@ -2016,24 +2015,17 @@ static int musb_probe(struct platform_device *pdev) if (!iomem || irq <= 0) return -ENODEV; - base = ioremap(iomem->start, resource_size(iomem)); - if (!base) { - dev_err(dev, "ioremap failed\n"); - return -ENOMEM; - } + base = devm_ioremap_resource(dev, iomem); + if (IS_ERR(base)) + return PTR_ERR(base); - status = musb_init_controller(dev, irq, base); - if (status < 0) - iounmap(base); - - return status; + return musb_init_controller(dev, irq, base); } static int musb_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct musb *musb = dev_to_musb(dev); - void __iomem *ctrl_base = musb->ctrl_base; /* this gets called on rmmod. * - Host mode: host may still be active @@ -2044,7 +2036,6 @@ static int musb_remove(struct platform_device *pdev) musb_shutdown(pdev); musb_free(musb); - iounmap(ctrl_base); device_init_wakeup(dev, 0); #ifndef CONFIG_MUSB_PIO_ONLY dma_set_mask(dev, *dev->parent->dma_mask); -- cgit v0.10.2 From 38c5df225692cde4d695e4c74eaa7d83546ebe53 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 4 Feb 2013 19:57:23 +0200 Subject: usb: musb: gadget: delete wrong comment Those comments haven't been updated for a long time, so much that they don't make sense anymore. Best to remove them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index e363033..b47f66d 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -46,48 +46,6 @@ #include "musb_core.h" -/* MUSB PERIPHERAL status 3-mar-2006: - * - * - EP0 seems solid. It passes both USBCV and usbtest control cases. - * Minor glitches: - * - * + remote wakeup to Linux hosts work, but saw USBCV failures; - * in one test run (operator error?) - * + endpoint halt tests -- in both usbtest and usbcv -- seem - * to break when dma is enabled ... is something wrongly - * clearing SENDSTALL? - * - * - Mass storage behaved ok when last tested. Network traffic patterns - * (with lots of short transfers etc) need retesting; they turn up the - * worst cases of the DMA, since short packets are typical but are not - * required. - * - * - TX/IN - * + both pio and dma behave in with network and g_zero tests - * + no cppi throughput issues other than no-hw-queueing - * + failed with FLAT_REG (DaVinci) - * + seems to behave with double buffering, PIO -and- CPPI - * + with gadgetfs + AIO, requests got lost? - * - * - RX/OUT - * + both pio and dma behave in with network and g_zero tests - * + dma is slow in typical case (short_not_ok is clear) - * + double buffering ok with PIO - * + double buffering *FAILS* with CPPI, wrong data bytes sometimes - * + request lossage observed with gadgetfs - * - * - ISO not tested ... might work, but only weakly isochronous - * - * - Gadget driver disabling of softconnect during bind() is ignored; so - * drivers can't hold off host requests until userspace is ready. - * (Workaround: they can turn it off later.) - * - * - PORTABILITY (assumes PIO works): - * + DaVinci, basically works with cppi dma - * + OMAP 2430, ditto with mentor dma - * + TUSB 6010, platform-specific dma in the works - */ - /* ----------------------------------------------------------------------- */ #define is_buffer_mapped(req) (is_dma_capable() && \ @@ -275,41 +233,6 @@ static inline int max_ep_writesize(struct musb *musb, struct musb_ep *ep) return ep->packet_sz; } - -#ifdef CONFIG_USB_INVENTRA_DMA - -/* Peripheral tx (IN) using Mentor DMA works as follows: - Only mode 0 is used for transfers <= wPktSize, - mode 1 is used for larger transfers, - - One of the following happens: - - Host sends IN token which causes an endpoint interrupt - -> TxAvail - -> if DMA is currently busy, exit. - -> if queue is non-empty, txstate(). - - - Request is queued by the gadget driver. - -> if queue was previously empty, txstate() - - txstate() - -> start - /\ -> setup DMA - | (data is transferred to the FIFO, then sent out when - | IN token(s) are recd from Host. - | -> DMA interrupt on completion - | calls TxAvail. - | -> stop DMA, ~DMAENAB, - | -> set TxPktRdy for last short pkt or zlp - | -> Complete Request - | -> Continue next request (call txstate) - |___________________________________| - - * Non-Mentor DMA engines can of course work differently, such as by - * upleveling from irq-per-packet to irq-per-buffer. - */ - -#endif - /* * An endpoint is transmitting data. This can be called either from * the IRQ routine or from ep.queue() to kickstart a request on an @@ -616,37 +539,6 @@ void musb_g_tx(struct musb *musb, u8 epnum) /* ------------------------------------------------------------ */ -#ifdef CONFIG_USB_INVENTRA_DMA - -/* Peripheral rx (OUT) using Mentor DMA works as follows: - - Only mode 0 is used. - - - Request is queued by the gadget class driver. - -> if queue was previously empty, rxstate() - - - Host sends OUT token which causes an endpoint interrupt - /\ -> RxReady - | -> if request queued, call rxstate - | /\ -> setup DMA - | | -> DMA interrupt on completion - | | -> RxReady - | | -> stop DMA - | | -> ack the read - | | -> if data recd = max expected - | | by the request, or host - | | sent a short packet, - | | complete the request, - | | and start the next one. - | |_____________________________________| - | else just wait for the host - | to send the next OUT token. - |__________________________________________________| - - * Non-Mentor DMA engines can of course work differently. - */ - -#endif - /* * Context: controller locked, IRQs blocked, endpoint selected */ -- cgit v0.10.2 From 99b7856f3cec5db7ec71a8b4675a63e4bcadd63e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 09:22:47 +0200 Subject: usb: musb: force PIO-only if we're building multiplatform kernels MUSB still needs lots of work on the DMA part if we want to enable multiple DMA engines on a multiplatform kernel. Meanwhile, we're forcing PIO-only so that we, at least, have a working driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 05e5143..39864e3 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -67,6 +67,7 @@ endchoice choice prompt 'MUSB DMA mode' + default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM default USB_UX500_DMA if USB_MUSB_UX500 default USB_INVENTRA_DMA if USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN default USB_TI_CPPI_DMA if USB_MUSB_DAVINCI -- cgit v0.10.2 From 787f5627bec80094db487bfcb401e9744f181aed Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 09:24:55 +0200 Subject: usb: musb: make davinci and da8xx glues depend on BROKEN those two glues are still including headers and no active developement has been going on those glues for quite some time. Apparently, for da8xx glue, only initial commit 3ee076de (usb: musb: introduce DA8xx/OMAP-L1x glue layer) has been tested. All other patches seem to have been compile-tested only. For davinci glue layer, last real commit dates back from 2010, with commit f405387 (USB: MUSB: fix kernel WARNING/oops when unloading module in OTG mode). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 39864e3..de0fc88 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -34,10 +34,12 @@ choice config USB_MUSB_DAVINCI tristate "DaVinci" depends on ARCH_DAVINCI_DMx + depends on BROKEN config USB_MUSB_DA8XX tristate "DA8xx/OMAP-L1x" depends on ARCH_DAVINCI_DA8XX + depends on BROKEN config USB_MUSB_TUSB6010 tristate "TUSB6010" -- cgit v0.10.2 From 0f53e48132cd95b359fc79e0aa44db1c406b4eff Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 09:47:58 +0200 Subject: usb: musb: dsps: add missing include is the header defining SZ_4 and SZ_16M, we shouldn't depend on indirect inclusion so let's explicitly include it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 6bb8971..dfaa024 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 039e567..12dd6ed 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "musb_core.h" -- cgit v0.10.2 From 6a3b003620f0bd31390422619092fcb87cf1069e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 09:53:01 +0200 Subject: usb: musb: ux500_dma: kill compile warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following compile warnings: drivers/usb/musb/ux500_dma.c: In function ‘ux500_configure_channel’: drivers/usb/musb/ux500_dma.c:96:2: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 6 has type ‘dma_addr_t’ [-Wformat] drivers/usb/musb/ux500_dma.c: In function ‘ux500_dma_is_compatible’: drivers/usb/musb/ux500_dma.c:195:4: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 12dd6ed..c3a584c 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -94,8 +94,9 @@ static bool ux500_configure_channel(struct dma_channel *channel, struct musb *musb = ux500_channel->controller->private_data; dev_dbg(musb->controller, - "packet_sz=%d, mode=%d, dma_addr=0x%x, len=%d is_tx=%d\n", - packet_sz, mode, dma_addr, len, ux500_channel->is_tx); + "packet_sz=%d, mode=%d, dma_addr=0x%llu, len=%d is_tx=%d\n", + packet_sz, mode, (unsigned long long) dma_addr, + len, ux500_channel->is_tx); ux500_channel->cur_len = len; @@ -192,7 +193,7 @@ static int ux500_dma_is_compatible(struct dma_channel *channel, u16 maxpacket, void *buf, u32 length) { if ((maxpacket & 0x3) || - ((int)buf & 0x3) || + ((unsigned long int) buf & 0x3) || (length < 512) || (length & 0x3)) return false; -- cgit v0.10.2 From cc5060366b3c8cc20f0f4020a15fa5d39f4dc936 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 09:57:18 +0200 Subject: usb: musb: dsps: fix possible compile warning if CONFIG_OF is disabled, np will be unused and that will give us a compile warning. This patch just avoids it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index dfaa024..4b49874 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -597,14 +597,13 @@ err0: static int dsps_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; const struct dsps_musb_wrapper *wrp; struct dsps_glue *glue; struct resource *iomem; int ret, i; - match = of_match_node(musb_dsps_of_match, np); + match = of_match_node(musb_dsps_of_match, pdev->dev.of_node); if (!match) { dev_err(&pdev->dev, "fail to get matching of_match struct\n"); ret = -EINVAL; -- cgit v0.10.2 From 37730eccf214ec343b7b0e6cce31400f56ca09ff Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 10:19:15 +0200 Subject: usb: musb: gadget: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following compile warning: drivers/usb/musb/musb_gadget.c: In function ‘rxstate’: drivers/usb/musb/musb_gadget.c:714:22: warning: comparison of distinct pointer types lacks a cast [enabled by default] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index b47f66d..d35a375 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -627,7 +627,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) struct dma_controller *c; struct dma_channel *channel; int use_dma = 0; - int transfer_size; + unsigned int transfer_size; c = musb->dma_controller; channel = musb_ep->dma; @@ -669,10 +669,11 @@ static void rxstate(struct musb *musb, struct musb_request *req) csr | MUSB_RXCSR_DMAMODE); musb_writew(epio, MUSB_RXCSR, csr); - transfer_size = min(request->length - request->actual, + transfer_size = min_t(unsigned int, + request->length - + request->actual, channel->max_len); musb_ep->dma->desired_mode = 1; - } else { if (!musb_ep->hb_mult && musb_ep->hw_ep->rx_double_buffered) @@ -702,7 +703,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) struct dma_controller *c; struct dma_channel *channel; - int transfer_size = 0; + unsigned int transfer_size = 0; c = musb->dma_controller; channel = musb_ep->dma; @@ -711,11 +712,13 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (fifo_count < musb_ep->packet_sz) transfer_size = fifo_count; else if (request->short_not_ok) - transfer_size = min(request->length - + transfer_size = min_t(unsigned int, + request->length - request->actual, channel->max_len); else - transfer_size = min(request->length - + transfer_size = min_t(unsigned int, + request->length - request->actual, (unsigned)fifo_count); -- cgit v0.10.2 From a033f7ae607eb1d281448cda694c83f0c3f95e0d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 Feb 2013 10:23:37 +0200 Subject: usb: musb: Kconfig: drop unnecessary dependencies those glues can build cleanly anywhere. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index de0fc88..d38cf98 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -55,7 +55,6 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" - depends on SOC_TI81XX || SOC_AM33XX config USB_MUSB_BLACKFIN tristate "Blackfin" @@ -63,7 +62,6 @@ config USB_MUSB_BLACKFIN config USB_MUSB_UX500 tristate "U8500 and U5500" - depends on (ARCH_U8500 && AB8500_USB) endchoice -- cgit v0.10.2 From 9e86e71bcec99256fa29466d207b414919beb977 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 12:47:07 +0200 Subject: usb: dwc3: core: remove bogus comment to our structure that irq field has been removed already. This patch just removes its documentation. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 52e48e2..80f763f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -618,7 +618,6 @@ struct dwc3_scratchpad_array { * @gadget_driver: pointer to the gadget driver * @regs: base address for our registers * @regs_size: address space size - * @irq: IRQ number * @num_event_buffers: calculated number of event buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) -- cgit v0.10.2 From abed411869770a0f6c31e66388d7566bae672c2c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 4 Jul 2011 20:20:04 +0300 Subject: usb: dwc3: add a flags field to event buffer that way we know if a particular event buffer has pending events, or not. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 80f763f..e7b0679 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -369,6 +369,8 @@ struct dwc3_trb; * @list: a list of event buffers * @buf: _THE_ buffer * @length: size of this buffer + * @lpos: event offset + * @flags: flags related to this event buffer * @dma: dma_addr_t * @dwc: pointer to DWC controller */ @@ -376,6 +378,9 @@ struct dwc3_event_buffer { void *buf; unsigned length; unsigned int lpos; + unsigned int flags; + +#define DWC3_EVENT_PENDING BIT(0) dma_addr_t dma; -- cgit v0.10.2 From 60d04bbee0b729dc1e95d4dc669f68dea2a32570 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 4 Jul 2011 20:23:14 +0300 Subject: usb: dwc3: add count field to event buffer we can cache the last read value of the event buffer count register on this field, for later handling. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e7b0679..cdd70cb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -370,6 +370,7 @@ struct dwc3_trb; * @buf: _THE_ buffer * @length: size of this buffer * @lpos: event offset + * @count: cache of last read event count register * @flags: flags related to this event buffer * @dma: dma_addr_t * @dwc: pointer to DWC controller @@ -378,6 +379,7 @@ struct dwc3_event_buffer { void *buf; unsigned length; unsigned int lpos; + unsigned int count; unsigned int flags; #define DWC3_EVENT_PENDING BIT(0) -- cgit v0.10.2 From b15a762f02acb4f1e695a17435f719350f9d5bc1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 Jun 2011 16:57:15 +0300 Subject: usb: dwc3: gadget: move to threaded IRQ by moving to threaded IRQs, we allow our IRQ priorities to be configurable when running with realtime patch. Also, since we're running in thread context, we can call functions which might sleep, such as sysfs_notify() without problems. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 73b0b7f..742eb82 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1496,6 +1496,7 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) } static irqreturn_t dwc3_interrupt(int irq, void *_dwc); +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); static int dwc3_gadget_start(struct usb_gadget *g, struct usb_gadget_driver *driver) @@ -1566,8 +1567,8 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_ep0_out_start(dwc); irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, - "dwc3", dwc); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", irq, ret); @@ -2432,40 +2433,73 @@ static void dwc3_process_event_entry(struct dwc3 *dwc, } } +static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + int i; + + spin_lock_irqsave(&dwc->lock, flags); + + for (i = 0; i < dwc->num_event_buffers; i++) { + struct dwc3_event_buffer *evt; + int left; + + evt = dwc->ev_buffs[i]; + left = evt->count; + + if (!(evt->flags & DWC3_EVENT_PENDING)) + continue; + + while (left > 0) { + union dwc3_event event; + + event.raw = *(u32 *) (evt->buf + evt->lpos); + + dwc3_process_event_entry(dwc, &event); + + /* + * FIXME we wrap around correctly to the next entry as + * almost all entries are 4 bytes in size. There is one + * entry which has 12 bytes which is a regular entry + * followed by 8 bytes data. ATM I don't know how + * things are organized if we get next to the a + * boundary so I worry about that once we try to handle + * that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4); + } + + evt->count = 0; + evt->flags &= ~DWC3_EVENT_PENDING; + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) { struct dwc3_event_buffer *evt; - int left; u32 count; + evt = dwc->ev_buffs[buf]; + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) return IRQ_NONE; - evt = dwc->ev_buffs[buf]; - left = count; - - while (left > 0) { - union dwc3_event event; - - event.raw = *(u32 *) (evt->buf + evt->lpos); - - dwc3_process_event_entry(dwc, &event); - /* - * XXX we wrap around correctly to the next entry as almost all - * entries are 4 bytes in size. There is one entry which has 12 - * bytes which is a regular entry followed by 8 bytes data. ATM - * I don't know how things are organized if were get next to the - * a boundary so I worry about that once we try to handle that. - */ - evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; - left -= 4; - - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); - } + evt->count = count; + evt->flags |= DWC3_EVENT_PENDING; - return IRQ_HANDLED; + return IRQ_WAKE_THREAD; } static irqreturn_t dwc3_interrupt(int irq, void *_dwc) @@ -2480,7 +2514,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) irqreturn_t status; status = dwc3_process_event_buf(dwc, i); - if (status == IRQ_HANDLED) + if (status == IRQ_WAKE_THREAD) ret = status; } -- cgit v0.10.2 From d1e3d757f7aa91f15db347fc05ffd7ef7f413091 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 22:29:48 +0200 Subject: usb: common: introduce usb_state_string() this function will receive enum usb_device_state and return a human-readable string from it or, case an unknown value is passed as argument, the string "UNKNOWN". Signed-off-by: Felipe Balbi diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index d29503e..070b681 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -32,4 +32,25 @@ const char *usb_speed_string(enum usb_device_speed speed) } EXPORT_SYMBOL_GPL(usb_speed_string); +const char *usb_state_string(enum usb_device_state state) +{ + static const char *const names[] = { + [USB_STATE_NOTATTACHED] = "not attached", + [USB_STATE_ATTACHED] = "attached", + [USB_STATE_POWERED] = "powered", + [USB_STATE_RECONNECTING] = "reconnecting", + [USB_STATE_UNAUTHENTICATED] = "unauthenticated", + [USB_STATE_DEFAULT] = "default", + [USB_STATE_ADDRESS] = "addresssed", + [USB_STATE_CONFIGURED] = "configured", + [USB_STATE_SUSPENDED] = "suspended", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_state_string); + MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 9c210f2..27603bc 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -43,4 +43,13 @@ */ extern const char *usb_speed_string(enum usb_device_speed speed); + +/** + * usb_state_string - Returns human readable name for the state. + * @state: The state to return a human-readable name for. If it's not + * any of the states devices in usb_device_state_string enum, + * the string UNKNOWN will be returned. + */ +extern const char *usb_state_string(enum usb_device_state state); + #endif /* __LINUX_USB_CH9_H */ -- cgit v0.10.2 From 49401f4169c0e5a1b38f1a676d6f12eecaf77485 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:57:04 +0200 Subject: usb: gadget: introduce gadget state tracking that's useful information to expose to userland. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 40b1d88..8a1eeb2 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -101,6 +101,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); /* ------------------------------------------------------------------------- */ +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; + sysfs_notify(&gadget->dev.kobj, NULL, "status"); +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + /** * usb_gadget_udc_start - tells usb device controller to start up * @gadget: The gadget we want to get started @@ -197,6 +207,8 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (ret) goto err4; + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + mutex_unlock(&udc_lock); return 0; @@ -406,6 +418,16 @@ static ssize_t usb_udc_softconn_store(struct device *dev, } static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); +static ssize_t usb_gadget_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + struct usb_gadget *gadget = udc->gadget; + + return sprintf(buf, "%s\n", usb_state_string(gadget->state)); +} +static DEVICE_ATTR(state, S_IRUGO, usb_gadget_state_show, NULL); + #define USB_UDC_SPEED_ATTR(name, param) \ ssize_t usb_udc_##param##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ @@ -439,6 +461,7 @@ static USB_UDC_ATTR(a_alt_hnp_support); static struct attribute *usb_udc_attrs[] = { &dev_attr_srp.attr, &dev_attr_soft_connect.attr, + &dev_attr_state.attr, &dev_attr_current_speed.attr, &dev_attr_maximum_speed.attr, diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2e297e8..32b734d 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -482,6 +482,7 @@ struct usb_gadget_ops { * @speed: Speed of current connection to USB host. * @max_speed: Maximal speed the UDC can handle. UDC must support this * and all slower speeds. + * @state: the state we are now (attached, suspended, configured, etc) * @sg_supported: true if we can handle scatter-gather * @is_otg: True if the USB device port uses a Mini-AB jack, so that the * gadget driver must provide a USB OTG descriptor. @@ -525,6 +526,7 @@ struct usb_gadget { struct list_head ep_list; /* of usb_ep */ enum usb_device_speed speed; enum usb_device_speed max_speed; + enum usb_device_state state; unsigned sg_supported:1; unsigned is_otg:1; unsigned is_a_peripheral:1; @@ -959,6 +961,13 @@ extern void usb_gadget_unmap_request(struct usb_gadget *gadget, /*-------------------------------------------------------------------------*/ +/* utility to set gadget state properly */ + +extern void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state); + +/*-------------------------------------------------------------------------*/ + /* utility wrapping a simple endpoint selection policy */ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, -- cgit v0.10.2 From 14cd592f72ea1ce1a25d7a576a5ed6aa761456bc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 13:01:52 +0200 Subject: usb: dwc3: gadget: implement gadget state tracking make use of the previously introduced gadget->state field. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1d139ca..2a82b71 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -512,10 +512,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) reg |= DWC3_DCFG_DEVADDR(addr); dwc3_writel(dwc->regs, DWC3_DCFG, reg); - if (addr) + if (addr) { dwc->dev_state = DWC3_ADDRESS_STATE; - else + usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); + } else { dwc->dev_state = DWC3_DEFAULT_STATE; + usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); + } return 0; } @@ -549,6 +552,9 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { dwc->dev_state = DWC3_CONFIGURED_STATE; + usb_gadget_set_state(&dwc->gadget, + USB_STATE_CONFIGURED); + /* * Enable transition to U1/U2 state when * nothing is pending from application. @@ -564,8 +570,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) case DWC3_CONFIGURED_STATE: ret = dwc3_ep0_delegate_req(dwc, ctrl); - if (!cfg) + if (!cfg) { dwc->dev_state = DWC3_ADDRESS_STATE; + usb_gadget_set_state(&dwc->gadget, + USB_STATE_ADDRESS); + } break; default: ret = -EINVAL; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 742eb82..2686bf2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2137,7 +2137,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) } /* after reset -> Default State */ - dwc->dev_state = DWC3_DEFAULT_STATE; + usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); /* Recent versions support automatic phy suspend and don't need this */ if (dwc->revision < DWC3_REVISION_194A) { -- cgit v0.10.2 From fdba5aa54cfca795b73e50d45f617a0498a29af7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 25 Jan 2013 11:28:19 +0200 Subject: usb: dwc3: remove our homebrew state mechanism We can reuse the generic implementation via our struct usb_gadget. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index cdd70cb..d8c36fc 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -494,12 +494,6 @@ enum dwc3_link_state { DWC3_LINK_STATE_MASK = 0x0f, }; -enum dwc3_device_state { - DWC3_DEFAULT_STATE, - DWC3_ADDRESS_STATE, - DWC3_CONFIGURED_STATE, -}; - /* TRB Length, PCM and Status */ #define DWC3_TRB_SIZE_MASK (0x00ffffff) #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) @@ -721,7 +715,6 @@ struct dwc3 { enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; - enum dwc3_device_state dev_state; u16 isoch_delay; u16 u2sel; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2a82b71..5acbb94 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -394,10 +394,13 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, u32 wIndex; u32 reg; int ret; + enum usb_device_state state; wValue = le16_to_cpu(ctrl->wValue); wIndex = le16_to_cpu(ctrl->wIndex); recip = ctrl->bRequestType & USB_RECIP_MASK; + state = dwc->gadget.state; + switch (recip) { case USB_RECIP_DEVICE: @@ -409,7 +412,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, * default control pipe */ case USB_DEVICE_U1_ENABLE: - if (dwc->dev_state != DWC3_CONFIGURED_STATE) + if (state != USB_STATE_CONFIGURED) return -EINVAL; if (dwc->speed != DWC3_DSTS_SUPERSPEED) return -EINVAL; @@ -423,7 +426,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, break; case USB_DEVICE_U2_ENABLE: - if (dwc->dev_state != DWC3_CONFIGURED_STATE) + if (state != USB_STATE_CONFIGURED) return -EINVAL; if (dwc->speed != DWC3_DSTS_SUPERSPEED) return -EINVAL; @@ -493,6 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { + enum usb_device_state state = dwc->gadget.state; u32 addr; u32 reg; @@ -502,7 +506,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; } - if (dwc->dev_state == DWC3_CONFIGURED_STATE) { + if (state == USB_STATE_CONFIGURED) { dev_dbg(dwc->dev, "trying to set address when configured\n"); return -EINVAL; } @@ -512,13 +516,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) reg |= DWC3_DCFG_DEVADDR(addr); dwc3_writel(dwc->regs, DWC3_DCFG, reg); - if (addr) { - dwc->dev_state = DWC3_ADDRESS_STATE; + if (addr) usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); - } else { - dwc->dev_state = DWC3_DEFAULT_STATE; + else usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT); - } return 0; } @@ -535,6 +536,7 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { + enum usb_device_state state = dwc->gadget.state; u32 cfg; int ret; u32 reg; @@ -542,16 +544,15 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc->start_config_issued = false; cfg = le16_to_cpu(ctrl->wValue); - switch (dwc->dev_state) { - case DWC3_DEFAULT_STATE: + switch (state) { + case USB_STATE_DEFAULT: return -EINVAL; break; - case DWC3_ADDRESS_STATE: + case USB_STATE_ADDRESS: ret = dwc3_ep0_delegate_req(dwc, ctrl); /* if the cfg matches and the cfg is non zero */ if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) { - dwc->dev_state = DWC3_CONFIGURED_STATE; usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED); @@ -568,13 +569,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) } break; - case DWC3_CONFIGURED_STATE: + case USB_STATE_CONFIGURED: ret = dwc3_ep0_delegate_req(dwc, ctrl); - if (!cfg) { - dwc->dev_state = DWC3_ADDRESS_STATE; + if (!cfg) usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS); - } break; default: ret = -EINVAL; @@ -629,10 +628,11 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { struct dwc3_ep *dep; + enum usb_device_state state = dwc->gadget.state; u16 wLength; u16 wValue; - if (dwc->dev_state == DWC3_DEFAULT_STATE) + if (state == USB_STATE_DEFAULT) return -EINVAL; wValue = le16_to_cpu(ctrl->wValue); -- cgit v0.10.2 From 6b2a0eb854602b627a21b0256ae556506e91261e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 14:28:54 +0200 Subject: usb: dwc3: debugfs: add two missing Link States for Reset and Resume we were going to print "UNKNOWN" when we actually knew what those were. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 4a752e7..c740c76 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -577,6 +577,12 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) case DWC3_LINK_STATE_LPBK: seq_printf(s, "Loopback\n"); break; + case DWC3_LINK_STATE_RESET: + seq_printf(s, "Reset\n"); + break; + case DWC3_LINK_STATE_RESUME: + seq_printf(s, "Resume\n"); + break; default: seq_printf(s, "UNKNOWN %d\n", reg); } -- cgit v0.10.2 From 5b9ec339e45916ee682b8402597d7f2c6a04da17 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 14:29:39 +0200 Subject: usb: dwc3: debugfs: when unknown, print only the state value whenever we grab an unknown link_state we were printing the entire register value as a integer but that's hardly useful; instead, let's print only the bogus state value. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index c740c76..5512560 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -584,7 +584,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) seq_printf(s, "Resume\n"); break; default: - seq_printf(s, "UNKNOWN %d\n", reg); + seq_printf(s, "UNKNOWN %d\n", state); } return 0; -- cgit v0.10.2 From 4ec0ddb1b4fe3f7d93fdc862d2a7338cd354fe94 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 16:17:31 +0200 Subject: usb: dwc3: debugfs: mark our regset structure const nobody should be modifying that structure and debugfs has already being fixed to take const arguments, so we won't cause any new compile warnings. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 5512560..a1bac9a 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -59,7 +59,7 @@ .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \ } -static struct debugfs_reg32 dwc3_regs[] = { +static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GSBUSCFG0), dump_register(GSBUSCFG1), dump_register(GTXTHRCFG), -- cgit v0.10.2 From dbfff05d7c9b982e364c90a699961fb7000c6181 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 16:24:49 +0200 Subject: usb: dwc3: debugfs: improve debugfs file creation when commit 388e5c5 (usb: dwc3: remove dwc3 dependency on host AND gadget.) changed the way debugfs files are created, it failed to note that 'mode' is necessary in Dual Role mode only while 'testmode' and 'link_state' are valid in Dual Role and Peripheral-only builds. Fix this while also converting pre- processor conditional to C conditionals. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index a1bac9a..8b23d045 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -667,28 +667,31 @@ int dwc3_debugfs_init(struct dwc3 *dwc) goto err1; } -#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) - file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, - dwc, &dwc3_mode_fops); - if (!file) { - ret = -ENOMEM; - goto err1; + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) { + file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, + dwc, &dwc3_mode_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } } - file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, - dwc, &dwc3_testmode_fops); - if (!file) { - ret = -ENOMEM; - goto err1; - } - - file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, - dwc, &dwc3_link_state_fops); - if (!file) { - ret = -ENOMEM; - goto err1; + if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) || + IS_ENABLED(CONFIG_USB_DWC3_GADGET)) { + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, + dwc, &dwc3_testmode_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } + + file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, + dwc, &dwc3_link_state_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } } -#endif return 0; -- cgit v0.10.2 From 67d0b500ebbe78e2ca50036721fef530dfd3d9c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Feb 2013 16:31:07 +0200 Subject: usb: dwc3: core: avoid checkpatch.pl warning trivial patch to avoid "over 80-chars" rule break. No functional changes. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 75a9f88..b81b335 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -140,7 +140,8 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, * Returns a pointer to the allocated event buffer structure on success * otherwise ERR_PTR(errno). */ -static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length) +static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, + unsigned length) { struct dwc3_event_buffer *evt; -- cgit v0.10.2 From 756380e04276c9099f41716d279d24e5847b1030 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 25 Feb 2013 09:46:18 +0200 Subject: usb: gadget: pxa27x_udc: drop ARCH_PXA dependency This driver can compile in any arch quite easily by just removing a few headers and dropping cpu_is_* check from module_init. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5a0c541..50586ff 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -258,7 +258,6 @@ config USB_RENESAS_USBHS_UDC config USB_PXA27X tristate "PXA 27x" - depends on ARCH_PXA && (PXA27x || PXA3xx) select USB_OTG_UTILS help Intel's PXA 27x series XScale ARM v5TE processors include diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 07ce147..def73f2 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -24,14 +24,12 @@ #include #include #include - -#include -#include +#include +#include #include #include #include -#include #include "pxa27x_udc.h" @@ -2624,15 +2622,10 @@ static struct platform_driver udc_driver = { static int __init udc_init(void) { - if (!cpu_is_pxa27x() && !cpu_is_pxa3xx()) - return -ENODEV; - - printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); return platform_driver_probe(&udc_driver, pxa_udc_probe); } module_init(udc_init); - static void __exit udc_exit(void) { platform_driver_unregister(&udc_driver); -- cgit v0.10.2 From ef222cb5b575df57d8cd511965800519b90a6c31 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 25 Feb 2013 09:47:50 +0200 Subject: usb: gadget: pxa27x_udc: switch over to module_platform_driver just removing some boilerplate code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index def73f2..3276a6d 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2410,7 +2410,7 @@ static struct pxa_udc memory = { * Perform basic init : allocates udc clock, creates sysfs files, requests * irq. */ -static int __init pxa_udc_probe(struct platform_device *pdev) +static int pxa_udc_probe(struct platform_device *pdev) { struct resource *regs; struct pxa_udc *udc = &memory; @@ -2612,6 +2612,7 @@ static struct platform_driver udc_driver = { .name = "pxa27x-udc", .owner = THIS_MODULE, }, + .probe = pxa_udc_probe, .remove = __exit_p(pxa_udc_remove), .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM @@ -2620,17 +2621,7 @@ static struct platform_driver udc_driver = { #endif }; -static int __init udc_init(void) -{ - return platform_driver_probe(&udc_driver, pxa_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); +module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Robert Jarzmik"); -- cgit v0.10.2 From 658c8cf14dce1093e9f810f7e04a711ed79da6bd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:50:42 +0200 Subject: usb: gadget: udc-core: copy dma-related parameters from parent gadget's device pointer now is guaranteed to have valid dma_mask, dma_parms and coherent_dma_mask fields since we're always copying from our parent device. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 8a1eeb2..3e19a01 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -185,6 +185,10 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) dev_set_name(&gadget->dev, "gadget"); + dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask); + gadget->dev.dma_parms = parent->dma_parms; + gadget->dev.dma_mask = parent->dma_mask; + ret = device_register(&gadget->dev); if (ret) goto err2; -- cgit v0.10.2 From 2ed14320f3fed9e5b774ef68bdf73a4f3e28844e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:59:41 +0200 Subject: usb: gadget: udc-core: initialize parent if udc-core always does it, we can delete some extra lines from all UDC drivers. Besides, it avoids mistakes from happening and propagating. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 3e19a01..447a161 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -184,6 +184,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) goto err1; dev_set_name(&gadget->dev, "gadget"); + gadget->dev.parent = parent; dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask); gadget->dev.dma_parms = parent->dma_parms; -- cgit v0.10.2 From 036804a4b7089bdff9e5c70805c09ce4133b4440 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:52:43 +0200 Subject: usb: gadget: chipidea: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 1b65ac8..e303fd4 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1717,9 +1717,6 @@ static int udc_start(struct ci13xxx *ci) INIT_LIST_HEAD(&ci->gadget.ep_list); - ci->gadget.dev.dma_mask = dev->dma_mask; - ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; - ci->gadget.dev.parent = dev; ci->gadget.dev.release = udc_release; /* alloc resources */ -- cgit v0.10.2 From 6c39d90393a2feff052f9cb5e460a5c2b25d8214 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:53:40 +0200 Subject: usb: gadget: amd5536udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index eec4461..c994122 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3244,8 +3244,6 @@ static int udc_pci_probe( dev->phys_addr = resource; dev->irq = pdev->irq; dev->pdev = pdev; - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; /* general probing */ if (udc_probe(dev) == 0) -- cgit v0.10.2 From 442803d844315a96ec5f3ed981a427ed56d41835 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:54:46 +0200 Subject: usb: gadget: atmel_usba_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 94aeba8..e85d50b 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1885,9 +1885,6 @@ static int __init usba_udc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", (unsigned long)fifo->start, udc->fifo); - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - platform_set_drvdata(pdev, udc); /* Make sure we start from a clean slate */ -- cgit v0.10.2 From 9d2d93d86de32b82939a32d6abe6c8c5d3cb97f2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 10:57:06 +0200 Subject: usb: gadget: bcm63xx_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index d4f73e1..e7d2cd0 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2371,9 +2371,7 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) udc->gadget.ops = &bcm63xx_udc_ops; udc->gadget.name = dev_name(dev); - udc->gadget.dev.parent = dev; udc->gadget.dev.release = bcm63xx_udc_gadget_release; - udc->gadget.dev.dma_mask = dev->dma_mask; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; -- cgit v0.10.2 From 9502d03c429860f83370615922bcb45050417ad8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:03:46 +0200 Subject: usb: gadget: fusb300_udc: remove unnecessary initializations udc-core nos sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 5c9dd06..7f48aa6 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1420,8 +1420,6 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.ops = &fusb300_gadget_ops; fusb300->gadget.max_speed = USB_SPEED_HIGH; - fusb300->gadget.dev.parent = &pdev->dev; - fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask; fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; fusb300->reg = reg; -- cgit v0.10.2 From 1a36315c976bb1e0f8aa6e18c149a951d685cc20 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:04:34 +0200 Subject: usb: gadget: goku_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 8a6c666..c02cbdd 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1754,8 +1754,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_FULL; /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; -- cgit v0.10.2 From 975cbd49391822bc98f693e3a43d26754849948a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:05:19 +0200 Subject: usb: gadget: goku_udc: remove unused macro DMA_ADDR_INVALID isn't used anymore on goku_udc, we can just delete it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index c02cbdd..1c070f4 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -51,8 +51,6 @@ #define DRIVER_DESC "TC86C001 USB Device Controller" #define DRIVER_VERSION "30-Oct 2003" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - static const char driver_name [] = "goku_udc"; static const char driver_desc [] = DRIVER_DESC; @@ -275,7 +273,6 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; } -- cgit v0.10.2 From 53fae098be09eeced37cf06419ae4e9c872cf0d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:06:17 +0200 Subject: usb: gadget: imx_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 9c5b745..c29d9e8 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1461,9 +1461,6 @@ static int __init imx_udc_probe(struct platform_device *pdev) imx_usb->clk = clk; imx_usb->dev = &pdev->dev; - imx_usb->gadget.dev.parent = &pdev->dev; - imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; - platform_set_drvdata(pdev, imx_usb); usb_init_data(imx_usb); -- cgit v0.10.2 From 859d02c2d84da10536d1e0f1cebfa9105023f8e6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:07:39 +0200 Subject: usb: gadget: m66592-udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index eb61d0b..ae33e53 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1608,8 +1608,6 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.ops = &m66592_gadget_ops; m66592->gadget.max_speed = USB_SPEED_HIGH; - m66592->gadget.dev.parent = &pdev->dev; - m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; -- cgit v0.10.2 From 1048d83d5bfcbf1c71391fc1aae57326e940149c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:10:03 +0200 Subject: usb: dwc3: gadget: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2686bf2..322fb0b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2569,13 +2569,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.ops = &dwc3_gadget_ops; dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; - dwc->gadget.dev.parent = dwc->dev; dwc->gadget.sg_supported = true; - - dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask); - - dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; - dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; dwc->gadget.dev.release = dwc3_gadget_release; dwc->gadget.name = "dwc3-gadget"; -- cgit v0.10.2 From 2b5ced1a0cf2407e2bbe66e49687f6eb59d5df94 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:13:28 +0200 Subject: usb: gadget: mv_u3d_core: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index e5735fc..e6521b1 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1955,8 +1955,6 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ /* the "gadget" abstracts/virtualizes the controller */ - u3d->gadget.dev.parent = &dev->dev; - u3d->gadget.dev.dma_mask = dev->dev.dma_mask; u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ -- cgit v0.10.2 From d606b356cd07ffa3daea6dfcbf50fc2cbf4ba9a3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:14:14 +0200 Subject: usb: gadget: mv_udc_core: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 7f4d19d..b306111 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2268,8 +2268,6 @@ static int mv_udc_probe(struct platform_device *pdev) udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ /* the "gadget" abstracts/virtualizes the controller */ - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = pdev->dev.dma_mask; udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ -- cgit v0.10.2 From 162303f6d39c94d984d291fa4f13510093356404 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:14:54 +0200 Subject: usb: gadget: net2272: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 78c8bb5..bfb55d3 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2235,8 +2235,6 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - ret->gadget.dev.parent = dev; - ret->gadget.dev.dma_mask = dev->dma_mask; ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; -- cgit v0.10.2 From 2127ce0f2f11b2817e8ed8e3bd728731e06baac0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:15:29 +0200 Subject: usb: gadget: net2280: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 2089d9b..6ce2513 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2710,8 +2710,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; -- cgit v0.10.2 From 981e070fdde4316d5a8d0c9681db505ba6a833eb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:16:36 +0200 Subject: usb: gadget: omap_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index b23c861..bda1abd 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2631,12 +2631,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_FULL; udc->gadget.name = driver_name; - udc->gadget.dev.release = omap_udc_release; - udc->gadget.dev.parent = &odev->dev; - if (use_dma) - udc->gadget.dev.dma_mask = odev->dev.dma_mask; - udc->transceiver = xceiv; /* ep0 is special; put it right after the SETUP buffer */ -- cgit v0.10.2 From 91497600e27abf38ca00ead504fe2fdffab7e5c4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:17:25 +0200 Subject: usb: gadget: pch_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index e8c9afd..c1db902 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -3193,8 +3193,6 @@ static int pch_udc_probe(struct pci_dev *pdev, if (retval) goto finished; - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; -- cgit v0.10.2 From 6966fe8add4d47a2d55cab197925165e772f1197 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:19:56 +0200 Subject: usb: gadget: pxa25x_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index e29bb87..9fea053 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2138,9 +2138,6 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev; - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - the_controller = dev; platform_set_drvdata(pdev, dev); -- cgit v0.10.2 From b372c9572c4513500f0811371f3bd09616a64eba Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:20:39 +0200 Subject: usb: gadget: pxa27x_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 3276a6d..5fda425 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2453,8 +2453,6 @@ static int pxa_udc_probe(struct platform_device *pdev) goto err_map; } - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = NULL; udc->vbus_sensed = 0; the_controller = udc; -- cgit v0.10.2 From 71b0dd272d9c5d985c4174632a28d6b8c990093c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:21:35 +0200 Subject: usb: gadget: r8a66597-udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index a67d477..2de775d 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1915,8 +1915,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->gadget.ops = &r8a66597_gadget_ops; r8a66597->gadget.max_speed = USB_SPEED_HIGH; - r8a66597->gadget.dev.parent = &pdev->dev; - r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; -- cgit v0.10.2 From 2015760c2e626cc71ecd471648941b29789972bf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:23:05 +0200 Subject: usb: gadget: s3c-hsotg: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 2812fa5..9d2330d 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2927,7 +2927,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->driver = driver; hsotg->gadget.dev.driver = &driver->driver; hsotg->gadget.dev.of_node = hsotg->dev->of_node; - hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask; hsotg->gadget.speed = USB_SPEED_UNKNOWN; ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), @@ -3534,8 +3533,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); - hsotg->gadget.dev.parent = dev; - hsotg->gadget.dev.dma_mask = dev->dma_mask; hsotg->gadget.dev.release = s3c_hsotg_release; /* reset the system */ -- cgit v0.10.2 From 4c422049bd0f373b79b3dfbb7f984958c80bc7aa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:23:52 +0200 Subject: usb: gadget: s3c-hsudc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 7fc3de5..8db7b10 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1306,8 +1306,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->gadget.max_speed = USB_SPEED_HIGH; hsudc->gadget.ops = &s3c_hsudc_gadget_ops; hsudc->gadget.name = dev_name(dev); - hsudc->gadget.dev.parent = dev; - hsudc->gadget.dev.dma_mask = dev->dma_mask; hsudc->gadget.ep0 = &hsudc->ep[0].ep; hsudc->gadget.is_otg = 0; hsudc->gadget.is_a_peripheral = 0; -- cgit v0.10.2 From 31bff47aa2147ceab5d88a12f115afa44cdf4449 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:24:30 +0200 Subject: usb: gadget: s3c2410_udc: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index a669081..e15d1bb 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1824,9 +1824,6 @@ static int s3c2410_udc_probe(struct platform_device *pdev) goto err_mem; } - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - the_controller = udc; platform_set_drvdata(pdev, udc); -- cgit v0.10.2 From 5e6e3d3814004a509c9023abfc86ddea6803f8e1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 11:25:21 +0200 Subject: usb: musb: gadget: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d35a375..2dd952c 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1782,8 +1782,6 @@ int musb_gadget_setup(struct musb *musb) musb->g.speed = USB_SPEED_UNKNOWN; /* this "gadget" abstracts/virtualizes the controller */ - musb->g.dev.parent = musb->controller; - musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; musb->g.name = musb_driver_name; musb->g.is_otg = 1; -- cgit v0.10.2 From 8a1c33075e5e42074397aa8478fc16481e306b31 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:59:40 +0200 Subject: usb: gadget: fsl_udc_core: remove unnecessary initializations udc-core now sets dma-related and parent fields for us, we don't need to do it ourselves. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index c948241..acb176d 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2497,7 +2497,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) /* Setup gadget.dev and register with kernel */ dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = fsl_udc_release; - udc_controller->gadget.dev.parent = &pdev->dev; udc_controller->gadget.dev.of_node = pdev->dev.of_node; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) -- cgit v0.10.2 From 70d3a49878cb3fc0e5ec0bd1e607c7ac63743f67 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 13:51:24 +0200 Subject: usb: gadget: udc-core: initialize gadget->dev.driver if we initialize gadget->dev.driver ourselves, UDC drivers won't have to do the same, so we can remove some duplicated code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 447a161..2423d02 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -247,6 +247,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) udc->driver = NULL; udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; } /** @@ -296,6 +297,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri udc->driver = driver; udc->dev.driver = &driver->driver; + udc->gadget->dev.driver = &driver->driver; ret = driver->bind(udc->gadget, driver); if (ret) @@ -314,6 +316,7 @@ err1: udc->driver->function, ret); udc->driver = NULL; udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; return ret; } -- cgit v0.10.2 From 180cc68ed82ced0c19f5a478f8d1cdd2024c1875 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:15:15 +0200 Subject: usb: dwc3: gadget: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 322fb0b..9339eb1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1519,7 +1519,6 @@ static int dwc3_gadget_start(struct usb_gadget *g, } dwc->gadget_driver = driver; - dwc->gadget.dev.driver = &driver->driver; reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -1607,7 +1606,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g, __dwc3_gadget_ep_disable(dwc->eps[1]); dwc->gadget_driver = NULL; - dwc->gadget.dev.driver = NULL; spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v0.10.2 From fd3682d9fd49210447c37085fe4e96e74061ae7f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:15:51 +0200 Subject: usb: gadget: amd5536udc: don't touch gadget.dev.driver udc-core handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index c994122..a8ff93c 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1922,7 +1922,6 @@ static int amd5536_udc_start(struct usb_gadget *g, driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; /* Some gadget drivers use both ep0 directions. * NOTE: to gadget driver, ep0 is just one endpoint... @@ -1973,7 +1972,6 @@ static int amd5536_udc_stop(struct usb_gadget *g, shutdown(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; /* set SD */ -- cgit v0.10.2 From 8f3d7c86944ad03b2b45f1f4442577f087bb8591 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:16:21 +0200 Subject: usb: gadget: at91_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 9936de9..a690d64 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1631,7 +1631,6 @@ static int at91_start(struct usb_gadget *gadget, udc = container_of(gadget, struct at91_udc, gadget); udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->pdev->dev.of_node; udc->enabled = 1; udc->selfpowered = 1; @@ -1652,7 +1651,6 @@ static int at91_stop(struct usb_gadget *gadget, at91_udp_write(udc, AT91_UDP_IDR, ~0); spin_unlock_irqrestore(&udc->lock, flags); - udc->gadget.dev.driver = NULL; udc->driver = NULL; DBG("unbound from %s\n", driver->driver.name); -- cgit v0.10.2 From 1503a33932732dfbd880b905993e4122a7b53f53 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:34:17 +0200 Subject: usb: gadget: atmel_usba_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index e85d50b..f2a970f 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1784,7 +1784,6 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); clk_enable(udc->pclk); @@ -1826,7 +1825,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget, toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - udc->gadget.dev.driver = NULL; udc->driver = NULL; clk_disable(udc->hclk); -- cgit v0.10.2 From 155149e6724435eff47e0c6427b271b23815b40e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:34:33 +0200 Subject: usb: gadget: bcm63xx_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index e7d2cd0..904d474 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -1819,7 +1819,6 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget, udc->driver = driver; driver->driver.bus = NULL; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->dev->of_node; spin_unlock_irqrestore(&udc->lock, flags); @@ -1841,7 +1840,6 @@ static int bcm63xx_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&udc->lock, flags); udc->driver = NULL; - udc->gadget.dev.driver = NULL; /* * If we switch the PHY too abruptly after dropping D+, the host -- cgit v0.10.2 From 42c82fb4d1658bb9630f6248178f79d8854c5bfd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:34:49 +0200 Subject: usb: gadget: dummy_hcd: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index c4f27d5..9d54c01 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -912,7 +912,6 @@ static int dummy_udc_start(struct usb_gadget *g, dum->devstatus = 0; dum->driver = driver; - dum->gadget.dev.driver = &driver->driver; dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); return 0; @@ -927,7 +926,6 @@ static int dummy_udc_stop(struct usb_gadget *g, dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", driver->driver.name); - dum->gadget.dev.driver = NULL; dum->driver = NULL; return 0; -- cgit v0.10.2 From fc2dba950b27e6ec412efb932458cd6abfe77940 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:06 +0200 Subject: usb: gadget: fsl_qe_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 0e7531b..37feb62 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2296,7 +2296,6 @@ static int fsl_qe_start(struct usb_gadget *gadget, driver->driver.bus = NULL; /* hook up the driver */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.speed = driver->max_speed; /* Enable IRQ reg and Set usbcmd reg EN bit */ @@ -2338,7 +2337,6 @@ static int fsl_qe_stop(struct usb_gadget *gadget, nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc->lock, flags); - udc->gadget.dev.driver = NULL; udc->driver = NULL; dev_info(udc->dev, "unregistered gadget driver '%s'\r\n", -- cgit v0.10.2 From a1827ef6ac81072e9f2894d0db8cfa956d1b2a8d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:15 +0200 Subject: usb: gadget: fsl_udc_core: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index acb176d..4d5ff23 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1939,7 +1939,6 @@ static int fsl_udc_start(struct usb_gadget *g, driver->driver.bus = NULL; /* hook up the driver */ udc_controller->driver = driver; - udc_controller->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc_controller->lock, flags); if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { @@ -1955,7 +1954,6 @@ static int fsl_udc_start(struct usb_gadget *g, if (retval < 0) { ERR("can't bind to transceiver\n"); driver->unbind(&udc_controller->gadget); - udc_controller->gadget.dev.driver = 0; udc_controller->driver = 0; return retval; } @@ -1998,7 +1996,6 @@ static int fsl_udc_stop(struct usb_gadget *g, nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc_controller->lock, flags); - udc_controller->gadget.dev.driver = NULL; udc_controller->driver = NULL; return 0; -- cgit v0.10.2 From 6a609129c2e4da76bc3607671d33d660faf52590 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:24 +0200 Subject: usb: gadget: fusb300_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 7f48aa6..d69e840 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1313,7 +1313,6 @@ static int fusb300_udc_start(struct usb_gadget *g, /* hook up the driver */ driver->driver.bus = NULL; fusb300->driver = driver; - fusb300->gadget.dev.driver = &driver->driver; return 0; } @@ -1324,7 +1323,6 @@ static int fusb300_udc_stop(struct usb_gadget *g, struct fusb300 *fusb300 = to_fusb300(g); driver->unbind(&fusb300->gadget); - fusb300->gadget.dev.driver = NULL; init_controller(fusb300); fusb300->driver = NULL; -- cgit v0.10.2 From 88060d60b618cd71a5e109ac9d3d629556dcea25 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:31 +0200 Subject: usb: gadget: goku_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 1c070f4..aa1976e 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1351,7 +1351,6 @@ static int goku_udc_start(struct usb_gadget *g, /* hook up the driver */ driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; /* * then enable host detection and ep0; and we're ready @@ -1391,7 +1390,6 @@ static int goku_udc_stop(struct usb_gadget *g, dev->driver = NULL; stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - dev->gadget.dev.driver = NULL; return 0; } -- cgit v0.10.2 From 9fa4c960aa5ed69cde90283d8a0a750a2f36504c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:40 +0200 Subject: usb: gadget: imx_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index c29d9e8..b5cebd6 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1338,7 +1338,6 @@ static int imx_udc_start(struct usb_gadget *gadget, imx_usb = container_of(gadget, struct imx_udc_struct, gadget); /* first hook up the driver ... */ imx_usb->driver = driver; - imx_usb->gadget.dev.driver = &driver->driver; D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", __func__, driver->driver.name); @@ -1358,7 +1357,6 @@ static int imx_udc_stop(struct usb_gadget *gadget, imx_udc_disable(imx_usb); del_timer(&imx_usb->timer); - imx_usb->gadget.dev.driver = NULL; imx_usb->driver = NULL; D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", -- cgit v0.10.2 From ee4b47cf6b026d35528ecc78e7c82d2c5eae28be Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:48 +0200 Subject: usb: gadget: lpc32xx_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 1478327..b943d8c 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -2946,7 +2946,6 @@ static int lpc32xx_start(struct usb_gadget *gadget, } udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->dev->of_node; udc->enabled = 1; udc->selfpowered = 1; @@ -2995,7 +2994,6 @@ static int lpc32xx_stop(struct usb_gadget *gadget, } udc->enabled = 0; - udc->gadget.dev.driver = NULL; udc->driver = NULL; return 0; -- cgit v0.10.2 From e3ee46f291875a18afe9debeeb676483953e22e7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:53 +0200 Subject: usb: gadget: m66592-udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index ae33e53..21a20a6 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1471,7 +1471,6 @@ static int m66592_udc_start(struct usb_gadget *g, /* hook up the driver */ driver->driver.bus = NULL; m66592->driver = driver; - m66592->gadget.dev.driver = &driver->driver; m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) { @@ -1494,7 +1493,6 @@ static int m66592_udc_stop(struct usb_gadget *g, m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); driver->unbind(&m66592->gadget); - m66592->gadget.dev.driver = NULL; init_controller(m66592); disable_controller(m66592); -- cgit v0.10.2 From 900b5817d874dd894ca05b2cd8df8acd63dfd64e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:35:59 +0200 Subject: usb: gadget: mv_u3d_core: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index e6521b1..dc64450 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1264,7 +1264,6 @@ static int mv_u3d_start(struct usb_gadget *g, /* hook up the driver ... */ driver->driver.bus = NULL; u3d->driver = driver; - u3d->gadget.dev.driver = &driver->driver; u3d->ep0_dir = USB_DIR_OUT; @@ -1302,7 +1301,6 @@ static int mv_u3d_stop(struct usb_gadget *g, spin_unlock_irqrestore(&u3d->lock, flags); - u3d->gadget.dev.driver = NULL; u3d->driver = NULL; return 0; -- cgit v0.10.2 From 9ab7f79923f7ff2af798c42da104a03a936f704d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:05 +0200 Subject: usb: gadget: mv_udc_core: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index b306111..d1b243d 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1352,7 +1352,6 @@ static int mv_udc_start(struct usb_gadget *gadget, /* hook up the driver ... */ driver->driver.bus = NULL; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; @@ -1367,7 +1366,6 @@ static int mv_udc_start(struct usb_gadget *gadget, dev_err(&udc->dev->dev, "unable to register peripheral to otg\n"); udc->driver = NULL; - udc->gadget.dev.driver = NULL; return retval; } } @@ -1403,7 +1401,6 @@ static int mv_udc_stop(struct usb_gadget *gadget, spin_unlock_irqrestore(&udc->lock, flags); /* unbind gadget driver */ - udc->gadget.dev.driver = NULL; udc->driver = NULL; return 0; -- cgit v0.10.2 From 812abae5d66ce1a7a102afe1b3564df597687e79 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:14 +0200 Subject: usb: gadget: net2272: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index bfb55d3..af99223 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -1467,7 +1467,6 @@ static int net2272_start(struct usb_gadget *_gadget, dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1510,7 +1509,6 @@ static int net2272_stop(struct usb_gadget *_gadget, stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name); -- cgit v0.10.2 From 68abc94f8de89ef885332a101654a0aef2db6d97 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:21 +0200 Subject: usb: gadget: net2280: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 6ce2513..9e4f0a2 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1896,7 +1896,6 @@ static int net2280_start(struct usb_gadget *_gadget, dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; retval = device_create_file (&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; @@ -1925,7 +1924,6 @@ err_func: device_remove_file (&dev->pdev->dev, &dev_attr_function); err_unbind: driver->unbind (&dev->gadget); - dev->gadget.dev.driver = NULL; dev->driver = NULL; return retval; } @@ -1961,7 +1959,6 @@ static int net2280_stop(struct usb_gadget *_gadget, stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; net2280_led_active (dev, 0); -- cgit v0.10.2 From f6511d153eff9cd16e44ab54faa63149e2ddef75 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:26 +0200 Subject: usb: gadget: omap_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index bda1abd..19420ad 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2067,7 +2067,6 @@ static int omap_udc_start(struct usb_gadget *g, /* hook up the driver */ driver->driver.bus = NULL; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); if (udc->dc_clk != NULL) @@ -2083,7 +2082,6 @@ static int omap_udc_start(struct usb_gadget *g, ERR("can't bind to transceiver\n"); if (driver->unbind) { driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; udc->driver = NULL; } goto done; @@ -2129,7 +2127,6 @@ static int omap_udc_stop(struct usb_gadget *g, udc_quiesce(udc); spin_unlock_irqrestore(&udc->lock, flags); - udc->gadget.dev.driver = NULL; udc->driver = NULL; if (udc->dc_clk != NULL) -- cgit v0.10.2 From 37e337e1d3e20b101d6f3a6b55e7c7118a6d99a0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:40 +0200 Subject: usb: gadget: pch_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index c1db902..79cf296 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2988,7 +2988,6 @@ static int pch_udc_start(struct usb_gadget *g, driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; /* get ready for ep0 traffic */ pch_udc_setup_ep0(dev); @@ -3009,7 +3008,6 @@ static int pch_udc_stop(struct usb_gadget *g, pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); /* Assures that there are no pending requests with this driver */ - dev->gadget.dev.driver = NULL; dev->driver = NULL; dev->connected = 0; -- cgit v0.10.2 From 83a9adc9d815d12797df45a30c0f391c07c762bd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:47 +0200 Subject: usb: gadget: pxa25x_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9fea053..ef47495 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1263,7 +1263,6 @@ static int pxa25x_udc_start(struct usb_gadget *g, /* first hook up the driver ... */ dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; dev->pullup = 1; /* ... then enable host detection and ep0; and we're ready @@ -1325,7 +1324,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g, if (!IS_ERR_OR_NULL(dev->transceiver)) (void) otg_set_peripheral(dev->transceiver->otg, NULL); - dev->gadget.dev.driver = NULL; dev->driver = NULL; dump_state(dev); -- cgit v0.10.2 From 0280f4d99a1e9ff2883a3df20ad2c995110ed011 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:52 +0200 Subject: usb: gadget: pxa27x_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 5fda425..ad954c4 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g, /* first hook up the driver ... */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; dplus_pullup(udc, 1); if (!IS_ERR_OR_NULL(udc->transceiver)) { @@ -1827,7 +1826,6 @@ static int pxa27x_udc_start(struct usb_gadget *g, fail: udc->driver = NULL; - udc->gadget.dev.driver = NULL; return retval; } -- cgit v0.10.2 From 430e958e1d732b1d27d9ba31cdf79e5656b1a41b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:36:58 +0200 Subject: usb: gadget: s3c-hsotg: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 9d2330d..dfe7288 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2925,7 +2925,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, driver->driver.bus = NULL; hsotg->driver = driver; - hsotg->gadget.dev.driver = &driver->driver; hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; @@ -2942,7 +2941,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, err: hsotg->driver = NULL; - hsotg->gadget.dev.driver = NULL; return ret; } @@ -2977,7 +2975,6 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, hsotg->driver = NULL; hsotg->gadget.speed = USB_SPEED_UNKNOWN; - hsotg->gadget.dev.driver = NULL; spin_unlock_irqrestore(&hsotg->lock, flags); -- cgit v0.10.2 From 492a39022ad5825d8edbbdca993e18bf3f37f5fc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:37:04 +0200 Subject: usb: gadget: s3c-hsudc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 8db7b10..bfe7910 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1154,7 +1154,6 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, return -EBUSY; hsudc->driver = driver; - hsudc->gadget.dev.driver = &driver->driver; ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); @@ -1190,7 +1189,6 @@ err_otg: regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: hsudc->driver = NULL; - hsudc->gadget.dev.driver = NULL; return ret; } @@ -1208,7 +1206,6 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&hsudc->lock, flags); hsudc->driver = NULL; - hsudc->gadget.dev.driver = NULL; hsudc->gadget.speed = USB_SPEED_UNKNOWN; s3c_hsudc_uninit_phy(); -- cgit v0.10.2 From bbdb72702e9268cad8136b6bd0d8862eed90535d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:37:12 +0200 Subject: usb: gadget: s3c2410_udc: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index e15d1bb..d0e75e1 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1674,7 +1674,6 @@ static int s3c2410_udc_start(struct usb_gadget *g, /* Hook the driver */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; /* Enable udc */ s3c2410_udc_enable(udc); -- cgit v0.10.2 From 8707d5abbd96f7a124647357005511bee8d3ccdd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:37:17 +0200 Subject: usb: renesas: gadget: don't touch gadget.dev.driver udc-core now handles that for us, which means we can remove it from our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 6a3afa9..883b012 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -845,7 +845,6 @@ static int usbhsg_gadget_start(struct usb_gadget *gadget, /* first hook up the driver ... */ gpriv->driver = driver; - gpriv->gadget.dev.driver = &driver->driver; return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); } @@ -861,7 +860,6 @@ static int usbhsg_gadget_stop(struct usb_gadget *gadget, return -EINVAL; usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD); - gpriv->gadget.dev.driver = NULL; gpriv->driver = NULL; return 0; -- cgit v0.10.2 From 792bfcf7a1cd7913fa5d55f2b3a40e3275e98f6f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:47:44 +0200 Subject: usb: gadget: udc-core: introduce usb_add_gadget_udc_release() not all UDC drivers need a proper release function, for those which don't need it, we udc-core will provide a no-op release method so we can remove "redefinition" of such methods in almost every UDC driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 2423d02..a50811e 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -166,15 +166,23 @@ static void usb_udc_release(struct device *dev) } static const struct attribute_group *usb_udc_attr_groups[]; + +static void usb_udc_nop_release(struct device *dev) +{ + dev_vdbg(dev, "%s\n", __func__); +} + /** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. * * Returns zero on success, negative errno otherwise. */ -int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) { struct usb_udc *udc; int ret = -ENOMEM; @@ -190,6 +198,13 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) gadget->dev.dma_parms = parent->dma_parms; gadget->dev.dma_mask = parent->dma_mask; + if (release) { + gadget->dev.release = release; + } else { + if (!gadget->dev.release) + gadget->dev.release = usb_udc_nop_release; + } + ret = device_register(&gadget->dev); if (ret) goto err2; @@ -231,6 +246,20 @@ err2: err1: return ret; } +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} EXPORT_SYMBOL_GPL(usb_add_gadget_udc); static void usb_gadget_remove_driver(struct usb_udc *udc) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 32b734d..c454a88 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -874,6 +874,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver); */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); +extern int usb_add_gadget_udc_release(struct device *parent, + struct usb_gadget *gadget, void (*release)(struct device *dev)); extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); extern void usb_del_gadget_udc(struct usb_gadget *gadget); extern int udc_attach_driver(const char *name, -- cgit v0.10.2 From 79c7d849777bc24d995371a066ded2ab2b359a18 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:10:51 +0200 Subject: usb: chipidea: udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e303fd4..9bddf3f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1689,16 +1689,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) } /** - * udc_release: driver release function - * @dev: device - * - * Currently does nothing - */ -static void udc_release(struct device *dev) -{ -} - -/** * udc_start: initialize gadget role * @ci: chipidea controller */ @@ -1717,8 +1707,6 @@ static int udc_start(struct ci13xxx *ci) INIT_LIST_HEAD(&ci->gadget.ep_list); - ci->gadget.dev.release = udc_release; - /* alloc resources */ ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, sizeof(struct ci13xxx_qh), -- cgit v0.10.2 From e5caff6831d00d96b4618de939312570527ad54a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:11:05 +0200 Subject: usb: dwc3: gadget: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9339eb1..4ffec1a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1690,12 +1690,8 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) } } -static void dwc3_gadget_release(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); -} - /* -------------------------------------------------------------------------- */ + static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, struct dwc3_request *req, struct dwc3_trb *trb, const struct dwc3_event_depevt *event, int status) @@ -2568,7 +2564,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.sg_supported = true; - dwc->gadget.dev.release = dwc3_gadget_release; dwc->gadget.name = "dwc3-gadget"; /* -- cgit v0.10.2 From e1f07ced2a27a7068786beaf23e0ac9fda6d8ca6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:25 +0200 Subject: usb: gadget: amd5536udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index a8ff93c..f52dcfe 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3268,7 +3268,6 @@ static int udc_probe(struct udc *dev) dev->gadget.ops = &udc_ops; dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; @@ -3292,7 +3291,8 @@ static int udc_probe(struct udc *dev) "driver version: %s(for Geode5536 B1)\n", tmp); udc = dev; - retval = usb_add_gadget_udc(&udc->pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, + gadget_release); if (retval) goto finished; -- cgit v0.10.2 From a995d9e2a50b30907814f178eeaa1f632a66c0cb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: bcm63xx_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index 904d474..6e65182 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2304,17 +2304,6 @@ static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc) ***********************************************************************/ /** - * bcm63xx_udc_gadget_release - Called from device_release(). - * @dev: Unused. - * - * We get a warning if this function doesn't exist, but it's empty because - * we don't have to free any of the memory allocated with the devm_* APIs. - */ -static void bcm63xx_udc_gadget_release(struct device *dev) -{ -} - -/** * bcm63xx_udc_probe - Initialize a new instance of the UDC. * @pdev: Platform device struct from the bcm63xx BSP code. * @@ -2369,7 +2358,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) udc->gadget.ops = &bcm63xx_udc_ops; udc->gadget.name = dev_name(dev); - udc->gadget.dev.release = bcm63xx_udc_gadget_release; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; -- cgit v0.10.2 From f7162e9e1cead8510e371f8273902539102670ac Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: dummy_hcd: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 9d54c01..ce75155 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -935,11 +935,6 @@ static int dummy_udc_stop(struct usb_gadget *g, /* The gadget structure is stored inside the hcd structure and will be * released along with it. */ -static void dummy_gadget_release(struct device *dev) -{ - return; -} - static void init_dummy_udc_hw(struct dummy *dum) { int i; @@ -983,7 +978,6 @@ static int dummy_udc_probe(struct platform_device *pdev) dum->gadget.max_speed = USB_SPEED_SUPER; dum->gadget.dev.parent = &pdev->dev; - dum->gadget.dev.release = dummy_gadget_release; init_dummy_udc_hw(dum); rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget); -- cgit v0.10.2 From 29e7dbf32967361fa67e99cf97ff9935b7292ac4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: fsl_qe_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 37feb62..9a7ee33 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2521,7 +2521,6 @@ static int qe_udc_probe(struct platform_device *ofdev) /* name: Identifies the controller hardware type. */ udc->gadget.name = driver_name; - udc->gadget.dev.release = qe_udc_release; udc->gadget.dev.parent = &ofdev->dev; /* initialize qe_ep struct */ @@ -2585,7 +2584,8 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err5; } - ret = usb_add_gadget_udc(&ofdev->dev, &udc->gadget); + ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, + qe_udc_release); if (ret) goto err6; -- cgit v0.10.2 From 0e4d65e5292ed76578ff1571a1132e2f5f188261 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: fsl_udc_core: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 4d5ff23..f224169 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2493,7 +2493,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) /* Setup gadget.dev and register with kernel */ dev_set_name(&udc_controller->gadget.dev, "gadget"); - udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.of_node = pdev->dev.of_node; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) @@ -2530,7 +2529,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget); + ret = usb_add_gadget_udc_release(&pdev->dev, &udc_controller->gadget, + fsl_udc_release); if (ret) goto err_del_udc; -- cgit v0.10.2 From 509d986a37edd7d89cb706142b540c74c6fbab0e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: fusb300_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index d69e840..d053553 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1418,7 +1418,6 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.ops = &fusb300_gadget_ops; fusb300->gadget.max_speed = USB_SPEED_HIGH; - fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; fusb300->reg = reg; -- cgit v0.10.2 From 2ae837e4d83c5d1046f83b1bf3e3737126a6776a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: goku_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index aa1976e..991aba3 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1749,7 +1749,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_FULL; /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; /* now all the pci goodies ... */ @@ -1800,7 +1799,8 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); #endif - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto err; -- cgit v0.10.2 From 4b282fbe97412cc06fd9f173b4318e69a90b3442 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:26 +0200 Subject: usb: gadget: m66592-udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 21a20a6..866ef09 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1606,7 +1606,6 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.ops = &m66592_gadget_ops; m66592->gadget.max_speed = USB_SPEED_HIGH; - m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; init_timer(&m66592->timer); -- cgit v0.10.2 From 7c9c3c7e1855e28d35c8d70607e68690def85fed Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: mv_u3d_core: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index dc64450..e91281d 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1756,11 +1756,6 @@ static irqreturn_t mv_u3d_irq(int irq, void *dev) return IRQ_HANDLED; } -static void mv_u3d_gadget_release(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); -} - static int mv_u3d_remove(struct platform_device *dev) { struct mv_u3d *u3d = platform_get_drvdata(dev); @@ -1953,7 +1948,6 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ /* the "gadget" abstracts/virtualizes the controller */ - u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ mv_u3d_eps_init(u3d); -- cgit v0.10.2 From e861c768e57fd74ff947eadcf8ff86c01ba170d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: mv_udc_core: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index d1b243d..d278e8f 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2265,7 +2265,6 @@ static int mv_udc_probe(struct platform_device *pdev) udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ /* the "gadget" abstracts/virtualizes the controller */ - udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ eps_init(udc); @@ -2305,7 +2304,8 @@ static int mv_udc_probe(struct platform_device *pdev) else udc->vbus_active = 1; - retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + gadget_release); if (retval) goto err_create_workqueue; -- cgit v0.10.2 From 8efeeef61d4f253aaa1a566a8e0d01b635c216d9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: net2272: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index af99223..8dcbe77 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2233,7 +2233,6 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; return ret; @@ -2273,7 +2272,8 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) if (ret) goto err_irq; - ret = usb_add_gadget_udc(dev->dev, &dev->gadget); + ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget, + net2272_gadget_release); if (ret) goto err_add_udc; -- cgit v0.10.2 From 2901df68499d75cb43d37495797f7ca73fb548a4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: net2280: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 9e4f0a2..e5f2ef1 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2707,7 +2707,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; /* now all the pci goodies ... */ @@ -2819,7 +2818,8 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) retval = device_create_file (&pdev->dev, &dev_attr_registers); if (retval) goto done; - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto done; return 0; -- cgit v0.10.2 From 2fb29f215cc8f23eedabcc289cd4b5280a054aad Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: omap_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 19420ad..b8ed74a 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2628,7 +2628,6 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_FULL; udc->gadget.name = driver_name; - udc->gadget.dev.release = omap_udc_release; udc->transceiver = xceiv; /* ep0 is special; put it right after the SETUP buffer */ @@ -2902,7 +2901,8 @@ bad_on_1710: } create_proc_file(); - status = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + omap_udc_release); if (status) goto cleanup4; -- cgit v0.10.2 From ef98f7465fe28a39800b07e14b1e4a43906bd77b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: pch_udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 79cf296..44aacf7 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -3191,13 +3191,13 @@ static int pch_udc_probe(struct pci_dev *pdev, if (retval) goto finished; - dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; /* Put the device in disconnected state till a driver is bound */ pch_udc_set_disconnect(dev); - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto finished; return 0; -- cgit v0.10.2 From 59139706a0bc6950759c588ca3a28a732fa18d5f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:27 +0200 Subject: usb: gadget: r8a66597-udc: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 2de775d..0b742d1 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1915,7 +1915,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->gadget.ops = &r8a66597_gadget_ops; r8a66597->gadget.max_speed = USB_SPEED_HIGH; - r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; init_timer(&r8a66597->timer); -- cgit v0.10.2 From ad8033fcd08ed9d0e2c262015baf7e22e1544db2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:28 +0200 Subject: usb: gadget: s3c-hsotg: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index dfe7288..f1ceabf 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3444,16 +3444,6 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) } /** - * s3c_hsotg_release - release callback for hsotg device - * @dev: Device to for which release is called - * - * Nothing to do as the resource is allocated using devm_ API. - */ -static void s3c_hsotg_release(struct device *dev) -{ -} - -/** * s3c_hsotg_probe - probe function for hsotg driver * @pdev: The platform information for the driver */ @@ -3530,7 +3520,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); - hsotg->gadget.dev.release = s3c_hsotg_release; /* reset the system */ -- cgit v0.10.2 From 07d83168278a73647e3a30fc33e274708418c52b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:28 +0200 Subject: usb: musb: gadget: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 2dd952c..6101ebf 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1691,13 +1691,6 @@ static const struct usb_gadget_ops musb_gadget_operations = { * all peripheral ports are external... */ -static void musb_gadget_release(struct device *dev) -{ - /* kref_put(WHAT) */ - dev_dbg(dev, "%s\n", __func__); -} - - static void init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in) { @@ -1782,7 +1775,6 @@ int musb_gadget_setup(struct musb *musb) musb->g.speed = USB_SPEED_UNKNOWN; /* this "gadget" abstracts/virtualizes the controller */ - musb->g.dev.release = musb_gadget_release; musb->g.name = musb_driver_name; musb->g.is_otg = 1; -- cgit v0.10.2 From 3920193d8e71d1f7e0d077aa71624b64fa3499ac Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:15:51 +0200 Subject: usb: renesas: gadget: don't assign gadget.dev.release directly udc-core provides a better way to handle release methods, let's use it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 883b012..c2781bc 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -923,11 +923,6 @@ static int usbhsg_stop(struct usbhs_priv *priv) return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED); } -static void usbhs_mod_gadget_release(struct device *pdev) -{ - /* do nothing */ -} - int usbhs_mod_gadget_probe(struct usbhs_priv *priv) { struct usbhsg_gpriv *gpriv; @@ -975,7 +970,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) * init gadget */ gpriv->gadget.dev.parent = dev; - gpriv->gadget.dev.release = usbhs_mod_gadget_release; gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; -- cgit v0.10.2 From ddf47ccbfebc12add813cf729ecfc2d5ab59ca19 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 15:25:41 +0200 Subject: usb: gadget: udc-core: remove protection when setting gadget.dev.release now that no UDC driver touches gadget.dev.release we can assign our release function to it without being afraid of breaking anything. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index a50811e..26e116b 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -198,12 +198,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, gadget->dev.dma_parms = parent->dma_parms; gadget->dev.dma_mask = parent->dma_mask; - if (release) { + if (release) gadget->dev.release = release; - } else { - if (!gadget->dev.release) - gadget->dev.release = usb_udc_nop_release; - } + else + gadget->dev.release = usb_udc_nop_release; ret = device_register(&gadget->dev); if (ret) -- cgit v0.10.2 From a5fcb066d284d8e525e1e1a4799722e8616cfe76 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 19:15:14 +0200 Subject: usb: gadget: udc-core: anywone can read 'speed' attributes current code only allows the file owner (usually root) to read current_speed and maximum_speed sysfs files. Let anyone read those. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 26e116b..7999cc6 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -471,7 +471,7 @@ ssize_t usb_udc_##param##_show(struct device *dev, \ return snprintf(buf, PAGE_SIZE, "%s\n", \ usb_speed_string(udc->gadget->param)); \ } \ -static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL) +static DEVICE_ATTR(name, S_IRUGO, usb_udc_##param##_show, NULL) static USB_UDC_SPEED_ATTR(current_speed, speed); static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); -- cgit v0.10.2 From 7ac6a593d512de38e710591afea4c839626b3bd0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 18 Sep 2012 21:22:32 +0300 Subject: usb: dwc3: core: define more revisions Some new revisions of the DWC3 core have been released, let's add our defines to help implementing known erratas. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d8c36fc..ad2ffac 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -700,6 +700,9 @@ struct dwc3 { #define DWC3_REVISION_202A 0x5533202a #define DWC3_REVISION_210A 0x5533210a #define DWC3_REVISION_220A 0x5533220a +#define DWC3_REVISION_230A 0x5533230a +#define DWC3_REVISION_240A 0x5533240a +#define DWC3_REVISION_250A 0x5533250a unsigned is_selfpowered:1; unsigned three_stage_setup:1; -- cgit v0.10.2 From 0b0cc1cd31bed3e3147398e54530f1f819b27692 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 18 Sep 2012 21:39:24 +0300 Subject: usb: dwc3: workaround: unexpected transtion U3 -> RESUME In DWC3 versions < 2.50a configured without Hibernation mode enabled, there will be an extra link status change interrupt if device detects host-initiated U3 exit. In that case, core will generate an unnecessary U3 -> RESUME transition which should be ignored by the driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4ffec1a..8e53acc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2317,6 +2317,34 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + unsigned int pwropt; + + /* + * WORKAROUND: DWC3 < 2.50a have an issue when configured without + * Hibernation mode enabled which would show up when device detects + * host-initiated U3 exit. + * + * In that case, device will generate a Link State Change Interrupt + * from U3 to RESUME which is only necessary if Hibernation is + * configured in. + * + * There are no functional changes due to such spurious event and we + * just need to ignore it. + * + * Refers to: + * + * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation + * operational mode + */ + pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); + if ((dwc->revision < DWC3_REVISION_250A) && + (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { + if ((dwc->link_state == DWC3_LINK_STATE_U3) && + (next == DWC3_LINK_STATE_RESUME)) { + dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + return; + } + } /* * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending -- cgit v0.10.2 From cedf8602373a3a5d02e49af7bebc401ffe3b38f3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 27 Feb 2013 15:16:28 +0100 Subject: usb: phy: move bulk of otg/otg.c to phy/phy.c Most of otg/otg.c is not otg specific, but phy specific, so move it to the phy directory. Tested-by: Steffen Trumtrar Reported-by: Kishon Vijay Abraham I Signed-off-by: Sascha Hauer Signed-off-by: Marc Kleine-Budde Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 2bd03d2..358cfd9 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -8,436 +8,9 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ - -#include #include -#include -#include -#include -#include -#include - #include -static LIST_HEAD(phy_list); -static LIST_HEAD(phy_bind_list); -static DEFINE_SPINLOCK(phy_lock); - -static struct usb_phy *__usb_find_phy(struct list_head *list, - enum usb_phy_type type) -{ - struct usb_phy *phy = NULL; - - list_for_each_entry(phy, list, head) { - if (phy->type != type) - continue; - - return phy; - } - - return ERR_PTR(-ENODEV); -} - -static struct usb_phy *__usb_find_phy_dev(struct device *dev, - struct list_head *list, u8 index) -{ - struct usb_phy_bind *phy_bind = NULL; - - list_for_each_entry(phy_bind, list, list) { - if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && - phy_bind->index == index) { - if (phy_bind->phy) - return phy_bind->phy; - else - return ERR_PTR(-EPROBE_DEFER); - } - } - - return ERR_PTR(-ENODEV); -} - -static struct usb_phy *__of_usb_find_phy(struct device_node *node) -{ - struct usb_phy *phy; - - list_for_each_entry(phy, &phy_list, head) { - if (node != phy->dev->of_node) - continue; - - return phy; - } - - return ERR_PTR(-ENODEV); -} - -static void devm_usb_phy_release(struct device *dev, void *res) -{ - struct usb_phy *phy = *(struct usb_phy **)res; - - usb_put_phy(phy); -} - -static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) -{ - return res == match_data; -} - -/** - * devm_usb_get_phy - find the USB PHY - * @dev - device that requests this phy - * @type - the type of the phy the controller requires - * - * Gets the phy using usb_get_phy(), and associates a device with it using - * devres. On driver detach, release function is invoked on the devres data, - * then, devres data is freed. - * - * For use by USB host and peripheral drivers. - */ -struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) -{ - struct usb_phy **ptr, *phy; - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - phy = usb_get_phy(type); - if (!IS_ERR(phy)) { - *ptr = phy; - devres_add(dev, ptr); - } else - devres_free(ptr); - - return phy; -} -EXPORT_SYMBOL(devm_usb_get_phy); - -/** - * usb_get_phy - find the USB PHY - * @type - the type of the phy the controller requires - * - * Returns the phy driver, after getting a refcount to it; or - * -ENODEV if there is no such phy. The caller is responsible for - * calling usb_put_phy() to release that count. - * - * For use by USB host and peripheral drivers. - */ -struct usb_phy *usb_get_phy(enum usb_phy_type type) -{ - struct usb_phy *phy = NULL; - unsigned long flags; - - spin_lock_irqsave(&phy_lock, flags); - - phy = __usb_find_phy(&phy_list, type); - if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - pr_err("unable to find transceiver of type %s\n", - usb_phy_type_string(type)); - goto err0; - } - - get_device(phy->dev); - -err0: - spin_unlock_irqrestore(&phy_lock, flags); - - return phy; -} -EXPORT_SYMBOL(usb_get_phy); - - /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle - * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy - * - * Returns the phy driver associated with the given phandle value, - * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with - * the phy using devres. On driver detach, release function is invoked - * on the devres data, then, devres data is freed. - * - * For use by USB host and peripheral drivers. - */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) -{ - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; - unsigned long flags; - struct device_node *node; - - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) { - dev_dbg(dev, "failed to allocate memory for devres\n"); - goto err0; - } - - spin_lock_irqsave(&phy_lock, flags); - - phy = __of_usb_find_phy(node); - if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - phy = ERR_PTR(-EPROBE_DEFER); - devres_free(ptr); - goto err1; - } - - *ptr = phy; - devres_add(dev, ptr); - - get_device(phy->dev); - -err1: - spin_unlock_irqrestore(&phy_lock, flags); - -err0: - of_node_put(node); - - return phy; -} -EXPORT_SYMBOL(devm_usb_get_phy_by_phandle); - -/** - * usb_get_phy_dev - find the USB PHY - * @dev - device that requests this phy - * @index - the index of the phy - * - * Returns the phy driver, after getting a refcount to it; or - * -ENODEV if there is no such phy. The caller is responsible for - * calling usb_put_phy() to release that count. - * - * For use by USB host and peripheral drivers. - */ -struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) -{ - struct usb_phy *phy = NULL; - unsigned long flags; - - spin_lock_irqsave(&phy_lock, flags); - - phy = __usb_find_phy_dev(dev, &phy_bind_list, index); - if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - pr_err("unable to find transceiver\n"); - goto err0; - } - - get_device(phy->dev); - -err0: - spin_unlock_irqrestore(&phy_lock, flags); - - return phy; -} -EXPORT_SYMBOL(usb_get_phy_dev); - -/** - * devm_usb_get_phy_dev - find the USB PHY using device ptr and index - * @dev - device that requests this phy - * @index - the index of the phy - * - * Gets the phy using usb_get_phy_dev(), and associates a device with it using - * devres. On driver detach, release function is invoked on the devres data, - * then, devres data is freed. - * - * For use by USB host and peripheral drivers. - */ -struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) -{ - struct usb_phy **ptr, *phy; - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - phy = usb_get_phy_dev(dev, index); - if (!IS_ERR(phy)) { - *ptr = phy; - devres_add(dev, ptr); - } else - devres_free(ptr); - - return phy; -} -EXPORT_SYMBOL(devm_usb_get_phy_dev); - -/** - * devm_usb_put_phy - release the USB PHY - * @dev - device that wants to release this phy - * @phy - the phy returned by devm_usb_get_phy() - * - * destroys the devres associated with this phy and invokes usb_put_phy - * to release the phy. - * - * For use by USB host and peripheral drivers. - */ -void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) -{ - int r; - - r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); - dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); -} -EXPORT_SYMBOL(devm_usb_put_phy); - -/** - * usb_put_phy - release the USB PHY - * @x: the phy returned by usb_get_phy() - * - * Releases a refcount the caller received from usb_get_phy(). - * - * For use by USB host and peripheral drivers. - */ -void usb_put_phy(struct usb_phy *x) -{ - if (x) { - struct module *owner = x->dev->driver->owner; - - put_device(x->dev); - module_put(owner); - } -} -EXPORT_SYMBOL(usb_put_phy); - -/** - * usb_add_phy - declare the USB PHY - * @x: the USB phy to be used; or NULL - * @type - the type of this PHY - * - * This call is exclusively for use by phy drivers, which - * coordinate the activities of drivers for host and peripheral - * controllers, and in some cases for VBUS current regulation. - */ -int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) -{ - int ret = 0; - unsigned long flags; - struct usb_phy *phy; - - if (x->type != USB_PHY_TYPE_UNDEFINED) { - dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); - return -EINVAL; - } - - spin_lock_irqsave(&phy_lock, flags); - - list_for_each_entry(phy, &phy_list, head) { - if (phy->type == type) { - ret = -EBUSY; - dev_err(x->dev, "transceiver type %s already exists\n", - usb_phy_type_string(type)); - goto out; - } - } - - x->type = type; - list_add_tail(&x->head, &phy_list); - -out: - spin_unlock_irqrestore(&phy_lock, flags); - return ret; -} -EXPORT_SYMBOL(usb_add_phy); - -/** - * usb_add_phy_dev - declare the USB PHY - * @x: the USB phy to be used; or NULL - * - * This call is exclusively for use by phy drivers, which - * coordinate the activities of drivers for host and peripheral - * controllers, and in some cases for VBUS current regulation. - */ -int usb_add_phy_dev(struct usb_phy *x) -{ - struct usb_phy_bind *phy_bind; - unsigned long flags; - - if (!x->dev) { - dev_err(x->dev, "no device provided for PHY\n"); - return -EINVAL; - } - - spin_lock_irqsave(&phy_lock, flags); - list_for_each_entry(phy_bind, &phy_bind_list, list) - if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev)))) - phy_bind->phy = x; - - list_add_tail(&x->head, &phy_list); - - spin_unlock_irqrestore(&phy_lock, flags); - return 0; -} -EXPORT_SYMBOL(usb_add_phy_dev); - -/** - * usb_remove_phy - remove the OTG PHY - * @x: the USB OTG PHY to be removed; - * - * This reverts the effects of usb_add_phy - */ -void usb_remove_phy(struct usb_phy *x) -{ - unsigned long flags; - struct usb_phy_bind *phy_bind; - - spin_lock_irqsave(&phy_lock, flags); - if (x) { - list_for_each_entry(phy_bind, &phy_bind_list, list) - if (phy_bind->phy == x) - phy_bind->phy = NULL; - list_del(&x->head); - } - spin_unlock_irqrestore(&phy_lock, flags); -} -EXPORT_SYMBOL(usb_remove_phy); - -/** - * usb_bind_phy - bind the phy and the controller that uses the phy - * @dev_name: the device name of the device that will bind to the phy - * @index: index to specify the port number - * @phy_dev_name: the device name of the phy - * - * Fills the phy_bind structure with the dev_name and phy_dev_name. This will - * be used when the phy driver registers the phy and when the controller - * requests this phy. - * - * To be used by platform specific initialization code. - */ -int __init usb_bind_phy(const char *dev_name, u8 index, - const char *phy_dev_name) -{ - struct usb_phy_bind *phy_bind; - unsigned long flags; - - phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); - if (!phy_bind) { - pr_err("phy_bind(): No memory for phy_bind"); - return -ENOMEM; - } - - phy_bind->dev_name = dev_name; - phy_bind->phy_dev_name = phy_dev_name; - phy_bind->index = index; - - spin_lock_irqsave(&phy_lock, flags); - list_add_tail(&phy_bind->list, &phy_bind_list); - spin_unlock_irqrestore(&phy_lock, flags); - - return 0; -} -EXPORT_SYMBOL_GPL(usb_bind_phy); - const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index b13faa1..9fa6327 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG +obj-$(CONFIG_USB_OTG_UTILS) += phy.o obj-$(CONFIG_OMAP_USB2) += omap-usb2.o obj-$(CONFIG_OMAP_USB3) += omap-usb3.o obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c new file mode 100644 index 0000000..bc1970c --- /dev/null +++ b/drivers/usb/phy/phy.c @@ -0,0 +1,438 @@ +/* + * phy.c -- USB phy handling + * + * Copyright (C) 2004-2013 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +static LIST_HEAD(phy_list); +static LIST_HEAD(phy_bind_list); +static DEFINE_SPINLOCK(phy_lock); + +static struct usb_phy *__usb_find_phy(struct list_head *list, + enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + + list_for_each_entry(phy, list, head) { + if (phy->type != type) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} + +static struct usb_phy *__usb_find_phy_dev(struct device *dev, + struct list_head *list, u8 index) +{ + struct usb_phy_bind *phy_bind = NULL; + + list_for_each_entry(phy_bind, list, list) { + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && + phy_bind->index == index) { + if (phy_bind->phy) + return phy_bind->phy; + else + return ERR_PTR(-EPROBE_DEFER); + } + } + + return ERR_PTR(-ENODEV); +} + +static struct usb_phy *__of_usb_find_phy(struct device_node *node) +{ + struct usb_phy *phy; + + list_for_each_entry(phy, &phy_list, head) { + if (node != phy->dev->of_node) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} + +static void devm_usb_phy_release(struct device *dev, void *res) +{ + struct usb_phy *phy = *(struct usb_phy **)res; + + usb_put_phy(phy); +} + +static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) +{ + return res == match_data; +} + +/** + * devm_usb_get_phy - find the USB PHY + * @dev - device that requests this phy + * @type - the type of the phy the controller requires + * + * Gets the phy using usb_get_phy(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy(type); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy); + +/** + * usb_get_phy - find the USB PHY + * @type - the type of the phy the controller requires + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *usb_get_phy(enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy(&phy_list, type); + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { + pr_err("unable to find transceiver of type %s\n", + usb_phy_type_string(type)); + goto err0; + } + + get_device(phy->dev); + +err0: + spin_unlock_irqrestore(&phy_lock, flags); + + return phy; +} +EXPORT_SYMBOL(usb_get_phy); + + /** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + unsigned long flags; + struct device_node *node; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + dev_dbg(dev, "failed to allocate memory for devres\n"); + goto err0; + } + + spin_lock_irqsave(&phy_lock, flags); + + phy = __of_usb_find_phy(node); + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { + phy = ERR_PTR(-EPROBE_DEFER); + devres_free(ptr); + goto err1; + } + + *ptr = phy; + devres_add(dev, ptr); + + get_device(phy->dev); + +err1: + spin_unlock_irqrestore(&phy_lock, flags); + +err0: + of_node_put(node); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy_by_phandle); + +/** + * usb_get_phy_dev - find the USB PHY + * @dev - device that requests this phy + * @index - the index of the phy + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) +{ + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy_dev(dev, &phy_bind_list, index); + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { + pr_err("unable to find transceiver\n"); + goto err0; + } + + get_device(phy->dev); + +err0: + spin_unlock_irqrestore(&phy_lock, flags); + + return phy; +} +EXPORT_SYMBOL(usb_get_phy_dev); + +/** + * devm_usb_get_phy_dev - find the USB PHY using device ptr and index + * @dev - device that requests this phy + * @index - the index of the phy + * + * Gets the phy using usb_get_phy_dev(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy_dev(dev, index); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy_dev); + +/** + * devm_usb_put_phy - release the USB PHY + * @dev - device that wants to release this phy + * @phy - the phy returned by devm_usb_get_phy() + * + * destroys the devres associated with this phy and invokes usb_put_phy + * to release the phy. + * + * For use by USB host and peripheral drivers. + */ +void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL(devm_usb_put_phy); + +/** + * usb_put_phy - release the USB PHY + * @x: the phy returned by usb_get_phy() + * + * Releases a refcount the caller received from usb_get_phy(). + * + * For use by USB host and peripheral drivers. + */ +void usb_put_phy(struct usb_phy *x) +{ + if (x) { + struct module *owner = x->dev->driver->owner; + + put_device(x->dev); + module_put(owner); + } +} +EXPORT_SYMBOL(usb_put_phy); + +/** + * usb_add_phy - declare the USB PHY + * @x: the USB phy to be used; or NULL + * @type - the type of this PHY + * + * This call is exclusively for use by phy drivers, which + * coordinate the activities of drivers for host and peripheral + * controllers, and in some cases for VBUS current regulation. + */ +int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) +{ + int ret = 0; + unsigned long flags; + struct usb_phy *phy; + + if (x->type != USB_PHY_TYPE_UNDEFINED) { + dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->type == type) { + ret = -EBUSY; + dev_err(x->dev, "transceiver type %s already exists\n", + usb_phy_type_string(type)); + goto out; + } + } + + x->type = type; + list_add_tail(&x->head, &phy_list); + +out: + spin_unlock_irqrestore(&phy_lock, flags); + return ret; +} +EXPORT_SYMBOL(usb_add_phy); + +/** + * usb_add_phy_dev - declare the USB PHY + * @x: the USB phy to be used; or NULL + * + * This call is exclusively for use by phy drivers, which + * coordinate the activities of drivers for host and peripheral + * controllers, and in some cases for VBUS current regulation. + */ +int usb_add_phy_dev(struct usb_phy *x) +{ + struct usb_phy_bind *phy_bind; + unsigned long flags; + + if (!x->dev) { + dev_err(x->dev, "no device provided for PHY\n"); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev)))) + phy_bind->phy = x; + + list_add_tail(&x->head, &phy_list); + + spin_unlock_irqrestore(&phy_lock, flags); + return 0; +} +EXPORT_SYMBOL(usb_add_phy_dev); + +/** + * usb_remove_phy - remove the OTG PHY + * @x: the USB OTG PHY to be removed; + * + * This reverts the effects of usb_add_phy + */ +void usb_remove_phy(struct usb_phy *x) +{ + unsigned long flags; + struct usb_phy_bind *phy_bind; + + spin_lock_irqsave(&phy_lock, flags); + if (x) { + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (phy_bind->phy == x) + phy_bind->phy = NULL; + list_del(&x->head); + } + spin_unlock_irqrestore(&phy_lock, flags); +} +EXPORT_SYMBOL(usb_remove_phy); + +/** + * usb_bind_phy - bind the phy and the controller that uses the phy + * @dev_name: the device name of the device that will bind to the phy + * @index: index to specify the port number + * @phy_dev_name: the device name of the phy + * + * Fills the phy_bind structure with the dev_name and phy_dev_name. This will + * be used when the phy driver registers the phy and when the controller + * requests this phy. + * + * To be used by platform specific initialization code. + */ +int __init usb_bind_phy(const char *dev_name, u8 index, + const char *phy_dev_name) +{ + struct usb_phy_bind *phy_bind; + unsigned long flags; + + phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); + if (!phy_bind) { + pr_err("phy_bind(): No memory for phy_bind"); + return -ENOMEM; + } + + phy_bind->dev_name = dev_name; + phy_bind->phy_dev_name = phy_dev_name; + phy_bind->index = index; + + spin_lock_irqsave(&phy_lock, flags); + list_add_tail(&phy_bind->list, &phy_bind_list); + spin_unlock_irqrestore(&phy_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_bind_phy); -- cgit v0.10.2 From 25df6397a6c063811154868b868e8bd10e5ae9b1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 27 Feb 2013 15:16:30 +0100 Subject: usb: phy: mxs-phy: register phy with framework We now have usb_add_phy_dev(), so use it to register with the framework to be able to find the phy from the USB driver. Tested-by: Steffen Trumtrar Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Peter Chen Signed-off-by: Sascha Hauer Signed-off-by: Marc Kleine-Budde Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c index b0d9f11..aa40325 100644 --- a/drivers/usb/otg/mxs-phy.c +++ b/drivers/usb/otg/mxs-phy.c @@ -127,6 +127,7 @@ static int mxs_phy_probe(struct platform_device *pdev) void __iomem *base; struct clk *clk; struct mxs_phy *mxs_phy; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -166,11 +167,19 @@ static int mxs_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &mxs_phy->phy); + ret = usb_add_phy_dev(&mxs_phy->phy); + if (ret) + return ret; + return 0; } static int mxs_phy_remove(struct platform_device *pdev) { + struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&mxs_phy->phy); + platform_set_drvdata(pdev, NULL); return 0; -- cgit v0.10.2 From b5a726b30436ab332aea4133bbfa0484d1c658b3 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 28 Feb 2013 11:52:30 +0100 Subject: usb: phy: mxs: use readl(), writel() instead of the _relaxed() versions This patch converts the mxs-phy driver from readl_relaxed(), writel_relaxed() to the plain readl(), writel() functions, which are available on all platforms. This is done to enable compile time testing on non ARM platforms. Reported-by: Alexander Shishkin Signed-off-by: Marc Kleine-Budde Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c index aa40325..9d4381e 100644 --- a/drivers/usb/otg/mxs-phy.c +++ b/drivers/usb/otg/mxs-phy.c @@ -48,12 +48,12 @@ static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) stmp_reset_block(base + HW_USBPHY_CTRL); /* Power up the PHY */ - writel_relaxed(0, base + HW_USBPHY_PWD); + writel(0, base + HW_USBPHY_PWD); /* enable FS/LS device */ - writel_relaxed(BM_USBPHY_CTRL_ENUTMILEVEL2 | - BM_USBPHY_CTRL_ENUTMILEVEL3, - base + HW_USBPHY_CTRL_SET); + writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, + base + HW_USBPHY_CTRL_SET); } static int mxs_phy_init(struct usb_phy *phy) @@ -70,8 +70,8 @@ static void mxs_phy_shutdown(struct usb_phy *phy) { struct mxs_phy *mxs_phy = to_mxs_phy(phy); - writel_relaxed(BM_USBPHY_CTRL_CLKGATE, - phy->io_priv + HW_USBPHY_CTRL_SET); + writel(BM_USBPHY_CTRL_CLKGATE, + phy->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } @@ -81,15 +81,15 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) struct mxs_phy *mxs_phy = to_mxs_phy(x); if (suspend) { - writel_relaxed(0xffffffff, x->io_priv + HW_USBPHY_PWD); - writel_relaxed(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_SET); + writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } else { clk_prepare_enable(mxs_phy->clk); - writel_relaxed(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_CLR); - writel_relaxed(0, x->io_priv + HW_USBPHY_PWD); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_CLR); + writel(0, x->io_priv + HW_USBPHY_PWD); } return 0; @@ -102,8 +102,8 @@ static int mxs_phy_on_connect(struct usb_phy *phy, (speed == USB_SPEED_HIGH) ? "high" : "non-high"); if (speed == USB_SPEED_HIGH) - writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_SET); + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_SET); return 0; } @@ -115,8 +115,8 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, (speed == USB_SPEED_HIGH) ? "high" : "non-high"); if (speed == USB_SPEED_HIGH) - writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_CLR); + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_CLR); return 0; } -- cgit v0.10.2 From cd051da2c81c8a86f331b4caa0c135c33d3ea3f6 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Sat, 2 Mar 2013 18:55:24 +0530 Subject: usb: dwc3: set 'mode' based on selected Kconfig choices Now that machines may select dwc3's working mode (HOST only, GADGET only or DUAL_ROLE) via Kconfig, let's set dwc3's mode based on that, rather than fixing it to whatever hardware says. This way we can skip initializing Gadget/Host in case we are using Host-only/Gadget-only mode respectively. Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b81b335..c6a46e0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -479,7 +479,12 @@ static int dwc3_probe(struct platform_device *pdev) goto err1; } - mode = DWC3_MODE(dwc->hwparams.hwparams0); + if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + mode = DWC3_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + mode = DWC3_MODE_DEVICE; + else + mode = DWC3_MODE_DRD; switch (mode) { case DWC3_MODE_DEVICE: -- cgit v0.10.2 From d25ab3ece05c7cb740af155ecac87b6b36f16566 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 5 Mar 2013 09:42:15 +0530 Subject: usb: gadget: fsl_udc_core: Use module_platform_driver_probe macro module_platform_driver_probe() eliminates the boilerplate and simplifies the code. Signed-off-by: Sachin Kamat Acked-by: Li Yang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index f224169..7c2a101 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2712,21 +2712,7 @@ static struct platform_driver udc_driver = { }, }; -static int __init udc_init(void) -{ - printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); - return platform_driver_probe(&udc_driver, fsl_udc_probe); -} - -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); - printk(KERN_WARNING "%s unregistered\n", driver_desc); -} - -module_exit(udc_exit); +module_platform_driver_probe(udc_driver, fsl_udc_probe); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v0.10.2 From 2daf5966d140d23d1b5ba347b53d102eeb029d2c Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 20 Feb 2013 09:53:39 +0100 Subject: usb: musb: drop dangling CONFIG_USB_MUSB_DEBUG CONFIG_USB_MUSB_DEBUG option was removed in 5c8a86e usb: musb: drop unneeded musb_debug trickery to cleanup the code from driver specific debug facilities. This patch drops the last references to the musb debug config option, unconditionally enabling all debug code paths, thus letting that code being dropped at compile time if not needed. Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index f522000..9db211e 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -435,7 +435,6 @@ cppi_rndis_update(struct cppi_channel *c, int is_rx, } } -#ifdef CONFIG_USB_MUSB_DEBUG static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) { pr_debug("RXBD/%s %08x: " @@ -444,21 +443,16 @@ static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); } -#endif static void cppi_dump_rxq(int level, const char *tag, struct cppi_channel *rx) { -#ifdef CONFIG_USB_MUSB_DEBUG struct cppi_descriptor *bd; - if (!_dbg_level(level)) - return; cppi_dump_rx(level, rx, tag); if (rx->last_processed) cppi_dump_rxbd("last", rx->last_processed); for (bd = rx->head; bd; bd = bd->next) cppi_dump_rxbd("active", bd); -#endif } @@ -784,6 +778,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) void __iomem *tibase = musb->ctrl_base; int is_rndis = 0; struct cppi_rx_stateram __iomem *rx_ram = rx->state_ram; + struct cppi_descriptor *d; if (onepacket) { /* almost every USB driver, host or peripheral side */ @@ -897,14 +892,8 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) bd->hw_options |= CPPI_SOP_SET; tail->hw_options |= CPPI_EOP_SET; -#ifdef CONFIG_USB_MUSB_DEBUG - if (_dbg_level(5)) { - struct cppi_descriptor *d; - - for (d = rx->head; d; d = d->next) - cppi_dump_rxbd("S", d); - } -#endif + for (d = rx->head; d; d = d->next) + cppi_dump_rxbd("S", d); /* in case the preceding transfer left some state... */ tail = rx->last_processed; -- cgit v0.10.2 From 5f71791947fcb1942df7d0df45520d420c2aee2b Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Wed, 6 Mar 2013 01:04:50 +0530 Subject: usb: otg: fsl_otg: remove redundant NULL check before kfree kfree on NULL pointer is a no-op. Signed-off-by: Syam Sidhardhan Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index d16adb4..37e8e15 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -361,28 +361,18 @@ int fsl_otg_init_timers(struct otg_fsm *fsm) void fsl_otg_uninit_timers(void) { /* FSM used timers */ - if (a_wait_vrise_tmr != NULL) - kfree(a_wait_vrise_tmr); - if (a_wait_bcon_tmr != NULL) - kfree(a_wait_bcon_tmr); - if (a_aidl_bdis_tmr != NULL) - kfree(a_aidl_bdis_tmr); - if (b_ase0_brst_tmr != NULL) - kfree(b_ase0_brst_tmr); - if (b_se0_srp_tmr != NULL) - kfree(b_se0_srp_tmr); - if (b_srp_fail_tmr != NULL) - kfree(b_srp_fail_tmr); - if (a_wait_enum_tmr != NULL) - kfree(a_wait_enum_tmr); + kfree(a_wait_vrise_tmr); + kfree(a_wait_bcon_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_ase0_brst_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_fail_tmr); + kfree(a_wait_enum_tmr); /* device driver used timers */ - if (b_srp_wait_tmr != NULL) - kfree(b_srp_wait_tmr); - if (b_data_pulse_tmr != NULL) - kfree(b_data_pulse_tmr); - if (b_vbus_pulse_tmr != NULL) - kfree(b_vbus_pulse_tmr); + kfree(b_srp_wait_tmr); + kfree(b_data_pulse_tmr); + kfree(b_vbus_pulse_tmr); } /* Add timer to timer list */ -- cgit v0.10.2 From 789451f6c698282d0745f265bde47173c9372867 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 5 May 2011 15:53:10 +0300 Subject: usb: dwc3: calculate the number of endpoints hwparams2 holds the number of endpoints which were selected during RTL generation, we can use that on our driver. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c6a46e0..66c0572 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -260,6 +260,17 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static void dwc3_core_num_eps(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); + dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; + + dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n", + dwc->num_in_eps, dwc->num_out_eps); +} + static void dwc3_cache_hwparams(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; @@ -336,6 +347,8 @@ static int dwc3_core_init(struct dwc3 *dwc) if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; + dwc3_core_num_eps(dwc); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); return 0; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index ad2ffac..b42f71c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -575,6 +575,14 @@ struct dwc3_hwparams { /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) +/* HWPARAMS3 */ +#define DWC3_NUM_IN_EPS_MASK (0x1f << 18) +#define DWC3_NUM_EPS_MASK (0x3f << 12) +#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_EPS_MASK)) >> 12) +#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_IN_EPS_MASK)) >> 18) + /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) @@ -641,6 +649,8 @@ struct dwc3_scratchpad_array { * @u2pel: parameter from Set SEL request. * @u1sel: parameter from Set SEL request. * @u1pel: parameter from Set SEL request. + * @num_out_eps: number of out endpoints + * @num_in_eps: number of in endpoints * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -658,8 +668,10 @@ struct dwc3 { dma_addr_t ep0_trb_addr; dma_addr_t ep0_bounce_addr; struct dwc3_request ep0_usb_req; + /* device lock */ spinlock_t lock; + struct device *dev; struct platform_device *xhci; @@ -727,6 +739,9 @@ struct dwc3 { u8 speed; + u8 num_out_eps; + u8 num_in_eps; + void *mem; struct dwc3_hwparams hwparams; -- cgit v0.10.2 From 6a1e3ef45fb0c4d79cbb5190c8fc59263c630b0e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 5 May 2011 16:21:59 +0300 Subject: usb: dwc3: gadget: use num_(in|out)_eps from HW params that way we will only tell gadget framework about the endpoints we actually have. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8e53acc..2b6e7e0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1623,14 +1623,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = { /* -------------------------------------------------------------------------- */ -static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) +static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, + u8 num, u32 direction) { struct dwc3_ep *dep; - u8 epnum; + u8 i; - INIT_LIST_HEAD(&dwc->gadget.ep_list); + for (i = 0; i < num; i++) { + u8 epnum = (i << 1) | (!!direction); - for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) { dev_err(dwc->dev, "can't allocate endpoint %d\n", @@ -1644,6 +1645,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, (epnum & 1) ? "in" : "out"); + dep->endpoint.name = dep->name; dep->direction = (epnum & 1); @@ -1674,6 +1676,27 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) return 0; } +static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + int ret; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n"); + return ret; + } + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n"); + return ret; + } + + return 0; +} + static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -1681,6 +1704,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = dwc->eps[epnum]; + if (!dep) + continue; + dwc3_free_trb_pool(dep); if (epnum != 0 && epnum != 1) @@ -2015,6 +2041,9 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc) struct dwc3_ep *dep; dep = dwc->eps[epnum]; + if (!dep) + continue; + if (!(dep->flags & DWC3_EP_ENABLED)) continue; @@ -2032,6 +2061,8 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) int ret; dep = dwc->eps[epnum]; + if (!dep) + continue; if (!(dep->flags & DWC3_EP_STALL)) continue; -- cgit v0.10.2 From 42c0bf1ce7c067bbc3e77d5626f102a16bc4fb6b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 10:39:57 +0200 Subject: usb: otg: prefix otg_state_string with usb_ all other functions under drivers/usb/ start with usb_, let's do the same thing. This patch is in preparation for moving otg_state_string to usb-common.c and deleting otg.c completely. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 59eea21..2231850 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -149,7 +149,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -195,7 +195,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -208,7 +208,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -298,7 +298,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index dbb31b3..5e63b16 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -280,13 +280,13 @@ static void musb_conn_timer_handler(unsigned long _musb) break; default: dev_dbg(musb->controller, "%s state not handled\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; } spin_unlock_irqrestore(&musb->lock, flags); dev_dbg(musb->controller, "state is %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } static void bfin_musb_enable(struct musb *musb) @@ -307,7 +307,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 7c71769d..ea7e591 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -198,7 +198,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -267,7 +267,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -280,7 +280,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -360,7 +360,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index e040d91..bea6cc3 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -215,7 +215,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -349,7 +349,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) davinci_musb_source_power(musb, drvvbus, 0); dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); retval = IRQ_HANDLED; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fad8571..6bd8792 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -372,13 +372,13 @@ static void musb_otg_timer_func(unsigned long data) case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: dev_dbg(musb->controller, "HNP: %s timeout\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } musb->ignore_disconnect = 0; spin_unlock_irqrestore(&musb->lock, flags); @@ -393,13 +393,14 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - dev_dbg(musb->controller, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "HNP: stop from %s\n", + usb_otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: musb_g_disconnect(musb); dev_dbg(musb->controller, "HNP: back to %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: dev_dbg(musb->controller, "HNP: Disabling HR\n"); @@ -413,7 +414,7 @@ void musb_hnp_stop(struct musb *musb) break; default: dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } /* @@ -451,7 +452,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { void __iomem *mbase = musb->mregs; @@ -493,7 +494,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "host", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } else { switch (musb->xceiv->state) { @@ -522,7 +523,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "peripheral", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } } @@ -538,7 +539,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -603,7 +604,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), devctl, ({ char *s; switch (devctl & MUSB_DEVCTL_VBUS) { @@ -628,7 +629,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (int_usb & MUSB_INTR_SUSPEND) { dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x\n", - otg_state_string(musb->xceiv->state), devctl); + usb_otg_state_string(musb->xceiv->state), devctl); handled = IRQ_HANDLED; switch (musb->xceiv->state) { @@ -745,12 +746,12 @@ b_host: usb_hcd_resume_root_hub(hcd); dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", - otg_state_string(musb->xceiv->state), devctl); + usb_otg_state_string(musb->xceiv->state), devctl); } if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; @@ -787,7 +788,7 @@ b_host: break; default: WARNING("unhandled DISCONNECT transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; } } @@ -813,7 +814,7 @@ b_host: } } else { dev_dbg(musb->controller, "BUS RESET as %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* We need to ignore disconnect on suspend @@ -826,7 +827,7 @@ b_host: case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */ dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), TA_WAIT_BCON(musb)); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies(TA_WAIT_BCON(musb))); @@ -838,7 +839,7 @@ b_host: break; case OTG_STATE_B_WAIT_ACON: dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; @@ -850,7 +851,7 @@ b_host: break; default: dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } } @@ -1632,7 +1633,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) int ret = -EINVAL; spin_lock_irqsave(&musb->lock, flags); - ret = sprintf(buf, "%s\n", otg_state_string(musb->xceiv->state)); + ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->state)); spin_unlock_irqrestore(&musb->lock, flags); return ret; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4b49874..1ea553d 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -225,7 +225,7 @@ static void otg_timer(unsigned long _musb) */ devctl = dsps_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -274,7 +274,7 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&glue->timer[pdev->id]); glue->last_timer[pdev->id] = jiffies; return; @@ -289,7 +289,7 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) glue->last_timer[pdev->id] = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&glue->timer[pdev->id], timeout); } @@ -378,7 +378,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6101ebf..e840888 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1571,7 +1571,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) goto done; default: dev_dbg(musb->controller, "Unhandled wake: %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); goto done; } @@ -1970,7 +1970,7 @@ void musb_g_resume(struct musb *musb) break; default: WARNING("unhandled RESUME transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } @@ -2000,7 +2000,7 @@ void musb_g_suspend(struct musb *musb) * A_PERIPHERAL may need care too */ WARNING("unhandled SUSPEND transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } @@ -2034,7 +2034,7 @@ void musb_g_disconnect(struct musb *musb) switch (musb->xceiv->state) { default: dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); break; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 1ce1fcf..51e9e8a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2453,7 +2453,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) if (musb->is_active) { WARNING("trying to suspend as %s while active\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); return -EBUSY; } else return 0; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index f705791..ef7d110 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -95,7 +95,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; default: dev_dbg(musb->controller, "bogus rh suspend? %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; @@ -203,7 +203,7 @@ void musb_root_disconnect(struct musb *musb) break; default: dev_dbg(musb->controller, "host disconnect (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 1a42a45..8ba9bb2 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -117,7 +117,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -134,7 +134,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -200,7 +200,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 464bd23..7369ba3 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -423,7 +423,7 @@ static void musb_do_idle(unsigned long _musb) && (musb->idle_timeout == 0 || time_after(jiffies, musb->idle_timeout))) { dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } /* FALLTHROUGH */ case OTG_STATE_A_IDLE: @@ -478,7 +478,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -495,7 +495,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -571,7 +571,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on) musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL), musb_readl(tbase, TUSB_DEV_OTG_STAT), conf, prcm); @@ -678,13 +678,13 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) musb->is_active = 0; } dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: @@ -733,7 +733,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) u8 devctl; dev_dbg(musb->controller, "%s timer, %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 37e8e15..72a2a00 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -992,7 +992,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, /* State */ t = scnprintf(next, size, "OTG state: %s\n\n", - otg_state_string(fsl_otg_dev->phy.state)); + usb_otg_state_string(fsl_otg_dev->phy.state)); size -= t; next += t; diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index af9cb11..8fe0c3b 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -236,7 +236,7 @@ isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) static inline const char *state_name(struct isp1301 *isp) { - return otg_state_string(isp->phy.state); + return usb_otg_state_string(isp->phy.state); } /*-------------------------------------------------------------------------*/ @@ -481,7 +481,7 @@ static void check_state(struct isp1301 *isp, const char *tag) if (isp->phy.state == state && !extra) return; pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - otg_state_string(state), fsm, state_name(isp), + usb_otg_state_string(state), fsm, state_name(isp), omap_readl(OTG_CTRL)); } @@ -1077,7 +1077,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) if (state != isp->phy.state) pr_debug(" isp, %s -> %s\n", - otg_state_string(state), state_name(isp)); + usb_otg_state_string(state), state_name(isp)); #ifdef CONFIG_USB_OTG /* update the OTG controller state to match the isp1301; may diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 358cfd9..fd9a4b7 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,7 +11,7 @@ #include #include -const char *otg_state_string(enum usb_otg_state state) +const char *usb_otg_state_string(enum usb_otg_state state) { switch (state) { case OTG_STATE_A_IDLE: @@ -44,4 +44,4 @@ const char *otg_state_string(enum usb_otg_state state) return "UNDEFINED"; } } -EXPORT_SYMBOL(otg_state_string); +EXPORT_SYMBOL(usb_otg_state_string); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c index ade131a..1f729a1 100644 --- a/drivers/usb/otg/otg_fsm.c +++ b/drivers/usb/otg/otg_fsm.c @@ -119,7 +119,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) state_changed = 1; if (fsm->otg->phy->state == new_state) return 0; - VDBG("Set state: %s\n", otg_state_string(new_state)); + VDBG("Set state: %s\n", usb_otg_state_string(new_state)); otg_leave_state(fsm, fsm->otg->phy->state); switch (new_state) { case OTG_STATE_B_IDLE: diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index e8a5fe8..9f9fb39 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -37,9 +37,9 @@ struct usb_otg { }; #ifdef CONFIG_USB_OTG_UTILS -extern const char *otg_state_string(enum usb_otg_state state); +extern const char *usb_otg_state_string(enum usb_otg_state state); #else -static inline const char *otg_state_string(enum usb_otg_state state) +static inline const char *usb_otg_state_string(enum usb_otg_state state) { return NULL; } -- cgit v0.10.2 From 7009bdd7f31ed6e769af0f76e2368bb6033be572 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 10:45:56 +0200 Subject: usb: otg: move usb_otg_state_string to usb-common.c otg.c only had a single function definition which might make more sense to be placed in usb-common.c. While doing that, we also delete otg.c since it's now empty. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index a844b8d..6abc453 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -5,9 +5,6 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG -# infrastructure -obj-$(CONFIG_USB_OTG_UTILS) += otg.o - # transceiver drivers obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c deleted file mode 100644 index fd9a4b7..0000000 --- a/drivers/usb/otg/otg.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * otg.c -- USB OTG utility code - * - * Copyright (C) 2004 Texas Instruments - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#include -#include - -const char *usb_otg_state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: - return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: - return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: - return "a_wait_bcon"; - case OTG_STATE_A_HOST: - return "a_host"; - case OTG_STATE_A_SUSPEND: - return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: - return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: - return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: - return "a_vbus_err"; - case OTG_STATE_B_IDLE: - return "b_idle"; - case OTG_STATE_B_SRP_INIT: - return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: - return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: - return "b_wait_acon"; - case OTG_STATE_B_HOST: - return "b_host"; - default: - return "UNDEFINED"; - } -} -EXPORT_SYMBOL(usb_otg_state_string); diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index 070b681..0db0a91 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -14,6 +14,32 @@ #include #include #include +#include + +const char *usb_otg_state_string(enum usb_otg_state state) +{ + static const char *const names[] = { + [OTG_STATE_A_IDLE] = "a_idle", + [OTG_STATE_A_WAIT_VRISE] = "a_wait_vrise", + [OTG_STATE_A_WAIT_BCON] = "a_wait_bcon", + [OTG_STATE_A_HOST] = "a_host", + [OTG_STATE_A_SUSPEND] = "a_suspend", + [OTG_STATE_A_PERIPHERAL] = "a_peripheral", + [OTG_STATE_A_WAIT_VFALL] = "a_wait_vfall", + [OTG_STATE_A_VBUS_ERR] = "a_vbus_err", + [OTG_STATE_B_IDLE] = "b_idle", + [OTG_STATE_B_SRP_INIT] = "b_srp_init", + [OTG_STATE_B_PERIPHERAL] = "b_peripheral", + [OTG_STATE_B_WAIT_ACON] = "b_wait_acon", + [OTG_STATE_B_HOST] = "b_host", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNDEFINED"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_otg_state_string); const char *usb_speed_string(enum usb_device_speed speed) { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 9f9fb39..291e01b 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -36,14 +36,7 @@ struct usb_otg { }; -#ifdef CONFIG_USB_OTG_UTILS extern const char *usb_otg_state_string(enum usb_otg_state state); -#else -static inline const char *usb_otg_state_string(enum usb_otg_state state) -{ - return NULL; -} -#endif /* Context: can sleep */ static inline int -- cgit v0.10.2 From 110ff6d04162a8a3b288019eaf84dee0800270e0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 10:49:27 +0200 Subject: usb: phy: convert EXPORT_SYMBOL to EXPORT_SYMBOL_GPL we only want GPL users for our generic functions, so let's switch over to EXPORT_SYMBOL_GPL. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index bc1970c..f52c006 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -109,7 +109,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) return phy; } -EXPORT_SYMBOL(devm_usb_get_phy); +EXPORT_SYMBOL_GPL(devm_usb_get_phy); /** * usb_get_phy - find the USB PHY @@ -142,7 +142,7 @@ err0: return phy; } -EXPORT_SYMBOL(usb_get_phy); +EXPORT_SYMBOL_GPL(usb_get_phy); /** * devm_usb_get_phy_by_phandle - find the USB PHY by phandle @@ -206,7 +206,7 @@ err0: return phy; } -EXPORT_SYMBOL(devm_usb_get_phy_by_phandle); +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** * usb_get_phy_dev - find the USB PHY @@ -239,7 +239,7 @@ err0: return phy; } -EXPORT_SYMBOL(usb_get_phy_dev); +EXPORT_SYMBOL_GPL(usb_get_phy_dev); /** * devm_usb_get_phy_dev - find the USB PHY using device ptr and index @@ -269,7 +269,7 @@ struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) return phy; } -EXPORT_SYMBOL(devm_usb_get_phy_dev); +EXPORT_SYMBOL_GPL(devm_usb_get_phy_dev); /** * devm_usb_put_phy - release the USB PHY @@ -288,7 +288,7 @@ void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); } -EXPORT_SYMBOL(devm_usb_put_phy); +EXPORT_SYMBOL_GPL(devm_usb_put_phy); /** * usb_put_phy - release the USB PHY @@ -307,7 +307,7 @@ void usb_put_phy(struct usb_phy *x) module_put(owner); } } -EXPORT_SYMBOL(usb_put_phy); +EXPORT_SYMBOL_GPL(usb_put_phy); /** * usb_add_phy - declare the USB PHY @@ -347,7 +347,7 @@ out: spin_unlock_irqrestore(&phy_lock, flags); return ret; } -EXPORT_SYMBOL(usb_add_phy); +EXPORT_SYMBOL_GPL(usb_add_phy); /** * usb_add_phy_dev - declare the USB PHY @@ -377,7 +377,7 @@ int usb_add_phy_dev(struct usb_phy *x) spin_unlock_irqrestore(&phy_lock, flags); return 0; } -EXPORT_SYMBOL(usb_add_phy_dev); +EXPORT_SYMBOL_GPL(usb_add_phy_dev); /** * usb_remove_phy - remove the OTG PHY @@ -399,7 +399,7 @@ void usb_remove_phy(struct usb_phy *x) } spin_unlock_irqrestore(&phy_lock, flags); } -EXPORT_SYMBOL(usb_remove_phy); +EXPORT_SYMBOL_GPL(usb_remove_phy); /** * usb_bind_phy - bind the phy and the controller that uses the phy -- cgit v0.10.2 From a0e631235a04f8a815a1ecec93ef418f7d1e6086 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:01:15 +0200 Subject: usb: phy: move all PHY drivers to drivers/usb/phy/ that's a much more reasonable location for those drivers. It helps us saving drivers/usb/otg/ for when we actually start adding generic OTG code. Also completely delete drivers/usb/otg/ as there's nothing left there. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 640ae6c..2c481b8 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -186,6 +186,4 @@ source "drivers/usb/atm/Kconfig" source "drivers/usb/gadget/Kconfig" -source "drivers/usb/otg/Kconfig" - endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8f5ebce..860306b 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -6,8 +6,6 @@ obj-$(CONFIG_USB) += core/ -obj-$(CONFIG_USB_OTG_UTILS) += otg/ - obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MON) += mon/ diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig deleted file mode 100644 index 37962c9..0000000 --- a/drivers/usb/otg/Kconfig +++ /dev/null @@ -1,141 +0,0 @@ -# -# USB OTG infrastructure may be needed for peripheral-only, host-only, -# or OTG-capable configurations when OTG transceivers or controllers -# are used. -# - -comment "OTG and related infrastructure" - -config USB_OTG_UTILS - bool - help - Select this to make sure the build includes objects from - the OTG infrastructure directory. - -if USB || USB_GADGET - -# -# USB Transceiver Drivers -# -config USB_GPIO_VBUS - tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GENERIC_GPIO - select USB_OTG_UTILS - help - Provides simple GPIO VBUS sensing for controllers with an - internal transceiver via the usb_phy interface, and - optionally control of a D+ pullup GPIO as well as a VBUS - current limit regulator. - -config ISP1301_OMAP - tristate "Philips ISP1301 with OMAP OTG" - depends on I2C && ARCH_OMAP_OTG - select USB_OTG_UTILS - help - If you say yes here you get support for the Philips ISP1301 - USB-On-The-Go transceiver working with the OMAP OTG controller. - The ISP1301 is a full speed USB transceiver which is used in - products including H2, H3, and H4 development boards for Texas - Instruments OMAP processors. - - This driver can also be built as a module. If so, the module - will be called isp1301_omap. - -config USB_ULPI - bool "Generic ULPI Transceiver Driver" - depends on ARM - select USB_OTG_UTILS - help - Enable this to support ULPI connected USB OTG transceivers which - are likely found on embedded boards. - -config USB_ULPI_VIEWPORT - bool - depends on USB_ULPI - help - Provides read/write operations to the ULPI phy register set for - controllers with a viewport register (e.g. Chipidea/ARC controllers). - -config TWL4030_USB - tristate "TWL4030 USB Transceiver Driver" - depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS - select USB_OTG_UTILS - help - Enable this to support the USB OTG transceiver on TWL4030 - family chips (including the TWL5030 and TPS659x0 devices). - This transceiver supports high and full speed devices plus, - in host mode, low speed. - -config TWL6030_USB - tristate "TWL6030 USB Transceiver Driver" - depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS - select USB_OTG_UTILS - help - Enable this to support the USB OTG transceiver on TWL6030 - family chips. This TWL6030 transceiver has the VBUS and ID GND - and OTG SRP events capabilities. For all other transceiver functionality - UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs - are hooked to this driver through platform_data structure. - The definition of internal PHY APIs are in the mach-omap2 layer. - -config NOP_USB_XCEIV - tristate "NOP USB Transceiver Driver" - select USB_OTG_UTILS - help - This driver is to be used by all the usb transceiver which are either - built-in with usb ip or which are autonomous and doesn't require any - phy programming such as ISP1x04 etc. - -config USB_MSM_OTG - tristate "OTG support for Qualcomm on-chip USB controller" - depends on (USB || USB_GADGET) && ARCH_MSM - select USB_OTG_UTILS - help - Enable this to support the USB OTG transceiver on MSM chips. It - handles PHY initialization, clock management, and workarounds - required after resetting the hardware and power management. - This driver is required even for peripheral only or host only - mode configurations. - This driver is not supported on boards like trout which - has an external PHY. - -config AB8500_USB - tristate "AB8500 USB Transceiver Driver" - depends on AB8500_CORE - select USB_OTG_UTILS - help - Enable this to support the USB OTG transceiver in AB8500 chip. - This transceiver supports high and full speed devices plus, - in host mode, low speed. - -config FSL_USB2_OTG - bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND - select USB_OTG - select USB_OTG_UTILS - help - Enable this to support Freescale USB OTG transceiver. - -config USB_MXS_PHY - tristate "Freescale MXS USB PHY support" - depends on ARCH_MXC || ARCH_MXS - select STMP_DEVICE - select USB_OTG_UTILS - help - Enable this to support the Freescale MXS USB PHY. - - MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. - -config USB_MV_OTG - tristate "Marvell USB OTG support" - depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND - select USB_OTG - select USB_OTG_UTILS - help - Say Y here if you want to build Marvell USB OTG transciever - driver in kernel (including PXA and MMP series). This driver - implements role switch between EHCI host driver and gadget driver. - - To compile this driver as a module, choose M here. - -endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile deleted file mode 100644 index 6abc453..0000000 --- a/drivers/usb/otg/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# -# OTG infrastructure and transceiver drivers -# - -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG - -# transceiver drivers -obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o -obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o -obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o -obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o -obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o -obj-$(CONFIG_USB_ULPI) += ulpi.o -obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o -obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o -obj-$(CONFIG_AB8500_USB) += ab8500-usb.o -fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o -obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o -obj-$(CONFIG_USB_MXS_PHY) += mxs-phy.o -obj-$(CONFIG_USB_MV_OTG) += mv_otg.o diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c deleted file mode 100644 index 2d86f26..0000000 --- a/drivers/usb/otg/ab8500-usb.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * drivers/usb/otg/ab8500_usb.c - * - * USB transceiver driver for AB8500 chip - * - * Copyright (C) 2010 ST-Ericsson AB - * Mian Yousaf Kaukab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AB8500_MAIN_WD_CTRL_REG 0x01 -#define AB8500_USB_LINE_STAT_REG 0x80 -#define AB8500_USB_PHY_CTRL_REG 0x8A - -#define AB8500_BIT_OTG_STAT_ID (1 << 0) -#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) -#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) -#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) -#define AB8500_BIT_WD_CTRL_KICK (1 << 1) - -#define AB8500_V1x_LINK_STAT_WAIT (HZ/10) -#define AB8500_WD_KICK_DELAY_US 100 /* usec */ -#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ -#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */ - -/* Usb line status register */ -enum ab8500_usb_link_status { - USB_LINK_NOT_CONFIGURED = 0, - USB_LINK_STD_HOST_NC, - USB_LINK_STD_HOST_C_NS, - USB_LINK_STD_HOST_C_S, - USB_LINK_HOST_CHG_NM, - USB_LINK_HOST_CHG_HS, - USB_LINK_HOST_CHG_HS_CHIRP, - USB_LINK_DEDICATED_CHG, - USB_LINK_ACA_RID_A, - USB_LINK_ACA_RID_B, - USB_LINK_ACA_RID_C_NM, - USB_LINK_ACA_RID_C_HS, - USB_LINK_ACA_RID_C_HS_CHIRP, - USB_LINK_HM_IDGND, - USB_LINK_RESERVED, - USB_LINK_NOT_VALID_LINK -}; - -struct ab8500_usb { - struct usb_phy phy; - struct device *dev; - int irq_num_id_rise; - int irq_num_id_fall; - int irq_num_vbus_rise; - int irq_num_vbus_fall; - int irq_num_link_status; - unsigned vbus_draw; - struct delayed_work dwork; - struct work_struct phy_dis_work; - unsigned long link_status_wait; - int rev; -}; - -static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) -{ - return container_of(x, struct ab8500_usb, phy); -} - -static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) -{ - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - AB8500_BIT_WD_CTRL_ENABLE); - - udelay(AB8500_WD_KICK_DELAY_US); - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - (AB8500_BIT_WD_CTRL_ENABLE - | AB8500_BIT_WD_CTRL_KICK)); - - if (ab->rev > 0x10) /* v1.1 v2.0 */ - udelay(AB8500_WD_V11_DISABLE_DELAY_US); - else /* v1.0 */ - msleep(AB8500_WD_V10_DISABLE_DELAY_MS); - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - 0); -} - -static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, - bool enable) -{ - u8 ctrl_reg; - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - &ctrl_reg); - if (sel_host) { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN; - } else { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN; - } - - abx500_set_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - ctrl_reg); - - /* Needed to enable the phy.*/ - if (enable) - ab8500_usb_wd_workaround(ab); -} - -#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true) -#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false) -#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) -#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) - -static int ab8500_usb_link_status_update(struct ab8500_usb *ab) -{ - u8 reg; - enum ab8500_usb_link_status lsts; - void *v = NULL; - enum usb_phy_events event; - - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_LINE_STAT_REG, - ®); - - lsts = (reg >> 3) & 0x0F; - - switch (lsts) { - case USB_LINK_NOT_CONFIGURED: - case USB_LINK_RESERVED: - case USB_LINK_NOT_VALID_LINK: - /* TODO: Disable regulators. */ - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - ab->phy.state = OTG_STATE_B_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - event = USB_EVENT_NONE; - break; - - case USB_LINK_STD_HOST_NC: - case USB_LINK_STD_HOST_C_NS: - case USB_LINK_STD_HOST_C_S: - case USB_LINK_HOST_CHG_NM: - case USB_LINK_HOST_CHG_HS: - case USB_LINK_HOST_CHG_HS_CHIRP: - if (ab->phy.otg->gadget) { - /* TODO: Enable regulators. */ - ab8500_usb_peri_phy_en(ab); - v = ab->phy.otg->gadget; - } - event = USB_EVENT_VBUS; - break; - - case USB_LINK_HM_IDGND: - if (ab->phy.otg->host) { - /* TODO: Enable regulators. */ - ab8500_usb_host_phy_en(ab); - v = ab->phy.otg->host; - } - ab->phy.state = OTG_STATE_A_IDLE; - ab->phy.otg->default_a = true; - event = USB_EVENT_ID; - break; - - case USB_LINK_ACA_RID_A: - case USB_LINK_ACA_RID_B: - /* TODO */ - case USB_LINK_ACA_RID_C_NM: - case USB_LINK_ACA_RID_C_HS: - case USB_LINK_ACA_RID_C_HS_CHIRP: - case USB_LINK_DEDICATED_CHG: - /* TODO: vbus_draw */ - event = USB_EVENT_CHARGER; - break; - } - - atomic_notifier_call_chain(&ab->phy.notifier, event, v); - - return 0; -} - -static void ab8500_usb_delayed_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - dwork.work); - - ab8500_usb_link_status_update(ab); -} - -static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - -static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Link status will not be updated till phy is disabled. */ - ab8500_usb_peri_phy_dis(ab); - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - -static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - ab8500_usb_link_status_update(ab); - - return IRQ_HANDLED; -} - -static void ab8500_usb_phy_disable_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - phy_dis_work); - - if (!ab->phy.otg->host) - ab8500_usb_host_phy_dis(ab); - - if (!ab->phy.otg->gadget) - ab8500_usb_peri_phy_dis(ab); -} - -static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) -{ - struct ab8500_usb *ab; - - if (!phy) - return -ENODEV; - - ab = phy_to_ab(phy); - - ab->vbus_draw = mA; - - if (mA) - atomic_notifier_call_chain(&ab->phy.notifier, - USB_EVENT_ENUMERATED, ab->phy.otg->gadget); - return 0; -} - -/* TODO: Implement some way for charging or other drivers to read - * ab->vbus_draw. - */ - -static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) -{ - /* TODO */ - return 0; -} - -static int ab8500_usb_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct ab8500_usb *ab; - - if (!otg) - return -ENODEV; - - ab = phy_to_ab(otg->phy); - - /* Some drivers call this function in atomic context. - * Do not update ab8500 registers directly till this - * is fixed. - */ - - if (!gadget) { - /* TODO: Disable regulators. */ - otg->gadget = NULL; - schedule_work(&ab->phy_dis_work); - } else { - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - } - - return 0; -} - -static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct ab8500_usb *ab; - - if (!otg) - return -ENODEV; - - ab = phy_to_ab(otg->phy); - - /* Some drivers call this function in atomic context. - * Do not update ab8500 registers directly till this - * is fixed. - */ - - if (!host) { - /* TODO: Disable regulators. */ - otg->host = NULL; - schedule_work(&ab->phy_dis_work); - } else { - otg->host = host; - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - } - - return 0; -} - -static void ab8500_usb_irq_free(struct ab8500_usb *ab) -{ - if (ab->rev < 0x20) { - free_irq(ab->irq_num_id_rise, ab); - free_irq(ab->irq_num_id_fall, ab); - free_irq(ab->irq_num_vbus_rise, ab); - free_irq(ab->irq_num_vbus_fall, ab); - } else { - free_irq(ab->irq_num_link_status, ab); - } -} - -static int ab8500_usb_v1x_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) -{ - int err; - - ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R"); - if (ab->irq_num_id_rise < 0) { - dev_err(&pdev->dev, "ID rise irq not found\n"); - return ab->irq_num_id_rise; - } - err = request_threaded_irq(ab->irq_num_id_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID rise irq\n"); - goto fail0; - } - - ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); - if (ab->irq_num_id_fall < 0) { - dev_err(&pdev->dev, "ID fall irq not found\n"); - return ab->irq_num_id_fall; - } - err = request_threaded_irq(ab->irq_num_id_fall, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID fall irq\n"); - goto fail1; - } - - ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R"); - if (ab->irq_num_vbus_rise < 0) { - dev_err(&pdev->dev, "VBUS rise irq not found\n"); - return ab->irq_num_vbus_rise; - } - err = request_threaded_irq(ab->irq_num_vbus_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus rise irq\n"); - goto fail2; - } - - ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F"); - if (ab->irq_num_vbus_fall < 0) { - dev_err(&pdev->dev, "VBUS fall irq not found\n"); - return ab->irq_num_vbus_fall; - } - err = request_threaded_irq(ab->irq_num_vbus_fall, NULL, - ab8500_usb_v1x_vbus_fall_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); - goto fail3; - } - - return 0; -fail3: - free_irq(ab->irq_num_vbus_rise, ab); -fail2: - free_irq(ab->irq_num_id_fall, ab); -fail1: - free_irq(ab->irq_num_id_rise, ab); -fail0: - return err; -} - -static int ab8500_usb_v2_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) -{ - int err; - - ab->irq_num_link_status = platform_get_irq_byname(pdev, - "USB_LINK_STATUS"); - if (ab->irq_num_link_status < 0) { - dev_err(&pdev->dev, "Link status irq not found\n"); - return ab->irq_num_link_status; - } - - err = request_threaded_irq(ab->irq_num_link_status, NULL, - ab8500_usb_v20_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-link-status", ab); - if (err < 0) { - dev_err(ab->dev, - "request_irq failed for link status irq\n"); - return err; - } - - return 0; -} - -static int ab8500_usb_probe(struct platform_device *pdev) -{ - struct ab8500_usb *ab; - struct usb_otg *otg; - int err; - int rev; - - rev = abx500_get_chip_id(&pdev->dev); - if (rev < 0) { - dev_err(&pdev->dev, "Chip id read failed\n"); - return rev; - } else if (rev < 0x10) { - dev_err(&pdev->dev, "Unsupported AB8500 chip\n"); - return -ENODEV; - } - - ab = kzalloc(sizeof *ab, GFP_KERNEL); - if (!ab) - return -ENOMEM; - - otg = kzalloc(sizeof *otg, GFP_KERNEL); - if (!otg) { - kfree(ab); - return -ENOMEM; - } - - ab->dev = &pdev->dev; - ab->rev = rev; - ab->phy.dev = ab->dev; - ab->phy.otg = otg; - ab->phy.label = "ab8500"; - ab->phy.set_suspend = ab8500_usb_set_suspend; - ab->phy.set_power = ab8500_usb_set_power; - ab->phy.state = OTG_STATE_UNDEFINED; - - otg->phy = &ab->phy; - otg->set_host = ab8500_usb_set_host; - otg->set_peripheral = ab8500_usb_set_peripheral; - - platform_set_drvdata(pdev, ab); - - ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); - - /* v1: Wait for link status to become stable. - * all: Updates form set_host and set_peripheral as they are atomic. - */ - INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work); - - /* all: Disable phy when called from set_host and set_peripheral */ - INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - - if (ab->rev < 0x20) { - err = ab8500_usb_v1x_res_setup(pdev, ab); - ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT; - } else { - err = ab8500_usb_v2_res_setup(pdev, ab); - } - - if (err < 0) - goto fail0; - - err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); - if (err) { - dev_err(&pdev->dev, "Can't register transceiver\n"); - goto fail1; - } - - dev_info(&pdev->dev, "AB8500 usb driver initialized\n"); - - return 0; -fail1: - ab8500_usb_irq_free(ab); -fail0: - kfree(otg); - kfree(ab); - return err; -} - -static int ab8500_usb_remove(struct platform_device *pdev) -{ - struct ab8500_usb *ab = platform_get_drvdata(pdev); - - ab8500_usb_irq_free(ab); - - cancel_delayed_work_sync(&ab->dwork); - - cancel_work_sync(&ab->phy_dis_work); - - usb_remove_phy(&ab->phy); - - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - - platform_set_drvdata(pdev, NULL); - - kfree(ab->phy.otg); - kfree(ab); - - return 0; -} - -static struct platform_driver ab8500_usb_driver = { - .probe = ab8500_usb_probe, - .remove = ab8500_usb_remove, - .driver = { - .name = "ab8500-usb", - .owner = THIS_MODULE, - }, -}; - -static int __init ab8500_usb_init(void) -{ - return platform_driver_register(&ab8500_usb_driver); -} -subsys_initcall(ab8500_usb_init); - -static void __exit ab8500_usb_exit(void) -{ - platform_driver_unregister(&ab8500_usb_driver); -} -module_exit(ab8500_usb_exit); - -MODULE_ALIAS("platform:ab8500_usb"); -MODULE_AUTHOR("ST-Ericsson AB"); -MODULE_DESCRIPTION("AB8500 usb transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c deleted file mode 100644 index 72a2a00..0000000 --- a/drivers/usb/otg/fsl_otg.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * Copyright (C) 2007,2008 Freescale semiconductor, Inc. - * - * Author: Li Yang - * Jerry Huang - * - * Initialization based on code from Shlomi Gridish. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "fsl_otg.h" - -#define DRIVER_VERSION "Rev. 1.55" -#define DRIVER_AUTHOR "Jerry Huang/Li Yang" -#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" -#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION - -static const char driver_name[] = "fsl-usb2-otg"; - -const pm_message_t otg_suspend_state = { - .event = 1, -}; - -#define HA_DATA_PULSE - -static struct usb_dr_mmap *usb_dr_regs; -static struct fsl_otg *fsl_otg_dev; -static int srp_wait_done; - -/* FSM timers */ -struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, - *b_ase0_brst_tmr, *b_se0_srp_tmr; - -/* Driver specific timers */ -struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, - *b_srp_wait_tmr, *a_wait_enum_tmr; - -static struct list_head active_timers; - -static struct fsl_otg_config fsl_otg_initdata = { - .otg_port = 1, -}; - -#ifdef CONFIG_PPC32 -static u32 _fsl_readl_be(const unsigned __iomem *p) -{ - return in_be32(p); -} - -static u32 _fsl_readl_le(const unsigned __iomem *p) -{ - return in_le32(p); -} - -static void _fsl_writel_be(u32 v, unsigned __iomem *p) -{ - out_be32(p, v); -} - -static void _fsl_writel_le(u32 v, unsigned __iomem *p) -{ - out_le32(p, v); -} - -static u32 (*_fsl_readl)(const unsigned __iomem *p); -static void (*_fsl_writel)(u32 v, unsigned __iomem *p); - -#define fsl_readl(p) (*_fsl_readl)((p)) -#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) - -#else -#define fsl_readl(addr) readl(addr) -#define fsl_writel(val, addr) writel(val, addr) -#endif /* CONFIG_PPC32 */ - -/* Routines to access transceiver ULPI registers */ -u8 view_ulpi(u8 addr) -{ - u32 temp; - - temp = 0x40000000 | (addr << 16); - fsl_writel(temp, &usb_dr_regs->ulpiview); - udelay(1000); - while (temp & 0x40) - temp = fsl_readl(&usb_dr_regs->ulpiview); - return (le32_to_cpu(temp) & 0x0000ff00) >> 8; -} - -int write_ulpi(u8 addr, u8 data) -{ - u32 temp; - - temp = 0x60000000 | (addr << 16) | data; - fsl_writel(temp, &usb_dr_regs->ulpiview); - return 0; -} - -/* -------------------------------------------------------------*/ -/* Operations that will be called from OTG Finite State Machine */ - -/* Charge vbus for vbus pulsing in SRP */ -void fsl_otg_chrg_vbus(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - /* stop discharging, start charging */ - tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | - OTGSC_CTRL_VBUS_CHARGE; - else - /* stop charging */ - tmp &= ~OTGSC_CTRL_VBUS_CHARGE; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* Discharge vbus through a resistor to ground */ -void fsl_otg_dischrg_vbus(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - /* stop charging, start discharging */ - tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | - OTGSC_CTRL_VBUS_DISCHARGE; - else - /* stop discharging */ - tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* A-device driver vbus, controlled through PP bit in PORTSC */ -void fsl_otg_drv_vbus(int on) -{ - u32 tmp; - - if (on) { - tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; - fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); - } else { - tmp = fsl_readl(&usb_dr_regs->portsc) & - ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; - fsl_writel(tmp, &usb_dr_regs->portsc); - } -} - -/* - * Pull-up D+, signalling connect by periperal. Also used in - * data-line pulsing in SRP - */ -void fsl_otg_loc_conn(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - tmp |= OTGSC_CTRL_DATA_PULSING; - else - tmp &= ~OTGSC_CTRL_DATA_PULSING; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* - * Generate SOF by host. This is controlled through suspend/resume the - * port. In host mode, controller will automatically send SOF. - * Suspend will block the data on the port. - */ -void fsl_otg_loc_sof(int on) -{ - u32 tmp; - - tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; - if (on) - tmp |= PORTSC_PORT_FORCE_RESUME; - else - tmp |= PORTSC_PORT_SUSPEND; - - fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); - -} - -/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ -void fsl_otg_start_pulse(void) -{ - u32 tmp; - - srp_wait_done = 0; -#ifdef HA_DATA_PULSE - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - tmp |= OTGSC_HA_DATA_PULSE; - fsl_writel(tmp, &usb_dr_regs->otgsc); -#else - fsl_otg_loc_conn(1); -#endif - - fsl_otg_add_timer(b_data_pulse_tmr); -} - -void b_data_pulse_end(unsigned long foo) -{ -#ifdef HA_DATA_PULSE -#else - fsl_otg_loc_conn(0); -#endif - - /* Do VBUS pulse after data pulse */ - fsl_otg_pulse_vbus(); -} - -void fsl_otg_pulse_vbus(void) -{ - srp_wait_done = 0; - fsl_otg_chrg_vbus(1); - /* start the timer to end vbus charge */ - fsl_otg_add_timer(b_vbus_pulse_tmr); -} - -void b_vbus_pulse_end(unsigned long foo) -{ - fsl_otg_chrg_vbus(0); - - /* - * As USB3300 using the same a_sess_vld and b_sess_vld voltage - * we need to discharge the bus for a while to distinguish - * residual voltage of vbus pulsing and A device pull up - */ - fsl_otg_dischrg_vbus(1); - fsl_otg_add_timer(b_srp_wait_tmr); -} - -void b_srp_end(unsigned long foo) -{ - fsl_otg_dischrg_vbus(0); - srp_wait_done = 1; - - if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) && - fsl_otg_dev->fsm.b_sess_vld) - fsl_otg_dev->fsm.b_srp_done = 1; -} - -/* - * Workaround for a_host suspending too fast. When a_bus_req=0, - * a_host will start by SRP. It needs to set b_hnp_enable before - * actually suspending to start HNP - */ -void a_wait_enum(unsigned long foo) -{ - VDBG("a_wait_enum timeout\n"); - if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) - fsl_otg_add_timer(a_wait_enum_tmr); - else - otg_statemachine(&fsl_otg_dev->fsm); -} - -/* The timeout callback function to set time out bit */ -void set_tmout(unsigned long indicator) -{ - *(int *)indicator = 1; -} - -/* Initialize timers */ -int fsl_otg_init_timers(struct otg_fsm *fsm) -{ - /* FSM used timers */ - a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, - (unsigned long)&fsm->a_wait_vrise_tmout); - if (!a_wait_vrise_tmr) - return -ENOMEM; - - a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, - (unsigned long)&fsm->a_wait_bcon_tmout); - if (!a_wait_bcon_tmr) - return -ENOMEM; - - a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, - (unsigned long)&fsm->a_aidl_bdis_tmout); - if (!a_aidl_bdis_tmr) - return -ENOMEM; - - b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, - (unsigned long)&fsm->b_ase0_brst_tmout); - if (!b_ase0_brst_tmr) - return -ENOMEM; - - b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, - (unsigned long)&fsm->b_se0_srp); - if (!b_se0_srp_tmr) - return -ENOMEM; - - b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, - (unsigned long)&fsm->b_srp_done); - if (!b_srp_fail_tmr) - return -ENOMEM; - - a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, - (unsigned long)&fsm); - if (!a_wait_enum_tmr) - return -ENOMEM; - - /* device driver used timers */ - b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); - if (!b_srp_wait_tmr) - return -ENOMEM; - - b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, - TB_DATA_PLS, 0); - if (!b_data_pulse_tmr) - return -ENOMEM; - - b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, - TB_VBUS_PLS, 0); - if (!b_vbus_pulse_tmr) - return -ENOMEM; - - return 0; -} - -/* Uninitialize timers */ -void fsl_otg_uninit_timers(void) -{ - /* FSM used timers */ - kfree(a_wait_vrise_tmr); - kfree(a_wait_bcon_tmr); - kfree(a_aidl_bdis_tmr); - kfree(b_ase0_brst_tmr); - kfree(b_se0_srp_tmr); - kfree(b_srp_fail_tmr); - kfree(a_wait_enum_tmr); - - /* device driver used timers */ - kfree(b_srp_wait_tmr); - kfree(b_data_pulse_tmr); - kfree(b_vbus_pulse_tmr); -} - -/* Add timer to timer list */ -void fsl_otg_add_timer(void *gtimer) -{ - struct fsl_otg_timer *timer = gtimer; - struct fsl_otg_timer *tmp_timer; - - /* - * Check if the timer is already in the active list, - * if so update timer count - */ - list_for_each_entry(tmp_timer, &active_timers, list) - if (tmp_timer == timer) { - timer->count = timer->expires; - return; - } - timer->count = timer->expires; - list_add_tail(&timer->list, &active_timers); -} - -/* Remove timer from the timer list; clear timeout status */ -void fsl_otg_del_timer(void *gtimer) -{ - struct fsl_otg_timer *timer = gtimer; - struct fsl_otg_timer *tmp_timer, *del_tmp; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) - if (tmp_timer == timer) - list_del(&timer->list); -} - -/* - * Reduce timer count by 1, and find timeout conditions. - * Called by fsl_otg 1ms timer interrupt - */ -int fsl_otg_tick_timer(void) -{ - struct fsl_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - return expired; -} - -/* Reset controller, not reset the bus */ -void otg_reset_controller(void) -{ - u32 command; - - command = fsl_readl(&usb_dr_regs->usbcmd); - command |= (1 << 1); - fsl_writel(command, &usb_dr_regs->usbcmd); - while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) - ; -} - -/* Call suspend/resume routines in host driver */ -int fsl_otg_start_host(struct otg_fsm *fsm, int on) -{ - struct usb_otg *otg = fsm->otg; - struct device *dev; - struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy); - u32 retval = 0; - - if (!otg->host) - return -ENODEV; - dev = otg->host->controller; - - /* - * Update a_vbus_vld state as a_vbus_vld int is disabled - * in device mode - */ - fsm->a_vbus_vld = - !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); - if (on) { - /* start fsl usb host controller */ - if (otg_dev->host_working) - goto end; - else { - otg_reset_controller(); - VDBG("host on......\n"); - if (dev->driver->pm && dev->driver->pm->resume) { - retval = dev->driver->pm->resume(dev); - if (fsm->id) { - /* default-b */ - fsl_otg_drv_vbus(1); - /* - * Workaround: b_host can't driver - * vbus, but PP in PORTSC needs to - * be 1 for host to work. - * So we set drv_vbus bit in - * transceiver to 0 thru ULPI. - */ - write_ulpi(0x0c, 0x20); - } - } - - otg_dev->host_working = 1; - } - } else { - /* stop fsl usb host controller */ - if (!otg_dev->host_working) - goto end; - else { - VDBG("host off......\n"); - if (dev && dev->driver) { - if (dev->driver->pm && dev->driver->pm->suspend) - retval = dev->driver->pm->suspend(dev); - if (fsm->id) - /* default-b */ - fsl_otg_drv_vbus(0); - } - otg_dev->host_working = 0; - } - } -end: - return retval; -} - -/* - * Call suspend and resume function in udc driver - * to stop and start udc driver. - */ -int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) -{ - struct usb_otg *otg = fsm->otg; - struct device *dev; - - if (!otg->gadget || !otg->gadget->dev.parent) - return -ENODEV; - - VDBG("gadget %s\n", on ? "on" : "off"); - dev = otg->gadget->dev.parent; - - if (on) { - if (dev->driver->resume) - dev->driver->resume(dev); - } else { - if (dev->driver->suspend) - dev->driver->suspend(dev, otg_suspend_state); - } - - return 0; -} - -/* - * Called by initialization code of host driver. Register host controller - * to the OTG. Suspend host for OTG role detection. - */ -static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - otg->host = host; - - otg_dev->fsm.a_bus_drop = 0; - otg_dev->fsm.a_bus_req = 1; - - if (host) { - VDBG("host off......\n"); - - otg->host->otg_port = fsl_otg_initdata.otg_port; - otg->host->is_b_host = otg_dev->fsm.id; - /* - * must leave time for khubd to finish its thing - * before yanking the host driver out from under it, - * so suspend the host after a short delay. - */ - otg_dev->host_working = 1; - schedule_delayed_work(&otg_dev->otg_event, 100); - return 0; - } else { - /* host driver going away */ - if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & - OTGSC_STS_USB_ID)) { - /* Mini-A cable connected */ - struct otg_fsm *fsm = &otg_dev->fsm; - - otg->phy->state = OTG_STATE_UNDEFINED; - fsm->protocol = PROTO_UNDEF; - } - } - - otg_dev->host_working = 0; - - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* Called by initialization code of udc. Register udc to OTG. */ -static int fsl_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - VDBG("otg_dev 0x%x\n", (int)otg_dev); - VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - if (!gadget) { - if (!otg->default_a) - otg->gadget->ops->vbus_draw(otg->gadget, 0); - usb_gadget_vbus_disconnect(otg->gadget); - otg->gadget = 0; - otg_dev->fsm.b_bus_req = 0; - otg_statemachine(&otg_dev->fsm); - return 0; - } - - otg->gadget = gadget; - otg->gadget->is_a_peripheral = !otg_dev->fsm.id; - - otg_dev->fsm.b_bus_req = 1; - - /* start the gadget right away if the ID pin says Mini-B */ - DBG("ID pin=%d\n", otg_dev->fsm.id); - if (otg_dev->fsm.id == 1) { - fsl_otg_start_host(&otg_dev->fsm, 0); - otg_drv_vbus(&otg_dev->fsm, 0); - fsl_otg_start_gadget(&otg_dev->fsm, 1); - } - - return 0; -} - -/* Set OTG port power, only for B-device */ -static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA) -{ - if (!fsl_otg_dev) - return -ENODEV; - if (phy->state == OTG_STATE_B_PERIPHERAL) - pr_info("FSL OTG: Draw %d mA\n", mA); - - return 0; -} - -/* - * Delayed pin detect interrupt processing. - * - * When the Mini-A cable is disconnected from the board, - * the pin-detect interrupt happens before the disconnect - * interrupts for the connected device(s). In order to - * process the disconnect interrupt(s) prior to switching - * roles, the pin-detect interrupts are delayed, and handled - * by this routine. - */ -static void fsl_otg_event(struct work_struct *work) -{ - struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); - struct otg_fsm *fsm = &og->fsm; - - if (fsm->id) { /* switch to gadget */ - fsl_otg_start_host(fsm, 0); - otg_drv_vbus(fsm, 0); - fsl_otg_start_gadget(fsm, 1); - } -} - -/* B-device start SRP */ -static int fsl_otg_start_srp(struct usb_otg *otg) -{ - struct fsl_otg *otg_dev; - - if (!otg || otg->phy->state != OTG_STATE_B_IDLE) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - otg_dev->fsm.b_bus_req = 1; - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* A_host suspend will call this function to start hnp */ -static int fsl_otg_start_hnp(struct usb_otg *otg) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - DBG("start_hnp...n"); - - /* clear a_bus_req to enter a_suspend state */ - otg_dev->fsm.a_bus_req = 0; - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* - * Interrupt handler. OTG/host/peripheral share the same int line. - * OTG driver clears OTGSC interrupts and leaves USB interrupts - * intact. It needs to have knowledge of some USB interrupts - * such as port change. - */ -irqreturn_t fsl_otg_isr(int irq, void *dev_id) -{ - struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; - struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; - u32 otg_int_src, otg_sc; - - otg_sc = fsl_readl(&usb_dr_regs->otgsc); - otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); - - /* Only clear otg interrupts */ - fsl_writel(otg_sc, &usb_dr_regs->otgsc); - - /*FIXME: ID change not generate when init to 0 */ - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); - - /* process OTG interrupts */ - if (otg_int_src) { - if (otg_int_src & OTGSC_INTSTS_USB_ID) { - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); - /* clear conn information */ - if (fsm->id) - fsm->b_conn = 0; - else - fsm->a_conn = 0; - - if (otg->host) - otg->host->is_b_host = fsm->id; - if (otg->gadget) - otg->gadget->is_a_peripheral = !fsm->id; - VDBG("ID int (ID is %d)\n", fsm->id); - - if (fsm->id) { /* switch to gadget */ - schedule_delayed_work( - &((struct fsl_otg *)dev_id)->otg_event, - 100); - } else { /* switch to host */ - cancel_delayed_work(& - ((struct fsl_otg *)dev_id)-> - otg_event); - fsl_otg_start_gadget(fsm, 0); - otg_drv_vbus(fsm, 1); - fsl_otg_start_host(fsm, 1); - } - return IRQ_HANDLED; - } - } - return IRQ_NONE; -} - -static struct otg_fsm_ops fsl_otg_ops = { - .chrg_vbus = fsl_otg_chrg_vbus, - .drv_vbus = fsl_otg_drv_vbus, - .loc_conn = fsl_otg_loc_conn, - .loc_sof = fsl_otg_loc_sof, - .start_pulse = fsl_otg_start_pulse, - - .add_timer = fsl_otg_add_timer, - .del_timer = fsl_otg_del_timer, - - .start_host = fsl_otg_start_host, - .start_gadget = fsl_otg_start_gadget, -}; - -/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ -static int fsl_otg_conf(struct platform_device *pdev) -{ - struct fsl_otg *fsl_otg_tc; - int status; - - if (fsl_otg_dev) - return 0; - - /* allocate space to fsl otg device */ - fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); - if (!fsl_otg_tc) - return -ENOMEM; - - fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!fsl_otg_tc->phy.otg) { - kfree(fsl_otg_tc); - return -ENOMEM; - } - - INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); - - INIT_LIST_HEAD(&active_timers); - status = fsl_otg_init_timers(&fsl_otg_tc->fsm); - if (status) { - pr_info("Couldn't init OTG timers\n"); - goto err; - } - spin_lock_init(&fsl_otg_tc->fsm.lock); - - /* Set OTG state machine operations */ - fsl_otg_tc->fsm.ops = &fsl_otg_ops; - - /* initialize the otg structure */ - fsl_otg_tc->phy.label = DRIVER_DESC; - fsl_otg_tc->phy.set_power = fsl_otg_set_power; - - fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy; - fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host; - fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral; - fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp; - fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp; - - fsl_otg_dev = fsl_otg_tc; - - /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); - if (status) { - pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); - goto err; - } - - return 0; -err: - fsl_otg_uninit_timers(); - kfree(fsl_otg_tc->phy.otg); - kfree(fsl_otg_tc); - return status; -} - -/* OTG Initialization */ -int usb_otg_start(struct platform_device *pdev) -{ - struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); - struct otg_fsm *fsm; - int status; - struct resource *res; - u32 temp; - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - p_otg = container_of(otg_trans, struct fsl_otg, phy); - fsm = &p_otg->fsm; - - /* Initialize the state machine structure with default values */ - SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); - fsm->otg = p_otg->phy.otg; - - /* We don't require predefined MEM/IRQ resource index */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - /* We don't request_mem_region here to enable resource sharing - * with host/device */ - - usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); - p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; - pdata->regs = (void *)usb_dr_regs; - - if (pdata->init && pdata->init(pdev) != 0) - return -EINVAL; - - if (pdata->big_endian_mmio) { - _fsl_readl = _fsl_readl_be; - _fsl_writel = _fsl_writel_be; - } else { - _fsl_readl = _fsl_readl_le; - _fsl_writel = _fsl_writel_le; - } - - /* request irq */ - p_otg->irq = platform_get_irq(pdev, 0); - status = request_irq(p_otg->irq, fsl_otg_isr, - IRQF_SHARED, driver_name, p_otg); - if (status) { - dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n", - p_otg->irq, status); - iounmap(p_otg->dr_mem_map); - kfree(p_otg->phy.otg); - kfree(p_otg); - return status; - } - - /* stop the controller */ - temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); - temp &= ~USB_CMD_RUN_STOP; - fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); - - /* reset the controller */ - temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); - temp |= USB_CMD_CTRL_RESET; - fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); - - /* wait reset completed */ - while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) - ; - - /* configure the VBUSHS as IDLE(both host and device) */ - temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); - fsl_writel(temp, &p_otg->dr_mem_map->usbmode); - - /* configure PHY interface */ - temp = fsl_readl(&p_otg->dr_mem_map->portsc); - temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); - switch (pdata->phy_mode) { - case FSL_USB2_PHY_ULPI: - temp |= PORTSC_PTS_ULPI; - break; - case FSL_USB2_PHY_UTMI_WIDE: - temp |= PORTSC_PTW_16BIT; - /* fall through */ - case FSL_USB2_PHY_UTMI: - temp |= PORTSC_PTS_UTMI; - /* fall through */ - default: - break; - } - fsl_writel(temp, &p_otg->dr_mem_map->portsc); - - if (pdata->have_sysif_regs) { - /* configure control enable IO output, big endian register */ - temp = __raw_readl(&p_otg->dr_mem_map->control); - temp |= USB_CTRL_IOENB; - __raw_writel(temp, &p_otg->dr_mem_map->control); - } - - /* disable all interrupt and clear all OTGSC status */ - temp = fsl_readl(&p_otg->dr_mem_map->otgsc); - temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; - temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; - fsl_writel(temp, &p_otg->dr_mem_map->otgsc); - - /* - * The identification (id) input is FALSE when a Mini-A plug is inserted - * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. - * Also: record initial state of ID pin - */ - if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { - p_otg->phy.state = OTG_STATE_UNDEFINED; - p_otg->fsm.id = 1; - } else { - p_otg->phy.state = OTG_STATE_A_IDLE; - p_otg->fsm.id = 0; - } - - DBG("initial ID pin=%d\n", p_otg->fsm.id); - - /* enable OTG ID pin interrupt */ - temp = fsl_readl(&p_otg->dr_mem_map->otgsc); - temp |= OTGSC_INTR_USB_ID_EN; - temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); - fsl_writel(temp, &p_otg->dr_mem_map->otgsc); - - return 0; -} - -/* - * state file in sysfs - */ -static int show_fsl_usb2_otg_state(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct otg_fsm *fsm = &fsl_otg_dev->fsm; - char *next = buf; - unsigned size = PAGE_SIZE; - unsigned long flags; - int t; - - spin_lock_irqsave(&fsm->lock, flags); - - /* basic driver infomation */ - t = scnprintf(next, size, - DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", - DRIVER_VERSION); - size -= t; - next += t; - - /* Registers */ - t = scnprintf(next, size, - "OTGSC: 0x%08x\n" - "PORTSC: 0x%08x\n" - "USBMODE: 0x%08x\n" - "USBCMD: 0x%08x\n" - "USBSTS: 0x%08x\n" - "USBINTR: 0x%08x\n", - fsl_readl(&usb_dr_regs->otgsc), - fsl_readl(&usb_dr_regs->portsc), - fsl_readl(&usb_dr_regs->usbmode), - fsl_readl(&usb_dr_regs->usbcmd), - fsl_readl(&usb_dr_regs->usbsts), - fsl_readl(&usb_dr_regs->usbintr)); - size -= t; - next += t; - - /* State */ - t = scnprintf(next, size, - "OTG state: %s\n\n", - usb_otg_state_string(fsl_otg_dev->phy.state)); - size -= t; - next += t; - - /* State Machine Variables */ - t = scnprintf(next, size, - "a_bus_req: %d\n" - "b_bus_req: %d\n" - "a_bus_resume: %d\n" - "a_bus_suspend: %d\n" - "a_conn: %d\n" - "a_sess_vld: %d\n" - "a_srp_det: %d\n" - "a_vbus_vld: %d\n" - "b_bus_resume: %d\n" - "b_bus_suspend: %d\n" - "b_conn: %d\n" - "b_se0_srp: %d\n" - "b_sess_end: %d\n" - "b_sess_vld: %d\n" - "id: %d\n", - fsm->a_bus_req, - fsm->b_bus_req, - fsm->a_bus_resume, - fsm->a_bus_suspend, - fsm->a_conn, - fsm->a_sess_vld, - fsm->a_srp_det, - fsm->a_vbus_vld, - fsm->b_bus_resume, - fsm->b_bus_suspend, - fsm->b_conn, - fsm->b_se0_srp, - fsm->b_sess_end, - fsm->b_sess_vld, - fsm->id); - size -= t; - next += t; - - spin_unlock_irqrestore(&fsm->lock, flags); - - return PAGE_SIZE - size; -} - -static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); - - -/* Char driver interface to control some OTG input */ - -/* - * Handle some ioctl command, such as get otg - * status and set host suspend - */ -static long fsl_otg_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - u32 retval = 0; - - switch (cmd) { - case GET_OTG_STATUS: - retval = fsl_otg_dev->host_working; - break; - - case SET_A_SUSPEND_REQ: - fsl_otg_dev->fsm.a_suspend_req = arg; - break; - - case SET_A_BUS_DROP: - fsl_otg_dev->fsm.a_bus_drop = arg; - break; - - case SET_A_BUS_REQ: - fsl_otg_dev->fsm.a_bus_req = arg; - break; - - case SET_B_BUS_REQ: - fsl_otg_dev->fsm.b_bus_req = arg; - break; - - default: - break; - } - - otg_statemachine(&fsl_otg_dev->fsm); - - return retval; -} - -static int fsl_otg_open(struct inode *inode, struct file *file) -{ - return 0; -} - -static int fsl_otg_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static const struct file_operations otg_fops = { - .owner = THIS_MODULE, - .llseek = NULL, - .read = NULL, - .write = NULL, - .unlocked_ioctl = fsl_otg_ioctl, - .open = fsl_otg_open, - .release = fsl_otg_release, -}; - -static int fsl_otg_probe(struct platform_device *pdev) -{ - int ret; - - if (!pdev->dev.platform_data) - return -ENODEV; - - /* configure the OTG */ - ret = fsl_otg_conf(pdev); - if (ret) { - dev_err(&pdev->dev, "Couldn't configure OTG module\n"); - return ret; - } - - /* start OTG */ - ret = usb_otg_start(pdev); - if (ret) { - dev_err(&pdev->dev, "Can't init FSL OTG device\n"); - return ret; - } - - ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); - if (ret) { - dev_err(&pdev->dev, "unable to register FSL OTG device\n"); - return ret; - } - - ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - if (ret) - dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); - - return ret; -} - -static int fsl_otg_remove(struct platform_device *pdev) -{ - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - usb_remove_phy(&fsl_otg_dev->phy); - free_irq(fsl_otg_dev->irq, fsl_otg_dev); - - iounmap((void *)usb_dr_regs); - - fsl_otg_uninit_timers(); - kfree(fsl_otg_dev->phy.otg); - kfree(fsl_otg_dev); - - device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - - unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); - - if (pdata->exit) - pdata->exit(pdev); - - return 0; -} - -struct platform_driver fsl_otg_driver = { - .probe = fsl_otg_probe, - .remove = fsl_otg_remove, - .driver = { - .name = driver_name, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(fsl_otg_driver); - -MODULE_DESCRIPTION(DRIVER_INFO); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/fsl_otg.h b/drivers/usb/otg/fsl_otg.h deleted file mode 100644 index ca26628..0000000 --- a/drivers/usb/otg/fsl_otg.h +++ /dev/null @@ -1,406 +0,0 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "otg_fsm.h" -#include -#include - -/* USB Command Register Bit Masks */ -#define USB_CMD_RUN_STOP (0x1<<0) -#define USB_CMD_CTRL_RESET (0x1<<1) -#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) -#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) -#define USB_CMD_INT_AA_DOORBELL (0x1<<6) -#define USB_CMD_ASP (0x3<<8) -#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) -#define USB_CMD_SUTW (0x1<<13) -#define USB_CMD_ATDTW (0x1<<14) -#define USB_CMD_ITC (0xFF<<16) - -/* bit 15,3,2 are frame list size */ -#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) -#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) -#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) -#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) -#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) -#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) -#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) -#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) - -/* bit 9-8 are async schedule park mode count */ -#define USB_CMD_ASP_00 (0x0<<8) -#define USB_CMD_ASP_01 (0x1<<8) -#define USB_CMD_ASP_10 (0x2<<8) -#define USB_CMD_ASP_11 (0x3<<8) -#define USB_CMD_ASP_BIT_POS (8) - -/* bit 23-16 are interrupt threshold control */ -#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) -#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) -#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) -#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) -#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) -#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) -#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) -#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) -#define USB_CMD_ITC_BIT_POS (16) - -/* USB Status Register Bit Masks */ -#define USB_STS_INT (0x1<<0) -#define USB_STS_ERR (0x1<<1) -#define USB_STS_PORT_CHANGE (0x1<<2) -#define USB_STS_FRM_LST_ROLL (0x1<<3) -#define USB_STS_SYS_ERR (0x1<<4) -#define USB_STS_IAA (0x1<<5) -#define USB_STS_RESET_RECEIVED (0x1<<6) -#define USB_STS_SOF (0x1<<7) -#define USB_STS_DCSUSPEND (0x1<<8) -#define USB_STS_HC_HALTED (0x1<<12) -#define USB_STS_RCL (0x1<<13) -#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) -#define USB_STS_ASYNC_SCHEDULE (0x1<<15) - -/* USB Interrupt Enable Register Bit Masks */ -#define USB_INTR_INT_EN (0x1<<0) -#define USB_INTR_ERR_INT_EN (0x1<<1) -#define USB_INTR_PC_DETECT_EN (0x1<<2) -#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) -#define USB_INTR_SYS_ERR_EN (0x1<<4) -#define USB_INTR_ASYN_ADV_EN (0x1<<5) -#define USB_INTR_RESET_EN (0x1<<6) -#define USB_INTR_SOF_EN (0x1<<7) -#define USB_INTR_DEVICE_SUSPEND (0x1<<8) - -/* Device Address bit masks */ -#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) -#define USB_DEVICE_ADDRESS_BIT_POS (25) -/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ -#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) -#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) -#define PORTSC_PORT_ENABLE (0x1<<2) -#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) -#define PORTSC_OVER_CURRENT_ACT (0x1<<4) -#define PORTSC_OVER_CUURENT_CHG (0x1<<5) -#define PORTSC_PORT_FORCE_RESUME (0x1<<6) -#define PORTSC_PORT_SUSPEND (0x1<<7) -#define PORTSC_PORT_RESET (0x1<<8) -#define PORTSC_LINE_STATUS_BITS (0x3<<10) -#define PORTSC_PORT_POWER (0x1<<12) -#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) -#define PORTSC_PORT_TEST_CTRL (0xF<<16) -#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) -#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) -#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) -#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) -#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) -#define PORTSC_PORT_SPEED_MASK (0x3<<26) -#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) -#define PORTSC_PHY_TYPE_SEL (0x3<<30) -/* bit 11-10 are line status */ -#define PORTSC_LINE_STATUS_SE0 (0x0<<10) -#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) -#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) -#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) -#define PORTSC_LINE_STATUS_BIT_POS (10) - -/* bit 15-14 are port indicator control */ -#define PORTSC_PIC_OFF (0x0<<14) -#define PORTSC_PIC_AMBER (0x1<<14) -#define PORTSC_PIC_GREEN (0x2<<14) -#define PORTSC_PIC_UNDEF (0x3<<14) -#define PORTSC_PIC_BIT_POS (14) - -/* bit 19-16 are port test control */ -#define PORTSC_PTC_DISABLE (0x0<<16) -#define PORTSC_PTC_JSTATE (0x1<<16) -#define PORTSC_PTC_KSTATE (0x2<<16) -#define PORTSC_PTC_SEQNAK (0x3<<16) -#define PORTSC_PTC_PACKET (0x4<<16) -#define PORTSC_PTC_FORCE_EN (0x5<<16) -#define PORTSC_PTC_BIT_POS (16) - -/* bit 27-26 are port speed */ -#define PORTSC_PORT_SPEED_FULL (0x0<<26) -#define PORTSC_PORT_SPEED_LOW (0x1<<26) -#define PORTSC_PORT_SPEED_HIGH (0x2<<26) -#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) -#define PORTSC_SPEED_BIT_POS (26) - -/* bit 28 is parallel transceiver width for UTMI interface */ -#define PORTSC_PTW (0x1<<28) -#define PORTSC_PTW_8BIT (0x0<<28) -#define PORTSC_PTW_16BIT (0x1<<28) - -/* bit 31-30 are port transceiver select */ -#define PORTSC_PTS_UTMI (0x0<<30) -#define PORTSC_PTS_ULPI (0x2<<30) -#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) -#define PORTSC_PTS_BIT_POS (30) - -#define PORTSC_W1C_BITS \ - (PORTSC_CONNECT_STATUS_CHANGE | \ - PORTSC_PORT_EN_DIS_CHANGE | \ - PORTSC_OVER_CUURENT_CHG) - -/* OTG Status Control Register Bit Masks */ -#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) -#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) -#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) -#define OTGSC_CTRL_DATA_PULSING (0x1<<4) -#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) -#define OTGSC_HA_DATA_PULSE (0x1<<6) -#define OTGSC_HA_BA (0x1<<7) -#define OTGSC_STS_USB_ID (0x1<<8) -#define OTGSC_STS_A_VBUS_VALID (0x1<<9) -#define OTGSC_STS_A_SESSION_VALID (0x1<<10) -#define OTGSC_STS_B_SESSION_VALID (0x1<<11) -#define OTGSC_STS_B_SESSION_END (0x1<<12) -#define OTGSC_STS_1MS_TOGGLE (0x1<<13) -#define OTGSC_STS_DATA_PULSING (0x1<<14) -#define OTGSC_INTSTS_USB_ID (0x1<<16) -#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) -#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) -#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) -#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) -#define OTGSC_INTSTS_1MS (0x1<<21) -#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) -#define OTGSC_INTR_USB_ID_EN (0x1<<24) -#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) -#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) -#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) -#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) -#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) -#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) -#define OTGSC_INTSTS_MASK (0x00ff0000) - -/* USB MODE Register Bit Masks */ -#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) -#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) -#define USB_MODE_CTRL_MODE_HOST (0x3<<0) -#define USB_MODE_CTRL_MODE_RSV (0x1<<0) -#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) -#define USB_MODE_STREAM_DISABLE (0x1<<4) -#define USB_MODE_ES (0x1<<2) /* Endian Select */ - -/* control Register Bit Masks */ -#define USB_CTRL_IOENB (0x1<<2) -#define USB_CTRL_ULPI_INT0EN (0x1<<0) - -/* BCSR5 */ -#define BCSR5_INT_USB (0x02) - -/* USB module clk cfg */ -#define SCCR_OFFS (0xA08) -#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ -#define SCCR_USB_MPHCM_11 (0x00c00000) -#define SCCR_USB_MPHCM_01 (0x00400000) -#define SCCR_USB_MPHCM_10 (0x00800000) -#define SCCR_USB_DRCM_11 (0x00300000) -#define SCCR_USB_DRCM_01 (0x00100000) -#define SCCR_USB_DRCM_10 (0x00200000) - -#define SICRL_OFFS (0x114) -#define SICRL_USB0 (0x40000000) -#define SICRL_USB1 (0x20000000) - -#define SICRH_OFFS (0x118) -#define SICRH_USB_UTMI (0x00020000) - -/* OTG interrupt enable bit masks */ -#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ - (OTGSC_INTR_USB_ID_EN | \ - OTGSC_INTR_1MS_TIMER_EN | \ - OTGSC_INTR_A_VBUS_VALID_EN | \ - OTGSC_INTR_A_SESSION_VALID_EN | \ - OTGSC_INTR_B_SESSION_VALID_EN | \ - OTGSC_INTR_B_SESSION_END_EN | \ - OTGSC_INTR_DATA_PULSING_EN) - -/* OTG interrupt status bit masks */ -#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ - (OTGSC_INTSTS_USB_ID | \ - OTGSC_INTR_1MS_TIMER_EN | \ - OTGSC_INTSTS_A_VBUS_VALID | \ - OTGSC_INTSTS_A_SESSION_VALID | \ - OTGSC_INTSTS_B_SESSION_VALID | \ - OTGSC_INTSTS_B_SESSION_END | \ - OTGSC_INTSTS_DATA_PULSING) - -/* - * A-DEVICE timing constants - */ - -/* Wait for VBUS Rise */ -#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ - -/* Wait for B-Connect */ -#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 - * This is only used to get out of - * OTG_STATE_A_WAIT_BCON state if there was - * no connection for these many milliseconds - */ - -/* A-Idle to B-Disconnect */ -/* It is necessary for this timer to be more than 750 ms because of a bug in OPT - * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated - * in the test description - */ -#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ - -/* B-Idle to A-Disconnect */ -#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ - -/* B-device timing constants */ - - -/* Data-Line Pulse Time*/ -#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ -#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ -#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ - -/* SRP Initiate Time */ -#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ - -/* SRP Fail Time */ -#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ - -/* SRP result wait time */ -#define TB_SRP_WAIT (60) - -/* VBus time */ -#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ - -/* Discharge time */ -/* This time should be less than 10ms. It varies from system to system. */ -#define TB_VBUS_DSCHRG (8) - -/* A-SE0 to B-Reset */ -#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ - -/* A bus suspend timer before we can switch to b_wait_aconn */ -#define TB_A_SUSPEND (7) -#define TB_BUS_RESUME (12) - -/* SE0 Time Before SRP */ -#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ - -#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) - -struct usb_dr_mmap { - /* Capability register */ - u8 res1[256]; - u16 caplength; /* Capability Register Length */ - u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ - u32 hccparams; /* Host Controller Capability Parameters */ - u8 res2[20]; - u32 dciversion; /* Device Controller Interface Version */ - u32 dccparams; /* Device Controller Capability Parameters */ - u8 res3[24]; - /* Operation register */ - u32 usbcmd; /* USB Command Register */ - u32 usbsts; /* USB Status Register */ - u32 usbintr; /* USB Interrupt Enable Register */ - u32 frindex; /* Frame Index Register */ - u8 res4[4]; - u32 deviceaddr; /* Device Address */ - u32 endpointlistaddr; /* Endpoint List Address Register */ - u8 res5[4]; - u32 burstsize; /* Master Interface Data Burst Size Register */ - u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ - u8 res6[8]; - u32 ulpiview; /* ULPI register access */ - u8 res7[12]; - u32 configflag; /* Configure Flag Register */ - u32 portsc; /* Port 1 Status and Control Register */ - u8 res8[28]; - u32 otgsc; /* On-The-Go Status and Control */ - u32 usbmode; /* USB Mode Register */ - u32 endptsetupstat; /* Endpoint Setup Status Register */ - u32 endpointprime; /* Endpoint Initialization Register */ - u32 endptflush; /* Endpoint Flush Register */ - u32 endptstatus; /* Endpoint Status Register */ - u32 endptcomplete; /* Endpoint Complete Register */ - u32 endptctrl[6]; /* Endpoint Control Registers */ - u8 res9[552]; - u32 snoop1; - u32 snoop2; - u32 age_cnt_thresh; /* Age Count Threshold Register */ - u32 pri_ctrl; /* Priority Control Register */ - u32 si_ctrl; /* System Interface Control Register */ - u8 res10[236]; - u32 control; /* General Purpose Control Register */ -}; - -struct fsl_otg_timer { - unsigned long expires; /* Number of count increase to timeout */ - unsigned long count; /* Tick counter */ - void (*function)(unsigned long); /* Timeout function */ - unsigned long data; /* Data passed to function */ - struct list_head list; -}; - -inline struct fsl_otg_timer *otg_timer_initializer -(void (*function)(unsigned long), unsigned long expires, unsigned long data) -{ - struct fsl_otg_timer *timer; - - timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); - if (!timer) - return NULL; - timer->function = function; - timer->expires = expires; - timer->data = data; - return timer; -} - -struct fsl_otg { - struct usb_phy phy; - struct otg_fsm fsm; - struct usb_dr_mmap *dr_mem_map; - struct delayed_work otg_event; - - /* used for usb host */ - struct work_struct work_wq; - u8 host_working; - - int irq; -}; - -struct fsl_otg_config { - u8 otg_port; -}; - -/* For SRP and HNP handle */ -#define FSL_OTG_MAJOR 240 -#define FSL_OTG_NAME "fsl-usb2-otg" -/* Command to OTG driver ioctl */ -#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR -/* if otg work as host, it should return 1, otherwise return 0 */ -#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) -#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) -#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) -#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) -#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) -#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) -#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) -#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) -#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) - -void fsl_otg_add_timer(void *timer); -void fsl_otg_del_timer(void *timer); -void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c deleted file mode 100644 index a7d4ac5..0000000 --- a/drivers/usb/otg/gpio_vbus.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices - * - * Copyright (c) 2008 Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - - -/* - * A simple GPIO VBUS sensing driver for B peripheral only devices - * with internal transceivers. It can control a D+ pullup GPIO and - * a regulator to limit the current drawn from VBUS. - * - * Needs to be loaded before the UDC driver that will use it. - */ -struct gpio_vbus_data { - struct usb_phy phy; - struct device *dev; - struct regulator *vbus_draw; - int vbus_draw_enabled; - unsigned mA; - struct delayed_work work; - int vbus; - int irq; -}; - - -/* - * This driver relies on "both edges" triggering. VBUS has 100 msec to - * stabilize, so the peripheral controller driver may need to cope with - * some bouncing due to current surges (e.g. charging local capacitance) - * and contact chatter. - * - * REVISIT in desperate straits, toggling between rising and falling - * edges might be workable. - */ -#define VBUS_IRQ_FLAGS \ - (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) - - -/* interface to regulator framework */ -static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) -{ - struct regulator *vbus_draw = gpio_vbus->vbus_draw; - int enabled; - - if (!vbus_draw) - return; - - enabled = gpio_vbus->vbus_draw_enabled; - if (mA) { - regulator_set_current_limit(vbus_draw, 0, 1000 * mA); - if (!enabled) { - regulator_enable(vbus_draw); - gpio_vbus->vbus_draw_enabled = 1; - } - } else { - if (enabled) { - regulator_disable(vbus_draw); - gpio_vbus->vbus_draw_enabled = 0; - } - } - gpio_vbus->mA = mA; -} - -static int is_vbus_powered(struct gpio_vbus_mach_info *pdata) -{ - int vbus; - - vbus = gpio_get_value(pdata->gpio_vbus); - if (pdata->gpio_vbus_inverted) - vbus = !vbus; - - return vbus; -} - -static void gpio_vbus_work(struct work_struct *work) -{ - struct gpio_vbus_data *gpio_vbus = - container_of(work, struct gpio_vbus_data, work.work); - struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; - int gpio, status, vbus; - - if (!gpio_vbus->phy.otg->gadget) - return; - - vbus = is_vbus_powered(pdata); - if ((vbus ^ gpio_vbus->vbus) == 0) - return; - gpio_vbus->vbus = vbus; - - /* Peripheral controllers which manage the pullup themselves won't have - * gpio_pullup configured here. If it's configured here, we'll do what - * isp1301_omap::b_peripheral() does and enable the pullup here... although - * that may complicate usb_gadget_{,dis}connect() support. - */ - gpio = pdata->gpio_pullup; - - if (vbus) { - status = USB_EVENT_VBUS; - gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL; - gpio_vbus->phy.last_event = status; - usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget); - - /* drawing a "unit load" is *always* OK, except for OTG */ - set_vbus_draw(gpio_vbus, 100); - - /* optionally enable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, !pdata->gpio_pullup_inverted); - - atomic_notifier_call_chain(&gpio_vbus->phy.notifier, - status, gpio_vbus->phy.otg->gadget); - } else { - /* optionally disable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, pdata->gpio_pullup_inverted); - - set_vbus_draw(gpio_vbus, 0); - - usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget); - status = USB_EVENT_NONE; - gpio_vbus->phy.state = OTG_STATE_B_IDLE; - gpio_vbus->phy.last_event = status; - - atomic_notifier_call_chain(&gpio_vbus->phy.notifier, - status, gpio_vbus->phy.otg->gadget); - } -} - -/* VBUS change IRQ handler */ -static irqreturn_t gpio_vbus_irq(int irq, void *data) -{ - struct platform_device *pdev = data; - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct usb_otg *otg = gpio_vbus->phy.otg; - - dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", - is_vbus_powered(pdata) ? "supplied" : "inactive", - otg->gadget ? otg->gadget->name : "none"); - - if (otg->gadget) - schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100)); - - return IRQ_HANDLED; -} - -/* OTG transceiver interface */ - -/* bind/unbind the peripheral controller */ -static int gpio_vbus_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct gpio_vbus_data *gpio_vbus; - struct gpio_vbus_mach_info *pdata; - struct platform_device *pdev; - int gpio; - - gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy); - pdev = to_platform_device(gpio_vbus->dev); - pdata = gpio_vbus->dev->platform_data; - gpio = pdata->gpio_pullup; - - if (!gadget) { - dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", - otg->gadget->name); - - /* optionally disable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, pdata->gpio_pullup_inverted); - - set_vbus_draw(gpio_vbus, 0); - - usb_gadget_vbus_disconnect(otg->gadget); - otg->phy->state = OTG_STATE_UNDEFINED; - - otg->gadget = NULL; - return 0; - } - - otg->gadget = gadget; - dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); - - /* initialize connection state */ - gpio_vbus->vbus = 0; /* start with disconnected */ - gpio_vbus_irq(gpio_vbus->irq, pdev); - return 0; -} - -/* effective for B devices, ignored for A-peripheral */ -static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA) -{ - struct gpio_vbus_data *gpio_vbus; - - gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); - - if (phy->state == OTG_STATE_B_PERIPHERAL) - set_vbus_draw(gpio_vbus, mA); - return 0; -} - -/* for non-OTG B devices: set/clear transceiver suspend mode */ -static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend) -{ - struct gpio_vbus_data *gpio_vbus; - - gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); - - /* draw max 0 mA from vbus in suspend mode; or the previously - * recorded amount of current if not suspended - * - * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA - * if they're wake-enabled ... we don't handle that yet. - */ - return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA); -} - -/* platform driver interface */ - -static int __init gpio_vbus_probe(struct platform_device *pdev) -{ - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - struct gpio_vbus_data *gpio_vbus; - struct resource *res; - int err, gpio, irq; - unsigned long irqflags; - - if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) - return -EINVAL; - gpio = pdata->gpio_vbus; - - gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); - if (!gpio_vbus) - return -ENOMEM; - - gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!gpio_vbus->phy.otg) { - kfree(gpio_vbus); - return -ENOMEM; - } - - platform_set_drvdata(pdev, gpio_vbus); - gpio_vbus->dev = &pdev->dev; - gpio_vbus->phy.label = "gpio-vbus"; - gpio_vbus->phy.set_power = gpio_vbus_set_power; - gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend; - gpio_vbus->phy.state = OTG_STATE_UNDEFINED; - - gpio_vbus->phy.otg->phy = &gpio_vbus->phy; - gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; - - err = gpio_request(gpio, "vbus_detect"); - if (err) { - dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", - gpio, err); - goto err_gpio; - } - gpio_direction_input(gpio); - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) { - irq = res->start; - irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; - } else { - irq = gpio_to_irq(gpio); - irqflags = VBUS_IRQ_FLAGS; - } - - gpio_vbus->irq = irq; - - /* if data line pullup is in use, initialize it to "not pulling up" */ - gpio = pdata->gpio_pullup; - if (gpio_is_valid(gpio)) { - err = gpio_request(gpio, "udc_pullup"); - if (err) { - dev_err(&pdev->dev, - "can't request pullup gpio %d, err: %d\n", - gpio, err); - gpio_free(pdata->gpio_vbus); - goto err_gpio; - } - gpio_direction_output(gpio, pdata->gpio_pullup_inverted); - } - - err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); - if (err) { - dev_err(&pdev->dev, "can't request irq %i, err: %d\n", - irq, err); - goto err_irq; - } - - ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); - - INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); - - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); - if (IS_ERR(gpio_vbus->vbus_draw)) { - dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", - PTR_ERR(gpio_vbus->vbus_draw)); - gpio_vbus->vbus_draw = NULL; - } - - /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_otg; - } - - device_init_wakeup(&pdev->dev, pdata->wakeup); - - return 0; -err_otg: - regulator_put(gpio_vbus->vbus_draw); - free_irq(irq, pdev); -err_irq: - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(pdata->gpio_vbus); -err_gpio: - platform_set_drvdata(pdev, NULL); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return err; -} - -static int __exit gpio_vbus_remove(struct platform_device *pdev) -{ - struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - int gpio = pdata->gpio_vbus; - - device_init_wakeup(&pdev->dev, 0); - cancel_delayed_work_sync(&gpio_vbus->work); - regulator_put(gpio_vbus->vbus_draw); - - usb_remove_phy(&gpio_vbus->phy); - - free_irq(gpio_vbus->irq, pdev); - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(gpio); - platform_set_drvdata(pdev, NULL); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - - return 0; -} - -#ifdef CONFIG_PM -static int gpio_vbus_pm_suspend(struct device *dev) -{ - struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); - - if (device_may_wakeup(dev)) - enable_irq_wake(gpio_vbus->irq); - - return 0; -} - -static int gpio_vbus_pm_resume(struct device *dev) -{ - struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); - - if (device_may_wakeup(dev)) - disable_irq_wake(gpio_vbus->irq); - - return 0; -} - -static const struct dev_pm_ops gpio_vbus_dev_pm_ops = { - .suspend = gpio_vbus_pm_suspend, - .resume = gpio_vbus_pm_resume, -}; -#endif - -/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ - -MODULE_ALIAS("platform:gpio-vbus"); - -static struct platform_driver gpio_vbus_driver = { - .driver = { - .name = "gpio-vbus", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &gpio_vbus_dev_pm_ops, -#endif - }, - .remove = __exit_p(gpio_vbus_remove), -}; - -module_platform_driver_probe(gpio_vbus_driver, gpio_vbus_probe); - -MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); -MODULE_AUTHOR("Philipp Zabel"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c deleted file mode 100644 index 8fe0c3b..0000000 --- a/drivers/usb/otg/isp1301_omap.c +++ /dev/null @@ -1,1656 +0,0 @@ -/* - * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller - * - * Copyright (C) 2004 Texas Instruments - * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#ifndef DEBUG -#undef VERBOSE -#endif - - -#define DRIVER_VERSION "24 August 2004" -#define DRIVER_NAME (isp1301_driver.driver.name) - -MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); -MODULE_LICENSE("GPL"); - -struct isp1301 { - struct usb_phy phy; - struct i2c_client *client; - void (*i2c_release)(struct device *dev); - - int irq_type; - - u32 last_otg_ctrl; - unsigned working:1; - - struct timer_list timer; - - /* use keventd context to change the state for us */ - struct work_struct work; - - unsigned long todo; -# define WORK_UPDATE_ISP 0 /* update ISP from OTG */ -# define WORK_UPDATE_OTG 1 /* update OTG from ISP */ -# define WORK_HOST_RESUME 4 /* resume host */ -# define WORK_TIMER 6 /* timer fired */ -# define WORK_STOP 7 /* don't resubmit */ -}; - - -/* bits in OTG_CTRL */ - -#define OTG_XCEIV_OUTPUTS \ - (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) -#define OTG_XCEIV_INPUTS \ - (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) -#define OTG_CTRL_BITS \ - (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP) - /* and OTG_PULLUP is sometimes written */ - -#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \ - OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \ - OTG_CTRL_BITS) - - -/*-------------------------------------------------------------------------*/ - -/* board-specific PM hooks */ - -#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) - -#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) - -#include - -#else - -static inline int tps65010_set_vbus_draw(unsigned mA) -{ - pr_debug("tps65010: draw %d mA (STUB)\n", mA); - return 0; -} - -#endif - -static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) -{ - int status = tps65010_set_vbus_draw(mA); - if (status < 0) - pr_debug(" VBUS %d mA error %d\n", mA, status); -} - -#else - -static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) -{ - /* H4 controls this by DIP switch S2.4; no soft control. - * ON means the charger is always enabled. Leave it OFF - * unless the OTG port is used only in B-peripheral mode. - */ -} - -#endif - -static void enable_vbus_source(struct isp1301 *isp) -{ - /* this board won't supply more than 8mA vbus power. - * some boards can switch a 100ma "unit load" (or more). - */ -} - - -/* products will deliver OTG messages with LEDs, GUI, etc */ -static inline void notresponding(struct isp1301 *isp) -{ - printk(KERN_NOTICE "OTG device not responding.\n"); -} - - -/*-------------------------------------------------------------------------*/ - -static struct i2c_driver isp1301_driver; - -/* smbus apis are used for portability */ - -static inline u8 -isp1301_get_u8(struct isp1301 *isp, u8 reg) -{ - return i2c_smbus_read_byte_data(isp->client, reg + 0); -} - -static inline int -isp1301_get_u16(struct isp1301 *isp, u8 reg) -{ - return i2c_smbus_read_word_data(isp->client, reg); -} - -static inline int -isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits) -{ - return i2c_smbus_write_byte_data(isp->client, reg + 0, bits); -} - -static inline int -isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) -{ - return i2c_smbus_write_byte_data(isp->client, reg + 1, bits); -} - -/*-------------------------------------------------------------------------*/ - -/* identification */ -#define ISP1301_VENDOR_ID 0x00 /* u16 read */ -#define ISP1301_PRODUCT_ID 0x02 /* u16 read */ -#define ISP1301_BCD_DEVICE 0x14 /* u16 read */ - -#define I2C_VENDOR_ID_PHILIPS 0x04cc -#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301 - -/* operational registers */ -#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */ -# define MC1_SPEED (1 << 0) -# define MC1_SUSPEND (1 << 1) -# define MC1_DAT_SE0 (1 << 2) -# define MC1_TRANSPARENT (1 << 3) -# define MC1_BDIS_ACON_EN (1 << 4) -# define MC1_OE_INT_EN (1 << 5) -# define MC1_UART_EN (1 << 6) -# define MC1_MASK 0x7f -#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */ -# define MC2_GLOBAL_PWR_DN (1 << 0) -# define MC2_SPD_SUSP_CTRL (1 << 1) -# define MC2_BI_DI (1 << 2) -# define MC2_TRANSP_BDIR0 (1 << 3) -# define MC2_TRANSP_BDIR1 (1 << 4) -# define MC2_AUDIO_EN (1 << 5) -# define MC2_PSW_EN (1 << 6) -# define MC2_EN2V7 (1 << 7) -#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */ -# define OTG1_DP_PULLUP (1 << 0) -# define OTG1_DM_PULLUP (1 << 1) -# define OTG1_DP_PULLDOWN (1 << 2) -# define OTG1_DM_PULLDOWN (1 << 3) -# define OTG1_ID_PULLDOWN (1 << 4) -# define OTG1_VBUS_DRV (1 << 5) -# define OTG1_VBUS_DISCHRG (1 << 6) -# define OTG1_VBUS_CHRG (1 << 7) -#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */ -# define OTG_B_SESS_END (1 << 6) -# define OTG_B_SESS_VLD (1 << 7) - -#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */ -#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */ - -#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */ -#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */ - -/* same bitfields in all interrupt registers */ -# define INTR_VBUS_VLD (1 << 0) -# define INTR_SESS_VLD (1 << 1) -# define INTR_DP_HI (1 << 2) -# define INTR_ID_GND (1 << 3) -# define INTR_DM_HI (1 << 4) -# define INTR_ID_FLOAT (1 << 5) -# define INTR_BDIS_ACON (1 << 6) -# define INTR_CR_INT (1 << 7) - -/*-------------------------------------------------------------------------*/ - -static inline const char *state_name(struct isp1301 *isp) -{ - return usb_otg_state_string(isp->phy.state); -} - -/*-------------------------------------------------------------------------*/ - -/* NOTE: some of this ISP1301 setup is specific to H2 boards; - * not everything is guarded by board-specific checks, or even using - * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. - * - * ALSO: this currently doesn't use ISP1301 low-power modes - * while OTG is running. - */ - -static void power_down(struct isp1301 *isp) -{ - isp->phy.state = OTG_STATE_UNDEFINED; - - // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); - - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); -} - -static void power_up(struct isp1301 *isp) -{ - // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); - - /* do this only when cpu is driving transceiver, - * so host won't see a low speed device... - */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); -} - -#define NO_HOST_SUSPEND - -static int host_suspend(struct isp1301 *isp) -{ -#ifdef NO_HOST_SUSPEND - return 0; -#else - struct device *dev; - - if (!isp->phy.otg->host) - return -ENODEV; - - /* Currently ASSUMES only the OTG port matters; - * other ports could be active... - */ - dev = isp->phy.otg->host->controller; - return dev->driver->suspend(dev, 3, 0); -#endif -} - -static int host_resume(struct isp1301 *isp) -{ -#ifdef NO_HOST_SUSPEND - return 0; -#else - struct device *dev; - - if (!isp->phy.otg->host) - return -ENODEV; - - dev = isp->phy.otg->host->controller; - return dev->driver->resume(dev, 0); -#endif -} - -static int gadget_suspend(struct isp1301 *isp) -{ - isp->phy.otg->gadget->b_hnp_enable = 0; - isp->phy.otg->gadget->a_hnp_support = 0; - isp->phy.otg->gadget->a_alt_hnp_support = 0; - return usb_gadget_vbus_disconnect(isp->phy.otg->gadget); -} - -/*-------------------------------------------------------------------------*/ - -#define TIMER_MINUTES 10 -#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ) - -/* Almost all our I2C messaging comes from a work queue's task context. - * NOTE: guaranteeing certain response times might mean we shouldn't - * share keventd's work queue; a realtime task might be safest. - */ -static void isp1301_defer_work(struct isp1301 *isp, int work) -{ - int status; - - if (isp && !test_and_set_bit(work, &isp->todo)) { - (void) get_device(&isp->client->dev); - status = schedule_work(&isp->work); - if (!status && !isp->working) - dev_vdbg(&isp->client->dev, - "work item %d may be lost\n", work); - } -} - -/* called from irq handlers */ -static void a_idle(struct isp1301 *isp, const char *tag) -{ - u32 l; - - if (isp->phy.state == OTG_STATE_A_IDLE) - return; - - isp->phy.otg->default_a = 1; - if (isp->phy.otg->host) { - isp->phy.otg->host->is_b_host = 0; - host_suspend(isp); - } - if (isp->phy.otg->gadget) { - isp->phy.otg->gadget->is_a_peripheral = 1; - gadget_suspend(isp); - } - isp->phy.state = OTG_STATE_A_IDLE; - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - isp->last_otg_ctrl = l; - pr_debug(" --> %s/%s\n", state_name(isp), tag); -} - -/* called from irq handlers */ -static void b_idle(struct isp1301 *isp, const char *tag) -{ - u32 l; - - if (isp->phy.state == OTG_STATE_B_IDLE) - return; - - isp->phy.otg->default_a = 0; - if (isp->phy.otg->host) { - isp->phy.otg->host->is_b_host = 1; - host_suspend(isp); - } - if (isp->phy.otg->gadget) { - isp->phy.otg->gadget->is_a_peripheral = 0; - gadget_suspend(isp); - } - isp->phy.state = OTG_STATE_B_IDLE; - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - isp->last_otg_ctrl = l; - pr_debug(" --> %s/%s\n", state_name(isp), tag); -} - -static void -dump_regs(struct isp1301 *isp, const char *label) -{ -#ifdef DEBUG - u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); - u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); - - pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", - omap_readl(OTG_CTRL), label, state_name(isp), - ctrl, status, src); - /* mode control and irq enables don't change much */ -#endif -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_OTG - -/* - * The OMAP OTG controller handles most of the OTG state transitions. - * - * We translate isp1301 outputs (mostly voltage comparator status) into - * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state - * flags into isp1301 inputs ... and infer state transitions. - */ - -#ifdef VERBOSE - -static void check_state(struct isp1301 *isp, const char *tag) -{ - enum usb_otg_state state = OTG_STATE_UNDEFINED; - u8 fsm = omap_readw(OTG_TEST) & 0x0ff; - unsigned extra = 0; - - switch (fsm) { - - /* default-b */ - case 0x0: - state = OTG_STATE_B_IDLE; - break; - case 0x3: - case 0x7: - extra = 1; - case 0x1: - state = OTG_STATE_B_PERIPHERAL; - break; - case 0x11: - state = OTG_STATE_B_SRP_INIT; - break; - - /* extra dual-role default-b states */ - case 0x12: - case 0x13: - case 0x16: - extra = 1; - case 0x17: - state = OTG_STATE_B_WAIT_ACON; - break; - case 0x34: - state = OTG_STATE_B_HOST; - break; - - /* default-a */ - case 0x36: - state = OTG_STATE_A_IDLE; - break; - case 0x3c: - state = OTG_STATE_A_WAIT_VFALL; - break; - case 0x7d: - state = OTG_STATE_A_VBUS_ERR; - break; - case 0x9e: - case 0x9f: - extra = 1; - case 0x89: - state = OTG_STATE_A_PERIPHERAL; - break; - case 0xb7: - state = OTG_STATE_A_WAIT_VRISE; - break; - case 0xb8: - state = OTG_STATE_A_WAIT_BCON; - break; - case 0xb9: - state = OTG_STATE_A_HOST; - break; - case 0xba: - state = OTG_STATE_A_SUSPEND; - break; - default: - break; - } - if (isp->phy.state == state && !extra) - return; - pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - usb_otg_state_string(state), fsm, state_name(isp), - omap_readl(OTG_CTRL)); -} - -#else - -static inline void check_state(struct isp1301 *isp, const char *tag) { } - -#endif - -/* outputs from ISP1301_INTERRUPT_SOURCE */ -static void update_otg1(struct isp1301 *isp, u8 int_src) -{ - u32 otg_ctrl; - - otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - otg_ctrl &= ~OTG_XCEIV_INPUTS; - otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); - - if (int_src & INTR_SESS_VLD) - otg_ctrl |= OTG_ASESSVLD; - else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) { - a_idle(isp, "vfall"); - otg_ctrl &= ~OTG_CTRL_BITS; - } - if (int_src & INTR_VBUS_VLD) - otg_ctrl |= OTG_VBUSVLD; - if (int_src & INTR_ID_GND) { /* default-A */ - if (isp->phy.state == OTG_STATE_B_IDLE - || isp->phy.state - == OTG_STATE_UNDEFINED) { - a_idle(isp, "init"); - return; - } - } else { /* default-B */ - otg_ctrl |= OTG_ID; - if (isp->phy.state == OTG_STATE_A_IDLE - || isp->phy.state == OTG_STATE_UNDEFINED) { - b_idle(isp, "init"); - return; - } - } - omap_writel(otg_ctrl, OTG_CTRL); -} - -/* outputs from ISP1301_OTG_STATUS */ -static void update_otg2(struct isp1301 *isp, u8 otg_status) -{ - u32 otg_ctrl; - - otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - otg_ctrl &= ~OTG_XCEIV_INPUTS; - otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND); - if (otg_status & OTG_B_SESS_VLD) - otg_ctrl |= OTG_BSESSVLD; - else if (otg_status & OTG_B_SESS_END) - otg_ctrl |= OTG_BSESSEND; - omap_writel(otg_ctrl, OTG_CTRL); -} - -/* inputs going to ISP1301 */ -static void otg_update_isp(struct isp1301 *isp) -{ - u32 otg_ctrl, otg_change; - u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; - - otg_ctrl = omap_readl(OTG_CTRL); - otg_change = otg_ctrl ^ isp->last_otg_ctrl; - isp->last_otg_ctrl = otg_ctrl; - otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; - - switch (isp->phy.state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_SRP_INIT: - if (!(otg_ctrl & OTG_PULLUP)) { - // if (otg_ctrl & OTG_B_HNPEN) { - if (isp->phy.otg->gadget->b_hnp_enable) { - isp->phy.state = OTG_STATE_B_WAIT_ACON; - pr_debug(" --> b_wait_acon\n"); - } - goto pulldown; - } -pullup: - set |= OTG1_DP_PULLUP; - clr |= OTG1_DP_PULLDOWN; - break; - case OTG_STATE_A_SUSPEND: - case OTG_STATE_A_PERIPHERAL: - if (otg_ctrl & OTG_PULLUP) - goto pullup; - /* FALLTHROUGH */ - // case OTG_STATE_B_WAIT_ACON: - default: -pulldown: - set |= OTG1_DP_PULLDOWN; - clr |= OTG1_DP_PULLUP; - break; - } - -# define toggle(OTG,ISP) do { \ - if (otg_ctrl & OTG) set |= ISP; \ - else clr |= ISP; \ - } while (0) - - if (!(isp->phy.otg->host)) - otg_ctrl &= ~OTG_DRV_VBUS; - - switch (isp->phy.state) { - case OTG_STATE_A_SUSPEND: - if (otg_ctrl & OTG_DRV_VBUS) { - set |= OTG1_VBUS_DRV; - break; - } - /* HNP failed for some reason (A_AIDL_BDIS timeout) */ - notresponding(isp); - - /* FALLTHROUGH */ - case OTG_STATE_A_VBUS_ERR: - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - pr_debug(" --> a_wait_vfall\n"); - /* FALLTHROUGH */ - case OTG_STATE_A_WAIT_VFALL: - /* FIXME usbcore thinks port power is still on ... */ - clr |= OTG1_VBUS_DRV; - break; - case OTG_STATE_A_IDLE: - if (otg_ctrl & OTG_DRV_VBUS) { - isp->phy.state = OTG_STATE_A_WAIT_VRISE; - pr_debug(" --> a_wait_vrise\n"); - } - /* FALLTHROUGH */ - default: - toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); - } - - toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); - toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); - -# undef toggle - - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); - - /* HNP switch to host or peripheral; and SRP */ - if (otg_change & OTG_PULLUP) { - u32 l; - - switch (isp->phy.state) { - case OTG_STATE_B_IDLE: - if (clr & OTG1_DP_PULLUP) - break; - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - break; - case OTG_STATE_A_SUSPEND: - if (clr & OTG1_DP_PULLUP) - break; - isp->phy.state = OTG_STATE_A_PERIPHERAL; - pr_debug(" --> a_peripheral\n"); - break; - default: - break; - } - l = omap_readl(OTG_CTRL); - l |= OTG_PULLUP; - omap_writel(l, OTG_CTRL); - } - - check_state(isp, __func__); - dump_regs(isp, "otg->isp1301"); -} - -static irqreturn_t omap_otg_irq(int irq, void *_isp) -{ - u16 otg_irq = omap_readw(OTG_IRQ_SRC); - u32 otg_ctrl; - int ret = IRQ_NONE; - struct isp1301 *isp = _isp; - struct usb_otg *otg = isp->phy.otg; - - /* update ISP1301 transceiver from OTG controller */ - if (otg_irq & OPRT_CHG) { - omap_writew(OPRT_CHG, OTG_IRQ_SRC); - isp1301_defer_work(isp, WORK_UPDATE_ISP); - ret = IRQ_HANDLED; - - /* SRP to become b_peripheral failed */ - } else if (otg_irq & B_SRP_TMROUT) { - pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL)); - notresponding(isp); - - /* gadget drivers that care should monitor all kinds of - * remote wakeup (SRP, normal) using their own timer - * to give "check cable and A-device" messages. - */ - if (isp->phy.state == OTG_STATE_B_SRP_INIT) - b_idle(isp, "srp_timeout"); - - omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* HNP to become b_host failed */ - } else if (otg_irq & B_HNP_FAIL) { - pr_debug("otg: %s B_HNP_FAIL, %06x\n", - state_name(isp), omap_readl(OTG_CTRL)); - notresponding(isp); - - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - - /* subset of b_peripheral()... */ - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - - omap_writew(B_HNP_FAIL, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* detect SRP from B-device ... */ - } else if (otg_irq & A_SRP_DETECT) { - pr_debug("otg: %s SRP_DETECT, %06x\n", - state_name(isp), omap_readl(OTG_CTRL)); - - isp1301_defer_work(isp, WORK_UPDATE_OTG); - switch (isp->phy.state) { - case OTG_STATE_A_IDLE: - if (!otg->host) - break; - isp1301_defer_work(isp, WORK_HOST_RESUME); - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_A_BUSREQ; - otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) - & ~OTG_XCEIV_INPUTS - & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - break; - default: - break; - } - - omap_writew(A_SRP_DETECT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) - * we don't track them separately - */ - } else if (otg_irq & A_REQ_TMROUT) { - otg_ctrl = omap_readl(OTG_CTRL); - pr_info("otg: BCON_TMOUT from %s, %06x\n", - state_name(isp), otg_ctrl); - notresponding(isp); - - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - - omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* A-supplied voltage fell too low; overcurrent */ - } else if (otg_irq & A_VBUS_ERR) { - otg_ctrl = omap_readl(OTG_CTRL); - printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", - state_name(isp), otg_irq, otg_ctrl); - - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_A_VBUS_ERR; - - omap_writew(A_VBUS_ERR, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* switch driver; the transceiver code activates it, - * ungating the udc clock or resuming OHCI. - */ - } else if (otg_irq & DRIVER_SWITCH) { - int kick = 0; - - otg_ctrl = omap_readl(OTG_CTRL); - printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", - state_name(isp), - (otg_ctrl & OTG_DRIVER_SEL) - ? "gadget" : "host", - otg_ctrl); - isp1301_defer_work(isp, WORK_UPDATE_ISP); - - /* role is peripheral */ - if (otg_ctrl & OTG_DRIVER_SEL) { - switch (isp->phy.state) { - case OTG_STATE_A_IDLE: - b_idle(isp, __func__); - break; - default: - break; - } - isp1301_defer_work(isp, WORK_UPDATE_ISP); - - /* role is host */ - } else { - if (!(otg_ctrl & OTG_ID)) { - otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL); - } - - if (otg->host) { - switch (isp->phy.state) { - case OTG_STATE_B_WAIT_ACON: - isp->phy.state = OTG_STATE_B_HOST; - pr_debug(" --> b_host\n"); - kick = 1; - break; - case OTG_STATE_A_WAIT_BCON: - isp->phy.state = OTG_STATE_A_HOST; - pr_debug(" --> a_host\n"); - break; - case OTG_STATE_A_PERIPHERAL: - isp->phy.state = OTG_STATE_A_WAIT_BCON; - pr_debug(" --> a_wait_bcon\n"); - break; - default: - break; - } - isp1301_defer_work(isp, WORK_HOST_RESUME); - } - } - - omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - if (kick) - usb_bus_start_enum(otg->host, otg->host->otg_port); - } - - check_state(isp, __func__); - return ret; -} - -static struct platform_device *otg_dev; - -static int isp1301_otg_init(struct isp1301 *isp) -{ - u32 l; - - if (!otg_dev) - return -ENODEV; - - dump_regs(isp, __func__); - /* some of these values are board-specific... */ - l = omap_readl(OTG_SYSCON_2); - l |= OTG_EN - /* for B-device: */ - | SRP_GPDATA /* 9msec Bdev D+ pulse */ - | SRP_GPDVBUS /* discharge after VBUS pulse */ - // | (3 << 24) /* 2msec VBUS pulse */ - /* for A-device: */ - | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ - | SRP_DPW /* detect 167+ns SRP pulses */ - | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ - ; - omap_writel(l, OTG_SYSCON_2); - - update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); - update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); - - check_state(isp, __func__); - pr_debug("otg: %s, %s %06x\n", - state_name(isp), __func__, omap_readl(OTG_CTRL)); - - omap_writew(DRIVER_SWITCH | OPRT_CHG - | B_SRP_TMROUT | B_HNP_FAIL - | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN); - - l = omap_readl(OTG_SYSCON_2); - l |= OTG_EN; - omap_writel(l, OTG_SYSCON_2); - - return 0; -} - -static int otg_probe(struct platform_device *dev) -{ - // struct omap_usb_config *config = dev->platform_data; - - otg_dev = dev; - return 0; -} - -static int otg_remove(struct platform_device *dev) -{ - otg_dev = NULL; - return 0; -} - -static struct platform_driver omap_otg_driver = { - .probe = otg_probe, - .remove = otg_remove, - .driver = { - .owner = THIS_MODULE, - .name = "omap_otg", - }, -}; - -static int otg_bind(struct isp1301 *isp) -{ - int status; - - if (otg_dev) - return -EBUSY; - - status = platform_driver_register(&omap_otg_driver); - if (status < 0) - return status; - - if (otg_dev) - status = request_irq(otg_dev->resource[1].start, omap_otg_irq, - 0, DRIVER_NAME, isp); - else - status = -ENODEV; - - if (status < 0) - platform_driver_unregister(&omap_otg_driver); - return status; -} - -static void otg_unbind(struct isp1301 *isp) -{ - if (!otg_dev) - return; - free_irq(otg_dev->resource[1].start, isp); -} - -#else - -/* OTG controller isn't clocked */ - -#endif /* CONFIG_USB_OTG */ - -/*-------------------------------------------------------------------------*/ - -static void b_peripheral(struct isp1301 *isp) -{ - u32 l; - - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - - usb_gadget_vbus_connect(isp->phy.otg->gadget); - -#ifdef CONFIG_USB_OTG - enable_vbus_draw(isp, 8); - otg_update_isp(isp); -#else - enable_vbus_draw(isp, 100); - /* UDC driver just set OTG_BSESSVLD */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - dump_regs(isp, "2periph"); -#endif -} - -static void isp_update_otg(struct isp1301 *isp, u8 stat) -{ - struct usb_otg *otg = isp->phy.otg; - u8 isp_stat, isp_bstat; - enum usb_otg_state state = isp->phy.state; - - if (stat & INTR_BDIS_ACON) - pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); - - /* start certain state transitions right away */ - isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); - if (isp_stat & INTR_ID_GND) { - if (otg->default_a) { - switch (state) { - case OTG_STATE_B_IDLE: - a_idle(isp, "idle"); - /* FALLTHROUGH */ - case OTG_STATE_A_IDLE: - enable_vbus_source(isp); - /* FALLTHROUGH */ - case OTG_STATE_A_WAIT_VRISE: - /* we skip over OTG_STATE_A_WAIT_BCON, since - * the HC will transition to A_HOST (or - * A_SUSPEND!) without our noticing except - * when HNP is used. - */ - if (isp_stat & INTR_VBUS_VLD) - isp->phy.state = OTG_STATE_A_HOST; - break; - case OTG_STATE_A_WAIT_VFALL: - if (!(isp_stat & INTR_SESS_VLD)) - a_idle(isp, "vfell"); - break; - default: - if (!(isp_stat & INTR_VBUS_VLD)) - isp->phy.state = OTG_STATE_A_VBUS_ERR; - break; - } - isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - } else { - switch (state) { - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_HOST: - case OTG_STATE_B_WAIT_ACON: - usb_gadget_vbus_disconnect(otg->gadget); - break; - default: - break; - } - if (state != OTG_STATE_A_IDLE) - a_idle(isp, "id"); - if (otg->host && state == OTG_STATE_A_IDLE) - isp1301_defer_work(isp, WORK_HOST_RESUME); - isp_bstat = 0; - } - } else { - u32 l; - - /* if user unplugged mini-A end of cable, - * don't bypass A_WAIT_VFALL. - */ - if (otg->default_a) { - switch (state) { - default: - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_A_WAIT_VFALL: - state = OTG_STATE_A_IDLE; - /* khubd may take a while to notice and - * handle this disconnect, so don't go - * to B_IDLE quite yet. - */ - break; - case OTG_STATE_A_IDLE: - host_suspend(isp); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_BDIS_ACON_EN); - isp->phy.state = OTG_STATE_B_IDLE; - l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - l &= ~OTG_CTRL_BITS; - omap_writel(l, OTG_CTRL); - break; - case OTG_STATE_B_IDLE: - break; - } - } - isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - - switch (isp->phy.state) { - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_WAIT_ACON: - case OTG_STATE_B_HOST: - if (likely(isp_bstat & OTG_B_SESS_VLD)) - break; - enable_vbus_draw(isp, 0); -#ifndef CONFIG_USB_OTG - /* UDC driver will clear OTG_BSESSVLD */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DP_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DP_PULLUP); - dump_regs(isp, __func__); -#endif - /* FALLTHROUGH */ - case OTG_STATE_B_SRP_INIT: - b_idle(isp, __func__); - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - /* FALLTHROUGH */ - case OTG_STATE_B_IDLE: - if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) { -#ifdef CONFIG_USB_OTG - update_otg1(isp, isp_stat); - update_otg2(isp, isp_bstat); -#endif - b_peripheral(isp); - } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) - isp_bstat |= OTG_B_SESS_END; - break; - case OTG_STATE_A_WAIT_VFALL: - break; - default: - pr_debug("otg: unsupported b-device %s\n", - state_name(isp)); - break; - } - } - - if (state != isp->phy.state) - pr_debug(" isp, %s -> %s\n", - usb_otg_state_string(state), state_name(isp)); - -#ifdef CONFIG_USB_OTG - /* update the OTG controller state to match the isp1301; may - * trigger OPRT_CHG irqs for changes going to the isp1301. - */ - update_otg1(isp, isp_stat); - update_otg2(isp, isp_bstat); - check_state(isp, __func__); -#endif - - dump_regs(isp, "isp1301->otg"); -} - -/*-------------------------------------------------------------------------*/ - -static u8 isp1301_clear_latch(struct isp1301 *isp) -{ - u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); - return latch; -} - -static void -isp1301_work(struct work_struct *work) -{ - struct isp1301 *isp = container_of(work, struct isp1301, work); - int stop; - - /* implicit lock: we're the only task using this device */ - isp->working = 1; - do { - stop = test_bit(WORK_STOP, &isp->todo); - -#ifdef CONFIG_USB_OTG - /* transfer state from otg engine to isp1301 */ - if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { - otg_update_isp(isp); - put_device(&isp->client->dev); - } -#endif - /* transfer state from isp1301 to otg engine */ - if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { - u8 stat = isp1301_clear_latch(isp); - - isp_update_otg(isp, stat); - put_device(&isp->client->dev); - } - - if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { - u32 otg_ctrl; - - /* - * skip A_WAIT_VRISE; hc transitions invisibly - * skip A_WAIT_BCON; same. - */ - switch (isp->phy.state) { - case OTG_STATE_A_WAIT_BCON: - case OTG_STATE_A_WAIT_VRISE: - isp->phy.state = OTG_STATE_A_HOST; - pr_debug(" --> a_host\n"); - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_A_BUSREQ; - otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) - & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - break; - case OTG_STATE_B_WAIT_ACON: - isp->phy.state = OTG_STATE_B_HOST; - pr_debug(" --> b_host (acon)\n"); - break; - case OTG_STATE_B_HOST: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_IDLE: - break; - default: - pr_debug(" host resume in %s\n", - state_name(isp)); - } - host_resume(isp); - // mdelay(10); - put_device(&isp->client->dev); - } - - if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { -#ifdef VERBOSE - dump_regs(isp, "timer"); - if (!stop) - mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); -#endif - put_device(&isp->client->dev); - } - - if (isp->todo) - dev_vdbg(&isp->client->dev, - "work done, todo = 0x%lx\n", - isp->todo); - if (stop) { - dev_dbg(&isp->client->dev, "stop\n"); - break; - } - } while (isp->todo); - isp->working = 0; -} - -static irqreturn_t isp1301_irq(int irq, void *isp) -{ - isp1301_defer_work(isp, WORK_UPDATE_OTG); - return IRQ_HANDLED; -} - -static void isp1301_timer(unsigned long _isp) -{ - isp1301_defer_work((void *)_isp, WORK_TIMER); -} - -/*-------------------------------------------------------------------------*/ - -static void isp1301_release(struct device *dev) -{ - struct isp1301 *isp; - - isp = dev_get_drvdata(dev); - - /* FIXME -- not with a "new style" driver, it doesn't!! */ - - /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ - if (isp->i2c_release) - isp->i2c_release(dev); - kfree(isp->phy.otg); - kfree (isp); -} - -static struct isp1301 *the_transceiver; - -static int __exit isp1301_remove(struct i2c_client *i2c) -{ - struct isp1301 *isp; - - isp = i2c_get_clientdata(i2c); - - isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); - free_irq(i2c->irq, isp); -#ifdef CONFIG_USB_OTG - otg_unbind(isp); -#endif - if (machine_is_omap_h2()) - gpio_free(2); - - isp->timer.data = 0; - set_bit(WORK_STOP, &isp->todo); - del_timer_sync(&isp->timer); - flush_work(&isp->work); - - put_device(&i2c->dev); - the_transceiver = NULL; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* NOTE: three modes are possible here, only one of which - * will be standards-conformant on any given system: - * - * - OTG mode (dual-role), required if there's a Mini-AB connector - * - HOST mode, for when there's one or more A (host) connectors - * - DEVICE mode, for when there's a B/Mini-B (device) connector - * - * As a rule, you won't have an isp1301 chip unless it's there to - * support the OTG mode. Other modes help testing USB controllers - * in isolation from (full) OTG support, or maybe so later board - * revisions can help to support those feature. - */ - -#ifdef CONFIG_USB_OTG - -static int isp1301_otg_enable(struct isp1301 *isp) -{ - power_up(isp); - isp1301_otg_init(isp); - - /* NOTE: since we don't change this, this provides - * a few more interrupts than are strictly needed. - */ - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); - - dev_info(&isp->client->dev, "ready for dual-role USB ...\n"); - - return 0; -} - -#endif - -/* add or disable the host device+driver */ -static int -isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - - if (!otg || isp != the_transceiver) - return -ENODEV; - - if (!host) { - omap_writew(0, OTG_IRQ_EN); - power_down(isp); - otg->host = NULL; - return 0; - } - -#ifdef CONFIG_USB_OTG - otg->host = host; - dev_dbg(&isp->client->dev, "registered host\n"); - host_suspend(isp); - if (otg->gadget) - return isp1301_otg_enable(isp); - return 0; - -#elif !defined(CONFIG_USB_GADGET_OMAP) - // FIXME update its refcount - otg->host = host; - - power_up(isp); - - if (machine_is_omap_h2()) - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); - - dev_info(&isp->client->dev, "A-Host sessions ok\n"); - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_ID_GND); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_ID_GND); - - /* If this has a Mini-AB connector, this mode is highly - * nonstandard ... but can be handy for testing, especially with - * the Mini-A end of an OTG cable. (Or something nonstandard - * like MiniB-to-StandardB, maybe built with a gender mender.) - */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); - - dump_regs(isp, __func__); - - return 0; - -#else - dev_dbg(&isp->client->dev, "host sessions not allowed\n"); - return -EINVAL; -#endif - -} - -static int -isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - - if (!otg || isp != the_transceiver) - return -ENODEV; - - if (!gadget) { - omap_writew(0, OTG_IRQ_EN); - if (!otg->default_a) - enable_vbus_draw(isp, 0); - usb_gadget_vbus_disconnect(otg->gadget); - otg->gadget = NULL; - power_down(isp); - return 0; - } - -#ifdef CONFIG_USB_OTG - otg->gadget = gadget; - dev_dbg(&isp->client->dev, "registered gadget\n"); - /* gadget driver may be suspended until vbus_connect () */ - if (otg->host) - return isp1301_otg_enable(isp); - return 0; - -#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) - otg->gadget = gadget; - // FIXME update its refcount - - { - u32 l; - - l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); - l |= OTG_ID; - omap_writel(l, OTG_CTRL); - } - - power_up(isp); - isp->phy.state = OTG_STATE_B_IDLE; - - if (machine_is_omap_h2() || machine_is_omap_h3()) - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); - - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_SESS_VLD); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_VBUS_VLD); - dev_info(&isp->client->dev, "B-Peripheral sessions ok\n"); - dump_regs(isp, __func__); - - /* If this has a Mini-AB connector, this mode is highly - * nonstandard ... but can be handy for testing, so long - * as you don't plug a Mini-A cable into the jack. - */ - if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) - b_peripheral(isp); - - return 0; - -#else - dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n"); - return -EINVAL; -#endif -} - - -/*-------------------------------------------------------------------------*/ - -static int -isp1301_set_power(struct usb_phy *dev, unsigned mA) -{ - if (!the_transceiver) - return -ENODEV; - if (dev->state == OTG_STATE_B_PERIPHERAL) - enable_vbus_draw(the_transceiver, mA); - return 0; -} - -static int -isp1301_start_srp(struct usb_otg *otg) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - u32 otg_ctrl; - - if (!otg || isp != the_transceiver - || isp->phy.state != OTG_STATE_B_IDLE) - return -ENODEV; - - otg_ctrl = omap_readl(OTG_CTRL); - if (!(otg_ctrl & OTG_BSESSEND)) - return -EINVAL; - - otg_ctrl |= OTG_B_BUSREQ; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_B_SRP_INIT; - - pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), - omap_readl(OTG_CTRL)); -#ifdef CONFIG_USB_OTG - check_state(isp, __func__); -#endif - return 0; -} - -static int -isp1301_start_hnp(struct usb_otg *otg) -{ -#ifdef CONFIG_USB_OTG - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - u32 l; - - if (!otg || isp != the_transceiver) - return -ENODEV; - if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) - return -ENOTCONN; - if (!otg->default_a && (otg->gadget == NULL - || !otg->gadget->b_hnp_enable)) - return -ENOTCONN; - - /* We want hardware to manage most HNP protocol timings. - * So do this part as early as possible... - */ - switch (isp->phy.state) { - case OTG_STATE_B_HOST: - isp->phy.state = OTG_STATE_B_PERIPHERAL; - /* caller will suspend next */ - break; - case OTG_STATE_A_HOST: -#if 0 - /* autoconnect mode avoids irq latency bugs */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_BDIS_ACON_EN); -#endif - /* caller must suspend then clear A_BUSREQ */ - usb_gadget_vbus_connect(otg->gadget); - l = omap_readl(OTG_CTRL); - l |= OTG_A_SETB_HNPEN; - omap_writel(l, OTG_CTRL); - - break; - case OTG_STATE_A_PERIPHERAL: - /* initiated by B-Host suspend */ - break; - default: - return -EILSEQ; - } - pr_debug("otg: HNP %s, %06x ...\n", - state_name(isp), omap_readl(OTG_CTRL)); - check_state(isp, __func__); - return 0; -#else - /* srp-only */ - return -EINVAL; -#endif -} - -/*-------------------------------------------------------------------------*/ - -static int -isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) -{ - int status; - struct isp1301 *isp; - - if (the_transceiver) - return 0; - - isp = kzalloc(sizeof *isp, GFP_KERNEL); - if (!isp) - return 0; - - isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL); - if (!isp->phy.otg) { - kfree(isp); - return 0; - } - - INIT_WORK(&isp->work, isp1301_work); - init_timer(&isp->timer); - isp->timer.function = isp1301_timer; - isp->timer.data = (unsigned long) isp; - - i2c_set_clientdata(i2c, isp); - isp->client = i2c; - - /* verify the chip (shouldn't be necessary) */ - status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); - if (status != I2C_VENDOR_ID_PHILIPS) { - dev_dbg(&i2c->dev, "not philips id: %d\n", status); - goto fail; - } - status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); - if (status != I2C_PRODUCT_ID_PHILIPS_1301) { - dev_dbg(&i2c->dev, "not isp1301, %d\n", status); - goto fail; - } - isp->i2c_release = i2c->dev.release; - i2c->dev.release = isp1301_release; - - /* initial development used chiprev 2.00 */ - status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); - dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", - status >> 8, status & 0xff); - - /* make like power-on reset */ - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); - - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); - - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, - ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); - - isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); - -#ifdef CONFIG_USB_OTG - status = otg_bind(isp); - if (status < 0) { - dev_dbg(&i2c->dev, "can't bind OTG\n"); - goto fail; - } -#endif - - if (machine_is_omap_h2()) { - /* full speed signaling by default */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_SPEED); - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, - MC2_SPD_SUSP_CTRL); - - /* IRQ wired at M14 */ - omap_cfg_reg(M14_1510_GPIO2); - if (gpio_request(2, "isp1301") == 0) - gpio_direction_input(2); - isp->irq_type = IRQF_TRIGGER_FALLING; - } - - status = request_irq(i2c->irq, isp1301_irq, - isp->irq_type, DRIVER_NAME, isp); - if (status < 0) { - dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", - i2c->irq, status); - goto fail; - } - - isp->phy.dev = &i2c->dev; - isp->phy.label = DRIVER_NAME; - isp->phy.set_power = isp1301_set_power, - - isp->phy.otg->phy = &isp->phy; - isp->phy.otg->set_host = isp1301_set_host, - isp->phy.otg->set_peripheral = isp1301_set_peripheral, - isp->phy.otg->start_srp = isp1301_start_srp, - isp->phy.otg->start_hnp = isp1301_start_hnp, - - enable_vbus_draw(isp, 0); - power_down(isp); - the_transceiver = isp; - -#ifdef CONFIG_USB_OTG - update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); - update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); -#endif - - dump_regs(isp, __func__); - -#ifdef VERBOSE - mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); - dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); -#endif - - status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); - if (status < 0) - dev_err(&i2c->dev, "can't register transceiver, %d\n", - status); - - return 0; - -fail: - kfree(isp->phy.otg); - kfree(isp); - return -ENODEV; -} - -static const struct i2c_device_id isp1301_id[] = { - { "isp1301_omap", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, isp1301_id); - -static struct i2c_driver isp1301_driver = { - .driver = { - .name = "isp1301_omap", - }, - .probe = isp1301_probe, - .remove = __exit_p(isp1301_remove), - .id_table = isp1301_id, -}; - -/*-------------------------------------------------------------------------*/ - -static int __init isp_init(void) -{ - return i2c_add_driver(&isp1301_driver); -} -subsys_initcall(isp_init); - -static void __exit isp_exit(void) -{ - if (the_transceiver) - usb_remove_phy(&the_transceiver->phy); - i2c_del_driver(&isp1301_driver); -} -module_exit(isp_exit); - diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c deleted file mode 100644 index 749fbf4..0000000 --- a/drivers/usb/otg/msm_otg.c +++ /dev/null @@ -1,1762 +0,0 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MSM_USB_BASE (motg->regs) -#define DRIVER_NAME "msm_otg" - -#define ULPI_IO_TIMEOUT_USEC (10 * 1000) - -#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ -#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ -#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ -#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ -#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ -#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ -#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ -#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ - -static struct regulator *hsusb_3p3; -static struct regulator *hsusb_1p8; -static struct regulator *hsusb_vddcx; - -static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) -{ - int ret = 0; - - if (init) { - hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); - if (IS_ERR(hsusb_vddcx)) { - dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); - return PTR_ERR(hsusb_vddcx); - } - - ret = regulator_set_voltage(hsusb_vddcx, - USB_PHY_VDD_DIG_VOL_MIN, - USB_PHY_VDD_DIG_VOL_MAX); - if (ret) { - dev_err(motg->phy.dev, "unable to set the voltage " - "for hsusb vddcx\n"); - regulator_put(hsusb_vddcx); - return ret; - } - - ret = regulator_enable(hsusb_vddcx); - if (ret) { - dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); - regulator_put(hsusb_vddcx); - } - } else { - ret = regulator_set_voltage(hsusb_vddcx, 0, - USB_PHY_VDD_DIG_VOL_MAX); - if (ret) - dev_err(motg->phy.dev, "unable to set the voltage " - "for hsusb vddcx\n"); - ret = regulator_disable(hsusb_vddcx); - if (ret) - dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); - - regulator_put(hsusb_vddcx); - } - - return ret; -} - -static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) -{ - int rc = 0; - - if (init) { - hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); - if (IS_ERR(hsusb_3p3)) { - dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); - return PTR_ERR(hsusb_3p3); - } - - rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, - USB_PHY_3P3_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "unable to set voltage level " - "for hsusb 3p3\n"); - goto put_3p3; - } - rc = regulator_enable(hsusb_3p3); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); - goto put_3p3; - } - hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); - if (IS_ERR(hsusb_1p8)) { - dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); - rc = PTR_ERR(hsusb_1p8); - goto disable_3p3; - } - rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, - USB_PHY_1P8_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "unable to set voltage level " - "for hsusb 1p8\n"); - goto put_1p8; - } - rc = regulator_enable(hsusb_1p8); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); - goto put_1p8; - } - - return 0; - } - - regulator_disable(hsusb_1p8); -put_1p8: - regulator_put(hsusb_1p8); -disable_3p3: - regulator_disable(hsusb_3p3); -put_3p3: - regulator_put(hsusb_3p3); - return rc; -} - -#ifdef CONFIG_PM_SLEEP -#define USB_PHY_SUSP_DIG_VOL 500000 -static int msm_hsusb_config_vddcx(int high) -{ - int max_vol = USB_PHY_VDD_DIG_VOL_MAX; - int min_vol; - int ret; - - if (high) - min_vol = USB_PHY_VDD_DIG_VOL_MIN; - else - min_vol = USB_PHY_SUSP_DIG_VOL; - - ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); - if (ret) { - pr_err("%s: unable to set the voltage for regulator " - "HSUSB_VDDCX\n", __func__); - return ret; - } - - pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); - - return ret; -} -#endif - -static int msm_hsusb_ldo_set_mode(int on) -{ - int ret = 0; - - if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { - pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); - return -ENODEV; - } - - if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { - pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); - return -ENODEV; - } - - if (on) { - ret = regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_HPM_LOAD); - if (ret < 0) { - pr_err("%s: Unable to set HPM of the regulator " - "HSUSB_1p8\n", __func__); - return ret; - } - ret = regulator_set_optimum_mode(hsusb_3p3, - USB_PHY_3P3_HPM_LOAD); - if (ret < 0) { - pr_err("%s: Unable to set HPM of the regulator " - "HSUSB_3p3\n", __func__); - regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_LPM_LOAD); - return ret; - } - } else { - ret = regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_LPM_LOAD); - if (ret < 0) - pr_err("%s: Unable to set LPM of the regulator " - "HSUSB_1p8\n", __func__); - ret = regulator_set_optimum_mode(hsusb_3p3, - USB_PHY_3P3_LPM_LOAD); - if (ret < 0) - pr_err("%s: Unable to set LPM of the regulator " - "HSUSB_3p3\n", __func__); - } - - pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); - return ret < 0 ? ret : 0; -} - -static int ulpi_read(struct usb_phy *phy, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate read operation */ - writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_read: timeout %08x\n", - readl(USB_ULPI_VIEWPORT)); - return -ETIMEDOUT; - } - return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); -} - -static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate write operation */ - writel(ULPI_RUN | ULPI_WRITE | - ULPI_ADDR(reg) | ULPI_DATA(val), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_write: timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - -static struct usb_phy_io_ops msm_otg_io_ops = { - .read = ulpi_read, - .write = ulpi_write, -}; - -static void ulpi_init(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - int *seq = pdata->phy_init_seq; - - if (!seq) - return; - - while (seq[0] >= 0) { - dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", - seq[0], seq[1]); - ulpi_write(&motg->phy, seq[0], seq[1]); - seq += 2; - } -} - -static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) -{ - int ret; - - if (assert) { - ret = clk_reset(motg->clk, CLK_RESET_ASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); - } else { - ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); - } - return ret; -} - -static int msm_otg_phy_clk_reset(struct msm_otg *motg) -{ - int ret; - - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); - if (ret) { - dev_err(motg->phy.dev, "usb phy clk assert failed\n"); - return ret; - } - usleep_range(10000, 12000); - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); - return ret; -} - -static int msm_otg_phy_reset(struct msm_otg *motg) -{ - u32 val; - int ret; - int retries; - - ret = msm_otg_link_clk_reset(motg, 1); - if (ret) - return ret; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - ret = msm_otg_link_clk_reset(motg, 0); - if (ret) - return ret; - - val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_ULPI, USB_PORTSC); - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, - ULPI_CLR(ULPI_FUNC_CTRL)); - if (!ret) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - /* This reset calibrates the phy, if the above write succeeded */ - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_read(&motg->phy, ULPI_DEBUG); - if (ret != -ETIMEDOUT) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - dev_info(motg->phy.dev, "phy_reset: success\n"); - return 0; -} - -#define LINK_RESET_TIMEOUT_USEC (250 * 1000) -static int msm_otg_reset(struct usb_phy *phy) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - int ret; - u32 val = 0; - u32 ulpi_val = 0; - - ret = msm_otg_phy_reset(motg); - if (ret) { - dev_err(phy->dev, "phy_reset failed\n"); - return ret; - } - - ulpi_init(motg); - - writel(USBCMD_RESET, USB_USBCMD); - while (cnt < LINK_RESET_TIMEOUT_USEC) { - if (!(readl(USB_USBCMD) & USBCMD_RESET)) - break; - udelay(1); - cnt++; - } - if (cnt >= LINK_RESET_TIMEOUT_USEC) - return -ETIMEDOUT; - - /* select ULPI phy */ - writel(0x80000000, USB_PORTSC); - - msleep(100); - - writel(0x0, USB_AHBBURST); - writel(0x00, USB_AHBMODE); - - if (pdata->otg_control == OTG_PHY_CONTROL) { - val = readl(USB_OTGSC); - if (pdata->mode == USB_OTG) { - ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; - val |= OTGSC_IDIE | OTGSC_BSVIE; - } else if (pdata->mode == USB_PERIPHERAL) { - ulpi_val = ULPI_INT_SESS_VALID; - val |= OTGSC_BSVIE; - } - writel(val, USB_OTGSC); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); - } - - return 0; -} - -#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) -#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_suspend(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - - if (atomic_read(&motg->in_lpm)) - return 0; - - disable_irq(motg->irq); - /* - * Chipidea 45-nm PHY suspend sequence: - * - * Interrupt Latch Register auto-clear feature is not present - * in all PHY versions. Latch register is clear on read type. - * Clear latch register to avoid spurious wakeup from - * low power mode (LPM). - * - * PHY comparators are disabled when PHY enters into low power - * mode (LPM). Keep PHY comparators ON in LPM only when we expect - * VBUS/Id notifications from USB PHY. Otherwise turn off USB - * PHY comparators. This save significant amount of power. - * - * PLL is not turned off when PHY enters into low power mode (LPM). - * Disable PLL for maximum power savings. - */ - - if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { - ulpi_read(phy, 0x14); - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(phy, 0x01, 0x30); - ulpi_write(phy, 0x08, 0x09); - } - - /* - * PHY may take some time or even fail to enter into low power - * mode (LPM). Hence poll for 500 msec and reset the PHY and link - * in failure case. - */ - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { - dev_err(phy->dev, "Unable to suspend PHY\n"); - msm_otg_reset(phy); - enable_irq(motg->irq); - return -ETIMEDOUT; - } - - /* - * PHY has capability to generate interrupt asynchronously in low - * power mode (LPM). This interrupt is level triggered. So USB IRQ - * line must be disabled till async interrupt enable bit is cleared - * in USBCMD register. Assert STP (ULPI interface STOP signal) to - * block data communication from PHY. - */ - writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) - writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - - if (!IS_ERR(motg->pclk_src)) - clk_disable(motg->pclk_src); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(0); - msm_hsusb_config_vddcx(0); - } - - if (device_may_wakeup(phy->dev)) - enable_irq_wake(motg->irq); - if (bus) - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 1); - enable_irq(motg->irq); - - dev_info(phy->dev, "USB in low power mode\n"); - - return 0; -} - -static int msm_otg_resume(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - int cnt = 0; - unsigned temp; - - if (!atomic_read(&motg->in_lpm)) - return 0; - - if (!IS_ERR(motg->pclk_src)) - clk_enable(motg->pclk_src); - - clk_enable(motg->pclk); - clk_enable(motg->clk); - if (motg->core_clk) - clk_enable(motg->core_clk); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(1); - msm_hsusb_config_vddcx(1); - writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); - } - - temp = readl(USB_USBCMD); - temp &= ~ASYNC_INTR_CTRL; - temp &= ~ULPI_STP_CTRL; - writel(temp, USB_USBCMD); - - /* - * PHY comes out of low power mode (LPM) in case of wakeup - * from asynchronous interrupt. - */ - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - goto skip_phy_resume; - - writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_RESUME_TIMEOUT_USEC) { - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_RESUME_TIMEOUT_USEC) { - /* - * This is a fatal error. Reset the link and - * PHY. USB state can not be restored. Re-insertion - * of USB cable is the only way to get USB working. - */ - dev_err(phy->dev, "Unable to resume USB." - "Re-plugin the cable\n"); - msm_otg_reset(phy); - } - -skip_phy_resume: - if (device_may_wakeup(phy->dev)) - disable_irq_wake(motg->irq); - if (bus) - set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 0); - - if (motg->async_int) { - motg->async_int = 0; - pm_runtime_put(phy->dev); - enable_irq(motg->irq); - } - - dev_info(phy->dev, "USB exited from low power mode\n"); - - return 0; -} -#endif - -static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) -{ - if (motg->cur_power == mA) - return; - - /* TODO: Notify PMIC about available current */ - dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); - motg->cur_power = mA; -} - -static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - - /* - * Gadget driver uses set_power method to notify about the - * available current based on suspend/configured states. - * - * IDEV_CHG can be drawn irrespective of suspend/un-configured - * states when CDP/ACA is connected. - */ - if (motg->chg_type == USB_SDP_CHARGER) - msm_otg_notify_charger(motg, mA); - - return 0; -} - -static void msm_otg_start_host(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - struct usb_hcd *hcd; - - if (!phy->otg->host) - return; - - hcd = bus_to_hcd(phy->otg->host); - - if (on) { - dev_dbg(phy->dev, "host on\n"); - - if (pdata->vbus_power) - pdata->vbus_power(1); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Enable internal - * HUB before kicking the host. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_A_HOST); -#ifdef CONFIG_USB - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); -#endif - } else { - dev_dbg(phy->dev, "host off\n"); - -#ifdef CONFIG_USB - usb_remove_hcd(hcd); -#endif - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - if (pdata->vbus_power) - pdata->vbus_power(0); - } -} - -static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); - struct usb_hcd *hcd; - - /* - * Fail host registration if this board can support - * only peripheral configuration. - */ - if (motg->pdata->mode == USB_PERIPHERAL) { - dev_info(otg->phy->dev, "Host mode is not supported\n"); - return -ENODEV; - } - - if (!host) { - if (otg->phy->state == OTG_STATE_A_HOST) { - pm_runtime_get_sync(otg->phy->dev); - msm_otg_start_host(otg->phy, 0); - otg->host = NULL; - otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->host = NULL; - } - - return 0; - } - - hcd = bus_to_hcd(host); - hcd->power_budget = motg->pdata->power_budget; - - otg->host = host; - dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if peripheral is not supported - * or peripheral is already registered with us. - */ - if (motg->pdata->mode == USB_HOST || otg->gadget) { - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -static void msm_otg_start_peripheral(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - - if (!phy->otg->gadget) - return; - - if (on) { - dev_dbg(phy->dev, "gadget on\n"); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Disable internal - * HUB before kicking the gadget. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); - usb_gadget_vbus_connect(phy->otg->gadget); - } else { - dev_dbg(phy->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(phy->otg->gadget); - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - } - -} - -static int msm_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); - - /* - * Fail peripheral registration if this board can support - * only host configuration. - */ - if (motg->pdata->mode == USB_HOST) { - dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); - return -ENODEV; - } - - if (!gadget) { - if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { - pm_runtime_get_sync(otg->phy->dev); - msm_otg_start_peripheral(otg->phy, 0); - otg->gadget = NULL; - otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->gadget = NULL; - } - - return 0; - } - otg->gadget = gadget; - dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if host is not supported - * or host is already registered with us. - */ - if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -static bool msm_chg_check_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* put it in host mode for enabling D- source */ - chg_det &= ~(1 << 2); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DM as current source, DP as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x8, 0x85); - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DP as current source, DM as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 line_state; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x15); - ret = !(line_state & 1); - break; - case SNPS_28NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x87); - ret = line_state & 2; - break; - default: - break; - } - return ret; -} - -static void msm_chg_disable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - chg_det &= ~(1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - ulpi_write(phy, 0x10, 0x86); - break; - default: - break; - } -} - -static void msm_chg_enable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn on D+ current source */ - chg_det |= (1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Data contact detection enable */ - ulpi_write(phy, 0x10, 0x85); - break; - default: - break; - } -} - -static void msm_chg_block_on(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - /* put the controller in non-driving mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - udelay(100); - break; - default: - break; - } -} - -static void msm_chg_block_off(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - break; - default: - break; - } - - /* put the controller in normal mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); -} - -#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ -#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ -#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ -#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ -static void msm_chg_detect_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); - struct usb_phy *phy = &motg->phy; - bool is_dcd, tmout, vout; - unsigned long delay; - - dev_dbg(phy->dev, "chg detection work\n"); - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - pm_runtime_get_sync(phy->dev); - msm_chg_block_on(motg); - msm_chg_enable_dcd(motg); - motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; - motg->dcd_retries = 0; - delay = MSM_CHG_DCD_POLL_TIME; - break; - case USB_CHG_STATE_WAIT_FOR_DCD: - is_dcd = msm_chg_check_dcd(motg); - tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; - if (is_dcd || tmout) { - msm_chg_disable_dcd(motg); - msm_chg_enable_primary_det(motg); - delay = MSM_CHG_PRIMARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_DCD_DONE; - } else { - delay = MSM_CHG_DCD_POLL_TIME; - } - break; - case USB_CHG_STATE_DCD_DONE: - vout = msm_chg_check_primary_det(motg); - if (vout) { - msm_chg_enable_secondary_det(motg); - delay = MSM_CHG_SECONDARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; - } else { - motg->chg_type = USB_SDP_CHARGER; - motg->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - } - break; - case USB_CHG_STATE_PRIMARY_DONE: - vout = msm_chg_check_secondary_det(motg); - if (vout) - motg->chg_type = USB_DCP_CHARGER; - else - motg->chg_type = USB_CDP_CHARGER; - motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; - /* fall through */ - case USB_CHG_STATE_SECONDARY_DONE: - motg->chg_state = USB_CHG_STATE_DETECTED; - case USB_CHG_STATE_DETECTED: - msm_chg_block_off(motg); - dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); - schedule_work(&motg->sm_work); - return; - default: - return; - } - - schedule_delayed_work(&motg->chg_work, delay); -} - -/* - * We support OTG, Peripheral only and Host only configurations. In case - * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen - * via Id pin status or user request (debugfs). Id/BSV interrupts are not - * enabled when switch is controlled by user and default mode is supplied - * by board file, which can be changed by userspace later. - */ -static void msm_otg_init_sm(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - u32 otgsc = readl(USB_OTGSC); - - switch (pdata->mode) { - case USB_OTG: - if (pdata->otg_control == OTG_PHY_CONTROL) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - } else if (pdata->otg_control == OTG_USER_CONTROL) { - if (pdata->default_mode == USB_HOST) { - clear_bit(ID, &motg->inputs); - } else if (pdata->default_mode == USB_PERIPHERAL) { - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - } else { - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - } - } - break; - case USB_HOST: - clear_bit(ID, &motg->inputs); - break; - case USB_PERIPHERAL: - set_bit(ID, &motg->inputs); - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - break; - } -} - -static void msm_otg_sm_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct usb_otg *otg = motg->phy.otg; - - switch (otg->phy->state) { - case OTG_STATE_UNDEFINED: - dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); - msm_otg_reset(otg->phy); - msm_otg_init_sm(motg); - otg->phy->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); - if (!test_bit(ID, &motg->inputs) && otg->host) { - /* disable BSV bit */ - writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); - msm_otg_start_host(otg->phy, 1); - otg->phy->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs)) { - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - msm_chg_detect_work(&motg->chg_work.work); - break; - case USB_CHG_STATE_DETECTED: - switch (motg->chg_type) { - case USB_DCP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - break; - case USB_CDP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; - break; - case USB_SDP_CHARGER: - msm_otg_notify_charger(motg, IUNIT); - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; - break; - default: - break; - } - break; - default: - break; - } - } else { - /* - * If charger detection work is pending, decrement - * the pm usage counter to balance with the one that - * is incremented in charger detection work. - */ - if (cancel_delayed_work_sync(&motg->chg_work)) { - pm_runtime_put_sync(otg->phy->dev); - msm_otg_reset(otg->phy); - } - msm_otg_notify_charger(motg, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - } - pm_runtime_put_sync(otg->phy->dev); - break; - case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); - if (!test_bit(B_SESS_VLD, &motg->inputs) || - !test_bit(ID, &motg->inputs)) { - msm_otg_notify_charger(motg, 0); - msm_otg_start_peripheral(otg->phy, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - otg->phy->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->phy); - schedule_work(w); - } - break; - case OTG_STATE_A_HOST: - dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); - if (test_bit(ID, &motg->inputs)) { - msm_otg_start_host(otg->phy, 0); - otg->phy->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->phy); - schedule_work(w); - } - break; - default: - break; - } -} - -static irqreturn_t msm_otg_irq(int irq, void *data) -{ - struct msm_otg *motg = data; - struct usb_phy *phy = &motg->phy; - u32 otgsc = 0; - - if (atomic_read(&motg->in_lpm)) { - disable_irq_nosync(irq); - motg->async_int = 1; - pm_runtime_get(phy->dev); - return IRQ_HANDLED; - } - - otgsc = readl(USB_OTGSC); - if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) - return IRQ_NONE; - - if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - dev_dbg(phy->dev, "ID set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - dev_dbg(phy->dev, "BSV set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } - - writel(otgsc, USB_OTGSC); - schedule_work(&motg->sm_work); - return IRQ_HANDLED; -} - -static int msm_otg_mode_show(struct seq_file *s, void *unused) -{ - struct msm_otg *motg = s->private; - struct usb_otg *otg = motg->phy.otg; - - switch (otg->phy->state) { - case OTG_STATE_A_HOST: - seq_printf(s, "host\n"); - break; - case OTG_STATE_B_PERIPHERAL: - seq_printf(s, "peripheral\n"); - break; - default: - seq_printf(s, "none\n"); - break; - } - - return 0; -} - -static int msm_otg_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, msm_otg_mode_show, inode->i_private); -} - -static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct msm_otg *motg = s->private; - char buf[16]; - struct usb_otg *otg = motg->phy.otg; - int status = count; - enum usb_mode_type req_mode; - - memset(buf, 0x00, sizeof(buf)); - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { - status = -EFAULT; - goto out; - } - - if (!strncmp(buf, "host", 4)) { - req_mode = USB_HOST; - } else if (!strncmp(buf, "peripheral", 10)) { - req_mode = USB_PERIPHERAL; - } else if (!strncmp(buf, "none", 4)) { - req_mode = USB_NONE; - } else { - status = -EINVAL; - goto out; - } - - switch (req_mode) { - case USB_NONE: - switch (otg->phy->state) { - case OTG_STATE_A_HOST: - case OTG_STATE_B_PERIPHERAL: - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_PERIPHERAL: - switch (otg->phy->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_A_HOST: - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_HOST: - switch (otg->phy->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - clear_bit(ID, &motg->inputs); - break; - default: - goto out; - } - break; - default: - goto out; - } - - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); -out: - return status; -} - -const struct file_operations msm_otg_mode_fops = { - .open = msm_otg_mode_open, - .read = seq_read, - .write = msm_otg_mode_write, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *msm_otg_dbg_root; -static struct dentry *msm_otg_dbg_mode; - -static int msm_otg_debugfs_init(struct msm_otg *motg) -{ - msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); - - if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) - return -ENODEV; - - msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, - msm_otg_dbg_root, motg, &msm_otg_mode_fops); - if (!msm_otg_dbg_mode) { - debugfs_remove(msm_otg_dbg_root); - msm_otg_dbg_root = NULL; - return -ENODEV; - } - - return 0; -} - -static void msm_otg_debugfs_cleanup(void) -{ - debugfs_remove(msm_otg_dbg_mode); - debugfs_remove(msm_otg_dbg_root); -} - -static int __init msm_otg_probe(struct platform_device *pdev) -{ - int ret = 0; - struct resource *res; - struct msm_otg *motg; - struct usb_phy *phy; - - dev_info(&pdev->dev, "msm_otg probe\n"); - if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "No platform data given. Bailing out\n"); - return -ENODEV; - } - - motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); - if (!motg) { - dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; - } - - motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!motg->phy.otg) { - dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; - } - - motg->pdata = pdev->dev.platform_data; - phy = &motg->phy; - phy->dev = &pdev->dev; - - motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); - if (IS_ERR(motg->phy_reset_clk)) { - dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - ret = PTR_ERR(motg->phy_reset_clk); - goto free_motg; - } - - motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); - if (IS_ERR(motg->clk)) { - dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); - ret = PTR_ERR(motg->clk); - goto put_phy_reset_clk; - } - clk_set_rate(motg->clk, 60000000); - - /* - * If USB Core is running its protocol engine based on CORE CLK, - * CORE CLK must be running at >55Mhz for correct HSUSB - * operation and USB core cannot tolerate frequency changes on - * CORE CLK. For such USB cores, vote for maximum clk frequency - * on pclk source - */ - if (motg->pdata->pclk_src_name) { - motg->pclk_src = clk_get(&pdev->dev, - motg->pdata->pclk_src_name); - if (IS_ERR(motg->pclk_src)) - goto put_clk; - clk_set_rate(motg->pclk_src, INT_MAX); - clk_enable(motg->pclk_src); - } else - motg->pclk_src = ERR_PTR(-ENOENT); - - - motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); - if (IS_ERR(motg->pclk)) { - dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); - ret = PTR_ERR(motg->pclk); - goto put_pclk_src; - } - - /* - * USB core clock is not present on all MSM chips. This - * clock is introduced to remove the dependency on AXI - * bus frequency. - */ - motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); - if (IS_ERR(motg->core_clk)) - motg->core_clk = NULL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get platform resource mem\n"); - ret = -ENODEV; - goto put_core_clk; - } - - motg->regs = ioremap(res->start, resource_size(res)); - if (!motg->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto put_core_clk; - } - dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); - - motg->irq = platform_get_irq(pdev, 0); - if (!motg->irq) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - ret = -ENODEV; - goto free_regs; - } - - clk_enable(motg->clk); - clk_enable(motg->pclk); - - ret = msm_hsusb_init_vddcx(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); - goto free_regs; - } - - ret = msm_hsusb_ldo_init(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); - goto vddcx_exit; - } - ret = msm_hsusb_ldo_set_mode(1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg enable failed\n"); - goto ldo_exit; - } - - if (motg->core_clk) - clk_enable(motg->core_clk); - - writel(0, USB_USBINTR); - writel(0, USB_OTGSC); - - INIT_WORK(&motg->sm_work, msm_otg_sm_work); - INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); - ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, - "msm_otg", motg); - if (ret) { - dev_err(&pdev->dev, "request irq failed\n"); - goto disable_clks; - } - - phy->init = msm_otg_reset; - phy->set_power = msm_otg_set_power; - - phy->io_ops = &msm_otg_io_ops; - - phy->otg->phy = &motg->phy; - phy->otg->set_host = msm_otg_set_host; - phy->otg->set_peripheral = msm_otg_set_peripheral; - - ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); - if (ret) { - dev_err(&pdev->dev, "usb_add_phy failed\n"); - goto free_irq; - } - - platform_set_drvdata(pdev, motg); - device_init_wakeup(&pdev->dev, 1); - - if (motg->pdata->mode == USB_OTG && - motg->pdata->otg_control == OTG_USER_CONTROL) { - ret = msm_otg_debugfs_init(motg); - if (ret) - dev_dbg(&pdev->dev, "mode debugfs file is" - "not available\n"); - } - - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; -free_irq: - free_irq(motg->irq, motg); -disable_clks: - clk_disable(motg->pclk); - clk_disable(motg->clk); -ldo_exit: - msm_hsusb_ldo_init(motg, 0); -vddcx_exit: - msm_hsusb_init_vddcx(motg, 0); -free_regs: - iounmap(motg->regs); -put_core_clk: - if (motg->core_clk) - clk_put(motg->core_clk); - clk_put(motg->pclk); -put_pclk_src: - if (!IS_ERR(motg->pclk_src)) { - clk_disable(motg->pclk_src); - clk_put(motg->pclk_src); - } -put_clk: - clk_put(motg->clk); -put_phy_reset_clk: - clk_put(motg->phy_reset_clk); -free_motg: - kfree(motg->phy.otg); - kfree(motg); - return ret; -} - -static int msm_otg_remove(struct platform_device *pdev) -{ - struct msm_otg *motg = platform_get_drvdata(pdev); - struct usb_phy *phy = &motg->phy; - int cnt = 0; - - if (phy->otg->host || phy->otg->gadget) - return -EBUSY; - - msm_otg_debugfs_cleanup(); - cancel_delayed_work_sync(&motg->chg_work); - cancel_work_sync(&motg->sm_work); - - pm_runtime_resume(&pdev->dev); - - device_init_wakeup(&pdev->dev, 0); - pm_runtime_disable(&pdev->dev); - - usb_remove_phy(phy); - free_irq(motg->irq, motg); - - /* - * Put PHY in low power mode. - */ - ulpi_read(phy, 0x14); - ulpi_write(phy, 0x08, 0x09); - - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) - dev_err(phy->dev, "Unable to suspend PHY\n"); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - if (!IS_ERR(motg->pclk_src)) { - clk_disable(motg->pclk_src); - clk_put(motg->pclk_src); - } - msm_hsusb_ldo_init(motg, 0); - - iounmap(motg->regs); - pm_runtime_set_suspended(&pdev->dev); - - clk_put(motg->phy_reset_clk); - clk_put(motg->pclk); - clk_put(motg->clk); - if (motg->core_clk) - clk_put(motg->core_clk); - - kfree(motg->phy.otg); - kfree(motg); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME -static int msm_otg_runtime_idle(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - struct usb_otg *otg = motg->phy.otg; - - dev_dbg(dev, "OTG runtime idle\n"); - - /* - * It is observed some times that a spurious interrupt - * comes when PHY is put into LPM immediately after PHY reset. - * This 1 sec delay also prevents entering into LPM immediately - * after asynchronous interrupt. - */ - if (otg->phy->state != OTG_STATE_UNDEFINED) - pm_schedule_suspend(dev, 1000); - - return -EAGAIN; -} - -static int msm_otg_runtime_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_runtime_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime resume\n"); - return msm_otg_resume(motg); -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_pm_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG PM suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_pm_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "OTG PM resume\n"); - - ret = msm_otg_resume(motg); - if (ret) - return ret; - - /* - * Runtime PM Documentation recommends bringing the - * device to full powered state upon resume. - */ - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static const struct dev_pm_ops msm_otg_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) - SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, - msm_otg_runtime_idle) -}; -#endif - -static struct platform_driver msm_otg_driver = { - .remove = msm_otg_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &msm_otg_dev_pm_ops, -#endif - }, -}; - -module_platform_driver_probe(msm_otg_driver, msm_otg_probe); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c deleted file mode 100644 index b6a9be3..0000000 --- a/drivers/usb/otg/mv_otg.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * Author: Chao Xie - * Neil Zhang - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mv_otg.h" - -#define DRIVER_DESC "Marvell USB OTG transceiver driver" -#define DRIVER_VERSION "Jan 20, 2010" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "mv-otg"; - -static char *state_string[] = { - "undefined", - "b_idle", - "b_srp_init", - "b_peripheral", - "b_wait_acon", - "b_host", - "a_idle", - "a_wait_vrise", - "a_wait_bcon", - "a_host", - "a_suspend", - "a_peripheral", - "a_wait_vfall", - "a_vbus_err" -}; - -static int mv_otg_set_vbus(struct usb_otg *otg, bool on) -{ - struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy); - if (mvotg->pdata->set_vbus == NULL) - return -ENODEV; - - return mvotg->pdata->set_vbus(on); -} - -static int mv_otg_set_host(struct usb_otg *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int mv_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static void mv_otg_run_state_machine(struct mv_otg *mvotg, - unsigned long delay) -{ - dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n"); - if (!mvotg->qwork) - return; - - queue_delayed_work(mvotg->qwork, &mvotg->work, delay); -} - -static void mv_otg_timer_await_bcon(unsigned long data) -{ - struct mv_otg *mvotg = (struct mv_otg *) data; - - mvotg->otg_ctrl.a_wait_bcon_timeout = 1; - - dev_info(&mvotg->pdev->dev, "B Device No Response!\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } -} - -static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - - if (timer_pending(timer)) - del_timer(timer); - - return 0; -} - -static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, - unsigned long interval, - void (*callback) (unsigned long)) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - if (timer_pending(timer)) { - dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id); - return -EBUSY; - } - - init_timer(timer); - timer->data = (unsigned long) mvotg; - timer->function = callback; - timer->expires = jiffies + interval; - add_timer(timer); - - return 0; -} - -static int mv_otg_reset(struct mv_otg *mvotg) -{ - unsigned int loops; - u32 tmp; - - /* Stop the controller */ - tmp = readl(&mvotg->op_regs->usbcmd); - tmp &= ~USBCMD_RUN_STOP; - writel(tmp, &mvotg->op_regs->usbcmd); - - /* Reset the controller to get default values */ - writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd); - - loops = 500; - while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) { - if (loops == 0) { - dev_err(&mvotg->pdev->dev, - "Wait for RESET completed TIMEOUT\n"); - return -ETIMEDOUT; - } - loops--; - udelay(20); - } - - writel(0x0, &mvotg->op_regs->usbintr); - tmp = readl(&mvotg->op_regs->usbsts); - writel(tmp, &mvotg->op_regs->usbsts); - - return 0; -} - -static void mv_otg_init_irq(struct mv_otg *mvotg) -{ - u32 otgsc; - - mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID - | OTGSC_INTR_A_VBUS_VALID; - mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID - | OTGSC_INTSTS_A_VBUS_VALID; - - if (mvotg->pdata->vbus == NULL) { - mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID - | OTGSC_INTR_B_SESSION_END; - mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID - | OTGSC_INTSTS_B_SESSION_END; - } - - if (mvotg->pdata->id == NULL) { - mvotg->irq_en |= OTGSC_INTR_USB_ID; - mvotg->irq_status |= OTGSC_INTSTS_USB_ID; - } - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); -} - -static void mv_otg_start_host(struct mv_otg *mvotg, int on) -{ -#ifdef CONFIG_USB - struct usb_otg *otg = mvotg->phy.otg; - struct usb_hcd *hcd; - - if (!otg->host) - return; - - dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop"); - - hcd = bus_to_hcd(otg->host); - - if (on) - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - else - usb_remove_hcd(hcd); -#endif /* CONFIG_USB */ -} - -static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) -{ - struct usb_otg *otg = mvotg->phy.otg; - - if (!otg->gadget) - return; - - dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off"); - - if (on) - usb_gadget_vbus_connect(otg->gadget); - else - usb_gadget_vbus_disconnect(otg->gadget); -} - -static void otg_clock_enable(struct mv_otg *mvotg) -{ - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_prepare_enable(mvotg->clk[i]); -} - -static void otg_clock_disable(struct mv_otg *mvotg) -{ - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_disable_unprepare(mvotg->clk[i]); -} - -static int mv_otg_enable_internal(struct mv_otg *mvotg) -{ - int retval = 0; - - if (mvotg->active) - return 0; - - dev_dbg(&mvotg->pdev->dev, "otg enabled\n"); - - otg_clock_enable(mvotg); - if (mvotg->pdata->phy_init) { - retval = mvotg->pdata->phy_init(mvotg->phy_regs); - if (retval) { - dev_err(&mvotg->pdev->dev, - "init phy error %d\n", retval); - otg_clock_disable(mvotg); - return retval; - } - } - mvotg->active = 1; - - return 0; - -} - -static int mv_otg_enable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - return mv_otg_enable_internal(mvotg); - - return 0; -} - -static void mv_otg_disable_internal(struct mv_otg *mvotg) -{ - if (mvotg->active) { - dev_dbg(&mvotg->pdev->dev, "otg disabled\n"); - if (mvotg->pdata->phy_deinit) - mvotg->pdata->phy_deinit(mvotg->phy_regs); - otg_clock_disable(mvotg); - mvotg->active = 0; - } -} - -static void mv_otg_disable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - mv_otg_disable_internal(mvotg); -} - -static void mv_otg_update_inputs(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - - if (mvotg->pdata->vbus) { - if (mvotg->pdata->vbus->poll() == VBUS_HIGH) { - otg_ctrl->b_sess_vld = 1; - otg_ctrl->b_sess_end = 0; - } else { - otg_ctrl->b_sess_vld = 0; - otg_ctrl->b_sess_end = 1; - } - } else { - otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID); - otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END); - } - - if (mvotg->pdata->id) - otg_ctrl->id = !!mvotg->pdata->id->poll(); - else - otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID); - - if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id) - otg_ctrl->a_bus_req = 1; - - otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID); - otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID); - - dev_dbg(&mvotg->pdev->dev, "%s: ", __func__); - dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id); - dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld); - dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end); - dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld); - dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld); -} - -static void mv_otg_update_state(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - struct usb_phy *phy = &mvotg->phy; - int old_state = phy->state; - - switch (old_state) { - case OTG_STATE_UNDEFINED: - phy->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - if (otg_ctrl->id == 0) - phy->state = OTG_STATE_A_IDLE; - else if (otg_ctrl->b_sess_vld) - phy->state = OTG_STATE_B_PERIPHERAL; - break; - case OTG_STATE_B_PERIPHERAL: - if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0) - phy->state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_A_IDLE: - if (otg_ctrl->id) - phy->state = OTG_STATE_B_IDLE; - else if (!(otg_ctrl->a_bus_drop) && - (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det)) - phy->state = OTG_STATE_A_WAIT_VRISE; - break; - case OTG_STATE_A_WAIT_VRISE: - if (otg_ctrl->a_vbus_vld) - phy->state = OTG_STATE_A_WAIT_BCON; - break; - case OTG_STATE_A_WAIT_BCON: - if (otg_ctrl->id || otg_ctrl->a_bus_drop - || otg_ctrl->a_wait_bcon_timeout) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_WAIT_VFALL; - otg_ctrl->a_bus_req = 0; - } else if (!otg_ctrl->a_vbus_vld) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_VBUS_ERR; - } else if (otg_ctrl->b_conn) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_HOST; - } - break; - case OTG_STATE_A_HOST: - if (otg_ctrl->id || !otg_ctrl->b_conn - || otg_ctrl->a_bus_drop) - phy->state = OTG_STATE_A_WAIT_BCON; - else if (!otg_ctrl->a_vbus_vld) - phy->state = OTG_STATE_A_VBUS_ERR; - break; - case OTG_STATE_A_WAIT_VFALL: - if (otg_ctrl->id - || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld) - || otg_ctrl->a_bus_req) - phy->state = OTG_STATE_A_IDLE; - break; - case OTG_STATE_A_VBUS_ERR: - if (otg_ctrl->id || otg_ctrl->a_clr_err - || otg_ctrl->a_bus_drop) { - otg_ctrl->a_clr_err = 0; - phy->state = OTG_STATE_A_WAIT_VFALL; - } - break; - default: - break; - } -} - -static void mv_otg_work(struct work_struct *work) -{ - struct mv_otg *mvotg; - struct usb_phy *phy; - struct usb_otg *otg; - int old_state; - - mvotg = container_of(to_delayed_work(work), struct mv_otg, work); - -run: - /* work queue is single thread, or we need spin_lock to protect */ - phy = &mvotg->phy; - otg = phy->otg; - old_state = phy->state; - - if (!mvotg->active) - return; - - mv_otg_update_inputs(mvotg); - mv_otg_update_state(mvotg); - - if (old_state != phy->state) { - dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", - state_string[old_state], - state_string[phy->state]); - - switch (phy->state) { - case OTG_STATE_B_IDLE: - otg->default_a = 0; - if (old_state == OTG_STATE_B_PERIPHERAL) - mv_otg_start_periphrals(mvotg, 0); - mv_otg_reset(mvotg); - mv_otg_disable(mvotg); - break; - case OTG_STATE_B_PERIPHERAL: - mv_otg_enable(mvotg); - mv_otg_start_periphrals(mvotg, 1); - break; - case OTG_STATE_A_IDLE: - otg->default_a = 1; - mv_otg_enable(mvotg); - if (old_state == OTG_STATE_A_WAIT_VFALL) - mv_otg_start_host(mvotg, 0); - mv_otg_reset(mvotg); - break; - case OTG_STATE_A_WAIT_VRISE: - mv_otg_set_vbus(otg, 1); - break; - case OTG_STATE_A_WAIT_BCON: - if (old_state != OTG_STATE_A_HOST) - mv_otg_start_host(mvotg, 1); - mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, - T_A_WAIT_BCON, - mv_otg_timer_await_bcon); - /* - * Now, we directly enter A_HOST. So set b_conn = 1 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 1; - break; - case OTG_STATE_A_HOST: - break; - case OTG_STATE_A_WAIT_VFALL: - /* - * Now, we has exited A_HOST. So set b_conn = 0 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 0; - mv_otg_set_vbus(otg, 0); - break; - case OTG_STATE_A_VBUS_ERR: - break; - default: - break; - } - goto run; - } -} - -static irqreturn_t mv_otg_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - writel(otgsc, &mvotg->op_regs->otgsc); - - /* - * if we have vbus, then the vbus detection for B-device - * will be done by mv_otg_inputs_irq(). - */ - if (mvotg->pdata->vbus) - if ((otgsc & OTGSC_STS_USB_ID) && - !(otgsc & OTGSC_INTSTS_USB_ID)) - return IRQ_NONE; - - if ((otgsc & mvotg->irq_status) == 0) - return IRQ_NONE; - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - - /* The clock may disabled at this time */ - if (!mvotg->active) { - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - } - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_req); -} - -static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - - if (count > 2) - return -1; - - /* We will use this interface to change to A device */ - if (mvotg->phy.state != OTG_STATE_B_IDLE - && mvotg->phy.state != OTG_STATE_A_IDLE) - return -1; - - /* The clock may disabled and we need to set irq for ID detected */ - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_req = 1; - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_req = 1\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - - return count; -} - -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, - set_a_bus_req); - -static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_clr_err = 1; - dev_dbg(&mvotg->pdev->dev, - "User request: a_clr_err = 1\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); - -static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_drop); -} - -static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_drop = 1; - mvotg->otg_ctrl.a_bus_req = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 1\n"); - dev_dbg(&mvotg->pdev->dev, - "User request: and a_bus_req = 0\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, - get_a_bus_drop, set_a_bus_drop); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_clr_err.attr, - &dev_attr_a_bus_drop.attr, - NULL, -}; - -static struct attribute_group inputs_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -int mv_otg_remove(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group); - - if (mvotg->qwork) { - flush_workqueue(mvotg->qwork); - destroy_workqueue(mvotg->qwork); - } - - mv_otg_disable(mvotg); - - usb_remove_phy(&mvotg->phy); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static int mv_otg_probe(struct platform_device *pdev) -{ - struct mv_usb_platform_data *pdata = pdev->dev.platform_data; - struct mv_otg *mvotg; - struct usb_otg *otg; - struct resource *r; - int retval = 0, clk_i, i; - size_t size; - - if (pdata == NULL) { - dev_err(&pdev->dev, "failed to get platform data\n"); - return -ENODEV; - } - - size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum; - mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!mvotg) { - dev_err(&pdev->dev, "failed to allocate memory!\n"); - return -ENOMEM; - } - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - platform_set_drvdata(pdev, mvotg); - - mvotg->pdev = pdev; - mvotg->pdata = pdata; - - mvotg->clknum = pdata->clknum; - for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) { - mvotg->clk[clk_i] = devm_clk_get(&pdev->dev, - pdata->clkname[clk_i]); - if (IS_ERR(mvotg->clk[clk_i])) { - retval = PTR_ERR(mvotg->clk[clk_i]); - return retval; - } - } - - mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); - if (!mvotg->qwork) { - dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n"); - return -ENOMEM; - } - - INIT_DELAYED_WORK(&mvotg->work, mv_otg_work); - - /* OTG common part */ - mvotg->pdev = pdev; - mvotg->phy.dev = &pdev->dev; - mvotg->phy.otg = otg; - mvotg->phy.label = driver_name; - mvotg->phy.state = OTG_STATE_UNDEFINED; - - otg->phy = &mvotg->phy; - otg->set_host = mv_otg_set_host; - otg->set_peripheral = mv_otg_set_peripheral; - otg->set_vbus = mv_otg_set_vbus; - - for (i = 0; i < OTG_TIMER_NUM; i++) - init_timer(&mvotg->otg_ctrl.timer[i]); - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "phyregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->phy_regs == NULL) { - dev_err(&pdev->dev, "failed to map phy I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "capregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->cap_regs == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - /* we will acces controller register, so enable the udc controller */ - retval = mv_otg_enable_internal(mvotg); - if (retval) { - dev_err(&pdev->dev, "mv otg enable error %d\n", retval); - goto err_destroy_workqueue; - } - - mvotg->op_regs = - (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs - + (readl(mvotg->cap_regs) & CAPLENGTH_MASK)); - - if (pdata->id) { - retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "id", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for ID\n"); - pdata->id = NULL; - } - } - - if (pdata->vbus) { - mvotg->clock_gating = 1; - retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "vbus", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for VBUS, " - "disable clock gating\n"); - mvotg->clock_gating = 0; - pdata->vbus = NULL; - } - } - - if (pdata->disable_otg_clock_gating) - mvotg->clock_gating = 0; - - mv_otg_reset(mvotg); - mv_otg_init_irq(mvotg); - - r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - retval = -ENODEV; - goto err_disable_clk; - } - - mvotg->irq = r->start; - if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED, - driver_name, mvotg)) { - dev_err(&pdev->dev, "Request irq %d for OTG failed\n", - mvotg->irq); - mvotg->irq = 0; - retval = -ENODEV; - goto err_disable_clk; - } - - retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); - if (retval < 0) { - dev_err(&pdev->dev, "can't register transceiver, %d\n", - retval); - goto err_disable_clk; - } - - retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group); - if (retval < 0) { - dev_dbg(&pdev->dev, - "Can't register sysfs attr group: %d\n", retval); - goto err_remove_phy; - } - - spin_lock_init(&mvotg->wq_lock); - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 2 * HZ); - spin_unlock(&mvotg->wq_lock); - } - - dev_info(&pdev->dev, - "successful probe OTG device %s clock gating.\n", - mvotg->clock_gating ? "with" : "without"); - - return 0; - -err_remove_phy: - usb_remove_phy(&mvotg->phy); -err_disable_clk: - mv_otg_disable_internal(mvotg); -err_destroy_workqueue: - flush_workqueue(mvotg->qwork); - destroy_workqueue(mvotg->qwork); - - platform_set_drvdata(pdev, NULL); - - return retval; -} - -#ifdef CONFIG_PM -static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - if (mvotg->phy.state != OTG_STATE_B_IDLE) { - dev_info(&pdev->dev, - "OTG state is not B_IDLE, it is %d!\n", - mvotg->phy.state); - return -EAGAIN; - } - - if (!mvotg->clock_gating) - mv_otg_disable_internal(mvotg); - - return 0; -} - -static int mv_otg_resume(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - u32 otgsc; - - if (!mvotg->clock_gating) { - mv_otg_enable_internal(mvotg); - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - return 0; -} -#endif - -static struct platform_driver mv_otg_driver = { - .probe = mv_otg_probe, - .remove = __exit_p(mv_otg_remove), - .driver = { - .owner = THIS_MODULE, - .name = driver_name, - }, -#ifdef CONFIG_PM - .suspend = mv_otg_suspend, - .resume = mv_otg_resume, -#endif -}; -module_platform_driver(mv_otg_driver); diff --git a/drivers/usb/otg/mv_otg.h b/drivers/usb/otg/mv_otg.h deleted file mode 100644 index 8a9e351..0000000 --- a/drivers/usb/otg/mv_otg.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __MV_USB_OTG_CONTROLLER__ -#define __MV_USB_OTG_CONTROLLER__ - -#include - -/* Command Register Bit Masks */ -#define USBCMD_RUN_STOP (0x00000001) -#define USBCMD_CTRL_RESET (0x00000002) - -/* otgsc Register Bit Masks */ -#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 -#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 -#define OTGSC_CTRL_OTG_TERM 0x00000008 -#define OTGSC_CTRL_DATA_PULSING 0x00000010 -#define OTGSC_STS_USB_ID 0x00000100 -#define OTGSC_STS_A_VBUS_VALID 0x00000200 -#define OTGSC_STS_A_SESSION_VALID 0x00000400 -#define OTGSC_STS_B_SESSION_VALID 0x00000800 -#define OTGSC_STS_B_SESSION_END 0x00001000 -#define OTGSC_STS_1MS_TOGGLE 0x00002000 -#define OTGSC_STS_DATA_PULSING 0x00004000 -#define OTGSC_INTSTS_USB_ID 0x00010000 -#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 -#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 -#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 -#define OTGSC_INTSTS_B_SESSION_END 0x00100000 -#define OTGSC_INTSTS_1MS 0x00200000 -#define OTGSC_INTSTS_DATA_PULSING 0x00400000 -#define OTGSC_INTR_USB_ID 0x01000000 -#define OTGSC_INTR_A_VBUS_VALID 0x02000000 -#define OTGSC_INTR_A_SESSION_VALID 0x04000000 -#define OTGSC_INTR_B_SESSION_VALID 0x08000000 -#define OTGSC_INTR_B_SESSION_END 0x10000000 -#define OTGSC_INTR_1MS_TIMER 0x20000000 -#define OTGSC_INTR_DATA_PULSING 0x40000000 - -#define CAPLENGTH_MASK (0xff) - -/* Timer's interval, unit 10ms */ -#define T_A_WAIT_VRISE 100 -#define T_A_WAIT_BCON 2000 -#define T_A_AIDL_BDIS 100 -#define T_A_BIDL_ADIS 20 -#define T_B_ASE0_BRST 400 -#define T_B_SE0_SRP 300 -#define T_B_SRP_FAIL 2000 -#define T_B_DATA_PLS 10 -#define T_B_SRP_INIT 100 -#define T_A_SRP_RSPNS 10 -#define T_A_DRV_RSM 5 - -enum otg_function { - OTG_B_DEVICE = 0, - OTG_A_DEVICE -}; - -enum mv_otg_timer { - A_WAIT_BCON_TIMER = 0, - OTG_TIMER_NUM -}; - -/* PXA OTG state machine */ -struct mv_otg_ctrl { - /* internal variables */ - u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */ - u8 b_srp_done; - u8 b_hnp_en; - - /* OTG inputs */ - u8 a_bus_drop; - u8 a_bus_req; - u8 a_clr_err; - u8 a_bus_resume; - u8 a_bus_suspend; - u8 a_conn; - u8 a_sess_vld; - u8 a_srp_det; - u8 a_vbus_vld; - u8 b_bus_req; /* B-Device Require Bus */ - u8 b_bus_resume; - u8 b_bus_suspend; - u8 b_conn; - u8 b_se0_srp; - u8 b_sess_end; - u8 b_sess_vld; - u8 id; - u8 a_suspend_req; - - /*Timer event */ - u8 a_aidl_bdis_timeout; - u8 b_ase0_brst_timeout; - u8 a_bidl_adis_timeout; - u8 a_wait_bcon_timeout; - - struct timer_list timer[OTG_TIMER_NUM]; -}; - -#define VUSBHS_MAX_PORTS 8 - -struct mv_otg_regs { - u32 usbcmd; /* Command register */ - u32 usbsts; /* Status register */ - u32 usbintr; /* Interrupt enable */ - u32 frindex; /* Frame index */ - u32 reserved1[1]; - u32 deviceaddr; /* Device Address */ - u32 eplistaddr; /* Endpoint List Address */ - u32 ttctrl; /* HOST TT status and control */ - u32 burstsize; /* Programmable Burst Size */ - u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ - u32 reserved[4]; - u32 epnak; /* Endpoint NAK */ - u32 epnaken; /* Endpoint NAK Enable */ - u32 configflag; /* Configured Flag register */ - u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ - u32 otgsc; - u32 usbmode; /* USB Host/Device mode */ - u32 epsetupstat; /* Endpoint Setup Status */ - u32 epprime; /* Endpoint Initialize */ - u32 epflush; /* Endpoint De-initialize */ - u32 epstatus; /* Endpoint Status */ - u32 epcomplete; /* Endpoint Interrupt On Complete */ - u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ - u32 mcr; /* Mux Control */ - u32 isr; /* Interrupt Status */ - u32 ier; /* Interrupt Enable */ -}; - -struct mv_otg { - struct usb_phy phy; - struct mv_otg_ctrl otg_ctrl; - - /* base address */ - void __iomem *phy_regs; - void __iomem *cap_regs; - struct mv_otg_regs __iomem *op_regs; - - struct platform_device *pdev; - int irq; - u32 irq_status; - u32 irq_en; - - struct delayed_work work; - struct workqueue_struct *qwork; - - spinlock_t wq_lock; - - struct mv_usb_platform_data *pdata; - - unsigned int active; - unsigned int clock_gating; - unsigned int clknum; - struct clk *clk[0]; -}; - -#endif diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c deleted file mode 100644 index 9d4381e..0000000 --- a/drivers/usb/otg/mxs-phy.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * Copyright (C) 2012 Marek Vasut - * on behalf of DENX Software Engineering GmbH - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "mxs_phy" - -#define HW_USBPHY_PWD 0x00 -#define HW_USBPHY_CTRL 0x30 -#define HW_USBPHY_CTRL_SET 0x34 -#define HW_USBPHY_CTRL_CLR 0x38 - -#define BM_USBPHY_CTRL_SFTRST BIT(31) -#define BM_USBPHY_CTRL_CLKGATE BIT(30) -#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) -#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) -#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) - -struct mxs_phy { - struct usb_phy phy; - struct clk *clk; -}; - -#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) - -static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) -{ - void __iomem *base = mxs_phy->phy.io_priv; - - stmp_reset_block(base + HW_USBPHY_CTRL); - - /* Power up the PHY */ - writel(0, base + HW_USBPHY_PWD); - - /* enable FS/LS device */ - writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | - BM_USBPHY_CTRL_ENUTMILEVEL3, - base + HW_USBPHY_CTRL_SET); -} - -static int mxs_phy_init(struct usb_phy *phy) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(phy); - - clk_prepare_enable(mxs_phy->clk); - mxs_phy_hw_init(mxs_phy); - - return 0; -} - -static void mxs_phy_shutdown(struct usb_phy *phy) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(phy); - - writel(BM_USBPHY_CTRL_CLKGATE, - phy->io_priv + HW_USBPHY_CTRL_SET); - - clk_disable_unprepare(mxs_phy->clk); -} - -static int mxs_phy_suspend(struct usb_phy *x, int suspend) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(x); - - if (suspend) { - writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); - writel(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_SET); - clk_disable_unprepare(mxs_phy->clk); - } else { - clk_prepare_enable(mxs_phy->clk); - writel(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_CLR); - writel(0, x->io_priv + HW_USBPHY_PWD); - } - - return 0; -} - -static int mxs_phy_on_connect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - dev_dbg(phy->dev, "%s speed device has connected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); - - if (speed == USB_SPEED_HIGH) - writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_SET); - - return 0; -} - -static int mxs_phy_on_disconnect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - dev_dbg(phy->dev, "%s speed device has disconnected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); - - if (speed == USB_SPEED_HIGH) - writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_CLR); - - return 0; -} - -static int mxs_phy_probe(struct platform_device *pdev) -{ - struct resource *res; - void __iomem *base; - struct clk *clk; - struct mxs_phy *mxs_phy; - int ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENOENT; - } - - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, - "can't get the clock, err=%ld", PTR_ERR(clk)); - return PTR_ERR(clk); - } - - mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); - if (!mxs_phy) { - dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); - return -ENOMEM; - } - - mxs_phy->phy.io_priv = base; - mxs_phy->phy.dev = &pdev->dev; - mxs_phy->phy.label = DRIVER_NAME; - mxs_phy->phy.init = mxs_phy_init; - mxs_phy->phy.shutdown = mxs_phy_shutdown; - mxs_phy->phy.set_suspend = mxs_phy_suspend; - mxs_phy->phy.notify_connect = mxs_phy_on_connect; - mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; - - ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); - - mxs_phy->clk = clk; - - platform_set_drvdata(pdev, &mxs_phy->phy); - - ret = usb_add_phy_dev(&mxs_phy->phy); - if (ret) - return ret; - - return 0; -} - -static int mxs_phy_remove(struct platform_device *pdev) -{ - struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); - - usb_remove_phy(&mxs_phy->phy); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static const struct of_device_id mxs_phy_dt_ids[] = { - { .compatible = "fsl,imx23-usbphy", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); - -static struct platform_driver mxs_phy_driver = { - .probe = mxs_phy_probe, - .remove = mxs_phy_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = mxs_phy_dt_ids, - }, -}; - -static int __init mxs_phy_module_init(void) -{ - return platform_driver_register(&mxs_phy_driver); -} -postcore_initcall(mxs_phy_module_init); - -static void __exit mxs_phy_module_exit(void) -{ - platform_driver_unregister(&mxs_phy_driver); -} -module_exit(mxs_phy_module_exit); - -MODULE_ALIAS("platform:mxs-usb-phy"); -MODULE_AUTHOR("Marek Vasut "); -MODULE_AUTHOR("Richard Zhao "); -MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c deleted file mode 100644 index 2b10cc9..0000000 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * drivers/usb/otg/nop-usb-xceiv.c - * - * NOP USB transceiver for all USB transceiver which are either built-in - * into USB IP or which are mostly autonomous. - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Ajay Kumar Gupta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * This provides a "nop" transceiver for PHYs which are - * autonomous such as isp1504, isp1707, etc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; - -static struct platform_device *pd; - -void usb_nop_xceiv_register(void) -{ - if (pd) - return; - pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); - if (!pd) { - printk(KERN_ERR "Unable to register usb nop transceiver\n"); - return; - } -} -EXPORT_SYMBOL(usb_nop_xceiv_register); - -void usb_nop_xceiv_unregister(void) -{ - platform_device_unregister(pd); - pd = NULL; -} -EXPORT_SYMBOL(usb_nop_xceiv_unregister); - -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - return 0; -} - -static int nop_init(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->vcc)) { - if (regulator_enable(nop->vcc)) - dev_err(phy->dev, "Failed to enable power\n"); - } - - if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); - - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } - - return 0; -} - -static void nop_shutdown(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } - - if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); - - if (!IS_ERR(nop->vcc)) { - if (regulator_disable(nop->vcc)) - dev_err(phy->dev, "Failed to disable power\n"); - } -} - -static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - if (!gadget) { - otg->gadget = NULL; - return -ENODEV; - } - - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - return 0; -} - -static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!host) { - otg->host = NULL; - return -ENODEV; - } - - otg->host = host; - return 0; -} - -static int nop_usb_xceiv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; - struct nop_usb_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); - if (!nop->phy.otg) - return -ENOMEM; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - } - - if (!IS_ERR(nop->clk) && clk_rate) { - err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); - return err; - } - } - - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); - return err; - } - } - - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", - PTR_ERR(nop->vcc)); - if (needs_vcc) - return -EPROBE_DEFER; - } - - nop->reset = devm_regulator_get(&pdev->dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; - } - - nop->dev = &pdev->dev; - nop->phy.dev = nop->dev; - nop->phy.label = "nop-xceiv"; - nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; - nop->phy.state = OTG_STATE_UNDEFINED; - nop->phy.type = type; - - nop->phy.otg->phy = &nop->phy; - nop->phy.otg->set_host = nop_set_host; - nop->phy.otg->set_peripheral = nop_set_peripheral; - - err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_add; - } - - platform_set_drvdata(pdev, nop); - - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - - return 0; - -err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - return err; -} - -static int nop_usb_xceiv_remove(struct platform_device *pdev) -{ - struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - - usb_remove_phy(&nop->phy); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static const struct of_device_id nop_xceiv_dt_ids[] = { - { .compatible = "usb-nop-xceiv" }, - { } -}; - -MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); - -static struct platform_driver nop_usb_xceiv_driver = { - .probe = nop_usb_xceiv_probe, - .remove = nop_usb_xceiv_remove, - .driver = { - .name = "nop_usb_xceiv", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(nop_xceiv_dt_ids), - }, -}; - -static int __init nop_usb_xceiv_init(void) -{ - return platform_driver_register(&nop_usb_xceiv_driver); -} -subsys_initcall(nop_usb_xceiv_init); - -static void __exit nop_usb_xceiv_exit(void) -{ - platform_driver_unregister(&nop_usb_xceiv_driver); -} -module_exit(nop_usb_xceiv_exit); - -MODULE_ALIAS("platform:nop_usb_xceiv"); -MODULE_AUTHOR("Texas Instruments Inc"); -MODULE_DESCRIPTION("NOP USB Transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c deleted file mode 100644 index 1f729a1..0000000 --- a/drivers/usb/otg/otg_fsm.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * OTG Finite State Machine from OTG spec - * - * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * Author: Li Yang - * Jerry Huang - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "otg_fsm.h" - -/* Change USB protocol when there is a protocol change */ -static int otg_set_protocol(struct otg_fsm *fsm, int protocol) -{ - int ret = 0; - - if (fsm->protocol != protocol) { - VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", - fsm->protocol, protocol); - /* stop old protocol */ - if (fsm->protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 0); - else if (fsm->protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 0); - if (ret) - return ret; - - /* start new protocol */ - if (protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 1); - else if (protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 1); - if (ret) - return ret; - - fsm->protocol = protocol; - return 0; - } - - return 0; -} - -static int state_changed; - -/* Called when leaving a state. Do state clean up jobs here */ -void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) -{ - switch (old_state) { - case OTG_STATE_B_IDLE: - otg_del_timer(fsm, b_se0_srp_tmr); - fsm->b_se0_srp = 0; - break; - case OTG_STATE_B_SRP_INIT: - fsm->b_srp_done = 0; - break; - case OTG_STATE_B_PERIPHERAL: - break; - case OTG_STATE_B_WAIT_ACON: - otg_del_timer(fsm, b_ase0_brst_tmr); - fsm->b_ase0_brst_tmout = 0; - break; - case OTG_STATE_B_HOST: - break; - case OTG_STATE_A_IDLE: - break; - case OTG_STATE_A_WAIT_VRISE: - otg_del_timer(fsm, a_wait_vrise_tmr); - fsm->a_wait_vrise_tmout = 0; - break; - case OTG_STATE_A_WAIT_BCON: - otg_del_timer(fsm, a_wait_bcon_tmr); - fsm->a_wait_bcon_tmout = 0; - break; - case OTG_STATE_A_HOST: - otg_del_timer(fsm, a_wait_enum_tmr); - break; - case OTG_STATE_A_SUSPEND: - otg_del_timer(fsm, a_aidl_bdis_tmr); - fsm->a_aidl_bdis_tmout = 0; - fsm->a_suspend_req = 0; - break; - case OTG_STATE_A_PERIPHERAL: - break; - case OTG_STATE_A_WAIT_VFALL: - otg_del_timer(fsm, a_wait_vrise_tmr); - break; - case OTG_STATE_A_VBUS_ERR: - break; - default: - break; - } -} - -/* Called when entering a state */ -int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) -{ - state_changed = 1; - if (fsm->otg->phy->state == new_state) - return 0; - VDBG("Set state: %s\n", usb_otg_state_string(new_state)); - otg_leave_state(fsm, fsm->otg->phy->state); - switch (new_state) { - case OTG_STATE_B_IDLE: - otg_drv_vbus(fsm, 0); - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_se0_srp_tmr); - break; - case OTG_STATE_B_SRP_INIT: - otg_start_pulse(fsm); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_srp_fail_tmr); - break; - case OTG_STATE_B_PERIPHERAL: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 1); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_GADGET); - break; - case OTG_STATE_B_WAIT_ACON: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, b_ase0_brst_tmr); - fsm->a_bus_suspend = 0; - break; - case OTG_STATE_B_HOST: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 1); - otg_set_protocol(fsm, PROTO_HOST); - usb_bus_start_enum(fsm->otg->host, - fsm->otg->host->otg_port); - break; - case OTG_STATE_A_IDLE: - otg_drv_vbus(fsm, 0); - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - break; - case OTG_STATE_A_WAIT_VRISE: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_vrise_tmr); - break; - case OTG_STATE_A_WAIT_BCON: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_bcon_tmr); - break; - case OTG_STATE_A_HOST: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 1); - otg_set_protocol(fsm, PROTO_HOST); - /* - * When HNP is triggered while a_bus_req = 0, a_host will - * suspend too fast to complete a_set_b_hnp_en - */ - if (!fsm->a_bus_req || fsm->a_suspend_req) - otg_add_timer(fsm, a_wait_enum_tmr); - break; - case OTG_STATE_A_SUSPEND: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_aidl_bdis_tmr); - - break; - case OTG_STATE_A_PERIPHERAL: - otg_loc_conn(fsm, 1); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_GADGET); - otg_drv_vbus(fsm, 1); - break; - case OTG_STATE_A_WAIT_VFALL: - otg_drv_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - break; - case OTG_STATE_A_VBUS_ERR: - otg_drv_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - break; - default: - break; - } - - fsm->otg->phy->state = new_state; - return 0; -} - -/* State change judgement */ -int otg_statemachine(struct otg_fsm *fsm) -{ - enum usb_otg_state state; - unsigned long flags; - - spin_lock_irqsave(&fsm->lock, flags); - - state = fsm->otg->phy->state; - state_changed = 0; - /* State machine state change judgement */ - - switch (state) { - case OTG_STATE_UNDEFINED: - VDBG("fsm->id = %d\n", fsm->id); - if (fsm->id) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else - otg_set_state(fsm, OTG_STATE_A_IDLE); - break; - case OTG_STATE_B_IDLE: - if (!fsm->id) - otg_set_state(fsm, OTG_STATE_A_IDLE); - else if (fsm->b_sess_vld && fsm->otg->gadget) - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) - otg_set_state(fsm, OTG_STATE_B_SRP_INIT); - break; - case OTG_STATE_B_SRP_INIT: - if (!fsm->id || fsm->b_srp_done) - otg_set_state(fsm, OTG_STATE_B_IDLE); - break; - case OTG_STATE_B_PERIPHERAL: - if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (fsm->b_bus_req && fsm->otg-> - gadget->b_hnp_enable && fsm->a_bus_suspend) - otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); - break; - case OTG_STATE_B_WAIT_ACON: - if (fsm->a_conn) - otg_set_state(fsm, OTG_STATE_B_HOST); - else if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { - fsm->b_ase0_brst_tmout = 0; - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - } - break; - case OTG_STATE_B_HOST: - if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->b_bus_req || !fsm->a_conn) - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - break; - case OTG_STATE_A_IDLE: - if (fsm->id) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) - otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); - break; - case OTG_STATE_A_WAIT_VRISE: - if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || - fsm->a_wait_vrise_tmout) { - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - } - break; - case OTG_STATE_A_WAIT_BCON: - if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - else if (fsm->b_conn) - otg_set_state(fsm, OTG_STATE_A_HOST); - else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - break; - case OTG_STATE_A_HOST: - if ((!fsm->a_bus_req || fsm->a_suspend_req) && - fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_SUSPEND); - else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_SUSPEND: - if (!fsm->b_conn && fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); - else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (fsm->a_bus_req || fsm->b_bus_resume) - otg_set_state(fsm, OTG_STATE_A_HOST); - else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_PERIPHERAL: - if (fsm->id || fsm->a_bus_drop) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - else if (fsm->b_bus_suspend) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_WAIT_VFALL: - if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && - !fsm->b_conn)) - otg_set_state(fsm, OTG_STATE_A_IDLE); - break; - case OTG_STATE_A_VBUS_ERR: - if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - break; - default: - break; - } - spin_unlock_irqrestore(&fsm->lock, flags); - - VDBG("quit statemachine, changed = %d\n", state_changed); - return state_changed; -} diff --git a/drivers/usb/otg/otg_fsm.h b/drivers/usb/otg/otg_fsm.h deleted file mode 100644 index c30a2e1..0000000 --- a/drivers/usb/otg/otg_fsm.h +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#undef DEBUG -#undef VERBOSE - -#ifdef DEBUG -#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ - __func__, ## args) -#else -#define DBG(fmt, args...) do {} while (0) -#endif - -#ifdef VERBOSE -#define VDBG DBG -#else -#define VDBG(stuff...) do {} while (0) -#endif - -#ifdef VERBOSE -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) -#else -#define MPC_LOC do {} while (0) -#endif - -#define PROTO_UNDEF (0) -#define PROTO_HOST (1) -#define PROTO_GADGET (2) - -/* OTG state machine according to the OTG spec */ -struct otg_fsm { - /* Input */ - int a_bus_resume; - int a_bus_suspend; - int a_conn; - int a_sess_vld; - int a_srp_det; - int a_vbus_vld; - int b_bus_resume; - int b_bus_suspend; - int b_conn; - int b_se0_srp; - int b_sess_end; - int b_sess_vld; - int id; - - /* Internal variables */ - int a_set_b_hnp_en; - int b_srp_done; - int b_hnp_enable; - - /* Timeout indicator for timers */ - int a_wait_vrise_tmout; - int a_wait_bcon_tmout; - int a_aidl_bdis_tmout; - int b_ase0_brst_tmout; - - /* Informative variables */ - int a_bus_drop; - int a_bus_req; - int a_clr_err; - int a_suspend_req; - int b_bus_req; - - /* Output */ - int drv_vbus; - int loc_conn; - int loc_sof; - - struct otg_fsm_ops *ops; - struct usb_otg *otg; - - /* Current usb protocol used: 0:undefine; 1:host; 2:client */ - int protocol; - spinlock_t lock; -}; - -struct otg_fsm_ops { - void (*chrg_vbus)(int on); - void (*drv_vbus)(int on); - void (*loc_conn)(int on); - void (*loc_sof)(int on); - void (*start_pulse)(void); - void (*add_timer)(void *timer); - void (*del_timer)(void *timer); - int (*start_host)(struct otg_fsm *fsm, int on); - int (*start_gadget)(struct otg_fsm *fsm, int on); -}; - - -static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) -{ - fsm->ops->chrg_vbus(on); -} - -static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) -{ - if (fsm->drv_vbus != on) { - fsm->drv_vbus = on; - fsm->ops->drv_vbus(on); - } -} - -static inline void otg_loc_conn(struct otg_fsm *fsm, int on) -{ - if (fsm->loc_conn != on) { - fsm->loc_conn = on; - fsm->ops->loc_conn(on); - } -} - -static inline void otg_loc_sof(struct otg_fsm *fsm, int on) -{ - if (fsm->loc_sof != on) { - fsm->loc_sof = on; - fsm->ops->loc_sof(on); - } -} - -static inline void otg_start_pulse(struct otg_fsm *fsm) -{ - fsm->ops->start_pulse(); -} - -static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) -{ - fsm->ops->add_timer(timer); -} - -static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) -{ - fsm->ops->del_timer(timer); -} - -int otg_statemachine(struct otg_fsm *fsm); - -/* Defined by device specific driver, for different timer implementation */ -extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, - *a_wait_enum_tmr; diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c deleted file mode 100644 index a994715..0000000 --- a/drivers/usb/otg/twl4030-usb.c +++ /dev/null @@ -1,728 +0,0 @@ -/* - * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller - * - * Copyright (C) 2004-2007 Texas Instruments - * Copyright (C) 2008 Nokia Corporation - * Contact: Felipe Balbi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * - HS USB ULPI mode works. - * - 3-pin mode support may be added in future. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register defines */ - -#define MCPC_CTRL 0x30 -#define MCPC_CTRL_RTSOL (1 << 7) -#define MCPC_CTRL_EXTSWR (1 << 6) -#define MCPC_CTRL_EXTSWC (1 << 5) -#define MCPC_CTRL_VOICESW (1 << 4) -#define MCPC_CTRL_OUT64K (1 << 3) -#define MCPC_CTRL_RTSCTSSW (1 << 2) -#define MCPC_CTRL_HS_UART (1 << 0) - -#define MCPC_IO_CTRL 0x33 -#define MCPC_IO_CTRL_MICBIASEN (1 << 5) -#define MCPC_IO_CTRL_CTS_NPU (1 << 4) -#define MCPC_IO_CTRL_RXD_PU (1 << 3) -#define MCPC_IO_CTRL_TXDTYP (1 << 2) -#define MCPC_IO_CTRL_CTSTYP (1 << 1) -#define MCPC_IO_CTRL_RTSTYP (1 << 0) - -#define MCPC_CTRL2 0x36 -#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) - -#define OTHER_FUNC_CTRL 0x80 -#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) -#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) - -#define OTHER_IFC_CTRL 0x83 -#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) -#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) -#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) -#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) -#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) -#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) - -#define OTHER_INT_EN_RISE 0x86 -#define OTHER_INT_EN_FALL 0x89 -#define OTHER_INT_STS 0x8C -#define OTHER_INT_LATCH 0x8D -#define OTHER_INT_VB_SESS_VLD (1 << 7) -#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ -#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ -#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ -#define OTHER_INT_MANU (1 << 1) -#define OTHER_INT_ABNORMAL_STRESS (1 << 0) - -#define ID_STATUS 0x96 -#define ID_RES_FLOAT (1 << 4) -#define ID_RES_440K (1 << 3) -#define ID_RES_200K (1 << 2) -#define ID_RES_102K (1 << 1) -#define ID_RES_GND (1 << 0) - -#define POWER_CTRL 0xAC -#define POWER_CTRL_OTG_ENAB (1 << 5) - -#define OTHER_IFC_CTRL2 0xAF -#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) -#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) -#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) - -#define REG_CTRL_EN 0xB2 -#define REG_CTRL_ERROR 0xB5 -#define ULPI_I2C_CONFLICT_INTEN (1 << 0) - -#define OTHER_FUNC_CTRL2 0xB8 -#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) - -/* following registers do not have separate _clr and _set registers */ -#define VBUS_DEBOUNCE 0xC0 -#define ID_DEBOUNCE 0xC1 -#define VBAT_TIMER 0xD3 -#define PHY_PWR_CTRL 0xFD -#define PHY_PWR_PHYPWD (1 << 0) -#define PHY_CLK_CTRL 0xFE -#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) -#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) -#define REQ_PHY_DPLL_CLK (1 << 0) -#define PHY_CLK_CTRL_STS 0xFF -#define PHY_DPLL_CLK (1 << 0) - -/* In module TWL_MODULE_PM_MASTER */ -#define STS_HW_CONDITIONS 0x0F - -/* In module TWL_MODULE_PM_RECEIVER */ -#define VUSB_DEDICATED1 0x7D -#define VUSB_DEDICATED2 0x7E -#define VUSB1V5_DEV_GRP 0x71 -#define VUSB1V5_TYPE 0x72 -#define VUSB1V5_REMAP 0x73 -#define VUSB1V8_DEV_GRP 0x74 -#define VUSB1V8_TYPE 0x75 -#define VUSB1V8_REMAP 0x76 -#define VUSB3V1_DEV_GRP 0x77 -#define VUSB3V1_TYPE 0x78 -#define VUSB3V1_REMAP 0x79 - -/* In module TWL4030_MODULE_INTBR */ -#define PMBR1 0x0D -#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) - -struct twl4030_usb { - struct usb_phy phy; - struct device *dev; - - /* TWL4030 internal USB regulator supplies */ - struct regulator *usb1v5; - struct regulator *usb1v8; - struct regulator *usb3v1; - - /* for vbus reporting with irqs disabled */ - spinlock_t lock; - - /* pin configuration */ - enum twl4030_usb_mode usb_mode; - - int irq; - enum omap_musb_vbus_id_status linkstat; - bool vbus_supplied; - u8 asleep; - bool irq_enabled; -}; - -/* internal define on top of container_of */ -#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) - -/*-------------------------------------------------------------------------*/ - -static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, - u8 module, u8 data, u8 address) -{ - u8 check; - - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 1, module, address, check, data); - - /* Failed once: Try again */ - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 2, module, address, check, data); - - /* Failed again: Return error */ - return -EBUSY; -} - -#define twl4030_usb_write_verify(twl, address, data) \ - twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) - -static inline int twl4030_usb_write(struct twl4030_usb *twl, - u8 address, u8 data) -{ - int ret = 0; - - ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); - if (ret < 0) - dev_dbg(twl->dev, - "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); - return ret; -} - -static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) -{ - u8 data; - int ret = 0; - - ret = twl_i2c_read_u8(module, &data, address); - if (ret >= 0) - ret = data; - else - dev_dbg(twl->dev, - "TWL4030:readb[0x%x,0x%x] Error %d\n", - module, address, ret); - - return ret; -} - -static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) -{ - return twl4030_readb(twl, TWL_MODULE_USB, address); -} - -/*-------------------------------------------------------------------------*/ - -static inline int -twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_SET(reg), bits); -} - -static inline int -twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_CLR(reg), bits); -} - -/*-------------------------------------------------------------------------*/ - -static enum omap_musb_vbus_id_status - twl4030_usb_linkstat(struct twl4030_usb *twl) -{ - int status; - enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; - - twl->vbus_supplied = false; - - /* - * For ID/VBUS sensing, see manual section 15.4.8 ... - * except when using only battery backup power, two - * comparators produce VBUS_PRES and ID_PRES signals, - * which don't match docs elsewhere. But ... BIT(7) - * and BIT(2) of STS_HW_CONDITIONS, respectively, do - * seem to match up. If either is true the USB_PRES - * signal is active, the OTG module is activated, and - * its interrupt may be raised (may wake the system). - */ - status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); - if (status < 0) - dev_err(twl->dev, "USB link status err %d\n", status); - else if (status & (BIT(7) | BIT(2))) { - if (status & (BIT(7))) - twl->vbus_supplied = true; - - if (status & BIT(2)) - linkstat = OMAP_MUSB_ID_GROUND; - else - linkstat = OMAP_MUSB_VBUS_VALID; - } else { - if (twl->linkstat != OMAP_MUSB_UNKNOWN) - linkstat = OMAP_MUSB_VBUS_OFF; - } - - dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", - status, status, linkstat); - - /* REVISIT this assumes host and peripheral controllers - * are registered, and that both are active... - */ - - spin_lock_irq(&twl->lock); - twl->linkstat = linkstat; - spin_unlock_irq(&twl->lock); - - return linkstat; -} - -static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) -{ - twl->usb_mode = mode; - - switch (mode) { - case T2_USB_MODE_ULPI: - twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, - ULPI_IFC_CTRL_CARKITMODE); - twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, - ULPI_FUNC_CTRL_XCVRSEL_MASK | - ULPI_FUNC_CTRL_OPMODE_MASK); - break; - case -1: - /* FIXME: power on defaults */ - break; - default: - dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", - mode); - break; - }; -} - -static void twl4030_i2c_access(struct twl4030_usb *twl, int on) -{ - unsigned long timeout; - int val = twl4030_usb_read(twl, PHY_CLK_CTRL); - - if (val >= 0) { - if (on) { - /* enable DPLL to access PHY registers over I2C */ - val |= REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - - timeout = jiffies + HZ; - while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK) - && time_before(jiffies, timeout)) - udelay(10); - if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK)) - dev_err(twl->dev, "Timeout setting T2 HSUSB " - "PHY DPLL clock\n"); - } else { - /* let ULPI control the DPLL clock */ - val &= ~REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - } - } -} - -static void __twl4030_phy_power(struct twl4030_usb *twl, int on) -{ - u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); - - if (on) - pwr &= ~PHY_PWR_PHYPWD; - else - pwr |= PHY_PWR_PHYPWD; - - WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); -} - -static void twl4030_phy_power(struct twl4030_usb *twl, int on) -{ - if (on) { - regulator_enable(twl->usb3v1); - regulator_enable(twl->usb1v8); - /* - * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP - * in twl4030) resets the VUSB_DEDICATED2 register. This reset - * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to - * SLEEP. We work around this by clearing the bit after usv3v1 - * is re-activated. This ensures that VUSB3V1 is really active. - */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); - regulator_enable(twl->usb1v5); - __twl4030_phy_power(twl, 1); - twl4030_usb_write(twl, PHY_CLK_CTRL, - twl4030_usb_read(twl, PHY_CLK_CTRL) | - (PHY_CLK_CTRL_CLOCKGATING_EN | - PHY_CLK_CTRL_CLK32K_EN)); - } else { - __twl4030_phy_power(twl, 0); - regulator_disable(twl->usb1v5); - regulator_disable(twl->usb1v8); - regulator_disable(twl->usb3v1); - } -} - -static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) -{ - if (twl->asleep) - return; - - twl4030_phy_power(twl, 0); - twl->asleep = 1; - dev_dbg(twl->dev, "%s\n", __func__); -} - -static void __twl4030_phy_resume(struct twl4030_usb *twl) -{ - twl4030_phy_power(twl, 1); - twl4030_i2c_access(twl, 1); - twl4030_usb_set_mode(twl, twl->usb_mode); - if (twl->usb_mode == T2_USB_MODE_ULPI) - twl4030_i2c_access(twl, 0); -} - -static void twl4030_phy_resume(struct twl4030_usb *twl) -{ - if (!twl->asleep) - return; - __twl4030_phy_resume(twl); - twl->asleep = 0; - dev_dbg(twl->dev, "%s\n", __func__); -} - -static int twl4030_usb_ldo_init(struct twl4030_usb *twl) -{ - /* Enable writing to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); - - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); - - /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ - /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ - - /* input to VUSB3V1 LDO is from VBAT, not VBUS */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); - - /* Initialize 3.1V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); - - twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); - if (IS_ERR(twl->usb3v1)) - return -ENODEV; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); - - /* Initialize 1.5V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); - - twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); - if (IS_ERR(twl->usb1v5)) - goto fail1; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); - - /* Initialize 1.8V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); - - twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); - if (IS_ERR(twl->usb1v8)) - goto fail2; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); - - /* disable access to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); - - return 0; - -fail2: - regulator_put(twl->usb1v5); - twl->usb1v5 = NULL; -fail1: - regulator_put(twl->usb3v1); - twl->usb3v1 = NULL; - return -ENODEV; -} - -static ssize_t twl4030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct twl4030_usb *twl = dev_get_drvdata(dev); - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&twl->lock, flags); - ret = sprintf(buf, "%s\n", - twl->vbus_supplied ? "on" : "off"); - spin_unlock_irqrestore(&twl->lock, flags); - - return ret; -} -static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); - -static irqreturn_t twl4030_usb_irq(int irq, void *_twl) -{ - struct twl4030_usb *twl = _twl; - enum omap_musb_vbus_id_status status; - - status = twl4030_usb_linkstat(twl); - if (status > 0) { - /* FIXME add a set_power() method so that B-devices can - * configure the charger appropriately. It's not always - * correct to consume VBUS power, and how much current to - * consume is a function of the USB configuration chosen - * by the host. - * - * REVISIT usb_gadget_vbus_connect(...) as needed, ditto - * its disconnect() sibling, when changing to/from the - * USB_LINK_VBUS state. musb_hdrc won't care until it - * starts to handle softconnect right. - */ - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) - twl4030_phy_suspend(twl, 0); - else - twl4030_phy_resume(twl); - - omap_musb_mailbox(twl->linkstat); - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); - - return IRQ_HANDLED; -} - -static void twl4030_usb_phy_init(struct twl4030_usb *twl) -{ - enum omap_musb_vbus_id_status status; - - status = twl4030_usb_linkstat(twl); - if (status > 0) { - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) { - __twl4030_phy_power(twl, 0); - twl->asleep = 1; - } else { - __twl4030_phy_resume(twl); - twl->asleep = 0; - } - - omap_musb_mailbox(twl->linkstat); - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); -} - -static int twl4030_set_suspend(struct usb_phy *x, int suspend) -{ - struct twl4030_usb *twl = phy_to_twl(x); - - if (suspend) - twl4030_phy_suspend(twl, 1); - else - twl4030_phy_resume(twl); - - return 0; -} - -static int twl4030_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - otg->gadget = gadget; - if (!gadget) - otg->phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - otg->host = host; - if (!host) - otg->phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int twl4030_usb_probe(struct platform_device *pdev) -{ - struct twl4030_usb_data *pdata = pdev->dev.platform_data; - struct twl4030_usb *twl; - int status, err; - struct usb_otg *otg; - struct device_node *np = pdev->dev.of_node; - - twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); - if (!twl) - return -ENOMEM; - - if (np) - of_property_read_u32(np, "usb_mode", - (enum twl4030_usb_mode *)&twl->usb_mode); - else if (pdata) - twl->usb_mode = pdata->usb_mode; - else { - dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); - return -EINVAL; - } - - otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); - if (!otg) - return -ENOMEM; - - twl->dev = &pdev->dev; - twl->irq = platform_get_irq(pdev, 0); - twl->vbus_supplied = false; - twl->asleep = 1; - twl->linkstat = OMAP_MUSB_UNKNOWN; - - twl->phy.dev = twl->dev; - twl->phy.label = "twl4030"; - twl->phy.otg = otg; - twl->phy.type = USB_PHY_TYPE_USB2; - twl->phy.set_suspend = twl4030_set_suspend; - - otg->phy = &twl->phy; - otg->set_host = twl4030_set_host; - otg->set_peripheral = twl4030_set_peripheral; - - /* init spinlock for workqueue */ - spin_lock_init(&twl->lock); - - err = twl4030_usb_ldo_init(twl); - if (err) { - dev_err(&pdev->dev, "ldo init failed\n"); - return err; - } - usb_add_phy_dev(&twl->phy); - - platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); - - /* Our job is to use irqs and status from the power module - * to keep the transceiver disabled when nothing's connected. - * - * FIXME we actually shouldn't start enabling it until the - * USB controller drivers have said they're ready, by calling - * set_host() and/or set_peripheral() ... OTG_capable boards - * need both handles, otherwise just one suffices. - */ - twl->irq_enabled = true; - status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT, "twl4030_usb", twl); - if (status < 0) { - dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq, status); - return status; - } - - /* Power down phy or make it work according to - * current link state. - */ - twl4030_usb_phy_init(twl); - - dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); - return 0; -} - -static int __exit twl4030_usb_remove(struct platform_device *pdev) -{ - struct twl4030_usb *twl = platform_get_drvdata(pdev); - int val; - - free_irq(twl->irq, twl); - device_remove_file(twl->dev, &dev_attr_vbus); - - /* set transceiver mode to power on defaults */ - twl4030_usb_set_mode(twl, -1); - - /* autogate 60MHz ULPI clock, - * clear dpll clock request for i2c access, - * disable 32KHz - */ - val = twl4030_usb_read(twl, PHY_CLK_CTRL); - if (val >= 0) { - val |= PHY_CLK_CTRL_CLOCKGATING_EN; - val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); - twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); - } - - /* disable complete OTG block */ - twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - - if (!twl->asleep) - twl4030_phy_power(twl, 0); - regulator_put(twl->usb1v5); - regulator_put(twl->usb1v8); - regulator_put(twl->usb3v1); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl4030_usb_id_table[] = { - { .compatible = "ti,twl4030-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); -#endif - -static struct platform_driver twl4030_usb_driver = { - .probe = twl4030_usb_probe, - .remove = __exit_p(twl4030_usb_remove), - .driver = { - .name = "twl4030_usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl4030_usb_id_table), - }, -}; - -static int __init twl4030_usb_init(void) -{ - return platform_driver_register(&twl4030_usb_driver); -} -subsys_initcall(twl4030_usb_init); - -static void __exit twl4030_usb_exit(void) -{ - platform_driver_unregister(&twl4030_usb_driver); -} -module_exit(twl4030_usb_exit); - -MODULE_ALIAS("platform:twl4030_usb"); -MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); -MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c deleted file mode 100644 index 8cd6cf4..0000000 --- a/drivers/usb/otg/twl6030-usb.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. - * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Hema HK - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* usb register definitions */ -#define USB_VENDOR_ID_LSB 0x00 -#define USB_VENDOR_ID_MSB 0x01 -#define USB_PRODUCT_ID_LSB 0x02 -#define USB_PRODUCT_ID_MSB 0x03 -#define USB_VBUS_CTRL_SET 0x04 -#define USB_VBUS_CTRL_CLR 0x05 -#define USB_ID_CTRL_SET 0x06 -#define USB_ID_CTRL_CLR 0x07 -#define USB_VBUS_INT_SRC 0x08 -#define USB_VBUS_INT_LATCH_SET 0x09 -#define USB_VBUS_INT_LATCH_CLR 0x0A -#define USB_VBUS_INT_EN_LO_SET 0x0B -#define USB_VBUS_INT_EN_LO_CLR 0x0C -#define USB_VBUS_INT_EN_HI_SET 0x0D -#define USB_VBUS_INT_EN_HI_CLR 0x0E -#define USB_ID_INT_SRC 0x0F -#define USB_ID_INT_LATCH_SET 0x10 -#define USB_ID_INT_LATCH_CLR 0x11 - -#define USB_ID_INT_EN_LO_SET 0x12 -#define USB_ID_INT_EN_LO_CLR 0x13 -#define USB_ID_INT_EN_HI_SET 0x14 -#define USB_ID_INT_EN_HI_CLR 0x15 -#define USB_OTG_ADP_CTRL 0x16 -#define USB_OTG_ADP_HIGH 0x17 -#define USB_OTG_ADP_LOW 0x18 -#define USB_OTG_ADP_RISE 0x19 -#define USB_OTG_REVISION 0x1A - -/* to be moved to LDO */ -#define TWL6030_MISC2 0xE5 -#define TWL6030_CFG_LDO_PD2 0xF5 -#define TWL6030_BACKUP_REG 0xFA - -#define STS_HW_CONDITIONS 0x21 - -/* In module TWL6030_MODULE_PM_MASTER */ -#define STS_HW_CONDITIONS 0x21 -#define STS_USB_ID BIT(2) - -/* In module TWL6030_MODULE_PM_RECEIVER */ -#define VUSB_CFG_TRANS 0x71 -#define VUSB_CFG_STATE 0x72 -#define VUSB_CFG_VOLTAGE 0x73 - -/* in module TWL6030_MODULE_MAIN_CHARGE */ - -#define CHARGERUSB_CTRL1 0x8 - -#define CONTROLLER_STAT1 0x03 -#define VBUS_DET BIT(2) - -struct twl6030_usb { - struct phy_companion comparator; - struct device *dev; - - /* for vbus reporting with irqs disabled */ - spinlock_t lock; - - struct regulator *usb3v3; - - /* used to set vbus, in atomic path */ - struct work_struct set_vbus_work; - - int irq1; - int irq2; - enum omap_musb_vbus_id_status linkstat; - u8 asleep; - bool irq_enabled; - bool vbus_enable; - const char *regulator; -}; - -#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator) - -/*-------------------------------------------------------------------------*/ - -static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, - u8 data, u8 address) -{ - int ret = 0; - - ret = twl_i2c_write_u8(module, data, address); - if (ret < 0) - dev_err(twl->dev, - "Write[0x%x] Error %d\n", address, ret); - return ret; -} - -static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) -{ - u8 data, ret = 0; - - ret = twl_i2c_read_u8(module, &data, address); - if (ret >= 0) - ret = data; - else - dev_err(twl->dev, - "readb[0x%x,0x%x] Error %d\n", - module, address, ret); - return ret; -} - -static int twl6030_start_srp(struct phy_companion *comparator) -{ - struct twl6030_usb *twl = comparator_to_twl(comparator); - - twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); - twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); - - mdelay(100); - twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); - - return 0; -} - -static int twl6030_usb_ldo_init(struct twl6030_usb *twl) -{ - /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); - - /* Program CFG_LDO_PD2 register and set VUSB bit */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2); - - /* Program MISC2 register and set bit VUSB_IN_VBAT */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); - - twl->usb3v3 = regulator_get(twl->dev, twl->regulator); - if (IS_ERR(twl->usb3v3)) - return -ENODEV; - - /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ - twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); - - /* - * Program the USB_ID_CTRL_SET register to enable GND drive - * and the ID comparators - */ - twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); - - return 0; -} - -static ssize_t twl6030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct twl6030_usb *twl = dev_get_drvdata(dev); - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&twl->lock, flags); - - switch (twl->linkstat) { - case OMAP_MUSB_VBUS_VALID: - ret = snprintf(buf, PAGE_SIZE, "vbus\n"); - break; - case OMAP_MUSB_ID_GROUND: - ret = snprintf(buf, PAGE_SIZE, "id\n"); - break; - case OMAP_MUSB_VBUS_OFF: - ret = snprintf(buf, PAGE_SIZE, "none\n"); - break; - default: - ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); - } - spin_unlock_irqrestore(&twl->lock, flags); - - return ret; -} -static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); - -static irqreturn_t twl6030_usb_irq(int irq, void *_twl) -{ - struct twl6030_usb *twl = _twl; - enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; - u8 vbus_state, hw_state; - - hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); - - vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, - CONTROLLER_STAT1); - if (!(hw_state & STS_USB_ID)) { - if (vbus_state & VBUS_DET) { - regulator_enable(twl->usb3v3); - twl->asleep = 1; - status = OMAP_MUSB_VBUS_VALID; - twl->linkstat = status; - omap_musb_mailbox(status); - } else { - if (twl->linkstat != OMAP_MUSB_UNKNOWN) { - status = OMAP_MUSB_VBUS_OFF; - twl->linkstat = status; - omap_musb_mailbox(status); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; - } - } - } - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); - - return IRQ_HANDLED; -} - -static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) -{ - struct twl6030_usb *twl = _twl; - enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; - u8 hw_state; - - hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); - - if (hw_state & STS_USB_ID) { - - regulator_enable(twl->usb3v3); - twl->asleep = 1; - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); - twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); - status = OMAP_MUSB_ID_GROUND; - twl->linkstat = status; - omap_musb_mailbox(status); - } else { - twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); - } - twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); - - return IRQ_HANDLED; -} - -static int twl6030_enable_irq(struct twl6030_usb *twl) -{ - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); - twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); - twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); - - twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, - REG_INT_MSK_LINE_C); - twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, - REG_INT_MSK_STS_C); - twl6030_usb_irq(twl->irq2, twl); - twl6030_usbotg_irq(twl->irq1, twl); - - return 0; -} - -static void otg_set_vbus_work(struct work_struct *data) -{ - struct twl6030_usb *twl = container_of(data, struct twl6030_usb, - set_vbus_work); - - /* - * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 - * register. This enables boost mode. - */ - - if (twl->vbus_enable) - twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40, - CHARGERUSB_CTRL1); - else - twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00, - CHARGERUSB_CTRL1); -} - -static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) -{ - struct twl6030_usb *twl = comparator_to_twl(comparator); - - twl->vbus_enable = enabled; - schedule_work(&twl->set_vbus_work); - - return 0; -} - -static int twl6030_usb_probe(struct platform_device *pdev) -{ - u32 ret; - struct twl6030_usb *twl; - int status, err; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - struct twl4030_usb_data *pdata = dev->platform_data; - - twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); - if (!twl) - return -ENOMEM; - - twl->dev = &pdev->dev; - twl->irq1 = platform_get_irq(pdev, 0); - twl->irq2 = platform_get_irq(pdev, 1); - twl->linkstat = OMAP_MUSB_UNKNOWN; - - twl->comparator.set_vbus = twl6030_set_vbus; - twl->comparator.start_srp = twl6030_start_srp; - - ret = omap_usb2_set_comparator(&twl->comparator); - if (ret == -ENODEV) { - dev_info(&pdev->dev, "phy not ready, deferring probe"); - return -EPROBE_DEFER; - } - - if (np) { - twl->regulator = "usb"; - } else if (pdata) { - if (pdata->features & TWL6025_SUBCLASS) - twl->regulator = "ldousb"; - else - twl->regulator = "vusb"; - } else { - dev_err(&pdev->dev, "twl6030 initialized without pdata\n"); - return -EINVAL; - } - - /* init spinlock for workqueue */ - spin_lock_init(&twl->lock); - - err = twl6030_usb_ldo_init(twl); - if (err) { - dev_err(&pdev->dev, "ldo init failed\n"); - return err; - } - - platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); - - INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); - - twl->irq_enabled = true; - status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "twl6030_usb", twl); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq1, status); - device_remove_file(twl->dev, &dev_attr_vbus); - return status; - } - - status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "twl6030_usb", twl); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq2, status); - free_irq(twl->irq1, twl); - device_remove_file(twl->dev, &dev_attr_vbus); - return status; - } - - twl->asleep = 0; - twl6030_enable_irq(twl); - dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); - - return 0; -} - -static int __exit twl6030_usb_remove(struct platform_device *pdev) -{ - struct twl6030_usb *twl = platform_get_drvdata(pdev); - - twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, - REG_INT_MSK_LINE_C); - twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, - REG_INT_MSK_STS_C); - free_irq(twl->irq1, twl); - free_irq(twl->irq2, twl); - regulator_put(twl->usb3v3); - device_remove_file(twl->dev, &dev_attr_vbus); - cancel_work_sync(&twl->set_vbus_work); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl6030_usb_id_table[] = { - { .compatible = "ti,twl6030-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); -#endif - -static struct platform_driver twl6030_usb_driver = { - .probe = twl6030_usb_probe, - .remove = __exit_p(twl6030_usb_remove), - .driver = { - .name = "twl6030_usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl6030_usb_id_table), - }, -}; - -static int __init twl6030_usb_init(void) -{ - return platform_driver_register(&twl6030_usb_driver); -} -subsys_initcall(twl6030_usb_init); - -static void __exit twl6030_usb_exit(void) -{ - platform_driver_unregister(&twl6030_usb_driver); -} -module_exit(twl6030_usb_exit); - -MODULE_ALIAS("platform:twl6030_usb"); -MODULE_AUTHOR("Hema HK "); -MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c deleted file mode 100644 index 217339d..0000000 --- a/drivers/usb/otg/ulpi.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Generic ULPI USB transceiver support - * - * Copyright (C) 2009 Daniel Mack - * - * Based on sources from - * - * Sascha Hauer - * Freescale Semiconductors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - - -struct ulpi_info { - unsigned int id; - char *name; -}; - -#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) -#define ULPI_INFO(_id, _name) \ - { \ - .id = (_id), \ - .name = (_name), \ - } - -/* ULPI hardcoded IDs, used for probing */ -static struct ulpi_info ulpi_ids[] = { - ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), - ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), -}; - -static int ulpi_set_otg_flags(struct usb_phy *phy) -{ - unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | - ULPI_OTG_CTRL_DM_PULLDOWN; - - if (phy->flags & ULPI_OTG_ID_PULLUP) - flags |= ULPI_OTG_CTRL_ID_PULLUP; - - /* - * ULPI Specification rev.1.1 default - * for Dp/DmPulldown is enabled. - */ - if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) - flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; - - if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) - flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; - - if (phy->flags & ULPI_OTG_EXTVBUSIND) - flags |= ULPI_OTG_CTRL_EXTVBUSIND; - - return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); -} - -static int ulpi_set_fc_flags(struct usb_phy *phy) -{ - unsigned int flags = 0; - - /* - * ULPI Specification rev.1.1 default - * for XcvrSelect is Full Speed. - */ - if (phy->flags & ULPI_FC_HS) - flags |= ULPI_FUNC_CTRL_HIGH_SPEED; - else if (phy->flags & ULPI_FC_LS) - flags |= ULPI_FUNC_CTRL_LOW_SPEED; - else if (phy->flags & ULPI_FC_FS4LS) - flags |= ULPI_FUNC_CTRL_FS4LS; - else - flags |= ULPI_FUNC_CTRL_FULL_SPEED; - - if (phy->flags & ULPI_FC_TERMSEL) - flags |= ULPI_FUNC_CTRL_TERMSELECT; - - /* - * ULPI Specification rev.1.1 default - * for OpMode is Normal Operation. - */ - if (phy->flags & ULPI_FC_OP_NODRV) - flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - else if (phy->flags & ULPI_FC_OP_DIS_NRZI) - flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; - else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) - flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; - else - flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - - /* - * ULPI Specification rev.1.1 default - * for SuspendM is Powered. - */ - flags |= ULPI_FUNC_CTRL_SUSPENDM; - - return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); -} - -static int ulpi_set_ic_flags(struct usb_phy *phy) -{ - unsigned int flags = 0; - - if (phy->flags & ULPI_IC_AUTORESUME) - flags |= ULPI_IFC_CTRL_AUTORESUME; - - if (phy->flags & ULPI_IC_EXTVBUS_INDINV) - flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; - - if (phy->flags & ULPI_IC_IND_PASSTHRU) - flags |= ULPI_IFC_CTRL_PASSTHRU; - - if (phy->flags & ULPI_IC_PROTECT_DIS) - flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; - - return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); -} - -static int ulpi_set_flags(struct usb_phy *phy) -{ - int ret; - - ret = ulpi_set_otg_flags(phy); - if (ret) - return ret; - - ret = ulpi_set_ic_flags(phy); - if (ret) - return ret; - - return ulpi_set_fc_flags(phy); -} - -static int ulpi_check_integrity(struct usb_phy *phy) -{ - int ret, i; - unsigned int val = 0x55; - - for (i = 0; i < 2; i++) { - ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); - if (ret < 0) - return ret; - - ret = usb_phy_io_read(phy, ULPI_SCRATCH); - if (ret < 0) - return ret; - - if (ret != val) { - pr_err("ULPI integrity check: failed!"); - return -ENODEV; - } - val = val << 1; - } - - pr_info("ULPI integrity check: passed.\n"); - - return 0; -} - -static int ulpi_init(struct usb_phy *phy) -{ - int i, vid, pid, ret; - u32 ulpi_id = 0; - - for (i = 0; i < 4; i++) { - ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); - if (ret < 0) - return ret; - ulpi_id = (ulpi_id << 8) | ret; - } - vid = ulpi_id & 0xffff; - pid = ulpi_id >> 16; - - pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); - - for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) { - if (ulpi_ids[i].id == ULPI_ID(vid, pid)) { - pr_info("Found %s ULPI transceiver.\n", - ulpi_ids[i].name); - break; - } - } - - ret = ulpi_check_integrity(phy); - if (ret) - return ret; - - return ulpi_set_flags(phy); -} - -static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct usb_phy *phy = otg->phy; - unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); - - if (!host) { - otg->host = NULL; - return 0; - } - - otg->host = host; - - flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE | - ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | - ULPI_IFC_CTRL_CARKITMODE); - - if (phy->flags & ULPI_IC_6PIN_SERIAL) - flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; - else if (phy->flags & ULPI_IC_3PIN_SERIAL) - flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; - else if (phy->flags & ULPI_IC_CARKIT) - flags |= ULPI_IFC_CTRL_CARKITMODE; - - return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); -} - -static int ulpi_set_vbus(struct usb_otg *otg, bool on) -{ - struct usb_phy *phy = otg->phy; - unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); - - flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); - - if (on) { - if (phy->flags & ULPI_OTG_DRVVBUS) - flags |= ULPI_OTG_CTRL_DRVVBUS; - - if (phy->flags & ULPI_OTG_DRVVBUS_EXT) - flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; - } - - return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); -} - -struct usb_phy * -otg_ulpi_create(struct usb_phy_io_ops *ops, - unsigned int flags) -{ - struct usb_phy *phy; - struct usb_otg *otg; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return NULL; - - otg = kzalloc(sizeof(*otg), GFP_KERNEL); - if (!otg) { - kfree(phy); - return NULL; - } - - phy->label = "ULPI"; - phy->flags = flags; - phy->io_ops = ops; - phy->otg = otg; - phy->init = ulpi_init; - - otg->phy = phy; - otg->set_host = ulpi_set_host; - otg->set_vbus = ulpi_set_vbus; - - return phy; -} -EXPORT_SYMBOL_GPL(otg_ulpi_create); - diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c deleted file mode 100644 index c5ba7e5..0000000 --- a/drivers/usb/otg/ulpi_viewport.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include - -#define ULPI_VIEW_WAKEUP (1 << 31) -#define ULPI_VIEW_RUN (1 << 30) -#define ULPI_VIEW_WRITE (1 << 29) -#define ULPI_VIEW_READ (0 << 29) -#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) -#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) -#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) - -static int ulpi_viewport_wait(void __iomem *view, u32 mask) -{ - unsigned long usec = 2000; - - while (usec--) { - if (!(readl(view) & mask)) - return 0; - - udelay(1); - }; - - return -ETIMEDOUT; -} - -static int ulpi_viewport_read(struct usb_phy *otg, u32 reg) -{ - int ret; - void __iomem *view = otg->io_priv; - - writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); - if (ret) - return ret; - - writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); - if (ret) - return ret; - - return ULPI_VIEW_DATA_READ(readl(view)); -} - -static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) -{ - int ret; - void __iomem *view = otg->io_priv; - - writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); - if (ret) - return ret; - - writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | - ULPI_VIEW_ADDR(reg), view); - - return ulpi_viewport_wait(view, ULPI_VIEW_RUN); -} - -struct usb_phy_io_ops ulpi_viewport_access_ops = { - .read = ulpi_viewport_read, - .write = ulpi_viewport_write, -}; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 65217a5..32ce740 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -4,6 +4,73 @@ comment "USB Physical Layer drivers" depends on USB || USB_GADGET +config USB_OTG_UTILS + bool + help + Select this to make sure the build includes objects from + the OTG infrastructure directory. + +if USB || USB_GADGET + +# +# USB Transceiver Drivers +# +config AB8500_USB + tristate "AB8500 USB Transceiver Driver" + depends on AB8500_CORE + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver in AB8500 chip. + This transceiver supports high and full speed devices plus, + in host mode, low speed. + +config FSL_USB2_OTG + bool "Freescale USB OTG Transceiver Driver" + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND + select USB_OTG + select USB_OTG_UTILS + help + Enable this to support Freescale USB OTG transceiver. + +config ISP1301_OMAP + tristate "Philips ISP1301 with OMAP OTG" + depends on I2C && ARCH_OMAP_OTG + select USB_OTG_UTILS + help + If you say yes here you get support for the Philips ISP1301 + USB-On-The-Go transceiver working with the OMAP OTG controller. + The ISP1301 is a full speed USB transceiver which is used in + products including H2, H3, and H4 development boards for Texas + Instruments OMAP processors. + + This driver can also be built as a module. If so, the module + will be called isp1301_omap. + +config MV_U3D_PHY + bool "Marvell USB 3.0 PHY controller Driver" + depends on USB_MV_U3D + select USB_OTG_UTILS + help + Enable this to support Marvell USB 3.0 phy controller for Marvell + SoC. + +config NOP_USB_XCEIV + tristate "NOP USB Transceiver Driver" + select USB_OTG_UTILS + help + This driver is to be used by all the usb transceiver which are either + built-in with usb ip or which are autonomous and doesn't require any + phy programming such as ISP1x04 etc. + +config OMAP_CONTROL_USB + tristate "OMAP CONTROL USB Driver" + help + Enable this to add support for the USB part present in the control + module. This driver has API to power on the USB2 PHY and to write to + the mailbox. The mailbox is present only in omap4 and the register to + power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an + additional register to power on USB3 PHY. + config OMAP_USB2 tristate "OMAP USB2 PHY Driver" depends on ARCH_OMAP2PLUS @@ -25,14 +92,45 @@ config OMAP_USB3 This driver interacts with the "OMAP Control USB Driver" to power on/off the PHY. -config OMAP_CONTROL_USB - tristate "OMAP CONTROL USB Driver" +config SAMSUNG_USBPHY + bool "Samsung USB PHY controller Driver" + depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS + select USB_OTG_UTILS help - Enable this to add support for the USB part present in the control - module. This driver has API to power on the USB2 PHY and to write to - the mailbox. The mailbox is present only in omap4 and the register to - power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an - additional register to power on USB3 PHY. + Enable this to support Samsung USB phy controller for samsung + SoCs. + +config TWL4030_USB + tristate "TWL4030 USB Transceiver Driver" + depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver on TWL4030 + family chips (including the TWL5030 and TPS659x0 devices). + This transceiver supports high and full speed devices plus, + in host mode, low speed. + +config TWL6030_USB + tristate "TWL6030 USB Transceiver Driver" + depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver on TWL6030 + family chips. This TWL6030 transceiver has the VBUS and ID GND + and OTG SRP events capabilities. For all other transceiver functionality + UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs + are hooked to this driver through platform_data structure. + The definition of internal PHY APIs are in the mach-omap2 layer. + +config USB_GPIO_VBUS + tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" + depends on GENERIC_GPIO + select USB_OTG_UTILS + help + Provides simple GPIO VBUS sensing for controllers with an + internal transceiver via the usb_phy interface, and + optionally control of a D+ pullup GPIO as well as a VBUS + current limit regulator. config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" @@ -46,13 +144,40 @@ config USB_ISP1301 To compile this driver as a module, choose M here: the module will be called isp1301. -config MV_U3D_PHY - bool "Marvell USB 3.0 PHY controller Driver" - depends on USB_MV_U3D +config USB_MSM_OTG + tristate "OTG support for Qualcomm on-chip USB controller" + depends on (USB || USB_GADGET) && ARCH_MSM select USB_OTG_UTILS help - Enable this to support Marvell USB 3.0 phy controller for Marvell - SoC. + Enable this to support the USB OTG transceiver on MSM chips. It + handles PHY initialization, clock management, and workarounds + required after resetting the hardware and power management. + This driver is required even for peripheral only or host only + mode configurations. + This driver is not supported on boards like trout which + has an external PHY. + +config USB_MV_OTG + tristate "Marvell USB OTG support" + depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND + select USB_OTG + select USB_OTG_UTILS + help + Say Y here if you want to build Marvell USB OTG transciever + driver in kernel (including PXA and MMP series). This driver + implements role switch between EHCI host driver and gadget driver. + + To compile this driver as a module, choose M here. + +config USB_MXS_PHY + tristate "Freescale MXS USB PHY support" + depends on ARCH_MXC || ARCH_MXS + select STMP_DEVICE + select USB_OTG_UTILS + help + Enable this to support the Freescale MXS USB PHY. + + MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x. config USB_RCAR_PHY tristate "Renesas R-Car USB phy support" @@ -66,10 +191,19 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called rcar-phy. -config SAMSUNG_USBPHY - bool "Samsung USB PHY controller Driver" - depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS +config USB_ULPI + bool "Generic ULPI Transceiver Driver" + depends on ARM select USB_OTG_UTILS help - Enable this to support Samsung USB phy controller for samsung - SoCs. + Enable this to support ULPI connected USB OTG transceivers which + are likely found on embedded boards. + +config USB_ULPI_VIEWPORT + bool + depends on USB_ULPI + help + Provides read/write operations to the ULPI phy register set for + controllers with a viewport register (e.g. Chipidea/ARC controllers). + +endif # USB || OTG diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 9fa6327..34488ce 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -5,11 +5,27 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG obj-$(CONFIG_USB_OTG_UTILS) += phy.o + +# transceiver drivers, keep the list sorted + +obj-$(CONFIG_AB8500_USB) += ab8500-usb.o +fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o +obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o +obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o +obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o +obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o +obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o obj-$(CONFIG_OMAP_USB2) += omap-usb2.o obj-$(CONFIG_OMAP_USB3) += omap-usb3.o -obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o +obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o +obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o +obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o +obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o +obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_USB_ISP1301) += isp1301.o -obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o -obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o +obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o +obj-$(CONFIG_USB_MV_OTG) += mv_otg.o +obj-$(CONFIG_USB_MXS_PHY) += mxs-phy.o obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o -obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o +obj-$(CONFIG_USB_ULPI) += ulpi.o +obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o diff --git a/drivers/usb/phy/ab8500-usb.c b/drivers/usb/phy/ab8500-usb.c new file mode 100644 index 0000000..2d86f26 --- /dev/null +++ b/drivers/usb/phy/ab8500-usb.c @@ -0,0 +1,596 @@ +/* + * drivers/usb/otg/ab8500_usb.c + * + * USB transceiver driver for AB8500 chip + * + * Copyright (C) 2010 ST-Ericsson AB + * Mian Yousaf Kaukab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB8500_MAIN_WD_CTRL_REG 0x01 +#define AB8500_USB_LINE_STAT_REG 0x80 +#define AB8500_USB_PHY_CTRL_REG 0x8A + +#define AB8500_BIT_OTG_STAT_ID (1 << 0) +#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) +#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) +#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) +#define AB8500_BIT_WD_CTRL_KICK (1 << 1) + +#define AB8500_V1x_LINK_STAT_WAIT (HZ/10) +#define AB8500_WD_KICK_DELAY_US 100 /* usec */ +#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ +#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */ + +/* Usb line status register */ +enum ab8500_usb_link_status { + USB_LINK_NOT_CONFIGURED = 0, + USB_LINK_STD_HOST_NC, + USB_LINK_STD_HOST_C_NS, + USB_LINK_STD_HOST_C_S, + USB_LINK_HOST_CHG_NM, + USB_LINK_HOST_CHG_HS, + USB_LINK_HOST_CHG_HS_CHIRP, + USB_LINK_DEDICATED_CHG, + USB_LINK_ACA_RID_A, + USB_LINK_ACA_RID_B, + USB_LINK_ACA_RID_C_NM, + USB_LINK_ACA_RID_C_HS, + USB_LINK_ACA_RID_C_HS_CHIRP, + USB_LINK_HM_IDGND, + USB_LINK_RESERVED, + USB_LINK_NOT_VALID_LINK +}; + +struct ab8500_usb { + struct usb_phy phy; + struct device *dev; + int irq_num_id_rise; + int irq_num_id_fall; + int irq_num_vbus_rise; + int irq_num_vbus_fall; + int irq_num_link_status; + unsigned vbus_draw; + struct delayed_work dwork; + struct work_struct phy_dis_work; + unsigned long link_status_wait; + int rev; +}; + +static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) +{ + return container_of(x, struct ab8500_usb, phy); +} + +static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) +{ + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + AB8500_BIT_WD_CTRL_ENABLE); + + udelay(AB8500_WD_KICK_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + (AB8500_BIT_WD_CTRL_ENABLE + | AB8500_BIT_WD_CTRL_KICK)); + + if (ab->rev > 0x10) /* v1.1 v2.0 */ + udelay(AB8500_WD_V11_DISABLE_DELAY_US); + else /* v1.0 */ + msleep(AB8500_WD_V10_DISABLE_DELAY_MS); + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + 0); +} + +static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, + bool enable) +{ + u8 ctrl_reg; + abx500_get_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_PHY_CTRL_REG, + &ctrl_reg); + if (sel_host) { + if (enable) + ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN; + else + ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN; + } else { + if (enable) + ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN; + else + ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN; + } + + abx500_set_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_PHY_CTRL_REG, + ctrl_reg); + + /* Needed to enable the phy.*/ + if (enable) + ab8500_usb_wd_workaround(ab); +} + +#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true) +#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false) +#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) +#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) + +static int ab8500_usb_link_status_update(struct ab8500_usb *ab) +{ + u8 reg; + enum ab8500_usb_link_status lsts; + void *v = NULL; + enum usb_phy_events event; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_LINE_STAT_REG, + ®); + + lsts = (reg >> 3) & 0x0F; + + switch (lsts) { + case USB_LINK_NOT_CONFIGURED: + case USB_LINK_RESERVED: + case USB_LINK_NOT_VALID_LINK: + /* TODO: Disable regulators. */ + ab8500_usb_host_phy_dis(ab); + ab8500_usb_peri_phy_dis(ab); + ab->phy.state = OTG_STATE_B_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + event = USB_EVENT_NONE; + break; + + case USB_LINK_STD_HOST_NC: + case USB_LINK_STD_HOST_C_NS: + case USB_LINK_STD_HOST_C_S: + case USB_LINK_HOST_CHG_NM: + case USB_LINK_HOST_CHG_HS: + case USB_LINK_HOST_CHG_HS_CHIRP: + if (ab->phy.otg->gadget) { + /* TODO: Enable regulators. */ + ab8500_usb_peri_phy_en(ab); + v = ab->phy.otg->gadget; + } + event = USB_EVENT_VBUS; + break; + + case USB_LINK_HM_IDGND: + if (ab->phy.otg->host) { + /* TODO: Enable regulators. */ + ab8500_usb_host_phy_en(ab); + v = ab->phy.otg->host; + } + ab->phy.state = OTG_STATE_A_IDLE; + ab->phy.otg->default_a = true; + event = USB_EVENT_ID; + break; + + case USB_LINK_ACA_RID_A: + case USB_LINK_ACA_RID_B: + /* TODO */ + case USB_LINK_ACA_RID_C_NM: + case USB_LINK_ACA_RID_C_HS: + case USB_LINK_ACA_RID_C_HS_CHIRP: + case USB_LINK_DEDICATED_CHG: + /* TODO: vbus_draw */ + event = USB_EVENT_CHARGER; + break; + } + + atomic_notifier_call_chain(&ab->phy.notifier, event, v); + + return 0; +} + +static void ab8500_usb_delayed_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + dwork.work); + + ab8500_usb_link_status_update(ab); +} + +static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + /* Wait for link status to become stable. */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + + return IRQ_HANDLED; +} + +static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + /* Link status will not be updated till phy is disabled. */ + ab8500_usb_peri_phy_dis(ab); + + /* Wait for link status to become stable. */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + + return IRQ_HANDLED; +} + +static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + ab8500_usb_link_status_update(ab); + + return IRQ_HANDLED; +} + +static void ab8500_usb_phy_disable_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + phy_dis_work); + + if (!ab->phy.otg->host) + ab8500_usb_host_phy_dis(ab); + + if (!ab->phy.otg->gadget) + ab8500_usb_peri_phy_dis(ab); +} + +static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) +{ + struct ab8500_usb *ab; + + if (!phy) + return -ENODEV; + + ab = phy_to_ab(phy); + + ab->vbus_draw = mA; + + if (mA) + atomic_notifier_call_chain(&ab->phy.notifier, + USB_EVENT_ENUMERATED, ab->phy.otg->gadget); + return 0; +} + +/* TODO: Implement some way for charging or other drivers to read + * ab->vbus_draw. + */ + +static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) +{ + /* TODO */ + return 0; +} + +static int ab8500_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct ab8500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = phy_to_ab(otg->phy); + + /* Some drivers call this function in atomic context. + * Do not update ab8500 registers directly till this + * is fixed. + */ + + if (!gadget) { + /* TODO: Disable regulators. */ + otg->gadget = NULL; + schedule_work(&ab->phy_dis_work); + } else { + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct ab8500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = phy_to_ab(otg->phy); + + /* Some drivers call this function in atomic context. + * Do not update ab8500 registers directly till this + * is fixed. + */ + + if (!host) { + /* TODO: Disable regulators. */ + otg->host = NULL; + schedule_work(&ab->phy_dis_work); + } else { + otg->host = host; + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static void ab8500_usb_irq_free(struct ab8500_usb *ab) +{ + if (ab->rev < 0x20) { + free_irq(ab->irq_num_id_rise, ab); + free_irq(ab->irq_num_id_fall, ab); + free_irq(ab->irq_num_vbus_rise, ab); + free_irq(ab->irq_num_vbus_fall, ab); + } else { + free_irq(ab->irq_num_link_status, ab); + } +} + +static int ab8500_usb_v1x_res_setup(struct platform_device *pdev, + struct ab8500_usb *ab) +{ + int err; + + ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R"); + if (ab->irq_num_id_rise < 0) { + dev_err(&pdev->dev, "ID rise irq not found\n"); + return ab->irq_num_id_rise; + } + err = request_threaded_irq(ab->irq_num_id_rise, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-rise", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for ID rise irq\n"); + goto fail0; + } + + ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); + if (ab->irq_num_id_fall < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + return ab->irq_num_id_fall; + } + err = request_threaded_irq(ab->irq_num_id_fall, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for ID fall irq\n"); + goto fail1; + } + + ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R"); + if (ab->irq_num_vbus_rise < 0) { + dev_err(&pdev->dev, "VBUS rise irq not found\n"); + return ab->irq_num_vbus_rise; + } + err = request_threaded_irq(ab->irq_num_vbus_rise, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-rise", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus rise irq\n"); + goto fail2; + } + + ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F"); + if (ab->irq_num_vbus_fall < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + return ab->irq_num_vbus_fall; + } + err = request_threaded_irq(ab->irq_num_vbus_fall, NULL, + ab8500_usb_v1x_vbus_fall_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); + goto fail3; + } + + return 0; +fail3: + free_irq(ab->irq_num_vbus_rise, ab); +fail2: + free_irq(ab->irq_num_id_fall, ab); +fail1: + free_irq(ab->irq_num_id_rise, ab); +fail0: + return err; +} + +static int ab8500_usb_v2_res_setup(struct platform_device *pdev, + struct ab8500_usb *ab) +{ + int err; + + ab->irq_num_link_status = platform_get_irq_byname(pdev, + "USB_LINK_STATUS"); + if (ab->irq_num_link_status < 0) { + dev_err(&pdev->dev, "Link status irq not found\n"); + return ab->irq_num_link_status; + } + + err = request_threaded_irq(ab->irq_num_link_status, NULL, + ab8500_usb_v20_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-link-status", ab); + if (err < 0) { + dev_err(ab->dev, + "request_irq failed for link status irq\n"); + return err; + } + + return 0; +} + +static int ab8500_usb_probe(struct platform_device *pdev) +{ + struct ab8500_usb *ab; + struct usb_otg *otg; + int err; + int rev; + + rev = abx500_get_chip_id(&pdev->dev); + if (rev < 0) { + dev_err(&pdev->dev, "Chip id read failed\n"); + return rev; + } else if (rev < 0x10) { + dev_err(&pdev->dev, "Unsupported AB8500 chip\n"); + return -ENODEV; + } + + ab = kzalloc(sizeof *ab, GFP_KERNEL); + if (!ab) + return -ENOMEM; + + otg = kzalloc(sizeof *otg, GFP_KERNEL); + if (!otg) { + kfree(ab); + return -ENOMEM; + } + + ab->dev = &pdev->dev; + ab->rev = rev; + ab->phy.dev = ab->dev; + ab->phy.otg = otg; + ab->phy.label = "ab8500"; + ab->phy.set_suspend = ab8500_usb_set_suspend; + ab->phy.set_power = ab8500_usb_set_power; + ab->phy.state = OTG_STATE_UNDEFINED; + + otg->phy = &ab->phy; + otg->set_host = ab8500_usb_set_host; + otg->set_peripheral = ab8500_usb_set_peripheral; + + platform_set_drvdata(pdev, ab); + + ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); + + /* v1: Wait for link status to become stable. + * all: Updates form set_host and set_peripheral as they are atomic. + */ + INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work); + + /* all: Disable phy when called from set_host and set_peripheral */ + INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); + + if (ab->rev < 0x20) { + err = ab8500_usb_v1x_res_setup(pdev, ab); + ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT; + } else { + err = ab8500_usb_v2_res_setup(pdev, ab); + } + + if (err < 0) + goto fail0; + + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); + if (err) { + dev_err(&pdev->dev, "Can't register transceiver\n"); + goto fail1; + } + + dev_info(&pdev->dev, "AB8500 usb driver initialized\n"); + + return 0; +fail1: + ab8500_usb_irq_free(ab); +fail0: + kfree(otg); + kfree(ab); + return err; +} + +static int ab8500_usb_remove(struct platform_device *pdev) +{ + struct ab8500_usb *ab = platform_get_drvdata(pdev); + + ab8500_usb_irq_free(ab); + + cancel_delayed_work_sync(&ab->dwork); + + cancel_work_sync(&ab->phy_dis_work); + + usb_remove_phy(&ab->phy); + + ab8500_usb_host_phy_dis(ab); + ab8500_usb_peri_phy_dis(ab); + + platform_set_drvdata(pdev, NULL); + + kfree(ab->phy.otg); + kfree(ab); + + return 0; +} + +static struct platform_driver ab8500_usb_driver = { + .probe = ab8500_usb_probe, + .remove = ab8500_usb_remove, + .driver = { + .name = "ab8500-usb", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_usb_init(void) +{ + return platform_driver_register(&ab8500_usb_driver); +} +subsys_initcall(ab8500_usb_init); + +static void __exit ab8500_usb_exit(void) +{ + platform_driver_unregister(&ab8500_usb_driver); +} +module_exit(ab8500_usb_exit); + +MODULE_ALIAS("platform:ab8500_usb"); +MODULE_AUTHOR("ST-Ericsson AB"); +MODULE_DESCRIPTION("AB8500 usb transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/fsl_otg.c b/drivers/usb/phy/fsl_otg.c new file mode 100644 index 0000000..72a2a00 --- /dev/null +++ b/drivers/usb/phy/fsl_otg.c @@ -0,0 +1,1173 @@ +/* + * Copyright (C) 2007,2008 Freescale semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * Initialization based on code from Shlomi Gridish. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fsl_otg.h" + +#define DRIVER_VERSION "Rev. 1.55" +#define DRIVER_AUTHOR "Jerry Huang/Li Yang" +#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" +#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION + +static const char driver_name[] = "fsl-usb2-otg"; + +const pm_message_t otg_suspend_state = { + .event = 1, +}; + +#define HA_DATA_PULSE + +static struct usb_dr_mmap *usb_dr_regs; +static struct fsl_otg *fsl_otg_dev; +static int srp_wait_done; + +/* FSM timers */ +struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr; + +/* Driver specific timers */ +struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, + *b_srp_wait_tmr, *a_wait_enum_tmr; + +static struct list_head active_timers; + +static struct fsl_otg_config fsl_otg_initdata = { + .otg_port = 1, +}; + +#ifdef CONFIG_PPC32 +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(val, addr) writel(val, addr) +#endif /* CONFIG_PPC32 */ + +/* Routines to access transceiver ULPI registers */ +u8 view_ulpi(u8 addr) +{ + u32 temp; + + temp = 0x40000000 | (addr << 16); + fsl_writel(temp, &usb_dr_regs->ulpiview); + udelay(1000); + while (temp & 0x40) + temp = fsl_readl(&usb_dr_regs->ulpiview); + return (le32_to_cpu(temp) & 0x0000ff00) >> 8; +} + +int write_ulpi(u8 addr, u8 data) +{ + u32 temp; + + temp = 0x60000000 | (addr << 16) | data; + fsl_writel(temp, &usb_dr_regs->ulpiview); + return 0; +} + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ + +/* Charge vbus for vbus pulsing in SRP */ +void fsl_otg_chrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop discharging, start charging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | + OTGSC_CTRL_VBUS_CHARGE; + else + /* stop charging */ + tmp &= ~OTGSC_CTRL_VBUS_CHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* Discharge vbus through a resistor to ground */ +void fsl_otg_dischrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop charging, start discharging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | + OTGSC_CTRL_VBUS_DISCHARGE; + else + /* stop discharging */ + tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* A-device driver vbus, controlled through PP bit in PORTSC */ +void fsl_otg_drv_vbus(int on) +{ + u32 tmp; + + if (on) { + tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; + fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); + } else { + tmp = fsl_readl(&usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; + fsl_writel(tmp, &usb_dr_regs->portsc); + } +} + +/* + * Pull-up D+, signalling connect by periperal. Also used in + * data-line pulsing in SRP + */ +void fsl_otg_loc_conn(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + tmp |= OTGSC_CTRL_DATA_PULSING; + else + tmp &= ~OTGSC_CTRL_DATA_PULSING; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* + * Generate SOF by host. This is controlled through suspend/resume the + * port. In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +void fsl_otg_loc_sof(int on) +{ + u32 tmp; + + tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; + if (on) + tmp |= PORTSC_PORT_FORCE_RESUME; + else + tmp |= PORTSC_PORT_SUSPEND; + + fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); + +} + +/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ +void fsl_otg_start_pulse(void) +{ + u32 tmp; + + srp_wait_done = 0; +#ifdef HA_DATA_PULSE + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + tmp |= OTGSC_HA_DATA_PULSE; + fsl_writel(tmp, &usb_dr_regs->otgsc); +#else + fsl_otg_loc_conn(1); +#endif + + fsl_otg_add_timer(b_data_pulse_tmr); +} + +void b_data_pulse_end(unsigned long foo) +{ +#ifdef HA_DATA_PULSE +#else + fsl_otg_loc_conn(0); +#endif + + /* Do VBUS pulse after data pulse */ + fsl_otg_pulse_vbus(); +} + +void fsl_otg_pulse_vbus(void) +{ + srp_wait_done = 0; + fsl_otg_chrg_vbus(1); + /* start the timer to end vbus charge */ + fsl_otg_add_timer(b_vbus_pulse_tmr); +} + +void b_vbus_pulse_end(unsigned long foo) +{ + fsl_otg_chrg_vbus(0); + + /* + * As USB3300 using the same a_sess_vld and b_sess_vld voltage + * we need to discharge the bus for a while to distinguish + * residual voltage of vbus pulsing and A device pull up + */ + fsl_otg_dischrg_vbus(1); + fsl_otg_add_timer(b_srp_wait_tmr); +} + +void b_srp_end(unsigned long foo) +{ + fsl_otg_dischrg_vbus(0); + srp_wait_done = 1; + + if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) && + fsl_otg_dev->fsm.b_sess_vld) + fsl_otg_dev->fsm.b_srp_done = 1; +} + +/* + * Workaround for a_host suspending too fast. When a_bus_req=0, + * a_host will start by SRP. It needs to set b_hnp_enable before + * actually suspending to start HNP + */ +void a_wait_enum(unsigned long foo) +{ + VDBG("a_wait_enum timeout\n"); + if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) + fsl_otg_add_timer(a_wait_enum_tmr); + else + otg_statemachine(&fsl_otg_dev->fsm); +} + +/* The timeout callback function to set time out bit */ +void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +/* Initialize timers */ +int fsl_otg_init_timers(struct otg_fsm *fsm) +{ + /* FSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + if (!a_wait_vrise_tmr) + return -ENOMEM; + + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + if (!a_wait_bcon_tmr) + return -ENOMEM; + + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + if (!a_aidl_bdis_tmr) + return -ENOMEM; + + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + if (!b_ase0_brst_tmr) + return -ENOMEM; + + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + if (!b_se0_srp_tmr) + return -ENOMEM; + + b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + if (!b_srp_fail_tmr) + return -ENOMEM; + + a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, + (unsigned long)&fsm); + if (!a_wait_enum_tmr) + return -ENOMEM; + + /* device driver used timers */ + b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); + if (!b_srp_wait_tmr) + return -ENOMEM; + + b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, + TB_DATA_PLS, 0); + if (!b_data_pulse_tmr) + return -ENOMEM; + + b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, + TB_VBUS_PLS, 0); + if (!b_vbus_pulse_tmr) + return -ENOMEM; + + return 0; +} + +/* Uninitialize timers */ +void fsl_otg_uninit_timers(void) +{ + /* FSM used timers */ + kfree(a_wait_vrise_tmr); + kfree(a_wait_bcon_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_ase0_brst_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_fail_tmr); + kfree(a_wait_enum_tmr); + + /* device driver used timers */ + kfree(b_srp_wait_tmr); + kfree(b_data_pulse_tmr); + kfree(b_vbus_pulse_tmr); +} + +/* Add timer to timer list */ +void fsl_otg_add_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer; + + /* + * Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +void fsl_otg_del_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer, *del_tmp; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); +} + +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by fsl_otg 1ms timer interrupt + */ +int fsl_otg_tick_timer(void) +{ + struct fsl_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + return expired; +} + +/* Reset controller, not reset the bus */ +void otg_reset_controller(void) +{ + u32 command; + + command = fsl_readl(&usb_dr_regs->usbcmd); + command |= (1 << 1); + fsl_writel(command, &usb_dr_regs->usbcmd); + while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) + ; +} + +/* Call suspend/resume routines in host driver */ +int fsl_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct usb_otg *otg = fsm->otg; + struct device *dev; + struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy); + u32 retval = 0; + + if (!otg->host) + return -ENODEV; + dev = otg->host->controller; + + /* + * Update a_vbus_vld state as a_vbus_vld int is disabled + * in device mode + */ + fsm->a_vbus_vld = + !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); + if (on) { + /* start fsl usb host controller */ + if (otg_dev->host_working) + goto end; + else { + otg_reset_controller(); + VDBG("host on......\n"); + if (dev->driver->pm && dev->driver->pm->resume) { + retval = dev->driver->pm->resume(dev); + if (fsm->id) { + /* default-b */ + fsl_otg_drv_vbus(1); + /* + * Workaround: b_host can't driver + * vbus, but PP in PORTSC needs to + * be 1 for host to work. + * So we set drv_vbus bit in + * transceiver to 0 thru ULPI. + */ + write_ulpi(0x0c, 0x20); + } + } + + otg_dev->host_working = 1; + } + } else { + /* stop fsl usb host controller */ + if (!otg_dev->host_working) + goto end; + else { + VDBG("host off......\n"); + if (dev && dev->driver) { + if (dev->driver->pm && dev->driver->pm->suspend) + retval = dev->driver->pm->suspend(dev); + if (fsm->id) + /* default-b */ + fsl_otg_drv_vbus(0); + } + otg_dev->host_working = 0; + } + } +end: + return retval; +} + +/* + * Call suspend and resume function in udc driver + * to stop and start udc driver. + */ +int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct usb_otg *otg = fsm->otg; + struct device *dev; + + if (!otg->gadget || !otg->gadget->dev.parent) + return -ENODEV; + + VDBG("gadget %s\n", on ? "on" : "off"); + dev = otg->gadget->dev.parent; + + if (on) { + if (dev->driver->resume) + dev->driver->resume(dev); + } else { + if (dev->driver->suspend) + dev->driver->suspend(dev, otg_suspend_state); + } + + return 0; +} + +/* + * Called by initialization code of host driver. Register host controller + * to the OTG. Suspend host for OTG role detection. + */ +static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + otg->host = host; + + otg_dev->fsm.a_bus_drop = 0; + otg_dev->fsm.a_bus_req = 1; + + if (host) { + VDBG("host off......\n"); + + otg->host->otg_port = fsl_otg_initdata.otg_port; + otg->host->is_b_host = otg_dev->fsm.id; + /* + * must leave time for khubd to finish its thing + * before yanking the host driver out from under it, + * so suspend the host after a short delay. + */ + otg_dev->host_working = 1; + schedule_delayed_work(&otg_dev->otg_event, 100); + return 0; + } else { + /* host driver going away */ + if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & + OTGSC_STS_USB_ID)) { + /* Mini-A cable connected */ + struct otg_fsm *fsm = &otg_dev->fsm; + + otg->phy->state = OTG_STATE_UNDEFINED; + fsm->protocol = PROTO_UNDEF; + } + } + + otg_dev->host_working = 0; + + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* Called by initialization code of udc. Register udc to OTG. */ +static int fsl_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + VDBG("otg_dev 0x%x\n", (int)otg_dev); + VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + if (!gadget) { + if (!otg->default_a) + otg->gadget->ops->vbus_draw(otg->gadget, 0); + usb_gadget_vbus_disconnect(otg->gadget); + otg->gadget = 0; + otg_dev->fsm.b_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + return 0; + } + + otg->gadget = gadget; + otg->gadget->is_a_peripheral = !otg_dev->fsm.id; + + otg_dev->fsm.b_bus_req = 1; + + /* start the gadget right away if the ID pin says Mini-B */ + DBG("ID pin=%d\n", otg_dev->fsm.id); + if (otg_dev->fsm.id == 1) { + fsl_otg_start_host(&otg_dev->fsm, 0); + otg_drv_vbus(&otg_dev->fsm, 0); + fsl_otg_start_gadget(&otg_dev->fsm, 1); + } + + return 0; +} + +/* Set OTG port power, only for B-device */ +static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA) +{ + if (!fsl_otg_dev) + return -ENODEV; + if (phy->state == OTG_STATE_B_PERIPHERAL) + pr_info("FSL OTG: Draw %d mA\n", mA); + + return 0; +} + +/* + * Delayed pin detect interrupt processing. + * + * When the Mini-A cable is disconnected from the board, + * the pin-detect interrupt happens before the disconnect + * interrupts for the connected device(s). In order to + * process the disconnect interrupt(s) prior to switching + * roles, the pin-detect interrupts are delayed, and handled + * by this routine. + */ +static void fsl_otg_event(struct work_struct *work) +{ + struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); + struct otg_fsm *fsm = &og->fsm; + + if (fsm->id) { /* switch to gadget */ + fsl_otg_start_host(fsm, 0); + otg_drv_vbus(fsm, 0); + fsl_otg_start_gadget(fsm, 1); + } +} + +/* B-device start SRP */ +static int fsl_otg_start_srp(struct usb_otg *otg) +{ + struct fsl_otg *otg_dev; + + if (!otg || otg->phy->state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + otg_dev->fsm.b_bus_req = 1; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* A_host suspend will call this function to start hnp */ +static int fsl_otg_start_hnp(struct usb_otg *otg) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + DBG("start_hnp...n"); + + /* clear a_bus_req to enter a_suspend state */ + otg_dev->fsm.a_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* + * Interrupt handler. OTG/host/peripheral share the same int line. + * OTG driver clears OTGSC interrupts and leaves USB interrupts + * intact. It needs to have knowledge of some USB interrupts + * such as port change. + */ +irqreturn_t fsl_otg_isr(int irq, void *dev_id) +{ + struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; + struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; + u32 otg_int_src, otg_sc; + + otg_sc = fsl_readl(&usb_dr_regs->otgsc); + otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); + + /* Only clear otg interrupts */ + fsl_writel(otg_sc, &usb_dr_regs->otgsc); + + /*FIXME: ID change not generate when init to 0 */ + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + + /* process OTG interrupts */ + if (otg_int_src) { + if (otg_int_src & OTGSC_INTSTS_USB_ID) { + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + /* clear conn information */ + if (fsm->id) + fsm->b_conn = 0; + else + fsm->a_conn = 0; + + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; + VDBG("ID int (ID is %d)\n", fsm->id); + + if (fsm->id) { /* switch to gadget */ + schedule_delayed_work( + &((struct fsl_otg *)dev_id)->otg_event, + 100); + } else { /* switch to host */ + cancel_delayed_work(& + ((struct fsl_otg *)dev_id)-> + otg_event); + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); + } + return IRQ_HANDLED; + } + } + return IRQ_NONE; +} + +static struct otg_fsm_ops fsl_otg_ops = { + .chrg_vbus = fsl_otg_chrg_vbus, + .drv_vbus = fsl_otg_drv_vbus, + .loc_conn = fsl_otg_loc_conn, + .loc_sof = fsl_otg_loc_sof, + .start_pulse = fsl_otg_start_pulse, + + .add_timer = fsl_otg_add_timer, + .del_timer = fsl_otg_del_timer, + + .start_host = fsl_otg_start_host, + .start_gadget = fsl_otg_start_gadget, +}; + +/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ +static int fsl_otg_conf(struct platform_device *pdev) +{ + struct fsl_otg *fsl_otg_tc; + int status; + + if (fsl_otg_dev) + return 0; + + /* allocate space to fsl otg device */ + fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); + if (!fsl_otg_tc) + return -ENOMEM; + + fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!fsl_otg_tc->phy.otg) { + kfree(fsl_otg_tc); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); + + INIT_LIST_HEAD(&active_timers); + status = fsl_otg_init_timers(&fsl_otg_tc->fsm); + if (status) { + pr_info("Couldn't init OTG timers\n"); + goto err; + } + spin_lock_init(&fsl_otg_tc->fsm.lock); + + /* Set OTG state machine operations */ + fsl_otg_tc->fsm.ops = &fsl_otg_ops; + + /* initialize the otg structure */ + fsl_otg_tc->phy.label = DRIVER_DESC; + fsl_otg_tc->phy.set_power = fsl_otg_set_power; + + fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy; + fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host; + fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral; + fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp; + fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp; + + fsl_otg_dev = fsl_otg_tc; + + /* Store the otg transceiver */ + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); + if (status) { + pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); + goto err; + } + + return 0; +err: + fsl_otg_uninit_timers(); + kfree(fsl_otg_tc->phy.otg); + kfree(fsl_otg_tc); + return status; +} + +/* OTG Initialization */ +int usb_otg_start(struct platform_device *pdev) +{ + struct fsl_otg *p_otg; + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); + struct otg_fsm *fsm; + int status; + struct resource *res; + u32 temp; + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + p_otg = container_of(otg_trans, struct fsl_otg, phy); + fsm = &p_otg->fsm; + + /* Initialize the state machine structure with default values */ + SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); + fsm->otg = p_otg->phy.otg; + + /* We don't require predefined MEM/IRQ resource index */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + /* We don't request_mem_region here to enable resource sharing + * with host/device */ + + usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); + p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; + pdata->regs = (void *)usb_dr_regs; + + if (pdata->init && pdata->init(pdev) != 0) + return -EINVAL; + + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + + /* request irq */ + p_otg->irq = platform_get_irq(pdev, 0); + status = request_irq(p_otg->irq, fsl_otg_isr, + IRQF_SHARED, driver_name, p_otg); + if (status) { + dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n", + p_otg->irq, status); + iounmap(p_otg->dr_mem_map); + kfree(p_otg->phy.otg); + kfree(p_otg); + return status; + } + + /* stop the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp &= ~USB_CMD_RUN_STOP; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* reset the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp |= USB_CMD_CTRL_RESET; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* wait reset completed */ + while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) + ; + + /* configure the VBUSHS as IDLE(both host and device) */ + temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); + fsl_writel(temp, &p_otg->dr_mem_map->usbmode); + + /* configure PHY interface */ + temp = fsl_readl(&p_otg->dr_mem_map->portsc); + temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); + switch (pdata->phy_mode) { + case FSL_USB2_PHY_ULPI: + temp |= PORTSC_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI_WIDE: + temp |= PORTSC_PTW_16BIT; + /* fall through */ + case FSL_USB2_PHY_UTMI: + temp |= PORTSC_PTS_UTMI; + /* fall through */ + default: + break; + } + fsl_writel(temp, &p_otg->dr_mem_map->portsc); + + if (pdata->have_sysif_regs) { + /* configure control enable IO output, big endian register */ + temp = __raw_readl(&p_otg->dr_mem_map->control); + temp |= USB_CTRL_IOENB; + __raw_writel(temp, &p_otg->dr_mem_map->control); + } + + /* disable all interrupt and clear all OTGSC status */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + /* + * The identification (id) input is FALSE when a Mini-A plug is inserted + * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. + * Also: record initial state of ID pin + */ + if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { + p_otg->phy.state = OTG_STATE_UNDEFINED; + p_otg->fsm.id = 1; + } else { + p_otg->phy.state = OTG_STATE_A_IDLE; + p_otg->fsm.id = 0; + } + + DBG("initial ID pin=%d\n", p_otg->fsm.id); + + /* enable OTG ID pin interrupt */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp |= OTGSC_INTR_USB_ID_EN; + temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + return 0; +} + +/* + * state file in sysfs + */ +static int show_fsl_usb2_otg_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct otg_fsm *fsm = &fsl_otg_dev->fsm; + char *next = buf; + unsigned size = PAGE_SIZE; + unsigned long flags; + int t; + + spin_lock_irqsave(&fsm->lock, flags); + + /* basic driver infomation */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", + DRIVER_VERSION); + size -= t; + next += t; + + /* Registers */ + t = scnprintf(next, size, + "OTGSC: 0x%08x\n" + "PORTSC: 0x%08x\n" + "USBMODE: 0x%08x\n" + "USBCMD: 0x%08x\n" + "USBSTS: 0x%08x\n" + "USBINTR: 0x%08x\n", + fsl_readl(&usb_dr_regs->otgsc), + fsl_readl(&usb_dr_regs->portsc), + fsl_readl(&usb_dr_regs->usbmode), + fsl_readl(&usb_dr_regs->usbcmd), + fsl_readl(&usb_dr_regs->usbsts), + fsl_readl(&usb_dr_regs->usbintr)); + size -= t; + next += t; + + /* State */ + t = scnprintf(next, size, + "OTG state: %s\n\n", + usb_otg_state_string(fsl_otg_dev->phy.state)); + size -= t; + next += t; + + /* State Machine Variables */ + t = scnprintf(next, size, + "a_bus_req: %d\n" + "b_bus_req: %d\n" + "a_bus_resume: %d\n" + "a_bus_suspend: %d\n" + "a_conn: %d\n" + "a_sess_vld: %d\n" + "a_srp_det: %d\n" + "a_vbus_vld: %d\n" + "b_bus_resume: %d\n" + "b_bus_suspend: %d\n" + "b_conn: %d\n" + "b_se0_srp: %d\n" + "b_sess_end: %d\n" + "b_sess_vld: %d\n" + "id: %d\n", + fsm->a_bus_req, + fsm->b_bus_req, + fsm->a_bus_resume, + fsm->a_bus_suspend, + fsm->a_conn, + fsm->a_sess_vld, + fsm->a_srp_det, + fsm->a_vbus_vld, + fsm->b_bus_resume, + fsm->b_bus_suspend, + fsm->b_conn, + fsm->b_se0_srp, + fsm->b_sess_end, + fsm->b_sess_vld, + fsm->id); + size -= t; + next += t; + + spin_unlock_irqrestore(&fsm->lock, flags); + + return PAGE_SIZE - size; +} + +static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); + + +/* Char driver interface to control some OTG input */ + +/* + * Handle some ioctl command, such as get otg + * status and set host suspend + */ +static long fsl_otg_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u32 retval = 0; + + switch (cmd) { + case GET_OTG_STATUS: + retval = fsl_otg_dev->host_working; + break; + + case SET_A_SUSPEND_REQ: + fsl_otg_dev->fsm.a_suspend_req = arg; + break; + + case SET_A_BUS_DROP: + fsl_otg_dev->fsm.a_bus_drop = arg; + break; + + case SET_A_BUS_REQ: + fsl_otg_dev->fsm.a_bus_req = arg; + break; + + case SET_B_BUS_REQ: + fsl_otg_dev->fsm.b_bus_req = arg; + break; + + default: + break; + } + + otg_statemachine(&fsl_otg_dev->fsm); + + return retval; +} + +static int fsl_otg_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int fsl_otg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations otg_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .unlocked_ioctl = fsl_otg_ioctl, + .open = fsl_otg_open, + .release = fsl_otg_release, +}; + +static int fsl_otg_probe(struct platform_device *pdev) +{ + int ret; + + if (!pdev->dev.platform_data) + return -ENODEV; + + /* configure the OTG */ + ret = fsl_otg_conf(pdev); + if (ret) { + dev_err(&pdev->dev, "Couldn't configure OTG module\n"); + return ret; + } + + /* start OTG */ + ret = usb_otg_start(pdev); + if (ret) { + dev_err(&pdev->dev, "Can't init FSL OTG device\n"); + return ret; + } + + ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); + if (ret) { + dev_err(&pdev->dev, "unable to register FSL OTG device\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + if (ret) + dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); + + return ret; +} + +static int fsl_otg_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + usb_remove_phy(&fsl_otg_dev->phy); + free_irq(fsl_otg_dev->irq, fsl_otg_dev); + + iounmap((void *)usb_dr_regs); + + fsl_otg_uninit_timers(); + kfree(fsl_otg_dev->phy.otg); + kfree(fsl_otg_dev); + + device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + + unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); + + if (pdata->exit) + pdata->exit(pdev); + + return 0; +} + +struct platform_driver fsl_otg_driver = { + .probe = fsl_otg_probe, + .remove = fsl_otg_remove, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(fsl_otg_driver); + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/fsl_otg.h b/drivers/usb/phy/fsl_otg.h new file mode 100644 index 0000000..ca26628 --- /dev/null +++ b/drivers/usb/phy/fsl_otg.h @@ -0,0 +1,406 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "otg_fsm.h" +#include +#include + +/* USB Command Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x1<<0) +#define USB_CMD_CTRL_RESET (0x1<<1) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) +#define USB_CMD_INT_AA_DOORBELL (0x1<<6) +#define USB_CMD_ASP (0x3<<8) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) +#define USB_CMD_SUTW (0x1<<13) +#define USB_CMD_ATDTW (0x1<<14) +#define USB_CMD_ITC (0xFF<<16) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) +#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x0<<8) +#define USB_CMD_ASP_01 (0x1<<8) +#define USB_CMD_ASP_10 (0x2<<8) +#define USB_CMD_ASP_11 (0x3<<8) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) +#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) +#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) +#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) +#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) +#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) +#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) +#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB Status Register Bit Masks */ +#define USB_STS_INT (0x1<<0) +#define USB_STS_ERR (0x1<<1) +#define USB_STS_PORT_CHANGE (0x1<<2) +#define USB_STS_FRM_LST_ROLL (0x1<<3) +#define USB_STS_SYS_ERR (0x1<<4) +#define USB_STS_IAA (0x1<<5) +#define USB_STS_RESET_RECEIVED (0x1<<6) +#define USB_STS_SOF (0x1<<7) +#define USB_STS_DCSUSPEND (0x1<<8) +#define USB_STS_HC_HALTED (0x1<<12) +#define USB_STS_RCL (0x1<<13) +#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) +#define USB_STS_ASYNC_SCHEDULE (0x1<<15) + +/* USB Interrupt Enable Register Bit Masks */ +#define USB_INTR_INT_EN (0x1<<0) +#define USB_INTR_ERR_INT_EN (0x1<<1) +#define USB_INTR_PC_DETECT_EN (0x1<<2) +#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) +#define USB_INTR_SYS_ERR_EN (0x1<<4) +#define USB_INTR_ASYN_ADV_EN (0x1<<5) +#define USB_INTR_RESET_EN (0x1<<6) +#define USB_INTR_SOF_EN (0x1<<7) +#define USB_INTR_DEVICE_SUSPEND (0x1<<8) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) +#define USB_DEVICE_ADDRESS_BIT_POS (25) +/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ +#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) +#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) +#define PORTSC_PORT_ENABLE (0x1<<2) +#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) +#define PORTSC_OVER_CURRENT_ACT (0x1<<4) +#define PORTSC_OVER_CUURENT_CHG (0x1<<5) +#define PORTSC_PORT_FORCE_RESUME (0x1<<6) +#define PORTSC_PORT_SUSPEND (0x1<<7) +#define PORTSC_PORT_RESET (0x1<<8) +#define PORTSC_LINE_STATUS_BITS (0x3<<10) +#define PORTSC_PORT_POWER (0x1<<12) +#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) +#define PORTSC_PORT_TEST_CTRL (0xF<<16) +#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) +#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) +#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) +#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) +#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) +#define PORTSC_PORT_SPEED_MASK (0x3<<26) +#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) +#define PORTSC_PHY_TYPE_SEL (0x3<<30) +/* bit 11-10 are line status */ +#define PORTSC_LINE_STATUS_SE0 (0x0<<10) +#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) +#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) +#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) +#define PORTSC_LINE_STATUS_BIT_POS (10) + +/* bit 15-14 are port indicator control */ +#define PORTSC_PIC_OFF (0x0<<14) +#define PORTSC_PIC_AMBER (0x1<<14) +#define PORTSC_PIC_GREEN (0x2<<14) +#define PORTSC_PIC_UNDEF (0x3<<14) +#define PORTSC_PIC_BIT_POS (14) + +/* bit 19-16 are port test control */ +#define PORTSC_PTC_DISABLE (0x0<<16) +#define PORTSC_PTC_JSTATE (0x1<<16) +#define PORTSC_PTC_KSTATE (0x2<<16) +#define PORTSC_PTC_SEQNAK (0x3<<16) +#define PORTSC_PTC_PACKET (0x4<<16) +#define PORTSC_PTC_FORCE_EN (0x5<<16) +#define PORTSC_PTC_BIT_POS (16) + +/* bit 27-26 are port speed */ +#define PORTSC_PORT_SPEED_FULL (0x0<<26) +#define PORTSC_PORT_SPEED_LOW (0x1<<26) +#define PORTSC_PORT_SPEED_HIGH (0x2<<26) +#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) +#define PORTSC_SPEED_BIT_POS (26) + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSC_PTW (0x1<<28) +#define PORTSC_PTW_8BIT (0x0<<28) +#define PORTSC_PTW_16BIT (0x1<<28) + +/* bit 31-30 are port transceiver select */ +#define PORTSC_PTS_UTMI (0x0<<30) +#define PORTSC_PTS_ULPI (0x2<<30) +#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) +#define PORTSC_PTS_BIT_POS (30) + +#define PORTSC_W1C_BITS \ + (PORTSC_CONNECT_STATUS_CHANGE | \ + PORTSC_PORT_EN_DIS_CHANGE | \ + PORTSC_OVER_CUURENT_CHG) + +/* OTG Status Control Register Bit Masks */ +#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) +#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) +#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) +#define OTGSC_CTRL_DATA_PULSING (0x1<<4) +#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) +#define OTGSC_HA_DATA_PULSE (0x1<<6) +#define OTGSC_HA_BA (0x1<<7) +#define OTGSC_STS_USB_ID (0x1<<8) +#define OTGSC_STS_A_VBUS_VALID (0x1<<9) +#define OTGSC_STS_A_SESSION_VALID (0x1<<10) +#define OTGSC_STS_B_SESSION_VALID (0x1<<11) +#define OTGSC_STS_B_SESSION_END (0x1<<12) +#define OTGSC_STS_1MS_TOGGLE (0x1<<13) +#define OTGSC_STS_DATA_PULSING (0x1<<14) +#define OTGSC_INTSTS_USB_ID (0x1<<16) +#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) +#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) +#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) +#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) +#define OTGSC_INTSTS_1MS (0x1<<21) +#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) +#define OTGSC_INTR_USB_ID_EN (0x1<<24) +#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) +#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) +#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) +#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) +#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) +#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) +#define OTGSC_INTSTS_MASK (0x00ff0000) + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) +#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) +#define USB_MODE_CTRL_MODE_HOST (0x3<<0) +#define USB_MODE_CTRL_MODE_RSV (0x1<<0) +#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) +#define USB_MODE_STREAM_DISABLE (0x1<<4) +#define USB_MODE_ES (0x1<<2) /* Endian Select */ + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB (0x1<<2) +#define USB_CTRL_ULPI_INT0EN (0x1<<0) + +/* BCSR5 */ +#define BCSR5_INT_USB (0x02) + +/* USB module clk cfg */ +#define SCCR_OFFS (0xA08) +#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ +#define SCCR_USB_MPHCM_11 (0x00c00000) +#define SCCR_USB_MPHCM_01 (0x00400000) +#define SCCR_USB_MPHCM_10 (0x00800000) +#define SCCR_USB_DRCM_11 (0x00300000) +#define SCCR_USB_DRCM_01 (0x00100000) +#define SCCR_USB_DRCM_10 (0x00200000) + +#define SICRL_OFFS (0x114) +#define SICRL_USB0 (0x40000000) +#define SICRL_USB1 (0x20000000) + +#define SICRH_OFFS (0x118) +#define SICRH_USB_UTMI (0x00020000) + +/* OTG interrupt enable bit masks */ +#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ + (OTGSC_INTR_USB_ID_EN | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTR_A_VBUS_VALID_EN | \ + OTGSC_INTR_A_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_END_EN | \ + OTGSC_INTR_DATA_PULSING_EN) + +/* OTG interrupt status bit masks */ +#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ + (OTGSC_INTSTS_USB_ID | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTSTS_A_VBUS_VALID | \ + OTGSC_INTSTS_A_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_END | \ + OTGSC_INTSTS_DATA_PULSING) + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ + +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 + * This is only used to get out of + * OTG_STATE_A_WAIT_BCON state if there was + * no connection for these many milliseconds + */ + +/* A-Idle to B-Disconnect */ +/* It is necessary for this timer to be more than 750 ms because of a bug in OPT + * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated + * in the test description + */ +#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ + +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ + +/* B-device timing constants */ + + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ +#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ +#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ + +/* SRP Initiate Time */ +#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ + +/* SRP Fail Time */ +#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ + +/* SRP result wait time */ +#define TB_SRP_WAIT (60) + +/* VBus time */ +#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ + +/* Discharge time */ +/* This time should be less than 10ms. It varies from system to system. */ +#define TB_VBUS_DSCHRG (8) + +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ + +/* A bus suspend timer before we can switch to b_wait_aconn */ +#define TB_A_SUSPEND (7) +#define TB_BUS_RESUME (12) + +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ + +#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) + +struct usb_dr_mmap { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[8]; + u32 ulpiview; /* ULPI register access */ + u8 res7[12]; + u32 configflag; /* Configure Flag Register */ + u32 portsc; /* Port 1 Status and Control Register */ + u8 res8[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ + u8 res9[552]; + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res10[236]; + u32 control; /* General Purpose Control Register */ +}; + +struct fsl_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +inline struct fsl_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct fsl_otg_timer *timer; + + timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + if (!timer) + return NULL; + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +struct fsl_otg { + struct usb_phy phy; + struct otg_fsm fsm; + struct usb_dr_mmap *dr_mem_map; + struct delayed_work otg_event; + + /* used for usb host */ + struct work_struct work_wq; + u8 host_working; + + int irq; +}; + +struct fsl_otg_config { + u8 otg_port; +}; + +/* For SRP and HNP handle */ +#define FSL_OTG_MAJOR 240 +#define FSL_OTG_NAME "fsl-usb2-otg" +/* Command to OTG driver ioctl */ +#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR +/* if otg work as host, it should return 1, otherwise return 0 */ +#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) +#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) +#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) +#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) +#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) +#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) +#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) +#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) +#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) + +void fsl_otg_add_timer(void *timer); +void fsl_otg_del_timer(void *timer); +void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/phy/gpio_vbus.c b/drivers/usb/phy/gpio_vbus.c new file mode 100644 index 0000000..a7d4ac5 --- /dev/null +++ b/drivers/usb/phy/gpio_vbus.c @@ -0,0 +1,416 @@ +/* + * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices + * + * Copyright (c) 2008 Philipp Zabel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +/* + * A simple GPIO VBUS sensing driver for B peripheral only devices + * with internal transceivers. It can control a D+ pullup GPIO and + * a regulator to limit the current drawn from VBUS. + * + * Needs to be loaded before the UDC driver that will use it. + */ +struct gpio_vbus_data { + struct usb_phy phy; + struct device *dev; + struct regulator *vbus_draw; + int vbus_draw_enabled; + unsigned mA; + struct delayed_work work; + int vbus; + int irq; +}; + + +/* + * This driver relies on "both edges" triggering. VBUS has 100 msec to + * stabilize, so the peripheral controller driver may need to cope with + * some bouncing due to current surges (e.g. charging local capacitance) + * and contact chatter. + * + * REVISIT in desperate straits, toggling between rising and falling + * edges might be workable. + */ +#define VBUS_IRQ_FLAGS \ + (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) + + +/* interface to regulator framework */ +static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) +{ + struct regulator *vbus_draw = gpio_vbus->vbus_draw; + int enabled; + + if (!vbus_draw) + return; + + enabled = gpio_vbus->vbus_draw_enabled; + if (mA) { + regulator_set_current_limit(vbus_draw, 0, 1000 * mA); + if (!enabled) { + regulator_enable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 1; + } + } else { + if (enabled) { + regulator_disable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 0; + } + } + gpio_vbus->mA = mA; +} + +static int is_vbus_powered(struct gpio_vbus_mach_info *pdata) +{ + int vbus; + + vbus = gpio_get_value(pdata->gpio_vbus); + if (pdata->gpio_vbus_inverted) + vbus = !vbus; + + return vbus; +} + +static void gpio_vbus_work(struct work_struct *work) +{ + struct gpio_vbus_data *gpio_vbus = + container_of(work, struct gpio_vbus_data, work.work); + struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; + int gpio, status, vbus; + + if (!gpio_vbus->phy.otg->gadget) + return; + + vbus = is_vbus_powered(pdata); + if ((vbus ^ gpio_vbus->vbus) == 0) + return; + gpio_vbus->vbus = vbus; + + /* Peripheral controllers which manage the pullup themselves won't have + * gpio_pullup configured here. If it's configured here, we'll do what + * isp1301_omap::b_peripheral() does and enable the pullup here... although + * that may complicate usb_gadget_{,dis}connect() support. + */ + gpio = pdata->gpio_pullup; + + if (vbus) { + status = USB_EVENT_VBUS; + gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL; + gpio_vbus->phy.last_event = status; + usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget); + + /* drawing a "unit load" is *always* OK, except for OTG */ + set_vbus_draw(gpio_vbus, 100); + + /* optionally enable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, !pdata->gpio_pullup_inverted); + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); + } else { + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget); + status = USB_EVENT_NONE; + gpio_vbus->phy.state = OTG_STATE_B_IDLE; + gpio_vbus->phy.last_event = status; + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); + } +} + +/* VBUS change IRQ handler */ +static irqreturn_t gpio_vbus_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct usb_otg *otg = gpio_vbus->phy.otg; + + dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", + is_vbus_powered(pdata) ? "supplied" : "inactive", + otg->gadget ? otg->gadget->name : "none"); + + if (otg->gadget) + schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +/* OTG transceiver interface */ + +/* bind/unbind the peripheral controller */ +static int gpio_vbus_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct gpio_vbus_data *gpio_vbus; + struct gpio_vbus_mach_info *pdata; + struct platform_device *pdev; + int gpio; + + gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy); + pdev = to_platform_device(gpio_vbus->dev); + pdata = gpio_vbus->dev->platform_data; + gpio = pdata->gpio_pullup; + + if (!gadget) { + dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", + otg->gadget->name); + + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(otg->gadget); + otg->phy->state = OTG_STATE_UNDEFINED; + + otg->gadget = NULL; + return 0; + } + + otg->gadget = gadget; + dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); + + /* initialize connection state */ + gpio_vbus->vbus = 0; /* start with disconnected */ + gpio_vbus_irq(gpio_vbus->irq, pdev); + return 0; +} + +/* effective for B devices, ignored for A-peripheral */ +static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); + + if (phy->state == OTG_STATE_B_PERIPHERAL) + set_vbus_draw(gpio_vbus, mA); + return 0; +} + +/* for non-OTG B devices: set/clear transceiver suspend mode */ +static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); + + /* draw max 0 mA from vbus in suspend mode; or the previously + * recorded amount of current if not suspended + * + * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA + * if they're wake-enabled ... we don't handle that yet. + */ + return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA); +} + +/* platform driver interface */ + +static int __init gpio_vbus_probe(struct platform_device *pdev) +{ + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus; + struct resource *res; + int err, gpio, irq; + unsigned long irqflags; + + if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) + return -EINVAL; + gpio = pdata->gpio_vbus; + + gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + if (!gpio_vbus) + return -ENOMEM; + + gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!gpio_vbus->phy.otg) { + kfree(gpio_vbus); + return -ENOMEM; + } + + platform_set_drvdata(pdev, gpio_vbus); + gpio_vbus->dev = &pdev->dev; + gpio_vbus->phy.label = "gpio-vbus"; + gpio_vbus->phy.set_power = gpio_vbus_set_power; + gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend; + gpio_vbus->phy.state = OTG_STATE_UNDEFINED; + + gpio_vbus->phy.otg->phy = &gpio_vbus->phy; + gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; + + err = gpio_request(gpio, "vbus_detect"); + if (err) { + dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", + gpio, err); + goto err_gpio; + } + gpio_direction_input(gpio); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) { + irq = res->start; + irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; + } else { + irq = gpio_to_irq(gpio); + irqflags = VBUS_IRQ_FLAGS; + } + + gpio_vbus->irq = irq; + + /* if data line pullup is in use, initialize it to "not pulling up" */ + gpio = pdata->gpio_pullup; + if (gpio_is_valid(gpio)) { + err = gpio_request(gpio, "udc_pullup"); + if (err) { + dev_err(&pdev->dev, + "can't request pullup gpio %d, err: %d\n", + gpio, err); + gpio_free(pdata->gpio_vbus); + goto err_gpio; + } + gpio_direction_output(gpio, pdata->gpio_pullup_inverted); + } + + err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); + if (err) { + dev_err(&pdev->dev, "can't request irq %i, err: %d\n", + irq, err); + goto err_irq; + } + + ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); + + INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); + + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + + /* only active when a gadget is registered */ + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_otg; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; +err_otg: + regulator_put(gpio_vbus->vbus_draw); + free_irq(irq, pdev); +err_irq: + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(pdata->gpio_vbus); +err_gpio: + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus->phy.otg); + kfree(gpio_vbus); + return err; +} + +static int __exit gpio_vbus_remove(struct platform_device *pdev) +{ + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + int gpio = pdata->gpio_vbus; + + device_init_wakeup(&pdev->dev, 0); + cancel_delayed_work_sync(&gpio_vbus->work); + regulator_put(gpio_vbus->vbus_draw); + + usb_remove_phy(&gpio_vbus->phy); + + free_irq(gpio_vbus->irq, pdev); + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(gpio); + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus->phy.otg); + kfree(gpio_vbus); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_vbus_pm_suspend(struct device *dev) +{ + struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(gpio_vbus->irq); + + return 0; +} + +static int gpio_vbus_pm_resume(struct device *dev) +{ + struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(gpio_vbus->irq); + + return 0; +} + +static const struct dev_pm_ops gpio_vbus_dev_pm_ops = { + .suspend = gpio_vbus_pm_suspend, + .resume = gpio_vbus_pm_resume, +}; +#endif + +/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ + +MODULE_ALIAS("platform:gpio-vbus"); + +static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gpio_vbus_dev_pm_ops, +#endif + }, + .remove = __exit_p(gpio_vbus_remove), +}; + +module_platform_driver_probe(gpio_vbus_driver, gpio_vbus_probe); + +MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); +MODULE_AUTHOR("Philipp Zabel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/isp1301_omap.c b/drivers/usb/phy/isp1301_omap.c new file mode 100644 index 0000000..8fe0c3b --- /dev/null +++ b/drivers/usb/phy/isp1301_omap.c @@ -0,0 +1,1656 @@ +/* + * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#ifndef DEBUG +#undef VERBOSE +#endif + + +#define DRIVER_VERSION "24 August 2004" +#define DRIVER_NAME (isp1301_driver.driver.name) + +MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); +MODULE_LICENSE("GPL"); + +struct isp1301 { + struct usb_phy phy; + struct i2c_client *client; + void (*i2c_release)(struct device *dev); + + int irq_type; + + u32 last_otg_ctrl; + unsigned working:1; + + struct timer_list timer; + + /* use keventd context to change the state for us */ + struct work_struct work; + + unsigned long todo; +# define WORK_UPDATE_ISP 0 /* update ISP from OTG */ +# define WORK_UPDATE_OTG 1 /* update OTG from ISP */ +# define WORK_HOST_RESUME 4 /* resume host */ +# define WORK_TIMER 6 /* timer fired */ +# define WORK_STOP 7 /* don't resubmit */ +}; + + +/* bits in OTG_CTRL */ + +#define OTG_XCEIV_OUTPUTS \ + (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) +#define OTG_XCEIV_INPUTS \ + (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) +#define OTG_CTRL_BITS \ + (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP) + /* and OTG_PULLUP is sometimes written */ + +#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \ + OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \ + OTG_CTRL_BITS) + + +/*-------------------------------------------------------------------------*/ + +/* board-specific PM hooks */ + +#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) + +#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) + +#include + +#else + +static inline int tps65010_set_vbus_draw(unsigned mA) +{ + pr_debug("tps65010: draw %d mA (STUB)\n", mA); + return 0; +} + +#endif + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + int status = tps65010_set_vbus_draw(mA); + if (status < 0) + pr_debug(" VBUS %d mA error %d\n", mA, status); +} + +#else + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + /* H4 controls this by DIP switch S2.4; no soft control. + * ON means the charger is always enabled. Leave it OFF + * unless the OTG port is used only in B-peripheral mode. + */ +} + +#endif + +static void enable_vbus_source(struct isp1301 *isp) +{ + /* this board won't supply more than 8mA vbus power. + * some boards can switch a 100ma "unit load" (or more). + */ +} + + +/* products will deliver OTG messages with LEDs, GUI, etc */ +static inline void notresponding(struct isp1301 *isp) +{ + printk(KERN_NOTICE "OTG device not responding.\n"); +} + + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver isp1301_driver; + +/* smbus apis are used for portability */ + +static inline u8 +isp1301_get_u8(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_byte_data(isp->client, reg + 0); +} + +static inline int +isp1301_get_u16(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_word_data(isp->client, reg); +} + +static inline int +isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(isp->client, reg + 0, bits); +} + +static inline int +isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(isp->client, reg + 1, bits); +} + +/*-------------------------------------------------------------------------*/ + +/* identification */ +#define ISP1301_VENDOR_ID 0x00 /* u16 read */ +#define ISP1301_PRODUCT_ID 0x02 /* u16 read */ +#define ISP1301_BCD_DEVICE 0x14 /* u16 read */ + +#define I2C_VENDOR_ID_PHILIPS 0x04cc +#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301 + +/* operational registers */ +#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */ +# define MC1_SPEED (1 << 0) +# define MC1_SUSPEND (1 << 1) +# define MC1_DAT_SE0 (1 << 2) +# define MC1_TRANSPARENT (1 << 3) +# define MC1_BDIS_ACON_EN (1 << 4) +# define MC1_OE_INT_EN (1 << 5) +# define MC1_UART_EN (1 << 6) +# define MC1_MASK 0x7f +#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */ +# define MC2_GLOBAL_PWR_DN (1 << 0) +# define MC2_SPD_SUSP_CTRL (1 << 1) +# define MC2_BI_DI (1 << 2) +# define MC2_TRANSP_BDIR0 (1 << 3) +# define MC2_TRANSP_BDIR1 (1 << 4) +# define MC2_AUDIO_EN (1 << 5) +# define MC2_PSW_EN (1 << 6) +# define MC2_EN2V7 (1 << 7) +#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */ +# define OTG1_DP_PULLUP (1 << 0) +# define OTG1_DM_PULLUP (1 << 1) +# define OTG1_DP_PULLDOWN (1 << 2) +# define OTG1_DM_PULLDOWN (1 << 3) +# define OTG1_ID_PULLDOWN (1 << 4) +# define OTG1_VBUS_DRV (1 << 5) +# define OTG1_VBUS_DISCHRG (1 << 6) +# define OTG1_VBUS_CHRG (1 << 7) +#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */ +# define OTG_B_SESS_END (1 << 6) +# define OTG_B_SESS_VLD (1 << 7) + +#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */ +#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */ + +#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */ +#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */ + +/* same bitfields in all interrupt registers */ +# define INTR_VBUS_VLD (1 << 0) +# define INTR_SESS_VLD (1 << 1) +# define INTR_DP_HI (1 << 2) +# define INTR_ID_GND (1 << 3) +# define INTR_DM_HI (1 << 4) +# define INTR_ID_FLOAT (1 << 5) +# define INTR_BDIS_ACON (1 << 6) +# define INTR_CR_INT (1 << 7) + +/*-------------------------------------------------------------------------*/ + +static inline const char *state_name(struct isp1301 *isp) +{ + return usb_otg_state_string(isp->phy.state); +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: some of this ISP1301 setup is specific to H2 boards; + * not everything is guarded by board-specific checks, or even using + * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. + * + * ALSO: this currently doesn't use ISP1301 low-power modes + * while OTG is running. + */ + +static void power_down(struct isp1301 *isp) +{ + isp->phy.state = OTG_STATE_UNDEFINED; + + // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); + + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +static void power_up(struct isp1301 *isp) +{ + // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); + + /* do this only when cpu is driving transceiver, + * so host won't see a low speed device... + */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +#define NO_HOST_SUSPEND + +static int host_suspend(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->phy.otg->host) + return -ENODEV; + + /* Currently ASSUMES only the OTG port matters; + * other ports could be active... + */ + dev = isp->phy.otg->host->controller; + return dev->driver->suspend(dev, 3, 0); +#endif +} + +static int host_resume(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->phy.otg->host) + return -ENODEV; + + dev = isp->phy.otg->host->controller; + return dev->driver->resume(dev, 0); +#endif +} + +static int gadget_suspend(struct isp1301 *isp) +{ + isp->phy.otg->gadget->b_hnp_enable = 0; + isp->phy.otg->gadget->a_hnp_support = 0; + isp->phy.otg->gadget->a_alt_hnp_support = 0; + return usb_gadget_vbus_disconnect(isp->phy.otg->gadget); +} + +/*-------------------------------------------------------------------------*/ + +#define TIMER_MINUTES 10 +#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ) + +/* Almost all our I2C messaging comes from a work queue's task context. + * NOTE: guaranteeing certain response times might mean we shouldn't + * share keventd's work queue; a realtime task might be safest. + */ +static void isp1301_defer_work(struct isp1301 *isp, int work) +{ + int status; + + if (isp && !test_and_set_bit(work, &isp->todo)) { + (void) get_device(&isp->client->dev); + status = schedule_work(&isp->work); + if (!status && !isp->working) + dev_vdbg(&isp->client->dev, + "work item %d may be lost\n", work); + } +} + +/* called from irq handlers */ +static void a_idle(struct isp1301 *isp, const char *tag) +{ + u32 l; + + if (isp->phy.state == OTG_STATE_A_IDLE) + return; + + isp->phy.otg->default_a = 1; + if (isp->phy.otg->host) { + isp->phy.otg->host->is_b_host = 0; + host_suspend(isp); + } + if (isp->phy.otg->gadget) { + isp->phy.otg->gadget->is_a_peripheral = 1; + gadget_suspend(isp); + } + isp->phy.state = OTG_STATE_A_IDLE; + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + isp->last_otg_ctrl = l; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +/* called from irq handlers */ +static void b_idle(struct isp1301 *isp, const char *tag) +{ + u32 l; + + if (isp->phy.state == OTG_STATE_B_IDLE) + return; + + isp->phy.otg->default_a = 0; + if (isp->phy.otg->host) { + isp->phy.otg->host->is_b_host = 1; + host_suspend(isp); + } + if (isp->phy.otg->gadget) { + isp->phy.otg->gadget->is_a_peripheral = 0; + gadget_suspend(isp); + } + isp->phy.state = OTG_STATE_B_IDLE; + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + isp->last_otg_ctrl = l; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +static void +dump_regs(struct isp1301 *isp, const char *label) +{ +#ifdef DEBUG + u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); + u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + + pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", + omap_readl(OTG_CTRL), label, state_name(isp), + ctrl, status, src); + /* mode control and irq enables don't change much */ +#endif +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OTG + +/* + * The OMAP OTG controller handles most of the OTG state transitions. + * + * We translate isp1301 outputs (mostly voltage comparator status) into + * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state + * flags into isp1301 inputs ... and infer state transitions. + */ + +#ifdef VERBOSE + +static void check_state(struct isp1301 *isp, const char *tag) +{ + enum usb_otg_state state = OTG_STATE_UNDEFINED; + u8 fsm = omap_readw(OTG_TEST) & 0x0ff; + unsigned extra = 0; + + switch (fsm) { + + /* default-b */ + case 0x0: + state = OTG_STATE_B_IDLE; + break; + case 0x3: + case 0x7: + extra = 1; + case 0x1: + state = OTG_STATE_B_PERIPHERAL; + break; + case 0x11: + state = OTG_STATE_B_SRP_INIT; + break; + + /* extra dual-role default-b states */ + case 0x12: + case 0x13: + case 0x16: + extra = 1; + case 0x17: + state = OTG_STATE_B_WAIT_ACON; + break; + case 0x34: + state = OTG_STATE_B_HOST; + break; + + /* default-a */ + case 0x36: + state = OTG_STATE_A_IDLE; + break; + case 0x3c: + state = OTG_STATE_A_WAIT_VFALL; + break; + case 0x7d: + state = OTG_STATE_A_VBUS_ERR; + break; + case 0x9e: + case 0x9f: + extra = 1; + case 0x89: + state = OTG_STATE_A_PERIPHERAL; + break; + case 0xb7: + state = OTG_STATE_A_WAIT_VRISE; + break; + case 0xb8: + state = OTG_STATE_A_WAIT_BCON; + break; + case 0xb9: + state = OTG_STATE_A_HOST; + break; + case 0xba: + state = OTG_STATE_A_SUSPEND; + break; + default: + break; + } + if (isp->phy.state == state && !extra) + return; + pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, + usb_otg_state_string(state), fsm, state_name(isp), + omap_readl(OTG_CTRL)); +} + +#else + +static inline void check_state(struct isp1301 *isp, const char *tag) { } + +#endif + +/* outputs from ISP1301_INTERRUPT_SOURCE */ +static void update_otg1(struct isp1301 *isp, u8 int_src) +{ + u32 otg_ctrl; + + otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + otg_ctrl &= ~OTG_XCEIV_INPUTS; + otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); + + if (int_src & INTR_SESS_VLD) + otg_ctrl |= OTG_ASESSVLD; + else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) { + a_idle(isp, "vfall"); + otg_ctrl &= ~OTG_CTRL_BITS; + } + if (int_src & INTR_VBUS_VLD) + otg_ctrl |= OTG_VBUSVLD; + if (int_src & INTR_ID_GND) { /* default-A */ + if (isp->phy.state == OTG_STATE_B_IDLE + || isp->phy.state + == OTG_STATE_UNDEFINED) { + a_idle(isp, "init"); + return; + } + } else { /* default-B */ + otg_ctrl |= OTG_ID; + if (isp->phy.state == OTG_STATE_A_IDLE + || isp->phy.state == OTG_STATE_UNDEFINED) { + b_idle(isp, "init"); + return; + } + } + omap_writel(otg_ctrl, OTG_CTRL); +} + +/* outputs from ISP1301_OTG_STATUS */ +static void update_otg2(struct isp1301 *isp, u8 otg_status) +{ + u32 otg_ctrl; + + otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + otg_ctrl &= ~OTG_XCEIV_INPUTS; + otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND); + if (otg_status & OTG_B_SESS_VLD) + otg_ctrl |= OTG_BSESSVLD; + else if (otg_status & OTG_B_SESS_END) + otg_ctrl |= OTG_BSESSEND; + omap_writel(otg_ctrl, OTG_CTRL); +} + +/* inputs going to ISP1301 */ +static void otg_update_isp(struct isp1301 *isp) +{ + u32 otg_ctrl, otg_change; + u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; + + otg_ctrl = omap_readl(OTG_CTRL); + otg_change = otg_ctrl ^ isp->last_otg_ctrl; + isp->last_otg_ctrl = otg_ctrl; + otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; + + switch (isp->phy.state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_SRP_INIT: + if (!(otg_ctrl & OTG_PULLUP)) { + // if (otg_ctrl & OTG_B_HNPEN) { + if (isp->phy.otg->gadget->b_hnp_enable) { + isp->phy.state = OTG_STATE_B_WAIT_ACON; + pr_debug(" --> b_wait_acon\n"); + } + goto pulldown; + } +pullup: + set |= OTG1_DP_PULLUP; + clr |= OTG1_DP_PULLDOWN; + break; + case OTG_STATE_A_SUSPEND: + case OTG_STATE_A_PERIPHERAL: + if (otg_ctrl & OTG_PULLUP) + goto pullup; + /* FALLTHROUGH */ + // case OTG_STATE_B_WAIT_ACON: + default: +pulldown: + set |= OTG1_DP_PULLDOWN; + clr |= OTG1_DP_PULLUP; + break; + } + +# define toggle(OTG,ISP) do { \ + if (otg_ctrl & OTG) set |= ISP; \ + else clr |= ISP; \ + } while (0) + + if (!(isp->phy.otg->host)) + otg_ctrl &= ~OTG_DRV_VBUS; + + switch (isp->phy.state) { + case OTG_STATE_A_SUSPEND: + if (otg_ctrl & OTG_DRV_VBUS) { + set |= OTG1_VBUS_DRV; + break; + } + /* HNP failed for some reason (A_AIDL_BDIS timeout) */ + notresponding(isp); + + /* FALLTHROUGH */ + case OTG_STATE_A_VBUS_ERR: + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + pr_debug(" --> a_wait_vfall\n"); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VFALL: + /* FIXME usbcore thinks port power is still on ... */ + clr |= OTG1_VBUS_DRV; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl & OTG_DRV_VBUS) { + isp->phy.state = OTG_STATE_A_WAIT_VRISE; + pr_debug(" --> a_wait_vrise\n"); + } + /* FALLTHROUGH */ + default: + toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); + } + + toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); + toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); + +# undef toggle + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); + + /* HNP switch to host or peripheral; and SRP */ + if (otg_change & OTG_PULLUP) { + u32 l; + + switch (isp->phy.state) { + case OTG_STATE_B_IDLE: + if (clr & OTG1_DP_PULLUP) + break; + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + break; + case OTG_STATE_A_SUSPEND: + if (clr & OTG1_DP_PULLUP) + break; + isp->phy.state = OTG_STATE_A_PERIPHERAL; + pr_debug(" --> a_peripheral\n"); + break; + default: + break; + } + l = omap_readl(OTG_CTRL); + l |= OTG_PULLUP; + omap_writel(l, OTG_CTRL); + } + + check_state(isp, __func__); + dump_regs(isp, "otg->isp1301"); +} + +static irqreturn_t omap_otg_irq(int irq, void *_isp) +{ + u16 otg_irq = omap_readw(OTG_IRQ_SRC); + u32 otg_ctrl; + int ret = IRQ_NONE; + struct isp1301 *isp = _isp; + struct usb_otg *otg = isp->phy.otg; + + /* update ISP1301 transceiver from OTG controller */ + if (otg_irq & OPRT_CHG) { + omap_writew(OPRT_CHG, OTG_IRQ_SRC); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + ret = IRQ_HANDLED; + + /* SRP to become b_peripheral failed */ + } else if (otg_irq & B_SRP_TMROUT) { + pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL)); + notresponding(isp); + + /* gadget drivers that care should monitor all kinds of + * remote wakeup (SRP, normal) using their own timer + * to give "check cable and A-device" messages. + */ + if (isp->phy.state == OTG_STATE_B_SRP_INIT) + b_idle(isp, "srp_timeout"); + + omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* HNP to become b_host failed */ + } else if (otg_irq & B_HNP_FAIL) { + pr_debug("otg: %s B_HNP_FAIL, %06x\n", + state_name(isp), omap_readl(OTG_CTRL)); + notresponding(isp); + + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + + /* subset of b_peripheral()... */ + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + + omap_writew(B_HNP_FAIL, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* detect SRP from B-device ... */ + } else if (otg_irq & A_SRP_DETECT) { + pr_debug("otg: %s SRP_DETECT, %06x\n", + state_name(isp), omap_readl(OTG_CTRL)); + + isp1301_defer_work(isp, WORK_UPDATE_OTG); + switch (isp->phy.state) { + case OTG_STATE_A_IDLE: + if (!otg->host) + break; + isp1301_defer_work(isp, WORK_HOST_RESUME); + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & ~OTG_XCEIV_INPUTS + & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + break; + default: + break; + } + + omap_writew(A_SRP_DETECT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) + * we don't track them separately + */ + } else if (otg_irq & A_REQ_TMROUT) { + otg_ctrl = omap_readl(OTG_CTRL); + pr_info("otg: BCON_TMOUT from %s, %06x\n", + state_name(isp), otg_ctrl); + notresponding(isp); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + + omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* A-supplied voltage fell too low; overcurrent */ + } else if (otg_irq & A_VBUS_ERR) { + otg_ctrl = omap_readl(OTG_CTRL); + printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", + state_name(isp), otg_irq, otg_ctrl); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_A_VBUS_ERR; + + omap_writew(A_VBUS_ERR, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* switch driver; the transceiver code activates it, + * ungating the udc clock or resuming OHCI. + */ + } else if (otg_irq & DRIVER_SWITCH) { + int kick = 0; + + otg_ctrl = omap_readl(OTG_CTRL); + printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", + state_name(isp), + (otg_ctrl & OTG_DRIVER_SEL) + ? "gadget" : "host", + otg_ctrl); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is peripheral */ + if (otg_ctrl & OTG_DRIVER_SEL) { + switch (isp->phy.state) { + case OTG_STATE_A_IDLE: + b_idle(isp, __func__); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is host */ + } else { + if (!(otg_ctrl & OTG_ID)) { + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL); + } + + if (otg->host) { + switch (isp->phy.state) { + case OTG_STATE_B_WAIT_ACON: + isp->phy.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host\n"); + kick = 1; + break; + case OTG_STATE_A_WAIT_BCON: + isp->phy.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + break; + case OTG_STATE_A_PERIPHERAL: + isp->phy.state = OTG_STATE_A_WAIT_BCON; + pr_debug(" --> a_wait_bcon\n"); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_HOST_RESUME); + } + } + + omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + if (kick) + usb_bus_start_enum(otg->host, otg->host->otg_port); + } + + check_state(isp, __func__); + return ret; +} + +static struct platform_device *otg_dev; + +static int isp1301_otg_init(struct isp1301 *isp) +{ + u32 l; + + if (!otg_dev) + return -ENODEV; + + dump_regs(isp, __func__); + /* some of these values are board-specific... */ + l = omap_readl(OTG_SYSCON_2); + l |= OTG_EN + /* for B-device: */ + | SRP_GPDATA /* 9msec Bdev D+ pulse */ + | SRP_GPDVBUS /* discharge after VBUS pulse */ + // | (3 << 24) /* 2msec VBUS pulse */ + /* for A-device: */ + | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ + | SRP_DPW /* detect 167+ns SRP pulses */ + | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ + ; + omap_writel(l, OTG_SYSCON_2); + + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); + + check_state(isp, __func__); + pr_debug("otg: %s, %s %06x\n", + state_name(isp), __func__, omap_readl(OTG_CTRL)); + + omap_writew(DRIVER_SWITCH | OPRT_CHG + | B_SRP_TMROUT | B_HNP_FAIL + | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN); + + l = omap_readl(OTG_SYSCON_2); + l |= OTG_EN; + omap_writel(l, OTG_SYSCON_2); + + return 0; +} + +static int otg_probe(struct platform_device *dev) +{ + // struct omap_usb_config *config = dev->platform_data; + + otg_dev = dev; + return 0; +} + +static int otg_remove(struct platform_device *dev) +{ + otg_dev = NULL; + return 0; +} + +static struct platform_driver omap_otg_driver = { + .probe = otg_probe, + .remove = otg_remove, + .driver = { + .owner = THIS_MODULE, + .name = "omap_otg", + }, +}; + +static int otg_bind(struct isp1301 *isp) +{ + int status; + + if (otg_dev) + return -EBUSY; + + status = platform_driver_register(&omap_otg_driver); + if (status < 0) + return status; + + if (otg_dev) + status = request_irq(otg_dev->resource[1].start, omap_otg_irq, + 0, DRIVER_NAME, isp); + else + status = -ENODEV; + + if (status < 0) + platform_driver_unregister(&omap_otg_driver); + return status; +} + +static void otg_unbind(struct isp1301 *isp) +{ + if (!otg_dev) + return; + free_irq(otg_dev->resource[1].start, isp); +} + +#else + +/* OTG controller isn't clocked */ + +#endif /* CONFIG_USB_OTG */ + +/*-------------------------------------------------------------------------*/ + +static void b_peripheral(struct isp1301 *isp) +{ + u32 l; + + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + + usb_gadget_vbus_connect(isp->phy.otg->gadget); + +#ifdef CONFIG_USB_OTG + enable_vbus_draw(isp, 8); + otg_update_isp(isp); +#else + enable_vbus_draw(isp, 100); + /* UDC driver just set OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + dump_regs(isp, "2periph"); +#endif +} + +static void isp_update_otg(struct isp1301 *isp, u8 stat) +{ + struct usb_otg *otg = isp->phy.otg; + u8 isp_stat, isp_bstat; + enum usb_otg_state state = isp->phy.state; + + if (stat & INTR_BDIS_ACON) + pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); + + /* start certain state transitions right away */ + isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + if (isp_stat & INTR_ID_GND) { + if (otg->default_a) { + switch (state) { + case OTG_STATE_B_IDLE: + a_idle(isp, "idle"); + /* FALLTHROUGH */ + case OTG_STATE_A_IDLE: + enable_vbus_source(isp); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VRISE: + /* we skip over OTG_STATE_A_WAIT_BCON, since + * the HC will transition to A_HOST (or + * A_SUSPEND!) without our noticing except + * when HNP is used. + */ + if (isp_stat & INTR_VBUS_VLD) + isp->phy.state = OTG_STATE_A_HOST; + break; + case OTG_STATE_A_WAIT_VFALL: + if (!(isp_stat & INTR_SESS_VLD)) + a_idle(isp, "vfell"); + break; + default: + if (!(isp_stat & INTR_VBUS_VLD)) + isp->phy.state = OTG_STATE_A_VBUS_ERR; + break; + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + } else { + switch (state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_HOST: + case OTG_STATE_B_WAIT_ACON: + usb_gadget_vbus_disconnect(otg->gadget); + break; + default: + break; + } + if (state != OTG_STATE_A_IDLE) + a_idle(isp, "id"); + if (otg->host && state == OTG_STATE_A_IDLE) + isp1301_defer_work(isp, WORK_HOST_RESUME); + isp_bstat = 0; + } + } else { + u32 l; + + /* if user unplugged mini-A end of cable, + * don't bypass A_WAIT_VFALL. + */ + if (otg->default_a) { + switch (state) { + default: + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_WAIT_VFALL: + state = OTG_STATE_A_IDLE; + /* khubd may take a while to notice and + * handle this disconnect, so don't go + * to B_IDLE quite yet. + */ + break; + case OTG_STATE_A_IDLE: + host_suspend(isp); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); + isp->phy.state = OTG_STATE_B_IDLE; + l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + l &= ~OTG_CTRL_BITS; + omap_writel(l, OTG_CTRL); + break; + case OTG_STATE_B_IDLE: + break; + } + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + + switch (isp->phy.state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: + if (likely(isp_bstat & OTG_B_SESS_VLD)) + break; + enable_vbus_draw(isp, 0); +#ifndef CONFIG_USB_OTG + /* UDC driver will clear OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLUP); + dump_regs(isp, __func__); +#endif + /* FALLTHROUGH */ + case OTG_STATE_B_SRP_INIT: + b_idle(isp, __func__); + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + /* FALLTHROUGH */ + case OTG_STATE_B_IDLE: + if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) { +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); +#endif + b_peripheral(isp); + } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) + isp_bstat |= OTG_B_SESS_END; + break; + case OTG_STATE_A_WAIT_VFALL: + break; + default: + pr_debug("otg: unsupported b-device %s\n", + state_name(isp)); + break; + } + } + + if (state != isp->phy.state) + pr_debug(" isp, %s -> %s\n", + usb_otg_state_string(state), state_name(isp)); + +#ifdef CONFIG_USB_OTG + /* update the OTG controller state to match the isp1301; may + * trigger OPRT_CHG irqs for changes going to the isp1301. + */ + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); + check_state(isp, __func__); +#endif + + dump_regs(isp, "isp1301->otg"); +} + +/*-------------------------------------------------------------------------*/ + +static u8 isp1301_clear_latch(struct isp1301 *isp) +{ + u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); + return latch; +} + +static void +isp1301_work(struct work_struct *work) +{ + struct isp1301 *isp = container_of(work, struct isp1301, work); + int stop; + + /* implicit lock: we're the only task using this device */ + isp->working = 1; + do { + stop = test_bit(WORK_STOP, &isp->todo); + +#ifdef CONFIG_USB_OTG + /* transfer state from otg engine to isp1301 */ + if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { + otg_update_isp(isp); + put_device(&isp->client->dev); + } +#endif + /* transfer state from isp1301 to otg engine */ + if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { + u8 stat = isp1301_clear_latch(isp); + + isp_update_otg(isp, stat); + put_device(&isp->client->dev); + } + + if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { + u32 otg_ctrl; + + /* + * skip A_WAIT_VRISE; hc transitions invisibly + * skip A_WAIT_BCON; same. + */ + switch (isp->phy.state) { + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + isp->phy.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + break; + case OTG_STATE_B_WAIT_ACON: + isp->phy.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host (acon)\n"); + break; + case OTG_STATE_B_HOST: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_IDLE: + break; + default: + pr_debug(" host resume in %s\n", + state_name(isp)); + } + host_resume(isp); + // mdelay(10); + put_device(&isp->client->dev); + } + + if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { +#ifdef VERBOSE + dump_regs(isp, "timer"); + if (!stop) + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); +#endif + put_device(&isp->client->dev); + } + + if (isp->todo) + dev_vdbg(&isp->client->dev, + "work done, todo = 0x%lx\n", + isp->todo); + if (stop) { + dev_dbg(&isp->client->dev, "stop\n"); + break; + } + } while (isp->todo); + isp->working = 0; +} + +static irqreturn_t isp1301_irq(int irq, void *isp) +{ + isp1301_defer_work(isp, WORK_UPDATE_OTG); + return IRQ_HANDLED; +} + +static void isp1301_timer(unsigned long _isp) +{ + isp1301_defer_work((void *)_isp, WORK_TIMER); +} + +/*-------------------------------------------------------------------------*/ + +static void isp1301_release(struct device *dev) +{ + struct isp1301 *isp; + + isp = dev_get_drvdata(dev); + + /* FIXME -- not with a "new style" driver, it doesn't!! */ + + /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ + if (isp->i2c_release) + isp->i2c_release(dev); + kfree(isp->phy.otg); + kfree (isp); +} + +static struct isp1301 *the_transceiver; + +static int __exit isp1301_remove(struct i2c_client *i2c) +{ + struct isp1301 *isp; + + isp = i2c_get_clientdata(i2c); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + free_irq(i2c->irq, isp); +#ifdef CONFIG_USB_OTG + otg_unbind(isp); +#endif + if (machine_is_omap_h2()) + gpio_free(2); + + isp->timer.data = 0; + set_bit(WORK_STOP, &isp->todo); + del_timer_sync(&isp->timer); + flush_work(&isp->work); + + put_device(&i2c->dev); + the_transceiver = NULL; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: three modes are possible here, only one of which + * will be standards-conformant on any given system: + * + * - OTG mode (dual-role), required if there's a Mini-AB connector + * - HOST mode, for when there's one or more A (host) connectors + * - DEVICE mode, for when there's a B/Mini-B (device) connector + * + * As a rule, you won't have an isp1301 chip unless it's there to + * support the OTG mode. Other modes help testing USB controllers + * in isolation from (full) OTG support, or maybe so later board + * revisions can help to support those feature. + */ + +#ifdef CONFIG_USB_OTG + +static int isp1301_otg_enable(struct isp1301 *isp) +{ + power_up(isp); + isp1301_otg_init(isp); + + /* NOTE: since we don't change this, this provides + * a few more interrupts than are strictly needed. + */ + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + + dev_info(&isp->client->dev, "ready for dual-role USB ...\n"); + + return 0; +} + +#endif + +/* add or disable the host device+driver */ +static int +isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!host) { + omap_writew(0, OTG_IRQ_EN); + power_down(isp); + otg->host = NULL; + return 0; + } + +#ifdef CONFIG_USB_OTG + otg->host = host; + dev_dbg(&isp->client->dev, "registered host\n"); + host_suspend(isp); + if (otg->gadget) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_GADGET_OMAP) + // FIXME update its refcount + otg->host = host; + + power_up(isp); + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + dev_info(&isp->client->dev, "A-Host sessions ok\n"); + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_ID_GND); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, especially with + * the Mini-A end of an OTG cable. (Or something nonstandard + * like MiniB-to-StandardB, maybe built with a gender mender.) + */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); + + dump_regs(isp, __func__); + + return 0; + +#else + dev_dbg(&isp->client->dev, "host sessions not allowed\n"); + return -EINVAL; +#endif + +} + +static int +isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!gadget) { + omap_writew(0, OTG_IRQ_EN); + if (!otg->default_a) + enable_vbus_draw(isp, 0); + usb_gadget_vbus_disconnect(otg->gadget); + otg->gadget = NULL; + power_down(isp); + return 0; + } + +#ifdef CONFIG_USB_OTG + otg->gadget = gadget; + dev_dbg(&isp->client->dev, "registered gadget\n"); + /* gadget driver may be suspended until vbus_connect () */ + if (otg->host) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) + otg->gadget = gadget; + // FIXME update its refcount + + { + u32 l; + + l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); + l |= OTG_ID; + omap_writel(l, OTG_CTRL); + } + + power_up(isp); + isp->phy.state = OTG_STATE_B_IDLE; + + if (machine_is_omap_h2() || machine_is_omap_h3()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_SESS_VLD); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD); + dev_info(&isp->client->dev, "B-Peripheral sessions ok\n"); + dump_regs(isp, __func__); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, so long + * as you don't plug a Mini-A cable into the jack. + */ + if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) + b_peripheral(isp); + + return 0; + +#else + dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n"); + return -EINVAL; +#endif +} + + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_set_power(struct usb_phy *dev, unsigned mA) +{ + if (!the_transceiver) + return -ENODEV; + if (dev->state == OTG_STATE_B_PERIPHERAL) + enable_vbus_draw(the_transceiver, mA); + return 0; +} + +static int +isp1301_start_srp(struct usb_otg *otg) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + u32 otg_ctrl; + + if (!otg || isp != the_transceiver + || isp->phy.state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_ctrl = omap_readl(OTG_CTRL); + if (!(otg_ctrl & OTG_BSESSEND)) + return -EINVAL; + + otg_ctrl |= OTG_B_BUSREQ; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_B_SRP_INIT; + + pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), + omap_readl(OTG_CTRL)); +#ifdef CONFIG_USB_OTG + check_state(isp, __func__); +#endif + return 0; +} + +static int +isp1301_start_hnp(struct usb_otg *otg) +{ +#ifdef CONFIG_USB_OTG + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + u32 l; + + if (!otg || isp != the_transceiver) + return -ENODEV; + if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) + return -ENOTCONN; + if (!otg->default_a && (otg->gadget == NULL + || !otg->gadget->b_hnp_enable)) + return -ENOTCONN; + + /* We want hardware to manage most HNP protocol timings. + * So do this part as early as possible... + */ + switch (isp->phy.state) { + case OTG_STATE_B_HOST: + isp->phy.state = OTG_STATE_B_PERIPHERAL; + /* caller will suspend next */ + break; + case OTG_STATE_A_HOST: +#if 0 + /* autoconnect mode avoids irq latency bugs */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); +#endif + /* caller must suspend then clear A_BUSREQ */ + usb_gadget_vbus_connect(otg->gadget); + l = omap_readl(OTG_CTRL); + l |= OTG_A_SETB_HNPEN; + omap_writel(l, OTG_CTRL); + + break; + case OTG_STATE_A_PERIPHERAL: + /* initiated by B-Host suspend */ + break; + default: + return -EILSEQ; + } + pr_debug("otg: HNP %s, %06x ...\n", + state_name(isp), omap_readl(OTG_CTRL)); + check_state(isp, __func__); + return 0; +#else + /* srp-only */ + return -EINVAL; +#endif +} + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int status; + struct isp1301 *isp; + + if (the_transceiver) + return 0; + + isp = kzalloc(sizeof *isp, GFP_KERNEL); + if (!isp) + return 0; + + isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL); + if (!isp->phy.otg) { + kfree(isp); + return 0; + } + + INIT_WORK(&isp->work, isp1301_work); + init_timer(&isp->timer); + isp->timer.function = isp1301_timer; + isp->timer.data = (unsigned long) isp; + + i2c_set_clientdata(i2c, isp); + isp->client = i2c; + + /* verify the chip (shouldn't be necessary) */ + status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); + if (status != I2C_VENDOR_ID_PHILIPS) { + dev_dbg(&i2c->dev, "not philips id: %d\n", status); + goto fail; + } + status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); + if (status != I2C_PRODUCT_ID_PHILIPS_1301) { + dev_dbg(&i2c->dev, "not isp1301, %d\n", status); + goto fail; + } + isp->i2c_release = i2c->dev.release; + i2c->dev.release = isp1301_release; + + /* initial development used chiprev 2.00 */ + status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); + dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", + status >> 8, status & 0xff); + + /* make like power-on reset */ + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); + + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + +#ifdef CONFIG_USB_OTG + status = otg_bind(isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't bind OTG\n"); + goto fail; + } +#endif + + if (machine_is_omap_h2()) { + /* full speed signaling by default */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_SPEED); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, + MC2_SPD_SUSP_CTRL); + + /* IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); + if (gpio_request(2, "isp1301") == 0) + gpio_direction_input(2); + isp->irq_type = IRQF_TRIGGER_FALLING; + } + + status = request_irq(i2c->irq, isp1301_irq, + isp->irq_type, DRIVER_NAME, isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", + i2c->irq, status); + goto fail; + } + + isp->phy.dev = &i2c->dev; + isp->phy.label = DRIVER_NAME; + isp->phy.set_power = isp1301_set_power, + + isp->phy.otg->phy = &isp->phy; + isp->phy.otg->set_host = isp1301_set_host, + isp->phy.otg->set_peripheral = isp1301_set_peripheral, + isp->phy.otg->start_srp = isp1301_start_srp, + isp->phy.otg->start_hnp = isp1301_start_hnp, + + enable_vbus_draw(isp, 0); + power_down(isp); + the_transceiver = isp; + +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); +#endif + + dump_regs(isp, __func__); + +#ifdef VERBOSE + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); + dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); +#endif + + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); + if (status < 0) + dev_err(&i2c->dev, "can't register transceiver, %d\n", + status); + + return 0; + +fail: + kfree(isp->phy.otg); + kfree(isp); + return -ENODEV; +} + +static const struct i2c_device_id isp1301_id[] = { + { "isp1301_omap", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isp1301_id); + +static struct i2c_driver isp1301_driver = { + .driver = { + .name = "isp1301_omap", + }, + .probe = isp1301_probe, + .remove = __exit_p(isp1301_remove), + .id_table = isp1301_id, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp_init(void) +{ + return i2c_add_driver(&isp1301_driver); +} +subsys_initcall(isp_init); + +static void __exit isp_exit(void) +{ + if (the_transceiver) + usb_remove_phy(&the_transceiver->phy); + i2c_del_driver(&isp1301_driver); +} +module_exit(isp_exit); + diff --git a/drivers/usb/phy/msm_otg.c b/drivers/usb/phy/msm_otg.c new file mode 100644 index 0000000..749fbf4 --- /dev/null +++ b/drivers/usb/phy/msm_otg.c @@ -0,0 +1,1762 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MSM_USB_BASE (motg->regs) +#define DRIVER_NAME "msm_otg" + +#define ULPI_IO_TIMEOUT_USEC (10 * 1000) + +#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ +#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ +#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ +#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ +#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ +#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ +#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ + +static struct regulator *hsusb_3p3; +static struct regulator *hsusb_1p8; +static struct regulator *hsusb_vddcx; + +static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) +{ + int ret = 0; + + if (init) { + hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); + if (IS_ERR(hsusb_vddcx)) { + dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); + return PTR_ERR(hsusb_vddcx); + } + + ret = regulator_set_voltage(hsusb_vddcx, + USB_PHY_VDD_DIG_VOL_MIN, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) { + dev_err(motg->phy.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + return ret; + } + + ret = regulator_enable(hsusb_vddcx); + if (ret) { + dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + } + } else { + ret = regulator_set_voltage(hsusb_vddcx, 0, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) + dev_err(motg->phy.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + ret = regulator_disable(hsusb_vddcx); + if (ret) + dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); + + regulator_put(hsusb_vddcx); + } + + return ret; +} + +static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) +{ + int rc = 0; + + if (init) { + hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); + if (IS_ERR(hsusb_3p3)) { + dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); + return PTR_ERR(hsusb_3p3); + } + + rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + USB_PHY_3P3_VOL_MAX); + if (rc) { + dev_err(motg->phy.dev, "unable to set voltage level " + "for hsusb 3p3\n"); + goto put_3p3; + } + rc = regulator_enable(hsusb_3p3); + if (rc) { + dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); + goto put_3p3; + } + hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); + if (IS_ERR(hsusb_1p8)) { + dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); + rc = PTR_ERR(hsusb_1p8); + goto disable_3p3; + } + rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + USB_PHY_1P8_VOL_MAX); + if (rc) { + dev_err(motg->phy.dev, "unable to set voltage level " + "for hsusb 1p8\n"); + goto put_1p8; + } + rc = regulator_enable(hsusb_1p8); + if (rc) { + dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); + goto put_1p8; + } + + return 0; + } + + regulator_disable(hsusb_1p8); +put_1p8: + regulator_put(hsusb_1p8); +disable_3p3: + regulator_disable(hsusb_3p3); +put_3p3: + regulator_put(hsusb_3p3); + return rc; +} + +#ifdef CONFIG_PM_SLEEP +#define USB_PHY_SUSP_DIG_VOL 500000 +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_VDD_DIG_VOL_MIN; + else + min_vol = USB_PHY_SUSP_DIG_VOL; + + ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator " + "HSUSB_VDDCX\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} +#endif + +static int msm_hsusb_ldo_set_mode(int on) +{ + int ret = 0; + + if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { + pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); + return -ENODEV; + } + + if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { + pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); + return -ENODEV; + } + + if (on) { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_1p8\n", __func__); + return ret; + } + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_3p3\n", __func__); + regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + return ret; + } + } else { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_1p8\n", __func__); + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_3p3\n", __func__); + } + + pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); + return ret < 0 ? ret : 0; +} + +static int ulpi_read(struct usb_phy *phy, u32 reg) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + int cnt = 0; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(phy->dev, "ulpi_read: timeout %08x\n", + readl(USB_ULPI_VIEWPORT)); + return -ETIMEDOUT; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + int cnt = 0; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(phy->dev, "ulpi_write: timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + +static struct usb_phy_io_ops msm_otg_io_ops = { + .read = ulpi_read, + .write = ulpi_write, +}; + +static void ulpi_init(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + int *seq = pdata->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", + seq[0], seq[1]); + ulpi_write(&motg->phy, seq[0], seq[1]); + seq += 2; + } +} + +static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(motg->clk, CLK_RESET_ASSERT); + if (ret) + dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); + } else { + ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); + } + return ret; +} + +static int msm_otg_phy_clk_reset(struct msm_otg *motg) +{ + int ret; + + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); + if (ret) { + dev_err(motg->phy.dev, "usb phy clk assert failed\n"); + return ret; + } + usleep_range(10000, 12000); + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); + return ret; +} + +static int msm_otg_phy_reset(struct msm_otg *motg) +{ + u32 val; + int ret; + int retries; + + ret = msm_otg_link_clk_reset(motg, 1); + if (ret) + return ret; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + ret = msm_otg_link_clk_reset(motg, 0); + if (ret) + return ret; + + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, + ULPI_CLR(ULPI_FUNC_CTRL)); + if (!ret) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + /* This reset calibrates the phy, if the above write succeeded */ + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_read(&motg->phy, ULPI_DEBUG); + if (ret != -ETIMEDOUT) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + dev_info(motg->phy.dev, "phy_reset: success\n"); + return 0; +} + +#define LINK_RESET_TIMEOUT_USEC (250 * 1000) +static int msm_otg_reset(struct usb_phy *phy) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + int ret; + u32 val = 0; + u32 ulpi_val = 0; + + ret = msm_otg_phy_reset(motg); + if (ret) { + dev_err(phy->dev, "phy_reset failed\n"); + return ret; + } + + ulpi_init(motg); + + writel(USBCMD_RESET, USB_USBCMD); + while (cnt < LINK_RESET_TIMEOUT_USEC) { + if (!(readl(USB_USBCMD) & USBCMD_RESET)) + break; + udelay(1); + cnt++; + } + if (cnt >= LINK_RESET_TIMEOUT_USEC) + return -ETIMEDOUT; + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + msleep(100); + + writel(0x0, USB_AHBBURST); + writel(0x00, USB_AHBMODE); + + if (pdata->otg_control == OTG_PHY_CONTROL) { + val = readl(USB_OTGSC); + if (pdata->mode == USB_OTG) { + ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; + val |= OTGSC_IDIE | OTGSC_BSVIE; + } else if (pdata->mode == USB_PERIPHERAL) { + ulpi_val = ULPI_INT_SESS_VALID; + val |= OTGSC_BSVIE; + } + writel(val, USB_OTGSC); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); + } + + return 0; +} + +#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) +#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_suspend(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + + if (atomic_read(&motg->in_lpm)) + return 0; + + disable_irq(motg->irq); + /* + * Chipidea 45-nm PHY suspend sequence: + * + * Interrupt Latch Register auto-clear feature is not present + * in all PHY versions. Latch register is clear on read type. + * Clear latch register to avoid spurious wakeup from + * low power mode (LPM). + * + * PHY comparators are disabled when PHY enters into low power + * mode (LPM). Keep PHY comparators ON in LPM only when we expect + * VBUS/Id notifications from USB PHY. Otherwise turn off USB + * PHY comparators. This save significant amount of power. + * + * PLL is not turned off when PHY enters into low power mode (LPM). + * Disable PLL for maximum power savings. + */ + + if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { + ulpi_read(phy, 0x14); + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(phy, 0x01, 0x30); + ulpi_write(phy, 0x08, 0x09); + } + + /* + * PHY may take some time or even fail to enter into low power + * mode (LPM). Hence poll for 500 msec and reset the PHY and link + * in failure case. + */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { + dev_err(phy->dev, "Unable to suspend PHY\n"); + msm_otg_reset(phy); + enable_irq(motg->irq); + return -ETIMEDOUT; + } + + /* + * PHY has capability to generate interrupt asynchronously in low + * power mode (LPM). This interrupt is level triggered. So USB IRQ + * line must be disabled till async interrupt enable bit is cleared + * in USBCMD register. Assert STP (ULPI interface STOP signal) to + * block data communication from PHY. + */ + writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) + writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + if (!IS_ERR(motg->pclk_src)) + clk_disable(motg->pclk_src); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(0); + msm_hsusb_config_vddcx(0); + } + + if (device_may_wakeup(phy->dev)) + enable_irq_wake(motg->irq); + if (bus) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 1); + enable_irq(motg->irq); + + dev_info(phy->dev, "USB in low power mode\n"); + + return 0; +} + +static int msm_otg_resume(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; + int cnt = 0; + unsigned temp; + + if (!atomic_read(&motg->in_lpm)) + return 0; + + if (!IS_ERR(motg->pclk_src)) + clk_enable(motg->pclk_src); + + clk_enable(motg->pclk); + clk_enable(motg->clk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(1); + msm_hsusb_config_vddcx(1); + writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + } + + temp = readl(USB_USBCMD); + temp &= ~ASYNC_INTR_CTRL; + temp &= ~ULPI_STP_CTRL; + writel(temp, USB_USBCMD); + + /* + * PHY comes out of low power mode (LPM) in case of wakeup + * from asynchronous interrupt. + */ + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + goto skip_phy_resume; + + writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_RESUME_TIMEOUT_USEC) { + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_RESUME_TIMEOUT_USEC) { + /* + * This is a fatal error. Reset the link and + * PHY. USB state can not be restored. Re-insertion + * of USB cable is the only way to get USB working. + */ + dev_err(phy->dev, "Unable to resume USB." + "Re-plugin the cable\n"); + msm_otg_reset(phy); + } + +skip_phy_resume: + if (device_may_wakeup(phy->dev)) + disable_irq_wake(motg->irq); + if (bus) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 0); + + if (motg->async_int) { + motg->async_int = 0; + pm_runtime_put(phy->dev); + enable_irq(motg->irq); + } + + dev_info(phy->dev, "USB exited from low power mode\n"); + + return 0; +} +#endif + +static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) +{ + if (motg->cur_power == mA) + return; + + /* TODO: Notify PMIC about available current */ + dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); + motg->cur_power = mA; +} + +static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + + /* + * Gadget driver uses set_power method to notify about the + * available current based on suspend/configured states. + * + * IDEV_CHG can be drawn irrespective of suspend/un-configured + * states when CDP/ACA is connected. + */ + if (motg->chg_type == USB_SDP_CHARGER) + msm_otg_notify_charger(motg, mA); + + return 0; +} + +static void msm_otg_start_host(struct usb_phy *phy, int on) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + struct usb_hcd *hcd; + + if (!phy->otg->host) + return; + + hcd = bus_to_hcd(phy->otg->host); + + if (on) { + dev_dbg(phy->dev, "host on\n"); + + if (pdata->vbus_power) + pdata->vbus_power(1); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Enable internal + * HUB before kicking the host. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_A_HOST); +#ifdef CONFIG_USB + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); +#endif + } else { + dev_dbg(phy->dev, "host off\n"); + +#ifdef CONFIG_USB + usb_remove_hcd(hcd); +#endif + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + if (pdata->vbus_power) + pdata->vbus_power(0); + } +} + +static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + struct usb_hcd *hcd; + + /* + * Fail host registration if this board can support + * only peripheral configuration. + */ + if (motg->pdata->mode == USB_PERIPHERAL) { + dev_info(otg->phy->dev, "Host mode is not supported\n"); + return -ENODEV; + } + + if (!host) { + if (otg->phy->state == OTG_STATE_A_HOST) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_host(otg->phy, 0); + otg->host = NULL; + otg->phy->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->host = NULL; + } + + return 0; + } + + hcd = bus_to_hcd(host); + hcd->power_budget = motg->pdata->power_budget; + + otg->host = host; + dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if peripheral is not supported + * or peripheral is already registered with us. + */ + if (motg->pdata->mode == USB_HOST || otg->gadget) { + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +static void msm_otg_start_peripheral(struct usb_phy *phy, int on) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + + if (!phy->otg->gadget) + return; + + if (on) { + dev_dbg(phy->dev, "gadget on\n"); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Disable internal + * HUB before kicking the gadget. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); + usb_gadget_vbus_connect(phy->otg->gadget); + } else { + dev_dbg(phy->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(phy->otg->gadget); + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + } + +} + +static int msm_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + + /* + * Fail peripheral registration if this board can support + * only host configuration. + */ + if (motg->pdata->mode == USB_HOST) { + dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); + return -ENODEV; + } + + if (!gadget) { + if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_peripheral(otg->phy, 0); + otg->gadget = NULL; + otg->phy->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->gadget = NULL; + } + + return 0; + } + otg->gadget = gadget; + dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if host is not supported + * or host is already registered with us. + */ + if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +static bool msm_chg_check_secondary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_secondary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(phy, chg_det, 0x34); + /* put it in host mode for enabling D- source */ + chg_det &= ~(1 << 2); + ulpi_write(phy, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DM as current source, DP as current sink + * and enable battery charging comparators. + */ + ulpi_write(phy, 0x8, 0x85); + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_primary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_primary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DP as current source, DM as current sink + * and enable battery charging comparators. + */ + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 line_state; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + line_state = ulpi_read(phy, 0x15); + ret = !(line_state & 1); + break; + case SNPS_28NM_INTEGRATED_PHY: + line_state = ulpi_read(phy, 0x87); + ret = line_state & 2; + break; + default: + break; + } + return ret; +} + +static void msm_chg_disable_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + chg_det &= ~(1 << 5); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + ulpi_write(phy, 0x10, 0x86); + break; + default: + break; + } +} + +static void msm_chg_enable_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn on D+ current source */ + chg_det |= (1 << 5); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Data contact detection enable */ + ulpi_write(phy, 0x10, 0x85); + break; + default: + break; + } +} + +static void msm_chg_block_on(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 func_ctrl, chg_det; + + /* put the controller in non-driving mode */ + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(phy, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(phy, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); + udelay(100); + break; + default: + break; + } +} + +static void msm_chg_block_off(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 func_ctrl, chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(phy, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); + break; + default: + break; + } + + /* put the controller in normal mode */ + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); +} + +#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ +#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ +#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ +#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ +static void msm_chg_detect_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); + struct usb_phy *phy = &motg->phy; + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(phy->dev, "chg detection work\n"); + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + pm_runtime_get_sync(phy->dev); + msm_chg_block_on(motg); + msm_chg_enable_dcd(motg); + motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + motg->dcd_retries = 0; + delay = MSM_CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + is_dcd = msm_chg_check_dcd(motg); + tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; + if (is_dcd || tmout) { + msm_chg_disable_dcd(motg); + msm_chg_enable_primary_det(motg); + delay = MSM_CHG_PRIMARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + delay = MSM_CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = msm_chg_check_primary_det(motg); + if (vout) { + msm_chg_enable_secondary_det(motg); + delay = MSM_CHG_SECONDARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + motg->chg_type = USB_SDP_CHARGER; + motg->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = msm_chg_check_secondary_det(motg); + if (vout) + motg->chg_type = USB_DCP_CHARGER; + else + motg->chg_type = USB_CDP_CHARGER; + motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + motg->chg_state = USB_CHG_STATE_DETECTED; + case USB_CHG_STATE_DETECTED: + msm_chg_block_off(motg); + dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); + schedule_work(&motg->sm_work); + return; + default: + return; + } + + schedule_delayed_work(&motg->chg_work, delay); +} + +/* + * We support OTG, Peripheral only and Host only configurations. In case + * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen + * via Id pin status or user request (debugfs). Id/BSV interrupts are not + * enabled when switch is controlled by user and default mode is supplied + * by board file, which can be changed by userspace later. + */ +static void msm_otg_init_sm(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + u32 otgsc = readl(USB_OTGSC); + + switch (pdata->mode) { + case USB_OTG: + if (pdata->otg_control == OTG_PHY_CONTROL) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } else if (pdata->otg_control == OTG_USER_CONTROL) { + if (pdata->default_mode == USB_HOST) { + clear_bit(ID, &motg->inputs); + } else if (pdata->default_mode == USB_PERIPHERAL) { + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + } else { + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + } + } + break; + case USB_HOST: + clear_bit(ID, &motg->inputs); + break; + case USB_PERIPHERAL: + set_bit(ID, &motg->inputs); + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + break; + } +} + +static void msm_otg_sm_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); + struct usb_otg *otg = motg->phy.otg; + + switch (otg->phy->state) { + case OTG_STATE_UNDEFINED: + dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); + msm_otg_reset(otg->phy); + msm_otg_init_sm(motg); + otg->phy->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); + if (!test_bit(ID, &motg->inputs) && otg->host) { + /* disable BSV bit */ + writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); + msm_otg_start_host(otg->phy, 1); + otg->phy->state = OTG_STATE_A_HOST; + } else if (test_bit(B_SESS_VLD, &motg->inputs)) { + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + msm_chg_detect_work(&motg->chg_work.work); + break; + case USB_CHG_STATE_DETECTED: + switch (motg->chg_type) { + case USB_DCP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + break; + case USB_CDP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; + break; + case USB_SDP_CHARGER: + msm_otg_notify_charger(motg, IUNIT); + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; + break; + default: + break; + } + break; + default: + break; + } + } else { + /* + * If charger detection work is pending, decrement + * the pm usage counter to balance with the one that + * is incremented in charger detection work. + */ + if (cancel_delayed_work_sync(&motg->chg_work)) { + pm_runtime_put_sync(otg->phy->dev); + msm_otg_reset(otg->phy); + } + msm_otg_notify_charger(motg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + } + pm_runtime_put_sync(otg->phy->dev); + break; + case OTG_STATE_B_PERIPHERAL: + dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); + if (!test_bit(B_SESS_VLD, &motg->inputs) || + !test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); + msm_otg_start_peripheral(otg->phy, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); + schedule_work(w); + } + break; + case OTG_STATE_A_HOST: + dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); + if (test_bit(ID, &motg->inputs)) { + msm_otg_start_host(otg->phy, 0); + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); + schedule_work(w); + } + break; + default: + break; + } +} + +static irqreturn_t msm_otg_irq(int irq, void *data) +{ + struct msm_otg *motg = data; + struct usb_phy *phy = &motg->phy; + u32 otgsc = 0; + + if (atomic_read(&motg->in_lpm)) { + disable_irq_nosync(irq); + motg->async_int = 1; + pm_runtime_get(phy->dev); + return IRQ_HANDLED; + } + + otgsc = readl(USB_OTGSC); + if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) + return IRQ_NONE; + + if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + dev_dbg(phy->dev, "ID set/clear\n"); + pm_runtime_get_noresume(phy->dev); + } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + dev_dbg(phy->dev, "BSV set/clear\n"); + pm_runtime_get_noresume(phy->dev); + } + + writel(otgsc, USB_OTGSC); + schedule_work(&motg->sm_work); + return IRQ_HANDLED; +} + +static int msm_otg_mode_show(struct seq_file *s, void *unused) +{ + struct msm_otg *motg = s->private; + struct usb_otg *otg = motg->phy.otg; + + switch (otg->phy->state) { + case OTG_STATE_A_HOST: + seq_printf(s, "host\n"); + break; + case OTG_STATE_B_PERIPHERAL: + seq_printf(s, "peripheral\n"); + break; + default: + seq_printf(s, "none\n"); + break; + } + + return 0; +} + +static int msm_otg_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, msm_otg_mode_show, inode->i_private); +} + +static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_otg *motg = s->private; + char buf[16]; + struct usb_otg *otg = motg->phy.otg; + int status = count; + enum usb_mode_type req_mode; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { + status = -EFAULT; + goto out; + } + + if (!strncmp(buf, "host", 4)) { + req_mode = USB_HOST; + } else if (!strncmp(buf, "peripheral", 10)) { + req_mode = USB_PERIPHERAL; + } else if (!strncmp(buf, "none", 4)) { + req_mode = USB_NONE; + } else { + status = -EINVAL; + goto out; + } + + switch (req_mode) { + case USB_NONE: + switch (otg->phy->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_B_PERIPHERAL: + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_PERIPHERAL: + switch (otg->phy->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_A_HOST: + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_HOST: + switch (otg->phy->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + clear_bit(ID, &motg->inputs); + break; + default: + goto out; + } + break; + default: + goto out; + } + + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); +out: + return status; +} + +const struct file_operations msm_otg_mode_fops = { + .open = msm_otg_mode_open, + .read = seq_read, + .write = msm_otg_mode_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *msm_otg_dbg_root; +static struct dentry *msm_otg_dbg_mode; + +static int msm_otg_debugfs_init(struct msm_otg *motg) +{ + msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); + + if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) + return -ENODEV; + + msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, + msm_otg_dbg_root, motg, &msm_otg_mode_fops); + if (!msm_otg_dbg_mode) { + debugfs_remove(msm_otg_dbg_root); + msm_otg_dbg_root = NULL; + return -ENODEV; + } + + return 0; +} + +static void msm_otg_debugfs_cleanup(void) +{ + debugfs_remove(msm_otg_dbg_mode); + debugfs_remove(msm_otg_dbg_root); +} + +static int __init msm_otg_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct msm_otg *motg; + struct usb_phy *phy; + + dev_info(&pdev->dev, "msm_otg probe\n"); + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "No platform data given. Bailing out\n"); + return -ENODEV; + } + + motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); + if (!motg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!motg->phy.otg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->pdata = pdev->dev.platform_data; + phy = &motg->phy; + phy->dev = &pdev->dev; + + motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + ret = PTR_ERR(motg->phy_reset_clk); + goto free_motg; + } + + motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(motg->clk)) { + dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); + ret = PTR_ERR(motg->clk); + goto put_phy_reset_clk; + } + clk_set_rate(motg->clk, 60000000); + + /* + * If USB Core is running its protocol engine based on CORE CLK, + * CORE CLK must be running at >55Mhz for correct HSUSB + * operation and USB core cannot tolerate frequency changes on + * CORE CLK. For such USB cores, vote for maximum clk frequency + * on pclk source + */ + if (motg->pdata->pclk_src_name) { + motg->pclk_src = clk_get(&pdev->dev, + motg->pdata->pclk_src_name); + if (IS_ERR(motg->pclk_src)) + goto put_clk; + clk_set_rate(motg->pclk_src, INT_MAX); + clk_enable(motg->pclk_src); + } else + motg->pclk_src = ERR_PTR(-ENOENT); + + + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(motg->pclk)) { + dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); + ret = PTR_ERR(motg->pclk); + goto put_pclk_src; + } + + /* + * USB core clock is not present on all MSM chips. This + * clock is introduced to remove the dependency on AXI + * bus frequency. + */ + motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(motg->core_clk)) + motg->core_clk = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + ret = -ENODEV; + goto put_core_clk; + } + + motg->regs = ioremap(res->start, resource_size(res)); + if (!motg->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto put_core_clk; + } + dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); + + motg->irq = platform_get_irq(pdev, 0); + if (!motg->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + ret = -ENODEV; + goto free_regs; + } + + clk_enable(motg->clk); + clk_enable(motg->pclk); + + ret = msm_hsusb_init_vddcx(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); + goto free_regs; + } + + ret = msm_hsusb_ldo_init(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); + goto vddcx_exit; + } + ret = msm_hsusb_ldo_set_mode(1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg enable failed\n"); + goto ldo_exit; + } + + if (motg->core_clk) + clk_enable(motg->core_clk); + + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + + INIT_WORK(&motg->sm_work, msm_otg_sm_work); + INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); + ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, + "msm_otg", motg); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + goto disable_clks; + } + + phy->init = msm_otg_reset; + phy->set_power = msm_otg_set_power; + + phy->io_ops = &msm_otg_io_ops; + + phy->otg->phy = &motg->phy; + phy->otg->set_host = msm_otg_set_host; + phy->otg->set_peripheral = msm_otg_set_peripheral; + + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); + if (ret) { + dev_err(&pdev->dev, "usb_add_phy failed\n"); + goto free_irq; + } + + platform_set_drvdata(pdev, motg); + device_init_wakeup(&pdev->dev, 1); + + if (motg->pdata->mode == USB_OTG && + motg->pdata->otg_control == OTG_USER_CONTROL) { + ret = msm_otg_debugfs_init(motg); + if (ret) + dev_dbg(&pdev->dev, "mode debugfs file is" + "not available\n"); + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +free_irq: + free_irq(motg->irq, motg); +disable_clks: + clk_disable(motg->pclk); + clk_disable(motg->clk); +ldo_exit: + msm_hsusb_ldo_init(motg, 0); +vddcx_exit: + msm_hsusb_init_vddcx(motg, 0); +free_regs: + iounmap(motg->regs); +put_core_clk: + if (motg->core_clk) + clk_put(motg->core_clk); + clk_put(motg->pclk); +put_pclk_src: + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } +put_clk: + clk_put(motg->clk); +put_phy_reset_clk: + clk_put(motg->phy_reset_clk); +free_motg: + kfree(motg->phy.otg); + kfree(motg); + return ret; +} + +static int msm_otg_remove(struct platform_device *pdev) +{ + struct msm_otg *motg = platform_get_drvdata(pdev); + struct usb_phy *phy = &motg->phy; + int cnt = 0; + + if (phy->otg->host || phy->otg->gadget) + return -EBUSY; + + msm_otg_debugfs_cleanup(); + cancel_delayed_work_sync(&motg->chg_work); + cancel_work_sync(&motg->sm_work); + + pm_runtime_resume(&pdev->dev); + + device_init_wakeup(&pdev->dev, 0); + pm_runtime_disable(&pdev->dev); + + usb_remove_phy(phy); + free_irq(motg->irq, motg); + + /* + * Put PHY in low power mode. + */ + ulpi_read(phy, 0x14); + ulpi_write(phy, 0x08, 0x09); + + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) + dev_err(phy->dev, "Unable to suspend PHY\n"); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } + msm_hsusb_ldo_init(motg, 0); + + iounmap(motg->regs); + pm_runtime_set_suspended(&pdev->dev); + + clk_put(motg->phy_reset_clk); + clk_put(motg->pclk); + clk_put(motg->clk); + if (motg->core_clk) + clk_put(motg->core_clk); + + kfree(motg->phy.otg); + kfree(motg); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int msm_otg_runtime_idle(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + struct usb_otg *otg = motg->phy.otg; + + dev_dbg(dev, "OTG runtime idle\n"); + + /* + * It is observed some times that a spurious interrupt + * comes when PHY is put into LPM immediately after PHY reset. + * This 1 sec delay also prevents entering into LPM immediately + * after asynchronous interrupt. + */ + if (otg->phy->state != OTG_STATE_UNDEFINED) + pm_schedule_suspend(dev, 1000); + + return -EAGAIN; +} + +static int msm_otg_runtime_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_runtime_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime resume\n"); + return msm_otg_resume(motg); +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_pm_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG PM suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_pm_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "OTG PM resume\n"); + + ret = msm_otg_resume(motg); + if (ret) + return ret; + + /* + * Runtime PM Documentation recommends bringing the + * device to full powered state upon resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops msm_otg_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) + SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, + msm_otg_runtime_idle) +}; +#endif + +static struct platform_driver msm_otg_driver = { + .remove = msm_otg_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &msm_otg_dev_pm_ops, +#endif + }, +}; + +module_platform_driver_probe(msm_otg_driver, msm_otg_probe); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/phy/mv_otg.c b/drivers/usb/phy/mv_otg.c new file mode 100644 index 0000000..b6a9be3 --- /dev/null +++ b/drivers/usb/phy/mv_otg.c @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie + * Neil Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mv_otg.h" + +#define DRIVER_DESC "Marvell USB OTG transceiver driver" +#define DRIVER_VERSION "Jan 20, 2010" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static const char driver_name[] = "mv-otg"; + +static char *state_string[] = { + "undefined", + "b_idle", + "b_srp_init", + "b_peripheral", + "b_wait_acon", + "b_host", + "a_idle", + "a_wait_vrise", + "a_wait_bcon", + "a_host", + "a_suspend", + "a_peripheral", + "a_wait_vfall", + "a_vbus_err" +}; + +static int mv_otg_set_vbus(struct usb_otg *otg, bool on) +{ + struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy); + if (mvotg->pdata->set_vbus == NULL) + return -ENODEV; + + return mvotg->pdata->set_vbus(on); +} + +static int mv_otg_set_host(struct usb_otg *otg, + struct usb_bus *host) +{ + otg->host = host; + + return 0; +} + +static int mv_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + + return 0; +} + +static void mv_otg_run_state_machine(struct mv_otg *mvotg, + unsigned long delay) +{ + dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n"); + if (!mvotg->qwork) + return; + + queue_delayed_work(mvotg->qwork, &mvotg->work, delay); +} + +static void mv_otg_timer_await_bcon(unsigned long data) +{ + struct mv_otg *mvotg = (struct mv_otg *) data; + + mvotg->otg_ctrl.a_wait_bcon_timeout = 1; + + dev_info(&mvotg->pdev->dev, "B Device No Response!\n"); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } +} + +static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) +{ + struct timer_list *timer; + + if (id >= OTG_TIMER_NUM) + return -EINVAL; + + timer = &mvotg->otg_ctrl.timer[id]; + + if (timer_pending(timer)) + del_timer(timer); + + return 0; +} + +static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, + unsigned long interval, + void (*callback) (unsigned long)) +{ + struct timer_list *timer; + + if (id >= OTG_TIMER_NUM) + return -EINVAL; + + timer = &mvotg->otg_ctrl.timer[id]; + if (timer_pending(timer)) { + dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id); + return -EBUSY; + } + + init_timer(timer); + timer->data = (unsigned long) mvotg; + timer->function = callback; + timer->expires = jiffies + interval; + add_timer(timer); + + return 0; +} + +static int mv_otg_reset(struct mv_otg *mvotg) +{ + unsigned int loops; + u32 tmp; + + /* Stop the controller */ + tmp = readl(&mvotg->op_regs->usbcmd); + tmp &= ~USBCMD_RUN_STOP; + writel(tmp, &mvotg->op_regs->usbcmd); + + /* Reset the controller to get default values */ + writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd); + + loops = 500; + while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) { + if (loops == 0) { + dev_err(&mvotg->pdev->dev, + "Wait for RESET completed TIMEOUT\n"); + return -ETIMEDOUT; + } + loops--; + udelay(20); + } + + writel(0x0, &mvotg->op_regs->usbintr); + tmp = readl(&mvotg->op_regs->usbsts); + writel(tmp, &mvotg->op_regs->usbsts); + + return 0; +} + +static void mv_otg_init_irq(struct mv_otg *mvotg) +{ + u32 otgsc; + + mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID + | OTGSC_INTR_A_VBUS_VALID; + mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID + | OTGSC_INTSTS_A_VBUS_VALID; + + if (mvotg->pdata->vbus == NULL) { + mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID + | OTGSC_INTR_B_SESSION_END; + mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID + | OTGSC_INTSTS_B_SESSION_END; + } + + if (mvotg->pdata->id == NULL) { + mvotg->irq_en |= OTGSC_INTR_USB_ID; + mvotg->irq_status |= OTGSC_INTSTS_USB_ID; + } + + otgsc = readl(&mvotg->op_regs->otgsc); + otgsc |= mvotg->irq_en; + writel(otgsc, &mvotg->op_regs->otgsc); +} + +static void mv_otg_start_host(struct mv_otg *mvotg, int on) +{ +#ifdef CONFIG_USB + struct usb_otg *otg = mvotg->phy.otg; + struct usb_hcd *hcd; + + if (!otg->host) + return; + + dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop"); + + hcd = bus_to_hcd(otg->host); + + if (on) + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + else + usb_remove_hcd(hcd); +#endif /* CONFIG_USB */ +} + +static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) +{ + struct usb_otg *otg = mvotg->phy.otg; + + if (!otg->gadget) + return; + + dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off"); + + if (on) + usb_gadget_vbus_connect(otg->gadget); + else + usb_gadget_vbus_disconnect(otg->gadget); +} + +static void otg_clock_enable(struct mv_otg *mvotg) +{ + unsigned int i; + + for (i = 0; i < mvotg->clknum; i++) + clk_prepare_enable(mvotg->clk[i]); +} + +static void otg_clock_disable(struct mv_otg *mvotg) +{ + unsigned int i; + + for (i = 0; i < mvotg->clknum; i++) + clk_disable_unprepare(mvotg->clk[i]); +} + +static int mv_otg_enable_internal(struct mv_otg *mvotg) +{ + int retval = 0; + + if (mvotg->active) + return 0; + + dev_dbg(&mvotg->pdev->dev, "otg enabled\n"); + + otg_clock_enable(mvotg); + if (mvotg->pdata->phy_init) { + retval = mvotg->pdata->phy_init(mvotg->phy_regs); + if (retval) { + dev_err(&mvotg->pdev->dev, + "init phy error %d\n", retval); + otg_clock_disable(mvotg); + return retval; + } + } + mvotg->active = 1; + + return 0; + +} + +static int mv_otg_enable(struct mv_otg *mvotg) +{ + if (mvotg->clock_gating) + return mv_otg_enable_internal(mvotg); + + return 0; +} + +static void mv_otg_disable_internal(struct mv_otg *mvotg) +{ + if (mvotg->active) { + dev_dbg(&mvotg->pdev->dev, "otg disabled\n"); + if (mvotg->pdata->phy_deinit) + mvotg->pdata->phy_deinit(mvotg->phy_regs); + otg_clock_disable(mvotg); + mvotg->active = 0; + } +} + +static void mv_otg_disable(struct mv_otg *mvotg) +{ + if (mvotg->clock_gating) + mv_otg_disable_internal(mvotg); +} + +static void mv_otg_update_inputs(struct mv_otg *mvotg) +{ + struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; + u32 otgsc; + + otgsc = readl(&mvotg->op_regs->otgsc); + + if (mvotg->pdata->vbus) { + if (mvotg->pdata->vbus->poll() == VBUS_HIGH) { + otg_ctrl->b_sess_vld = 1; + otg_ctrl->b_sess_end = 0; + } else { + otg_ctrl->b_sess_vld = 0; + otg_ctrl->b_sess_end = 1; + } + } else { + otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID); + otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END); + } + + if (mvotg->pdata->id) + otg_ctrl->id = !!mvotg->pdata->id->poll(); + else + otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID); + + if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id) + otg_ctrl->a_bus_req = 1; + + otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID); + otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID); + + dev_dbg(&mvotg->pdev->dev, "%s: ", __func__); + dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id); + dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld); + dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end); + dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld); + dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld); +} + +static void mv_otg_update_state(struct mv_otg *mvotg) +{ + struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; + struct usb_phy *phy = &mvotg->phy; + int old_state = phy->state; + + switch (old_state) { + case OTG_STATE_UNDEFINED: + phy->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + if (otg_ctrl->id == 0) + phy->state = OTG_STATE_A_IDLE; + else if (otg_ctrl->b_sess_vld) + phy->state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_B_PERIPHERAL: + if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0) + phy->state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl->id) + phy->state = OTG_STATE_B_IDLE; + else if (!(otg_ctrl->a_bus_drop) && + (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det)) + phy->state = OTG_STATE_A_WAIT_VRISE; + break; + case OTG_STATE_A_WAIT_VRISE: + if (otg_ctrl->a_vbus_vld) + phy->state = OTG_STATE_A_WAIT_BCON; + break; + case OTG_STATE_A_WAIT_BCON: + if (otg_ctrl->id || otg_ctrl->a_bus_drop + || otg_ctrl->a_wait_bcon_timeout) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_WAIT_VFALL; + otg_ctrl->a_bus_req = 0; + } else if (!otg_ctrl->a_vbus_vld) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_VBUS_ERR; + } else if (otg_ctrl->b_conn) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_HOST; + } + break; + case OTG_STATE_A_HOST: + if (otg_ctrl->id || !otg_ctrl->b_conn + || otg_ctrl->a_bus_drop) + phy->state = OTG_STATE_A_WAIT_BCON; + else if (!otg_ctrl->a_vbus_vld) + phy->state = OTG_STATE_A_VBUS_ERR; + break; + case OTG_STATE_A_WAIT_VFALL: + if (otg_ctrl->id + || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld) + || otg_ctrl->a_bus_req) + phy->state = OTG_STATE_A_IDLE; + break; + case OTG_STATE_A_VBUS_ERR: + if (otg_ctrl->id || otg_ctrl->a_clr_err + || otg_ctrl->a_bus_drop) { + otg_ctrl->a_clr_err = 0; + phy->state = OTG_STATE_A_WAIT_VFALL; + } + break; + default: + break; + } +} + +static void mv_otg_work(struct work_struct *work) +{ + struct mv_otg *mvotg; + struct usb_phy *phy; + struct usb_otg *otg; + int old_state; + + mvotg = container_of(to_delayed_work(work), struct mv_otg, work); + +run: + /* work queue is single thread, or we need spin_lock to protect */ + phy = &mvotg->phy; + otg = phy->otg; + old_state = phy->state; + + if (!mvotg->active) + return; + + mv_otg_update_inputs(mvotg); + mv_otg_update_state(mvotg); + + if (old_state != phy->state) { + dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", + state_string[old_state], + state_string[phy->state]); + + switch (phy->state) { + case OTG_STATE_B_IDLE: + otg->default_a = 0; + if (old_state == OTG_STATE_B_PERIPHERAL) + mv_otg_start_periphrals(mvotg, 0); + mv_otg_reset(mvotg); + mv_otg_disable(mvotg); + break; + case OTG_STATE_B_PERIPHERAL: + mv_otg_enable(mvotg); + mv_otg_start_periphrals(mvotg, 1); + break; + case OTG_STATE_A_IDLE: + otg->default_a = 1; + mv_otg_enable(mvotg); + if (old_state == OTG_STATE_A_WAIT_VFALL) + mv_otg_start_host(mvotg, 0); + mv_otg_reset(mvotg); + break; + case OTG_STATE_A_WAIT_VRISE: + mv_otg_set_vbus(otg, 1); + break; + case OTG_STATE_A_WAIT_BCON: + if (old_state != OTG_STATE_A_HOST) + mv_otg_start_host(mvotg, 1); + mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, + T_A_WAIT_BCON, + mv_otg_timer_await_bcon); + /* + * Now, we directly enter A_HOST. So set b_conn = 1 + * here. In fact, it need host driver to notify us. + */ + mvotg->otg_ctrl.b_conn = 1; + break; + case OTG_STATE_A_HOST: + break; + case OTG_STATE_A_WAIT_VFALL: + /* + * Now, we has exited A_HOST. So set b_conn = 0 + * here. In fact, it need host driver to notify us. + */ + mvotg->otg_ctrl.b_conn = 0; + mv_otg_set_vbus(otg, 0); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } + goto run; + } +} + +static irqreturn_t mv_otg_irq(int irq, void *dev) +{ + struct mv_otg *mvotg = dev; + u32 otgsc; + + otgsc = readl(&mvotg->op_regs->otgsc); + writel(otgsc, &mvotg->op_regs->otgsc); + + /* + * if we have vbus, then the vbus detection for B-device + * will be done by mv_otg_inputs_irq(). + */ + if (mvotg->pdata->vbus) + if ((otgsc & OTGSC_STS_USB_ID) && + !(otgsc & OTGSC_INTSTS_USB_ID)) + return IRQ_NONE; + + if ((otgsc & mvotg->irq_status) == 0) + return IRQ_NONE; + + mv_otg_run_state_machine(mvotg, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) +{ + struct mv_otg *mvotg = dev; + + /* The clock may disabled at this time */ + if (!mvotg->active) { + mv_otg_enable(mvotg); + mv_otg_init_irq(mvotg); + } + + mv_otg_run_state_machine(mvotg, 0); + + return IRQ_HANDLED; +} + +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + mvotg->otg_ctrl.a_bus_req); +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + /* We will use this interface to change to A device */ + if (mvotg->phy.state != OTG_STATE_B_IDLE + && mvotg->phy.state != OTG_STATE_A_IDLE) + return -1; + + /* The clock may disabled and we need to set irq for ID detected */ + mv_otg_enable(mvotg); + mv_otg_init_irq(mvotg); + + if (buf[0] == '1') { + mvotg->otg_ctrl.a_bus_req = 1; + mvotg->otg_ctrl.a_bus_drop = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_req = 1\n"); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + } + + return count; +} + +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, + set_a_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + if (!mvotg->phy.otg->default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '1') { + mvotg->otg_ctrl.a_clr_err = 1; + dev_dbg(&mvotg->pdev->dev, + "User request: a_clr_err = 1\n"); + } + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + + return count; +} + +static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + mvotg->otg_ctrl.a_bus_drop); +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + if (!mvotg->phy.otg->default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '0') { + mvotg->otg_ctrl.a_bus_drop = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_drop = 0\n"); + } else if (buf[0] == '1') { + mvotg->otg_ctrl.a_bus_drop = 1; + mvotg->otg_ctrl.a_bus_req = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_drop = 1\n"); + dev_dbg(&mvotg->pdev->dev, + "User request: and a_bus_req = 0\n"); + } + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + + return count; +} + +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, + get_a_bus_drop, set_a_bus_drop); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_clr_err.attr, + &dev_attr_a_bus_drop.attr, + NULL, +}; + +static struct attribute_group inputs_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +int mv_otg_remove(struct platform_device *pdev) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + + sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group); + + if (mvotg->qwork) { + flush_workqueue(mvotg->qwork); + destroy_workqueue(mvotg->qwork); + } + + mv_otg_disable(mvotg); + + usb_remove_phy(&mvotg->phy); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int mv_otg_probe(struct platform_device *pdev) +{ + struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct mv_otg *mvotg; + struct usb_otg *otg; + struct resource *r; + int retval = 0, clk_i, i; + size_t size; + + if (pdata == NULL) { + dev_err(&pdev->dev, "failed to get platform data\n"); + return -ENODEV; + } + + size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum; + mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!mvotg) { + dev_err(&pdev->dev, "failed to allocate memory!\n"); + return -ENOMEM; + } + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + platform_set_drvdata(pdev, mvotg); + + mvotg->pdev = pdev; + mvotg->pdata = pdata; + + mvotg->clknum = pdata->clknum; + for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) { + mvotg->clk[clk_i] = devm_clk_get(&pdev->dev, + pdata->clkname[clk_i]); + if (IS_ERR(mvotg->clk[clk_i])) { + retval = PTR_ERR(mvotg->clk[clk_i]); + return retval; + } + } + + mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); + if (!mvotg->qwork) { + dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n"); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&mvotg->work, mv_otg_work); + + /* OTG common part */ + mvotg->pdev = pdev; + mvotg->phy.dev = &pdev->dev; + mvotg->phy.otg = otg; + mvotg->phy.label = driver_name; + mvotg->phy.state = OTG_STATE_UNDEFINED; + + otg->phy = &mvotg->phy; + otg->set_host = mv_otg_set_host; + otg->set_peripheral = mv_otg_set_peripheral; + otg->set_vbus = mv_otg_set_vbus; + + for (i = 0; i < OTG_TIMER_NUM; i++) + init_timer(&mvotg->otg_ctrl.timer[i]); + + r = platform_get_resource_byname(mvotg->pdev, + IORESOURCE_MEM, "phyregs"); + if (r == NULL) { + dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); + retval = -ENODEV; + goto err_destroy_workqueue; + } + + mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (mvotg->phy_regs == NULL) { + dev_err(&pdev->dev, "failed to map phy I/O memory\n"); + retval = -EFAULT; + goto err_destroy_workqueue; + } + + r = platform_get_resource_byname(mvotg->pdev, + IORESOURCE_MEM, "capregs"); + if (r == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + retval = -ENODEV; + goto err_destroy_workqueue; + } + + mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (mvotg->cap_regs == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + retval = -EFAULT; + goto err_destroy_workqueue; + } + + /* we will acces controller register, so enable the udc controller */ + retval = mv_otg_enable_internal(mvotg); + if (retval) { + dev_err(&pdev->dev, "mv otg enable error %d\n", retval); + goto err_destroy_workqueue; + } + + mvotg->op_regs = + (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs + + (readl(mvotg->cap_regs) & CAPLENGTH_MASK)); + + if (pdata->id) { + retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq, + NULL, mv_otg_inputs_irq, + IRQF_ONESHOT, "id", mvotg); + if (retval) { + dev_info(&pdev->dev, + "Failed to request irq for ID\n"); + pdata->id = NULL; + } + } + + if (pdata->vbus) { + mvotg->clock_gating = 1; + retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq, + NULL, mv_otg_inputs_irq, + IRQF_ONESHOT, "vbus", mvotg); + if (retval) { + dev_info(&pdev->dev, + "Failed to request irq for VBUS, " + "disable clock gating\n"); + mvotg->clock_gating = 0; + pdata->vbus = NULL; + } + } + + if (pdata->disable_otg_clock_gating) + mvotg->clock_gating = 0; + + mv_otg_reset(mvotg); + mv_otg_init_irq(mvotg); + + r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + retval = -ENODEV; + goto err_disable_clk; + } + + mvotg->irq = r->start; + if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED, + driver_name, mvotg)) { + dev_err(&pdev->dev, "Request irq %d for OTG failed\n", + mvotg->irq); + mvotg->irq = 0; + retval = -ENODEV; + goto err_disable_clk; + } + + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); + if (retval < 0) { + dev_err(&pdev->dev, "can't register transceiver, %d\n", + retval); + goto err_disable_clk; + } + + retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group); + if (retval < 0) { + dev_dbg(&pdev->dev, + "Can't register sysfs attr group: %d\n", retval); + goto err_remove_phy; + } + + spin_lock_init(&mvotg->wq_lock); + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 2 * HZ); + spin_unlock(&mvotg->wq_lock); + } + + dev_info(&pdev->dev, + "successful probe OTG device %s clock gating.\n", + mvotg->clock_gating ? "with" : "without"); + + return 0; + +err_remove_phy: + usb_remove_phy(&mvotg->phy); +err_disable_clk: + mv_otg_disable_internal(mvotg); +err_destroy_workqueue: + flush_workqueue(mvotg->qwork); + destroy_workqueue(mvotg->qwork); + + platform_set_drvdata(pdev, NULL); + + return retval; +} + +#ifdef CONFIG_PM +static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + + if (mvotg->phy.state != OTG_STATE_B_IDLE) { + dev_info(&pdev->dev, + "OTG state is not B_IDLE, it is %d!\n", + mvotg->phy.state); + return -EAGAIN; + } + + if (!mvotg->clock_gating) + mv_otg_disable_internal(mvotg); + + return 0; +} + +static int mv_otg_resume(struct platform_device *pdev) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + u32 otgsc; + + if (!mvotg->clock_gating) { + mv_otg_enable_internal(mvotg); + + otgsc = readl(&mvotg->op_regs->otgsc); + otgsc |= mvotg->irq_en; + writel(otgsc, &mvotg->op_regs->otgsc); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + } + return 0; +} +#endif + +static struct platform_driver mv_otg_driver = { + .probe = mv_otg_probe, + .remove = __exit_p(mv_otg_remove), + .driver = { + .owner = THIS_MODULE, + .name = driver_name, + }, +#ifdef CONFIG_PM + .suspend = mv_otg_suspend, + .resume = mv_otg_resume, +#endif +}; +module_platform_driver(mv_otg_driver); diff --git a/drivers/usb/phy/mv_otg.h b/drivers/usb/phy/mv_otg.h new file mode 100644 index 0000000..8a9e351 --- /dev/null +++ b/drivers/usb/phy/mv_otg.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MV_USB_OTG_CONTROLLER__ +#define __MV_USB_OTG_CONTROLLER__ + +#include + +/* Command Register Bit Masks */ +#define USBCMD_RUN_STOP (0x00000001) +#define USBCMD_CTRL_RESET (0x00000002) + +/* otgsc Register Bit Masks */ +#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 +#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 +#define OTGSC_CTRL_OTG_TERM 0x00000008 +#define OTGSC_CTRL_DATA_PULSING 0x00000010 +#define OTGSC_STS_USB_ID 0x00000100 +#define OTGSC_STS_A_VBUS_VALID 0x00000200 +#define OTGSC_STS_A_SESSION_VALID 0x00000400 +#define OTGSC_STS_B_SESSION_VALID 0x00000800 +#define OTGSC_STS_B_SESSION_END 0x00001000 +#define OTGSC_STS_1MS_TOGGLE 0x00002000 +#define OTGSC_STS_DATA_PULSING 0x00004000 +#define OTGSC_INTSTS_USB_ID 0x00010000 +#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 +#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 +#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 +#define OTGSC_INTSTS_B_SESSION_END 0x00100000 +#define OTGSC_INTSTS_1MS 0x00200000 +#define OTGSC_INTSTS_DATA_PULSING 0x00400000 +#define OTGSC_INTR_USB_ID 0x01000000 +#define OTGSC_INTR_A_VBUS_VALID 0x02000000 +#define OTGSC_INTR_A_SESSION_VALID 0x04000000 +#define OTGSC_INTR_B_SESSION_VALID 0x08000000 +#define OTGSC_INTR_B_SESSION_END 0x10000000 +#define OTGSC_INTR_1MS_TIMER 0x20000000 +#define OTGSC_INTR_DATA_PULSING 0x40000000 + +#define CAPLENGTH_MASK (0xff) + +/* Timer's interval, unit 10ms */ +#define T_A_WAIT_VRISE 100 +#define T_A_WAIT_BCON 2000 +#define T_A_AIDL_BDIS 100 +#define T_A_BIDL_ADIS 20 +#define T_B_ASE0_BRST 400 +#define T_B_SE0_SRP 300 +#define T_B_SRP_FAIL 2000 +#define T_B_DATA_PLS 10 +#define T_B_SRP_INIT 100 +#define T_A_SRP_RSPNS 10 +#define T_A_DRV_RSM 5 + +enum otg_function { + OTG_B_DEVICE = 0, + OTG_A_DEVICE +}; + +enum mv_otg_timer { + A_WAIT_BCON_TIMER = 0, + OTG_TIMER_NUM +}; + +/* PXA OTG state machine */ +struct mv_otg_ctrl { + /* internal variables */ + u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */ + u8 b_srp_done; + u8 b_hnp_en; + + /* OTG inputs */ + u8 a_bus_drop; + u8 a_bus_req; + u8 a_clr_err; + u8 a_bus_resume; + u8 a_bus_suspend; + u8 a_conn; + u8 a_sess_vld; + u8 a_srp_det; + u8 a_vbus_vld; + u8 b_bus_req; /* B-Device Require Bus */ + u8 b_bus_resume; + u8 b_bus_suspend; + u8 b_conn; + u8 b_se0_srp; + u8 b_sess_end; + u8 b_sess_vld; + u8 id; + u8 a_suspend_req; + + /*Timer event */ + u8 a_aidl_bdis_timeout; + u8 b_ase0_brst_timeout; + u8 a_bidl_adis_timeout; + u8 a_wait_bcon_timeout; + + struct timer_list timer[OTG_TIMER_NUM]; +}; + +#define VUSBHS_MAX_PORTS 8 + +struct mv_otg_regs { + u32 usbcmd; /* Command register */ + u32 usbsts; /* Status register */ + u32 usbintr; /* Interrupt enable */ + u32 frindex; /* Frame index */ + u32 reserved1[1]; + u32 deviceaddr; /* Device Address */ + u32 eplistaddr; /* Endpoint List Address */ + u32 ttctrl; /* HOST TT status and control */ + u32 burstsize; /* Programmable Burst Size */ + u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ + u32 reserved[4]; + u32 epnak; /* Endpoint NAK */ + u32 epnaken; /* Endpoint NAK Enable */ + u32 configflag; /* Configured Flag register */ + u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ + u32 otgsc; + u32 usbmode; /* USB Host/Device mode */ + u32 epsetupstat; /* Endpoint Setup Status */ + u32 epprime; /* Endpoint Initialize */ + u32 epflush; /* Endpoint De-initialize */ + u32 epstatus; /* Endpoint Status */ + u32 epcomplete; /* Endpoint Interrupt On Complete */ + u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ + u32 mcr; /* Mux Control */ + u32 isr; /* Interrupt Status */ + u32 ier; /* Interrupt Enable */ +}; + +struct mv_otg { + struct usb_phy phy; + struct mv_otg_ctrl otg_ctrl; + + /* base address */ + void __iomem *phy_regs; + void __iomem *cap_regs; + struct mv_otg_regs __iomem *op_regs; + + struct platform_device *pdev; + int irq; + u32 irq_status; + u32 irq_en; + + struct delayed_work work; + struct workqueue_struct *qwork; + + spinlock_t wq_lock; + + struct mv_usb_platform_data *pdata; + + unsigned int active; + unsigned int clock_gating; + unsigned int clknum; + struct clk *clk[0]; +}; + +#endif diff --git a/drivers/usb/phy/mxs-phy.c b/drivers/usb/phy/mxs-phy.c new file mode 100644 index 0000000..9d4381e --- /dev/null +++ b/drivers/usb/phy/mxs-phy.c @@ -0,0 +1,220 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs_phy" + +#define HW_USBPHY_PWD 0x00 +#define HW_USBPHY_CTRL 0x30 +#define HW_USBPHY_CTRL_SET 0x34 +#define HW_USBPHY_CTRL_CLR 0x38 + +#define BM_USBPHY_CTRL_SFTRST BIT(31) +#define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) +#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) +#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) + +struct mxs_phy { + struct usb_phy phy; + struct clk *clk; +}; + +#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) + +static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + + stmp_reset_block(base + HW_USBPHY_CTRL); + + /* Power up the PHY */ + writel(0, base + HW_USBPHY_PWD); + + /* enable FS/LS device */ + writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, + base + HW_USBPHY_CTRL_SET); +} + +static int mxs_phy_init(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + clk_prepare_enable(mxs_phy->clk); + mxs_phy_hw_init(mxs_phy); + + return 0; +} + +static void mxs_phy_shutdown(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + writel(BM_USBPHY_CTRL_CLKGATE, + phy->io_priv + HW_USBPHY_CTRL_SET); + + clk_disable_unprepare(mxs_phy->clk); +} + +static int mxs_phy_suspend(struct usb_phy *x, int suspend) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + + if (suspend) { + writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_SET); + clk_disable_unprepare(mxs_phy->clk); + } else { + clk_prepare_enable(mxs_phy->clk); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_CLR); + writel(0, x->io_priv + HW_USBPHY_PWD); + } + + return 0; +} + +static int mxs_phy_on_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + dev_dbg(phy->dev, "%s speed device has connected\n", + (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + + if (speed == USB_SPEED_HIGH) + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_SET); + + return 0; +} + +static int mxs_phy_on_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + dev_dbg(phy->dev, "%s speed device has disconnected\n", + (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + + if (speed == USB_SPEED_HIGH) + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_CLR); + + return 0; +} + +static int mxs_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + struct clk *clk; + struct mxs_phy *mxs_phy; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENOENT; + } + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, + "can't get the clock, err=%ld", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); + if (!mxs_phy) { + dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); + return -ENOMEM; + } + + mxs_phy->phy.io_priv = base; + mxs_phy->phy.dev = &pdev->dev; + mxs_phy->phy.label = DRIVER_NAME; + mxs_phy->phy.init = mxs_phy_init; + mxs_phy->phy.shutdown = mxs_phy_shutdown; + mxs_phy->phy.set_suspend = mxs_phy_suspend; + mxs_phy->phy.notify_connect = mxs_phy_on_connect; + mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; + + ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); + + mxs_phy->clk = clk; + + platform_set_drvdata(pdev, &mxs_phy->phy); + + ret = usb_add_phy_dev(&mxs_phy->phy); + if (ret) + return ret; + + return 0; +} + +static int mxs_phy_remove(struct platform_device *pdev) +{ + struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&mxs_phy->phy); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id mxs_phy_dt_ids[] = { + { .compatible = "fsl,imx23-usbphy", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); + +static struct platform_driver mxs_phy_driver = { + .probe = mxs_phy_probe, + .remove = mxs_phy_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mxs_phy_dt_ids, + }, +}; + +static int __init mxs_phy_module_init(void) +{ + return platform_driver_register(&mxs_phy_driver); +} +postcore_initcall(mxs_phy_module_init); + +static void __exit mxs_phy_module_exit(void) +{ + platform_driver_unregister(&mxs_phy_driver); +} +module_exit(mxs_phy_module_exit); + +MODULE_ALIAS("platform:mxs-usb-phy"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_AUTHOR("Richard Zhao "); +MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/nop-usb-xceiv.c b/drivers/usb/phy/nop-usb-xceiv.c new file mode 100644 index 0000000..2b10cc9 --- /dev/null +++ b/drivers/usb/phy/nop-usb-xceiv.c @@ -0,0 +1,294 @@ +/* + * drivers/usb/otg/nop-usb-xceiv.c + * + * NOP USB transceiver for all USB transceiver which are either built-in + * into USB IP or which are mostly autonomous. + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Ajay Kumar Gupta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nop_usb_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +static struct platform_device *pd; + +void usb_nop_xceiv_register(void) +{ + if (pd) + return; + pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); + if (!pd) { + printk(KERN_ERR "Unable to register usb nop transceiver\n"); + return; + } +} +EXPORT_SYMBOL(usb_nop_xceiv_register); + +void usb_nop_xceiv_unregister(void) +{ + platform_device_unregister(pd); + pd = NULL; +} +EXPORT_SYMBOL(usb_nop_xceiv_unregister); + +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static int nop_init(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } +} + +static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + if (!gadget) { + otg->gadget = NULL; + return -ENODEV; + } + + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + return 0; +} + +static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!host) { + otg->host = NULL; + return -ENODEV; + } + + otg->host = host; + return 0; +} + +static int nop_usb_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct nop_usb_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) + return -ENOMEM; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; + } + + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; + } + + nop->dev = &pdev->dev; + nop->phy.dev = nop->dev; + nop->phy.label = "nop-xceiv"; + nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; + nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; + + nop->phy.otg->phy = &nop->phy; + nop->phy.otg->set_host = nop_set_host; + nop->phy.otg->set_peripheral = nop_set_peripheral; + + err = usb_add_phy_dev(&nop->phy); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_add; + } + + platform_set_drvdata(pdev, nop); + + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + + return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; +} + +static int nop_usb_xceiv_remove(struct platform_device *pdev) +{ + struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); + + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + + usb_remove_phy(&nop->phy); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + +static struct platform_driver nop_usb_xceiv_driver = { + .probe = nop_usb_xceiv_probe, + .remove = nop_usb_xceiv_remove, + .driver = { + .name = "nop_usb_xceiv", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nop_xceiv_dt_ids), + }, +}; + +static int __init nop_usb_xceiv_init(void) +{ + return platform_driver_register(&nop_usb_xceiv_driver); +} +subsys_initcall(nop_usb_xceiv_init); + +static void __exit nop_usb_xceiv_exit(void) +{ + platform_driver_unregister(&nop_usb_xceiv_driver); +} +module_exit(nop_usb_xceiv_exit); + +MODULE_ALIAS("platform:nop_usb_xceiv"); +MODULE_AUTHOR("Texas Instruments Inc"); +MODULE_DESCRIPTION("NOP USB Transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/otg_fsm.c b/drivers/usb/phy/otg_fsm.c new file mode 100644 index 0000000..1f729a1 --- /dev/null +++ b/drivers/usb/phy/otg_fsm.c @@ -0,0 +1,348 @@ +/* + * OTG Finite State Machine from OTG spec + * + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "otg_fsm.h" + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", + fsm->protocol, protocol); + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 0); + if (ret) + return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 1); + if (ret) + return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed; + +/* Called when leaving a state. Do state clean up jobs here */ +void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, b_se0_srp_tmr); + fsm->b_se0_srp = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, b_ase0_brst_tmr); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, a_wait_vrise_tmr); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, a_wait_bcon_tmr); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, a_aidl_bdis_tmr); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req = 0; + break; + case OTG_STATE_A_PERIPHERAL: + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + +/* Called when entering a state */ +int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->otg->phy->state == new_state) + return 0; + VDBG("Set state: %s\n", usb_otg_state_string(new_state)); + otg_leave_state(fsm, fsm->otg->phy->state); + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_se0_srp_tmr); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_srp_fail_tmr); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, b_ase0_brst_tmr); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->otg->host, + fsm->otg->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_bcon_tmr); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* + * When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req) + otg_add_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_aidl_bdis_tmr); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->otg->phy->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + unsigned long flags; + + spin_lock_irqsave(&fsm->lock, flags); + + state = fsm->otg->phy->state; + state_changed = 0; + /* State machine state change judgement */ + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d\n", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->otg->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && fsm->otg-> + gadget->b_hnp_enable && fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || + fsm->a_wait_vrise_tmout) { + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if ((!fsm->a_bus_req || fsm->a_suspend_req) && + fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && + !fsm->b_conn)) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + spin_unlock_irqrestore(&fsm->lock, flags); + + VDBG("quit statemachine, changed = %d\n", state_changed); + return state_changed; +} diff --git a/drivers/usb/phy/otg_fsm.h b/drivers/usb/phy/otg_fsm.h new file mode 100644 index 0000000..c30a2e1 --- /dev/null +++ b/drivers/usb/phy/otg_fsm.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef DEBUG +#undef VERBOSE + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ + __func__, ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do {} while (0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + struct otg_fsm_ops *ops; + struct usb_otg *otg; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + spinlock_t lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)(int on); + void (*drv_vbus)(int on); + void (*loc_conn)(int on); + void (*loc_sof)(int on); + void (*start_pulse)(void); + void (*add_timer)(void *timer); + void (*del_timer)(void *timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + fsm->ops->chrg_vbus(on); +} + +static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(on); + } +} + +static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(on); + } +} + +static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(on); + } +} + +static inline void otg_start_pulse(struct otg_fsm *fsm) +{ + fsm->ops->start_pulse(); +} + +static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->add_timer(timer); +} + +static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->del_timer(timer); +} + +int otg_statemachine(struct otg_fsm *fsm); + +/* Defined by device specific driver, for different timer implementation */ +extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, + *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, + *a_wait_enum_tmr; diff --git a/drivers/usb/phy/twl4030-usb.c b/drivers/usb/phy/twl4030-usb.c new file mode 100644 index 0000000..a994715 --- /dev/null +++ b/drivers/usb/phy/twl4030-usb.c @@ -0,0 +1,728 @@ +/* + * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004-2007 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * - HS USB ULPI mode works. + * - 3-pin mode support may be added in future. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register defines */ + +#define MCPC_CTRL 0x30 +#define MCPC_CTRL_RTSOL (1 << 7) +#define MCPC_CTRL_EXTSWR (1 << 6) +#define MCPC_CTRL_EXTSWC (1 << 5) +#define MCPC_CTRL_VOICESW (1 << 4) +#define MCPC_CTRL_OUT64K (1 << 3) +#define MCPC_CTRL_RTSCTSSW (1 << 2) +#define MCPC_CTRL_HS_UART (1 << 0) + +#define MCPC_IO_CTRL 0x33 +#define MCPC_IO_CTRL_MICBIASEN (1 << 5) +#define MCPC_IO_CTRL_CTS_NPU (1 << 4) +#define MCPC_IO_CTRL_RXD_PU (1 << 3) +#define MCPC_IO_CTRL_TXDTYP (1 << 2) +#define MCPC_IO_CTRL_CTSTYP (1 << 1) +#define MCPC_IO_CTRL_RTSTYP (1 << 0) + +#define MCPC_CTRL2 0x36 +#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) + +#define OTHER_FUNC_CTRL 0x80 +#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) +#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) + +#define OTHER_IFC_CTRL 0x83 +#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) +#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) +#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) +#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) +#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) + +#define OTHER_INT_EN_RISE 0x86 +#define OTHER_INT_EN_FALL 0x89 +#define OTHER_INT_STS 0x8C +#define OTHER_INT_LATCH 0x8D +#define OTHER_INT_VB_SESS_VLD (1 << 7) +#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ +#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ +#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ +#define OTHER_INT_MANU (1 << 1) +#define OTHER_INT_ABNORMAL_STRESS (1 << 0) + +#define ID_STATUS 0x96 +#define ID_RES_FLOAT (1 << 4) +#define ID_RES_440K (1 << 3) +#define ID_RES_200K (1 << 2) +#define ID_RES_102K (1 << 1) +#define ID_RES_GND (1 << 0) + +#define POWER_CTRL 0xAC +#define POWER_CTRL_OTG_ENAB (1 << 5) + +#define OTHER_IFC_CTRL2 0xAF +#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) +#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) +#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) + +#define REG_CTRL_EN 0xB2 +#define REG_CTRL_ERROR 0xB5 +#define ULPI_I2C_CONFLICT_INTEN (1 << 0) + +#define OTHER_FUNC_CTRL2 0xB8 +#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) + +/* following registers do not have separate _clr and _set registers */ +#define VBUS_DEBOUNCE 0xC0 +#define ID_DEBOUNCE 0xC1 +#define VBAT_TIMER 0xD3 +#define PHY_PWR_CTRL 0xFD +#define PHY_PWR_PHYPWD (1 << 0) +#define PHY_CLK_CTRL 0xFE +#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) +#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) +#define REQ_PHY_DPLL_CLK (1 << 0) +#define PHY_CLK_CTRL_STS 0xFF +#define PHY_DPLL_CLK (1 << 0) + +/* In module TWL_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x0F + +/* In module TWL_MODULE_PM_RECEIVER */ +#define VUSB_DEDICATED1 0x7D +#define VUSB_DEDICATED2 0x7E +#define VUSB1V5_DEV_GRP 0x71 +#define VUSB1V5_TYPE 0x72 +#define VUSB1V5_REMAP 0x73 +#define VUSB1V8_DEV_GRP 0x74 +#define VUSB1V8_TYPE 0x75 +#define VUSB1V8_REMAP 0x76 +#define VUSB3V1_DEV_GRP 0x77 +#define VUSB3V1_TYPE 0x78 +#define VUSB3V1_REMAP 0x79 + +/* In module TWL4030_MODULE_INTBR */ +#define PMBR1 0x0D +#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) + +struct twl4030_usb { + struct usb_phy phy; + struct device *dev; + + /* TWL4030 internal USB regulator supplies */ + struct regulator *usb1v5; + struct regulator *usb1v8; + struct regulator *usb3v1; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + /* pin configuration */ + enum twl4030_usb_mode usb_mode; + + int irq; + enum omap_musb_vbus_id_status linkstat; + bool vbus_supplied; + u8 asleep; + bool irq_enabled; +}; + +/* internal define on top of container_of */ +#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) + +/*-------------------------------------------------------------------------*/ + +static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, + u8 module, u8 data, u8 address) +{ + u8 check; + + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 1, module, address, check, data); + + /* Failed once: Try again */ + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 2, module, address, check, data); + + /* Failed again: Return error */ + return -EBUSY; +} + +#define twl4030_usb_write_verify(twl, address, data) \ + twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) + +static inline int twl4030_usb_write(struct twl4030_usb *twl, + u8 address, u8 data) +{ + int ret = 0; + + ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); + if (ret < 0) + dev_dbg(twl->dev, + "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) +{ + u8 data; + int ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_dbg(twl->dev, + "TWL4030:readb[0x%x,0x%x] Error %d\n", + module, address, ret); + + return ret; +} + +static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) +{ + return twl4030_readb(twl, TWL_MODULE_USB, address); +} + +/*-------------------------------------------------------------------------*/ + +static inline int +twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_SET(reg), bits); +} + +static inline int +twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_CLR(reg), bits); +} + +/*-------------------------------------------------------------------------*/ + +static enum omap_musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) +{ + int status; + enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; + + twl->vbus_supplied = false; + + /* + * For ID/VBUS sensing, see manual section 15.4.8 ... + * except when using only battery backup power, two + * comparators produce VBUS_PRES and ID_PRES signals, + * which don't match docs elsewhere. But ... BIT(7) + * and BIT(2) of STS_HW_CONDITIONS, respectively, do + * seem to match up. If either is true the USB_PRES + * signal is active, the OTG module is activated, and + * its interrupt may be raised (may wake the system). + */ + status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); + if (status < 0) + dev_err(twl->dev, "USB link status err %d\n", status); + else if (status & (BIT(7) | BIT(2))) { + if (status & (BIT(7))) + twl->vbus_supplied = true; + + if (status & BIT(2)) + linkstat = OMAP_MUSB_ID_GROUND; + else + linkstat = OMAP_MUSB_VBUS_VALID; + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) + linkstat = OMAP_MUSB_VBUS_OFF; + } + + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", + status, status, linkstat); + + /* REVISIT this assumes host and peripheral controllers + * are registered, and that both are active... + */ + + spin_lock_irq(&twl->lock); + twl->linkstat = linkstat; + spin_unlock_irq(&twl->lock); + + return linkstat; +} + +static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) +{ + twl->usb_mode = mode; + + switch (mode) { + case T2_USB_MODE_ULPI: + twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, + ULPI_IFC_CTRL_CARKITMODE); + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, + ULPI_FUNC_CTRL_XCVRSEL_MASK | + ULPI_FUNC_CTRL_OPMODE_MASK); + break; + case -1: + /* FIXME: power on defaults */ + break; + default: + dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", + mode); + break; + }; +} + +static void twl4030_i2c_access(struct twl4030_usb *twl, int on) +{ + unsigned long timeout; + int val = twl4030_usb_read(twl, PHY_CLK_CTRL); + + if (val >= 0) { + if (on) { + /* enable DPLL to access PHY registers over I2C */ + val |= REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + + timeout = jiffies + HZ; + while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK) + && time_before(jiffies, timeout)) + udelay(10); + if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK)) + dev_err(twl->dev, "Timeout setting T2 HSUSB " + "PHY DPLL clock\n"); + } else { + /* let ULPI control the DPLL clock */ + val &= ~REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + } + } +} + +static void __twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); + + if (on) + pwr &= ~PHY_PWR_PHYPWD; + else + pwr |= PHY_PWR_PHYPWD; + + WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); +} + +static void twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + if (on) { + regulator_enable(twl->usb3v1); + regulator_enable(twl->usb1v8); + /* + * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP + * in twl4030) resets the VUSB_DEDICATED2 register. This reset + * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to + * SLEEP. We work around this by clearing the bit after usv3v1 + * is re-activated. This ensures that VUSB3V1 is really active. + */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); + regulator_enable(twl->usb1v5); + __twl4030_phy_power(twl, 1); + twl4030_usb_write(twl, PHY_CLK_CTRL, + twl4030_usb_read(twl, PHY_CLK_CTRL) | + (PHY_CLK_CTRL_CLOCKGATING_EN | + PHY_CLK_CTRL_CLK32K_EN)); + } else { + __twl4030_phy_power(twl, 0); + regulator_disable(twl->usb1v5); + regulator_disable(twl->usb1v8); + regulator_disable(twl->usb3v1); + } +} + +static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) +{ + if (twl->asleep) + return; + + twl4030_phy_power(twl, 0); + twl->asleep = 1; + dev_dbg(twl->dev, "%s\n", __func__); +} + +static void __twl4030_phy_resume(struct twl4030_usb *twl) +{ + twl4030_phy_power(twl, 1); + twl4030_i2c_access(twl, 1); + twl4030_usb_set_mode(twl, twl->usb_mode); + if (twl->usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); +} + +static void twl4030_phy_resume(struct twl4030_usb *twl) +{ + if (!twl->asleep) + return; + __twl4030_phy_resume(twl); + twl->asleep = 0; + dev_dbg(twl->dev, "%s\n", __func__); +} + +static int twl4030_usb_ldo_init(struct twl4030_usb *twl) +{ + /* Enable writing to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); + + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); + + /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ + /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ + + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); + + /* Initialize 3.1V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); + + twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); + if (IS_ERR(twl->usb3v1)) + return -ENODEV; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); + + /* Initialize 1.5V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); + + twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); + if (IS_ERR(twl->usb1v5)) + goto fail1; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); + + /* Initialize 1.8V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); + + twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); + if (IS_ERR(twl->usb1v8)) + goto fail2; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); + + /* disable access to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); + + return 0; + +fail2: + regulator_put(twl->usb1v5); + twl->usb1v5 = NULL; +fail1: + regulator_put(twl->usb3v1); + twl->usb3v1 = NULL; + return -ENODEV; +} + +static ssize_t twl4030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + ret = sprintf(buf, "%s\n", + twl->vbus_supplied ? "on" : "off"); + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); + +static irqreturn_t twl4030_usb_irq(int irq, void *_twl) +{ + struct twl4030_usb *twl = _twl; + enum omap_musb_vbus_id_status status; + + status = twl4030_usb_linkstat(twl); + if (status > 0) { + /* FIXME add a set_power() method so that B-devices can + * configure the charger appropriately. It's not always + * correct to consume VBUS power, and how much current to + * consume is a function of the USB configuration chosen + * by the host. + * + * REVISIT usb_gadget_vbus_connect(...) as needed, ditto + * its disconnect() sibling, when changing to/from the + * USB_LINK_VBUS state. musb_hdrc won't care until it + * starts to handle softconnect right. + */ + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) + twl4030_phy_suspend(twl, 0); + else + twl4030_phy_resume(twl); + + omap_musb_mailbox(twl->linkstat); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static void twl4030_usb_phy_init(struct twl4030_usb *twl) +{ + enum omap_musb_vbus_id_status status; + + status = twl4030_usb_linkstat(twl); + if (status > 0) { + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) { + __twl4030_phy_power(twl, 0); + twl->asleep = 1; + } else { + __twl4030_phy_resume(twl); + twl->asleep = 0; + } + + omap_musb_mailbox(twl->linkstat); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); +} + +static int twl4030_set_suspend(struct usb_phy *x, int suspend) +{ + struct twl4030_usb *twl = phy_to_twl(x); + + if (suspend) + twl4030_phy_suspend(twl, 1); + else + twl4030_phy_resume(twl); + + return 0; +} + +static int twl4030_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + otg->gadget = gadget; + if (!gadget) + otg->phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + otg->host = host; + if (!host) + otg->phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_usb_probe(struct platform_device *pdev) +{ + struct twl4030_usb_data *pdata = pdev->dev.platform_data; + struct twl4030_usb *twl; + int status, err; + struct usb_otg *otg; + struct device_node *np = pdev->dev.of_node; + + twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (np) + of_property_read_u32(np, "usb_mode", + (enum twl4030_usb_mode *)&twl->usb_mode); + else if (pdata) + twl->usb_mode = pdata->usb_mode; + else { + dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); + return -EINVAL; + } + + otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); + if (!otg) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq = platform_get_irq(pdev, 0); + twl->vbus_supplied = false; + twl->asleep = 1; + twl->linkstat = OMAP_MUSB_UNKNOWN; + + twl->phy.dev = twl->dev; + twl->phy.label = "twl4030"; + twl->phy.otg = otg; + twl->phy.type = USB_PHY_TYPE_USB2; + twl->phy.set_suspend = twl4030_set_suspend; + + otg->phy = &twl->phy; + otg->set_host = twl4030_set_host; + otg->set_peripheral = twl4030_set_peripheral; + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + err = twl4030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + return err; + } + usb_add_phy_dev(&twl->phy); + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + /* Our job is to use irqs and status from the power module + * to keep the transceiver disabled when nothing's connected. + * + * FIXME we actually shouldn't start enabling it until the + * USB controller drivers have said they're ready, by calling + * set_host() and/or set_peripheral() ... OTG_capable boards + * need both handles, otherwise just one suffices. + */ + twl->irq_enabled = true; + status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "twl4030_usb", twl); + if (status < 0) { + dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq, status); + return status; + } + + /* Power down phy or make it work according to + * current link state. + */ + twl4030_usb_phy_init(twl); + + dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); + return 0; +} + +static int __exit twl4030_usb_remove(struct platform_device *pdev) +{ + struct twl4030_usb *twl = platform_get_drvdata(pdev); + int val; + + free_irq(twl->irq, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + + /* set transceiver mode to power on defaults */ + twl4030_usb_set_mode(twl, -1); + + /* autogate 60MHz ULPI clock, + * clear dpll clock request for i2c access, + * disable 32KHz + */ + val = twl4030_usb_read(twl, PHY_CLK_CTRL); + if (val >= 0) { + val |= PHY_CLK_CTRL_CLOCKGATING_EN; + val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); + twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); + } + + /* disable complete OTG block */ + twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (!twl->asleep) + twl4030_phy_power(twl, 0); + regulator_put(twl->usb1v5); + regulator_put(twl->usb1v8); + regulator_put(twl->usb3v1); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl4030_usb_id_table[] = { + { .compatible = "ti,twl4030-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); +#endif + +static struct platform_driver twl4030_usb_driver = { + .probe = twl4030_usb_probe, + .remove = __exit_p(twl4030_usb_remove), + .driver = { + .name = "twl4030_usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl4030_usb_id_table), + }, +}; + +static int __init twl4030_usb_init(void) +{ + return platform_driver_register(&twl4030_usb_driver); +} +subsys_initcall(twl4030_usb_init); + +static void __exit twl4030_usb_exit(void) +{ + platform_driver_unregister(&twl4030_usb_driver); +} +module_exit(twl4030_usb_exit); + +MODULE_ALIAS("platform:twl4030_usb"); +MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); +MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/twl6030-usb.c b/drivers/usb/phy/twl6030-usb.c new file mode 100644 index 0000000..8cd6cf4 --- /dev/null +++ b/drivers/usb/phy/twl6030-usb.c @@ -0,0 +1,446 @@ +/* + * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Hema HK + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* usb register definitions */ +#define USB_VENDOR_ID_LSB 0x00 +#define USB_VENDOR_ID_MSB 0x01 +#define USB_PRODUCT_ID_LSB 0x02 +#define USB_PRODUCT_ID_MSB 0x03 +#define USB_VBUS_CTRL_SET 0x04 +#define USB_VBUS_CTRL_CLR 0x05 +#define USB_ID_CTRL_SET 0x06 +#define USB_ID_CTRL_CLR 0x07 +#define USB_VBUS_INT_SRC 0x08 +#define USB_VBUS_INT_LATCH_SET 0x09 +#define USB_VBUS_INT_LATCH_CLR 0x0A +#define USB_VBUS_INT_EN_LO_SET 0x0B +#define USB_VBUS_INT_EN_LO_CLR 0x0C +#define USB_VBUS_INT_EN_HI_SET 0x0D +#define USB_VBUS_INT_EN_HI_CLR 0x0E +#define USB_ID_INT_SRC 0x0F +#define USB_ID_INT_LATCH_SET 0x10 +#define USB_ID_INT_LATCH_CLR 0x11 + +#define USB_ID_INT_EN_LO_SET 0x12 +#define USB_ID_INT_EN_LO_CLR 0x13 +#define USB_ID_INT_EN_HI_SET 0x14 +#define USB_ID_INT_EN_HI_CLR 0x15 +#define USB_OTG_ADP_CTRL 0x16 +#define USB_OTG_ADP_HIGH 0x17 +#define USB_OTG_ADP_LOW 0x18 +#define USB_OTG_ADP_RISE 0x19 +#define USB_OTG_REVISION 0x1A + +/* to be moved to LDO */ +#define TWL6030_MISC2 0xE5 +#define TWL6030_CFG_LDO_PD2 0xF5 +#define TWL6030_BACKUP_REG 0xFA + +#define STS_HW_CONDITIONS 0x21 + +/* In module TWL6030_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x21 +#define STS_USB_ID BIT(2) + +/* In module TWL6030_MODULE_PM_RECEIVER */ +#define VUSB_CFG_TRANS 0x71 +#define VUSB_CFG_STATE 0x72 +#define VUSB_CFG_VOLTAGE 0x73 + +/* in module TWL6030_MODULE_MAIN_CHARGE */ + +#define CHARGERUSB_CTRL1 0x8 + +#define CONTROLLER_STAT1 0x03 +#define VBUS_DET BIT(2) + +struct twl6030_usb { + struct phy_companion comparator; + struct device *dev; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + struct regulator *usb3v3; + + /* used to set vbus, in atomic path */ + struct work_struct set_vbus_work; + + int irq1; + int irq2; + enum omap_musb_vbus_id_status linkstat; + u8 asleep; + bool irq_enabled; + bool vbus_enable; + const char *regulator; +}; + +#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator) + +/*-------------------------------------------------------------------------*/ + +static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, + u8 data, u8 address) +{ + int ret = 0; + + ret = twl_i2c_write_u8(module, data, address); + if (ret < 0) + dev_err(twl->dev, + "Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) +{ + u8 data, ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_err(twl->dev, + "readb[0x%x,0x%x] Error %d\n", + module, address, ret); + return ret; +} + +static int twl6030_start_srp(struct phy_companion *comparator) +{ + struct twl6030_usb *twl = comparator_to_twl(comparator); + + twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); + twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); + + mdelay(100); + twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); + + return 0; +} + +static int twl6030_usb_ldo_init(struct twl6030_usb *twl) +{ + /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); + + /* Program CFG_LDO_PD2 register and set VUSB bit */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2); + + /* Program MISC2 register and set bit VUSB_IN_VBAT */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); + + twl->usb3v3 = regulator_get(twl->dev, twl->regulator); + if (IS_ERR(twl->usb3v3)) + return -ENODEV; + + /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); + + /* + * Program the USB_ID_CTRL_SET register to enable GND drive + * and the ID comparators + */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); + + return 0; +} + +static ssize_t twl6030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl6030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + + switch (twl->linkstat) { + case OMAP_MUSB_VBUS_VALID: + ret = snprintf(buf, PAGE_SIZE, "vbus\n"); + break; + case OMAP_MUSB_ID_GROUND: + ret = snprintf(buf, PAGE_SIZE, "id\n"); + break; + case OMAP_MUSB_VBUS_OFF: + ret = snprintf(buf, PAGE_SIZE, "none\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + } + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); + +static irqreturn_t twl6030_usb_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; + u8 vbus_state, hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, + CONTROLLER_STAT1); + if (!(hw_state & STS_USB_ID)) { + if (vbus_state & VBUS_DET) { + regulator_enable(twl->usb3v3); + twl->asleep = 1; + status = OMAP_MUSB_VBUS_VALID; + twl->linkstat = status; + omap_musb_mailbox(status); + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) { + status = OMAP_MUSB_VBUS_OFF; + twl->linkstat = status; + omap_musb_mailbox(status); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } + } + } + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; + u8 hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + if (hw_state & STS_USB_ID) { + + regulator_enable(twl->usb3v3); + twl->asleep = 1; + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); + twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); + status = OMAP_MUSB_ID_GROUND; + twl->linkstat = status; + omap_musb_mailbox(status); + } else { + twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); + } + twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); + + return IRQ_HANDLED; +} + +static int twl6030_enable_irq(struct twl6030_usb *twl) +{ + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); + + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_STS_C); + twl6030_usb_irq(twl->irq2, twl); + twl6030_usbotg_irq(twl->irq1, twl); + + return 0; +} + +static void otg_set_vbus_work(struct work_struct *data) +{ + struct twl6030_usb *twl = container_of(data, struct twl6030_usb, + set_vbus_work); + + /* + * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 + * register. This enables boost mode. + */ + + if (twl->vbus_enable) + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40, + CHARGERUSB_CTRL1); + else + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00, + CHARGERUSB_CTRL1); +} + +static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) +{ + struct twl6030_usb *twl = comparator_to_twl(comparator); + + twl->vbus_enable = enabled; + schedule_work(&twl->set_vbus_work); + + return 0; +} + +static int twl6030_usb_probe(struct platform_device *pdev) +{ + u32 ret; + struct twl6030_usb *twl; + int status, err; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct twl4030_usb_data *pdata = dev->platform_data; + + twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq1 = platform_get_irq(pdev, 0); + twl->irq2 = platform_get_irq(pdev, 1); + twl->linkstat = OMAP_MUSB_UNKNOWN; + + twl->comparator.set_vbus = twl6030_set_vbus; + twl->comparator.start_srp = twl6030_start_srp; + + ret = omap_usb2_set_comparator(&twl->comparator); + if (ret == -ENODEV) { + dev_info(&pdev->dev, "phy not ready, deferring probe"); + return -EPROBE_DEFER; + } + + if (np) { + twl->regulator = "usb"; + } else if (pdata) { + if (pdata->features & TWL6025_SUBCLASS) + twl->regulator = "ldousb"; + else + twl->regulator = "vusb"; + } else { + dev_err(&pdev->dev, "twl6030 initialized without pdata\n"); + return -EINVAL; + } + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + err = twl6030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + return err; + } + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); + + twl->irq_enabled = true; + status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq1, status); + device_remove_file(twl->dev, &dev_attr_vbus); + return status; + } + + status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq2, status); + free_irq(twl->irq1, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + return status; + } + + twl->asleep = 0; + twl6030_enable_irq(twl); + dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); + + return 0; +} + +static int __exit twl6030_usb_remove(struct platform_device *pdev) +{ + struct twl6030_usb *twl = platform_get_drvdata(pdev); + + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_STS_C); + free_irq(twl->irq1, twl); + free_irq(twl->irq2, twl); + regulator_put(twl->usb3v3); + device_remove_file(twl->dev, &dev_attr_vbus); + cancel_work_sync(&twl->set_vbus_work); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl6030_usb_id_table[] = { + { .compatible = "ti,twl6030-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); +#endif + +static struct platform_driver twl6030_usb_driver = { + .probe = twl6030_usb_probe, + .remove = __exit_p(twl6030_usb_remove), + .driver = { + .name = "twl6030_usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl6030_usb_id_table), + }, +}; + +static int __init twl6030_usb_init(void) +{ + return platform_driver_register(&twl6030_usb_driver); +} +subsys_initcall(twl6030_usb_init); + +static void __exit twl6030_usb_exit(void) +{ + platform_driver_unregister(&twl6030_usb_driver); +} +module_exit(twl6030_usb_exit); + +MODULE_ALIAS("platform:twl6030_usb"); +MODULE_AUTHOR("Hema HK "); +MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/ulpi.c b/drivers/usb/phy/ulpi.c new file mode 100644 index 0000000..217339d --- /dev/null +++ b/drivers/usb/phy/ulpi.c @@ -0,0 +1,283 @@ +/* + * Generic ULPI USB transceiver support + * + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + + +struct ulpi_info { + unsigned int id; + char *name; +}; + +#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) +#define ULPI_INFO(_id, _name) \ + { \ + .id = (_id), \ + .name = (_name), \ + } + +/* ULPI hardcoded IDs, used for probing */ +static struct ulpi_info ulpi_ids[] = { + ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), + ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), +}; + +static int ulpi_set_otg_flags(struct usb_phy *phy) +{ + unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | + ULPI_OTG_CTRL_DM_PULLDOWN; + + if (phy->flags & ULPI_OTG_ID_PULLUP) + flags |= ULPI_OTG_CTRL_ID_PULLUP; + + /* + * ULPI Specification rev.1.1 default + * for Dp/DmPulldown is enabled. + */ + if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; + + if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; + + if (phy->flags & ULPI_OTG_EXTVBUSIND) + flags |= ULPI_OTG_CTRL_EXTVBUSIND; + + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); +} + +static int ulpi_set_fc_flags(struct usb_phy *phy) +{ + unsigned int flags = 0; + + /* + * ULPI Specification rev.1.1 default + * for XcvrSelect is Full Speed. + */ + if (phy->flags & ULPI_FC_HS) + flags |= ULPI_FUNC_CTRL_HIGH_SPEED; + else if (phy->flags & ULPI_FC_LS) + flags |= ULPI_FUNC_CTRL_LOW_SPEED; + else if (phy->flags & ULPI_FC_FS4LS) + flags |= ULPI_FUNC_CTRL_FS4LS; + else + flags |= ULPI_FUNC_CTRL_FULL_SPEED; + + if (phy->flags & ULPI_FC_TERMSEL) + flags |= ULPI_FUNC_CTRL_TERMSELECT; + + /* + * ULPI Specification rev.1.1 default + * for OpMode is Normal Operation. + */ + if (phy->flags & ULPI_FC_OP_NODRV) + flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + else if (phy->flags & ULPI_FC_OP_DIS_NRZI) + flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; + else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) + flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; + else + flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + + /* + * ULPI Specification rev.1.1 default + * for SuspendM is Powered. + */ + flags |= ULPI_FUNC_CTRL_SUSPENDM; + + return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); +} + +static int ulpi_set_ic_flags(struct usb_phy *phy) +{ + unsigned int flags = 0; + + if (phy->flags & ULPI_IC_AUTORESUME) + flags |= ULPI_IFC_CTRL_AUTORESUME; + + if (phy->flags & ULPI_IC_EXTVBUS_INDINV) + flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; + + if (phy->flags & ULPI_IC_IND_PASSTHRU) + flags |= ULPI_IFC_CTRL_PASSTHRU; + + if (phy->flags & ULPI_IC_PROTECT_DIS) + flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; + + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); +} + +static int ulpi_set_flags(struct usb_phy *phy) +{ + int ret; + + ret = ulpi_set_otg_flags(phy); + if (ret) + return ret; + + ret = ulpi_set_ic_flags(phy); + if (ret) + return ret; + + return ulpi_set_fc_flags(phy); +} + +static int ulpi_check_integrity(struct usb_phy *phy) +{ + int ret, i; + unsigned int val = 0x55; + + for (i = 0; i < 2; i++) { + ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); + if (ret < 0) + return ret; + + ret = usb_phy_io_read(phy, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != val) { + pr_err("ULPI integrity check: failed!"); + return -ENODEV; + } + val = val << 1; + } + + pr_info("ULPI integrity check: passed.\n"); + + return 0; +} + +static int ulpi_init(struct usb_phy *phy) +{ + int i, vid, pid, ret; + u32 ulpi_id = 0; + + for (i = 0; i < 4; i++) { + ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); + if (ret < 0) + return ret; + ulpi_id = (ulpi_id << 8) | ret; + } + vid = ulpi_id & 0xffff; + pid = ulpi_id >> 16; + + pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); + + for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) { + if (ulpi_ids[i].id == ULPI_ID(vid, pid)) { + pr_info("Found %s ULPI transceiver.\n", + ulpi_ids[i].name); + break; + } + } + + ret = ulpi_check_integrity(phy); + if (ret) + return ret; + + return ulpi_set_flags(phy); +} + +static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); + + if (!host) { + otg->host = NULL; + return 0; + } + + otg->host = host; + + flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_CARKITMODE); + + if (phy->flags & ULPI_IC_6PIN_SERIAL) + flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; + else if (phy->flags & ULPI_IC_3PIN_SERIAL) + flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; + else if (phy->flags & ULPI_IC_CARKIT) + flags |= ULPI_IFC_CTRL_CARKITMODE; + + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); +} + +static int ulpi_set_vbus(struct usb_otg *otg, bool on) +{ + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); + + flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); + + if (on) { + if (phy->flags & ULPI_OTG_DRVVBUS) + flags |= ULPI_OTG_CTRL_DRVVBUS; + + if (phy->flags & ULPI_OTG_DRVVBUS_EXT) + flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; + } + + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); +} + +struct usb_phy * +otg_ulpi_create(struct usb_phy_io_ops *ops, + unsigned int flags) +{ + struct usb_phy *phy; + struct usb_otg *otg; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; + + otg = kzalloc(sizeof(*otg), GFP_KERNEL); + if (!otg) { + kfree(phy); + return NULL; + } + + phy->label = "ULPI"; + phy->flags = flags; + phy->io_ops = ops; + phy->otg = otg; + phy->init = ulpi_init; + + otg->phy = phy; + otg->set_host = ulpi_set_host; + otg->set_vbus = ulpi_set_vbus; + + return phy; +} +EXPORT_SYMBOL_GPL(otg_ulpi_create); + diff --git a/drivers/usb/phy/ulpi_viewport.c b/drivers/usb/phy/ulpi_viewport.c new file mode 100644 index 0000000..c5ba7e5 --- /dev/null +++ b/drivers/usb/phy/ulpi_viewport.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#define ULPI_VIEW_WAKEUP (1 << 31) +#define ULPI_VIEW_RUN (1 << 30) +#define ULPI_VIEW_WRITE (1 << 29) +#define ULPI_VIEW_READ (0 << 29) +#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) +#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) +#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) + +static int ulpi_viewport_wait(void __iomem *view, u32 mask) +{ + unsigned long usec = 2000; + + while (usec--) { + if (!(readl(view) & mask)) + return 0; + + udelay(1); + }; + + return -ETIMEDOUT; +} + +static int ulpi_viewport_read(struct usb_phy *otg, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); + if (ret) + return ret; + + return ULPI_VIEW_DATA_READ(readl(view)); +} + +static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | + ULPI_VIEW_ADDR(reg), view); + + return ulpi_viewport_wait(view, ULPI_VIEW_RUN); +} + +struct usb_phy_io_ops ulpi_viewport_access_ops = { + .read = ulpi_viewport_read, + .write = ulpi_viewport_write, +}; -- cgit v0.10.2 From edc7cb2e955f222fe51cd44c1cf9c94d58017344 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:13:43 +0200 Subject: usb: phy: make it a menuconfig We already have a considerable amount of USB PHY drivers, making it a menuconfig just prevents us from adding too much churn to USB's menuconfig. While at that, also select USB_OTG_UTILS from this new menuconfig just to keep backwards compatibility until we manage to remove that symbol. Signed-off-by: Felipe Balbi diff --git a/drivers/Makefile b/drivers/Makefile index dce39a9..3c200a2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -79,7 +79,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ -obj-$(CONFIG_USB_OTG_UTILS) += usb/ +obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/ diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 32ce740..832cd69 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -1,8 +1,17 @@ # # Physical Layer USB driver configuration # -comment "USB Physical Layer drivers" - depends on USB || USB_GADGET +menuconfig USB_PHY + tristate "USB Physical Layer drivers" + select USB_OTG_UTILS + help + USB controllers (those which are host, device or DRD) need a + device to handle the physical layer signalling, commonly called + a PHY. + + The following drivers add support for such PHY devices. + +if USB_PHY config USB_OTG_UTILS bool @@ -10,8 +19,6 @@ config USB_OTG_UTILS Select this to make sure the build includes objects from the OTG infrastructure directory. -if USB || USB_GADGET - # # USB Transceiver Drivers # @@ -206,4 +213,4 @@ config USB_ULPI_VIEWPORT Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). -endif # USB || OTG +endif # USB_PHY diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 34488ce..d10a8b3 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,7 +4,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG -obj-$(CONFIG_USB_OTG_UTILS) += phy.o +obj-$(CONFIG_USB_PHY) += phy.o # transceiver drivers, keep the list sorted diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 15847cb..b001dc3 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -161,7 +161,7 @@ usb_phy_shutdown(struct usb_phy *x) } /* for usb host and peripheral controller drivers */ -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); -- cgit v0.10.2 From 820d08835d2688963607dbd08e50d89a20cb0442 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:23:50 +0200 Subject: usb: power: pda_power: check against CONFIG_USB_PHY CONFIG_USB_OTG_UTILS will be removed very soon, so we should check CONFIG_USB_PHY instead. Signed-off-by: Felipe Balbi diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 7df7c5f..0c52e2a 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -35,7 +35,7 @@ static struct timer_list supply_timer; static struct timer_list polling_timer; static int polling; -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) static struct usb_phy *transceiver; static struct notifier_block otg_nb; #endif @@ -218,7 +218,7 @@ static void polling_timer_func(unsigned long unused) jiffies + msecs_to_jiffies(pdata->polling_interval)); } -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) static int otg_is_usb_online(void) { return (transceiver->last_event == USB_EVENT_VBUS || @@ -315,7 +315,7 @@ static int pda_power_probe(struct platform_device *pdev) pda_psy_usb.num_supplicants = pdata->num_supplicants; } -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (!IS_ERR_OR_NULL(transceiver)) { if (!pdata->is_usb_online) @@ -367,7 +367,7 @@ static int pda_power_probe(struct platform_device *pdev) } } -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) { otg_nb.notifier_call = otg_handle_notification; ret = usb_register_notifier(transceiver, &otg_nb); @@ -391,7 +391,7 @@ static int pda_power_probe(struct platform_device *pdev) return 0; -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) otg_reg_notifier_failed: if (pdata->is_usb_online && usb_irq) free_irq(usb_irq->start, &pda_psy_usb); @@ -402,7 +402,7 @@ usb_irq_failed: usb_supply_failed: if (pdata->is_ac_online && ac_irq) free_irq(ac_irq->start, &pda_psy_ac); -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif @@ -437,7 +437,7 @@ static int pda_power_remove(struct platform_device *pdev) power_supply_unregister(&pda_psy_usb); if (pdata->is_ac_online) power_supply_unregister(&pda_psy_ac); -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(transceiver)) usb_put_phy(transceiver); #endif -- cgit v0.10.2 From 1d3dbfc3a74f70750c8385dff36d4d46b6bd3a1a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:24:08 +0200 Subject: usb: gadget: mv_udc_core: check against CONFIG_USB_PHY CONFIG_USB_OTG_UTILS will be removed very soon, so we should check CONFIG_USB_PHY instead. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index d278e8f..d550b21 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2127,7 +2127,7 @@ static int mv_udc_probe(struct platform_device *pdev) udc->dev = pdev; -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (pdata->mode == MV_USB_MODE_OTG) { udc->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); -- cgit v0.10.2 From fcd12b9711816e7cb0a3eb1b47790979e4c14c58 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:24:32 +0200 Subject: usb: ehci: marvel: check against CONFIG_USB_PHY CONFIG_USB_OTG_UTILS will be removed very soon, so we should check CONFIG_USB_PHY instead. Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 3065809..9751823 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -240,7 +240,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(ehci_mv->otg)) { dev_err(&pdev->dev, @@ -260,7 +260,7 @@ static int mv_ehci_probe(struct platform_device *pdev) mv_ehci_disable(ehci_mv); #else dev_info(&pdev->dev, "MV_USB_MODE_OTG " - "must have CONFIG_USB_OTG_UTILS enabled\n"); + "must have CONFIG_USB_PHY enabled\n"); goto err_disable_clk; #endif } else { -- cgit v0.10.2 From a948712d2a064be5f928f37d137e9d14b48cc94f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:24:58 +0200 Subject: usb: ehci: tegra: check against CONFIG_USB_PHY CONFIG_USB_OTG_UTILS will be removed very soon, so we should check CONFIG_USB_PHY instead. Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 568aecc..fafbc81 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -768,7 +768,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail; } -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); @@ -794,7 +794,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) return err; fail: -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); #endif @@ -815,7 +815,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); #endif -- cgit v0.10.2 From fd891498751f53dda3733d9e9ff8a1f6ea16c5e5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:31:18 +0200 Subject: usb: phy: remove CONFIG_USB_OTG_UTILS there are no more users of CONFIG_USB_OTG_UTILS left in tree, we can remove it just fine. [ kishon@ti.com : fixed a linking error due to original patch forgetting to change drivers/usb/Makefile ] Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9e00c38..ffe02fb 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -254,7 +254,7 @@ config BATTERY_RX51 config CHARGER_ISP1704 tristate "ISP1704 USB Charger Detection" - depends on USB_OTG_UTILS + depends on USB_PHY help Say Y to enable support for USB Charger Detection with ISP1707/ISP1704 USB transceivers. diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 860306b..c41feba 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -44,7 +44,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ -obj-$(CONFIG_USB_OTG_UTILS) += phy/ +obj-$(CONFIG_USB_PHY) += phy/ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 68e9a2c..ea5ee9c 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,7 +1,6 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB || USB_GADGET) && GENERIC_HARDIRQS - select USB_OTG_UTILS select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD help Say Y or M here if your system has a Dual Role SuperSpeed diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 50586ff..7ad108a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -195,7 +195,6 @@ config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG - select USB_OTG_UTILS if ARCH_OMAP help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 @@ -210,7 +209,6 @@ config USB_OMAP config USB_PXA25X tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX - select USB_OTG_UTILS help Intel's PXA 25x series XScale ARM-5TE processors include an integrated full speed USB 1.1 device controller. The @@ -258,7 +256,6 @@ config USB_RENESAS_USBHS_UDC config USB_PXA27X tristate "PXA 27x" - select USB_OTG_UTILS help Intel's PXA 27x series XScale ARM v5TE processors include an integrated full speed USB 1.1 device controller. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c59a112..ba1347c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -300,7 +300,6 @@ config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 - select USB_OTG_UTILS if ARCH_OMAP depends on USB_ISP1301 || !ARCH_LPC32XX ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index d38cf98..47442d3 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -7,7 +7,6 @@ config USB_MUSB_HDRC tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' depends on USB && USB_GADGET - select USB_OTG_UTILS help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 832cd69..97de6de 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -3,7 +3,6 @@ # menuconfig USB_PHY tristate "USB Physical Layer drivers" - select USB_OTG_UTILS help USB controllers (those which are host, device or DRD) need a device to handle the physical layer signalling, commonly called @@ -13,19 +12,12 @@ menuconfig USB_PHY if USB_PHY -config USB_OTG_UTILS - bool - help - Select this to make sure the build includes objects from - the OTG infrastructure directory. - # # USB Transceiver Drivers # config AB8500_USB tristate "AB8500 USB Transceiver Driver" depends on AB8500_CORE - select USB_OTG_UTILS help Enable this to support the USB OTG transceiver in AB8500 chip. This transceiver supports high and full speed devices plus, @@ -35,14 +27,12 @@ config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND select USB_OTG - select USB_OTG_UTILS help Enable this to support Freescale USB OTG transceiver. config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG - select USB_OTG_UTILS help If you say yes here you get support for the Philips ISP1301 USB-On-The-Go transceiver working with the OMAP OTG controller. @@ -56,14 +46,12 @@ config ISP1301_OMAP config MV_U3D_PHY bool "Marvell USB 3.0 PHY controller Driver" depends on USB_MV_U3D - select USB_OTG_UTILS help Enable this to support Marvell USB 3.0 phy controller for Marvell SoC. config NOP_USB_XCEIV tristate "NOP USB Transceiver Driver" - select USB_OTG_UTILS help This driver is to be used by all the usb transceiver which are either built-in with usb ip or which are autonomous and doesn't require any @@ -81,7 +69,6 @@ config OMAP_CONTROL_USB config OMAP_USB2 tristate "OMAP USB2 PHY Driver" depends on ARCH_OMAP2PLUS - select USB_OTG_UTILS select OMAP_CONTROL_USB help Enable this to support the transceiver that is part of SOC. This @@ -91,7 +78,6 @@ config OMAP_USB2 config OMAP_USB3 tristate "OMAP USB3 PHY Driver" - select USB_OTG_UTILS select OMAP_CONTROL_USB help Enable this to support the USB3 PHY that is part of SOC. This @@ -102,7 +88,6 @@ config OMAP_USB3 config SAMSUNG_USBPHY bool "Samsung USB PHY controller Driver" depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS - select USB_OTG_UTILS help Enable this to support Samsung USB phy controller for samsung SoCs. @@ -110,7 +95,6 @@ config SAMSUNG_USBPHY config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS - select USB_OTG_UTILS help Enable this to support the USB OTG transceiver on TWL4030 family chips (including the TWL5030 and TPS659x0 devices). @@ -120,7 +104,6 @@ config TWL4030_USB config TWL6030_USB tristate "TWL6030 USB Transceiver Driver" depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS - select USB_OTG_UTILS help Enable this to support the USB OTG transceiver on TWL6030 family chips. This TWL6030 transceiver has the VBUS and ID GND @@ -132,7 +115,6 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" depends on GENERIC_GPIO - select USB_OTG_UTILS help Provides simple GPIO VBUS sensing for controllers with an internal transceiver via the usb_phy interface, and @@ -154,7 +136,6 @@ config USB_ISP1301 config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM - select USB_OTG_UTILS help Enable this to support the USB OTG transceiver on MSM chips. It handles PHY initialization, clock management, and workarounds @@ -168,7 +149,6 @@ config USB_MV_OTG tristate "Marvell USB OTG support" depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND select USB_OTG - select USB_OTG_UTILS help Say Y here if you want to build Marvell USB OTG transciever driver in kernel (including PXA and MMP series). This driver @@ -180,7 +160,6 @@ config USB_MXS_PHY tristate "Freescale MXS USB PHY support" depends on ARCH_MXC || ARCH_MXS select STMP_DEVICE - select USB_OTG_UTILS help Enable this to support the Freescale MXS USB PHY. @@ -189,7 +168,6 @@ config USB_MXS_PHY config USB_RCAR_PHY tristate "Renesas R-Car USB phy support" depends on USB || USB_GADGET - select USB_OTG_UTILS help Say Y here to add support for the Renesas R-Car USB phy driver. This chip is typically used as USB phy for USB host, gadget. @@ -201,7 +179,6 @@ config USB_RCAR_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM - select USB_OTG_UTILS help Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. -- cgit v0.10.2 From 94ae98433a397dd4695652fc62ca7bc784b08216 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 17:37:59 +0200 Subject: usb: phy: rename all phy drivers to phy-$name-usb.c this will make sure that we have sensible names for all phy drivers. Current situation was already quite bad with too generic names being used. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index d10a8b3..5fb4a5d 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -8,24 +8,24 @@ obj-$(CONFIG_USB_PHY) += phy.o # transceiver drivers, keep the list sorted -obj-$(CONFIG_AB8500_USB) += ab8500-usb.o -fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o -obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o -obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o -obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o -obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o -obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o -obj-$(CONFIG_OMAP_USB2) += omap-usb2.o -obj-$(CONFIG_OMAP_USB3) += omap-usb3.o -obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o -obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o -obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o -obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o -obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o -obj-$(CONFIG_USB_ISP1301) += isp1301.o -obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o -obj-$(CONFIG_USB_MV_OTG) += mv_otg.o -obj-$(CONFIG_USB_MXS_PHY) += mxs-phy.o -obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o -obj-$(CONFIG_USB_ULPI) += ulpi.o -obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o +obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o +phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o +obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o +obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301.omap.o +obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o +obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o +obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o +obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o +obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o +obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o +obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o +obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o +obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o +obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o +obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o +obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o +obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o +obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o +obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o +obj-$(CONFIG_USB_ULPI) += phy-ulpi.o +obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o diff --git a/drivers/usb/phy/ab8500-usb.c b/drivers/usb/phy/ab8500-usb.c deleted file mode 100644 index 2d86f26..0000000 --- a/drivers/usb/phy/ab8500-usb.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * drivers/usb/otg/ab8500_usb.c - * - * USB transceiver driver for AB8500 chip - * - * Copyright (C) 2010 ST-Ericsson AB - * Mian Yousaf Kaukab - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AB8500_MAIN_WD_CTRL_REG 0x01 -#define AB8500_USB_LINE_STAT_REG 0x80 -#define AB8500_USB_PHY_CTRL_REG 0x8A - -#define AB8500_BIT_OTG_STAT_ID (1 << 0) -#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) -#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) -#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) -#define AB8500_BIT_WD_CTRL_KICK (1 << 1) - -#define AB8500_V1x_LINK_STAT_WAIT (HZ/10) -#define AB8500_WD_KICK_DELAY_US 100 /* usec */ -#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ -#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */ - -/* Usb line status register */ -enum ab8500_usb_link_status { - USB_LINK_NOT_CONFIGURED = 0, - USB_LINK_STD_HOST_NC, - USB_LINK_STD_HOST_C_NS, - USB_LINK_STD_HOST_C_S, - USB_LINK_HOST_CHG_NM, - USB_LINK_HOST_CHG_HS, - USB_LINK_HOST_CHG_HS_CHIRP, - USB_LINK_DEDICATED_CHG, - USB_LINK_ACA_RID_A, - USB_LINK_ACA_RID_B, - USB_LINK_ACA_RID_C_NM, - USB_LINK_ACA_RID_C_HS, - USB_LINK_ACA_RID_C_HS_CHIRP, - USB_LINK_HM_IDGND, - USB_LINK_RESERVED, - USB_LINK_NOT_VALID_LINK -}; - -struct ab8500_usb { - struct usb_phy phy; - struct device *dev; - int irq_num_id_rise; - int irq_num_id_fall; - int irq_num_vbus_rise; - int irq_num_vbus_fall; - int irq_num_link_status; - unsigned vbus_draw; - struct delayed_work dwork; - struct work_struct phy_dis_work; - unsigned long link_status_wait; - int rev; -}; - -static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) -{ - return container_of(x, struct ab8500_usb, phy); -} - -static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) -{ - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - AB8500_BIT_WD_CTRL_ENABLE); - - udelay(AB8500_WD_KICK_DELAY_US); - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - (AB8500_BIT_WD_CTRL_ENABLE - | AB8500_BIT_WD_CTRL_KICK)); - - if (ab->rev > 0x10) /* v1.1 v2.0 */ - udelay(AB8500_WD_V11_DISABLE_DELAY_US); - else /* v1.0 */ - msleep(AB8500_WD_V10_DISABLE_DELAY_MS); - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, - AB8500_MAIN_WD_CTRL_REG, - 0); -} - -static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, - bool enable) -{ - u8 ctrl_reg; - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - &ctrl_reg); - if (sel_host) { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN; - } else { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN; - } - - abx500_set_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - ctrl_reg); - - /* Needed to enable the phy.*/ - if (enable) - ab8500_usb_wd_workaround(ab); -} - -#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true) -#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false) -#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) -#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) - -static int ab8500_usb_link_status_update(struct ab8500_usb *ab) -{ - u8 reg; - enum ab8500_usb_link_status lsts; - void *v = NULL; - enum usb_phy_events event; - - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_LINE_STAT_REG, - ®); - - lsts = (reg >> 3) & 0x0F; - - switch (lsts) { - case USB_LINK_NOT_CONFIGURED: - case USB_LINK_RESERVED: - case USB_LINK_NOT_VALID_LINK: - /* TODO: Disable regulators. */ - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - ab->phy.state = OTG_STATE_B_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - event = USB_EVENT_NONE; - break; - - case USB_LINK_STD_HOST_NC: - case USB_LINK_STD_HOST_C_NS: - case USB_LINK_STD_HOST_C_S: - case USB_LINK_HOST_CHG_NM: - case USB_LINK_HOST_CHG_HS: - case USB_LINK_HOST_CHG_HS_CHIRP: - if (ab->phy.otg->gadget) { - /* TODO: Enable regulators. */ - ab8500_usb_peri_phy_en(ab); - v = ab->phy.otg->gadget; - } - event = USB_EVENT_VBUS; - break; - - case USB_LINK_HM_IDGND: - if (ab->phy.otg->host) { - /* TODO: Enable regulators. */ - ab8500_usb_host_phy_en(ab); - v = ab->phy.otg->host; - } - ab->phy.state = OTG_STATE_A_IDLE; - ab->phy.otg->default_a = true; - event = USB_EVENT_ID; - break; - - case USB_LINK_ACA_RID_A: - case USB_LINK_ACA_RID_B: - /* TODO */ - case USB_LINK_ACA_RID_C_NM: - case USB_LINK_ACA_RID_C_HS: - case USB_LINK_ACA_RID_C_HS_CHIRP: - case USB_LINK_DEDICATED_CHG: - /* TODO: vbus_draw */ - event = USB_EVENT_CHARGER; - break; - } - - atomic_notifier_call_chain(&ab->phy.notifier, event, v); - - return 0; -} - -static void ab8500_usb_delayed_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - dwork.work); - - ab8500_usb_link_status_update(ab); -} - -static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - -static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Link status will not be updated till phy is disabled. */ - ab8500_usb_peri_phy_dis(ab); - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - -static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - ab8500_usb_link_status_update(ab); - - return IRQ_HANDLED; -} - -static void ab8500_usb_phy_disable_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - phy_dis_work); - - if (!ab->phy.otg->host) - ab8500_usb_host_phy_dis(ab); - - if (!ab->phy.otg->gadget) - ab8500_usb_peri_phy_dis(ab); -} - -static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) -{ - struct ab8500_usb *ab; - - if (!phy) - return -ENODEV; - - ab = phy_to_ab(phy); - - ab->vbus_draw = mA; - - if (mA) - atomic_notifier_call_chain(&ab->phy.notifier, - USB_EVENT_ENUMERATED, ab->phy.otg->gadget); - return 0; -} - -/* TODO: Implement some way for charging or other drivers to read - * ab->vbus_draw. - */ - -static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) -{ - /* TODO */ - return 0; -} - -static int ab8500_usb_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct ab8500_usb *ab; - - if (!otg) - return -ENODEV; - - ab = phy_to_ab(otg->phy); - - /* Some drivers call this function in atomic context. - * Do not update ab8500 registers directly till this - * is fixed. - */ - - if (!gadget) { - /* TODO: Disable regulators. */ - otg->gadget = NULL; - schedule_work(&ab->phy_dis_work); - } else { - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - } - - return 0; -} - -static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct ab8500_usb *ab; - - if (!otg) - return -ENODEV; - - ab = phy_to_ab(otg->phy); - - /* Some drivers call this function in atomic context. - * Do not update ab8500 registers directly till this - * is fixed. - */ - - if (!host) { - /* TODO: Disable regulators. */ - otg->host = NULL; - schedule_work(&ab->phy_dis_work); - } else { - otg->host = host; - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - } - - return 0; -} - -static void ab8500_usb_irq_free(struct ab8500_usb *ab) -{ - if (ab->rev < 0x20) { - free_irq(ab->irq_num_id_rise, ab); - free_irq(ab->irq_num_id_fall, ab); - free_irq(ab->irq_num_vbus_rise, ab); - free_irq(ab->irq_num_vbus_fall, ab); - } else { - free_irq(ab->irq_num_link_status, ab); - } -} - -static int ab8500_usb_v1x_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) -{ - int err; - - ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R"); - if (ab->irq_num_id_rise < 0) { - dev_err(&pdev->dev, "ID rise irq not found\n"); - return ab->irq_num_id_rise; - } - err = request_threaded_irq(ab->irq_num_id_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID rise irq\n"); - goto fail0; - } - - ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); - if (ab->irq_num_id_fall < 0) { - dev_err(&pdev->dev, "ID fall irq not found\n"); - return ab->irq_num_id_fall; - } - err = request_threaded_irq(ab->irq_num_id_fall, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID fall irq\n"); - goto fail1; - } - - ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R"); - if (ab->irq_num_vbus_rise < 0) { - dev_err(&pdev->dev, "VBUS rise irq not found\n"); - return ab->irq_num_vbus_rise; - } - err = request_threaded_irq(ab->irq_num_vbus_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus rise irq\n"); - goto fail2; - } - - ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F"); - if (ab->irq_num_vbus_fall < 0) { - dev_err(&pdev->dev, "VBUS fall irq not found\n"); - return ab->irq_num_vbus_fall; - } - err = request_threaded_irq(ab->irq_num_vbus_fall, NULL, - ab8500_usb_v1x_vbus_fall_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); - goto fail3; - } - - return 0; -fail3: - free_irq(ab->irq_num_vbus_rise, ab); -fail2: - free_irq(ab->irq_num_id_fall, ab); -fail1: - free_irq(ab->irq_num_id_rise, ab); -fail0: - return err; -} - -static int ab8500_usb_v2_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) -{ - int err; - - ab->irq_num_link_status = platform_get_irq_byname(pdev, - "USB_LINK_STATUS"); - if (ab->irq_num_link_status < 0) { - dev_err(&pdev->dev, "Link status irq not found\n"); - return ab->irq_num_link_status; - } - - err = request_threaded_irq(ab->irq_num_link_status, NULL, - ab8500_usb_v20_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-link-status", ab); - if (err < 0) { - dev_err(ab->dev, - "request_irq failed for link status irq\n"); - return err; - } - - return 0; -} - -static int ab8500_usb_probe(struct platform_device *pdev) -{ - struct ab8500_usb *ab; - struct usb_otg *otg; - int err; - int rev; - - rev = abx500_get_chip_id(&pdev->dev); - if (rev < 0) { - dev_err(&pdev->dev, "Chip id read failed\n"); - return rev; - } else if (rev < 0x10) { - dev_err(&pdev->dev, "Unsupported AB8500 chip\n"); - return -ENODEV; - } - - ab = kzalloc(sizeof *ab, GFP_KERNEL); - if (!ab) - return -ENOMEM; - - otg = kzalloc(sizeof *otg, GFP_KERNEL); - if (!otg) { - kfree(ab); - return -ENOMEM; - } - - ab->dev = &pdev->dev; - ab->rev = rev; - ab->phy.dev = ab->dev; - ab->phy.otg = otg; - ab->phy.label = "ab8500"; - ab->phy.set_suspend = ab8500_usb_set_suspend; - ab->phy.set_power = ab8500_usb_set_power; - ab->phy.state = OTG_STATE_UNDEFINED; - - otg->phy = &ab->phy; - otg->set_host = ab8500_usb_set_host; - otg->set_peripheral = ab8500_usb_set_peripheral; - - platform_set_drvdata(pdev, ab); - - ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); - - /* v1: Wait for link status to become stable. - * all: Updates form set_host and set_peripheral as they are atomic. - */ - INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work); - - /* all: Disable phy when called from set_host and set_peripheral */ - INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - - if (ab->rev < 0x20) { - err = ab8500_usb_v1x_res_setup(pdev, ab); - ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT; - } else { - err = ab8500_usb_v2_res_setup(pdev, ab); - } - - if (err < 0) - goto fail0; - - err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); - if (err) { - dev_err(&pdev->dev, "Can't register transceiver\n"); - goto fail1; - } - - dev_info(&pdev->dev, "AB8500 usb driver initialized\n"); - - return 0; -fail1: - ab8500_usb_irq_free(ab); -fail0: - kfree(otg); - kfree(ab); - return err; -} - -static int ab8500_usb_remove(struct platform_device *pdev) -{ - struct ab8500_usb *ab = platform_get_drvdata(pdev); - - ab8500_usb_irq_free(ab); - - cancel_delayed_work_sync(&ab->dwork); - - cancel_work_sync(&ab->phy_dis_work); - - usb_remove_phy(&ab->phy); - - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - - platform_set_drvdata(pdev, NULL); - - kfree(ab->phy.otg); - kfree(ab); - - return 0; -} - -static struct platform_driver ab8500_usb_driver = { - .probe = ab8500_usb_probe, - .remove = ab8500_usb_remove, - .driver = { - .name = "ab8500-usb", - .owner = THIS_MODULE, - }, -}; - -static int __init ab8500_usb_init(void) -{ - return platform_driver_register(&ab8500_usb_driver); -} -subsys_initcall(ab8500_usb_init); - -static void __exit ab8500_usb_exit(void) -{ - platform_driver_unregister(&ab8500_usb_driver); -} -module_exit(ab8500_usb_exit); - -MODULE_ALIAS("platform:ab8500_usb"); -MODULE_AUTHOR("ST-Ericsson AB"); -MODULE_DESCRIPTION("AB8500 usb transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/fsl_otg.c b/drivers/usb/phy/fsl_otg.c deleted file mode 100644 index 72a2a00..0000000 --- a/drivers/usb/phy/fsl_otg.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * Copyright (C) 2007,2008 Freescale semiconductor, Inc. - * - * Author: Li Yang - * Jerry Huang - * - * Initialization based on code from Shlomi Gridish. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "fsl_otg.h" - -#define DRIVER_VERSION "Rev. 1.55" -#define DRIVER_AUTHOR "Jerry Huang/Li Yang" -#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" -#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION - -static const char driver_name[] = "fsl-usb2-otg"; - -const pm_message_t otg_suspend_state = { - .event = 1, -}; - -#define HA_DATA_PULSE - -static struct usb_dr_mmap *usb_dr_regs; -static struct fsl_otg *fsl_otg_dev; -static int srp_wait_done; - -/* FSM timers */ -struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, - *b_ase0_brst_tmr, *b_se0_srp_tmr; - -/* Driver specific timers */ -struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, - *b_srp_wait_tmr, *a_wait_enum_tmr; - -static struct list_head active_timers; - -static struct fsl_otg_config fsl_otg_initdata = { - .otg_port = 1, -}; - -#ifdef CONFIG_PPC32 -static u32 _fsl_readl_be(const unsigned __iomem *p) -{ - return in_be32(p); -} - -static u32 _fsl_readl_le(const unsigned __iomem *p) -{ - return in_le32(p); -} - -static void _fsl_writel_be(u32 v, unsigned __iomem *p) -{ - out_be32(p, v); -} - -static void _fsl_writel_le(u32 v, unsigned __iomem *p) -{ - out_le32(p, v); -} - -static u32 (*_fsl_readl)(const unsigned __iomem *p); -static void (*_fsl_writel)(u32 v, unsigned __iomem *p); - -#define fsl_readl(p) (*_fsl_readl)((p)) -#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) - -#else -#define fsl_readl(addr) readl(addr) -#define fsl_writel(val, addr) writel(val, addr) -#endif /* CONFIG_PPC32 */ - -/* Routines to access transceiver ULPI registers */ -u8 view_ulpi(u8 addr) -{ - u32 temp; - - temp = 0x40000000 | (addr << 16); - fsl_writel(temp, &usb_dr_regs->ulpiview); - udelay(1000); - while (temp & 0x40) - temp = fsl_readl(&usb_dr_regs->ulpiview); - return (le32_to_cpu(temp) & 0x0000ff00) >> 8; -} - -int write_ulpi(u8 addr, u8 data) -{ - u32 temp; - - temp = 0x60000000 | (addr << 16) | data; - fsl_writel(temp, &usb_dr_regs->ulpiview); - return 0; -} - -/* -------------------------------------------------------------*/ -/* Operations that will be called from OTG Finite State Machine */ - -/* Charge vbus for vbus pulsing in SRP */ -void fsl_otg_chrg_vbus(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - /* stop discharging, start charging */ - tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | - OTGSC_CTRL_VBUS_CHARGE; - else - /* stop charging */ - tmp &= ~OTGSC_CTRL_VBUS_CHARGE; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* Discharge vbus through a resistor to ground */ -void fsl_otg_dischrg_vbus(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - /* stop charging, start discharging */ - tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | - OTGSC_CTRL_VBUS_DISCHARGE; - else - /* stop discharging */ - tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* A-device driver vbus, controlled through PP bit in PORTSC */ -void fsl_otg_drv_vbus(int on) -{ - u32 tmp; - - if (on) { - tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; - fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); - } else { - tmp = fsl_readl(&usb_dr_regs->portsc) & - ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; - fsl_writel(tmp, &usb_dr_regs->portsc); - } -} - -/* - * Pull-up D+, signalling connect by periperal. Also used in - * data-line pulsing in SRP - */ -void fsl_otg_loc_conn(int on) -{ - u32 tmp; - - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - - if (on) - tmp |= OTGSC_CTRL_DATA_PULSING; - else - tmp &= ~OTGSC_CTRL_DATA_PULSING; - - fsl_writel(tmp, &usb_dr_regs->otgsc); -} - -/* - * Generate SOF by host. This is controlled through suspend/resume the - * port. In host mode, controller will automatically send SOF. - * Suspend will block the data on the port. - */ -void fsl_otg_loc_sof(int on) -{ - u32 tmp; - - tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; - if (on) - tmp |= PORTSC_PORT_FORCE_RESUME; - else - tmp |= PORTSC_PORT_SUSPEND; - - fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); - -} - -/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ -void fsl_otg_start_pulse(void) -{ - u32 tmp; - - srp_wait_done = 0; -#ifdef HA_DATA_PULSE - tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; - tmp |= OTGSC_HA_DATA_PULSE; - fsl_writel(tmp, &usb_dr_regs->otgsc); -#else - fsl_otg_loc_conn(1); -#endif - - fsl_otg_add_timer(b_data_pulse_tmr); -} - -void b_data_pulse_end(unsigned long foo) -{ -#ifdef HA_DATA_PULSE -#else - fsl_otg_loc_conn(0); -#endif - - /* Do VBUS pulse after data pulse */ - fsl_otg_pulse_vbus(); -} - -void fsl_otg_pulse_vbus(void) -{ - srp_wait_done = 0; - fsl_otg_chrg_vbus(1); - /* start the timer to end vbus charge */ - fsl_otg_add_timer(b_vbus_pulse_tmr); -} - -void b_vbus_pulse_end(unsigned long foo) -{ - fsl_otg_chrg_vbus(0); - - /* - * As USB3300 using the same a_sess_vld and b_sess_vld voltage - * we need to discharge the bus for a while to distinguish - * residual voltage of vbus pulsing and A device pull up - */ - fsl_otg_dischrg_vbus(1); - fsl_otg_add_timer(b_srp_wait_tmr); -} - -void b_srp_end(unsigned long foo) -{ - fsl_otg_dischrg_vbus(0); - srp_wait_done = 1; - - if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) && - fsl_otg_dev->fsm.b_sess_vld) - fsl_otg_dev->fsm.b_srp_done = 1; -} - -/* - * Workaround for a_host suspending too fast. When a_bus_req=0, - * a_host will start by SRP. It needs to set b_hnp_enable before - * actually suspending to start HNP - */ -void a_wait_enum(unsigned long foo) -{ - VDBG("a_wait_enum timeout\n"); - if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) - fsl_otg_add_timer(a_wait_enum_tmr); - else - otg_statemachine(&fsl_otg_dev->fsm); -} - -/* The timeout callback function to set time out bit */ -void set_tmout(unsigned long indicator) -{ - *(int *)indicator = 1; -} - -/* Initialize timers */ -int fsl_otg_init_timers(struct otg_fsm *fsm) -{ - /* FSM used timers */ - a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, - (unsigned long)&fsm->a_wait_vrise_tmout); - if (!a_wait_vrise_tmr) - return -ENOMEM; - - a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, - (unsigned long)&fsm->a_wait_bcon_tmout); - if (!a_wait_bcon_tmr) - return -ENOMEM; - - a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, - (unsigned long)&fsm->a_aidl_bdis_tmout); - if (!a_aidl_bdis_tmr) - return -ENOMEM; - - b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, - (unsigned long)&fsm->b_ase0_brst_tmout); - if (!b_ase0_brst_tmr) - return -ENOMEM; - - b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, - (unsigned long)&fsm->b_se0_srp); - if (!b_se0_srp_tmr) - return -ENOMEM; - - b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, - (unsigned long)&fsm->b_srp_done); - if (!b_srp_fail_tmr) - return -ENOMEM; - - a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, - (unsigned long)&fsm); - if (!a_wait_enum_tmr) - return -ENOMEM; - - /* device driver used timers */ - b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); - if (!b_srp_wait_tmr) - return -ENOMEM; - - b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, - TB_DATA_PLS, 0); - if (!b_data_pulse_tmr) - return -ENOMEM; - - b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, - TB_VBUS_PLS, 0); - if (!b_vbus_pulse_tmr) - return -ENOMEM; - - return 0; -} - -/* Uninitialize timers */ -void fsl_otg_uninit_timers(void) -{ - /* FSM used timers */ - kfree(a_wait_vrise_tmr); - kfree(a_wait_bcon_tmr); - kfree(a_aidl_bdis_tmr); - kfree(b_ase0_brst_tmr); - kfree(b_se0_srp_tmr); - kfree(b_srp_fail_tmr); - kfree(a_wait_enum_tmr); - - /* device driver used timers */ - kfree(b_srp_wait_tmr); - kfree(b_data_pulse_tmr); - kfree(b_vbus_pulse_tmr); -} - -/* Add timer to timer list */ -void fsl_otg_add_timer(void *gtimer) -{ - struct fsl_otg_timer *timer = gtimer; - struct fsl_otg_timer *tmp_timer; - - /* - * Check if the timer is already in the active list, - * if so update timer count - */ - list_for_each_entry(tmp_timer, &active_timers, list) - if (tmp_timer == timer) { - timer->count = timer->expires; - return; - } - timer->count = timer->expires; - list_add_tail(&timer->list, &active_timers); -} - -/* Remove timer from the timer list; clear timeout status */ -void fsl_otg_del_timer(void *gtimer) -{ - struct fsl_otg_timer *timer = gtimer; - struct fsl_otg_timer *tmp_timer, *del_tmp; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) - if (tmp_timer == timer) - list_del(&timer->list); -} - -/* - * Reduce timer count by 1, and find timeout conditions. - * Called by fsl_otg 1ms timer interrupt - */ -int fsl_otg_tick_timer(void) -{ - struct fsl_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - return expired; -} - -/* Reset controller, not reset the bus */ -void otg_reset_controller(void) -{ - u32 command; - - command = fsl_readl(&usb_dr_regs->usbcmd); - command |= (1 << 1); - fsl_writel(command, &usb_dr_regs->usbcmd); - while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) - ; -} - -/* Call suspend/resume routines in host driver */ -int fsl_otg_start_host(struct otg_fsm *fsm, int on) -{ - struct usb_otg *otg = fsm->otg; - struct device *dev; - struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy); - u32 retval = 0; - - if (!otg->host) - return -ENODEV; - dev = otg->host->controller; - - /* - * Update a_vbus_vld state as a_vbus_vld int is disabled - * in device mode - */ - fsm->a_vbus_vld = - !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); - if (on) { - /* start fsl usb host controller */ - if (otg_dev->host_working) - goto end; - else { - otg_reset_controller(); - VDBG("host on......\n"); - if (dev->driver->pm && dev->driver->pm->resume) { - retval = dev->driver->pm->resume(dev); - if (fsm->id) { - /* default-b */ - fsl_otg_drv_vbus(1); - /* - * Workaround: b_host can't driver - * vbus, but PP in PORTSC needs to - * be 1 for host to work. - * So we set drv_vbus bit in - * transceiver to 0 thru ULPI. - */ - write_ulpi(0x0c, 0x20); - } - } - - otg_dev->host_working = 1; - } - } else { - /* stop fsl usb host controller */ - if (!otg_dev->host_working) - goto end; - else { - VDBG("host off......\n"); - if (dev && dev->driver) { - if (dev->driver->pm && dev->driver->pm->suspend) - retval = dev->driver->pm->suspend(dev); - if (fsm->id) - /* default-b */ - fsl_otg_drv_vbus(0); - } - otg_dev->host_working = 0; - } - } -end: - return retval; -} - -/* - * Call suspend and resume function in udc driver - * to stop and start udc driver. - */ -int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) -{ - struct usb_otg *otg = fsm->otg; - struct device *dev; - - if (!otg->gadget || !otg->gadget->dev.parent) - return -ENODEV; - - VDBG("gadget %s\n", on ? "on" : "off"); - dev = otg->gadget->dev.parent; - - if (on) { - if (dev->driver->resume) - dev->driver->resume(dev); - } else { - if (dev->driver->suspend) - dev->driver->suspend(dev, otg_suspend_state); - } - - return 0; -} - -/* - * Called by initialization code of host driver. Register host controller - * to the OTG. Suspend host for OTG role detection. - */ -static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - otg->host = host; - - otg_dev->fsm.a_bus_drop = 0; - otg_dev->fsm.a_bus_req = 1; - - if (host) { - VDBG("host off......\n"); - - otg->host->otg_port = fsl_otg_initdata.otg_port; - otg->host->is_b_host = otg_dev->fsm.id; - /* - * must leave time for khubd to finish its thing - * before yanking the host driver out from under it, - * so suspend the host after a short delay. - */ - otg_dev->host_working = 1; - schedule_delayed_work(&otg_dev->otg_event, 100); - return 0; - } else { - /* host driver going away */ - if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & - OTGSC_STS_USB_ID)) { - /* Mini-A cable connected */ - struct otg_fsm *fsm = &otg_dev->fsm; - - otg->phy->state = OTG_STATE_UNDEFINED; - fsm->protocol = PROTO_UNDEF; - } - } - - otg_dev->host_working = 0; - - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* Called by initialization code of udc. Register udc to OTG. */ -static int fsl_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - VDBG("otg_dev 0x%x\n", (int)otg_dev); - VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - if (!gadget) { - if (!otg->default_a) - otg->gadget->ops->vbus_draw(otg->gadget, 0); - usb_gadget_vbus_disconnect(otg->gadget); - otg->gadget = 0; - otg_dev->fsm.b_bus_req = 0; - otg_statemachine(&otg_dev->fsm); - return 0; - } - - otg->gadget = gadget; - otg->gadget->is_a_peripheral = !otg_dev->fsm.id; - - otg_dev->fsm.b_bus_req = 1; - - /* start the gadget right away if the ID pin says Mini-B */ - DBG("ID pin=%d\n", otg_dev->fsm.id); - if (otg_dev->fsm.id == 1) { - fsl_otg_start_host(&otg_dev->fsm, 0); - otg_drv_vbus(&otg_dev->fsm, 0); - fsl_otg_start_gadget(&otg_dev->fsm, 1); - } - - return 0; -} - -/* Set OTG port power, only for B-device */ -static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA) -{ - if (!fsl_otg_dev) - return -ENODEV; - if (phy->state == OTG_STATE_B_PERIPHERAL) - pr_info("FSL OTG: Draw %d mA\n", mA); - - return 0; -} - -/* - * Delayed pin detect interrupt processing. - * - * When the Mini-A cable is disconnected from the board, - * the pin-detect interrupt happens before the disconnect - * interrupts for the connected device(s). In order to - * process the disconnect interrupt(s) prior to switching - * roles, the pin-detect interrupts are delayed, and handled - * by this routine. - */ -static void fsl_otg_event(struct work_struct *work) -{ - struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); - struct otg_fsm *fsm = &og->fsm; - - if (fsm->id) { /* switch to gadget */ - fsl_otg_start_host(fsm, 0); - otg_drv_vbus(fsm, 0); - fsl_otg_start_gadget(fsm, 1); - } -} - -/* B-device start SRP */ -static int fsl_otg_start_srp(struct usb_otg *otg) -{ - struct fsl_otg *otg_dev; - - if (!otg || otg->phy->state != OTG_STATE_B_IDLE) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - otg_dev->fsm.b_bus_req = 1; - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* A_host suspend will call this function to start hnp */ -static int fsl_otg_start_hnp(struct usb_otg *otg) -{ - struct fsl_otg *otg_dev; - - if (!otg) - return -ENODEV; - - otg_dev = container_of(otg->phy, struct fsl_otg, phy); - if (otg_dev != fsl_otg_dev) - return -ENODEV; - - DBG("start_hnp...n"); - - /* clear a_bus_req to enter a_suspend state */ - otg_dev->fsm.a_bus_req = 0; - otg_statemachine(&otg_dev->fsm); - - return 0; -} - -/* - * Interrupt handler. OTG/host/peripheral share the same int line. - * OTG driver clears OTGSC interrupts and leaves USB interrupts - * intact. It needs to have knowledge of some USB interrupts - * such as port change. - */ -irqreturn_t fsl_otg_isr(int irq, void *dev_id) -{ - struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; - struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; - u32 otg_int_src, otg_sc; - - otg_sc = fsl_readl(&usb_dr_regs->otgsc); - otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); - - /* Only clear otg interrupts */ - fsl_writel(otg_sc, &usb_dr_regs->otgsc); - - /*FIXME: ID change not generate when init to 0 */ - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); - - /* process OTG interrupts */ - if (otg_int_src) { - if (otg_int_src & OTGSC_INTSTS_USB_ID) { - fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; - otg->default_a = (fsm->id == 0); - /* clear conn information */ - if (fsm->id) - fsm->b_conn = 0; - else - fsm->a_conn = 0; - - if (otg->host) - otg->host->is_b_host = fsm->id; - if (otg->gadget) - otg->gadget->is_a_peripheral = !fsm->id; - VDBG("ID int (ID is %d)\n", fsm->id); - - if (fsm->id) { /* switch to gadget */ - schedule_delayed_work( - &((struct fsl_otg *)dev_id)->otg_event, - 100); - } else { /* switch to host */ - cancel_delayed_work(& - ((struct fsl_otg *)dev_id)-> - otg_event); - fsl_otg_start_gadget(fsm, 0); - otg_drv_vbus(fsm, 1); - fsl_otg_start_host(fsm, 1); - } - return IRQ_HANDLED; - } - } - return IRQ_NONE; -} - -static struct otg_fsm_ops fsl_otg_ops = { - .chrg_vbus = fsl_otg_chrg_vbus, - .drv_vbus = fsl_otg_drv_vbus, - .loc_conn = fsl_otg_loc_conn, - .loc_sof = fsl_otg_loc_sof, - .start_pulse = fsl_otg_start_pulse, - - .add_timer = fsl_otg_add_timer, - .del_timer = fsl_otg_del_timer, - - .start_host = fsl_otg_start_host, - .start_gadget = fsl_otg_start_gadget, -}; - -/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ -static int fsl_otg_conf(struct platform_device *pdev) -{ - struct fsl_otg *fsl_otg_tc; - int status; - - if (fsl_otg_dev) - return 0; - - /* allocate space to fsl otg device */ - fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); - if (!fsl_otg_tc) - return -ENOMEM; - - fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!fsl_otg_tc->phy.otg) { - kfree(fsl_otg_tc); - return -ENOMEM; - } - - INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); - - INIT_LIST_HEAD(&active_timers); - status = fsl_otg_init_timers(&fsl_otg_tc->fsm); - if (status) { - pr_info("Couldn't init OTG timers\n"); - goto err; - } - spin_lock_init(&fsl_otg_tc->fsm.lock); - - /* Set OTG state machine operations */ - fsl_otg_tc->fsm.ops = &fsl_otg_ops; - - /* initialize the otg structure */ - fsl_otg_tc->phy.label = DRIVER_DESC; - fsl_otg_tc->phy.set_power = fsl_otg_set_power; - - fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy; - fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host; - fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral; - fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp; - fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp; - - fsl_otg_dev = fsl_otg_tc; - - /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); - if (status) { - pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); - goto err; - } - - return 0; -err: - fsl_otg_uninit_timers(); - kfree(fsl_otg_tc->phy.otg); - kfree(fsl_otg_tc); - return status; -} - -/* OTG Initialization */ -int usb_otg_start(struct platform_device *pdev) -{ - struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); - struct otg_fsm *fsm; - int status; - struct resource *res; - u32 temp; - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - p_otg = container_of(otg_trans, struct fsl_otg, phy); - fsm = &p_otg->fsm; - - /* Initialize the state machine structure with default values */ - SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); - fsm->otg = p_otg->phy.otg; - - /* We don't require predefined MEM/IRQ resource index */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - /* We don't request_mem_region here to enable resource sharing - * with host/device */ - - usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); - p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; - pdata->regs = (void *)usb_dr_regs; - - if (pdata->init && pdata->init(pdev) != 0) - return -EINVAL; - - if (pdata->big_endian_mmio) { - _fsl_readl = _fsl_readl_be; - _fsl_writel = _fsl_writel_be; - } else { - _fsl_readl = _fsl_readl_le; - _fsl_writel = _fsl_writel_le; - } - - /* request irq */ - p_otg->irq = platform_get_irq(pdev, 0); - status = request_irq(p_otg->irq, fsl_otg_isr, - IRQF_SHARED, driver_name, p_otg); - if (status) { - dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n", - p_otg->irq, status); - iounmap(p_otg->dr_mem_map); - kfree(p_otg->phy.otg); - kfree(p_otg); - return status; - } - - /* stop the controller */ - temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); - temp &= ~USB_CMD_RUN_STOP; - fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); - - /* reset the controller */ - temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); - temp |= USB_CMD_CTRL_RESET; - fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); - - /* wait reset completed */ - while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) - ; - - /* configure the VBUSHS as IDLE(both host and device) */ - temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); - fsl_writel(temp, &p_otg->dr_mem_map->usbmode); - - /* configure PHY interface */ - temp = fsl_readl(&p_otg->dr_mem_map->portsc); - temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); - switch (pdata->phy_mode) { - case FSL_USB2_PHY_ULPI: - temp |= PORTSC_PTS_ULPI; - break; - case FSL_USB2_PHY_UTMI_WIDE: - temp |= PORTSC_PTW_16BIT; - /* fall through */ - case FSL_USB2_PHY_UTMI: - temp |= PORTSC_PTS_UTMI; - /* fall through */ - default: - break; - } - fsl_writel(temp, &p_otg->dr_mem_map->portsc); - - if (pdata->have_sysif_regs) { - /* configure control enable IO output, big endian register */ - temp = __raw_readl(&p_otg->dr_mem_map->control); - temp |= USB_CTRL_IOENB; - __raw_writel(temp, &p_otg->dr_mem_map->control); - } - - /* disable all interrupt and clear all OTGSC status */ - temp = fsl_readl(&p_otg->dr_mem_map->otgsc); - temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; - temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; - fsl_writel(temp, &p_otg->dr_mem_map->otgsc); - - /* - * The identification (id) input is FALSE when a Mini-A plug is inserted - * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. - * Also: record initial state of ID pin - */ - if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { - p_otg->phy.state = OTG_STATE_UNDEFINED; - p_otg->fsm.id = 1; - } else { - p_otg->phy.state = OTG_STATE_A_IDLE; - p_otg->fsm.id = 0; - } - - DBG("initial ID pin=%d\n", p_otg->fsm.id); - - /* enable OTG ID pin interrupt */ - temp = fsl_readl(&p_otg->dr_mem_map->otgsc); - temp |= OTGSC_INTR_USB_ID_EN; - temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); - fsl_writel(temp, &p_otg->dr_mem_map->otgsc); - - return 0; -} - -/* - * state file in sysfs - */ -static int show_fsl_usb2_otg_state(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct otg_fsm *fsm = &fsl_otg_dev->fsm; - char *next = buf; - unsigned size = PAGE_SIZE; - unsigned long flags; - int t; - - spin_lock_irqsave(&fsm->lock, flags); - - /* basic driver infomation */ - t = scnprintf(next, size, - DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", - DRIVER_VERSION); - size -= t; - next += t; - - /* Registers */ - t = scnprintf(next, size, - "OTGSC: 0x%08x\n" - "PORTSC: 0x%08x\n" - "USBMODE: 0x%08x\n" - "USBCMD: 0x%08x\n" - "USBSTS: 0x%08x\n" - "USBINTR: 0x%08x\n", - fsl_readl(&usb_dr_regs->otgsc), - fsl_readl(&usb_dr_regs->portsc), - fsl_readl(&usb_dr_regs->usbmode), - fsl_readl(&usb_dr_regs->usbcmd), - fsl_readl(&usb_dr_regs->usbsts), - fsl_readl(&usb_dr_regs->usbintr)); - size -= t; - next += t; - - /* State */ - t = scnprintf(next, size, - "OTG state: %s\n\n", - usb_otg_state_string(fsl_otg_dev->phy.state)); - size -= t; - next += t; - - /* State Machine Variables */ - t = scnprintf(next, size, - "a_bus_req: %d\n" - "b_bus_req: %d\n" - "a_bus_resume: %d\n" - "a_bus_suspend: %d\n" - "a_conn: %d\n" - "a_sess_vld: %d\n" - "a_srp_det: %d\n" - "a_vbus_vld: %d\n" - "b_bus_resume: %d\n" - "b_bus_suspend: %d\n" - "b_conn: %d\n" - "b_se0_srp: %d\n" - "b_sess_end: %d\n" - "b_sess_vld: %d\n" - "id: %d\n", - fsm->a_bus_req, - fsm->b_bus_req, - fsm->a_bus_resume, - fsm->a_bus_suspend, - fsm->a_conn, - fsm->a_sess_vld, - fsm->a_srp_det, - fsm->a_vbus_vld, - fsm->b_bus_resume, - fsm->b_bus_suspend, - fsm->b_conn, - fsm->b_se0_srp, - fsm->b_sess_end, - fsm->b_sess_vld, - fsm->id); - size -= t; - next += t; - - spin_unlock_irqrestore(&fsm->lock, flags); - - return PAGE_SIZE - size; -} - -static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); - - -/* Char driver interface to control some OTG input */ - -/* - * Handle some ioctl command, such as get otg - * status and set host suspend - */ -static long fsl_otg_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - u32 retval = 0; - - switch (cmd) { - case GET_OTG_STATUS: - retval = fsl_otg_dev->host_working; - break; - - case SET_A_SUSPEND_REQ: - fsl_otg_dev->fsm.a_suspend_req = arg; - break; - - case SET_A_BUS_DROP: - fsl_otg_dev->fsm.a_bus_drop = arg; - break; - - case SET_A_BUS_REQ: - fsl_otg_dev->fsm.a_bus_req = arg; - break; - - case SET_B_BUS_REQ: - fsl_otg_dev->fsm.b_bus_req = arg; - break; - - default: - break; - } - - otg_statemachine(&fsl_otg_dev->fsm); - - return retval; -} - -static int fsl_otg_open(struct inode *inode, struct file *file) -{ - return 0; -} - -static int fsl_otg_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static const struct file_operations otg_fops = { - .owner = THIS_MODULE, - .llseek = NULL, - .read = NULL, - .write = NULL, - .unlocked_ioctl = fsl_otg_ioctl, - .open = fsl_otg_open, - .release = fsl_otg_release, -}; - -static int fsl_otg_probe(struct platform_device *pdev) -{ - int ret; - - if (!pdev->dev.platform_data) - return -ENODEV; - - /* configure the OTG */ - ret = fsl_otg_conf(pdev); - if (ret) { - dev_err(&pdev->dev, "Couldn't configure OTG module\n"); - return ret; - } - - /* start OTG */ - ret = usb_otg_start(pdev); - if (ret) { - dev_err(&pdev->dev, "Can't init FSL OTG device\n"); - return ret; - } - - ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); - if (ret) { - dev_err(&pdev->dev, "unable to register FSL OTG device\n"); - return ret; - } - - ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - if (ret) - dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); - - return ret; -} - -static int fsl_otg_remove(struct platform_device *pdev) -{ - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - - usb_remove_phy(&fsl_otg_dev->phy); - free_irq(fsl_otg_dev->irq, fsl_otg_dev); - - iounmap((void *)usb_dr_regs); - - fsl_otg_uninit_timers(); - kfree(fsl_otg_dev->phy.otg); - kfree(fsl_otg_dev); - - device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); - - unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); - - if (pdata->exit) - pdata->exit(pdev); - - return 0; -} - -struct platform_driver fsl_otg_driver = { - .probe = fsl_otg_probe, - .remove = fsl_otg_remove, - .driver = { - .name = driver_name, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(fsl_otg_driver); - -MODULE_DESCRIPTION(DRIVER_INFO); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/fsl_otg.h b/drivers/usb/phy/fsl_otg.h deleted file mode 100644 index ca26628..0000000 --- a/drivers/usb/phy/fsl_otg.h +++ /dev/null @@ -1,406 +0,0 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "otg_fsm.h" -#include -#include - -/* USB Command Register Bit Masks */ -#define USB_CMD_RUN_STOP (0x1<<0) -#define USB_CMD_CTRL_RESET (0x1<<1) -#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) -#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) -#define USB_CMD_INT_AA_DOORBELL (0x1<<6) -#define USB_CMD_ASP (0x3<<8) -#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) -#define USB_CMD_SUTW (0x1<<13) -#define USB_CMD_ATDTW (0x1<<14) -#define USB_CMD_ITC (0xFF<<16) - -/* bit 15,3,2 are frame list size */ -#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) -#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) -#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) -#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) -#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) -#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) -#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) -#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) - -/* bit 9-8 are async schedule park mode count */ -#define USB_CMD_ASP_00 (0x0<<8) -#define USB_CMD_ASP_01 (0x1<<8) -#define USB_CMD_ASP_10 (0x2<<8) -#define USB_CMD_ASP_11 (0x3<<8) -#define USB_CMD_ASP_BIT_POS (8) - -/* bit 23-16 are interrupt threshold control */ -#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) -#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) -#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) -#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) -#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) -#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) -#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) -#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) -#define USB_CMD_ITC_BIT_POS (16) - -/* USB Status Register Bit Masks */ -#define USB_STS_INT (0x1<<0) -#define USB_STS_ERR (0x1<<1) -#define USB_STS_PORT_CHANGE (0x1<<2) -#define USB_STS_FRM_LST_ROLL (0x1<<3) -#define USB_STS_SYS_ERR (0x1<<4) -#define USB_STS_IAA (0x1<<5) -#define USB_STS_RESET_RECEIVED (0x1<<6) -#define USB_STS_SOF (0x1<<7) -#define USB_STS_DCSUSPEND (0x1<<8) -#define USB_STS_HC_HALTED (0x1<<12) -#define USB_STS_RCL (0x1<<13) -#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) -#define USB_STS_ASYNC_SCHEDULE (0x1<<15) - -/* USB Interrupt Enable Register Bit Masks */ -#define USB_INTR_INT_EN (0x1<<0) -#define USB_INTR_ERR_INT_EN (0x1<<1) -#define USB_INTR_PC_DETECT_EN (0x1<<2) -#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) -#define USB_INTR_SYS_ERR_EN (0x1<<4) -#define USB_INTR_ASYN_ADV_EN (0x1<<5) -#define USB_INTR_RESET_EN (0x1<<6) -#define USB_INTR_SOF_EN (0x1<<7) -#define USB_INTR_DEVICE_SUSPEND (0x1<<8) - -/* Device Address bit masks */ -#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) -#define USB_DEVICE_ADDRESS_BIT_POS (25) -/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ -#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) -#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) -#define PORTSC_PORT_ENABLE (0x1<<2) -#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) -#define PORTSC_OVER_CURRENT_ACT (0x1<<4) -#define PORTSC_OVER_CUURENT_CHG (0x1<<5) -#define PORTSC_PORT_FORCE_RESUME (0x1<<6) -#define PORTSC_PORT_SUSPEND (0x1<<7) -#define PORTSC_PORT_RESET (0x1<<8) -#define PORTSC_LINE_STATUS_BITS (0x3<<10) -#define PORTSC_PORT_POWER (0x1<<12) -#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) -#define PORTSC_PORT_TEST_CTRL (0xF<<16) -#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) -#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) -#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) -#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) -#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) -#define PORTSC_PORT_SPEED_MASK (0x3<<26) -#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) -#define PORTSC_PHY_TYPE_SEL (0x3<<30) -/* bit 11-10 are line status */ -#define PORTSC_LINE_STATUS_SE0 (0x0<<10) -#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) -#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) -#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) -#define PORTSC_LINE_STATUS_BIT_POS (10) - -/* bit 15-14 are port indicator control */ -#define PORTSC_PIC_OFF (0x0<<14) -#define PORTSC_PIC_AMBER (0x1<<14) -#define PORTSC_PIC_GREEN (0x2<<14) -#define PORTSC_PIC_UNDEF (0x3<<14) -#define PORTSC_PIC_BIT_POS (14) - -/* bit 19-16 are port test control */ -#define PORTSC_PTC_DISABLE (0x0<<16) -#define PORTSC_PTC_JSTATE (0x1<<16) -#define PORTSC_PTC_KSTATE (0x2<<16) -#define PORTSC_PTC_SEQNAK (0x3<<16) -#define PORTSC_PTC_PACKET (0x4<<16) -#define PORTSC_PTC_FORCE_EN (0x5<<16) -#define PORTSC_PTC_BIT_POS (16) - -/* bit 27-26 are port speed */ -#define PORTSC_PORT_SPEED_FULL (0x0<<26) -#define PORTSC_PORT_SPEED_LOW (0x1<<26) -#define PORTSC_PORT_SPEED_HIGH (0x2<<26) -#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) -#define PORTSC_SPEED_BIT_POS (26) - -/* bit 28 is parallel transceiver width for UTMI interface */ -#define PORTSC_PTW (0x1<<28) -#define PORTSC_PTW_8BIT (0x0<<28) -#define PORTSC_PTW_16BIT (0x1<<28) - -/* bit 31-30 are port transceiver select */ -#define PORTSC_PTS_UTMI (0x0<<30) -#define PORTSC_PTS_ULPI (0x2<<30) -#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) -#define PORTSC_PTS_BIT_POS (30) - -#define PORTSC_W1C_BITS \ - (PORTSC_CONNECT_STATUS_CHANGE | \ - PORTSC_PORT_EN_DIS_CHANGE | \ - PORTSC_OVER_CUURENT_CHG) - -/* OTG Status Control Register Bit Masks */ -#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) -#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) -#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) -#define OTGSC_CTRL_DATA_PULSING (0x1<<4) -#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) -#define OTGSC_HA_DATA_PULSE (0x1<<6) -#define OTGSC_HA_BA (0x1<<7) -#define OTGSC_STS_USB_ID (0x1<<8) -#define OTGSC_STS_A_VBUS_VALID (0x1<<9) -#define OTGSC_STS_A_SESSION_VALID (0x1<<10) -#define OTGSC_STS_B_SESSION_VALID (0x1<<11) -#define OTGSC_STS_B_SESSION_END (0x1<<12) -#define OTGSC_STS_1MS_TOGGLE (0x1<<13) -#define OTGSC_STS_DATA_PULSING (0x1<<14) -#define OTGSC_INTSTS_USB_ID (0x1<<16) -#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) -#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) -#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) -#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) -#define OTGSC_INTSTS_1MS (0x1<<21) -#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) -#define OTGSC_INTR_USB_ID_EN (0x1<<24) -#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) -#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) -#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) -#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) -#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) -#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) -#define OTGSC_INTSTS_MASK (0x00ff0000) - -/* USB MODE Register Bit Masks */ -#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) -#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) -#define USB_MODE_CTRL_MODE_HOST (0x3<<0) -#define USB_MODE_CTRL_MODE_RSV (0x1<<0) -#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) -#define USB_MODE_STREAM_DISABLE (0x1<<4) -#define USB_MODE_ES (0x1<<2) /* Endian Select */ - -/* control Register Bit Masks */ -#define USB_CTRL_IOENB (0x1<<2) -#define USB_CTRL_ULPI_INT0EN (0x1<<0) - -/* BCSR5 */ -#define BCSR5_INT_USB (0x02) - -/* USB module clk cfg */ -#define SCCR_OFFS (0xA08) -#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ -#define SCCR_USB_MPHCM_11 (0x00c00000) -#define SCCR_USB_MPHCM_01 (0x00400000) -#define SCCR_USB_MPHCM_10 (0x00800000) -#define SCCR_USB_DRCM_11 (0x00300000) -#define SCCR_USB_DRCM_01 (0x00100000) -#define SCCR_USB_DRCM_10 (0x00200000) - -#define SICRL_OFFS (0x114) -#define SICRL_USB0 (0x40000000) -#define SICRL_USB1 (0x20000000) - -#define SICRH_OFFS (0x118) -#define SICRH_USB_UTMI (0x00020000) - -/* OTG interrupt enable bit masks */ -#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ - (OTGSC_INTR_USB_ID_EN | \ - OTGSC_INTR_1MS_TIMER_EN | \ - OTGSC_INTR_A_VBUS_VALID_EN | \ - OTGSC_INTR_A_SESSION_VALID_EN | \ - OTGSC_INTR_B_SESSION_VALID_EN | \ - OTGSC_INTR_B_SESSION_END_EN | \ - OTGSC_INTR_DATA_PULSING_EN) - -/* OTG interrupt status bit masks */ -#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ - (OTGSC_INTSTS_USB_ID | \ - OTGSC_INTR_1MS_TIMER_EN | \ - OTGSC_INTSTS_A_VBUS_VALID | \ - OTGSC_INTSTS_A_SESSION_VALID | \ - OTGSC_INTSTS_B_SESSION_VALID | \ - OTGSC_INTSTS_B_SESSION_END | \ - OTGSC_INTSTS_DATA_PULSING) - -/* - * A-DEVICE timing constants - */ - -/* Wait for VBUS Rise */ -#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ - -/* Wait for B-Connect */ -#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 - * This is only used to get out of - * OTG_STATE_A_WAIT_BCON state if there was - * no connection for these many milliseconds - */ - -/* A-Idle to B-Disconnect */ -/* It is necessary for this timer to be more than 750 ms because of a bug in OPT - * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated - * in the test description - */ -#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ - -/* B-Idle to A-Disconnect */ -#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ - -/* B-device timing constants */ - - -/* Data-Line Pulse Time*/ -#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ -#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ -#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ - -/* SRP Initiate Time */ -#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ - -/* SRP Fail Time */ -#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ - -/* SRP result wait time */ -#define TB_SRP_WAIT (60) - -/* VBus time */ -#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ - -/* Discharge time */ -/* This time should be less than 10ms. It varies from system to system. */ -#define TB_VBUS_DSCHRG (8) - -/* A-SE0 to B-Reset */ -#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ - -/* A bus suspend timer before we can switch to b_wait_aconn */ -#define TB_A_SUSPEND (7) -#define TB_BUS_RESUME (12) - -/* SE0 Time Before SRP */ -#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ - -#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) - -struct usb_dr_mmap { - /* Capability register */ - u8 res1[256]; - u16 caplength; /* Capability Register Length */ - u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ - u32 hccparams; /* Host Controller Capability Parameters */ - u8 res2[20]; - u32 dciversion; /* Device Controller Interface Version */ - u32 dccparams; /* Device Controller Capability Parameters */ - u8 res3[24]; - /* Operation register */ - u32 usbcmd; /* USB Command Register */ - u32 usbsts; /* USB Status Register */ - u32 usbintr; /* USB Interrupt Enable Register */ - u32 frindex; /* Frame Index Register */ - u8 res4[4]; - u32 deviceaddr; /* Device Address */ - u32 endpointlistaddr; /* Endpoint List Address Register */ - u8 res5[4]; - u32 burstsize; /* Master Interface Data Burst Size Register */ - u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ - u8 res6[8]; - u32 ulpiview; /* ULPI register access */ - u8 res7[12]; - u32 configflag; /* Configure Flag Register */ - u32 portsc; /* Port 1 Status and Control Register */ - u8 res8[28]; - u32 otgsc; /* On-The-Go Status and Control */ - u32 usbmode; /* USB Mode Register */ - u32 endptsetupstat; /* Endpoint Setup Status Register */ - u32 endpointprime; /* Endpoint Initialization Register */ - u32 endptflush; /* Endpoint Flush Register */ - u32 endptstatus; /* Endpoint Status Register */ - u32 endptcomplete; /* Endpoint Complete Register */ - u32 endptctrl[6]; /* Endpoint Control Registers */ - u8 res9[552]; - u32 snoop1; - u32 snoop2; - u32 age_cnt_thresh; /* Age Count Threshold Register */ - u32 pri_ctrl; /* Priority Control Register */ - u32 si_ctrl; /* System Interface Control Register */ - u8 res10[236]; - u32 control; /* General Purpose Control Register */ -}; - -struct fsl_otg_timer { - unsigned long expires; /* Number of count increase to timeout */ - unsigned long count; /* Tick counter */ - void (*function)(unsigned long); /* Timeout function */ - unsigned long data; /* Data passed to function */ - struct list_head list; -}; - -inline struct fsl_otg_timer *otg_timer_initializer -(void (*function)(unsigned long), unsigned long expires, unsigned long data) -{ - struct fsl_otg_timer *timer; - - timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); - if (!timer) - return NULL; - timer->function = function; - timer->expires = expires; - timer->data = data; - return timer; -} - -struct fsl_otg { - struct usb_phy phy; - struct otg_fsm fsm; - struct usb_dr_mmap *dr_mem_map; - struct delayed_work otg_event; - - /* used for usb host */ - struct work_struct work_wq; - u8 host_working; - - int irq; -}; - -struct fsl_otg_config { - u8 otg_port; -}; - -/* For SRP and HNP handle */ -#define FSL_OTG_MAJOR 240 -#define FSL_OTG_NAME "fsl-usb2-otg" -/* Command to OTG driver ioctl */ -#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR -/* if otg work as host, it should return 1, otherwise return 0 */ -#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) -#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) -#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) -#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) -#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) -#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) -#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) -#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) -#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) - -void fsl_otg_add_timer(void *timer); -void fsl_otg_del_timer(void *timer); -void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/phy/gpio_vbus.c b/drivers/usb/phy/gpio_vbus.c deleted file mode 100644 index a7d4ac5..0000000 --- a/drivers/usb/phy/gpio_vbus.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices - * - * Copyright (c) 2008 Philipp Zabel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - - -/* - * A simple GPIO VBUS sensing driver for B peripheral only devices - * with internal transceivers. It can control a D+ pullup GPIO and - * a regulator to limit the current drawn from VBUS. - * - * Needs to be loaded before the UDC driver that will use it. - */ -struct gpio_vbus_data { - struct usb_phy phy; - struct device *dev; - struct regulator *vbus_draw; - int vbus_draw_enabled; - unsigned mA; - struct delayed_work work; - int vbus; - int irq; -}; - - -/* - * This driver relies on "both edges" triggering. VBUS has 100 msec to - * stabilize, so the peripheral controller driver may need to cope with - * some bouncing due to current surges (e.g. charging local capacitance) - * and contact chatter. - * - * REVISIT in desperate straits, toggling between rising and falling - * edges might be workable. - */ -#define VBUS_IRQ_FLAGS \ - (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) - - -/* interface to regulator framework */ -static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) -{ - struct regulator *vbus_draw = gpio_vbus->vbus_draw; - int enabled; - - if (!vbus_draw) - return; - - enabled = gpio_vbus->vbus_draw_enabled; - if (mA) { - regulator_set_current_limit(vbus_draw, 0, 1000 * mA); - if (!enabled) { - regulator_enable(vbus_draw); - gpio_vbus->vbus_draw_enabled = 1; - } - } else { - if (enabled) { - regulator_disable(vbus_draw); - gpio_vbus->vbus_draw_enabled = 0; - } - } - gpio_vbus->mA = mA; -} - -static int is_vbus_powered(struct gpio_vbus_mach_info *pdata) -{ - int vbus; - - vbus = gpio_get_value(pdata->gpio_vbus); - if (pdata->gpio_vbus_inverted) - vbus = !vbus; - - return vbus; -} - -static void gpio_vbus_work(struct work_struct *work) -{ - struct gpio_vbus_data *gpio_vbus = - container_of(work, struct gpio_vbus_data, work.work); - struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; - int gpio, status, vbus; - - if (!gpio_vbus->phy.otg->gadget) - return; - - vbus = is_vbus_powered(pdata); - if ((vbus ^ gpio_vbus->vbus) == 0) - return; - gpio_vbus->vbus = vbus; - - /* Peripheral controllers which manage the pullup themselves won't have - * gpio_pullup configured here. If it's configured here, we'll do what - * isp1301_omap::b_peripheral() does and enable the pullup here... although - * that may complicate usb_gadget_{,dis}connect() support. - */ - gpio = pdata->gpio_pullup; - - if (vbus) { - status = USB_EVENT_VBUS; - gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL; - gpio_vbus->phy.last_event = status; - usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget); - - /* drawing a "unit load" is *always* OK, except for OTG */ - set_vbus_draw(gpio_vbus, 100); - - /* optionally enable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, !pdata->gpio_pullup_inverted); - - atomic_notifier_call_chain(&gpio_vbus->phy.notifier, - status, gpio_vbus->phy.otg->gadget); - } else { - /* optionally disable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, pdata->gpio_pullup_inverted); - - set_vbus_draw(gpio_vbus, 0); - - usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget); - status = USB_EVENT_NONE; - gpio_vbus->phy.state = OTG_STATE_B_IDLE; - gpio_vbus->phy.last_event = status; - - atomic_notifier_call_chain(&gpio_vbus->phy.notifier, - status, gpio_vbus->phy.otg->gadget); - } -} - -/* VBUS change IRQ handler */ -static irqreturn_t gpio_vbus_irq(int irq, void *data) -{ - struct platform_device *pdev = data; - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct usb_otg *otg = gpio_vbus->phy.otg; - - dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", - is_vbus_powered(pdata) ? "supplied" : "inactive", - otg->gadget ? otg->gadget->name : "none"); - - if (otg->gadget) - schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100)); - - return IRQ_HANDLED; -} - -/* OTG transceiver interface */ - -/* bind/unbind the peripheral controller */ -static int gpio_vbus_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct gpio_vbus_data *gpio_vbus; - struct gpio_vbus_mach_info *pdata; - struct platform_device *pdev; - int gpio; - - gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy); - pdev = to_platform_device(gpio_vbus->dev); - pdata = gpio_vbus->dev->platform_data; - gpio = pdata->gpio_pullup; - - if (!gadget) { - dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", - otg->gadget->name); - - /* optionally disable D+ pullup */ - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, pdata->gpio_pullup_inverted); - - set_vbus_draw(gpio_vbus, 0); - - usb_gadget_vbus_disconnect(otg->gadget); - otg->phy->state = OTG_STATE_UNDEFINED; - - otg->gadget = NULL; - return 0; - } - - otg->gadget = gadget; - dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); - - /* initialize connection state */ - gpio_vbus->vbus = 0; /* start with disconnected */ - gpio_vbus_irq(gpio_vbus->irq, pdev); - return 0; -} - -/* effective for B devices, ignored for A-peripheral */ -static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA) -{ - struct gpio_vbus_data *gpio_vbus; - - gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); - - if (phy->state == OTG_STATE_B_PERIPHERAL) - set_vbus_draw(gpio_vbus, mA); - return 0; -} - -/* for non-OTG B devices: set/clear transceiver suspend mode */ -static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend) -{ - struct gpio_vbus_data *gpio_vbus; - - gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); - - /* draw max 0 mA from vbus in suspend mode; or the previously - * recorded amount of current if not suspended - * - * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA - * if they're wake-enabled ... we don't handle that yet. - */ - return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA); -} - -/* platform driver interface */ - -static int __init gpio_vbus_probe(struct platform_device *pdev) -{ - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - struct gpio_vbus_data *gpio_vbus; - struct resource *res; - int err, gpio, irq; - unsigned long irqflags; - - if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) - return -EINVAL; - gpio = pdata->gpio_vbus; - - gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); - if (!gpio_vbus) - return -ENOMEM; - - gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!gpio_vbus->phy.otg) { - kfree(gpio_vbus); - return -ENOMEM; - } - - platform_set_drvdata(pdev, gpio_vbus); - gpio_vbus->dev = &pdev->dev; - gpio_vbus->phy.label = "gpio-vbus"; - gpio_vbus->phy.set_power = gpio_vbus_set_power; - gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend; - gpio_vbus->phy.state = OTG_STATE_UNDEFINED; - - gpio_vbus->phy.otg->phy = &gpio_vbus->phy; - gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; - - err = gpio_request(gpio, "vbus_detect"); - if (err) { - dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", - gpio, err); - goto err_gpio; - } - gpio_direction_input(gpio); - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) { - irq = res->start; - irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; - } else { - irq = gpio_to_irq(gpio); - irqflags = VBUS_IRQ_FLAGS; - } - - gpio_vbus->irq = irq; - - /* if data line pullup is in use, initialize it to "not pulling up" */ - gpio = pdata->gpio_pullup; - if (gpio_is_valid(gpio)) { - err = gpio_request(gpio, "udc_pullup"); - if (err) { - dev_err(&pdev->dev, - "can't request pullup gpio %d, err: %d\n", - gpio, err); - gpio_free(pdata->gpio_vbus); - goto err_gpio; - } - gpio_direction_output(gpio, pdata->gpio_pullup_inverted); - } - - err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); - if (err) { - dev_err(&pdev->dev, "can't request irq %i, err: %d\n", - irq, err); - goto err_irq; - } - - ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); - - INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); - - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); - if (IS_ERR(gpio_vbus->vbus_draw)) { - dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", - PTR_ERR(gpio_vbus->vbus_draw)); - gpio_vbus->vbus_draw = NULL; - } - - /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_otg; - } - - device_init_wakeup(&pdev->dev, pdata->wakeup); - - return 0; -err_otg: - regulator_put(gpio_vbus->vbus_draw); - free_irq(irq, pdev); -err_irq: - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(pdata->gpio_vbus); -err_gpio: - platform_set_drvdata(pdev, NULL); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return err; -} - -static int __exit gpio_vbus_remove(struct platform_device *pdev) -{ - struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; - int gpio = pdata->gpio_vbus; - - device_init_wakeup(&pdev->dev, 0); - cancel_delayed_work_sync(&gpio_vbus->work); - regulator_put(gpio_vbus->vbus_draw); - - usb_remove_phy(&gpio_vbus->phy); - - free_irq(gpio_vbus->irq, pdev); - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(gpio); - platform_set_drvdata(pdev, NULL); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - - return 0; -} - -#ifdef CONFIG_PM -static int gpio_vbus_pm_suspend(struct device *dev) -{ - struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); - - if (device_may_wakeup(dev)) - enable_irq_wake(gpio_vbus->irq); - - return 0; -} - -static int gpio_vbus_pm_resume(struct device *dev) -{ - struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); - - if (device_may_wakeup(dev)) - disable_irq_wake(gpio_vbus->irq); - - return 0; -} - -static const struct dev_pm_ops gpio_vbus_dev_pm_ops = { - .suspend = gpio_vbus_pm_suspend, - .resume = gpio_vbus_pm_resume, -}; -#endif - -/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ - -MODULE_ALIAS("platform:gpio-vbus"); - -static struct platform_driver gpio_vbus_driver = { - .driver = { - .name = "gpio-vbus", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &gpio_vbus_dev_pm_ops, -#endif - }, - .remove = __exit_p(gpio_vbus_remove), -}; - -module_platform_driver_probe(gpio_vbus_driver, gpio_vbus_probe); - -MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); -MODULE_AUTHOR("Philipp Zabel"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/isp1301.c b/drivers/usb/phy/isp1301.c deleted file mode 100644 index 18dbf7e..0000000 --- a/drivers/usb/phy/isp1301.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * NXP ISP1301 USB transceiver driver - * - * Copyright (C) 2012 Roland Stigge - * - * Author: Roland Stigge - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include - -#define DRV_NAME "isp1301" - -static const struct i2c_device_id isp1301_id[] = { - { "isp1301", 0 }, - { } -}; - -static struct i2c_client *isp1301_i2c_client; - -static int isp1301_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) -{ - isp1301_i2c_client = client; - return 0; -} - -static int isp1301_remove(struct i2c_client *client) -{ - return 0; -} - -static struct i2c_driver isp1301_driver = { - .driver = { - .name = DRV_NAME, - }, - .probe = isp1301_probe, - .remove = isp1301_remove, - .id_table = isp1301_id, -}; - -module_i2c_driver(isp1301_driver); - -static int match(struct device *dev, void *data) -{ - struct device_node *node = (struct device_node *)data; - return (dev->of_node == node) && - (dev->driver == &isp1301_driver.driver); -} - -struct i2c_client *isp1301_get_client(struct device_node *node) -{ - if (node) { /* reference of ISP1301 I2C node via DT */ - struct device *dev = bus_find_device(&i2c_bus_type, NULL, - node, match); - if (!dev) - return NULL; - return to_i2c_client(dev); - } else { /* non-DT: only one ISP1301 chip supported */ - return isp1301_i2c_client; - } -} -EXPORT_SYMBOL_GPL(isp1301_get_client); - -MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("NXP ISP1301 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/isp1301_omap.c b/drivers/usb/phy/isp1301_omap.c deleted file mode 100644 index 8fe0c3b..0000000 --- a/drivers/usb/phy/isp1301_omap.c +++ /dev/null @@ -1,1656 +0,0 @@ -/* - * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller - * - * Copyright (C) 2004 Texas Instruments - * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#ifndef DEBUG -#undef VERBOSE -#endif - - -#define DRIVER_VERSION "24 August 2004" -#define DRIVER_NAME (isp1301_driver.driver.name) - -MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); -MODULE_LICENSE("GPL"); - -struct isp1301 { - struct usb_phy phy; - struct i2c_client *client; - void (*i2c_release)(struct device *dev); - - int irq_type; - - u32 last_otg_ctrl; - unsigned working:1; - - struct timer_list timer; - - /* use keventd context to change the state for us */ - struct work_struct work; - - unsigned long todo; -# define WORK_UPDATE_ISP 0 /* update ISP from OTG */ -# define WORK_UPDATE_OTG 1 /* update OTG from ISP */ -# define WORK_HOST_RESUME 4 /* resume host */ -# define WORK_TIMER 6 /* timer fired */ -# define WORK_STOP 7 /* don't resubmit */ -}; - - -/* bits in OTG_CTRL */ - -#define OTG_XCEIV_OUTPUTS \ - (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) -#define OTG_XCEIV_INPUTS \ - (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) -#define OTG_CTRL_BITS \ - (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP) - /* and OTG_PULLUP is sometimes written */ - -#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \ - OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \ - OTG_CTRL_BITS) - - -/*-------------------------------------------------------------------------*/ - -/* board-specific PM hooks */ - -#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) - -#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) - -#include - -#else - -static inline int tps65010_set_vbus_draw(unsigned mA) -{ - pr_debug("tps65010: draw %d mA (STUB)\n", mA); - return 0; -} - -#endif - -static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) -{ - int status = tps65010_set_vbus_draw(mA); - if (status < 0) - pr_debug(" VBUS %d mA error %d\n", mA, status); -} - -#else - -static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) -{ - /* H4 controls this by DIP switch S2.4; no soft control. - * ON means the charger is always enabled. Leave it OFF - * unless the OTG port is used only in B-peripheral mode. - */ -} - -#endif - -static void enable_vbus_source(struct isp1301 *isp) -{ - /* this board won't supply more than 8mA vbus power. - * some boards can switch a 100ma "unit load" (or more). - */ -} - - -/* products will deliver OTG messages with LEDs, GUI, etc */ -static inline void notresponding(struct isp1301 *isp) -{ - printk(KERN_NOTICE "OTG device not responding.\n"); -} - - -/*-------------------------------------------------------------------------*/ - -static struct i2c_driver isp1301_driver; - -/* smbus apis are used for portability */ - -static inline u8 -isp1301_get_u8(struct isp1301 *isp, u8 reg) -{ - return i2c_smbus_read_byte_data(isp->client, reg + 0); -} - -static inline int -isp1301_get_u16(struct isp1301 *isp, u8 reg) -{ - return i2c_smbus_read_word_data(isp->client, reg); -} - -static inline int -isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits) -{ - return i2c_smbus_write_byte_data(isp->client, reg + 0, bits); -} - -static inline int -isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) -{ - return i2c_smbus_write_byte_data(isp->client, reg + 1, bits); -} - -/*-------------------------------------------------------------------------*/ - -/* identification */ -#define ISP1301_VENDOR_ID 0x00 /* u16 read */ -#define ISP1301_PRODUCT_ID 0x02 /* u16 read */ -#define ISP1301_BCD_DEVICE 0x14 /* u16 read */ - -#define I2C_VENDOR_ID_PHILIPS 0x04cc -#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301 - -/* operational registers */ -#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */ -# define MC1_SPEED (1 << 0) -# define MC1_SUSPEND (1 << 1) -# define MC1_DAT_SE0 (1 << 2) -# define MC1_TRANSPARENT (1 << 3) -# define MC1_BDIS_ACON_EN (1 << 4) -# define MC1_OE_INT_EN (1 << 5) -# define MC1_UART_EN (1 << 6) -# define MC1_MASK 0x7f -#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */ -# define MC2_GLOBAL_PWR_DN (1 << 0) -# define MC2_SPD_SUSP_CTRL (1 << 1) -# define MC2_BI_DI (1 << 2) -# define MC2_TRANSP_BDIR0 (1 << 3) -# define MC2_TRANSP_BDIR1 (1 << 4) -# define MC2_AUDIO_EN (1 << 5) -# define MC2_PSW_EN (1 << 6) -# define MC2_EN2V7 (1 << 7) -#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */ -# define OTG1_DP_PULLUP (1 << 0) -# define OTG1_DM_PULLUP (1 << 1) -# define OTG1_DP_PULLDOWN (1 << 2) -# define OTG1_DM_PULLDOWN (1 << 3) -# define OTG1_ID_PULLDOWN (1 << 4) -# define OTG1_VBUS_DRV (1 << 5) -# define OTG1_VBUS_DISCHRG (1 << 6) -# define OTG1_VBUS_CHRG (1 << 7) -#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */ -# define OTG_B_SESS_END (1 << 6) -# define OTG_B_SESS_VLD (1 << 7) - -#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */ -#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */ - -#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */ -#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */ - -/* same bitfields in all interrupt registers */ -# define INTR_VBUS_VLD (1 << 0) -# define INTR_SESS_VLD (1 << 1) -# define INTR_DP_HI (1 << 2) -# define INTR_ID_GND (1 << 3) -# define INTR_DM_HI (1 << 4) -# define INTR_ID_FLOAT (1 << 5) -# define INTR_BDIS_ACON (1 << 6) -# define INTR_CR_INT (1 << 7) - -/*-------------------------------------------------------------------------*/ - -static inline const char *state_name(struct isp1301 *isp) -{ - return usb_otg_state_string(isp->phy.state); -} - -/*-------------------------------------------------------------------------*/ - -/* NOTE: some of this ISP1301 setup is specific to H2 boards; - * not everything is guarded by board-specific checks, or even using - * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. - * - * ALSO: this currently doesn't use ISP1301 low-power modes - * while OTG is running. - */ - -static void power_down(struct isp1301 *isp) -{ - isp->phy.state = OTG_STATE_UNDEFINED; - - // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); - - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); -} - -static void power_up(struct isp1301 *isp) -{ - // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); - - /* do this only when cpu is driving transceiver, - * so host won't see a low speed device... - */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); -} - -#define NO_HOST_SUSPEND - -static int host_suspend(struct isp1301 *isp) -{ -#ifdef NO_HOST_SUSPEND - return 0; -#else - struct device *dev; - - if (!isp->phy.otg->host) - return -ENODEV; - - /* Currently ASSUMES only the OTG port matters; - * other ports could be active... - */ - dev = isp->phy.otg->host->controller; - return dev->driver->suspend(dev, 3, 0); -#endif -} - -static int host_resume(struct isp1301 *isp) -{ -#ifdef NO_HOST_SUSPEND - return 0; -#else - struct device *dev; - - if (!isp->phy.otg->host) - return -ENODEV; - - dev = isp->phy.otg->host->controller; - return dev->driver->resume(dev, 0); -#endif -} - -static int gadget_suspend(struct isp1301 *isp) -{ - isp->phy.otg->gadget->b_hnp_enable = 0; - isp->phy.otg->gadget->a_hnp_support = 0; - isp->phy.otg->gadget->a_alt_hnp_support = 0; - return usb_gadget_vbus_disconnect(isp->phy.otg->gadget); -} - -/*-------------------------------------------------------------------------*/ - -#define TIMER_MINUTES 10 -#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ) - -/* Almost all our I2C messaging comes from a work queue's task context. - * NOTE: guaranteeing certain response times might mean we shouldn't - * share keventd's work queue; a realtime task might be safest. - */ -static void isp1301_defer_work(struct isp1301 *isp, int work) -{ - int status; - - if (isp && !test_and_set_bit(work, &isp->todo)) { - (void) get_device(&isp->client->dev); - status = schedule_work(&isp->work); - if (!status && !isp->working) - dev_vdbg(&isp->client->dev, - "work item %d may be lost\n", work); - } -} - -/* called from irq handlers */ -static void a_idle(struct isp1301 *isp, const char *tag) -{ - u32 l; - - if (isp->phy.state == OTG_STATE_A_IDLE) - return; - - isp->phy.otg->default_a = 1; - if (isp->phy.otg->host) { - isp->phy.otg->host->is_b_host = 0; - host_suspend(isp); - } - if (isp->phy.otg->gadget) { - isp->phy.otg->gadget->is_a_peripheral = 1; - gadget_suspend(isp); - } - isp->phy.state = OTG_STATE_A_IDLE; - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - isp->last_otg_ctrl = l; - pr_debug(" --> %s/%s\n", state_name(isp), tag); -} - -/* called from irq handlers */ -static void b_idle(struct isp1301 *isp, const char *tag) -{ - u32 l; - - if (isp->phy.state == OTG_STATE_B_IDLE) - return; - - isp->phy.otg->default_a = 0; - if (isp->phy.otg->host) { - isp->phy.otg->host->is_b_host = 1; - host_suspend(isp); - } - if (isp->phy.otg->gadget) { - isp->phy.otg->gadget->is_a_peripheral = 0; - gadget_suspend(isp); - } - isp->phy.state = OTG_STATE_B_IDLE; - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - isp->last_otg_ctrl = l; - pr_debug(" --> %s/%s\n", state_name(isp), tag); -} - -static void -dump_regs(struct isp1301 *isp, const char *label) -{ -#ifdef DEBUG - u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); - u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); - - pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", - omap_readl(OTG_CTRL), label, state_name(isp), - ctrl, status, src); - /* mode control and irq enables don't change much */ -#endif -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_OTG - -/* - * The OMAP OTG controller handles most of the OTG state transitions. - * - * We translate isp1301 outputs (mostly voltage comparator status) into - * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state - * flags into isp1301 inputs ... and infer state transitions. - */ - -#ifdef VERBOSE - -static void check_state(struct isp1301 *isp, const char *tag) -{ - enum usb_otg_state state = OTG_STATE_UNDEFINED; - u8 fsm = omap_readw(OTG_TEST) & 0x0ff; - unsigned extra = 0; - - switch (fsm) { - - /* default-b */ - case 0x0: - state = OTG_STATE_B_IDLE; - break; - case 0x3: - case 0x7: - extra = 1; - case 0x1: - state = OTG_STATE_B_PERIPHERAL; - break; - case 0x11: - state = OTG_STATE_B_SRP_INIT; - break; - - /* extra dual-role default-b states */ - case 0x12: - case 0x13: - case 0x16: - extra = 1; - case 0x17: - state = OTG_STATE_B_WAIT_ACON; - break; - case 0x34: - state = OTG_STATE_B_HOST; - break; - - /* default-a */ - case 0x36: - state = OTG_STATE_A_IDLE; - break; - case 0x3c: - state = OTG_STATE_A_WAIT_VFALL; - break; - case 0x7d: - state = OTG_STATE_A_VBUS_ERR; - break; - case 0x9e: - case 0x9f: - extra = 1; - case 0x89: - state = OTG_STATE_A_PERIPHERAL; - break; - case 0xb7: - state = OTG_STATE_A_WAIT_VRISE; - break; - case 0xb8: - state = OTG_STATE_A_WAIT_BCON; - break; - case 0xb9: - state = OTG_STATE_A_HOST; - break; - case 0xba: - state = OTG_STATE_A_SUSPEND; - break; - default: - break; - } - if (isp->phy.state == state && !extra) - return; - pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - usb_otg_state_string(state), fsm, state_name(isp), - omap_readl(OTG_CTRL)); -} - -#else - -static inline void check_state(struct isp1301 *isp, const char *tag) { } - -#endif - -/* outputs from ISP1301_INTERRUPT_SOURCE */ -static void update_otg1(struct isp1301 *isp, u8 int_src) -{ - u32 otg_ctrl; - - otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - otg_ctrl &= ~OTG_XCEIV_INPUTS; - otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); - - if (int_src & INTR_SESS_VLD) - otg_ctrl |= OTG_ASESSVLD; - else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) { - a_idle(isp, "vfall"); - otg_ctrl &= ~OTG_CTRL_BITS; - } - if (int_src & INTR_VBUS_VLD) - otg_ctrl |= OTG_VBUSVLD; - if (int_src & INTR_ID_GND) { /* default-A */ - if (isp->phy.state == OTG_STATE_B_IDLE - || isp->phy.state - == OTG_STATE_UNDEFINED) { - a_idle(isp, "init"); - return; - } - } else { /* default-B */ - otg_ctrl |= OTG_ID; - if (isp->phy.state == OTG_STATE_A_IDLE - || isp->phy.state == OTG_STATE_UNDEFINED) { - b_idle(isp, "init"); - return; - } - } - omap_writel(otg_ctrl, OTG_CTRL); -} - -/* outputs from ISP1301_OTG_STATUS */ -static void update_otg2(struct isp1301 *isp, u8 otg_status) -{ - u32 otg_ctrl; - - otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - otg_ctrl &= ~OTG_XCEIV_INPUTS; - otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND); - if (otg_status & OTG_B_SESS_VLD) - otg_ctrl |= OTG_BSESSVLD; - else if (otg_status & OTG_B_SESS_END) - otg_ctrl |= OTG_BSESSEND; - omap_writel(otg_ctrl, OTG_CTRL); -} - -/* inputs going to ISP1301 */ -static void otg_update_isp(struct isp1301 *isp) -{ - u32 otg_ctrl, otg_change; - u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; - - otg_ctrl = omap_readl(OTG_CTRL); - otg_change = otg_ctrl ^ isp->last_otg_ctrl; - isp->last_otg_ctrl = otg_ctrl; - otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; - - switch (isp->phy.state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_SRP_INIT: - if (!(otg_ctrl & OTG_PULLUP)) { - // if (otg_ctrl & OTG_B_HNPEN) { - if (isp->phy.otg->gadget->b_hnp_enable) { - isp->phy.state = OTG_STATE_B_WAIT_ACON; - pr_debug(" --> b_wait_acon\n"); - } - goto pulldown; - } -pullup: - set |= OTG1_DP_PULLUP; - clr |= OTG1_DP_PULLDOWN; - break; - case OTG_STATE_A_SUSPEND: - case OTG_STATE_A_PERIPHERAL: - if (otg_ctrl & OTG_PULLUP) - goto pullup; - /* FALLTHROUGH */ - // case OTG_STATE_B_WAIT_ACON: - default: -pulldown: - set |= OTG1_DP_PULLDOWN; - clr |= OTG1_DP_PULLUP; - break; - } - -# define toggle(OTG,ISP) do { \ - if (otg_ctrl & OTG) set |= ISP; \ - else clr |= ISP; \ - } while (0) - - if (!(isp->phy.otg->host)) - otg_ctrl &= ~OTG_DRV_VBUS; - - switch (isp->phy.state) { - case OTG_STATE_A_SUSPEND: - if (otg_ctrl & OTG_DRV_VBUS) { - set |= OTG1_VBUS_DRV; - break; - } - /* HNP failed for some reason (A_AIDL_BDIS timeout) */ - notresponding(isp); - - /* FALLTHROUGH */ - case OTG_STATE_A_VBUS_ERR: - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - pr_debug(" --> a_wait_vfall\n"); - /* FALLTHROUGH */ - case OTG_STATE_A_WAIT_VFALL: - /* FIXME usbcore thinks port power is still on ... */ - clr |= OTG1_VBUS_DRV; - break; - case OTG_STATE_A_IDLE: - if (otg_ctrl & OTG_DRV_VBUS) { - isp->phy.state = OTG_STATE_A_WAIT_VRISE; - pr_debug(" --> a_wait_vrise\n"); - } - /* FALLTHROUGH */ - default: - toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); - } - - toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); - toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); - -# undef toggle - - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); - - /* HNP switch to host or peripheral; and SRP */ - if (otg_change & OTG_PULLUP) { - u32 l; - - switch (isp->phy.state) { - case OTG_STATE_B_IDLE: - if (clr & OTG1_DP_PULLUP) - break; - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - break; - case OTG_STATE_A_SUSPEND: - if (clr & OTG1_DP_PULLUP) - break; - isp->phy.state = OTG_STATE_A_PERIPHERAL; - pr_debug(" --> a_peripheral\n"); - break; - default: - break; - } - l = omap_readl(OTG_CTRL); - l |= OTG_PULLUP; - omap_writel(l, OTG_CTRL); - } - - check_state(isp, __func__); - dump_regs(isp, "otg->isp1301"); -} - -static irqreturn_t omap_otg_irq(int irq, void *_isp) -{ - u16 otg_irq = omap_readw(OTG_IRQ_SRC); - u32 otg_ctrl; - int ret = IRQ_NONE; - struct isp1301 *isp = _isp; - struct usb_otg *otg = isp->phy.otg; - - /* update ISP1301 transceiver from OTG controller */ - if (otg_irq & OPRT_CHG) { - omap_writew(OPRT_CHG, OTG_IRQ_SRC); - isp1301_defer_work(isp, WORK_UPDATE_ISP); - ret = IRQ_HANDLED; - - /* SRP to become b_peripheral failed */ - } else if (otg_irq & B_SRP_TMROUT) { - pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL)); - notresponding(isp); - - /* gadget drivers that care should monitor all kinds of - * remote wakeup (SRP, normal) using their own timer - * to give "check cable and A-device" messages. - */ - if (isp->phy.state == OTG_STATE_B_SRP_INIT) - b_idle(isp, "srp_timeout"); - - omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* HNP to become b_host failed */ - } else if (otg_irq & B_HNP_FAIL) { - pr_debug("otg: %s B_HNP_FAIL, %06x\n", - state_name(isp), omap_readl(OTG_CTRL)); - notresponding(isp); - - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - - /* subset of b_peripheral()... */ - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - - omap_writew(B_HNP_FAIL, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* detect SRP from B-device ... */ - } else if (otg_irq & A_SRP_DETECT) { - pr_debug("otg: %s SRP_DETECT, %06x\n", - state_name(isp), omap_readl(OTG_CTRL)); - - isp1301_defer_work(isp, WORK_UPDATE_OTG); - switch (isp->phy.state) { - case OTG_STATE_A_IDLE: - if (!otg->host) - break; - isp1301_defer_work(isp, WORK_HOST_RESUME); - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_A_BUSREQ; - otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) - & ~OTG_XCEIV_INPUTS - & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - break; - default: - break; - } - - omap_writew(A_SRP_DETECT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) - * we don't track them separately - */ - } else if (otg_irq & A_REQ_TMROUT) { - otg_ctrl = omap_readl(OTG_CTRL); - pr_info("otg: BCON_TMOUT from %s, %06x\n", - state_name(isp), otg_ctrl); - notresponding(isp); - - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - - omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* A-supplied voltage fell too low; overcurrent */ - } else if (otg_irq & A_VBUS_ERR) { - otg_ctrl = omap_readl(OTG_CTRL); - printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", - state_name(isp), otg_irq, otg_ctrl); - - otg_ctrl |= OTG_BUSDROP; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_A_VBUS_ERR; - - omap_writew(A_VBUS_ERR, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - /* switch driver; the transceiver code activates it, - * ungating the udc clock or resuming OHCI. - */ - } else if (otg_irq & DRIVER_SWITCH) { - int kick = 0; - - otg_ctrl = omap_readl(OTG_CTRL); - printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", - state_name(isp), - (otg_ctrl & OTG_DRIVER_SEL) - ? "gadget" : "host", - otg_ctrl); - isp1301_defer_work(isp, WORK_UPDATE_ISP); - - /* role is peripheral */ - if (otg_ctrl & OTG_DRIVER_SEL) { - switch (isp->phy.state) { - case OTG_STATE_A_IDLE: - b_idle(isp, __func__); - break; - default: - break; - } - isp1301_defer_work(isp, WORK_UPDATE_ISP); - - /* role is host */ - } else { - if (!(otg_ctrl & OTG_ID)) { - otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; - omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL); - } - - if (otg->host) { - switch (isp->phy.state) { - case OTG_STATE_B_WAIT_ACON: - isp->phy.state = OTG_STATE_B_HOST; - pr_debug(" --> b_host\n"); - kick = 1; - break; - case OTG_STATE_A_WAIT_BCON: - isp->phy.state = OTG_STATE_A_HOST; - pr_debug(" --> a_host\n"); - break; - case OTG_STATE_A_PERIPHERAL: - isp->phy.state = OTG_STATE_A_WAIT_BCON; - pr_debug(" --> a_wait_bcon\n"); - break; - default: - break; - } - isp1301_defer_work(isp, WORK_HOST_RESUME); - } - } - - omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC); - ret = IRQ_HANDLED; - - if (kick) - usb_bus_start_enum(otg->host, otg->host->otg_port); - } - - check_state(isp, __func__); - return ret; -} - -static struct platform_device *otg_dev; - -static int isp1301_otg_init(struct isp1301 *isp) -{ - u32 l; - - if (!otg_dev) - return -ENODEV; - - dump_regs(isp, __func__); - /* some of these values are board-specific... */ - l = omap_readl(OTG_SYSCON_2); - l |= OTG_EN - /* for B-device: */ - | SRP_GPDATA /* 9msec Bdev D+ pulse */ - | SRP_GPDVBUS /* discharge after VBUS pulse */ - // | (3 << 24) /* 2msec VBUS pulse */ - /* for A-device: */ - | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ - | SRP_DPW /* detect 167+ns SRP pulses */ - | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ - ; - omap_writel(l, OTG_SYSCON_2); - - update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); - update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); - - check_state(isp, __func__); - pr_debug("otg: %s, %s %06x\n", - state_name(isp), __func__, omap_readl(OTG_CTRL)); - - omap_writew(DRIVER_SWITCH | OPRT_CHG - | B_SRP_TMROUT | B_HNP_FAIL - | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN); - - l = omap_readl(OTG_SYSCON_2); - l |= OTG_EN; - omap_writel(l, OTG_SYSCON_2); - - return 0; -} - -static int otg_probe(struct platform_device *dev) -{ - // struct omap_usb_config *config = dev->platform_data; - - otg_dev = dev; - return 0; -} - -static int otg_remove(struct platform_device *dev) -{ - otg_dev = NULL; - return 0; -} - -static struct platform_driver omap_otg_driver = { - .probe = otg_probe, - .remove = otg_remove, - .driver = { - .owner = THIS_MODULE, - .name = "omap_otg", - }, -}; - -static int otg_bind(struct isp1301 *isp) -{ - int status; - - if (otg_dev) - return -EBUSY; - - status = platform_driver_register(&omap_otg_driver); - if (status < 0) - return status; - - if (otg_dev) - status = request_irq(otg_dev->resource[1].start, omap_otg_irq, - 0, DRIVER_NAME, isp); - else - status = -ENODEV; - - if (status < 0) - platform_driver_unregister(&omap_otg_driver); - return status; -} - -static void otg_unbind(struct isp1301 *isp) -{ - if (!otg_dev) - return; - free_irq(otg_dev->resource[1].start, isp); -} - -#else - -/* OTG controller isn't clocked */ - -#endif /* CONFIG_USB_OTG */ - -/*-------------------------------------------------------------------------*/ - -static void b_peripheral(struct isp1301 *isp) -{ - u32 l; - - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - - usb_gadget_vbus_connect(isp->phy.otg->gadget); - -#ifdef CONFIG_USB_OTG - enable_vbus_draw(isp, 8); - otg_update_isp(isp); -#else - enable_vbus_draw(isp, 100); - /* UDC driver just set OTG_BSESSVLD */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); - isp->phy.state = OTG_STATE_B_PERIPHERAL; - pr_debug(" --> b_peripheral\n"); - dump_regs(isp, "2periph"); -#endif -} - -static void isp_update_otg(struct isp1301 *isp, u8 stat) -{ - struct usb_otg *otg = isp->phy.otg; - u8 isp_stat, isp_bstat; - enum usb_otg_state state = isp->phy.state; - - if (stat & INTR_BDIS_ACON) - pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); - - /* start certain state transitions right away */ - isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); - if (isp_stat & INTR_ID_GND) { - if (otg->default_a) { - switch (state) { - case OTG_STATE_B_IDLE: - a_idle(isp, "idle"); - /* FALLTHROUGH */ - case OTG_STATE_A_IDLE: - enable_vbus_source(isp); - /* FALLTHROUGH */ - case OTG_STATE_A_WAIT_VRISE: - /* we skip over OTG_STATE_A_WAIT_BCON, since - * the HC will transition to A_HOST (or - * A_SUSPEND!) without our noticing except - * when HNP is used. - */ - if (isp_stat & INTR_VBUS_VLD) - isp->phy.state = OTG_STATE_A_HOST; - break; - case OTG_STATE_A_WAIT_VFALL: - if (!(isp_stat & INTR_SESS_VLD)) - a_idle(isp, "vfell"); - break; - default: - if (!(isp_stat & INTR_VBUS_VLD)) - isp->phy.state = OTG_STATE_A_VBUS_ERR; - break; - } - isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - } else { - switch (state) { - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_HOST: - case OTG_STATE_B_WAIT_ACON: - usb_gadget_vbus_disconnect(otg->gadget); - break; - default: - break; - } - if (state != OTG_STATE_A_IDLE) - a_idle(isp, "id"); - if (otg->host && state == OTG_STATE_A_IDLE) - isp1301_defer_work(isp, WORK_HOST_RESUME); - isp_bstat = 0; - } - } else { - u32 l; - - /* if user unplugged mini-A end of cable, - * don't bypass A_WAIT_VFALL. - */ - if (otg->default_a) { - switch (state) { - default: - isp->phy.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_A_WAIT_VFALL: - state = OTG_STATE_A_IDLE; - /* khubd may take a while to notice and - * handle this disconnect, so don't go - * to B_IDLE quite yet. - */ - break; - case OTG_STATE_A_IDLE: - host_suspend(isp); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_BDIS_ACON_EN); - isp->phy.state = OTG_STATE_B_IDLE; - l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - l &= ~OTG_CTRL_BITS; - omap_writel(l, OTG_CTRL); - break; - case OTG_STATE_B_IDLE: - break; - } - } - isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); - - switch (isp->phy.state) { - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_WAIT_ACON: - case OTG_STATE_B_HOST: - if (likely(isp_bstat & OTG_B_SESS_VLD)) - break; - enable_vbus_draw(isp, 0); -#ifndef CONFIG_USB_OTG - /* UDC driver will clear OTG_BSESSVLD */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DP_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DP_PULLUP); - dump_regs(isp, __func__); -#endif - /* FALLTHROUGH */ - case OTG_STATE_B_SRP_INIT: - b_idle(isp, __func__); - l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; - omap_writel(l, OTG_CTRL); - /* FALLTHROUGH */ - case OTG_STATE_B_IDLE: - if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) { -#ifdef CONFIG_USB_OTG - update_otg1(isp, isp_stat); - update_otg2(isp, isp_bstat); -#endif - b_peripheral(isp); - } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) - isp_bstat |= OTG_B_SESS_END; - break; - case OTG_STATE_A_WAIT_VFALL: - break; - default: - pr_debug("otg: unsupported b-device %s\n", - state_name(isp)); - break; - } - } - - if (state != isp->phy.state) - pr_debug(" isp, %s -> %s\n", - usb_otg_state_string(state), state_name(isp)); - -#ifdef CONFIG_USB_OTG - /* update the OTG controller state to match the isp1301; may - * trigger OPRT_CHG irqs for changes going to the isp1301. - */ - update_otg1(isp, isp_stat); - update_otg2(isp, isp_bstat); - check_state(isp, __func__); -#endif - - dump_regs(isp, "isp1301->otg"); -} - -/*-------------------------------------------------------------------------*/ - -static u8 isp1301_clear_latch(struct isp1301 *isp) -{ - u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); - return latch; -} - -static void -isp1301_work(struct work_struct *work) -{ - struct isp1301 *isp = container_of(work, struct isp1301, work); - int stop; - - /* implicit lock: we're the only task using this device */ - isp->working = 1; - do { - stop = test_bit(WORK_STOP, &isp->todo); - -#ifdef CONFIG_USB_OTG - /* transfer state from otg engine to isp1301 */ - if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { - otg_update_isp(isp); - put_device(&isp->client->dev); - } -#endif - /* transfer state from isp1301 to otg engine */ - if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { - u8 stat = isp1301_clear_latch(isp); - - isp_update_otg(isp, stat); - put_device(&isp->client->dev); - } - - if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { - u32 otg_ctrl; - - /* - * skip A_WAIT_VRISE; hc transitions invisibly - * skip A_WAIT_BCON; same. - */ - switch (isp->phy.state) { - case OTG_STATE_A_WAIT_BCON: - case OTG_STATE_A_WAIT_VRISE: - isp->phy.state = OTG_STATE_A_HOST; - pr_debug(" --> a_host\n"); - otg_ctrl = omap_readl(OTG_CTRL); - otg_ctrl |= OTG_A_BUSREQ; - otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) - & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - break; - case OTG_STATE_B_WAIT_ACON: - isp->phy.state = OTG_STATE_B_HOST; - pr_debug(" --> b_host (acon)\n"); - break; - case OTG_STATE_B_HOST: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_IDLE: - break; - default: - pr_debug(" host resume in %s\n", - state_name(isp)); - } - host_resume(isp); - // mdelay(10); - put_device(&isp->client->dev); - } - - if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { -#ifdef VERBOSE - dump_regs(isp, "timer"); - if (!stop) - mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); -#endif - put_device(&isp->client->dev); - } - - if (isp->todo) - dev_vdbg(&isp->client->dev, - "work done, todo = 0x%lx\n", - isp->todo); - if (stop) { - dev_dbg(&isp->client->dev, "stop\n"); - break; - } - } while (isp->todo); - isp->working = 0; -} - -static irqreturn_t isp1301_irq(int irq, void *isp) -{ - isp1301_defer_work(isp, WORK_UPDATE_OTG); - return IRQ_HANDLED; -} - -static void isp1301_timer(unsigned long _isp) -{ - isp1301_defer_work((void *)_isp, WORK_TIMER); -} - -/*-------------------------------------------------------------------------*/ - -static void isp1301_release(struct device *dev) -{ - struct isp1301 *isp; - - isp = dev_get_drvdata(dev); - - /* FIXME -- not with a "new style" driver, it doesn't!! */ - - /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ - if (isp->i2c_release) - isp->i2c_release(dev); - kfree(isp->phy.otg); - kfree (isp); -} - -static struct isp1301 *the_transceiver; - -static int __exit isp1301_remove(struct i2c_client *i2c) -{ - struct isp1301 *isp; - - isp = i2c_get_clientdata(i2c); - - isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); - free_irq(i2c->irq, isp); -#ifdef CONFIG_USB_OTG - otg_unbind(isp); -#endif - if (machine_is_omap_h2()) - gpio_free(2); - - isp->timer.data = 0; - set_bit(WORK_STOP, &isp->todo); - del_timer_sync(&isp->timer); - flush_work(&isp->work); - - put_device(&i2c->dev); - the_transceiver = NULL; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* NOTE: three modes are possible here, only one of which - * will be standards-conformant on any given system: - * - * - OTG mode (dual-role), required if there's a Mini-AB connector - * - HOST mode, for when there's one or more A (host) connectors - * - DEVICE mode, for when there's a B/Mini-B (device) connector - * - * As a rule, you won't have an isp1301 chip unless it's there to - * support the OTG mode. Other modes help testing USB controllers - * in isolation from (full) OTG support, or maybe so later board - * revisions can help to support those feature. - */ - -#ifdef CONFIG_USB_OTG - -static int isp1301_otg_enable(struct isp1301 *isp) -{ - power_up(isp); - isp1301_otg_init(isp); - - /* NOTE: since we don't change this, this provides - * a few more interrupts than are strictly needed. - */ - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); - - dev_info(&isp->client->dev, "ready for dual-role USB ...\n"); - - return 0; -} - -#endif - -/* add or disable the host device+driver */ -static int -isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - - if (!otg || isp != the_transceiver) - return -ENODEV; - - if (!host) { - omap_writew(0, OTG_IRQ_EN); - power_down(isp); - otg->host = NULL; - return 0; - } - -#ifdef CONFIG_USB_OTG - otg->host = host; - dev_dbg(&isp->client->dev, "registered host\n"); - host_suspend(isp); - if (otg->gadget) - return isp1301_otg_enable(isp); - return 0; - -#elif !defined(CONFIG_USB_GADGET_OMAP) - // FIXME update its refcount - otg->host = host; - - power_up(isp); - - if (machine_is_omap_h2()) - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); - - dev_info(&isp->client->dev, "A-Host sessions ok\n"); - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_ID_GND); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_ID_GND); - - /* If this has a Mini-AB connector, this mode is highly - * nonstandard ... but can be handy for testing, especially with - * the Mini-A end of an OTG cable. (Or something nonstandard - * like MiniB-to-StandardB, maybe built with a gender mender.) - */ - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); - - dump_regs(isp, __func__); - - return 0; - -#else - dev_dbg(&isp->client->dev, "host sessions not allowed\n"); - return -EINVAL; -#endif - -} - -static int -isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - - if (!otg || isp != the_transceiver) - return -ENODEV; - - if (!gadget) { - omap_writew(0, OTG_IRQ_EN); - if (!otg->default_a) - enable_vbus_draw(isp, 0); - usb_gadget_vbus_disconnect(otg->gadget); - otg->gadget = NULL; - power_down(isp); - return 0; - } - -#ifdef CONFIG_USB_OTG - otg->gadget = gadget; - dev_dbg(&isp->client->dev, "registered gadget\n"); - /* gadget driver may be suspended until vbus_connect () */ - if (otg->host) - return isp1301_otg_enable(isp); - return 0; - -#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) - otg->gadget = gadget; - // FIXME update its refcount - - { - u32 l; - - l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; - l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); - l |= OTG_ID; - omap_writel(l, OTG_CTRL); - } - - power_up(isp); - isp->phy.state = OTG_STATE_B_IDLE; - - if (machine_is_omap_h2() || machine_is_omap_h3()) - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); - - isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_SESS_VLD); - isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_VBUS_VLD); - dev_info(&isp->client->dev, "B-Peripheral sessions ok\n"); - dump_regs(isp, __func__); - - /* If this has a Mini-AB connector, this mode is highly - * nonstandard ... but can be handy for testing, so long - * as you don't plug a Mini-A cable into the jack. - */ - if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) - b_peripheral(isp); - - return 0; - -#else - dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n"); - return -EINVAL; -#endif -} - - -/*-------------------------------------------------------------------------*/ - -static int -isp1301_set_power(struct usb_phy *dev, unsigned mA) -{ - if (!the_transceiver) - return -ENODEV; - if (dev->state == OTG_STATE_B_PERIPHERAL) - enable_vbus_draw(the_transceiver, mA); - return 0; -} - -static int -isp1301_start_srp(struct usb_otg *otg) -{ - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - u32 otg_ctrl; - - if (!otg || isp != the_transceiver - || isp->phy.state != OTG_STATE_B_IDLE) - return -ENODEV; - - otg_ctrl = omap_readl(OTG_CTRL); - if (!(otg_ctrl & OTG_BSESSEND)) - return -EINVAL; - - otg_ctrl |= OTG_B_BUSREQ; - otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; - omap_writel(otg_ctrl, OTG_CTRL); - isp->phy.state = OTG_STATE_B_SRP_INIT; - - pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), - omap_readl(OTG_CTRL)); -#ifdef CONFIG_USB_OTG - check_state(isp, __func__); -#endif - return 0; -} - -static int -isp1301_start_hnp(struct usb_otg *otg) -{ -#ifdef CONFIG_USB_OTG - struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - u32 l; - - if (!otg || isp != the_transceiver) - return -ENODEV; - if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) - return -ENOTCONN; - if (!otg->default_a && (otg->gadget == NULL - || !otg->gadget->b_hnp_enable)) - return -ENOTCONN; - - /* We want hardware to manage most HNP protocol timings. - * So do this part as early as possible... - */ - switch (isp->phy.state) { - case OTG_STATE_B_HOST: - isp->phy.state = OTG_STATE_B_PERIPHERAL; - /* caller will suspend next */ - break; - case OTG_STATE_A_HOST: -#if 0 - /* autoconnect mode avoids irq latency bugs */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_BDIS_ACON_EN); -#endif - /* caller must suspend then clear A_BUSREQ */ - usb_gadget_vbus_connect(otg->gadget); - l = omap_readl(OTG_CTRL); - l |= OTG_A_SETB_HNPEN; - omap_writel(l, OTG_CTRL); - - break; - case OTG_STATE_A_PERIPHERAL: - /* initiated by B-Host suspend */ - break; - default: - return -EILSEQ; - } - pr_debug("otg: HNP %s, %06x ...\n", - state_name(isp), omap_readl(OTG_CTRL)); - check_state(isp, __func__); - return 0; -#else - /* srp-only */ - return -EINVAL; -#endif -} - -/*-------------------------------------------------------------------------*/ - -static int -isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) -{ - int status; - struct isp1301 *isp; - - if (the_transceiver) - return 0; - - isp = kzalloc(sizeof *isp, GFP_KERNEL); - if (!isp) - return 0; - - isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL); - if (!isp->phy.otg) { - kfree(isp); - return 0; - } - - INIT_WORK(&isp->work, isp1301_work); - init_timer(&isp->timer); - isp->timer.function = isp1301_timer; - isp->timer.data = (unsigned long) isp; - - i2c_set_clientdata(i2c, isp); - isp->client = i2c; - - /* verify the chip (shouldn't be necessary) */ - status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); - if (status != I2C_VENDOR_ID_PHILIPS) { - dev_dbg(&i2c->dev, "not philips id: %d\n", status); - goto fail; - } - status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); - if (status != I2C_PRODUCT_ID_PHILIPS_1301) { - dev_dbg(&i2c->dev, "not isp1301, %d\n", status); - goto fail; - } - isp->i2c_release = i2c->dev.release; - i2c->dev.release = isp1301_release; - - /* initial development used chiprev 2.00 */ - status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); - dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", - status >> 8, status & 0xff); - - /* make like power-on reset */ - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); - - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); - isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); - - isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, - OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); - isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, - ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); - - isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); - isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); - -#ifdef CONFIG_USB_OTG - status = otg_bind(isp); - if (status < 0) { - dev_dbg(&i2c->dev, "can't bind OTG\n"); - goto fail; - } -#endif - - if (machine_is_omap_h2()) { - /* full speed signaling by default */ - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, - MC1_SPEED); - isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, - MC2_SPD_SUSP_CTRL); - - /* IRQ wired at M14 */ - omap_cfg_reg(M14_1510_GPIO2); - if (gpio_request(2, "isp1301") == 0) - gpio_direction_input(2); - isp->irq_type = IRQF_TRIGGER_FALLING; - } - - status = request_irq(i2c->irq, isp1301_irq, - isp->irq_type, DRIVER_NAME, isp); - if (status < 0) { - dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", - i2c->irq, status); - goto fail; - } - - isp->phy.dev = &i2c->dev; - isp->phy.label = DRIVER_NAME; - isp->phy.set_power = isp1301_set_power, - - isp->phy.otg->phy = &isp->phy; - isp->phy.otg->set_host = isp1301_set_host, - isp->phy.otg->set_peripheral = isp1301_set_peripheral, - isp->phy.otg->start_srp = isp1301_start_srp, - isp->phy.otg->start_hnp = isp1301_start_hnp, - - enable_vbus_draw(isp, 0); - power_down(isp); - the_transceiver = isp; - -#ifdef CONFIG_USB_OTG - update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); - update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); -#endif - - dump_regs(isp, __func__); - -#ifdef VERBOSE - mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); - dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); -#endif - - status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); - if (status < 0) - dev_err(&i2c->dev, "can't register transceiver, %d\n", - status); - - return 0; - -fail: - kfree(isp->phy.otg); - kfree(isp); - return -ENODEV; -} - -static const struct i2c_device_id isp1301_id[] = { - { "isp1301_omap", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, isp1301_id); - -static struct i2c_driver isp1301_driver = { - .driver = { - .name = "isp1301_omap", - }, - .probe = isp1301_probe, - .remove = __exit_p(isp1301_remove), - .id_table = isp1301_id, -}; - -/*-------------------------------------------------------------------------*/ - -static int __init isp_init(void) -{ - return i2c_add_driver(&isp1301_driver); -} -subsys_initcall(isp_init); - -static void __exit isp_exit(void) -{ - if (the_transceiver) - usb_remove_phy(&the_transceiver->phy); - i2c_del_driver(&isp1301_driver); -} -module_exit(isp_exit); - diff --git a/drivers/usb/phy/msm_otg.c b/drivers/usb/phy/msm_otg.c deleted file mode 100644 index 749fbf4..0000000 --- a/drivers/usb/phy/msm_otg.c +++ /dev/null @@ -1,1762 +0,0 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MSM_USB_BASE (motg->regs) -#define DRIVER_NAME "msm_otg" - -#define ULPI_IO_TIMEOUT_USEC (10 * 1000) - -#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ -#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ -#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ -#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ -#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ -#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ -#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ -#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ - -static struct regulator *hsusb_3p3; -static struct regulator *hsusb_1p8; -static struct regulator *hsusb_vddcx; - -static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) -{ - int ret = 0; - - if (init) { - hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); - if (IS_ERR(hsusb_vddcx)) { - dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); - return PTR_ERR(hsusb_vddcx); - } - - ret = regulator_set_voltage(hsusb_vddcx, - USB_PHY_VDD_DIG_VOL_MIN, - USB_PHY_VDD_DIG_VOL_MAX); - if (ret) { - dev_err(motg->phy.dev, "unable to set the voltage " - "for hsusb vddcx\n"); - regulator_put(hsusb_vddcx); - return ret; - } - - ret = regulator_enable(hsusb_vddcx); - if (ret) { - dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); - regulator_put(hsusb_vddcx); - } - } else { - ret = regulator_set_voltage(hsusb_vddcx, 0, - USB_PHY_VDD_DIG_VOL_MAX); - if (ret) - dev_err(motg->phy.dev, "unable to set the voltage " - "for hsusb vddcx\n"); - ret = regulator_disable(hsusb_vddcx); - if (ret) - dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); - - regulator_put(hsusb_vddcx); - } - - return ret; -} - -static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) -{ - int rc = 0; - - if (init) { - hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); - if (IS_ERR(hsusb_3p3)) { - dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); - return PTR_ERR(hsusb_3p3); - } - - rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, - USB_PHY_3P3_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "unable to set voltage level " - "for hsusb 3p3\n"); - goto put_3p3; - } - rc = regulator_enable(hsusb_3p3); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); - goto put_3p3; - } - hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); - if (IS_ERR(hsusb_1p8)) { - dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); - rc = PTR_ERR(hsusb_1p8); - goto disable_3p3; - } - rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, - USB_PHY_1P8_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "unable to set voltage level " - "for hsusb 1p8\n"); - goto put_1p8; - } - rc = regulator_enable(hsusb_1p8); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); - goto put_1p8; - } - - return 0; - } - - regulator_disable(hsusb_1p8); -put_1p8: - regulator_put(hsusb_1p8); -disable_3p3: - regulator_disable(hsusb_3p3); -put_3p3: - regulator_put(hsusb_3p3); - return rc; -} - -#ifdef CONFIG_PM_SLEEP -#define USB_PHY_SUSP_DIG_VOL 500000 -static int msm_hsusb_config_vddcx(int high) -{ - int max_vol = USB_PHY_VDD_DIG_VOL_MAX; - int min_vol; - int ret; - - if (high) - min_vol = USB_PHY_VDD_DIG_VOL_MIN; - else - min_vol = USB_PHY_SUSP_DIG_VOL; - - ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); - if (ret) { - pr_err("%s: unable to set the voltage for regulator " - "HSUSB_VDDCX\n", __func__); - return ret; - } - - pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); - - return ret; -} -#endif - -static int msm_hsusb_ldo_set_mode(int on) -{ - int ret = 0; - - if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { - pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); - return -ENODEV; - } - - if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { - pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); - return -ENODEV; - } - - if (on) { - ret = regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_HPM_LOAD); - if (ret < 0) { - pr_err("%s: Unable to set HPM of the regulator " - "HSUSB_1p8\n", __func__); - return ret; - } - ret = regulator_set_optimum_mode(hsusb_3p3, - USB_PHY_3P3_HPM_LOAD); - if (ret < 0) { - pr_err("%s: Unable to set HPM of the regulator " - "HSUSB_3p3\n", __func__); - regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_LPM_LOAD); - return ret; - } - } else { - ret = regulator_set_optimum_mode(hsusb_1p8, - USB_PHY_1P8_LPM_LOAD); - if (ret < 0) - pr_err("%s: Unable to set LPM of the regulator " - "HSUSB_1p8\n", __func__); - ret = regulator_set_optimum_mode(hsusb_3p3, - USB_PHY_3P3_LPM_LOAD); - if (ret < 0) - pr_err("%s: Unable to set LPM of the regulator " - "HSUSB_3p3\n", __func__); - } - - pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); - return ret < 0 ? ret : 0; -} - -static int ulpi_read(struct usb_phy *phy, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate read operation */ - writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_read: timeout %08x\n", - readl(USB_ULPI_VIEWPORT)); - return -ETIMEDOUT; - } - return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); -} - -static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate write operation */ - writel(ULPI_RUN | ULPI_WRITE | - ULPI_ADDR(reg) | ULPI_DATA(val), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_write: timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - -static struct usb_phy_io_ops msm_otg_io_ops = { - .read = ulpi_read, - .write = ulpi_write, -}; - -static void ulpi_init(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - int *seq = pdata->phy_init_seq; - - if (!seq) - return; - - while (seq[0] >= 0) { - dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", - seq[0], seq[1]); - ulpi_write(&motg->phy, seq[0], seq[1]); - seq += 2; - } -} - -static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) -{ - int ret; - - if (assert) { - ret = clk_reset(motg->clk, CLK_RESET_ASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); - } else { - ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); - } - return ret; -} - -static int msm_otg_phy_clk_reset(struct msm_otg *motg) -{ - int ret; - - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); - if (ret) { - dev_err(motg->phy.dev, "usb phy clk assert failed\n"); - return ret; - } - usleep_range(10000, 12000); - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); - return ret; -} - -static int msm_otg_phy_reset(struct msm_otg *motg) -{ - u32 val; - int ret; - int retries; - - ret = msm_otg_link_clk_reset(motg, 1); - if (ret) - return ret; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - ret = msm_otg_link_clk_reset(motg, 0); - if (ret) - return ret; - - val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_ULPI, USB_PORTSC); - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, - ULPI_CLR(ULPI_FUNC_CTRL)); - if (!ret) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - /* This reset calibrates the phy, if the above write succeeded */ - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_read(&motg->phy, ULPI_DEBUG); - if (ret != -ETIMEDOUT) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - dev_info(motg->phy.dev, "phy_reset: success\n"); - return 0; -} - -#define LINK_RESET_TIMEOUT_USEC (250 * 1000) -static int msm_otg_reset(struct usb_phy *phy) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - int ret; - u32 val = 0; - u32 ulpi_val = 0; - - ret = msm_otg_phy_reset(motg); - if (ret) { - dev_err(phy->dev, "phy_reset failed\n"); - return ret; - } - - ulpi_init(motg); - - writel(USBCMD_RESET, USB_USBCMD); - while (cnt < LINK_RESET_TIMEOUT_USEC) { - if (!(readl(USB_USBCMD) & USBCMD_RESET)) - break; - udelay(1); - cnt++; - } - if (cnt >= LINK_RESET_TIMEOUT_USEC) - return -ETIMEDOUT; - - /* select ULPI phy */ - writel(0x80000000, USB_PORTSC); - - msleep(100); - - writel(0x0, USB_AHBBURST); - writel(0x00, USB_AHBMODE); - - if (pdata->otg_control == OTG_PHY_CONTROL) { - val = readl(USB_OTGSC); - if (pdata->mode == USB_OTG) { - ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; - val |= OTGSC_IDIE | OTGSC_BSVIE; - } else if (pdata->mode == USB_PERIPHERAL) { - ulpi_val = ULPI_INT_SESS_VALID; - val |= OTGSC_BSVIE; - } - writel(val, USB_OTGSC); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); - } - - return 0; -} - -#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) -#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_suspend(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - - if (atomic_read(&motg->in_lpm)) - return 0; - - disable_irq(motg->irq); - /* - * Chipidea 45-nm PHY suspend sequence: - * - * Interrupt Latch Register auto-clear feature is not present - * in all PHY versions. Latch register is clear on read type. - * Clear latch register to avoid spurious wakeup from - * low power mode (LPM). - * - * PHY comparators are disabled when PHY enters into low power - * mode (LPM). Keep PHY comparators ON in LPM only when we expect - * VBUS/Id notifications from USB PHY. Otherwise turn off USB - * PHY comparators. This save significant amount of power. - * - * PLL is not turned off when PHY enters into low power mode (LPM). - * Disable PLL for maximum power savings. - */ - - if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { - ulpi_read(phy, 0x14); - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(phy, 0x01, 0x30); - ulpi_write(phy, 0x08, 0x09); - } - - /* - * PHY may take some time or even fail to enter into low power - * mode (LPM). Hence poll for 500 msec and reset the PHY and link - * in failure case. - */ - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { - dev_err(phy->dev, "Unable to suspend PHY\n"); - msm_otg_reset(phy); - enable_irq(motg->irq); - return -ETIMEDOUT; - } - - /* - * PHY has capability to generate interrupt asynchronously in low - * power mode (LPM). This interrupt is level triggered. So USB IRQ - * line must be disabled till async interrupt enable bit is cleared - * in USBCMD register. Assert STP (ULPI interface STOP signal) to - * block data communication from PHY. - */ - writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) - writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - - if (!IS_ERR(motg->pclk_src)) - clk_disable(motg->pclk_src); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(0); - msm_hsusb_config_vddcx(0); - } - - if (device_may_wakeup(phy->dev)) - enable_irq_wake(motg->irq); - if (bus) - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 1); - enable_irq(motg->irq); - - dev_info(phy->dev, "USB in low power mode\n"); - - return 0; -} - -static int msm_otg_resume(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - int cnt = 0; - unsigned temp; - - if (!atomic_read(&motg->in_lpm)) - return 0; - - if (!IS_ERR(motg->pclk_src)) - clk_enable(motg->pclk_src); - - clk_enable(motg->pclk); - clk_enable(motg->clk); - if (motg->core_clk) - clk_enable(motg->core_clk); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(1); - msm_hsusb_config_vddcx(1); - writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); - } - - temp = readl(USB_USBCMD); - temp &= ~ASYNC_INTR_CTRL; - temp &= ~ULPI_STP_CTRL; - writel(temp, USB_USBCMD); - - /* - * PHY comes out of low power mode (LPM) in case of wakeup - * from asynchronous interrupt. - */ - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - goto skip_phy_resume; - - writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_RESUME_TIMEOUT_USEC) { - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_RESUME_TIMEOUT_USEC) { - /* - * This is a fatal error. Reset the link and - * PHY. USB state can not be restored. Re-insertion - * of USB cable is the only way to get USB working. - */ - dev_err(phy->dev, "Unable to resume USB." - "Re-plugin the cable\n"); - msm_otg_reset(phy); - } - -skip_phy_resume: - if (device_may_wakeup(phy->dev)) - disable_irq_wake(motg->irq); - if (bus) - set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 0); - - if (motg->async_int) { - motg->async_int = 0; - pm_runtime_put(phy->dev); - enable_irq(motg->irq); - } - - dev_info(phy->dev, "USB exited from low power mode\n"); - - return 0; -} -#endif - -static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) -{ - if (motg->cur_power == mA) - return; - - /* TODO: Notify PMIC about available current */ - dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); - motg->cur_power = mA; -} - -static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - - /* - * Gadget driver uses set_power method to notify about the - * available current based on suspend/configured states. - * - * IDEV_CHG can be drawn irrespective of suspend/un-configured - * states when CDP/ACA is connected. - */ - if (motg->chg_type == USB_SDP_CHARGER) - msm_otg_notify_charger(motg, mA); - - return 0; -} - -static void msm_otg_start_host(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - struct usb_hcd *hcd; - - if (!phy->otg->host) - return; - - hcd = bus_to_hcd(phy->otg->host); - - if (on) { - dev_dbg(phy->dev, "host on\n"); - - if (pdata->vbus_power) - pdata->vbus_power(1); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Enable internal - * HUB before kicking the host. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_A_HOST); -#ifdef CONFIG_USB - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); -#endif - } else { - dev_dbg(phy->dev, "host off\n"); - -#ifdef CONFIG_USB - usb_remove_hcd(hcd); -#endif - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - if (pdata->vbus_power) - pdata->vbus_power(0); - } -} - -static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); - struct usb_hcd *hcd; - - /* - * Fail host registration if this board can support - * only peripheral configuration. - */ - if (motg->pdata->mode == USB_PERIPHERAL) { - dev_info(otg->phy->dev, "Host mode is not supported\n"); - return -ENODEV; - } - - if (!host) { - if (otg->phy->state == OTG_STATE_A_HOST) { - pm_runtime_get_sync(otg->phy->dev); - msm_otg_start_host(otg->phy, 0); - otg->host = NULL; - otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->host = NULL; - } - - return 0; - } - - hcd = bus_to_hcd(host); - hcd->power_budget = motg->pdata->power_budget; - - otg->host = host; - dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if peripheral is not supported - * or peripheral is already registered with us. - */ - if (motg->pdata->mode == USB_HOST || otg->gadget) { - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -static void msm_otg_start_peripheral(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - - if (!phy->otg->gadget) - return; - - if (on) { - dev_dbg(phy->dev, "gadget on\n"); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Disable internal - * HUB before kicking the gadget. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); - usb_gadget_vbus_connect(phy->otg->gadget); - } else { - dev_dbg(phy->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(phy->otg->gadget); - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - } - -} - -static int msm_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); - - /* - * Fail peripheral registration if this board can support - * only host configuration. - */ - if (motg->pdata->mode == USB_HOST) { - dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); - return -ENODEV; - } - - if (!gadget) { - if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { - pm_runtime_get_sync(otg->phy->dev); - msm_otg_start_peripheral(otg->phy, 0); - otg->gadget = NULL; - otg->phy->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->gadget = NULL; - } - - return 0; - } - otg->gadget = gadget; - dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if host is not supported - * or host is already registered with us. - */ - if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -static bool msm_chg_check_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* put it in host mode for enabling D- source */ - chg_det &= ~(1 << 2); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DM as current source, DP as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x8, 0x85); - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DP as current source, DM as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 line_state; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x15); - ret = !(line_state & 1); - break; - case SNPS_28NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x87); - ret = line_state & 2; - break; - default: - break; - } - return ret; -} - -static void msm_chg_disable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - chg_det &= ~(1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - ulpi_write(phy, 0x10, 0x86); - break; - default: - break; - } -} - -static void msm_chg_enable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn on D+ current source */ - chg_det |= (1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Data contact detection enable */ - ulpi_write(phy, 0x10, 0x85); - break; - default: - break; - } -} - -static void msm_chg_block_on(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - /* put the controller in non-driving mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - udelay(100); - break; - default: - break; - } -} - -static void msm_chg_block_off(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - break; - default: - break; - } - - /* put the controller in normal mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); -} - -#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ -#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ -#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ -#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ -static void msm_chg_detect_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); - struct usb_phy *phy = &motg->phy; - bool is_dcd, tmout, vout; - unsigned long delay; - - dev_dbg(phy->dev, "chg detection work\n"); - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - pm_runtime_get_sync(phy->dev); - msm_chg_block_on(motg); - msm_chg_enable_dcd(motg); - motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; - motg->dcd_retries = 0; - delay = MSM_CHG_DCD_POLL_TIME; - break; - case USB_CHG_STATE_WAIT_FOR_DCD: - is_dcd = msm_chg_check_dcd(motg); - tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; - if (is_dcd || tmout) { - msm_chg_disable_dcd(motg); - msm_chg_enable_primary_det(motg); - delay = MSM_CHG_PRIMARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_DCD_DONE; - } else { - delay = MSM_CHG_DCD_POLL_TIME; - } - break; - case USB_CHG_STATE_DCD_DONE: - vout = msm_chg_check_primary_det(motg); - if (vout) { - msm_chg_enable_secondary_det(motg); - delay = MSM_CHG_SECONDARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; - } else { - motg->chg_type = USB_SDP_CHARGER; - motg->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - } - break; - case USB_CHG_STATE_PRIMARY_DONE: - vout = msm_chg_check_secondary_det(motg); - if (vout) - motg->chg_type = USB_DCP_CHARGER; - else - motg->chg_type = USB_CDP_CHARGER; - motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; - /* fall through */ - case USB_CHG_STATE_SECONDARY_DONE: - motg->chg_state = USB_CHG_STATE_DETECTED; - case USB_CHG_STATE_DETECTED: - msm_chg_block_off(motg); - dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); - schedule_work(&motg->sm_work); - return; - default: - return; - } - - schedule_delayed_work(&motg->chg_work, delay); -} - -/* - * We support OTG, Peripheral only and Host only configurations. In case - * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen - * via Id pin status or user request (debugfs). Id/BSV interrupts are not - * enabled when switch is controlled by user and default mode is supplied - * by board file, which can be changed by userspace later. - */ -static void msm_otg_init_sm(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - u32 otgsc = readl(USB_OTGSC); - - switch (pdata->mode) { - case USB_OTG: - if (pdata->otg_control == OTG_PHY_CONTROL) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - } else if (pdata->otg_control == OTG_USER_CONTROL) { - if (pdata->default_mode == USB_HOST) { - clear_bit(ID, &motg->inputs); - } else if (pdata->default_mode == USB_PERIPHERAL) { - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - } else { - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - } - } - break; - case USB_HOST: - clear_bit(ID, &motg->inputs); - break; - case USB_PERIPHERAL: - set_bit(ID, &motg->inputs); - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - break; - } -} - -static void msm_otg_sm_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct usb_otg *otg = motg->phy.otg; - - switch (otg->phy->state) { - case OTG_STATE_UNDEFINED: - dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); - msm_otg_reset(otg->phy); - msm_otg_init_sm(motg); - otg->phy->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); - if (!test_bit(ID, &motg->inputs) && otg->host) { - /* disable BSV bit */ - writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); - msm_otg_start_host(otg->phy, 1); - otg->phy->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs)) { - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - msm_chg_detect_work(&motg->chg_work.work); - break; - case USB_CHG_STATE_DETECTED: - switch (motg->chg_type) { - case USB_DCP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - break; - case USB_CDP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; - break; - case USB_SDP_CHARGER: - msm_otg_notify_charger(motg, IUNIT); - msm_otg_start_peripheral(otg->phy, 1); - otg->phy->state - = OTG_STATE_B_PERIPHERAL; - break; - default: - break; - } - break; - default: - break; - } - } else { - /* - * If charger detection work is pending, decrement - * the pm usage counter to balance with the one that - * is incremented in charger detection work. - */ - if (cancel_delayed_work_sync(&motg->chg_work)) { - pm_runtime_put_sync(otg->phy->dev); - msm_otg_reset(otg->phy); - } - msm_otg_notify_charger(motg, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - } - pm_runtime_put_sync(otg->phy->dev); - break; - case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); - if (!test_bit(B_SESS_VLD, &motg->inputs) || - !test_bit(ID, &motg->inputs)) { - msm_otg_notify_charger(motg, 0); - msm_otg_start_peripheral(otg->phy, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - otg->phy->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->phy); - schedule_work(w); - } - break; - case OTG_STATE_A_HOST: - dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); - if (test_bit(ID, &motg->inputs)) { - msm_otg_start_host(otg->phy, 0); - otg->phy->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->phy); - schedule_work(w); - } - break; - default: - break; - } -} - -static irqreturn_t msm_otg_irq(int irq, void *data) -{ - struct msm_otg *motg = data; - struct usb_phy *phy = &motg->phy; - u32 otgsc = 0; - - if (atomic_read(&motg->in_lpm)) { - disable_irq_nosync(irq); - motg->async_int = 1; - pm_runtime_get(phy->dev); - return IRQ_HANDLED; - } - - otgsc = readl(USB_OTGSC); - if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) - return IRQ_NONE; - - if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - dev_dbg(phy->dev, "ID set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - dev_dbg(phy->dev, "BSV set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } - - writel(otgsc, USB_OTGSC); - schedule_work(&motg->sm_work); - return IRQ_HANDLED; -} - -static int msm_otg_mode_show(struct seq_file *s, void *unused) -{ - struct msm_otg *motg = s->private; - struct usb_otg *otg = motg->phy.otg; - - switch (otg->phy->state) { - case OTG_STATE_A_HOST: - seq_printf(s, "host\n"); - break; - case OTG_STATE_B_PERIPHERAL: - seq_printf(s, "peripheral\n"); - break; - default: - seq_printf(s, "none\n"); - break; - } - - return 0; -} - -static int msm_otg_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, msm_otg_mode_show, inode->i_private); -} - -static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct msm_otg *motg = s->private; - char buf[16]; - struct usb_otg *otg = motg->phy.otg; - int status = count; - enum usb_mode_type req_mode; - - memset(buf, 0x00, sizeof(buf)); - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { - status = -EFAULT; - goto out; - } - - if (!strncmp(buf, "host", 4)) { - req_mode = USB_HOST; - } else if (!strncmp(buf, "peripheral", 10)) { - req_mode = USB_PERIPHERAL; - } else if (!strncmp(buf, "none", 4)) { - req_mode = USB_NONE; - } else { - status = -EINVAL; - goto out; - } - - switch (req_mode) { - case USB_NONE: - switch (otg->phy->state) { - case OTG_STATE_A_HOST: - case OTG_STATE_B_PERIPHERAL: - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_PERIPHERAL: - switch (otg->phy->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_A_HOST: - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_HOST: - switch (otg->phy->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - clear_bit(ID, &motg->inputs); - break; - default: - goto out; - } - break; - default: - goto out; - } - - pm_runtime_get_sync(otg->phy->dev); - schedule_work(&motg->sm_work); -out: - return status; -} - -const struct file_operations msm_otg_mode_fops = { - .open = msm_otg_mode_open, - .read = seq_read, - .write = msm_otg_mode_write, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *msm_otg_dbg_root; -static struct dentry *msm_otg_dbg_mode; - -static int msm_otg_debugfs_init(struct msm_otg *motg) -{ - msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); - - if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) - return -ENODEV; - - msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, - msm_otg_dbg_root, motg, &msm_otg_mode_fops); - if (!msm_otg_dbg_mode) { - debugfs_remove(msm_otg_dbg_root); - msm_otg_dbg_root = NULL; - return -ENODEV; - } - - return 0; -} - -static void msm_otg_debugfs_cleanup(void) -{ - debugfs_remove(msm_otg_dbg_mode); - debugfs_remove(msm_otg_dbg_root); -} - -static int __init msm_otg_probe(struct platform_device *pdev) -{ - int ret = 0; - struct resource *res; - struct msm_otg *motg; - struct usb_phy *phy; - - dev_info(&pdev->dev, "msm_otg probe\n"); - if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "No platform data given. Bailing out\n"); - return -ENODEV; - } - - motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); - if (!motg) { - dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; - } - - motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!motg->phy.otg) { - dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; - } - - motg->pdata = pdev->dev.platform_data; - phy = &motg->phy; - phy->dev = &pdev->dev; - - motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); - if (IS_ERR(motg->phy_reset_clk)) { - dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - ret = PTR_ERR(motg->phy_reset_clk); - goto free_motg; - } - - motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); - if (IS_ERR(motg->clk)) { - dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); - ret = PTR_ERR(motg->clk); - goto put_phy_reset_clk; - } - clk_set_rate(motg->clk, 60000000); - - /* - * If USB Core is running its protocol engine based on CORE CLK, - * CORE CLK must be running at >55Mhz for correct HSUSB - * operation and USB core cannot tolerate frequency changes on - * CORE CLK. For such USB cores, vote for maximum clk frequency - * on pclk source - */ - if (motg->pdata->pclk_src_name) { - motg->pclk_src = clk_get(&pdev->dev, - motg->pdata->pclk_src_name); - if (IS_ERR(motg->pclk_src)) - goto put_clk; - clk_set_rate(motg->pclk_src, INT_MAX); - clk_enable(motg->pclk_src); - } else - motg->pclk_src = ERR_PTR(-ENOENT); - - - motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); - if (IS_ERR(motg->pclk)) { - dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); - ret = PTR_ERR(motg->pclk); - goto put_pclk_src; - } - - /* - * USB core clock is not present on all MSM chips. This - * clock is introduced to remove the dependency on AXI - * bus frequency. - */ - motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); - if (IS_ERR(motg->core_clk)) - motg->core_clk = NULL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get platform resource mem\n"); - ret = -ENODEV; - goto put_core_clk; - } - - motg->regs = ioremap(res->start, resource_size(res)); - if (!motg->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto put_core_clk; - } - dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); - - motg->irq = platform_get_irq(pdev, 0); - if (!motg->irq) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - ret = -ENODEV; - goto free_regs; - } - - clk_enable(motg->clk); - clk_enable(motg->pclk); - - ret = msm_hsusb_init_vddcx(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); - goto free_regs; - } - - ret = msm_hsusb_ldo_init(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); - goto vddcx_exit; - } - ret = msm_hsusb_ldo_set_mode(1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg enable failed\n"); - goto ldo_exit; - } - - if (motg->core_clk) - clk_enable(motg->core_clk); - - writel(0, USB_USBINTR); - writel(0, USB_OTGSC); - - INIT_WORK(&motg->sm_work, msm_otg_sm_work); - INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); - ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, - "msm_otg", motg); - if (ret) { - dev_err(&pdev->dev, "request irq failed\n"); - goto disable_clks; - } - - phy->init = msm_otg_reset; - phy->set_power = msm_otg_set_power; - - phy->io_ops = &msm_otg_io_ops; - - phy->otg->phy = &motg->phy; - phy->otg->set_host = msm_otg_set_host; - phy->otg->set_peripheral = msm_otg_set_peripheral; - - ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); - if (ret) { - dev_err(&pdev->dev, "usb_add_phy failed\n"); - goto free_irq; - } - - platform_set_drvdata(pdev, motg); - device_init_wakeup(&pdev->dev, 1); - - if (motg->pdata->mode == USB_OTG && - motg->pdata->otg_control == OTG_USER_CONTROL) { - ret = msm_otg_debugfs_init(motg); - if (ret) - dev_dbg(&pdev->dev, "mode debugfs file is" - "not available\n"); - } - - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; -free_irq: - free_irq(motg->irq, motg); -disable_clks: - clk_disable(motg->pclk); - clk_disable(motg->clk); -ldo_exit: - msm_hsusb_ldo_init(motg, 0); -vddcx_exit: - msm_hsusb_init_vddcx(motg, 0); -free_regs: - iounmap(motg->regs); -put_core_clk: - if (motg->core_clk) - clk_put(motg->core_clk); - clk_put(motg->pclk); -put_pclk_src: - if (!IS_ERR(motg->pclk_src)) { - clk_disable(motg->pclk_src); - clk_put(motg->pclk_src); - } -put_clk: - clk_put(motg->clk); -put_phy_reset_clk: - clk_put(motg->phy_reset_clk); -free_motg: - kfree(motg->phy.otg); - kfree(motg); - return ret; -} - -static int msm_otg_remove(struct platform_device *pdev) -{ - struct msm_otg *motg = platform_get_drvdata(pdev); - struct usb_phy *phy = &motg->phy; - int cnt = 0; - - if (phy->otg->host || phy->otg->gadget) - return -EBUSY; - - msm_otg_debugfs_cleanup(); - cancel_delayed_work_sync(&motg->chg_work); - cancel_work_sync(&motg->sm_work); - - pm_runtime_resume(&pdev->dev); - - device_init_wakeup(&pdev->dev, 0); - pm_runtime_disable(&pdev->dev); - - usb_remove_phy(phy); - free_irq(motg->irq, motg); - - /* - * Put PHY in low power mode. - */ - ulpi_read(phy, 0x14); - ulpi_write(phy, 0x08, 0x09); - - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) - dev_err(phy->dev, "Unable to suspend PHY\n"); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - if (!IS_ERR(motg->pclk_src)) { - clk_disable(motg->pclk_src); - clk_put(motg->pclk_src); - } - msm_hsusb_ldo_init(motg, 0); - - iounmap(motg->regs); - pm_runtime_set_suspended(&pdev->dev); - - clk_put(motg->phy_reset_clk); - clk_put(motg->pclk); - clk_put(motg->clk); - if (motg->core_clk) - clk_put(motg->core_clk); - - kfree(motg->phy.otg); - kfree(motg); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME -static int msm_otg_runtime_idle(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - struct usb_otg *otg = motg->phy.otg; - - dev_dbg(dev, "OTG runtime idle\n"); - - /* - * It is observed some times that a spurious interrupt - * comes when PHY is put into LPM immediately after PHY reset. - * This 1 sec delay also prevents entering into LPM immediately - * after asynchronous interrupt. - */ - if (otg->phy->state != OTG_STATE_UNDEFINED) - pm_schedule_suspend(dev, 1000); - - return -EAGAIN; -} - -static int msm_otg_runtime_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_runtime_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime resume\n"); - return msm_otg_resume(motg); -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_pm_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG PM suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_pm_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "OTG PM resume\n"); - - ret = msm_otg_resume(motg); - if (ret) - return ret; - - /* - * Runtime PM Documentation recommends bringing the - * device to full powered state upon resume. - */ - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static const struct dev_pm_ops msm_otg_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) - SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, - msm_otg_runtime_idle) -}; -#endif - -static struct platform_driver msm_otg_driver = { - .remove = msm_otg_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &msm_otg_dev_pm_ops, -#endif - }, -}; - -module_platform_driver_probe(msm_otg_driver, msm_otg_probe); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/phy/mv_otg.c b/drivers/usb/phy/mv_otg.c deleted file mode 100644 index b6a9be3..0000000 --- a/drivers/usb/phy/mv_otg.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * Author: Chao Xie - * Neil Zhang - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mv_otg.h" - -#define DRIVER_DESC "Marvell USB OTG transceiver driver" -#define DRIVER_VERSION "Jan 20, 2010" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "mv-otg"; - -static char *state_string[] = { - "undefined", - "b_idle", - "b_srp_init", - "b_peripheral", - "b_wait_acon", - "b_host", - "a_idle", - "a_wait_vrise", - "a_wait_bcon", - "a_host", - "a_suspend", - "a_peripheral", - "a_wait_vfall", - "a_vbus_err" -}; - -static int mv_otg_set_vbus(struct usb_otg *otg, bool on) -{ - struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy); - if (mvotg->pdata->set_vbus == NULL) - return -ENODEV; - - return mvotg->pdata->set_vbus(on); -} - -static int mv_otg_set_host(struct usb_otg *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int mv_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static void mv_otg_run_state_machine(struct mv_otg *mvotg, - unsigned long delay) -{ - dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n"); - if (!mvotg->qwork) - return; - - queue_delayed_work(mvotg->qwork, &mvotg->work, delay); -} - -static void mv_otg_timer_await_bcon(unsigned long data) -{ - struct mv_otg *mvotg = (struct mv_otg *) data; - - mvotg->otg_ctrl.a_wait_bcon_timeout = 1; - - dev_info(&mvotg->pdev->dev, "B Device No Response!\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } -} - -static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - - if (timer_pending(timer)) - del_timer(timer); - - return 0; -} - -static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, - unsigned long interval, - void (*callback) (unsigned long)) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - if (timer_pending(timer)) { - dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id); - return -EBUSY; - } - - init_timer(timer); - timer->data = (unsigned long) mvotg; - timer->function = callback; - timer->expires = jiffies + interval; - add_timer(timer); - - return 0; -} - -static int mv_otg_reset(struct mv_otg *mvotg) -{ - unsigned int loops; - u32 tmp; - - /* Stop the controller */ - tmp = readl(&mvotg->op_regs->usbcmd); - tmp &= ~USBCMD_RUN_STOP; - writel(tmp, &mvotg->op_regs->usbcmd); - - /* Reset the controller to get default values */ - writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd); - - loops = 500; - while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) { - if (loops == 0) { - dev_err(&mvotg->pdev->dev, - "Wait for RESET completed TIMEOUT\n"); - return -ETIMEDOUT; - } - loops--; - udelay(20); - } - - writel(0x0, &mvotg->op_regs->usbintr); - tmp = readl(&mvotg->op_regs->usbsts); - writel(tmp, &mvotg->op_regs->usbsts); - - return 0; -} - -static void mv_otg_init_irq(struct mv_otg *mvotg) -{ - u32 otgsc; - - mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID - | OTGSC_INTR_A_VBUS_VALID; - mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID - | OTGSC_INTSTS_A_VBUS_VALID; - - if (mvotg->pdata->vbus == NULL) { - mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID - | OTGSC_INTR_B_SESSION_END; - mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID - | OTGSC_INTSTS_B_SESSION_END; - } - - if (mvotg->pdata->id == NULL) { - mvotg->irq_en |= OTGSC_INTR_USB_ID; - mvotg->irq_status |= OTGSC_INTSTS_USB_ID; - } - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); -} - -static void mv_otg_start_host(struct mv_otg *mvotg, int on) -{ -#ifdef CONFIG_USB - struct usb_otg *otg = mvotg->phy.otg; - struct usb_hcd *hcd; - - if (!otg->host) - return; - - dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop"); - - hcd = bus_to_hcd(otg->host); - - if (on) - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - else - usb_remove_hcd(hcd); -#endif /* CONFIG_USB */ -} - -static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) -{ - struct usb_otg *otg = mvotg->phy.otg; - - if (!otg->gadget) - return; - - dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off"); - - if (on) - usb_gadget_vbus_connect(otg->gadget); - else - usb_gadget_vbus_disconnect(otg->gadget); -} - -static void otg_clock_enable(struct mv_otg *mvotg) -{ - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_prepare_enable(mvotg->clk[i]); -} - -static void otg_clock_disable(struct mv_otg *mvotg) -{ - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_disable_unprepare(mvotg->clk[i]); -} - -static int mv_otg_enable_internal(struct mv_otg *mvotg) -{ - int retval = 0; - - if (mvotg->active) - return 0; - - dev_dbg(&mvotg->pdev->dev, "otg enabled\n"); - - otg_clock_enable(mvotg); - if (mvotg->pdata->phy_init) { - retval = mvotg->pdata->phy_init(mvotg->phy_regs); - if (retval) { - dev_err(&mvotg->pdev->dev, - "init phy error %d\n", retval); - otg_clock_disable(mvotg); - return retval; - } - } - mvotg->active = 1; - - return 0; - -} - -static int mv_otg_enable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - return mv_otg_enable_internal(mvotg); - - return 0; -} - -static void mv_otg_disable_internal(struct mv_otg *mvotg) -{ - if (mvotg->active) { - dev_dbg(&mvotg->pdev->dev, "otg disabled\n"); - if (mvotg->pdata->phy_deinit) - mvotg->pdata->phy_deinit(mvotg->phy_regs); - otg_clock_disable(mvotg); - mvotg->active = 0; - } -} - -static void mv_otg_disable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - mv_otg_disable_internal(mvotg); -} - -static void mv_otg_update_inputs(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - - if (mvotg->pdata->vbus) { - if (mvotg->pdata->vbus->poll() == VBUS_HIGH) { - otg_ctrl->b_sess_vld = 1; - otg_ctrl->b_sess_end = 0; - } else { - otg_ctrl->b_sess_vld = 0; - otg_ctrl->b_sess_end = 1; - } - } else { - otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID); - otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END); - } - - if (mvotg->pdata->id) - otg_ctrl->id = !!mvotg->pdata->id->poll(); - else - otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID); - - if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id) - otg_ctrl->a_bus_req = 1; - - otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID); - otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID); - - dev_dbg(&mvotg->pdev->dev, "%s: ", __func__); - dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id); - dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld); - dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end); - dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld); - dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld); -} - -static void mv_otg_update_state(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - struct usb_phy *phy = &mvotg->phy; - int old_state = phy->state; - - switch (old_state) { - case OTG_STATE_UNDEFINED: - phy->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - if (otg_ctrl->id == 0) - phy->state = OTG_STATE_A_IDLE; - else if (otg_ctrl->b_sess_vld) - phy->state = OTG_STATE_B_PERIPHERAL; - break; - case OTG_STATE_B_PERIPHERAL: - if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0) - phy->state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_A_IDLE: - if (otg_ctrl->id) - phy->state = OTG_STATE_B_IDLE; - else if (!(otg_ctrl->a_bus_drop) && - (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det)) - phy->state = OTG_STATE_A_WAIT_VRISE; - break; - case OTG_STATE_A_WAIT_VRISE: - if (otg_ctrl->a_vbus_vld) - phy->state = OTG_STATE_A_WAIT_BCON; - break; - case OTG_STATE_A_WAIT_BCON: - if (otg_ctrl->id || otg_ctrl->a_bus_drop - || otg_ctrl->a_wait_bcon_timeout) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_WAIT_VFALL; - otg_ctrl->a_bus_req = 0; - } else if (!otg_ctrl->a_vbus_vld) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_VBUS_ERR; - } else if (otg_ctrl->b_conn) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - phy->state = OTG_STATE_A_HOST; - } - break; - case OTG_STATE_A_HOST: - if (otg_ctrl->id || !otg_ctrl->b_conn - || otg_ctrl->a_bus_drop) - phy->state = OTG_STATE_A_WAIT_BCON; - else if (!otg_ctrl->a_vbus_vld) - phy->state = OTG_STATE_A_VBUS_ERR; - break; - case OTG_STATE_A_WAIT_VFALL: - if (otg_ctrl->id - || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld) - || otg_ctrl->a_bus_req) - phy->state = OTG_STATE_A_IDLE; - break; - case OTG_STATE_A_VBUS_ERR: - if (otg_ctrl->id || otg_ctrl->a_clr_err - || otg_ctrl->a_bus_drop) { - otg_ctrl->a_clr_err = 0; - phy->state = OTG_STATE_A_WAIT_VFALL; - } - break; - default: - break; - } -} - -static void mv_otg_work(struct work_struct *work) -{ - struct mv_otg *mvotg; - struct usb_phy *phy; - struct usb_otg *otg; - int old_state; - - mvotg = container_of(to_delayed_work(work), struct mv_otg, work); - -run: - /* work queue is single thread, or we need spin_lock to protect */ - phy = &mvotg->phy; - otg = phy->otg; - old_state = phy->state; - - if (!mvotg->active) - return; - - mv_otg_update_inputs(mvotg); - mv_otg_update_state(mvotg); - - if (old_state != phy->state) { - dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", - state_string[old_state], - state_string[phy->state]); - - switch (phy->state) { - case OTG_STATE_B_IDLE: - otg->default_a = 0; - if (old_state == OTG_STATE_B_PERIPHERAL) - mv_otg_start_periphrals(mvotg, 0); - mv_otg_reset(mvotg); - mv_otg_disable(mvotg); - break; - case OTG_STATE_B_PERIPHERAL: - mv_otg_enable(mvotg); - mv_otg_start_periphrals(mvotg, 1); - break; - case OTG_STATE_A_IDLE: - otg->default_a = 1; - mv_otg_enable(mvotg); - if (old_state == OTG_STATE_A_WAIT_VFALL) - mv_otg_start_host(mvotg, 0); - mv_otg_reset(mvotg); - break; - case OTG_STATE_A_WAIT_VRISE: - mv_otg_set_vbus(otg, 1); - break; - case OTG_STATE_A_WAIT_BCON: - if (old_state != OTG_STATE_A_HOST) - mv_otg_start_host(mvotg, 1); - mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, - T_A_WAIT_BCON, - mv_otg_timer_await_bcon); - /* - * Now, we directly enter A_HOST. So set b_conn = 1 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 1; - break; - case OTG_STATE_A_HOST: - break; - case OTG_STATE_A_WAIT_VFALL: - /* - * Now, we has exited A_HOST. So set b_conn = 0 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 0; - mv_otg_set_vbus(otg, 0); - break; - case OTG_STATE_A_VBUS_ERR: - break; - default: - break; - } - goto run; - } -} - -static irqreturn_t mv_otg_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - writel(otgsc, &mvotg->op_regs->otgsc); - - /* - * if we have vbus, then the vbus detection for B-device - * will be done by mv_otg_inputs_irq(). - */ - if (mvotg->pdata->vbus) - if ((otgsc & OTGSC_STS_USB_ID) && - !(otgsc & OTGSC_INTSTS_USB_ID)) - return IRQ_NONE; - - if ((otgsc & mvotg->irq_status) == 0) - return IRQ_NONE; - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - - /* The clock may disabled at this time */ - if (!mvotg->active) { - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - } - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_req); -} - -static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - - if (count > 2) - return -1; - - /* We will use this interface to change to A device */ - if (mvotg->phy.state != OTG_STATE_B_IDLE - && mvotg->phy.state != OTG_STATE_A_IDLE) - return -1; - - /* The clock may disabled and we need to set irq for ID detected */ - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_req = 1; - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_req = 1\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - - return count; -} - -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, - set_a_bus_req); - -static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_clr_err = 1; - dev_dbg(&mvotg->pdev->dev, - "User request: a_clr_err = 1\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); - -static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_drop); -} - -static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_drop = 1; - mvotg->otg_ctrl.a_bus_req = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 1\n"); - dev_dbg(&mvotg->pdev->dev, - "User request: and a_bus_req = 0\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, - get_a_bus_drop, set_a_bus_drop); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_clr_err.attr, - &dev_attr_a_bus_drop.attr, - NULL, -}; - -static struct attribute_group inputs_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -int mv_otg_remove(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group); - - if (mvotg->qwork) { - flush_workqueue(mvotg->qwork); - destroy_workqueue(mvotg->qwork); - } - - mv_otg_disable(mvotg); - - usb_remove_phy(&mvotg->phy); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static int mv_otg_probe(struct platform_device *pdev) -{ - struct mv_usb_platform_data *pdata = pdev->dev.platform_data; - struct mv_otg *mvotg; - struct usb_otg *otg; - struct resource *r; - int retval = 0, clk_i, i; - size_t size; - - if (pdata == NULL) { - dev_err(&pdev->dev, "failed to get platform data\n"); - return -ENODEV; - } - - size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum; - mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!mvotg) { - dev_err(&pdev->dev, "failed to allocate memory!\n"); - return -ENOMEM; - } - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - platform_set_drvdata(pdev, mvotg); - - mvotg->pdev = pdev; - mvotg->pdata = pdata; - - mvotg->clknum = pdata->clknum; - for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) { - mvotg->clk[clk_i] = devm_clk_get(&pdev->dev, - pdata->clkname[clk_i]); - if (IS_ERR(mvotg->clk[clk_i])) { - retval = PTR_ERR(mvotg->clk[clk_i]); - return retval; - } - } - - mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); - if (!mvotg->qwork) { - dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n"); - return -ENOMEM; - } - - INIT_DELAYED_WORK(&mvotg->work, mv_otg_work); - - /* OTG common part */ - mvotg->pdev = pdev; - mvotg->phy.dev = &pdev->dev; - mvotg->phy.otg = otg; - mvotg->phy.label = driver_name; - mvotg->phy.state = OTG_STATE_UNDEFINED; - - otg->phy = &mvotg->phy; - otg->set_host = mv_otg_set_host; - otg->set_peripheral = mv_otg_set_peripheral; - otg->set_vbus = mv_otg_set_vbus; - - for (i = 0; i < OTG_TIMER_NUM; i++) - init_timer(&mvotg->otg_ctrl.timer[i]); - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "phyregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->phy_regs == NULL) { - dev_err(&pdev->dev, "failed to map phy I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "capregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->cap_regs == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - /* we will acces controller register, so enable the udc controller */ - retval = mv_otg_enable_internal(mvotg); - if (retval) { - dev_err(&pdev->dev, "mv otg enable error %d\n", retval); - goto err_destroy_workqueue; - } - - mvotg->op_regs = - (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs - + (readl(mvotg->cap_regs) & CAPLENGTH_MASK)); - - if (pdata->id) { - retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "id", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for ID\n"); - pdata->id = NULL; - } - } - - if (pdata->vbus) { - mvotg->clock_gating = 1; - retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "vbus", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for VBUS, " - "disable clock gating\n"); - mvotg->clock_gating = 0; - pdata->vbus = NULL; - } - } - - if (pdata->disable_otg_clock_gating) - mvotg->clock_gating = 0; - - mv_otg_reset(mvotg); - mv_otg_init_irq(mvotg); - - r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - retval = -ENODEV; - goto err_disable_clk; - } - - mvotg->irq = r->start; - if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED, - driver_name, mvotg)) { - dev_err(&pdev->dev, "Request irq %d for OTG failed\n", - mvotg->irq); - mvotg->irq = 0; - retval = -ENODEV; - goto err_disable_clk; - } - - retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); - if (retval < 0) { - dev_err(&pdev->dev, "can't register transceiver, %d\n", - retval); - goto err_disable_clk; - } - - retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group); - if (retval < 0) { - dev_dbg(&pdev->dev, - "Can't register sysfs attr group: %d\n", retval); - goto err_remove_phy; - } - - spin_lock_init(&mvotg->wq_lock); - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 2 * HZ); - spin_unlock(&mvotg->wq_lock); - } - - dev_info(&pdev->dev, - "successful probe OTG device %s clock gating.\n", - mvotg->clock_gating ? "with" : "without"); - - return 0; - -err_remove_phy: - usb_remove_phy(&mvotg->phy); -err_disable_clk: - mv_otg_disable_internal(mvotg); -err_destroy_workqueue: - flush_workqueue(mvotg->qwork); - destroy_workqueue(mvotg->qwork); - - platform_set_drvdata(pdev, NULL); - - return retval; -} - -#ifdef CONFIG_PM -static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - if (mvotg->phy.state != OTG_STATE_B_IDLE) { - dev_info(&pdev->dev, - "OTG state is not B_IDLE, it is %d!\n", - mvotg->phy.state); - return -EAGAIN; - } - - if (!mvotg->clock_gating) - mv_otg_disable_internal(mvotg); - - return 0; -} - -static int mv_otg_resume(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - u32 otgsc; - - if (!mvotg->clock_gating) { - mv_otg_enable_internal(mvotg); - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - return 0; -} -#endif - -static struct platform_driver mv_otg_driver = { - .probe = mv_otg_probe, - .remove = __exit_p(mv_otg_remove), - .driver = { - .owner = THIS_MODULE, - .name = driver_name, - }, -#ifdef CONFIG_PM - .suspend = mv_otg_suspend, - .resume = mv_otg_resume, -#endif -}; -module_platform_driver(mv_otg_driver); diff --git a/drivers/usb/phy/mv_otg.h b/drivers/usb/phy/mv_otg.h deleted file mode 100644 index 8a9e351..0000000 --- a/drivers/usb/phy/mv_otg.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __MV_USB_OTG_CONTROLLER__ -#define __MV_USB_OTG_CONTROLLER__ - -#include - -/* Command Register Bit Masks */ -#define USBCMD_RUN_STOP (0x00000001) -#define USBCMD_CTRL_RESET (0x00000002) - -/* otgsc Register Bit Masks */ -#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 -#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 -#define OTGSC_CTRL_OTG_TERM 0x00000008 -#define OTGSC_CTRL_DATA_PULSING 0x00000010 -#define OTGSC_STS_USB_ID 0x00000100 -#define OTGSC_STS_A_VBUS_VALID 0x00000200 -#define OTGSC_STS_A_SESSION_VALID 0x00000400 -#define OTGSC_STS_B_SESSION_VALID 0x00000800 -#define OTGSC_STS_B_SESSION_END 0x00001000 -#define OTGSC_STS_1MS_TOGGLE 0x00002000 -#define OTGSC_STS_DATA_PULSING 0x00004000 -#define OTGSC_INTSTS_USB_ID 0x00010000 -#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 -#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 -#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 -#define OTGSC_INTSTS_B_SESSION_END 0x00100000 -#define OTGSC_INTSTS_1MS 0x00200000 -#define OTGSC_INTSTS_DATA_PULSING 0x00400000 -#define OTGSC_INTR_USB_ID 0x01000000 -#define OTGSC_INTR_A_VBUS_VALID 0x02000000 -#define OTGSC_INTR_A_SESSION_VALID 0x04000000 -#define OTGSC_INTR_B_SESSION_VALID 0x08000000 -#define OTGSC_INTR_B_SESSION_END 0x10000000 -#define OTGSC_INTR_1MS_TIMER 0x20000000 -#define OTGSC_INTR_DATA_PULSING 0x40000000 - -#define CAPLENGTH_MASK (0xff) - -/* Timer's interval, unit 10ms */ -#define T_A_WAIT_VRISE 100 -#define T_A_WAIT_BCON 2000 -#define T_A_AIDL_BDIS 100 -#define T_A_BIDL_ADIS 20 -#define T_B_ASE0_BRST 400 -#define T_B_SE0_SRP 300 -#define T_B_SRP_FAIL 2000 -#define T_B_DATA_PLS 10 -#define T_B_SRP_INIT 100 -#define T_A_SRP_RSPNS 10 -#define T_A_DRV_RSM 5 - -enum otg_function { - OTG_B_DEVICE = 0, - OTG_A_DEVICE -}; - -enum mv_otg_timer { - A_WAIT_BCON_TIMER = 0, - OTG_TIMER_NUM -}; - -/* PXA OTG state machine */ -struct mv_otg_ctrl { - /* internal variables */ - u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */ - u8 b_srp_done; - u8 b_hnp_en; - - /* OTG inputs */ - u8 a_bus_drop; - u8 a_bus_req; - u8 a_clr_err; - u8 a_bus_resume; - u8 a_bus_suspend; - u8 a_conn; - u8 a_sess_vld; - u8 a_srp_det; - u8 a_vbus_vld; - u8 b_bus_req; /* B-Device Require Bus */ - u8 b_bus_resume; - u8 b_bus_suspend; - u8 b_conn; - u8 b_se0_srp; - u8 b_sess_end; - u8 b_sess_vld; - u8 id; - u8 a_suspend_req; - - /*Timer event */ - u8 a_aidl_bdis_timeout; - u8 b_ase0_brst_timeout; - u8 a_bidl_adis_timeout; - u8 a_wait_bcon_timeout; - - struct timer_list timer[OTG_TIMER_NUM]; -}; - -#define VUSBHS_MAX_PORTS 8 - -struct mv_otg_regs { - u32 usbcmd; /* Command register */ - u32 usbsts; /* Status register */ - u32 usbintr; /* Interrupt enable */ - u32 frindex; /* Frame index */ - u32 reserved1[1]; - u32 deviceaddr; /* Device Address */ - u32 eplistaddr; /* Endpoint List Address */ - u32 ttctrl; /* HOST TT status and control */ - u32 burstsize; /* Programmable Burst Size */ - u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ - u32 reserved[4]; - u32 epnak; /* Endpoint NAK */ - u32 epnaken; /* Endpoint NAK Enable */ - u32 configflag; /* Configured Flag register */ - u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ - u32 otgsc; - u32 usbmode; /* USB Host/Device mode */ - u32 epsetupstat; /* Endpoint Setup Status */ - u32 epprime; /* Endpoint Initialize */ - u32 epflush; /* Endpoint De-initialize */ - u32 epstatus; /* Endpoint Status */ - u32 epcomplete; /* Endpoint Interrupt On Complete */ - u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ - u32 mcr; /* Mux Control */ - u32 isr; /* Interrupt Status */ - u32 ier; /* Interrupt Enable */ -}; - -struct mv_otg { - struct usb_phy phy; - struct mv_otg_ctrl otg_ctrl; - - /* base address */ - void __iomem *phy_regs; - void __iomem *cap_regs; - struct mv_otg_regs __iomem *op_regs; - - struct platform_device *pdev; - int irq; - u32 irq_status; - u32 irq_en; - - struct delayed_work work; - struct workqueue_struct *qwork; - - spinlock_t wq_lock; - - struct mv_usb_platform_data *pdata; - - unsigned int active; - unsigned int clock_gating; - unsigned int clknum; - struct clk *clk[0]; -}; - -#endif diff --git a/drivers/usb/phy/mv_u3d_phy.c b/drivers/usb/phy/mv_u3d_phy.c deleted file mode 100644 index 9d85991..0000000 --- a/drivers/usb/phy/mv_u3d_phy.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mv_u3d_phy.h" - -/* - * struct mv_u3d_phy - transceiver driver state - * @phy: transceiver structure - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @base: usb phy register memory base - */ -struct mv_u3d_phy { - struct usb_phy phy; - struct mv_usb_platform_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *base; -}; - -static u32 mv_u3d_phy_read(void __iomem *base, u32 reg) -{ - void __iomem *addr, *data; - - addr = base; - data = base + 0x4; - - writel_relaxed(reg, addr); - return readl_relaxed(data); -} - -static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value) -{ - void __iomem *addr, *data; - u32 tmp; - - addr = base; - data = base + 0x4; - - writel_relaxed(reg, addr); - tmp = readl_relaxed(data); - tmp |= value; - writel_relaxed(tmp, data); -} - -static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value) -{ - void __iomem *addr, *data; - u32 tmp; - - addr = base; - data = base + 0x4; - - writel_relaxed(reg, addr); - tmp = readl_relaxed(data); - tmp &= ~value; - writel_relaxed(tmp, data); -} - -static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value) -{ - void __iomem *addr, *data; - - addr = base; - data = base + 0x4; - - writel_relaxed(reg, addr); - writel_relaxed(value, data); -} - -void mv_u3d_phy_shutdown(struct usb_phy *phy) -{ - struct mv_u3d_phy *mv_u3d_phy; - void __iomem *base; - u32 val; - - mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); - base = mv_u3d_phy->base; - - /* Power down Reference Analog current, bit 15 - * Power down PLL, bit 14 - * Power down Receiver, bit 13 - * Power down Transmitter, bit 12 - * of USB3_POWER_PLL_CONTROL register - */ - val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); - val &= ~(USB3_POWER_PLL_CONTROL_PU); - mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); - - if (mv_u3d_phy->clk) - clk_disable(mv_u3d_phy->clk); -} - -static int mv_u3d_phy_init(struct usb_phy *phy) -{ - struct mv_u3d_phy *mv_u3d_phy; - void __iomem *base; - u32 val, count; - - /* enable usb3 phy */ - mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); - - if (mv_u3d_phy->clk) - clk_enable(mv_u3d_phy->clk); - - base = mv_u3d_phy->base; - - val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); - val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK); - val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT; - mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); - udelay(100); - - mv_u3d_phy_write(base, USB3_RESET_CONTROL, - USB3_RESET_CONTROL_RESET_PIPE); - udelay(100); - - mv_u3d_phy_write(base, USB3_RESET_CONTROL, - USB3_RESET_CONTROL_RESET_PIPE - | USB3_RESET_CONTROL_RESET_PHY); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); - val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK - | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK); - val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT) - | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT); - mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); - udelay(100); - - mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL, - USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE); - val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK - | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK - | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK); - val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT) - | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT) - | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT)); - mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_GEN1_SET0); - val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK; - val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT; - mv_u3d_phy_write(base, USB3_GEN1_SET0, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_GEN2_SET0); - val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK - | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK - | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK); - val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT) - | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT) - | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT) - | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT)); - mv_u3d_phy_write(base, USB3_GEN2_SET0, val); - udelay(100); - - mv_u3d_phy_read(base, USB3_TX_EMPPH); - val &= ~(USB3_TX_EMPPH_AMP_MASK - | USB3_TX_EMPPH_EN_MASK - | USB3_TX_EMPPH_AMP_FORCE_MASK - | USB3_TX_EMPPH_PAR1_MASK - | USB3_TX_EMPPH_PAR2_MASK); - val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT) - | (1 << USB3_TX_EMPPH_EN_SHIFT) - | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT) - | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT) - | (1 << USB3_TX_EMPPH_PAR2_SHIFT)); - - mv_u3d_phy_write(base, USB3_TX_EMPPH, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_GEN2_SET1); - val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK - | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK - | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK - | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK); - val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT) - | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT) - | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT) - | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT)); - mv_u3d_phy_write(base, USB3_GEN2_SET1, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN); - val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK; - val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT; - mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC); - val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK; - val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT; - mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL); - val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK; - val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT; - mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE); - val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK - | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK - | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK); - val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT) - | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT)); - mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val); - udelay(100); - - val = mv_u3d_phy_read(base, USB3_TXDETRX); - val &= ~(USB3_TXDETRX_VTHSEL_MASK); - val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT; - mv_u3d_phy_write(base, USB3_TXDETRX, val); - udelay(100); - - dev_dbg(mv_u3d_phy->dev, "start calibration\n"); - -calstart: - /* Perform Manual Calibration */ - mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL, - 1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT); - - mdelay(1); - - count = 0; - while (1) { - val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL); - if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT)) - break; - else if (count > 50) { - dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n"); - goto calstart; - } - count++; - mdelay(1); - } - - /* active PIPE interface */ - mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL, - 1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE); - - return 0; -} - -static int mv_u3d_phy_probe(struct platform_device *pdev) -{ - struct mv_u3d_phy *mv_u3d_phy; - struct mv_usb_platform_data *pdata; - struct device *dev = &pdev->dev; - struct resource *res; - void __iomem *phy_base; - int ret; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing mem resource\n"); - return -ENODEV; - } - - phy_base = devm_ioremap_resource(dev, res); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL); - if (!mv_u3d_phy) - return -ENOMEM; - - mv_u3d_phy->dev = &pdev->dev; - mv_u3d_phy->plat = pdata; - mv_u3d_phy->base = phy_base; - mv_u3d_phy->phy.dev = mv_u3d_phy->dev; - mv_u3d_phy->phy.label = "mv-u3d-phy"; - mv_u3d_phy->phy.init = mv_u3d_phy_init; - mv_u3d_phy->phy.shutdown = mv_u3d_phy_shutdown; - - ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3); - if (ret) - goto err; - - if (!mv_u3d_phy->clk) - mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy"); - - platform_set_drvdata(pdev, mv_u3d_phy); - - dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n"); -err: - return ret; -} - -static int __exit mv_u3d_phy_remove(struct platform_device *pdev) -{ - struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev); - - usb_remove_phy(&mv_u3d_phy->phy); - - if (mv_u3d_phy->clk) { - clk_put(mv_u3d_phy->clk); - mv_u3d_phy->clk = NULL; - } - - return 0; -} - -static struct platform_driver mv_u3d_phy_driver = { - .probe = mv_u3d_phy_probe, - .remove = mv_u3d_phy_remove, - .driver = { - .name = "mv-u3d-phy", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(mv_u3d_phy_driver); -MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller"); -MODULE_AUTHOR("Yu Xu "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mv-u3d-phy"); diff --git a/drivers/usb/phy/mv_u3d_phy.h b/drivers/usb/phy/mv_u3d_phy.h deleted file mode 100644 index 2a658cb..0000000 --- a/drivers/usb/phy/mv_u3d_phy.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - */ - -#ifndef __MV_U3D_PHY_H -#define __MV_U3D_PHY_H - -#define USB3_POWER_PLL_CONTROL 0x1 -#define USB3_KVCO_CALI_CONTROL 0x2 -#define USB3_IMPEDANCE_CALI_CTRL 0x3 -#define USB3_IMPEDANCE_TX_SSC 0x4 -#define USB3_SQUELCH_FFE 0x6 -#define USB3_GEN1_SET0 0xD -#define USB3_GEN2_SET0 0xF -#define USB3_GEN2_SET1 0x10 -#define USB3_DIGITAL_LOOPBACK_EN 0x23 -#define USB3_PHY_ISOLATION_MODE 0x26 -#define USB3_TXDETRX 0x48 -#define USB3_TX_EMPPH 0x5E -#define USB3_RESET_CONTROL 0x90 -#define USB3_PIPE_SM_CTRL 0x91 - -#define USB3_RESET_CONTROL_RESET_PIPE 0x1 -#define USB3_RESET_CONTROL_RESET_PHY 0x2 - -#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK (0x1F << 0) -#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT 0 -#define USB3_PLL_25MHZ 0x2 -#define USB3_PLL_26MHZ 0x5 -#define USB3_POWER_PLL_CONTROL_PHY_MODE_MASK (0x7 << 5) -#define USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT 5 -#define USB3_POWER_PLL_CONTROL_PU_MASK (0xF << 12) -#define USB3_POWER_PLL_CONTROL_PU_SHIFT 12 -#define USB3_POWER_PLL_CONTROL_PU (0xF << 12) - -#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK (0x1 << 12) -#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_SHIFT 12 -#define USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT 14 -#define USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT 15 - -#define USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK 0xF -#define USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT 0 -#define USB3_SQUELCH_FFE_FFE_RES_SEL_MASK (0x7 << 4) -#define USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT 4 -#define USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK (0x1F << 8) -#define USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT 8 - -#define USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK (0x1 << 15) -#define USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT 11 - -#define USB3_GEN2_SET0_G2_TX_AMP_MASK (0x1F << 1) -#define USB3_GEN2_SET0_G2_TX_AMP_SHIFT 1 -#define USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT 6 -#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK (0xF << 7) -#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT 7 -#define USB3_GEN2_SET0_G2_TX_EMPH_EN_MASK (0x1 << 11) -#define USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT 11 -#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK (0x1 << 15) -#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_SHIFT 15 - -#define USB3_GEN2_SET1_G2_RX_SELMUPI_MASK (0x7 << 0) -#define USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT 0 -#define USB3_GEN2_SET1_G2_RX_SELMUPF_MASK (0x7 << 3) -#define USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT 3 -#define USB3_GEN2_SET1_G2_RX_SELMUFI_MASK (0x3 << 6) -#define USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT 6 -#define USB3_GEN2_SET1_G2_RX_SELMUFF_MASK (0x3 << 8) -#define USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT 8 - -#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK (0x3 << 10) -#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT 10 - -#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK (0x7 << 12) -#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT 12 - -#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK (0x3F << 0) -#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT 0 - -#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK 0xF -#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT 0 -#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK (0xF << 4) -#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT 4 -#define USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK (0x1 << 8) - -#define USB3_TXDETRX_VTHSEL_MASK (0x3 << 4) -#define USB3_TXDETRX_VTHSEL_SHIFT 4 - -#define USB3_TX_EMPPH_AMP_MASK (0xF << 0) -#define USB3_TX_EMPPH_AMP_SHIFT 0 -#define USB3_TX_EMPPH_EN_MASK (0x1 << 6) -#define USB3_TX_EMPPH_EN_SHIFT 6 -#define USB3_TX_EMPPH_AMP_FORCE_MASK (0x1 << 7) -#define USB3_TX_EMPPH_AMP_FORCE_SHIFT 7 -#define USB3_TX_EMPPH_PAR1_MASK (0x1F << 8) -#define USB3_TX_EMPPH_PAR1_SHIFT 8 -#define USB3_TX_EMPPH_PAR2_MASK (0x1 << 13) -#define USB3_TX_EMPPH_PAR2_SHIFT 13 - -#define USB3_PIPE_SM_CTRL_PHY_INIT_DONE 15 - -#endif /* __MV_U3D_PHY_H */ diff --git a/drivers/usb/phy/mxs-phy.c b/drivers/usb/phy/mxs-phy.c deleted file mode 100644 index 9d4381e..0000000 --- a/drivers/usb/phy/mxs-phy.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * Copyright (C) 2012 Marek Vasut - * on behalf of DENX Software Engineering GmbH - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "mxs_phy" - -#define HW_USBPHY_PWD 0x00 -#define HW_USBPHY_CTRL 0x30 -#define HW_USBPHY_CTRL_SET 0x34 -#define HW_USBPHY_CTRL_CLR 0x38 - -#define BM_USBPHY_CTRL_SFTRST BIT(31) -#define BM_USBPHY_CTRL_CLKGATE BIT(30) -#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) -#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) -#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) - -struct mxs_phy { - struct usb_phy phy; - struct clk *clk; -}; - -#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) - -static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) -{ - void __iomem *base = mxs_phy->phy.io_priv; - - stmp_reset_block(base + HW_USBPHY_CTRL); - - /* Power up the PHY */ - writel(0, base + HW_USBPHY_PWD); - - /* enable FS/LS device */ - writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | - BM_USBPHY_CTRL_ENUTMILEVEL3, - base + HW_USBPHY_CTRL_SET); -} - -static int mxs_phy_init(struct usb_phy *phy) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(phy); - - clk_prepare_enable(mxs_phy->clk); - mxs_phy_hw_init(mxs_phy); - - return 0; -} - -static void mxs_phy_shutdown(struct usb_phy *phy) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(phy); - - writel(BM_USBPHY_CTRL_CLKGATE, - phy->io_priv + HW_USBPHY_CTRL_SET); - - clk_disable_unprepare(mxs_phy->clk); -} - -static int mxs_phy_suspend(struct usb_phy *x, int suspend) -{ - struct mxs_phy *mxs_phy = to_mxs_phy(x); - - if (suspend) { - writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); - writel(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_SET); - clk_disable_unprepare(mxs_phy->clk); - } else { - clk_prepare_enable(mxs_phy->clk); - writel(BM_USBPHY_CTRL_CLKGATE, - x->io_priv + HW_USBPHY_CTRL_CLR); - writel(0, x->io_priv + HW_USBPHY_PWD); - } - - return 0; -} - -static int mxs_phy_on_connect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - dev_dbg(phy->dev, "%s speed device has connected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); - - if (speed == USB_SPEED_HIGH) - writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_SET); - - return 0; -} - -static int mxs_phy_on_disconnect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - dev_dbg(phy->dev, "%s speed device has disconnected\n", - (speed == USB_SPEED_HIGH) ? "high" : "non-high"); - - if (speed == USB_SPEED_HIGH) - writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, - phy->io_priv + HW_USBPHY_CTRL_CLR); - - return 0; -} - -static int mxs_phy_probe(struct platform_device *pdev) -{ - struct resource *res; - void __iomem *base; - struct clk *clk; - struct mxs_phy *mxs_phy; - int ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENOENT; - } - - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, - "can't get the clock, err=%ld", PTR_ERR(clk)); - return PTR_ERR(clk); - } - - mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); - if (!mxs_phy) { - dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); - return -ENOMEM; - } - - mxs_phy->phy.io_priv = base; - mxs_phy->phy.dev = &pdev->dev; - mxs_phy->phy.label = DRIVER_NAME; - mxs_phy->phy.init = mxs_phy_init; - mxs_phy->phy.shutdown = mxs_phy_shutdown; - mxs_phy->phy.set_suspend = mxs_phy_suspend; - mxs_phy->phy.notify_connect = mxs_phy_on_connect; - mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; - - ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); - - mxs_phy->clk = clk; - - platform_set_drvdata(pdev, &mxs_phy->phy); - - ret = usb_add_phy_dev(&mxs_phy->phy); - if (ret) - return ret; - - return 0; -} - -static int mxs_phy_remove(struct platform_device *pdev) -{ - struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); - - usb_remove_phy(&mxs_phy->phy); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static const struct of_device_id mxs_phy_dt_ids[] = { - { .compatible = "fsl,imx23-usbphy", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); - -static struct platform_driver mxs_phy_driver = { - .probe = mxs_phy_probe, - .remove = mxs_phy_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = mxs_phy_dt_ids, - }, -}; - -static int __init mxs_phy_module_init(void) -{ - return platform_driver_register(&mxs_phy_driver); -} -postcore_initcall(mxs_phy_module_init); - -static void __exit mxs_phy_module_exit(void) -{ - platform_driver_unregister(&mxs_phy_driver); -} -module_exit(mxs_phy_module_exit); - -MODULE_ALIAS("platform:mxs-usb-phy"); -MODULE_AUTHOR("Marek Vasut "); -MODULE_AUTHOR("Richard Zhao "); -MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/nop-usb-xceiv.c b/drivers/usb/phy/nop-usb-xceiv.c deleted file mode 100644 index 2b10cc9..0000000 --- a/drivers/usb/phy/nop-usb-xceiv.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * drivers/usb/otg/nop-usb-xceiv.c - * - * NOP USB transceiver for all USB transceiver which are either built-in - * into USB IP or which are mostly autonomous. - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Ajay Kumar Gupta - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * This provides a "nop" transceiver for PHYs which are - * autonomous such as isp1504, isp1707, etc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; - -static struct platform_device *pd; - -void usb_nop_xceiv_register(void) -{ - if (pd) - return; - pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); - if (!pd) { - printk(KERN_ERR "Unable to register usb nop transceiver\n"); - return; - } -} -EXPORT_SYMBOL(usb_nop_xceiv_register); - -void usb_nop_xceiv_unregister(void) -{ - platform_device_unregister(pd); - pd = NULL; -} -EXPORT_SYMBOL(usb_nop_xceiv_unregister); - -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - return 0; -} - -static int nop_init(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->vcc)) { - if (regulator_enable(nop->vcc)) - dev_err(phy->dev, "Failed to enable power\n"); - } - - if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); - - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } - - return 0; -} - -static void nop_shutdown(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } - - if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); - - if (!IS_ERR(nop->vcc)) { - if (regulator_disable(nop->vcc)) - dev_err(phy->dev, "Failed to disable power\n"); - } -} - -static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - if (!gadget) { - otg->gadget = NULL; - return -ENODEV; - } - - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - return 0; -} - -static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!host) { - otg->host = NULL; - return -ENODEV; - } - - otg->host = host; - return 0; -} - -static int nop_usb_xceiv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; - struct nop_usb_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); - if (!nop->phy.otg) - return -ENOMEM; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - } - - if (!IS_ERR(nop->clk) && clk_rate) { - err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); - return err; - } - } - - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); - return err; - } - } - - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", - PTR_ERR(nop->vcc)); - if (needs_vcc) - return -EPROBE_DEFER; - } - - nop->reset = devm_regulator_get(&pdev->dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; - } - - nop->dev = &pdev->dev; - nop->phy.dev = nop->dev; - nop->phy.label = "nop-xceiv"; - nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; - nop->phy.state = OTG_STATE_UNDEFINED; - nop->phy.type = type; - - nop->phy.otg->phy = &nop->phy; - nop->phy.otg->set_host = nop_set_host; - nop->phy.otg->set_peripheral = nop_set_peripheral; - - err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_add; - } - - platform_set_drvdata(pdev, nop); - - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - - return 0; - -err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - return err; -} - -static int nop_usb_xceiv_remove(struct platform_device *pdev) -{ - struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - - usb_remove_phy(&nop->phy); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static const struct of_device_id nop_xceiv_dt_ids[] = { - { .compatible = "usb-nop-xceiv" }, - { } -}; - -MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); - -static struct platform_driver nop_usb_xceiv_driver = { - .probe = nop_usb_xceiv_probe, - .remove = nop_usb_xceiv_remove, - .driver = { - .name = "nop_usb_xceiv", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(nop_xceiv_dt_ids), - }, -}; - -static int __init nop_usb_xceiv_init(void) -{ - return platform_driver_register(&nop_usb_xceiv_driver); -} -subsys_initcall(nop_usb_xceiv_init); - -static void __exit nop_usb_xceiv_exit(void) -{ - platform_driver_unregister(&nop_usb_xceiv_driver); -} -module_exit(nop_usb_xceiv_exit); - -MODULE_ALIAS("platform:nop_usb_xceiv"); -MODULE_AUTHOR("Texas Instruments Inc"); -MODULE_DESCRIPTION("NOP USB Transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c deleted file mode 100644 index 1419ced..0000000 --- a/drivers/usb/phy/omap-control-usb.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * omap-control-usb.c - The USB part of control module. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct omap_control_usb *control_usb; - -/** - * omap_get_control_dev - returns the device pointer for this control device - * - * This API should be called to get the device pointer for this control - * module device. This device pointer should be used for called other - * exported API's in this driver. - * - * To be used by PHY driver and glue driver. - */ -struct device *omap_get_control_dev(void) -{ - if (!control_usb) - return ERR_PTR(-ENODEV); - - return control_usb->dev; -} -EXPORT_SYMBOL_GPL(omap_get_control_dev); - -/** - * omap_control_usb3_phy_power - power on/off the serializer using control - * module - * @dev: the control module device - * @on: 0 to off and 1 to on based on powering on or off the PHY - * - * usb3 PHY driver should call this API to power on or off the PHY. - */ -void omap_control_usb3_phy_power(struct device *dev, bool on) -{ - u32 val; - unsigned long rate; - struct omap_control_usb *control_usb = dev_get_drvdata(dev); - - if (control_usb->type != OMAP_CTRL_DEV_TYPE2) - return; - - rate = clk_get_rate(control_usb->sys_clk); - rate = rate/1000000; - - val = readl(control_usb->phy_power); - - if (on) { - val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | - OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; - } else { - val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; - val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << - OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; - } - - writel(val, control_usb->phy_power); -} -EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power); - -/** - * omap_control_usb_phy_power - power on/off the phy using control module reg - * @dev: the control module device - * @on: 0 or 1, based on powering on or off the PHY - */ -void omap_control_usb_phy_power(struct device *dev, int on) -{ - u32 val; - struct omap_control_usb *control_usb = dev_get_drvdata(dev); - - val = readl(control_usb->dev_conf); - - if (on) - val &= ~OMAP_CTRL_DEV_PHY_PD; - else - val |= OMAP_CTRL_DEV_PHY_PD; - - writel(val, control_usb->dev_conf); -} -EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); - -/** - * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that a usb - * device has been connected. - */ -static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); - val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core that it has been - * connected to a usb host. - */ -static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~OMAP_CTRL_DEV_SESSEND; - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | - OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high - * impedance - * @ctrl_usb: struct omap_control_usb * - * - * Writes to the mailbox register to notify the usb core it's now in - * disconnected state. - */ -static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) -{ - u32 val; - - val = readl(ctrl_usb->otghs_control); - val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; - writel(val, ctrl_usb->otghs_control); -} - -/** - * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode - * or device mode or to denote disconnected state - * @dev: the control module device - * @mode: The mode to which usb should be configured - * - * This is an API to write to the mailbox register to notify the usb core that - * a usb device has been connected. - */ -void omap_control_usb_set_mode(struct device *dev, - enum omap_control_usb_mode mode) -{ - struct omap_control_usb *ctrl_usb; - - if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1) - return; - - ctrl_usb = dev_get_drvdata(dev); - - switch (mode) { - case USB_MODE_HOST: - omap_control_usb_host_mode(ctrl_usb); - break; - case USB_MODE_DEVICE: - omap_control_usb_device_mode(ctrl_usb); - break; - case USB_MODE_DISCONNECT: - omap_control_usb_set_sessionend(ctrl_usb); - break; - default: - dev_vdbg(dev, "invalid omap control usb mode\n"); - } -} -EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); - -static int omap_control_usb_probe(struct platform_device *pdev) -{ - struct resource *res; - struct device_node *np = pdev->dev.of_node; - struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data; - - control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), - GFP_KERNEL); - if (!control_usb) { - dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); - return -ENOMEM; - } - - if (np) { - of_property_read_u32(np, "ti,type", &control_usb->type); - } else if (pdata) { - control_usb->type = pdata->type; - } else { - dev_err(&pdev->dev, "no pdata present\n"); - return -EINVAL; - } - - control_usb->dev = &pdev->dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "control_dev_conf"); - control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(control_usb->dev_conf)) - return PTR_ERR(control_usb->dev_conf); - - if (control_usb->type == OMAP_CTRL_DEV_TYPE1) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "otghs_control"); - control_usb->otghs_control = devm_ioremap_resource( - &pdev->dev, res); - if (IS_ERR(control_usb->otghs_control)) - return PTR_ERR(control_usb->otghs_control); - } - - if (control_usb->type == OMAP_CTRL_DEV_TYPE2) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "phy_power_usb"); - control_usb->phy_power = devm_ioremap_resource( - &pdev->dev, res); - if (IS_ERR(control_usb->phy_power)) - return PTR_ERR(control_usb->phy_power); - - control_usb->sys_clk = devm_clk_get(control_usb->dev, - "sys_clkin"); - if (IS_ERR(control_usb->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - } - - - dev_set_drvdata(control_usb->dev, control_usb); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id omap_control_usb_id_table[] = { - { .compatible = "ti,omap-control-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); -#endif - -static struct platform_driver omap_control_usb_driver = { - .probe = omap_control_usb_probe, - .driver = { - .name = "omap-control-usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(omap_control_usb_id_table), - }, -}; - -static int __init omap_control_usb_init(void) -{ - return platform_driver_register(&omap_control_usb_driver); -} -subsys_initcall(omap_control_usb_init); - -static void __exit omap_control_usb_exit(void) -{ - platform_driver_unregister(&omap_control_usb_driver); -} -module_exit(omap_control_usb_exit); - -MODULE_ALIAS("platform: omap_control_usb"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP Control Module USB Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c deleted file mode 100644 index 844ab68..0000000 --- a/drivers/usb/phy/omap-usb2.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * omap-usb2.c - USB PHY, talking to musb controller in OMAP. - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * omap_usb2_set_comparator - links the comparator present in the sytem with - * this phy - * @comparator - the companion phy(comparator) for this phy - * - * The phy companion driver should call this API passing the phy_companion - * filled with set_vbus and start_srp to be used by usb phy. - * - * For use by phy companion driver - */ -int omap_usb2_set_comparator(struct phy_companion *comparator) -{ - struct omap_usb *phy; - struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); - - if (IS_ERR(x)) - return -ENODEV; - - phy = phy_to_omapusb(x); - phy->comparator = comparator; - return 0; -} -EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); - -static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) -{ - struct omap_usb *phy = phy_to_omapusb(otg->phy); - - if (!phy->comparator) - return -ENODEV; - - return phy->comparator->set_vbus(phy->comparator, enabled); -} - -static int omap_usb_start_srp(struct usb_otg *otg) -{ - struct omap_usb *phy = phy_to_omapusb(otg->phy); - - if (!phy->comparator) - return -ENODEV; - - return phy->comparator->start_srp(phy->comparator); -} - -static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct usb_phy *phy = otg->phy; - - otg->host = host; - if (!host) - phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int omap_usb_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct usb_phy *phy = otg->phy; - - otg->gadget = gadget; - if (!gadget) - phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int omap_usb2_suspend(struct usb_phy *x, int suspend) -{ - u32 ret; - struct omap_usb *phy = phy_to_omapusb(x); - - if (suspend && !phy->is_suspended) { - omap_control_usb_phy_power(phy->control_dev, 0); - pm_runtime_put_sync(phy->dev); - phy->is_suspended = 1; - } else if (!suspend && phy->is_suspended) { - ret = pm_runtime_get_sync(phy->dev); - if (ret < 0) { - dev_err(phy->dev, "get_sync failed with err %d\n", - ret); - return ret; - } - omap_control_usb_phy_power(phy->control_dev, 1); - phy->is_suspended = 0; - } - - return 0; -} - -static int omap_usb2_probe(struct platform_device *pdev) -{ - struct omap_usb *phy; - struct usb_otg *otg; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n"); - return -ENOMEM; - } - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) { - dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n"); - return -ENOMEM; - } - - phy->dev = &pdev->dev; - - phy->phy.dev = phy->dev; - phy->phy.label = "omap-usb2"; - phy->phy.set_suspend = omap_usb2_suspend; - phy->phy.otg = otg; - phy->phy.type = USB_PHY_TYPE_USB2; - - phy->control_dev = omap_get_control_dev(); - if (IS_ERR(phy->control_dev)) { - dev_dbg(&pdev->dev, "Failed to get control device\n"); - return -ENODEV; - } - - phy->is_suspended = 1; - omap_control_usb_phy_power(phy->control_dev, 0); - - otg->set_host = omap_usb_set_host; - otg->set_peripheral = omap_usb_set_peripheral; - otg->set_vbus = omap_usb_set_vbus; - otg->start_srp = omap_usb_start_srp; - otg->phy = &phy->phy; - - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); - if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - return PTR_ERR(phy->wkupclk); - } - clk_prepare(phy->wkupclk); - - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); - if (IS_ERR(phy->optclk)) - dev_vdbg(&pdev->dev, "unable to get refclk960m\n"); - else - clk_prepare(phy->optclk); - - usb_add_phy_dev(&phy->phy); - - platform_set_drvdata(pdev, phy); - - pm_runtime_enable(phy->dev); - - return 0; -} - -static int omap_usb2_remove(struct platform_device *pdev) -{ - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_unprepare(phy->wkupclk); - if (!IS_ERR(phy->optclk)) - clk_unprepare(phy->optclk); - usb_remove_phy(&phy->phy); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME - -static int omap_usb2_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_disable(phy->wkupclk); - if (!IS_ERR(phy->optclk)) - clk_disable(phy->optclk); - - return 0; -} - -static int omap_usb2_runtime_resume(struct device *dev) -{ - u32 ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - ret = clk_enable(phy->wkupclk); - if (ret < 0) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto err0; - } - - if (!IS_ERR(phy->optclk)) { - ret = clk_enable(phy->optclk); - if (ret < 0) { - dev_err(phy->dev, "Failed to enable optclk %d\n", ret); - goto err1; - } - } - - return 0; - -err1: - clk_disable(phy->wkupclk); - -err0: - return ret; -} - -static const struct dev_pm_ops omap_usb2_pm_ops = { - SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume, - NULL) -}; - -#define DEV_PM_OPS (&omap_usb2_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -#ifdef CONFIG_OF -static const struct of_device_id omap_usb2_id_table[] = { - { .compatible = "ti,omap-usb2" }, - {} -}; -MODULE_DEVICE_TABLE(of, omap_usb2_id_table); -#endif - -static struct platform_driver omap_usb2_driver = { - .probe = omap_usb2_probe, - .remove = omap_usb2_remove, - .driver = { - .name = "omap-usb2", - .owner = THIS_MODULE, - .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(omap_usb2_id_table), - }, -}; - -module_platform_driver(omap_usb2_driver); - -MODULE_ALIAS("platform: omap_usb2"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP USB2 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/omap-usb3.c b/drivers/usb/phy/omap-usb3.c deleted file mode 100644 index a6e60b1..0000000 --- a/drivers/usb/phy/omap-usb3.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NUM_SYS_CLKS 5 -#define PLL_STATUS 0x00000004 -#define PLL_GO 0x00000008 -#define PLL_CONFIGURATION1 0x0000000C -#define PLL_CONFIGURATION2 0x00000010 -#define PLL_CONFIGURATION3 0x00000014 -#define PLL_CONFIGURATION4 0x00000020 - -#define PLL_REGM_MASK 0x001FFE00 -#define PLL_REGM_SHIFT 0x9 -#define PLL_REGM_F_MASK 0x0003FFFF -#define PLL_REGM_F_SHIFT 0x0 -#define PLL_REGN_MASK 0x000001FE -#define PLL_REGN_SHIFT 0x1 -#define PLL_SELFREQDCO_MASK 0x0000000E -#define PLL_SELFREQDCO_SHIFT 0x1 -#define PLL_SD_MASK 0x0003FC00 -#define PLL_SD_SHIFT 0x9 -#define SET_PLL_GO 0x1 -#define PLL_TICOPWDN 0x10000 -#define PLL_LOCK 0x2 -#define PLL_IDLE 0x1 - -/* - * This is an Empirical value that works, need to confirm the actual - * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status - * to be correctly reflected in the USB3PHY_PLL_STATUS register. - */ -# define PLL_IDLE_TIME 100; - -enum sys_clk_rate { - CLK_RATE_UNDEFINED = -1, - CLK_RATE_12MHZ, - CLK_RATE_16MHZ, - CLK_RATE_19MHZ, - CLK_RATE_26MHZ, - CLK_RATE_38MHZ -}; - -static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = { - {1250, 5, 4, 20, 0}, /* 12 MHz */ - {3125, 20, 4, 20, 0}, /* 16.8 MHz */ - {1172, 8, 4, 20, 65537}, /* 19.2 MHz */ - {1250, 12, 4, 20, 0}, /* 26 MHz */ - {3125, 47, 4, 20, 92843}, /* 38.4 MHz */ -}; - -static int omap_usb3_suspend(struct usb_phy *x, int suspend) -{ - struct omap_usb *phy = phy_to_omapusb(x); - int val; - int timeout = PLL_IDLE_TIME; - - if (suspend && !phy->is_suspended) { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val |= PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_TICOPWDN) - break; - udelay(1); - } while (--timeout); - - omap_control_usb3_phy_power(phy->control_dev, 0); - - phy->is_suspended = 1; - } else if (!suspend && phy->is_suspended) { - phy->is_suspended = 0; - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_IDLE; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (!(val & PLL_TICOPWDN)) - break; - udelay(1); - } while (--timeout); - } - - return 0; -} - -static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate) -{ - switch (rate) { - case 12000000: - return CLK_RATE_12MHZ; - case 16800000: - return CLK_RATE_16MHZ; - case 19200000: - return CLK_RATE_19MHZ; - case 26000000: - return CLK_RATE_26MHZ; - case 38400000: - return CLK_RATE_38MHZ; - default: - return CLK_RATE_UNDEFINED; - } -} - -static void omap_usb_dpll_relock(struct omap_usb *phy) -{ - u32 val; - unsigned long timeout; - - omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); - - timeout = jiffies + msecs_to_jiffies(20); - do { - val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_LOCK) - break; - } while (!WARN_ON(time_after(jiffies, timeout))); -} - -static int omap_usb_dpll_lock(struct omap_usb *phy) -{ - u32 val; - unsigned long rate; - enum sys_clk_rate clk_index; - - rate = clk_get_rate(phy->sys_clk); - clk_index = __get_sys_clk_index(rate); - - if (clk_index == CLK_RATE_UNDEFINED) { - pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate); - return -EINVAL; - } - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGN_MASK; - val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_SELFREQDCO_MASK; - val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGM_MASK; - val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); - val &= ~PLL_REGM_F_MASK; - val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); - - val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); - val &= ~PLL_SD_MASK; - val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT; - omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); - - omap_usb_dpll_relock(phy); - - return 0; -} - -static int omap_usb3_init(struct usb_phy *x) -{ - struct omap_usb *phy = phy_to_omapusb(x); - - omap_usb_dpll_lock(phy); - omap_control_usb3_phy_power(phy->control_dev, 1); - - return 0; -} - -static int omap_usb3_probe(struct platform_device *pdev) -{ - struct omap_usb *phy; - struct resource *res; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n"); - return -ENOMEM; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); - - phy->dev = &pdev->dev; - - phy->phy.dev = phy->dev; - phy->phy.label = "omap-usb3"; - phy->phy.init = omap_usb3_init; - phy->phy.set_suspend = omap_usb3_suspend; - phy->phy.type = USB_PHY_TYPE_USB3; - - phy->is_suspended = 1; - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); - if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - return PTR_ERR(phy->wkupclk); - } - clk_prepare(phy->wkupclk); - - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); - if (IS_ERR(phy->optclk)) { - dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n"); - return PTR_ERR(phy->optclk); - } - clk_prepare(phy->optclk); - - phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin"); - if (IS_ERR(phy->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - - phy->control_dev = omap_get_control_dev(); - if (IS_ERR(phy->control_dev)) { - dev_dbg(&pdev->dev, "Failed to get control device\n"); - return -ENODEV; - } - - omap_control_usb3_phy_power(phy->control_dev, 0); - usb_add_phy_dev(&phy->phy); - - platform_set_drvdata(pdev, phy); - - pm_runtime_enable(phy->dev); - pm_runtime_get(&pdev->dev); - - return 0; -} - -static int omap_usb3_remove(struct platform_device *pdev) -{ - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_unprepare(phy->wkupclk); - clk_unprepare(phy->optclk); - usb_remove_phy(&phy->phy); - if (!pm_runtime_suspended(&pdev->dev)) - pm_runtime_put(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME - -static int omap_usb3_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_disable(phy->wkupclk); - clk_disable(phy->optclk); - - return 0; -} - -static int omap_usb3_runtime_resume(struct device *dev) -{ - u32 ret = 0; - struct platform_device *pdev = to_platform_device(dev); - struct omap_usb *phy = platform_get_drvdata(pdev); - - ret = clk_enable(phy->optclk); - if (ret) { - dev_err(phy->dev, "Failed to enable optclk %d\n", ret); - goto err1; - } - - ret = clk_enable(phy->wkupclk); - if (ret) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto err2; - } - - return 0; - -err2: - clk_disable(phy->optclk); - -err1: - return ret; -} - -static const struct dev_pm_ops omap_usb3_pm_ops = { - SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume, - NULL) -}; - -#define DEV_PM_OPS (&omap_usb3_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -#ifdef CONFIG_OF -static const struct of_device_id omap_usb3_id_table[] = { - { .compatible = "ti,omap-usb3" }, - {} -}; -MODULE_DEVICE_TABLE(of, omap_usb3_id_table); -#endif - -static struct platform_driver omap_usb3_driver = { - .probe = omap_usb3_probe, - .remove = omap_usb3_remove, - .driver = { - .name = "omap-usb3", - .owner = THIS_MODULE, - .pm = DEV_PM_OPS, - .of_match_table = of_match_ptr(omap_usb3_id_table), - }, -}; - -module_platform_driver(omap_usb3_driver); - -MODULE_ALIAS("platform: omap_usb3"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP USB3 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/otg_fsm.c b/drivers/usb/phy/otg_fsm.c deleted file mode 100644 index 1f729a1..0000000 --- a/drivers/usb/phy/otg_fsm.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * OTG Finite State Machine from OTG spec - * - * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * Author: Li Yang - * Jerry Huang - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "otg_fsm.h" - -/* Change USB protocol when there is a protocol change */ -static int otg_set_protocol(struct otg_fsm *fsm, int protocol) -{ - int ret = 0; - - if (fsm->protocol != protocol) { - VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", - fsm->protocol, protocol); - /* stop old protocol */ - if (fsm->protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 0); - else if (fsm->protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 0); - if (ret) - return ret; - - /* start new protocol */ - if (protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 1); - else if (protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 1); - if (ret) - return ret; - - fsm->protocol = protocol; - return 0; - } - - return 0; -} - -static int state_changed; - -/* Called when leaving a state. Do state clean up jobs here */ -void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) -{ - switch (old_state) { - case OTG_STATE_B_IDLE: - otg_del_timer(fsm, b_se0_srp_tmr); - fsm->b_se0_srp = 0; - break; - case OTG_STATE_B_SRP_INIT: - fsm->b_srp_done = 0; - break; - case OTG_STATE_B_PERIPHERAL: - break; - case OTG_STATE_B_WAIT_ACON: - otg_del_timer(fsm, b_ase0_brst_tmr); - fsm->b_ase0_brst_tmout = 0; - break; - case OTG_STATE_B_HOST: - break; - case OTG_STATE_A_IDLE: - break; - case OTG_STATE_A_WAIT_VRISE: - otg_del_timer(fsm, a_wait_vrise_tmr); - fsm->a_wait_vrise_tmout = 0; - break; - case OTG_STATE_A_WAIT_BCON: - otg_del_timer(fsm, a_wait_bcon_tmr); - fsm->a_wait_bcon_tmout = 0; - break; - case OTG_STATE_A_HOST: - otg_del_timer(fsm, a_wait_enum_tmr); - break; - case OTG_STATE_A_SUSPEND: - otg_del_timer(fsm, a_aidl_bdis_tmr); - fsm->a_aidl_bdis_tmout = 0; - fsm->a_suspend_req = 0; - break; - case OTG_STATE_A_PERIPHERAL: - break; - case OTG_STATE_A_WAIT_VFALL: - otg_del_timer(fsm, a_wait_vrise_tmr); - break; - case OTG_STATE_A_VBUS_ERR: - break; - default: - break; - } -} - -/* Called when entering a state */ -int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) -{ - state_changed = 1; - if (fsm->otg->phy->state == new_state) - return 0; - VDBG("Set state: %s\n", usb_otg_state_string(new_state)); - otg_leave_state(fsm, fsm->otg->phy->state); - switch (new_state) { - case OTG_STATE_B_IDLE: - otg_drv_vbus(fsm, 0); - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_se0_srp_tmr); - break; - case OTG_STATE_B_SRP_INIT: - otg_start_pulse(fsm); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_srp_fail_tmr); - break; - case OTG_STATE_B_PERIPHERAL: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 1); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_GADGET); - break; - case OTG_STATE_B_WAIT_ACON: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, b_ase0_brst_tmr); - fsm->a_bus_suspend = 0; - break; - case OTG_STATE_B_HOST: - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 1); - otg_set_protocol(fsm, PROTO_HOST); - usb_bus_start_enum(fsm->otg->host, - fsm->otg->host->otg_port); - break; - case OTG_STATE_A_IDLE: - otg_drv_vbus(fsm, 0); - otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - break; - case OTG_STATE_A_WAIT_VRISE: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_vrise_tmr); - break; - case OTG_STATE_A_WAIT_BCON: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_bcon_tmr); - break; - case OTG_STATE_A_HOST: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 1); - otg_set_protocol(fsm, PROTO_HOST); - /* - * When HNP is triggered while a_bus_req = 0, a_host will - * suspend too fast to complete a_set_b_hnp_en - */ - if (!fsm->a_bus_req || fsm->a_suspend_req) - otg_add_timer(fsm, a_wait_enum_tmr); - break; - case OTG_STATE_A_SUSPEND: - otg_drv_vbus(fsm, 1); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_aidl_bdis_tmr); - - break; - case OTG_STATE_A_PERIPHERAL: - otg_loc_conn(fsm, 1); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_GADGET); - otg_drv_vbus(fsm, 1); - break; - case OTG_STATE_A_WAIT_VFALL: - otg_drv_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_HOST); - break; - case OTG_STATE_A_VBUS_ERR: - otg_drv_vbus(fsm, 0); - otg_loc_conn(fsm, 0); - otg_loc_sof(fsm, 0); - otg_set_protocol(fsm, PROTO_UNDEF); - break; - default: - break; - } - - fsm->otg->phy->state = new_state; - return 0; -} - -/* State change judgement */ -int otg_statemachine(struct otg_fsm *fsm) -{ - enum usb_otg_state state; - unsigned long flags; - - spin_lock_irqsave(&fsm->lock, flags); - - state = fsm->otg->phy->state; - state_changed = 0; - /* State machine state change judgement */ - - switch (state) { - case OTG_STATE_UNDEFINED: - VDBG("fsm->id = %d\n", fsm->id); - if (fsm->id) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else - otg_set_state(fsm, OTG_STATE_A_IDLE); - break; - case OTG_STATE_B_IDLE: - if (!fsm->id) - otg_set_state(fsm, OTG_STATE_A_IDLE); - else if (fsm->b_sess_vld && fsm->otg->gadget) - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) - otg_set_state(fsm, OTG_STATE_B_SRP_INIT); - break; - case OTG_STATE_B_SRP_INIT: - if (!fsm->id || fsm->b_srp_done) - otg_set_state(fsm, OTG_STATE_B_IDLE); - break; - case OTG_STATE_B_PERIPHERAL: - if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (fsm->b_bus_req && fsm->otg-> - gadget->b_hnp_enable && fsm->a_bus_suspend) - otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); - break; - case OTG_STATE_B_WAIT_ACON: - if (fsm->a_conn) - otg_set_state(fsm, OTG_STATE_B_HOST); - else if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { - fsm->b_ase0_brst_tmout = 0; - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - } - break; - case OTG_STATE_B_HOST: - if (!fsm->id || !fsm->b_sess_vld) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->b_bus_req || !fsm->a_conn) - otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - break; - case OTG_STATE_A_IDLE: - if (fsm->id) - otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) - otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); - break; - case OTG_STATE_A_WAIT_VRISE: - if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || - fsm->a_wait_vrise_tmout) { - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - } - break; - case OTG_STATE_A_WAIT_BCON: - if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - else if (fsm->b_conn) - otg_set_state(fsm, OTG_STATE_A_HOST); - else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - break; - case OTG_STATE_A_HOST: - if ((!fsm->a_bus_req || fsm->a_suspend_req) && - fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_SUSPEND); - else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_SUSPEND: - if (!fsm->b_conn && fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); - else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (fsm->a_bus_req || fsm->b_bus_resume) - otg_set_state(fsm, OTG_STATE_A_HOST); - else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_PERIPHERAL: - if (fsm->id || fsm->a_bus_drop) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - else if (fsm->b_bus_suspend) - otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); - else if (!fsm->a_vbus_vld) - otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); - break; - case OTG_STATE_A_WAIT_VFALL: - if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && - !fsm->b_conn)) - otg_set_state(fsm, OTG_STATE_A_IDLE); - break; - case OTG_STATE_A_VBUS_ERR: - if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) - otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - break; - default: - break; - } - spin_unlock_irqrestore(&fsm->lock, flags); - - VDBG("quit statemachine, changed = %d\n", state_changed); - return state_changed; -} diff --git a/drivers/usb/phy/otg_fsm.h b/drivers/usb/phy/otg_fsm.h deleted file mode 100644 index c30a2e1..0000000 --- a/drivers/usb/phy/otg_fsm.h +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#undef DEBUG -#undef VERBOSE - -#ifdef DEBUG -#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ - __func__, ## args) -#else -#define DBG(fmt, args...) do {} while (0) -#endif - -#ifdef VERBOSE -#define VDBG DBG -#else -#define VDBG(stuff...) do {} while (0) -#endif - -#ifdef VERBOSE -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) -#else -#define MPC_LOC do {} while (0) -#endif - -#define PROTO_UNDEF (0) -#define PROTO_HOST (1) -#define PROTO_GADGET (2) - -/* OTG state machine according to the OTG spec */ -struct otg_fsm { - /* Input */ - int a_bus_resume; - int a_bus_suspend; - int a_conn; - int a_sess_vld; - int a_srp_det; - int a_vbus_vld; - int b_bus_resume; - int b_bus_suspend; - int b_conn; - int b_se0_srp; - int b_sess_end; - int b_sess_vld; - int id; - - /* Internal variables */ - int a_set_b_hnp_en; - int b_srp_done; - int b_hnp_enable; - - /* Timeout indicator for timers */ - int a_wait_vrise_tmout; - int a_wait_bcon_tmout; - int a_aidl_bdis_tmout; - int b_ase0_brst_tmout; - - /* Informative variables */ - int a_bus_drop; - int a_bus_req; - int a_clr_err; - int a_suspend_req; - int b_bus_req; - - /* Output */ - int drv_vbus; - int loc_conn; - int loc_sof; - - struct otg_fsm_ops *ops; - struct usb_otg *otg; - - /* Current usb protocol used: 0:undefine; 1:host; 2:client */ - int protocol; - spinlock_t lock; -}; - -struct otg_fsm_ops { - void (*chrg_vbus)(int on); - void (*drv_vbus)(int on); - void (*loc_conn)(int on); - void (*loc_sof)(int on); - void (*start_pulse)(void); - void (*add_timer)(void *timer); - void (*del_timer)(void *timer); - int (*start_host)(struct otg_fsm *fsm, int on); - int (*start_gadget)(struct otg_fsm *fsm, int on); -}; - - -static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) -{ - fsm->ops->chrg_vbus(on); -} - -static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) -{ - if (fsm->drv_vbus != on) { - fsm->drv_vbus = on; - fsm->ops->drv_vbus(on); - } -} - -static inline void otg_loc_conn(struct otg_fsm *fsm, int on) -{ - if (fsm->loc_conn != on) { - fsm->loc_conn = on; - fsm->ops->loc_conn(on); - } -} - -static inline void otg_loc_sof(struct otg_fsm *fsm, int on) -{ - if (fsm->loc_sof != on) { - fsm->loc_sof = on; - fsm->ops->loc_sof(on); - } -} - -static inline void otg_start_pulse(struct otg_fsm *fsm) -{ - fsm->ops->start_pulse(); -} - -static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) -{ - fsm->ops->add_timer(timer); -} - -static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) -{ - fsm->ops->del_timer(timer); -} - -int otg_statemachine(struct otg_fsm *fsm); - -/* Defined by device specific driver, for different timer implementation */ -extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, - *a_wait_enum_tmr; diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c new file mode 100644 index 0000000..2d86f26 --- /dev/null +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -0,0 +1,596 @@ +/* + * drivers/usb/otg/ab8500_usb.c + * + * USB transceiver driver for AB8500 chip + * + * Copyright (C) 2010 ST-Ericsson AB + * Mian Yousaf Kaukab + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB8500_MAIN_WD_CTRL_REG 0x01 +#define AB8500_USB_LINE_STAT_REG 0x80 +#define AB8500_USB_PHY_CTRL_REG 0x8A + +#define AB8500_BIT_OTG_STAT_ID (1 << 0) +#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) +#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) +#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) +#define AB8500_BIT_WD_CTRL_KICK (1 << 1) + +#define AB8500_V1x_LINK_STAT_WAIT (HZ/10) +#define AB8500_WD_KICK_DELAY_US 100 /* usec */ +#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ +#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */ + +/* Usb line status register */ +enum ab8500_usb_link_status { + USB_LINK_NOT_CONFIGURED = 0, + USB_LINK_STD_HOST_NC, + USB_LINK_STD_HOST_C_NS, + USB_LINK_STD_HOST_C_S, + USB_LINK_HOST_CHG_NM, + USB_LINK_HOST_CHG_HS, + USB_LINK_HOST_CHG_HS_CHIRP, + USB_LINK_DEDICATED_CHG, + USB_LINK_ACA_RID_A, + USB_LINK_ACA_RID_B, + USB_LINK_ACA_RID_C_NM, + USB_LINK_ACA_RID_C_HS, + USB_LINK_ACA_RID_C_HS_CHIRP, + USB_LINK_HM_IDGND, + USB_LINK_RESERVED, + USB_LINK_NOT_VALID_LINK +}; + +struct ab8500_usb { + struct usb_phy phy; + struct device *dev; + int irq_num_id_rise; + int irq_num_id_fall; + int irq_num_vbus_rise; + int irq_num_vbus_fall; + int irq_num_link_status; + unsigned vbus_draw; + struct delayed_work dwork; + struct work_struct phy_dis_work; + unsigned long link_status_wait; + int rev; +}; + +static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) +{ + return container_of(x, struct ab8500_usb, phy); +} + +static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) +{ + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + AB8500_BIT_WD_CTRL_ENABLE); + + udelay(AB8500_WD_KICK_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + (AB8500_BIT_WD_CTRL_ENABLE + | AB8500_BIT_WD_CTRL_KICK)); + + if (ab->rev > 0x10) /* v1.1 v2.0 */ + udelay(AB8500_WD_V11_DISABLE_DELAY_US); + else /* v1.0 */ + msleep(AB8500_WD_V10_DISABLE_DELAY_MS); + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, + AB8500_MAIN_WD_CTRL_REG, + 0); +} + +static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, + bool enable) +{ + u8 ctrl_reg; + abx500_get_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_PHY_CTRL_REG, + &ctrl_reg); + if (sel_host) { + if (enable) + ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN; + else + ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN; + } else { + if (enable) + ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN; + else + ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN; + } + + abx500_set_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_PHY_CTRL_REG, + ctrl_reg); + + /* Needed to enable the phy.*/ + if (enable) + ab8500_usb_wd_workaround(ab); +} + +#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true) +#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false) +#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) +#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) + +static int ab8500_usb_link_status_update(struct ab8500_usb *ab) +{ + u8 reg; + enum ab8500_usb_link_status lsts; + void *v = NULL; + enum usb_phy_events event; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, + AB8500_USB_LINE_STAT_REG, + ®); + + lsts = (reg >> 3) & 0x0F; + + switch (lsts) { + case USB_LINK_NOT_CONFIGURED: + case USB_LINK_RESERVED: + case USB_LINK_NOT_VALID_LINK: + /* TODO: Disable regulators. */ + ab8500_usb_host_phy_dis(ab); + ab8500_usb_peri_phy_dis(ab); + ab->phy.state = OTG_STATE_B_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + event = USB_EVENT_NONE; + break; + + case USB_LINK_STD_HOST_NC: + case USB_LINK_STD_HOST_C_NS: + case USB_LINK_STD_HOST_C_S: + case USB_LINK_HOST_CHG_NM: + case USB_LINK_HOST_CHG_HS: + case USB_LINK_HOST_CHG_HS_CHIRP: + if (ab->phy.otg->gadget) { + /* TODO: Enable regulators. */ + ab8500_usb_peri_phy_en(ab); + v = ab->phy.otg->gadget; + } + event = USB_EVENT_VBUS; + break; + + case USB_LINK_HM_IDGND: + if (ab->phy.otg->host) { + /* TODO: Enable regulators. */ + ab8500_usb_host_phy_en(ab); + v = ab->phy.otg->host; + } + ab->phy.state = OTG_STATE_A_IDLE; + ab->phy.otg->default_a = true; + event = USB_EVENT_ID; + break; + + case USB_LINK_ACA_RID_A: + case USB_LINK_ACA_RID_B: + /* TODO */ + case USB_LINK_ACA_RID_C_NM: + case USB_LINK_ACA_RID_C_HS: + case USB_LINK_ACA_RID_C_HS_CHIRP: + case USB_LINK_DEDICATED_CHG: + /* TODO: vbus_draw */ + event = USB_EVENT_CHARGER; + break; + } + + atomic_notifier_call_chain(&ab->phy.notifier, event, v); + + return 0; +} + +static void ab8500_usb_delayed_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + dwork.work); + + ab8500_usb_link_status_update(ab); +} + +static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + /* Wait for link status to become stable. */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + + return IRQ_HANDLED; +} + +static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + /* Link status will not be updated till phy is disabled. */ + ab8500_usb_peri_phy_dis(ab); + + /* Wait for link status to become stable. */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + + return IRQ_HANDLED; +} + +static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + + ab8500_usb_link_status_update(ab); + + return IRQ_HANDLED; +} + +static void ab8500_usb_phy_disable_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + phy_dis_work); + + if (!ab->phy.otg->host) + ab8500_usb_host_phy_dis(ab); + + if (!ab->phy.otg->gadget) + ab8500_usb_peri_phy_dis(ab); +} + +static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) +{ + struct ab8500_usb *ab; + + if (!phy) + return -ENODEV; + + ab = phy_to_ab(phy); + + ab->vbus_draw = mA; + + if (mA) + atomic_notifier_call_chain(&ab->phy.notifier, + USB_EVENT_ENUMERATED, ab->phy.otg->gadget); + return 0; +} + +/* TODO: Implement some way for charging or other drivers to read + * ab->vbus_draw. + */ + +static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) +{ + /* TODO */ + return 0; +} + +static int ab8500_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct ab8500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = phy_to_ab(otg->phy); + + /* Some drivers call this function in atomic context. + * Do not update ab8500 registers directly till this + * is fixed. + */ + + if (!gadget) { + /* TODO: Disable regulators. */ + otg->gadget = NULL; + schedule_work(&ab->phy_dis_work); + } else { + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct ab8500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = phy_to_ab(otg->phy); + + /* Some drivers call this function in atomic context. + * Do not update ab8500 registers directly till this + * is fixed. + */ + + if (!host) { + /* TODO: Disable regulators. */ + otg->host = NULL; + schedule_work(&ab->phy_dis_work); + } else { + otg->host = host; + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static void ab8500_usb_irq_free(struct ab8500_usb *ab) +{ + if (ab->rev < 0x20) { + free_irq(ab->irq_num_id_rise, ab); + free_irq(ab->irq_num_id_fall, ab); + free_irq(ab->irq_num_vbus_rise, ab); + free_irq(ab->irq_num_vbus_fall, ab); + } else { + free_irq(ab->irq_num_link_status, ab); + } +} + +static int ab8500_usb_v1x_res_setup(struct platform_device *pdev, + struct ab8500_usb *ab) +{ + int err; + + ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R"); + if (ab->irq_num_id_rise < 0) { + dev_err(&pdev->dev, "ID rise irq not found\n"); + return ab->irq_num_id_rise; + } + err = request_threaded_irq(ab->irq_num_id_rise, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-rise", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for ID rise irq\n"); + goto fail0; + } + + ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); + if (ab->irq_num_id_fall < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + return ab->irq_num_id_fall; + } + err = request_threaded_irq(ab->irq_num_id_fall, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for ID fall irq\n"); + goto fail1; + } + + ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R"); + if (ab->irq_num_vbus_rise < 0) { + dev_err(&pdev->dev, "VBUS rise irq not found\n"); + return ab->irq_num_vbus_rise; + } + err = request_threaded_irq(ab->irq_num_vbus_rise, NULL, + ab8500_usb_v1x_common_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-rise", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus rise irq\n"); + goto fail2; + } + + ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F"); + if (ab->irq_num_vbus_fall < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + return ab->irq_num_vbus_fall; + } + err = request_threaded_irq(ab->irq_num_vbus_fall, NULL, + ab8500_usb_v1x_vbus_fall_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); + goto fail3; + } + + return 0; +fail3: + free_irq(ab->irq_num_vbus_rise, ab); +fail2: + free_irq(ab->irq_num_id_fall, ab); +fail1: + free_irq(ab->irq_num_id_rise, ab); +fail0: + return err; +} + +static int ab8500_usb_v2_res_setup(struct platform_device *pdev, + struct ab8500_usb *ab) +{ + int err; + + ab->irq_num_link_status = platform_get_irq_byname(pdev, + "USB_LINK_STATUS"); + if (ab->irq_num_link_status < 0) { + dev_err(&pdev->dev, "Link status irq not found\n"); + return ab->irq_num_link_status; + } + + err = request_threaded_irq(ab->irq_num_link_status, NULL, + ab8500_usb_v20_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-link-status", ab); + if (err < 0) { + dev_err(ab->dev, + "request_irq failed for link status irq\n"); + return err; + } + + return 0; +} + +static int ab8500_usb_probe(struct platform_device *pdev) +{ + struct ab8500_usb *ab; + struct usb_otg *otg; + int err; + int rev; + + rev = abx500_get_chip_id(&pdev->dev); + if (rev < 0) { + dev_err(&pdev->dev, "Chip id read failed\n"); + return rev; + } else if (rev < 0x10) { + dev_err(&pdev->dev, "Unsupported AB8500 chip\n"); + return -ENODEV; + } + + ab = kzalloc(sizeof *ab, GFP_KERNEL); + if (!ab) + return -ENOMEM; + + otg = kzalloc(sizeof *otg, GFP_KERNEL); + if (!otg) { + kfree(ab); + return -ENOMEM; + } + + ab->dev = &pdev->dev; + ab->rev = rev; + ab->phy.dev = ab->dev; + ab->phy.otg = otg; + ab->phy.label = "ab8500"; + ab->phy.set_suspend = ab8500_usb_set_suspend; + ab->phy.set_power = ab8500_usb_set_power; + ab->phy.state = OTG_STATE_UNDEFINED; + + otg->phy = &ab->phy; + otg->set_host = ab8500_usb_set_host; + otg->set_peripheral = ab8500_usb_set_peripheral; + + platform_set_drvdata(pdev, ab); + + ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); + + /* v1: Wait for link status to become stable. + * all: Updates form set_host and set_peripheral as they are atomic. + */ + INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work); + + /* all: Disable phy when called from set_host and set_peripheral */ + INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); + + if (ab->rev < 0x20) { + err = ab8500_usb_v1x_res_setup(pdev, ab); + ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT; + } else { + err = ab8500_usb_v2_res_setup(pdev, ab); + } + + if (err < 0) + goto fail0; + + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); + if (err) { + dev_err(&pdev->dev, "Can't register transceiver\n"); + goto fail1; + } + + dev_info(&pdev->dev, "AB8500 usb driver initialized\n"); + + return 0; +fail1: + ab8500_usb_irq_free(ab); +fail0: + kfree(otg); + kfree(ab); + return err; +} + +static int ab8500_usb_remove(struct platform_device *pdev) +{ + struct ab8500_usb *ab = platform_get_drvdata(pdev); + + ab8500_usb_irq_free(ab); + + cancel_delayed_work_sync(&ab->dwork); + + cancel_work_sync(&ab->phy_dis_work); + + usb_remove_phy(&ab->phy); + + ab8500_usb_host_phy_dis(ab); + ab8500_usb_peri_phy_dis(ab); + + platform_set_drvdata(pdev, NULL); + + kfree(ab->phy.otg); + kfree(ab); + + return 0; +} + +static struct platform_driver ab8500_usb_driver = { + .probe = ab8500_usb_probe, + .remove = ab8500_usb_remove, + .driver = { + .name = "ab8500-usb", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_usb_init(void) +{ + return platform_driver_register(&ab8500_usb_driver); +} +subsys_initcall(ab8500_usb_init); + +static void __exit ab8500_usb_exit(void) +{ + platform_driver_unregister(&ab8500_usb_driver); +} +module_exit(ab8500_usb_exit); + +MODULE_ALIAS("platform:ab8500_usb"); +MODULE_AUTHOR("ST-Ericsson AB"); +MODULE_DESCRIPTION("AB8500 usb transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c new file mode 100644 index 0000000..97b9308 --- /dev/null +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -0,0 +1,1173 @@ +/* + * Copyright (C) 2007,2008 Freescale semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * Initialization based on code from Shlomi Gridish. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-fsl-usb.h" + +#define DRIVER_VERSION "Rev. 1.55" +#define DRIVER_AUTHOR "Jerry Huang/Li Yang" +#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" +#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION + +static const char driver_name[] = "fsl-usb2-otg"; + +const pm_message_t otg_suspend_state = { + .event = 1, +}; + +#define HA_DATA_PULSE + +static struct usb_dr_mmap *usb_dr_regs; +static struct fsl_otg *fsl_otg_dev; +static int srp_wait_done; + +/* FSM timers */ +struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr; + +/* Driver specific timers */ +struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, + *b_srp_wait_tmr, *a_wait_enum_tmr; + +static struct list_head active_timers; + +static struct fsl_otg_config fsl_otg_initdata = { + .otg_port = 1, +}; + +#ifdef CONFIG_PPC32 +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(val, addr) writel(val, addr) +#endif /* CONFIG_PPC32 */ + +/* Routines to access transceiver ULPI registers */ +u8 view_ulpi(u8 addr) +{ + u32 temp; + + temp = 0x40000000 | (addr << 16); + fsl_writel(temp, &usb_dr_regs->ulpiview); + udelay(1000); + while (temp & 0x40) + temp = fsl_readl(&usb_dr_regs->ulpiview); + return (le32_to_cpu(temp) & 0x0000ff00) >> 8; +} + +int write_ulpi(u8 addr, u8 data) +{ + u32 temp; + + temp = 0x60000000 | (addr << 16) | data; + fsl_writel(temp, &usb_dr_regs->ulpiview); + return 0; +} + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ + +/* Charge vbus for vbus pulsing in SRP */ +void fsl_otg_chrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop discharging, start charging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | + OTGSC_CTRL_VBUS_CHARGE; + else + /* stop charging */ + tmp &= ~OTGSC_CTRL_VBUS_CHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* Discharge vbus through a resistor to ground */ +void fsl_otg_dischrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop charging, start discharging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | + OTGSC_CTRL_VBUS_DISCHARGE; + else + /* stop discharging */ + tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* A-device driver vbus, controlled through PP bit in PORTSC */ +void fsl_otg_drv_vbus(int on) +{ + u32 tmp; + + if (on) { + tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; + fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); + } else { + tmp = fsl_readl(&usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; + fsl_writel(tmp, &usb_dr_regs->portsc); + } +} + +/* + * Pull-up D+, signalling connect by periperal. Also used in + * data-line pulsing in SRP + */ +void fsl_otg_loc_conn(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + tmp |= OTGSC_CTRL_DATA_PULSING; + else + tmp &= ~OTGSC_CTRL_DATA_PULSING; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* + * Generate SOF by host. This is controlled through suspend/resume the + * port. In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +void fsl_otg_loc_sof(int on) +{ + u32 tmp; + + tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; + if (on) + tmp |= PORTSC_PORT_FORCE_RESUME; + else + tmp |= PORTSC_PORT_SUSPEND; + + fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); + +} + +/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ +void fsl_otg_start_pulse(void) +{ + u32 tmp; + + srp_wait_done = 0; +#ifdef HA_DATA_PULSE + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + tmp |= OTGSC_HA_DATA_PULSE; + fsl_writel(tmp, &usb_dr_regs->otgsc); +#else + fsl_otg_loc_conn(1); +#endif + + fsl_otg_add_timer(b_data_pulse_tmr); +} + +void b_data_pulse_end(unsigned long foo) +{ +#ifdef HA_DATA_PULSE +#else + fsl_otg_loc_conn(0); +#endif + + /* Do VBUS pulse after data pulse */ + fsl_otg_pulse_vbus(); +} + +void fsl_otg_pulse_vbus(void) +{ + srp_wait_done = 0; + fsl_otg_chrg_vbus(1); + /* start the timer to end vbus charge */ + fsl_otg_add_timer(b_vbus_pulse_tmr); +} + +void b_vbus_pulse_end(unsigned long foo) +{ + fsl_otg_chrg_vbus(0); + + /* + * As USB3300 using the same a_sess_vld and b_sess_vld voltage + * we need to discharge the bus for a while to distinguish + * residual voltage of vbus pulsing and A device pull up + */ + fsl_otg_dischrg_vbus(1); + fsl_otg_add_timer(b_srp_wait_tmr); +} + +void b_srp_end(unsigned long foo) +{ + fsl_otg_dischrg_vbus(0); + srp_wait_done = 1; + + if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) && + fsl_otg_dev->fsm.b_sess_vld) + fsl_otg_dev->fsm.b_srp_done = 1; +} + +/* + * Workaround for a_host suspending too fast. When a_bus_req=0, + * a_host will start by SRP. It needs to set b_hnp_enable before + * actually suspending to start HNP + */ +void a_wait_enum(unsigned long foo) +{ + VDBG("a_wait_enum timeout\n"); + if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) + fsl_otg_add_timer(a_wait_enum_tmr); + else + otg_statemachine(&fsl_otg_dev->fsm); +} + +/* The timeout callback function to set time out bit */ +void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +/* Initialize timers */ +int fsl_otg_init_timers(struct otg_fsm *fsm) +{ + /* FSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + if (!a_wait_vrise_tmr) + return -ENOMEM; + + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + if (!a_wait_bcon_tmr) + return -ENOMEM; + + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + if (!a_aidl_bdis_tmr) + return -ENOMEM; + + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + if (!b_ase0_brst_tmr) + return -ENOMEM; + + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + if (!b_se0_srp_tmr) + return -ENOMEM; + + b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + if (!b_srp_fail_tmr) + return -ENOMEM; + + a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, + (unsigned long)&fsm); + if (!a_wait_enum_tmr) + return -ENOMEM; + + /* device driver used timers */ + b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); + if (!b_srp_wait_tmr) + return -ENOMEM; + + b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, + TB_DATA_PLS, 0); + if (!b_data_pulse_tmr) + return -ENOMEM; + + b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, + TB_VBUS_PLS, 0); + if (!b_vbus_pulse_tmr) + return -ENOMEM; + + return 0; +} + +/* Uninitialize timers */ +void fsl_otg_uninit_timers(void) +{ + /* FSM used timers */ + kfree(a_wait_vrise_tmr); + kfree(a_wait_bcon_tmr); + kfree(a_aidl_bdis_tmr); + kfree(b_ase0_brst_tmr); + kfree(b_se0_srp_tmr); + kfree(b_srp_fail_tmr); + kfree(a_wait_enum_tmr); + + /* device driver used timers */ + kfree(b_srp_wait_tmr); + kfree(b_data_pulse_tmr); + kfree(b_vbus_pulse_tmr); +} + +/* Add timer to timer list */ +void fsl_otg_add_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer; + + /* + * Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +void fsl_otg_del_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer, *del_tmp; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); +} + +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by fsl_otg 1ms timer interrupt + */ +int fsl_otg_tick_timer(void) +{ + struct fsl_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + return expired; +} + +/* Reset controller, not reset the bus */ +void otg_reset_controller(void) +{ + u32 command; + + command = fsl_readl(&usb_dr_regs->usbcmd); + command |= (1 << 1); + fsl_writel(command, &usb_dr_regs->usbcmd); + while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) + ; +} + +/* Call suspend/resume routines in host driver */ +int fsl_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct usb_otg *otg = fsm->otg; + struct device *dev; + struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy); + u32 retval = 0; + + if (!otg->host) + return -ENODEV; + dev = otg->host->controller; + + /* + * Update a_vbus_vld state as a_vbus_vld int is disabled + * in device mode + */ + fsm->a_vbus_vld = + !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); + if (on) { + /* start fsl usb host controller */ + if (otg_dev->host_working) + goto end; + else { + otg_reset_controller(); + VDBG("host on......\n"); + if (dev->driver->pm && dev->driver->pm->resume) { + retval = dev->driver->pm->resume(dev); + if (fsm->id) { + /* default-b */ + fsl_otg_drv_vbus(1); + /* + * Workaround: b_host can't driver + * vbus, but PP in PORTSC needs to + * be 1 for host to work. + * So we set drv_vbus bit in + * transceiver to 0 thru ULPI. + */ + write_ulpi(0x0c, 0x20); + } + } + + otg_dev->host_working = 1; + } + } else { + /* stop fsl usb host controller */ + if (!otg_dev->host_working) + goto end; + else { + VDBG("host off......\n"); + if (dev && dev->driver) { + if (dev->driver->pm && dev->driver->pm->suspend) + retval = dev->driver->pm->suspend(dev); + if (fsm->id) + /* default-b */ + fsl_otg_drv_vbus(0); + } + otg_dev->host_working = 0; + } + } +end: + return retval; +} + +/* + * Call suspend and resume function in udc driver + * to stop and start udc driver. + */ +int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct usb_otg *otg = fsm->otg; + struct device *dev; + + if (!otg->gadget || !otg->gadget->dev.parent) + return -ENODEV; + + VDBG("gadget %s\n", on ? "on" : "off"); + dev = otg->gadget->dev.parent; + + if (on) { + if (dev->driver->resume) + dev->driver->resume(dev); + } else { + if (dev->driver->suspend) + dev->driver->suspend(dev, otg_suspend_state); + } + + return 0; +} + +/* + * Called by initialization code of host driver. Register host controller + * to the OTG. Suspend host for OTG role detection. + */ +static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + otg->host = host; + + otg_dev->fsm.a_bus_drop = 0; + otg_dev->fsm.a_bus_req = 1; + + if (host) { + VDBG("host off......\n"); + + otg->host->otg_port = fsl_otg_initdata.otg_port; + otg->host->is_b_host = otg_dev->fsm.id; + /* + * must leave time for khubd to finish its thing + * before yanking the host driver out from under it, + * so suspend the host after a short delay. + */ + otg_dev->host_working = 1; + schedule_delayed_work(&otg_dev->otg_event, 100); + return 0; + } else { + /* host driver going away */ + if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & + OTGSC_STS_USB_ID)) { + /* Mini-A cable connected */ + struct otg_fsm *fsm = &otg_dev->fsm; + + otg->phy->state = OTG_STATE_UNDEFINED; + fsm->protocol = PROTO_UNDEF; + } + } + + otg_dev->host_working = 0; + + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* Called by initialization code of udc. Register udc to OTG. */ +static int fsl_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + VDBG("otg_dev 0x%x\n", (int)otg_dev); + VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + if (!gadget) { + if (!otg->default_a) + otg->gadget->ops->vbus_draw(otg->gadget, 0); + usb_gadget_vbus_disconnect(otg->gadget); + otg->gadget = 0; + otg_dev->fsm.b_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + return 0; + } + + otg->gadget = gadget; + otg->gadget->is_a_peripheral = !otg_dev->fsm.id; + + otg_dev->fsm.b_bus_req = 1; + + /* start the gadget right away if the ID pin says Mini-B */ + DBG("ID pin=%d\n", otg_dev->fsm.id); + if (otg_dev->fsm.id == 1) { + fsl_otg_start_host(&otg_dev->fsm, 0); + otg_drv_vbus(&otg_dev->fsm, 0); + fsl_otg_start_gadget(&otg_dev->fsm, 1); + } + + return 0; +} + +/* Set OTG port power, only for B-device */ +static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA) +{ + if (!fsl_otg_dev) + return -ENODEV; + if (phy->state == OTG_STATE_B_PERIPHERAL) + pr_info("FSL OTG: Draw %d mA\n", mA); + + return 0; +} + +/* + * Delayed pin detect interrupt processing. + * + * When the Mini-A cable is disconnected from the board, + * the pin-detect interrupt happens before the disconnect + * interrupts for the connected device(s). In order to + * process the disconnect interrupt(s) prior to switching + * roles, the pin-detect interrupts are delayed, and handled + * by this routine. + */ +static void fsl_otg_event(struct work_struct *work) +{ + struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); + struct otg_fsm *fsm = &og->fsm; + + if (fsm->id) { /* switch to gadget */ + fsl_otg_start_host(fsm, 0); + otg_drv_vbus(fsm, 0); + fsl_otg_start_gadget(fsm, 1); + } +} + +/* B-device start SRP */ +static int fsl_otg_start_srp(struct usb_otg *otg) +{ + struct fsl_otg *otg_dev; + + if (!otg || otg->phy->state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + otg_dev->fsm.b_bus_req = 1; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* A_host suspend will call this function to start hnp */ +static int fsl_otg_start_hnp(struct usb_otg *otg) +{ + struct fsl_otg *otg_dev; + + if (!otg) + return -ENODEV; + + otg_dev = container_of(otg->phy, struct fsl_otg, phy); + if (otg_dev != fsl_otg_dev) + return -ENODEV; + + DBG("start_hnp...n"); + + /* clear a_bus_req to enter a_suspend state */ + otg_dev->fsm.a_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* + * Interrupt handler. OTG/host/peripheral share the same int line. + * OTG driver clears OTGSC interrupts and leaves USB interrupts + * intact. It needs to have knowledge of some USB interrupts + * such as port change. + */ +irqreturn_t fsl_otg_isr(int irq, void *dev_id) +{ + struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; + struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; + u32 otg_int_src, otg_sc; + + otg_sc = fsl_readl(&usb_dr_regs->otgsc); + otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); + + /* Only clear otg interrupts */ + fsl_writel(otg_sc, &usb_dr_regs->otgsc); + + /*FIXME: ID change not generate when init to 0 */ + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + + /* process OTG interrupts */ + if (otg_int_src) { + if (otg_int_src & OTGSC_INTSTS_USB_ID) { + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + /* clear conn information */ + if (fsm->id) + fsm->b_conn = 0; + else + fsm->a_conn = 0; + + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; + VDBG("ID int (ID is %d)\n", fsm->id); + + if (fsm->id) { /* switch to gadget */ + schedule_delayed_work( + &((struct fsl_otg *)dev_id)->otg_event, + 100); + } else { /* switch to host */ + cancel_delayed_work(& + ((struct fsl_otg *)dev_id)-> + otg_event); + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); + } + return IRQ_HANDLED; + } + } + return IRQ_NONE; +} + +static struct otg_fsm_ops fsl_otg_ops = { + .chrg_vbus = fsl_otg_chrg_vbus, + .drv_vbus = fsl_otg_drv_vbus, + .loc_conn = fsl_otg_loc_conn, + .loc_sof = fsl_otg_loc_sof, + .start_pulse = fsl_otg_start_pulse, + + .add_timer = fsl_otg_add_timer, + .del_timer = fsl_otg_del_timer, + + .start_host = fsl_otg_start_host, + .start_gadget = fsl_otg_start_gadget, +}; + +/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ +static int fsl_otg_conf(struct platform_device *pdev) +{ + struct fsl_otg *fsl_otg_tc; + int status; + + if (fsl_otg_dev) + return 0; + + /* allocate space to fsl otg device */ + fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); + if (!fsl_otg_tc) + return -ENOMEM; + + fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!fsl_otg_tc->phy.otg) { + kfree(fsl_otg_tc); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); + + INIT_LIST_HEAD(&active_timers); + status = fsl_otg_init_timers(&fsl_otg_tc->fsm); + if (status) { + pr_info("Couldn't init OTG timers\n"); + goto err; + } + spin_lock_init(&fsl_otg_tc->fsm.lock); + + /* Set OTG state machine operations */ + fsl_otg_tc->fsm.ops = &fsl_otg_ops; + + /* initialize the otg structure */ + fsl_otg_tc->phy.label = DRIVER_DESC; + fsl_otg_tc->phy.set_power = fsl_otg_set_power; + + fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy; + fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host; + fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral; + fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp; + fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp; + + fsl_otg_dev = fsl_otg_tc; + + /* Store the otg transceiver */ + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); + if (status) { + pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); + goto err; + } + + return 0; +err: + fsl_otg_uninit_timers(); + kfree(fsl_otg_tc->phy.otg); + kfree(fsl_otg_tc); + return status; +} + +/* OTG Initialization */ +int usb_otg_start(struct platform_device *pdev) +{ + struct fsl_otg *p_otg; + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); + struct otg_fsm *fsm; + int status; + struct resource *res; + u32 temp; + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + p_otg = container_of(otg_trans, struct fsl_otg, phy); + fsm = &p_otg->fsm; + + /* Initialize the state machine structure with default values */ + SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); + fsm->otg = p_otg->phy.otg; + + /* We don't require predefined MEM/IRQ resource index */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + /* We don't request_mem_region here to enable resource sharing + * with host/device */ + + usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); + p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; + pdata->regs = (void *)usb_dr_regs; + + if (pdata->init && pdata->init(pdev) != 0) + return -EINVAL; + + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + + /* request irq */ + p_otg->irq = platform_get_irq(pdev, 0); + status = request_irq(p_otg->irq, fsl_otg_isr, + IRQF_SHARED, driver_name, p_otg); + if (status) { + dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n", + p_otg->irq, status); + iounmap(p_otg->dr_mem_map); + kfree(p_otg->phy.otg); + kfree(p_otg); + return status; + } + + /* stop the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp &= ~USB_CMD_RUN_STOP; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* reset the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp |= USB_CMD_CTRL_RESET; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* wait reset completed */ + while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) + ; + + /* configure the VBUSHS as IDLE(both host and device) */ + temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); + fsl_writel(temp, &p_otg->dr_mem_map->usbmode); + + /* configure PHY interface */ + temp = fsl_readl(&p_otg->dr_mem_map->portsc); + temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); + switch (pdata->phy_mode) { + case FSL_USB2_PHY_ULPI: + temp |= PORTSC_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI_WIDE: + temp |= PORTSC_PTW_16BIT; + /* fall through */ + case FSL_USB2_PHY_UTMI: + temp |= PORTSC_PTS_UTMI; + /* fall through */ + default: + break; + } + fsl_writel(temp, &p_otg->dr_mem_map->portsc); + + if (pdata->have_sysif_regs) { + /* configure control enable IO output, big endian register */ + temp = __raw_readl(&p_otg->dr_mem_map->control); + temp |= USB_CTRL_IOENB; + __raw_writel(temp, &p_otg->dr_mem_map->control); + } + + /* disable all interrupt and clear all OTGSC status */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + /* + * The identification (id) input is FALSE when a Mini-A plug is inserted + * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. + * Also: record initial state of ID pin + */ + if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { + p_otg->phy.state = OTG_STATE_UNDEFINED; + p_otg->fsm.id = 1; + } else { + p_otg->phy.state = OTG_STATE_A_IDLE; + p_otg->fsm.id = 0; + } + + DBG("initial ID pin=%d\n", p_otg->fsm.id); + + /* enable OTG ID pin interrupt */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp |= OTGSC_INTR_USB_ID_EN; + temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + return 0; +} + +/* + * state file in sysfs + */ +static int show_fsl_usb2_otg_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct otg_fsm *fsm = &fsl_otg_dev->fsm; + char *next = buf; + unsigned size = PAGE_SIZE; + unsigned long flags; + int t; + + spin_lock_irqsave(&fsm->lock, flags); + + /* basic driver infomation */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", + DRIVER_VERSION); + size -= t; + next += t; + + /* Registers */ + t = scnprintf(next, size, + "OTGSC: 0x%08x\n" + "PORTSC: 0x%08x\n" + "USBMODE: 0x%08x\n" + "USBCMD: 0x%08x\n" + "USBSTS: 0x%08x\n" + "USBINTR: 0x%08x\n", + fsl_readl(&usb_dr_regs->otgsc), + fsl_readl(&usb_dr_regs->portsc), + fsl_readl(&usb_dr_regs->usbmode), + fsl_readl(&usb_dr_regs->usbcmd), + fsl_readl(&usb_dr_regs->usbsts), + fsl_readl(&usb_dr_regs->usbintr)); + size -= t; + next += t; + + /* State */ + t = scnprintf(next, size, + "OTG state: %s\n\n", + usb_otg_state_string(fsl_otg_dev->phy.state)); + size -= t; + next += t; + + /* State Machine Variables */ + t = scnprintf(next, size, + "a_bus_req: %d\n" + "b_bus_req: %d\n" + "a_bus_resume: %d\n" + "a_bus_suspend: %d\n" + "a_conn: %d\n" + "a_sess_vld: %d\n" + "a_srp_det: %d\n" + "a_vbus_vld: %d\n" + "b_bus_resume: %d\n" + "b_bus_suspend: %d\n" + "b_conn: %d\n" + "b_se0_srp: %d\n" + "b_sess_end: %d\n" + "b_sess_vld: %d\n" + "id: %d\n", + fsm->a_bus_req, + fsm->b_bus_req, + fsm->a_bus_resume, + fsm->a_bus_suspend, + fsm->a_conn, + fsm->a_sess_vld, + fsm->a_srp_det, + fsm->a_vbus_vld, + fsm->b_bus_resume, + fsm->b_bus_suspend, + fsm->b_conn, + fsm->b_se0_srp, + fsm->b_sess_end, + fsm->b_sess_vld, + fsm->id); + size -= t; + next += t; + + spin_unlock_irqrestore(&fsm->lock, flags); + + return PAGE_SIZE - size; +} + +static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); + + +/* Char driver interface to control some OTG input */ + +/* + * Handle some ioctl command, such as get otg + * status and set host suspend + */ +static long fsl_otg_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u32 retval = 0; + + switch (cmd) { + case GET_OTG_STATUS: + retval = fsl_otg_dev->host_working; + break; + + case SET_A_SUSPEND_REQ: + fsl_otg_dev->fsm.a_suspend_req = arg; + break; + + case SET_A_BUS_DROP: + fsl_otg_dev->fsm.a_bus_drop = arg; + break; + + case SET_A_BUS_REQ: + fsl_otg_dev->fsm.a_bus_req = arg; + break; + + case SET_B_BUS_REQ: + fsl_otg_dev->fsm.b_bus_req = arg; + break; + + default: + break; + } + + otg_statemachine(&fsl_otg_dev->fsm); + + return retval; +} + +static int fsl_otg_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int fsl_otg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations otg_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .unlocked_ioctl = fsl_otg_ioctl, + .open = fsl_otg_open, + .release = fsl_otg_release, +}; + +static int fsl_otg_probe(struct platform_device *pdev) +{ + int ret; + + if (!pdev->dev.platform_data) + return -ENODEV; + + /* configure the OTG */ + ret = fsl_otg_conf(pdev); + if (ret) { + dev_err(&pdev->dev, "Couldn't configure OTG module\n"); + return ret; + } + + /* start OTG */ + ret = usb_otg_start(pdev); + if (ret) { + dev_err(&pdev->dev, "Can't init FSL OTG device\n"); + return ret; + } + + ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); + if (ret) { + dev_err(&pdev->dev, "unable to register FSL OTG device\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + if (ret) + dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); + + return ret; +} + +static int fsl_otg_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + usb_remove_phy(&fsl_otg_dev->phy); + free_irq(fsl_otg_dev->irq, fsl_otg_dev); + + iounmap((void *)usb_dr_regs); + + fsl_otg_uninit_timers(); + kfree(fsl_otg_dev->phy.otg); + kfree(fsl_otg_dev); + + device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + + unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); + + if (pdata->exit) + pdata->exit(pdev); + + return 0; +} + +struct platform_driver fsl_otg_driver = { + .probe = fsl_otg_probe, + .remove = fsl_otg_remove, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(fsl_otg_driver); + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h new file mode 100644 index 0000000..ca26628 --- /dev/null +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -0,0 +1,406 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "otg_fsm.h" +#include +#include + +/* USB Command Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x1<<0) +#define USB_CMD_CTRL_RESET (0x1<<1) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) +#define USB_CMD_INT_AA_DOORBELL (0x1<<6) +#define USB_CMD_ASP (0x3<<8) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) +#define USB_CMD_SUTW (0x1<<13) +#define USB_CMD_ATDTW (0x1<<14) +#define USB_CMD_ITC (0xFF<<16) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) +#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x0<<8) +#define USB_CMD_ASP_01 (0x1<<8) +#define USB_CMD_ASP_10 (0x2<<8) +#define USB_CMD_ASP_11 (0x3<<8) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) +#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) +#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) +#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) +#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) +#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) +#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) +#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB Status Register Bit Masks */ +#define USB_STS_INT (0x1<<0) +#define USB_STS_ERR (0x1<<1) +#define USB_STS_PORT_CHANGE (0x1<<2) +#define USB_STS_FRM_LST_ROLL (0x1<<3) +#define USB_STS_SYS_ERR (0x1<<4) +#define USB_STS_IAA (0x1<<5) +#define USB_STS_RESET_RECEIVED (0x1<<6) +#define USB_STS_SOF (0x1<<7) +#define USB_STS_DCSUSPEND (0x1<<8) +#define USB_STS_HC_HALTED (0x1<<12) +#define USB_STS_RCL (0x1<<13) +#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) +#define USB_STS_ASYNC_SCHEDULE (0x1<<15) + +/* USB Interrupt Enable Register Bit Masks */ +#define USB_INTR_INT_EN (0x1<<0) +#define USB_INTR_ERR_INT_EN (0x1<<1) +#define USB_INTR_PC_DETECT_EN (0x1<<2) +#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) +#define USB_INTR_SYS_ERR_EN (0x1<<4) +#define USB_INTR_ASYN_ADV_EN (0x1<<5) +#define USB_INTR_RESET_EN (0x1<<6) +#define USB_INTR_SOF_EN (0x1<<7) +#define USB_INTR_DEVICE_SUSPEND (0x1<<8) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) +#define USB_DEVICE_ADDRESS_BIT_POS (25) +/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ +#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) +#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) +#define PORTSC_PORT_ENABLE (0x1<<2) +#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) +#define PORTSC_OVER_CURRENT_ACT (0x1<<4) +#define PORTSC_OVER_CUURENT_CHG (0x1<<5) +#define PORTSC_PORT_FORCE_RESUME (0x1<<6) +#define PORTSC_PORT_SUSPEND (0x1<<7) +#define PORTSC_PORT_RESET (0x1<<8) +#define PORTSC_LINE_STATUS_BITS (0x3<<10) +#define PORTSC_PORT_POWER (0x1<<12) +#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) +#define PORTSC_PORT_TEST_CTRL (0xF<<16) +#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) +#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) +#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) +#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) +#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) +#define PORTSC_PORT_SPEED_MASK (0x3<<26) +#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) +#define PORTSC_PHY_TYPE_SEL (0x3<<30) +/* bit 11-10 are line status */ +#define PORTSC_LINE_STATUS_SE0 (0x0<<10) +#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) +#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) +#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) +#define PORTSC_LINE_STATUS_BIT_POS (10) + +/* bit 15-14 are port indicator control */ +#define PORTSC_PIC_OFF (0x0<<14) +#define PORTSC_PIC_AMBER (0x1<<14) +#define PORTSC_PIC_GREEN (0x2<<14) +#define PORTSC_PIC_UNDEF (0x3<<14) +#define PORTSC_PIC_BIT_POS (14) + +/* bit 19-16 are port test control */ +#define PORTSC_PTC_DISABLE (0x0<<16) +#define PORTSC_PTC_JSTATE (0x1<<16) +#define PORTSC_PTC_KSTATE (0x2<<16) +#define PORTSC_PTC_SEQNAK (0x3<<16) +#define PORTSC_PTC_PACKET (0x4<<16) +#define PORTSC_PTC_FORCE_EN (0x5<<16) +#define PORTSC_PTC_BIT_POS (16) + +/* bit 27-26 are port speed */ +#define PORTSC_PORT_SPEED_FULL (0x0<<26) +#define PORTSC_PORT_SPEED_LOW (0x1<<26) +#define PORTSC_PORT_SPEED_HIGH (0x2<<26) +#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) +#define PORTSC_SPEED_BIT_POS (26) + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSC_PTW (0x1<<28) +#define PORTSC_PTW_8BIT (0x0<<28) +#define PORTSC_PTW_16BIT (0x1<<28) + +/* bit 31-30 are port transceiver select */ +#define PORTSC_PTS_UTMI (0x0<<30) +#define PORTSC_PTS_ULPI (0x2<<30) +#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) +#define PORTSC_PTS_BIT_POS (30) + +#define PORTSC_W1C_BITS \ + (PORTSC_CONNECT_STATUS_CHANGE | \ + PORTSC_PORT_EN_DIS_CHANGE | \ + PORTSC_OVER_CUURENT_CHG) + +/* OTG Status Control Register Bit Masks */ +#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) +#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) +#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) +#define OTGSC_CTRL_DATA_PULSING (0x1<<4) +#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) +#define OTGSC_HA_DATA_PULSE (0x1<<6) +#define OTGSC_HA_BA (0x1<<7) +#define OTGSC_STS_USB_ID (0x1<<8) +#define OTGSC_STS_A_VBUS_VALID (0x1<<9) +#define OTGSC_STS_A_SESSION_VALID (0x1<<10) +#define OTGSC_STS_B_SESSION_VALID (0x1<<11) +#define OTGSC_STS_B_SESSION_END (0x1<<12) +#define OTGSC_STS_1MS_TOGGLE (0x1<<13) +#define OTGSC_STS_DATA_PULSING (0x1<<14) +#define OTGSC_INTSTS_USB_ID (0x1<<16) +#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) +#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) +#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) +#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) +#define OTGSC_INTSTS_1MS (0x1<<21) +#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) +#define OTGSC_INTR_USB_ID_EN (0x1<<24) +#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) +#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) +#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) +#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) +#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) +#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) +#define OTGSC_INTSTS_MASK (0x00ff0000) + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) +#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) +#define USB_MODE_CTRL_MODE_HOST (0x3<<0) +#define USB_MODE_CTRL_MODE_RSV (0x1<<0) +#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) +#define USB_MODE_STREAM_DISABLE (0x1<<4) +#define USB_MODE_ES (0x1<<2) /* Endian Select */ + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB (0x1<<2) +#define USB_CTRL_ULPI_INT0EN (0x1<<0) + +/* BCSR5 */ +#define BCSR5_INT_USB (0x02) + +/* USB module clk cfg */ +#define SCCR_OFFS (0xA08) +#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ +#define SCCR_USB_MPHCM_11 (0x00c00000) +#define SCCR_USB_MPHCM_01 (0x00400000) +#define SCCR_USB_MPHCM_10 (0x00800000) +#define SCCR_USB_DRCM_11 (0x00300000) +#define SCCR_USB_DRCM_01 (0x00100000) +#define SCCR_USB_DRCM_10 (0x00200000) + +#define SICRL_OFFS (0x114) +#define SICRL_USB0 (0x40000000) +#define SICRL_USB1 (0x20000000) + +#define SICRH_OFFS (0x118) +#define SICRH_USB_UTMI (0x00020000) + +/* OTG interrupt enable bit masks */ +#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ + (OTGSC_INTR_USB_ID_EN | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTR_A_VBUS_VALID_EN | \ + OTGSC_INTR_A_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_END_EN | \ + OTGSC_INTR_DATA_PULSING_EN) + +/* OTG interrupt status bit masks */ +#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ + (OTGSC_INTSTS_USB_ID | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTSTS_A_VBUS_VALID | \ + OTGSC_INTSTS_A_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_END | \ + OTGSC_INTSTS_DATA_PULSING) + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ + +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 + * This is only used to get out of + * OTG_STATE_A_WAIT_BCON state if there was + * no connection for these many milliseconds + */ + +/* A-Idle to B-Disconnect */ +/* It is necessary for this timer to be more than 750 ms because of a bug in OPT + * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated + * in the test description + */ +#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ + +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ + +/* B-device timing constants */ + + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ +#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ +#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ + +/* SRP Initiate Time */ +#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ + +/* SRP Fail Time */ +#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ + +/* SRP result wait time */ +#define TB_SRP_WAIT (60) + +/* VBus time */ +#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ + +/* Discharge time */ +/* This time should be less than 10ms. It varies from system to system. */ +#define TB_VBUS_DSCHRG (8) + +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ + +/* A bus suspend timer before we can switch to b_wait_aconn */ +#define TB_A_SUSPEND (7) +#define TB_BUS_RESUME (12) + +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ + +#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) + +struct usb_dr_mmap { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[8]; + u32 ulpiview; /* ULPI register access */ + u8 res7[12]; + u32 configflag; /* Configure Flag Register */ + u32 portsc; /* Port 1 Status and Control Register */ + u8 res8[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ + u8 res9[552]; + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res10[236]; + u32 control; /* General Purpose Control Register */ +}; + +struct fsl_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +inline struct fsl_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct fsl_otg_timer *timer; + + timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + if (!timer) + return NULL; + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +struct fsl_otg { + struct usb_phy phy; + struct otg_fsm fsm; + struct usb_dr_mmap *dr_mem_map; + struct delayed_work otg_event; + + /* used for usb host */ + struct work_struct work_wq; + u8 host_working; + + int irq; +}; + +struct fsl_otg_config { + u8 otg_port; +}; + +/* For SRP and HNP handle */ +#define FSL_OTG_MAJOR 240 +#define FSL_OTG_NAME "fsl-usb2-otg" +/* Command to OTG driver ioctl */ +#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR +/* if otg work as host, it should return 1, otherwise return 0 */ +#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) +#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) +#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) +#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) +#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) +#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) +#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) +#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) +#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) + +void fsl_otg_add_timer(void *timer); +void fsl_otg_del_timer(void *timer); +void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c new file mode 100644 index 0000000..c520b35 --- /dev/null +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -0,0 +1,348 @@ +/* + * OTG Finite State Machine from OTG spec + * + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * Author: Li Yang + * Jerry Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "phy-otg-fsm.h" + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", + fsm->protocol, protocol); + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 0); + if (ret) + return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 1); + if (ret) + return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed; + +/* Called when leaving a state. Do state clean up jobs here */ +void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, b_se0_srp_tmr); + fsm->b_se0_srp = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, b_ase0_brst_tmr); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, a_wait_vrise_tmr); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, a_wait_bcon_tmr); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, a_aidl_bdis_tmr); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req = 0; + break; + case OTG_STATE_A_PERIPHERAL: + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + +/* Called when entering a state */ +int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->otg->phy->state == new_state) + return 0; + VDBG("Set state: %s\n", usb_otg_state_string(new_state)); + otg_leave_state(fsm, fsm->otg->phy->state); + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_se0_srp_tmr); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_srp_fail_tmr); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, b_ase0_brst_tmr); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->otg->host, + fsm->otg->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_bcon_tmr); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* + * When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req) + otg_add_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_aidl_bdis_tmr); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->otg->phy->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + unsigned long flags; + + spin_lock_irqsave(&fsm->lock, flags); + + state = fsm->otg->phy->state; + state_changed = 0; + /* State machine state change judgement */ + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d\n", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->otg->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && fsm->otg-> + gadget->b_hnp_enable && fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || + fsm->a_wait_vrise_tmout) { + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if ((!fsm->a_bus_req || fsm->a_suspend_req) && + fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->otg->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && + !fsm->b_conn)) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + spin_unlock_irqrestore(&fsm->lock, flags); + + VDBG("quit statemachine, changed = %d\n", state_changed); + return state_changed; +} diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h new file mode 100644 index 0000000..c30a2e1 --- /dev/null +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef DEBUG +#undef VERBOSE + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ + __func__, ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do {} while (0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + struct otg_fsm_ops *ops; + struct usb_otg *otg; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + spinlock_t lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)(int on); + void (*drv_vbus)(int on); + void (*loc_conn)(int on); + void (*loc_sof)(int on); + void (*start_pulse)(void); + void (*add_timer)(void *timer); + void (*del_timer)(void *timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + fsm->ops->chrg_vbus(on); +} + +static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(on); + } +} + +static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(on); + } +} + +static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(on); + } +} + +static inline void otg_start_pulse(struct otg_fsm *fsm) +{ + fsm->ops->start_pulse(); +} + +static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->add_timer(timer); +} + +static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->del_timer(timer); +} + +int otg_statemachine(struct otg_fsm *fsm); + +/* Defined by device specific driver, for different timer implementation */ +extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, + *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, + *a_wait_enum_tmr; diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c new file mode 100644 index 0000000..a7d4ac5 --- /dev/null +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -0,0 +1,416 @@ +/* + * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices + * + * Copyright (c) 2008 Philipp Zabel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +/* + * A simple GPIO VBUS sensing driver for B peripheral only devices + * with internal transceivers. It can control a D+ pullup GPIO and + * a regulator to limit the current drawn from VBUS. + * + * Needs to be loaded before the UDC driver that will use it. + */ +struct gpio_vbus_data { + struct usb_phy phy; + struct device *dev; + struct regulator *vbus_draw; + int vbus_draw_enabled; + unsigned mA; + struct delayed_work work; + int vbus; + int irq; +}; + + +/* + * This driver relies on "both edges" triggering. VBUS has 100 msec to + * stabilize, so the peripheral controller driver may need to cope with + * some bouncing due to current surges (e.g. charging local capacitance) + * and contact chatter. + * + * REVISIT in desperate straits, toggling between rising and falling + * edges might be workable. + */ +#define VBUS_IRQ_FLAGS \ + (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) + + +/* interface to regulator framework */ +static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) +{ + struct regulator *vbus_draw = gpio_vbus->vbus_draw; + int enabled; + + if (!vbus_draw) + return; + + enabled = gpio_vbus->vbus_draw_enabled; + if (mA) { + regulator_set_current_limit(vbus_draw, 0, 1000 * mA); + if (!enabled) { + regulator_enable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 1; + } + } else { + if (enabled) { + regulator_disable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 0; + } + } + gpio_vbus->mA = mA; +} + +static int is_vbus_powered(struct gpio_vbus_mach_info *pdata) +{ + int vbus; + + vbus = gpio_get_value(pdata->gpio_vbus); + if (pdata->gpio_vbus_inverted) + vbus = !vbus; + + return vbus; +} + +static void gpio_vbus_work(struct work_struct *work) +{ + struct gpio_vbus_data *gpio_vbus = + container_of(work, struct gpio_vbus_data, work.work); + struct gpio_vbus_mach_info *pdata = gpio_vbus->dev->platform_data; + int gpio, status, vbus; + + if (!gpio_vbus->phy.otg->gadget) + return; + + vbus = is_vbus_powered(pdata); + if ((vbus ^ gpio_vbus->vbus) == 0) + return; + gpio_vbus->vbus = vbus; + + /* Peripheral controllers which manage the pullup themselves won't have + * gpio_pullup configured here. If it's configured here, we'll do what + * isp1301_omap::b_peripheral() does and enable the pullup here... although + * that may complicate usb_gadget_{,dis}connect() support. + */ + gpio = pdata->gpio_pullup; + + if (vbus) { + status = USB_EVENT_VBUS; + gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL; + gpio_vbus->phy.last_event = status; + usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget); + + /* drawing a "unit load" is *always* OK, except for OTG */ + set_vbus_draw(gpio_vbus, 100); + + /* optionally enable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, !pdata->gpio_pullup_inverted); + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); + } else { + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget); + status = USB_EVENT_NONE; + gpio_vbus->phy.state = OTG_STATE_B_IDLE; + gpio_vbus->phy.last_event = status; + + atomic_notifier_call_chain(&gpio_vbus->phy.notifier, + status, gpio_vbus->phy.otg->gadget); + } +} + +/* VBUS change IRQ handler */ +static irqreturn_t gpio_vbus_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct usb_otg *otg = gpio_vbus->phy.otg; + + dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", + is_vbus_powered(pdata) ? "supplied" : "inactive", + otg->gadget ? otg->gadget->name : "none"); + + if (otg->gadget) + schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +/* OTG transceiver interface */ + +/* bind/unbind the peripheral controller */ +static int gpio_vbus_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct gpio_vbus_data *gpio_vbus; + struct gpio_vbus_mach_info *pdata; + struct platform_device *pdev; + int gpio; + + gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy); + pdev = to_platform_device(gpio_vbus->dev); + pdata = gpio_vbus->dev->platform_data; + gpio = pdata->gpio_pullup; + + if (!gadget) { + dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", + otg->gadget->name); + + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(otg->gadget); + otg->phy->state = OTG_STATE_UNDEFINED; + + otg->gadget = NULL; + return 0; + } + + otg->gadget = gadget; + dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); + + /* initialize connection state */ + gpio_vbus->vbus = 0; /* start with disconnected */ + gpio_vbus_irq(gpio_vbus->irq, pdev); + return 0; +} + +/* effective for B devices, ignored for A-peripheral */ +static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); + + if (phy->state == OTG_STATE_B_PERIPHERAL) + set_vbus_draw(gpio_vbus, mA); + return 0; +} + +/* for non-OTG B devices: set/clear transceiver suspend mode */ +static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(phy, struct gpio_vbus_data, phy); + + /* draw max 0 mA from vbus in suspend mode; or the previously + * recorded amount of current if not suspended + * + * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA + * if they're wake-enabled ... we don't handle that yet. + */ + return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA); +} + +/* platform driver interface */ + +static int __init gpio_vbus_probe(struct platform_device *pdev) +{ + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus; + struct resource *res; + int err, gpio, irq; + unsigned long irqflags; + + if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) + return -EINVAL; + gpio = pdata->gpio_vbus; + + gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + if (!gpio_vbus) + return -ENOMEM; + + gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!gpio_vbus->phy.otg) { + kfree(gpio_vbus); + return -ENOMEM; + } + + platform_set_drvdata(pdev, gpio_vbus); + gpio_vbus->dev = &pdev->dev; + gpio_vbus->phy.label = "gpio-vbus"; + gpio_vbus->phy.set_power = gpio_vbus_set_power; + gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend; + gpio_vbus->phy.state = OTG_STATE_UNDEFINED; + + gpio_vbus->phy.otg->phy = &gpio_vbus->phy; + gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; + + err = gpio_request(gpio, "vbus_detect"); + if (err) { + dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", + gpio, err); + goto err_gpio; + } + gpio_direction_input(gpio); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) { + irq = res->start; + irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; + } else { + irq = gpio_to_irq(gpio); + irqflags = VBUS_IRQ_FLAGS; + } + + gpio_vbus->irq = irq; + + /* if data line pullup is in use, initialize it to "not pulling up" */ + gpio = pdata->gpio_pullup; + if (gpio_is_valid(gpio)) { + err = gpio_request(gpio, "udc_pullup"); + if (err) { + dev_err(&pdev->dev, + "can't request pullup gpio %d, err: %d\n", + gpio, err); + gpio_free(pdata->gpio_vbus); + goto err_gpio; + } + gpio_direction_output(gpio, pdata->gpio_pullup_inverted); + } + + err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); + if (err) { + dev_err(&pdev->dev, "can't request irq %i, err: %d\n", + irq, err); + goto err_irq; + } + + ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); + + INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); + + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + + /* only active when a gadget is registered */ + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_otg; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; +err_otg: + regulator_put(gpio_vbus->vbus_draw); + free_irq(irq, pdev); +err_irq: + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(pdata->gpio_vbus); +err_gpio: + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus->phy.otg); + kfree(gpio_vbus); + return err; +} + +static int __exit gpio_vbus_remove(struct platform_device *pdev) +{ + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + int gpio = pdata->gpio_vbus; + + device_init_wakeup(&pdev->dev, 0); + cancel_delayed_work_sync(&gpio_vbus->work); + regulator_put(gpio_vbus->vbus_draw); + + usb_remove_phy(&gpio_vbus->phy); + + free_irq(gpio_vbus->irq, pdev); + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(gpio); + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus->phy.otg); + kfree(gpio_vbus); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_vbus_pm_suspend(struct device *dev) +{ + struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(gpio_vbus->irq); + + return 0; +} + +static int gpio_vbus_pm_resume(struct device *dev) +{ + struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(gpio_vbus->irq); + + return 0; +} + +static const struct dev_pm_ops gpio_vbus_dev_pm_ops = { + .suspend = gpio_vbus_pm_suspend, + .resume = gpio_vbus_pm_resume, +}; +#endif + +/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ + +MODULE_ALIAS("platform:gpio-vbus"); + +static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gpio_vbus_dev_pm_ops, +#endif + }, + .remove = __exit_p(gpio_vbus_remove), +}; + +module_platform_driver_probe(gpio_vbus_driver, gpio_vbus_probe); + +MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); +MODULE_AUTHOR("Philipp Zabel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c new file mode 100644 index 0000000..8fe0c3b --- /dev/null +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -0,0 +1,1656 @@ +/* + * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#ifndef DEBUG +#undef VERBOSE +#endif + + +#define DRIVER_VERSION "24 August 2004" +#define DRIVER_NAME (isp1301_driver.driver.name) + +MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); +MODULE_LICENSE("GPL"); + +struct isp1301 { + struct usb_phy phy; + struct i2c_client *client; + void (*i2c_release)(struct device *dev); + + int irq_type; + + u32 last_otg_ctrl; + unsigned working:1; + + struct timer_list timer; + + /* use keventd context to change the state for us */ + struct work_struct work; + + unsigned long todo; +# define WORK_UPDATE_ISP 0 /* update ISP from OTG */ +# define WORK_UPDATE_OTG 1 /* update OTG from ISP */ +# define WORK_HOST_RESUME 4 /* resume host */ +# define WORK_TIMER 6 /* timer fired */ +# define WORK_STOP 7 /* don't resubmit */ +}; + + +/* bits in OTG_CTRL */ + +#define OTG_XCEIV_OUTPUTS \ + (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) +#define OTG_XCEIV_INPUTS \ + (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) +#define OTG_CTRL_BITS \ + (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP) + /* and OTG_PULLUP is sometimes written */ + +#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \ + OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \ + OTG_CTRL_BITS) + + +/*-------------------------------------------------------------------------*/ + +/* board-specific PM hooks */ + +#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) + +#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) + +#include + +#else + +static inline int tps65010_set_vbus_draw(unsigned mA) +{ + pr_debug("tps65010: draw %d mA (STUB)\n", mA); + return 0; +} + +#endif + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + int status = tps65010_set_vbus_draw(mA); + if (status < 0) + pr_debug(" VBUS %d mA error %d\n", mA, status); +} + +#else + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + /* H4 controls this by DIP switch S2.4; no soft control. + * ON means the charger is always enabled. Leave it OFF + * unless the OTG port is used only in B-peripheral mode. + */ +} + +#endif + +static void enable_vbus_source(struct isp1301 *isp) +{ + /* this board won't supply more than 8mA vbus power. + * some boards can switch a 100ma "unit load" (or more). + */ +} + + +/* products will deliver OTG messages with LEDs, GUI, etc */ +static inline void notresponding(struct isp1301 *isp) +{ + printk(KERN_NOTICE "OTG device not responding.\n"); +} + + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver isp1301_driver; + +/* smbus apis are used for portability */ + +static inline u8 +isp1301_get_u8(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_byte_data(isp->client, reg + 0); +} + +static inline int +isp1301_get_u16(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_word_data(isp->client, reg); +} + +static inline int +isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(isp->client, reg + 0, bits); +} + +static inline int +isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(isp->client, reg + 1, bits); +} + +/*-------------------------------------------------------------------------*/ + +/* identification */ +#define ISP1301_VENDOR_ID 0x00 /* u16 read */ +#define ISP1301_PRODUCT_ID 0x02 /* u16 read */ +#define ISP1301_BCD_DEVICE 0x14 /* u16 read */ + +#define I2C_VENDOR_ID_PHILIPS 0x04cc +#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301 + +/* operational registers */ +#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */ +# define MC1_SPEED (1 << 0) +# define MC1_SUSPEND (1 << 1) +# define MC1_DAT_SE0 (1 << 2) +# define MC1_TRANSPARENT (1 << 3) +# define MC1_BDIS_ACON_EN (1 << 4) +# define MC1_OE_INT_EN (1 << 5) +# define MC1_UART_EN (1 << 6) +# define MC1_MASK 0x7f +#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */ +# define MC2_GLOBAL_PWR_DN (1 << 0) +# define MC2_SPD_SUSP_CTRL (1 << 1) +# define MC2_BI_DI (1 << 2) +# define MC2_TRANSP_BDIR0 (1 << 3) +# define MC2_TRANSP_BDIR1 (1 << 4) +# define MC2_AUDIO_EN (1 << 5) +# define MC2_PSW_EN (1 << 6) +# define MC2_EN2V7 (1 << 7) +#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */ +# define OTG1_DP_PULLUP (1 << 0) +# define OTG1_DM_PULLUP (1 << 1) +# define OTG1_DP_PULLDOWN (1 << 2) +# define OTG1_DM_PULLDOWN (1 << 3) +# define OTG1_ID_PULLDOWN (1 << 4) +# define OTG1_VBUS_DRV (1 << 5) +# define OTG1_VBUS_DISCHRG (1 << 6) +# define OTG1_VBUS_CHRG (1 << 7) +#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */ +# define OTG_B_SESS_END (1 << 6) +# define OTG_B_SESS_VLD (1 << 7) + +#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */ +#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */ + +#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */ +#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */ + +/* same bitfields in all interrupt registers */ +# define INTR_VBUS_VLD (1 << 0) +# define INTR_SESS_VLD (1 << 1) +# define INTR_DP_HI (1 << 2) +# define INTR_ID_GND (1 << 3) +# define INTR_DM_HI (1 << 4) +# define INTR_ID_FLOAT (1 << 5) +# define INTR_BDIS_ACON (1 << 6) +# define INTR_CR_INT (1 << 7) + +/*-------------------------------------------------------------------------*/ + +static inline const char *state_name(struct isp1301 *isp) +{ + return usb_otg_state_string(isp->phy.state); +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: some of this ISP1301 setup is specific to H2 boards; + * not everything is guarded by board-specific checks, or even using + * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. + * + * ALSO: this currently doesn't use ISP1301 low-power modes + * while OTG is running. + */ + +static void power_down(struct isp1301 *isp) +{ + isp->phy.state = OTG_STATE_UNDEFINED; + + // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); + + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +static void power_up(struct isp1301 *isp) +{ + // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); + + /* do this only when cpu is driving transceiver, + * so host won't see a low speed device... + */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +#define NO_HOST_SUSPEND + +static int host_suspend(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->phy.otg->host) + return -ENODEV; + + /* Currently ASSUMES only the OTG port matters; + * other ports could be active... + */ + dev = isp->phy.otg->host->controller; + return dev->driver->suspend(dev, 3, 0); +#endif +} + +static int host_resume(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->phy.otg->host) + return -ENODEV; + + dev = isp->phy.otg->host->controller; + return dev->driver->resume(dev, 0); +#endif +} + +static int gadget_suspend(struct isp1301 *isp) +{ + isp->phy.otg->gadget->b_hnp_enable = 0; + isp->phy.otg->gadget->a_hnp_support = 0; + isp->phy.otg->gadget->a_alt_hnp_support = 0; + return usb_gadget_vbus_disconnect(isp->phy.otg->gadget); +} + +/*-------------------------------------------------------------------------*/ + +#define TIMER_MINUTES 10 +#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ) + +/* Almost all our I2C messaging comes from a work queue's task context. + * NOTE: guaranteeing certain response times might mean we shouldn't + * share keventd's work queue; a realtime task might be safest. + */ +static void isp1301_defer_work(struct isp1301 *isp, int work) +{ + int status; + + if (isp && !test_and_set_bit(work, &isp->todo)) { + (void) get_device(&isp->client->dev); + status = schedule_work(&isp->work); + if (!status && !isp->working) + dev_vdbg(&isp->client->dev, + "work item %d may be lost\n", work); + } +} + +/* called from irq handlers */ +static void a_idle(struct isp1301 *isp, const char *tag) +{ + u32 l; + + if (isp->phy.state == OTG_STATE_A_IDLE) + return; + + isp->phy.otg->default_a = 1; + if (isp->phy.otg->host) { + isp->phy.otg->host->is_b_host = 0; + host_suspend(isp); + } + if (isp->phy.otg->gadget) { + isp->phy.otg->gadget->is_a_peripheral = 1; + gadget_suspend(isp); + } + isp->phy.state = OTG_STATE_A_IDLE; + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + isp->last_otg_ctrl = l; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +/* called from irq handlers */ +static void b_idle(struct isp1301 *isp, const char *tag) +{ + u32 l; + + if (isp->phy.state == OTG_STATE_B_IDLE) + return; + + isp->phy.otg->default_a = 0; + if (isp->phy.otg->host) { + isp->phy.otg->host->is_b_host = 1; + host_suspend(isp); + } + if (isp->phy.otg->gadget) { + isp->phy.otg->gadget->is_a_peripheral = 0; + gadget_suspend(isp); + } + isp->phy.state = OTG_STATE_B_IDLE; + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + isp->last_otg_ctrl = l; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +static void +dump_regs(struct isp1301 *isp, const char *label) +{ +#ifdef DEBUG + u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); + u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + + pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", + omap_readl(OTG_CTRL), label, state_name(isp), + ctrl, status, src); + /* mode control and irq enables don't change much */ +#endif +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OTG + +/* + * The OMAP OTG controller handles most of the OTG state transitions. + * + * We translate isp1301 outputs (mostly voltage comparator status) into + * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state + * flags into isp1301 inputs ... and infer state transitions. + */ + +#ifdef VERBOSE + +static void check_state(struct isp1301 *isp, const char *tag) +{ + enum usb_otg_state state = OTG_STATE_UNDEFINED; + u8 fsm = omap_readw(OTG_TEST) & 0x0ff; + unsigned extra = 0; + + switch (fsm) { + + /* default-b */ + case 0x0: + state = OTG_STATE_B_IDLE; + break; + case 0x3: + case 0x7: + extra = 1; + case 0x1: + state = OTG_STATE_B_PERIPHERAL; + break; + case 0x11: + state = OTG_STATE_B_SRP_INIT; + break; + + /* extra dual-role default-b states */ + case 0x12: + case 0x13: + case 0x16: + extra = 1; + case 0x17: + state = OTG_STATE_B_WAIT_ACON; + break; + case 0x34: + state = OTG_STATE_B_HOST; + break; + + /* default-a */ + case 0x36: + state = OTG_STATE_A_IDLE; + break; + case 0x3c: + state = OTG_STATE_A_WAIT_VFALL; + break; + case 0x7d: + state = OTG_STATE_A_VBUS_ERR; + break; + case 0x9e: + case 0x9f: + extra = 1; + case 0x89: + state = OTG_STATE_A_PERIPHERAL; + break; + case 0xb7: + state = OTG_STATE_A_WAIT_VRISE; + break; + case 0xb8: + state = OTG_STATE_A_WAIT_BCON; + break; + case 0xb9: + state = OTG_STATE_A_HOST; + break; + case 0xba: + state = OTG_STATE_A_SUSPEND; + break; + default: + break; + } + if (isp->phy.state == state && !extra) + return; + pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, + usb_otg_state_string(state), fsm, state_name(isp), + omap_readl(OTG_CTRL)); +} + +#else + +static inline void check_state(struct isp1301 *isp, const char *tag) { } + +#endif + +/* outputs from ISP1301_INTERRUPT_SOURCE */ +static void update_otg1(struct isp1301 *isp, u8 int_src) +{ + u32 otg_ctrl; + + otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + otg_ctrl &= ~OTG_XCEIV_INPUTS; + otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); + + if (int_src & INTR_SESS_VLD) + otg_ctrl |= OTG_ASESSVLD; + else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) { + a_idle(isp, "vfall"); + otg_ctrl &= ~OTG_CTRL_BITS; + } + if (int_src & INTR_VBUS_VLD) + otg_ctrl |= OTG_VBUSVLD; + if (int_src & INTR_ID_GND) { /* default-A */ + if (isp->phy.state == OTG_STATE_B_IDLE + || isp->phy.state + == OTG_STATE_UNDEFINED) { + a_idle(isp, "init"); + return; + } + } else { /* default-B */ + otg_ctrl |= OTG_ID; + if (isp->phy.state == OTG_STATE_A_IDLE + || isp->phy.state == OTG_STATE_UNDEFINED) { + b_idle(isp, "init"); + return; + } + } + omap_writel(otg_ctrl, OTG_CTRL); +} + +/* outputs from ISP1301_OTG_STATUS */ +static void update_otg2(struct isp1301 *isp, u8 otg_status) +{ + u32 otg_ctrl; + + otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + otg_ctrl &= ~OTG_XCEIV_INPUTS; + otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND); + if (otg_status & OTG_B_SESS_VLD) + otg_ctrl |= OTG_BSESSVLD; + else if (otg_status & OTG_B_SESS_END) + otg_ctrl |= OTG_BSESSEND; + omap_writel(otg_ctrl, OTG_CTRL); +} + +/* inputs going to ISP1301 */ +static void otg_update_isp(struct isp1301 *isp) +{ + u32 otg_ctrl, otg_change; + u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; + + otg_ctrl = omap_readl(OTG_CTRL); + otg_change = otg_ctrl ^ isp->last_otg_ctrl; + isp->last_otg_ctrl = otg_ctrl; + otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; + + switch (isp->phy.state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_SRP_INIT: + if (!(otg_ctrl & OTG_PULLUP)) { + // if (otg_ctrl & OTG_B_HNPEN) { + if (isp->phy.otg->gadget->b_hnp_enable) { + isp->phy.state = OTG_STATE_B_WAIT_ACON; + pr_debug(" --> b_wait_acon\n"); + } + goto pulldown; + } +pullup: + set |= OTG1_DP_PULLUP; + clr |= OTG1_DP_PULLDOWN; + break; + case OTG_STATE_A_SUSPEND: + case OTG_STATE_A_PERIPHERAL: + if (otg_ctrl & OTG_PULLUP) + goto pullup; + /* FALLTHROUGH */ + // case OTG_STATE_B_WAIT_ACON: + default: +pulldown: + set |= OTG1_DP_PULLDOWN; + clr |= OTG1_DP_PULLUP; + break; + } + +# define toggle(OTG,ISP) do { \ + if (otg_ctrl & OTG) set |= ISP; \ + else clr |= ISP; \ + } while (0) + + if (!(isp->phy.otg->host)) + otg_ctrl &= ~OTG_DRV_VBUS; + + switch (isp->phy.state) { + case OTG_STATE_A_SUSPEND: + if (otg_ctrl & OTG_DRV_VBUS) { + set |= OTG1_VBUS_DRV; + break; + } + /* HNP failed for some reason (A_AIDL_BDIS timeout) */ + notresponding(isp); + + /* FALLTHROUGH */ + case OTG_STATE_A_VBUS_ERR: + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + pr_debug(" --> a_wait_vfall\n"); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VFALL: + /* FIXME usbcore thinks port power is still on ... */ + clr |= OTG1_VBUS_DRV; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl & OTG_DRV_VBUS) { + isp->phy.state = OTG_STATE_A_WAIT_VRISE; + pr_debug(" --> a_wait_vrise\n"); + } + /* FALLTHROUGH */ + default: + toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); + } + + toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); + toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); + +# undef toggle + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); + + /* HNP switch to host or peripheral; and SRP */ + if (otg_change & OTG_PULLUP) { + u32 l; + + switch (isp->phy.state) { + case OTG_STATE_B_IDLE: + if (clr & OTG1_DP_PULLUP) + break; + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + break; + case OTG_STATE_A_SUSPEND: + if (clr & OTG1_DP_PULLUP) + break; + isp->phy.state = OTG_STATE_A_PERIPHERAL; + pr_debug(" --> a_peripheral\n"); + break; + default: + break; + } + l = omap_readl(OTG_CTRL); + l |= OTG_PULLUP; + omap_writel(l, OTG_CTRL); + } + + check_state(isp, __func__); + dump_regs(isp, "otg->isp1301"); +} + +static irqreturn_t omap_otg_irq(int irq, void *_isp) +{ + u16 otg_irq = omap_readw(OTG_IRQ_SRC); + u32 otg_ctrl; + int ret = IRQ_NONE; + struct isp1301 *isp = _isp; + struct usb_otg *otg = isp->phy.otg; + + /* update ISP1301 transceiver from OTG controller */ + if (otg_irq & OPRT_CHG) { + omap_writew(OPRT_CHG, OTG_IRQ_SRC); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + ret = IRQ_HANDLED; + + /* SRP to become b_peripheral failed */ + } else if (otg_irq & B_SRP_TMROUT) { + pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL)); + notresponding(isp); + + /* gadget drivers that care should monitor all kinds of + * remote wakeup (SRP, normal) using their own timer + * to give "check cable and A-device" messages. + */ + if (isp->phy.state == OTG_STATE_B_SRP_INIT) + b_idle(isp, "srp_timeout"); + + omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* HNP to become b_host failed */ + } else if (otg_irq & B_HNP_FAIL) { + pr_debug("otg: %s B_HNP_FAIL, %06x\n", + state_name(isp), omap_readl(OTG_CTRL)); + notresponding(isp); + + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + + /* subset of b_peripheral()... */ + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + + omap_writew(B_HNP_FAIL, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* detect SRP from B-device ... */ + } else if (otg_irq & A_SRP_DETECT) { + pr_debug("otg: %s SRP_DETECT, %06x\n", + state_name(isp), omap_readl(OTG_CTRL)); + + isp1301_defer_work(isp, WORK_UPDATE_OTG); + switch (isp->phy.state) { + case OTG_STATE_A_IDLE: + if (!otg->host) + break; + isp1301_defer_work(isp, WORK_HOST_RESUME); + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & ~OTG_XCEIV_INPUTS + & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + break; + default: + break; + } + + omap_writew(A_SRP_DETECT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) + * we don't track them separately + */ + } else if (otg_irq & A_REQ_TMROUT) { + otg_ctrl = omap_readl(OTG_CTRL); + pr_info("otg: BCON_TMOUT from %s, %06x\n", + state_name(isp), otg_ctrl); + notresponding(isp); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + + omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* A-supplied voltage fell too low; overcurrent */ + } else if (otg_irq & A_VBUS_ERR) { + otg_ctrl = omap_readl(OTG_CTRL); + printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", + state_name(isp), otg_irq, otg_ctrl); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_A_VBUS_ERR; + + omap_writew(A_VBUS_ERR, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + /* switch driver; the transceiver code activates it, + * ungating the udc clock or resuming OHCI. + */ + } else if (otg_irq & DRIVER_SWITCH) { + int kick = 0; + + otg_ctrl = omap_readl(OTG_CTRL); + printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", + state_name(isp), + (otg_ctrl & OTG_DRIVER_SEL) + ? "gadget" : "host", + otg_ctrl); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is peripheral */ + if (otg_ctrl & OTG_DRIVER_SEL) { + switch (isp->phy.state) { + case OTG_STATE_A_IDLE: + b_idle(isp, __func__); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is host */ + } else { + if (!(otg_ctrl & OTG_ID)) { + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL); + } + + if (otg->host) { + switch (isp->phy.state) { + case OTG_STATE_B_WAIT_ACON: + isp->phy.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host\n"); + kick = 1; + break; + case OTG_STATE_A_WAIT_BCON: + isp->phy.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + break; + case OTG_STATE_A_PERIPHERAL: + isp->phy.state = OTG_STATE_A_WAIT_BCON; + pr_debug(" --> a_wait_bcon\n"); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_HOST_RESUME); + } + } + + omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC); + ret = IRQ_HANDLED; + + if (kick) + usb_bus_start_enum(otg->host, otg->host->otg_port); + } + + check_state(isp, __func__); + return ret; +} + +static struct platform_device *otg_dev; + +static int isp1301_otg_init(struct isp1301 *isp) +{ + u32 l; + + if (!otg_dev) + return -ENODEV; + + dump_regs(isp, __func__); + /* some of these values are board-specific... */ + l = omap_readl(OTG_SYSCON_2); + l |= OTG_EN + /* for B-device: */ + | SRP_GPDATA /* 9msec Bdev D+ pulse */ + | SRP_GPDVBUS /* discharge after VBUS pulse */ + // | (3 << 24) /* 2msec VBUS pulse */ + /* for A-device: */ + | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ + | SRP_DPW /* detect 167+ns SRP pulses */ + | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ + ; + omap_writel(l, OTG_SYSCON_2); + + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); + + check_state(isp, __func__); + pr_debug("otg: %s, %s %06x\n", + state_name(isp), __func__, omap_readl(OTG_CTRL)); + + omap_writew(DRIVER_SWITCH | OPRT_CHG + | B_SRP_TMROUT | B_HNP_FAIL + | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN); + + l = omap_readl(OTG_SYSCON_2); + l |= OTG_EN; + omap_writel(l, OTG_SYSCON_2); + + return 0; +} + +static int otg_probe(struct platform_device *dev) +{ + // struct omap_usb_config *config = dev->platform_data; + + otg_dev = dev; + return 0; +} + +static int otg_remove(struct platform_device *dev) +{ + otg_dev = NULL; + return 0; +} + +static struct platform_driver omap_otg_driver = { + .probe = otg_probe, + .remove = otg_remove, + .driver = { + .owner = THIS_MODULE, + .name = "omap_otg", + }, +}; + +static int otg_bind(struct isp1301 *isp) +{ + int status; + + if (otg_dev) + return -EBUSY; + + status = platform_driver_register(&omap_otg_driver); + if (status < 0) + return status; + + if (otg_dev) + status = request_irq(otg_dev->resource[1].start, omap_otg_irq, + 0, DRIVER_NAME, isp); + else + status = -ENODEV; + + if (status < 0) + platform_driver_unregister(&omap_otg_driver); + return status; +} + +static void otg_unbind(struct isp1301 *isp) +{ + if (!otg_dev) + return; + free_irq(otg_dev->resource[1].start, isp); +} + +#else + +/* OTG controller isn't clocked */ + +#endif /* CONFIG_USB_OTG */ + +/*-------------------------------------------------------------------------*/ + +static void b_peripheral(struct isp1301 *isp) +{ + u32 l; + + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + + usb_gadget_vbus_connect(isp->phy.otg->gadget); + +#ifdef CONFIG_USB_OTG + enable_vbus_draw(isp, 8); + otg_update_isp(isp); +#else + enable_vbus_draw(isp, 100); + /* UDC driver just set OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); + isp->phy.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + dump_regs(isp, "2periph"); +#endif +} + +static void isp_update_otg(struct isp1301 *isp, u8 stat) +{ + struct usb_otg *otg = isp->phy.otg; + u8 isp_stat, isp_bstat; + enum usb_otg_state state = isp->phy.state; + + if (stat & INTR_BDIS_ACON) + pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); + + /* start certain state transitions right away */ + isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + if (isp_stat & INTR_ID_GND) { + if (otg->default_a) { + switch (state) { + case OTG_STATE_B_IDLE: + a_idle(isp, "idle"); + /* FALLTHROUGH */ + case OTG_STATE_A_IDLE: + enable_vbus_source(isp); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VRISE: + /* we skip over OTG_STATE_A_WAIT_BCON, since + * the HC will transition to A_HOST (or + * A_SUSPEND!) without our noticing except + * when HNP is used. + */ + if (isp_stat & INTR_VBUS_VLD) + isp->phy.state = OTG_STATE_A_HOST; + break; + case OTG_STATE_A_WAIT_VFALL: + if (!(isp_stat & INTR_SESS_VLD)) + a_idle(isp, "vfell"); + break; + default: + if (!(isp_stat & INTR_VBUS_VLD)) + isp->phy.state = OTG_STATE_A_VBUS_ERR; + break; + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + } else { + switch (state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_HOST: + case OTG_STATE_B_WAIT_ACON: + usb_gadget_vbus_disconnect(otg->gadget); + break; + default: + break; + } + if (state != OTG_STATE_A_IDLE) + a_idle(isp, "id"); + if (otg->host && state == OTG_STATE_A_IDLE) + isp1301_defer_work(isp, WORK_HOST_RESUME); + isp_bstat = 0; + } + } else { + u32 l; + + /* if user unplugged mini-A end of cable, + * don't bypass A_WAIT_VFALL. + */ + if (otg->default_a) { + switch (state) { + default: + isp->phy.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_WAIT_VFALL: + state = OTG_STATE_A_IDLE; + /* khubd may take a while to notice and + * handle this disconnect, so don't go + * to B_IDLE quite yet. + */ + break; + case OTG_STATE_A_IDLE: + host_suspend(isp); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); + isp->phy.state = OTG_STATE_B_IDLE; + l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + l &= ~OTG_CTRL_BITS; + omap_writel(l, OTG_CTRL); + break; + case OTG_STATE_B_IDLE: + break; + } + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + + switch (isp->phy.state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: + if (likely(isp_bstat & OTG_B_SESS_VLD)) + break; + enable_vbus_draw(isp, 0); +#ifndef CONFIG_USB_OTG + /* UDC driver will clear OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLUP); + dump_regs(isp, __func__); +#endif + /* FALLTHROUGH */ + case OTG_STATE_B_SRP_INIT: + b_idle(isp, __func__); + l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS; + omap_writel(l, OTG_CTRL); + /* FALLTHROUGH */ + case OTG_STATE_B_IDLE: + if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) { +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); +#endif + b_peripheral(isp); + } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) + isp_bstat |= OTG_B_SESS_END; + break; + case OTG_STATE_A_WAIT_VFALL: + break; + default: + pr_debug("otg: unsupported b-device %s\n", + state_name(isp)); + break; + } + } + + if (state != isp->phy.state) + pr_debug(" isp, %s -> %s\n", + usb_otg_state_string(state), state_name(isp)); + +#ifdef CONFIG_USB_OTG + /* update the OTG controller state to match the isp1301; may + * trigger OPRT_CHG irqs for changes going to the isp1301. + */ + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); + check_state(isp, __func__); +#endif + + dump_regs(isp, "isp1301->otg"); +} + +/*-------------------------------------------------------------------------*/ + +static u8 isp1301_clear_latch(struct isp1301 *isp) +{ + u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); + return latch; +} + +static void +isp1301_work(struct work_struct *work) +{ + struct isp1301 *isp = container_of(work, struct isp1301, work); + int stop; + + /* implicit lock: we're the only task using this device */ + isp->working = 1; + do { + stop = test_bit(WORK_STOP, &isp->todo); + +#ifdef CONFIG_USB_OTG + /* transfer state from otg engine to isp1301 */ + if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { + otg_update_isp(isp); + put_device(&isp->client->dev); + } +#endif + /* transfer state from isp1301 to otg engine */ + if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { + u8 stat = isp1301_clear_latch(isp); + + isp_update_otg(isp, stat); + put_device(&isp->client->dev); + } + + if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { + u32 otg_ctrl; + + /* + * skip A_WAIT_VRISE; hc transitions invisibly + * skip A_WAIT_BCON; same. + */ + switch (isp->phy.state) { + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + isp->phy.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + otg_ctrl = omap_readl(OTG_CTRL); + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + break; + case OTG_STATE_B_WAIT_ACON: + isp->phy.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host (acon)\n"); + break; + case OTG_STATE_B_HOST: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_IDLE: + break; + default: + pr_debug(" host resume in %s\n", + state_name(isp)); + } + host_resume(isp); + // mdelay(10); + put_device(&isp->client->dev); + } + + if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { +#ifdef VERBOSE + dump_regs(isp, "timer"); + if (!stop) + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); +#endif + put_device(&isp->client->dev); + } + + if (isp->todo) + dev_vdbg(&isp->client->dev, + "work done, todo = 0x%lx\n", + isp->todo); + if (stop) { + dev_dbg(&isp->client->dev, "stop\n"); + break; + } + } while (isp->todo); + isp->working = 0; +} + +static irqreturn_t isp1301_irq(int irq, void *isp) +{ + isp1301_defer_work(isp, WORK_UPDATE_OTG); + return IRQ_HANDLED; +} + +static void isp1301_timer(unsigned long _isp) +{ + isp1301_defer_work((void *)_isp, WORK_TIMER); +} + +/*-------------------------------------------------------------------------*/ + +static void isp1301_release(struct device *dev) +{ + struct isp1301 *isp; + + isp = dev_get_drvdata(dev); + + /* FIXME -- not with a "new style" driver, it doesn't!! */ + + /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ + if (isp->i2c_release) + isp->i2c_release(dev); + kfree(isp->phy.otg); + kfree (isp); +} + +static struct isp1301 *the_transceiver; + +static int __exit isp1301_remove(struct i2c_client *i2c) +{ + struct isp1301 *isp; + + isp = i2c_get_clientdata(i2c); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + free_irq(i2c->irq, isp); +#ifdef CONFIG_USB_OTG + otg_unbind(isp); +#endif + if (machine_is_omap_h2()) + gpio_free(2); + + isp->timer.data = 0; + set_bit(WORK_STOP, &isp->todo); + del_timer_sync(&isp->timer); + flush_work(&isp->work); + + put_device(&i2c->dev); + the_transceiver = NULL; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: three modes are possible here, only one of which + * will be standards-conformant on any given system: + * + * - OTG mode (dual-role), required if there's a Mini-AB connector + * - HOST mode, for when there's one or more A (host) connectors + * - DEVICE mode, for when there's a B/Mini-B (device) connector + * + * As a rule, you won't have an isp1301 chip unless it's there to + * support the OTG mode. Other modes help testing USB controllers + * in isolation from (full) OTG support, or maybe so later board + * revisions can help to support those feature. + */ + +#ifdef CONFIG_USB_OTG + +static int isp1301_otg_enable(struct isp1301 *isp) +{ + power_up(isp); + isp1301_otg_init(isp); + + /* NOTE: since we don't change this, this provides + * a few more interrupts than are strictly needed. + */ + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + + dev_info(&isp->client->dev, "ready for dual-role USB ...\n"); + + return 0; +} + +#endif + +/* add or disable the host device+driver */ +static int +isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!host) { + omap_writew(0, OTG_IRQ_EN); + power_down(isp); + otg->host = NULL; + return 0; + } + +#ifdef CONFIG_USB_OTG + otg->host = host; + dev_dbg(&isp->client->dev, "registered host\n"); + host_suspend(isp); + if (otg->gadget) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_GADGET_OMAP) + // FIXME update its refcount + otg->host = host; + + power_up(isp); + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + dev_info(&isp->client->dev, "A-Host sessions ok\n"); + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_ID_GND); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, especially with + * the Mini-A end of an OTG cable. (Or something nonstandard + * like MiniB-to-StandardB, maybe built with a gender mender.) + */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); + + dump_regs(isp, __func__); + + return 0; + +#else + dev_dbg(&isp->client->dev, "host sessions not allowed\n"); + return -EINVAL; +#endif + +} + +static int +isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!gadget) { + omap_writew(0, OTG_IRQ_EN); + if (!otg->default_a) + enable_vbus_draw(isp, 0); + usb_gadget_vbus_disconnect(otg->gadget); + otg->gadget = NULL; + power_down(isp); + return 0; + } + +#ifdef CONFIG_USB_OTG + otg->gadget = gadget; + dev_dbg(&isp->client->dev, "registered gadget\n"); + /* gadget driver may be suspended until vbus_connect () */ + if (otg->host) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) + otg->gadget = gadget; + // FIXME update its refcount + + { + u32 l; + + l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK; + l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS); + l |= OTG_ID; + omap_writel(l, OTG_CTRL); + } + + power_up(isp); + isp->phy.state = OTG_STATE_B_IDLE; + + if (machine_is_omap_h2() || machine_is_omap_h3()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_SESS_VLD); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD); + dev_info(&isp->client->dev, "B-Peripheral sessions ok\n"); + dump_regs(isp, __func__); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, so long + * as you don't plug a Mini-A cable into the jack. + */ + if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) + b_peripheral(isp); + + return 0; + +#else + dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n"); + return -EINVAL; +#endif +} + + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_set_power(struct usb_phy *dev, unsigned mA) +{ + if (!the_transceiver) + return -ENODEV; + if (dev->state == OTG_STATE_B_PERIPHERAL) + enable_vbus_draw(the_transceiver, mA); + return 0; +} + +static int +isp1301_start_srp(struct usb_otg *otg) +{ + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + u32 otg_ctrl; + + if (!otg || isp != the_transceiver + || isp->phy.state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_ctrl = omap_readl(OTG_CTRL); + if (!(otg_ctrl & OTG_BSESSEND)) + return -EINVAL; + + otg_ctrl |= OTG_B_BUSREQ; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; + omap_writel(otg_ctrl, OTG_CTRL); + isp->phy.state = OTG_STATE_B_SRP_INIT; + + pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), + omap_readl(OTG_CTRL)); +#ifdef CONFIG_USB_OTG + check_state(isp, __func__); +#endif + return 0; +} + +static int +isp1301_start_hnp(struct usb_otg *otg) +{ +#ifdef CONFIG_USB_OTG + struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); + u32 l; + + if (!otg || isp != the_transceiver) + return -ENODEV; + if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) + return -ENOTCONN; + if (!otg->default_a && (otg->gadget == NULL + || !otg->gadget->b_hnp_enable)) + return -ENOTCONN; + + /* We want hardware to manage most HNP protocol timings. + * So do this part as early as possible... + */ + switch (isp->phy.state) { + case OTG_STATE_B_HOST: + isp->phy.state = OTG_STATE_B_PERIPHERAL; + /* caller will suspend next */ + break; + case OTG_STATE_A_HOST: +#if 0 + /* autoconnect mode avoids irq latency bugs */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); +#endif + /* caller must suspend then clear A_BUSREQ */ + usb_gadget_vbus_connect(otg->gadget); + l = omap_readl(OTG_CTRL); + l |= OTG_A_SETB_HNPEN; + omap_writel(l, OTG_CTRL); + + break; + case OTG_STATE_A_PERIPHERAL: + /* initiated by B-Host suspend */ + break; + default: + return -EILSEQ; + } + pr_debug("otg: HNP %s, %06x ...\n", + state_name(isp), omap_readl(OTG_CTRL)); + check_state(isp, __func__); + return 0; +#else + /* srp-only */ + return -EINVAL; +#endif +} + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int status; + struct isp1301 *isp; + + if (the_transceiver) + return 0; + + isp = kzalloc(sizeof *isp, GFP_KERNEL); + if (!isp) + return 0; + + isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL); + if (!isp->phy.otg) { + kfree(isp); + return 0; + } + + INIT_WORK(&isp->work, isp1301_work); + init_timer(&isp->timer); + isp->timer.function = isp1301_timer; + isp->timer.data = (unsigned long) isp; + + i2c_set_clientdata(i2c, isp); + isp->client = i2c; + + /* verify the chip (shouldn't be necessary) */ + status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); + if (status != I2C_VENDOR_ID_PHILIPS) { + dev_dbg(&i2c->dev, "not philips id: %d\n", status); + goto fail; + } + status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); + if (status != I2C_PRODUCT_ID_PHILIPS_1301) { + dev_dbg(&i2c->dev, "not isp1301, %d\n", status); + goto fail; + } + isp->i2c_release = i2c->dev.release; + i2c->dev.release = isp1301_release; + + /* initial development used chiprev 2.00 */ + status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); + dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", + status >> 8, status & 0xff); + + /* make like power-on reset */ + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); + + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + +#ifdef CONFIG_USB_OTG + status = otg_bind(isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't bind OTG\n"); + goto fail; + } +#endif + + if (machine_is_omap_h2()) { + /* full speed signaling by default */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_SPEED); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, + MC2_SPD_SUSP_CTRL); + + /* IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); + if (gpio_request(2, "isp1301") == 0) + gpio_direction_input(2); + isp->irq_type = IRQF_TRIGGER_FALLING; + } + + status = request_irq(i2c->irq, isp1301_irq, + isp->irq_type, DRIVER_NAME, isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", + i2c->irq, status); + goto fail; + } + + isp->phy.dev = &i2c->dev; + isp->phy.label = DRIVER_NAME; + isp->phy.set_power = isp1301_set_power, + + isp->phy.otg->phy = &isp->phy; + isp->phy.otg->set_host = isp1301_set_host, + isp->phy.otg->set_peripheral = isp1301_set_peripheral, + isp->phy.otg->start_srp = isp1301_start_srp, + isp->phy.otg->start_hnp = isp1301_start_hnp, + + enable_vbus_draw(isp, 0); + power_down(isp); + the_transceiver = isp; + +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); +#endif + + dump_regs(isp, __func__); + +#ifdef VERBOSE + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); + dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); +#endif + + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); + if (status < 0) + dev_err(&i2c->dev, "can't register transceiver, %d\n", + status); + + return 0; + +fail: + kfree(isp->phy.otg); + kfree(isp); + return -ENODEV; +} + +static const struct i2c_device_id isp1301_id[] = { + { "isp1301_omap", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isp1301_id); + +static struct i2c_driver isp1301_driver = { + .driver = { + .name = "isp1301_omap", + }, + .probe = isp1301_probe, + .remove = __exit_p(isp1301_remove), + .id_table = isp1301_id, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp_init(void) +{ + return i2c_add_driver(&isp1301_driver); +} +subsys_initcall(isp_init); + +static void __exit isp_exit(void) +{ + if (the_transceiver) + usb_remove_phy(&the_transceiver->phy); + i2c_del_driver(&isp1301_driver); +} +module_exit(isp_exit); + diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c new file mode 100644 index 0000000..18dbf7e --- /dev/null +++ b/drivers/usb/phy/phy-isp1301.c @@ -0,0 +1,71 @@ +/* + * NXP ISP1301 USB transceiver driver + * + * Copyright (C) 2012 Roland Stigge + * + * Author: Roland Stigge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#define DRV_NAME "isp1301" + +static const struct i2c_device_id isp1301_id[] = { + { "isp1301", 0 }, + { } +}; + +static struct i2c_client *isp1301_i2c_client; + +static int isp1301_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + isp1301_i2c_client = client; + return 0; +} + +static int isp1301_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver isp1301_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = isp1301_probe, + .remove = isp1301_remove, + .id_table = isp1301_id, +}; + +module_i2c_driver(isp1301_driver); + +static int match(struct device *dev, void *data) +{ + struct device_node *node = (struct device_node *)data; + return (dev->of_node == node) && + (dev->driver == &isp1301_driver.driver); +} + +struct i2c_client *isp1301_get_client(struct device_node *node) +{ + if (node) { /* reference of ISP1301 I2C node via DT */ + struct device *dev = bus_find_device(&i2c_bus_type, NULL, + node, match); + if (!dev) + return NULL; + return to_i2c_client(dev); + } else { /* non-DT: only one ISP1301 chip supported */ + return isp1301_i2c_client; + } +} +EXPORT_SYMBOL_GPL(isp1301_get_client); + +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("NXP ISP1301 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c new file mode 100644 index 0000000..749fbf4 --- /dev/null +++ b/drivers/usb/phy/phy-msm-usb.c @@ -0,0 +1,1762 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MSM_USB_BASE (motg->regs) +#define DRIVER_NAME "msm_otg" + +#define ULPI_IO_TIMEOUT_USEC (10 * 1000) + +#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ +#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ +#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ +#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ +#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ +#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ +#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ + +static struct regulator *hsusb_3p3; +static struct regulator *hsusb_1p8; +static struct regulator *hsusb_vddcx; + +static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) +{ + int ret = 0; + + if (init) { + hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); + if (IS_ERR(hsusb_vddcx)) { + dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); + return PTR_ERR(hsusb_vddcx); + } + + ret = regulator_set_voltage(hsusb_vddcx, + USB_PHY_VDD_DIG_VOL_MIN, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) { + dev_err(motg->phy.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + return ret; + } + + ret = regulator_enable(hsusb_vddcx); + if (ret) { + dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + } + } else { + ret = regulator_set_voltage(hsusb_vddcx, 0, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) + dev_err(motg->phy.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + ret = regulator_disable(hsusb_vddcx); + if (ret) + dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); + + regulator_put(hsusb_vddcx); + } + + return ret; +} + +static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) +{ + int rc = 0; + + if (init) { + hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); + if (IS_ERR(hsusb_3p3)) { + dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); + return PTR_ERR(hsusb_3p3); + } + + rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + USB_PHY_3P3_VOL_MAX); + if (rc) { + dev_err(motg->phy.dev, "unable to set voltage level " + "for hsusb 3p3\n"); + goto put_3p3; + } + rc = regulator_enable(hsusb_3p3); + if (rc) { + dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); + goto put_3p3; + } + hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); + if (IS_ERR(hsusb_1p8)) { + dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); + rc = PTR_ERR(hsusb_1p8); + goto disable_3p3; + } + rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + USB_PHY_1P8_VOL_MAX); + if (rc) { + dev_err(motg->phy.dev, "unable to set voltage level " + "for hsusb 1p8\n"); + goto put_1p8; + } + rc = regulator_enable(hsusb_1p8); + if (rc) { + dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); + goto put_1p8; + } + + return 0; + } + + regulator_disable(hsusb_1p8); +put_1p8: + regulator_put(hsusb_1p8); +disable_3p3: + regulator_disable(hsusb_3p3); +put_3p3: + regulator_put(hsusb_3p3); + return rc; +} + +#ifdef CONFIG_PM_SLEEP +#define USB_PHY_SUSP_DIG_VOL 500000 +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_VDD_DIG_VOL_MIN; + else + min_vol = USB_PHY_SUSP_DIG_VOL; + + ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator " + "HSUSB_VDDCX\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} +#endif + +static int msm_hsusb_ldo_set_mode(int on) +{ + int ret = 0; + + if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { + pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); + return -ENODEV; + } + + if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { + pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); + return -ENODEV; + } + + if (on) { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_1p8\n", __func__); + return ret; + } + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_3p3\n", __func__); + regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + return ret; + } + } else { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_1p8\n", __func__); + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_3p3\n", __func__); + } + + pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); + return ret < 0 ? ret : 0; +} + +static int ulpi_read(struct usb_phy *phy, u32 reg) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + int cnt = 0; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(phy->dev, "ulpi_read: timeout %08x\n", + readl(USB_ULPI_VIEWPORT)); + return -ETIMEDOUT; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + int cnt = 0; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(phy->dev, "ulpi_write: timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + +static struct usb_phy_io_ops msm_otg_io_ops = { + .read = ulpi_read, + .write = ulpi_write, +}; + +static void ulpi_init(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + int *seq = pdata->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", + seq[0], seq[1]); + ulpi_write(&motg->phy, seq[0], seq[1]); + seq += 2; + } +} + +static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(motg->clk, CLK_RESET_ASSERT); + if (ret) + dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); + } else { + ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); + } + return ret; +} + +static int msm_otg_phy_clk_reset(struct msm_otg *motg) +{ + int ret; + + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); + if (ret) { + dev_err(motg->phy.dev, "usb phy clk assert failed\n"); + return ret; + } + usleep_range(10000, 12000); + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); + return ret; +} + +static int msm_otg_phy_reset(struct msm_otg *motg) +{ + u32 val; + int ret; + int retries; + + ret = msm_otg_link_clk_reset(motg, 1); + if (ret) + return ret; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + ret = msm_otg_link_clk_reset(motg, 0); + if (ret) + return ret; + + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, + ULPI_CLR(ULPI_FUNC_CTRL)); + if (!ret) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + /* This reset calibrates the phy, if the above write succeeded */ + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_read(&motg->phy, ULPI_DEBUG); + if (ret != -ETIMEDOUT) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + dev_info(motg->phy.dev, "phy_reset: success\n"); + return 0; +} + +#define LINK_RESET_TIMEOUT_USEC (250 * 1000) +static int msm_otg_reset(struct usb_phy *phy) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + int ret; + u32 val = 0; + u32 ulpi_val = 0; + + ret = msm_otg_phy_reset(motg); + if (ret) { + dev_err(phy->dev, "phy_reset failed\n"); + return ret; + } + + ulpi_init(motg); + + writel(USBCMD_RESET, USB_USBCMD); + while (cnt < LINK_RESET_TIMEOUT_USEC) { + if (!(readl(USB_USBCMD) & USBCMD_RESET)) + break; + udelay(1); + cnt++; + } + if (cnt >= LINK_RESET_TIMEOUT_USEC) + return -ETIMEDOUT; + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + msleep(100); + + writel(0x0, USB_AHBBURST); + writel(0x00, USB_AHBMODE); + + if (pdata->otg_control == OTG_PHY_CONTROL) { + val = readl(USB_OTGSC); + if (pdata->mode == USB_OTG) { + ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; + val |= OTGSC_IDIE | OTGSC_BSVIE; + } else if (pdata->mode == USB_PERIPHERAL) { + ulpi_val = ULPI_INT_SESS_VALID; + val |= OTGSC_BSVIE; + } + writel(val, USB_OTGSC); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); + } + + return 0; +} + +#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) +#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_suspend(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + + if (atomic_read(&motg->in_lpm)) + return 0; + + disable_irq(motg->irq); + /* + * Chipidea 45-nm PHY suspend sequence: + * + * Interrupt Latch Register auto-clear feature is not present + * in all PHY versions. Latch register is clear on read type. + * Clear latch register to avoid spurious wakeup from + * low power mode (LPM). + * + * PHY comparators are disabled when PHY enters into low power + * mode (LPM). Keep PHY comparators ON in LPM only when we expect + * VBUS/Id notifications from USB PHY. Otherwise turn off USB + * PHY comparators. This save significant amount of power. + * + * PLL is not turned off when PHY enters into low power mode (LPM). + * Disable PLL for maximum power savings. + */ + + if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { + ulpi_read(phy, 0x14); + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(phy, 0x01, 0x30); + ulpi_write(phy, 0x08, 0x09); + } + + /* + * PHY may take some time or even fail to enter into low power + * mode (LPM). Hence poll for 500 msec and reset the PHY and link + * in failure case. + */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { + dev_err(phy->dev, "Unable to suspend PHY\n"); + msm_otg_reset(phy); + enable_irq(motg->irq); + return -ETIMEDOUT; + } + + /* + * PHY has capability to generate interrupt asynchronously in low + * power mode (LPM). This interrupt is level triggered. So USB IRQ + * line must be disabled till async interrupt enable bit is cleared + * in USBCMD register. Assert STP (ULPI interface STOP signal) to + * block data communication from PHY. + */ + writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) + writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + if (!IS_ERR(motg->pclk_src)) + clk_disable(motg->pclk_src); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(0); + msm_hsusb_config_vddcx(0); + } + + if (device_may_wakeup(phy->dev)) + enable_irq_wake(motg->irq); + if (bus) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 1); + enable_irq(motg->irq); + + dev_info(phy->dev, "USB in low power mode\n"); + + return 0; +} + +static int msm_otg_resume(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; + int cnt = 0; + unsigned temp; + + if (!atomic_read(&motg->in_lpm)) + return 0; + + if (!IS_ERR(motg->pclk_src)) + clk_enable(motg->pclk_src); + + clk_enable(motg->pclk); + clk_enable(motg->clk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(1); + msm_hsusb_config_vddcx(1); + writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + } + + temp = readl(USB_USBCMD); + temp &= ~ASYNC_INTR_CTRL; + temp &= ~ULPI_STP_CTRL; + writel(temp, USB_USBCMD); + + /* + * PHY comes out of low power mode (LPM) in case of wakeup + * from asynchronous interrupt. + */ + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + goto skip_phy_resume; + + writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_RESUME_TIMEOUT_USEC) { + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_RESUME_TIMEOUT_USEC) { + /* + * This is a fatal error. Reset the link and + * PHY. USB state can not be restored. Re-insertion + * of USB cable is the only way to get USB working. + */ + dev_err(phy->dev, "Unable to resume USB." + "Re-plugin the cable\n"); + msm_otg_reset(phy); + } + +skip_phy_resume: + if (device_may_wakeup(phy->dev)) + disable_irq_wake(motg->irq); + if (bus) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 0); + + if (motg->async_int) { + motg->async_int = 0; + pm_runtime_put(phy->dev); + enable_irq(motg->irq); + } + + dev_info(phy->dev, "USB exited from low power mode\n"); + + return 0; +} +#endif + +static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) +{ + if (motg->cur_power == mA) + return; + + /* TODO: Notify PMIC about available current */ + dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); + motg->cur_power = mA; +} + +static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + + /* + * Gadget driver uses set_power method to notify about the + * available current based on suspend/configured states. + * + * IDEV_CHG can be drawn irrespective of suspend/un-configured + * states when CDP/ACA is connected. + */ + if (motg->chg_type == USB_SDP_CHARGER) + msm_otg_notify_charger(motg, mA); + + return 0; +} + +static void msm_otg_start_host(struct usb_phy *phy, int on) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + struct usb_hcd *hcd; + + if (!phy->otg->host) + return; + + hcd = bus_to_hcd(phy->otg->host); + + if (on) { + dev_dbg(phy->dev, "host on\n"); + + if (pdata->vbus_power) + pdata->vbus_power(1); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Enable internal + * HUB before kicking the host. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_A_HOST); +#ifdef CONFIG_USB + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); +#endif + } else { + dev_dbg(phy->dev, "host off\n"); + +#ifdef CONFIG_USB + usb_remove_hcd(hcd); +#endif + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + if (pdata->vbus_power) + pdata->vbus_power(0); + } +} + +static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + struct usb_hcd *hcd; + + /* + * Fail host registration if this board can support + * only peripheral configuration. + */ + if (motg->pdata->mode == USB_PERIPHERAL) { + dev_info(otg->phy->dev, "Host mode is not supported\n"); + return -ENODEV; + } + + if (!host) { + if (otg->phy->state == OTG_STATE_A_HOST) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_host(otg->phy, 0); + otg->host = NULL; + otg->phy->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->host = NULL; + } + + return 0; + } + + hcd = bus_to_hcd(host); + hcd->power_budget = motg->pdata->power_budget; + + otg->host = host; + dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if peripheral is not supported + * or peripheral is already registered with us. + */ + if (motg->pdata->mode == USB_HOST || otg->gadget) { + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +static void msm_otg_start_peripheral(struct usb_phy *phy, int on) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + + if (!phy->otg->gadget) + return; + + if (on) { + dev_dbg(phy->dev, "gadget on\n"); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Disable internal + * HUB before kicking the gadget. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); + usb_gadget_vbus_connect(phy->otg->gadget); + } else { + dev_dbg(phy->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(phy->otg->gadget); + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + } + +} + +static int msm_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); + + /* + * Fail peripheral registration if this board can support + * only host configuration. + */ + if (motg->pdata->mode == USB_HOST) { + dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); + return -ENODEV; + } + + if (!gadget) { + if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_peripheral(otg->phy, 0); + otg->gadget = NULL; + otg->phy->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->gadget = NULL; + } + + return 0; + } + otg->gadget = gadget; + dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if host is not supported + * or host is already registered with us. + */ + if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +static bool msm_chg_check_secondary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_secondary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(phy, chg_det, 0x34); + /* put it in host mode for enabling D- source */ + chg_det &= ~(1 << 2); + ulpi_write(phy, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DM as current source, DP as current sink + * and enable battery charging comparators. + */ + ulpi_write(phy, 0x8, 0x85); + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_primary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_primary_det(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DP as current source, DM as current sink + * and enable battery charging comparators. + */ + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 line_state; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + line_state = ulpi_read(phy, 0x15); + ret = !(line_state & 1); + break; + case SNPS_28NM_INTEGRATED_PHY: + line_state = ulpi_read(phy, 0x87); + ret = line_state & 2; + break; + default: + break; + } + return ret; +} + +static void msm_chg_disable_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + chg_det &= ~(1 << 5); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + ulpi_write(phy, 0x10, 0x86); + break; + default: + break; + } +} + +static void msm_chg_enable_dcd(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn on D+ current source */ + chg_det |= (1 << 5); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Data contact detection enable */ + ulpi_write(phy, 0x10, 0x85); + break; + default: + break; + } +} + +static void msm_chg_block_on(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 func_ctrl, chg_det; + + /* put the controller in non-driving mode */ + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(phy, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + udelay(20); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(phy, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); + udelay(100); + break; + default: + break; + } +} + +static void msm_chg_block_off(struct msm_otg *motg) +{ + struct usb_phy *phy = &motg->phy; + u32 func_ctrl, chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(phy, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(phy, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(phy, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); + break; + default: + break; + } + + /* put the controller in normal mode */ + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); +} + +#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ +#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ +#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ +#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ +static void msm_chg_detect_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); + struct usb_phy *phy = &motg->phy; + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(phy->dev, "chg detection work\n"); + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + pm_runtime_get_sync(phy->dev); + msm_chg_block_on(motg); + msm_chg_enable_dcd(motg); + motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + motg->dcd_retries = 0; + delay = MSM_CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + is_dcd = msm_chg_check_dcd(motg); + tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; + if (is_dcd || tmout) { + msm_chg_disable_dcd(motg); + msm_chg_enable_primary_det(motg); + delay = MSM_CHG_PRIMARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + delay = MSM_CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = msm_chg_check_primary_det(motg); + if (vout) { + msm_chg_enable_secondary_det(motg); + delay = MSM_CHG_SECONDARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + motg->chg_type = USB_SDP_CHARGER; + motg->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = msm_chg_check_secondary_det(motg); + if (vout) + motg->chg_type = USB_DCP_CHARGER; + else + motg->chg_type = USB_CDP_CHARGER; + motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + motg->chg_state = USB_CHG_STATE_DETECTED; + case USB_CHG_STATE_DETECTED: + msm_chg_block_off(motg); + dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); + schedule_work(&motg->sm_work); + return; + default: + return; + } + + schedule_delayed_work(&motg->chg_work, delay); +} + +/* + * We support OTG, Peripheral only and Host only configurations. In case + * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen + * via Id pin status or user request (debugfs). Id/BSV interrupts are not + * enabled when switch is controlled by user and default mode is supplied + * by board file, which can be changed by userspace later. + */ +static void msm_otg_init_sm(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + u32 otgsc = readl(USB_OTGSC); + + switch (pdata->mode) { + case USB_OTG: + if (pdata->otg_control == OTG_PHY_CONTROL) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } else if (pdata->otg_control == OTG_USER_CONTROL) { + if (pdata->default_mode == USB_HOST) { + clear_bit(ID, &motg->inputs); + } else if (pdata->default_mode == USB_PERIPHERAL) { + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + } else { + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + } + } + break; + case USB_HOST: + clear_bit(ID, &motg->inputs); + break; + case USB_PERIPHERAL: + set_bit(ID, &motg->inputs); + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + break; + } +} + +static void msm_otg_sm_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); + struct usb_otg *otg = motg->phy.otg; + + switch (otg->phy->state) { + case OTG_STATE_UNDEFINED: + dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); + msm_otg_reset(otg->phy); + msm_otg_init_sm(motg); + otg->phy->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); + if (!test_bit(ID, &motg->inputs) && otg->host) { + /* disable BSV bit */ + writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); + msm_otg_start_host(otg->phy, 1); + otg->phy->state = OTG_STATE_A_HOST; + } else if (test_bit(B_SESS_VLD, &motg->inputs)) { + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + msm_chg_detect_work(&motg->chg_work.work); + break; + case USB_CHG_STATE_DETECTED: + switch (motg->chg_type) { + case USB_DCP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + break; + case USB_CDP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; + break; + case USB_SDP_CHARGER: + msm_otg_notify_charger(motg, IUNIT); + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; + break; + default: + break; + } + break; + default: + break; + } + } else { + /* + * If charger detection work is pending, decrement + * the pm usage counter to balance with the one that + * is incremented in charger detection work. + */ + if (cancel_delayed_work_sync(&motg->chg_work)) { + pm_runtime_put_sync(otg->phy->dev); + msm_otg_reset(otg->phy); + } + msm_otg_notify_charger(motg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + } + pm_runtime_put_sync(otg->phy->dev); + break; + case OTG_STATE_B_PERIPHERAL: + dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); + if (!test_bit(B_SESS_VLD, &motg->inputs) || + !test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); + msm_otg_start_peripheral(otg->phy, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); + schedule_work(w); + } + break; + case OTG_STATE_A_HOST: + dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); + if (test_bit(ID, &motg->inputs)) { + msm_otg_start_host(otg->phy, 0); + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); + schedule_work(w); + } + break; + default: + break; + } +} + +static irqreturn_t msm_otg_irq(int irq, void *data) +{ + struct msm_otg *motg = data; + struct usb_phy *phy = &motg->phy; + u32 otgsc = 0; + + if (atomic_read(&motg->in_lpm)) { + disable_irq_nosync(irq); + motg->async_int = 1; + pm_runtime_get(phy->dev); + return IRQ_HANDLED; + } + + otgsc = readl(USB_OTGSC); + if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) + return IRQ_NONE; + + if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + dev_dbg(phy->dev, "ID set/clear\n"); + pm_runtime_get_noresume(phy->dev); + } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + dev_dbg(phy->dev, "BSV set/clear\n"); + pm_runtime_get_noresume(phy->dev); + } + + writel(otgsc, USB_OTGSC); + schedule_work(&motg->sm_work); + return IRQ_HANDLED; +} + +static int msm_otg_mode_show(struct seq_file *s, void *unused) +{ + struct msm_otg *motg = s->private; + struct usb_otg *otg = motg->phy.otg; + + switch (otg->phy->state) { + case OTG_STATE_A_HOST: + seq_printf(s, "host\n"); + break; + case OTG_STATE_B_PERIPHERAL: + seq_printf(s, "peripheral\n"); + break; + default: + seq_printf(s, "none\n"); + break; + } + + return 0; +} + +static int msm_otg_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, msm_otg_mode_show, inode->i_private); +} + +static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_otg *motg = s->private; + char buf[16]; + struct usb_otg *otg = motg->phy.otg; + int status = count; + enum usb_mode_type req_mode; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { + status = -EFAULT; + goto out; + } + + if (!strncmp(buf, "host", 4)) { + req_mode = USB_HOST; + } else if (!strncmp(buf, "peripheral", 10)) { + req_mode = USB_PERIPHERAL; + } else if (!strncmp(buf, "none", 4)) { + req_mode = USB_NONE; + } else { + status = -EINVAL; + goto out; + } + + switch (req_mode) { + case USB_NONE: + switch (otg->phy->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_B_PERIPHERAL: + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_PERIPHERAL: + switch (otg->phy->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_A_HOST: + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_HOST: + switch (otg->phy->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + clear_bit(ID, &motg->inputs); + break; + default: + goto out; + } + break; + default: + goto out; + } + + pm_runtime_get_sync(otg->phy->dev); + schedule_work(&motg->sm_work); +out: + return status; +} + +const struct file_operations msm_otg_mode_fops = { + .open = msm_otg_mode_open, + .read = seq_read, + .write = msm_otg_mode_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *msm_otg_dbg_root; +static struct dentry *msm_otg_dbg_mode; + +static int msm_otg_debugfs_init(struct msm_otg *motg) +{ + msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); + + if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) + return -ENODEV; + + msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, + msm_otg_dbg_root, motg, &msm_otg_mode_fops); + if (!msm_otg_dbg_mode) { + debugfs_remove(msm_otg_dbg_root); + msm_otg_dbg_root = NULL; + return -ENODEV; + } + + return 0; +} + +static void msm_otg_debugfs_cleanup(void) +{ + debugfs_remove(msm_otg_dbg_mode); + debugfs_remove(msm_otg_dbg_root); +} + +static int __init msm_otg_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct msm_otg *motg; + struct usb_phy *phy; + + dev_info(&pdev->dev, "msm_otg probe\n"); + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "No platform data given. Bailing out\n"); + return -ENODEV; + } + + motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); + if (!motg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!motg->phy.otg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->pdata = pdev->dev.platform_data; + phy = &motg->phy; + phy->dev = &pdev->dev; + + motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + ret = PTR_ERR(motg->phy_reset_clk); + goto free_motg; + } + + motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(motg->clk)) { + dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); + ret = PTR_ERR(motg->clk); + goto put_phy_reset_clk; + } + clk_set_rate(motg->clk, 60000000); + + /* + * If USB Core is running its protocol engine based on CORE CLK, + * CORE CLK must be running at >55Mhz for correct HSUSB + * operation and USB core cannot tolerate frequency changes on + * CORE CLK. For such USB cores, vote for maximum clk frequency + * on pclk source + */ + if (motg->pdata->pclk_src_name) { + motg->pclk_src = clk_get(&pdev->dev, + motg->pdata->pclk_src_name); + if (IS_ERR(motg->pclk_src)) + goto put_clk; + clk_set_rate(motg->pclk_src, INT_MAX); + clk_enable(motg->pclk_src); + } else + motg->pclk_src = ERR_PTR(-ENOENT); + + + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(motg->pclk)) { + dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); + ret = PTR_ERR(motg->pclk); + goto put_pclk_src; + } + + /* + * USB core clock is not present on all MSM chips. This + * clock is introduced to remove the dependency on AXI + * bus frequency. + */ + motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(motg->core_clk)) + motg->core_clk = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + ret = -ENODEV; + goto put_core_clk; + } + + motg->regs = ioremap(res->start, resource_size(res)); + if (!motg->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto put_core_clk; + } + dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); + + motg->irq = platform_get_irq(pdev, 0); + if (!motg->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + ret = -ENODEV; + goto free_regs; + } + + clk_enable(motg->clk); + clk_enable(motg->pclk); + + ret = msm_hsusb_init_vddcx(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); + goto free_regs; + } + + ret = msm_hsusb_ldo_init(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); + goto vddcx_exit; + } + ret = msm_hsusb_ldo_set_mode(1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg enable failed\n"); + goto ldo_exit; + } + + if (motg->core_clk) + clk_enable(motg->core_clk); + + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + + INIT_WORK(&motg->sm_work, msm_otg_sm_work); + INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); + ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, + "msm_otg", motg); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + goto disable_clks; + } + + phy->init = msm_otg_reset; + phy->set_power = msm_otg_set_power; + + phy->io_ops = &msm_otg_io_ops; + + phy->otg->phy = &motg->phy; + phy->otg->set_host = msm_otg_set_host; + phy->otg->set_peripheral = msm_otg_set_peripheral; + + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); + if (ret) { + dev_err(&pdev->dev, "usb_add_phy failed\n"); + goto free_irq; + } + + platform_set_drvdata(pdev, motg); + device_init_wakeup(&pdev->dev, 1); + + if (motg->pdata->mode == USB_OTG && + motg->pdata->otg_control == OTG_USER_CONTROL) { + ret = msm_otg_debugfs_init(motg); + if (ret) + dev_dbg(&pdev->dev, "mode debugfs file is" + "not available\n"); + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +free_irq: + free_irq(motg->irq, motg); +disable_clks: + clk_disable(motg->pclk); + clk_disable(motg->clk); +ldo_exit: + msm_hsusb_ldo_init(motg, 0); +vddcx_exit: + msm_hsusb_init_vddcx(motg, 0); +free_regs: + iounmap(motg->regs); +put_core_clk: + if (motg->core_clk) + clk_put(motg->core_clk); + clk_put(motg->pclk); +put_pclk_src: + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } +put_clk: + clk_put(motg->clk); +put_phy_reset_clk: + clk_put(motg->phy_reset_clk); +free_motg: + kfree(motg->phy.otg); + kfree(motg); + return ret; +} + +static int msm_otg_remove(struct platform_device *pdev) +{ + struct msm_otg *motg = platform_get_drvdata(pdev); + struct usb_phy *phy = &motg->phy; + int cnt = 0; + + if (phy->otg->host || phy->otg->gadget) + return -EBUSY; + + msm_otg_debugfs_cleanup(); + cancel_delayed_work_sync(&motg->chg_work); + cancel_work_sync(&motg->sm_work); + + pm_runtime_resume(&pdev->dev); + + device_init_wakeup(&pdev->dev, 0); + pm_runtime_disable(&pdev->dev); + + usb_remove_phy(phy); + free_irq(motg->irq, motg); + + /* + * Put PHY in low power mode. + */ + ulpi_read(phy, 0x14); + ulpi_write(phy, 0x08, 0x09); + + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) + dev_err(phy->dev, "Unable to suspend PHY\n"); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } + msm_hsusb_ldo_init(motg, 0); + + iounmap(motg->regs); + pm_runtime_set_suspended(&pdev->dev); + + clk_put(motg->phy_reset_clk); + clk_put(motg->pclk); + clk_put(motg->clk); + if (motg->core_clk) + clk_put(motg->core_clk); + + kfree(motg->phy.otg); + kfree(motg); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int msm_otg_runtime_idle(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + struct usb_otg *otg = motg->phy.otg; + + dev_dbg(dev, "OTG runtime idle\n"); + + /* + * It is observed some times that a spurious interrupt + * comes when PHY is put into LPM immediately after PHY reset. + * This 1 sec delay also prevents entering into LPM immediately + * after asynchronous interrupt. + */ + if (otg->phy->state != OTG_STATE_UNDEFINED) + pm_schedule_suspend(dev, 1000); + + return -EAGAIN; +} + +static int msm_otg_runtime_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_runtime_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime resume\n"); + return msm_otg_resume(motg); +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_pm_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG PM suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_pm_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "OTG PM resume\n"); + + ret = msm_otg_resume(motg); + if (ret) + return ret; + + /* + * Runtime PM Documentation recommends bringing the + * device to full powered state upon resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops msm_otg_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) + SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, + msm_otg_runtime_idle) +}; +#endif + +static struct platform_driver msm_otg_driver = { + .remove = msm_otg_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &msm_otg_dev_pm_ops, +#endif + }, +}; + +module_platform_driver_probe(msm_otg_driver, msm_otg_probe); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/phy/phy-mv-u3d-usb.c b/drivers/usb/phy/phy-mv-u3d-usb.c new file mode 100644 index 0000000..cb7e70f --- /dev/null +++ b/drivers/usb/phy/phy-mv-u3d-usb.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-mv-u3d-usb.h" + +/* + * struct mv_u3d_phy - transceiver driver state + * @phy: transceiver structure + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @base: usb phy register memory base + */ +struct mv_u3d_phy { + struct usb_phy phy; + struct mv_usb_platform_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *base; +}; + +static u32 mv_u3d_phy_read(void __iomem *base, u32 reg) +{ + void __iomem *addr, *data; + + addr = base; + data = base + 0x4; + + writel_relaxed(reg, addr); + return readl_relaxed(data); +} + +static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value) +{ + void __iomem *addr, *data; + u32 tmp; + + addr = base; + data = base + 0x4; + + writel_relaxed(reg, addr); + tmp = readl_relaxed(data); + tmp |= value; + writel_relaxed(tmp, data); +} + +static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value) +{ + void __iomem *addr, *data; + u32 tmp; + + addr = base; + data = base + 0x4; + + writel_relaxed(reg, addr); + tmp = readl_relaxed(data); + tmp &= ~value; + writel_relaxed(tmp, data); +} + +static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value) +{ + void __iomem *addr, *data; + + addr = base; + data = base + 0x4; + + writel_relaxed(reg, addr); + writel_relaxed(value, data); +} + +void mv_u3d_phy_shutdown(struct usb_phy *phy) +{ + struct mv_u3d_phy *mv_u3d_phy; + void __iomem *base; + u32 val; + + mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); + base = mv_u3d_phy->base; + + /* Power down Reference Analog current, bit 15 + * Power down PLL, bit 14 + * Power down Receiver, bit 13 + * Power down Transmitter, bit 12 + * of USB3_POWER_PLL_CONTROL register + */ + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); + val &= ~(USB3_POWER_PLL_CONTROL_PU); + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); + + if (mv_u3d_phy->clk) + clk_disable(mv_u3d_phy->clk); +} + +static int mv_u3d_phy_init(struct usb_phy *phy) +{ + struct mv_u3d_phy *mv_u3d_phy; + void __iomem *base; + u32 val, count; + + /* enable usb3 phy */ + mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy); + + if (mv_u3d_phy->clk) + clk_enable(mv_u3d_phy->clk); + + base = mv_u3d_phy->base; + + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); + val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK); + val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT; + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); + udelay(100); + + mv_u3d_phy_write(base, USB3_RESET_CONTROL, + USB3_RESET_CONTROL_RESET_PIPE); + udelay(100); + + mv_u3d_phy_write(base, USB3_RESET_CONTROL, + USB3_RESET_CONTROL_RESET_PIPE + | USB3_RESET_CONTROL_RESET_PHY); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL); + val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK + | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK); + val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT) + | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT); + mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val); + udelay(100); + + mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL, + USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE); + val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK + | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK + | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK); + val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT) + | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT) + | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT)); + mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_GEN1_SET0); + val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK; + val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT; + mv_u3d_phy_write(base, USB3_GEN1_SET0, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_GEN2_SET0); + val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK + | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK + | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK); + val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT) + | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT) + | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT) + | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT)); + mv_u3d_phy_write(base, USB3_GEN2_SET0, val); + udelay(100); + + mv_u3d_phy_read(base, USB3_TX_EMPPH); + val &= ~(USB3_TX_EMPPH_AMP_MASK + | USB3_TX_EMPPH_EN_MASK + | USB3_TX_EMPPH_AMP_FORCE_MASK + | USB3_TX_EMPPH_PAR1_MASK + | USB3_TX_EMPPH_PAR2_MASK); + val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT) + | (1 << USB3_TX_EMPPH_EN_SHIFT) + | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT) + | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT) + | (1 << USB3_TX_EMPPH_PAR2_SHIFT)); + + mv_u3d_phy_write(base, USB3_TX_EMPPH, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_GEN2_SET1); + val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK + | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK + | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK + | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK); + val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT) + | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT) + | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT) + | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT)); + mv_u3d_phy_write(base, USB3_GEN2_SET1, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN); + val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK; + val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT; + mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC); + val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK; + val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT; + mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL); + val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK; + val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT; + mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE); + val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK + | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK + | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK); + val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT) + | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT)); + mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val); + udelay(100); + + val = mv_u3d_phy_read(base, USB3_TXDETRX); + val &= ~(USB3_TXDETRX_VTHSEL_MASK); + val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT; + mv_u3d_phy_write(base, USB3_TXDETRX, val); + udelay(100); + + dev_dbg(mv_u3d_phy->dev, "start calibration\n"); + +calstart: + /* Perform Manual Calibration */ + mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL, + 1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT); + + mdelay(1); + + count = 0; + while (1) { + val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL); + if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT)) + break; + else if (count > 50) { + dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n"); + goto calstart; + } + count++; + mdelay(1); + } + + /* active PIPE interface */ + mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL, + 1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE); + + return 0; +} + +static int mv_u3d_phy_probe(struct platform_device *pdev) +{ + struct mv_u3d_phy *mv_u3d_phy; + struct mv_usb_platform_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *phy_base; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "missing mem resource\n"); + return -ENODEV; + } + + phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL); + if (!mv_u3d_phy) + return -ENOMEM; + + mv_u3d_phy->dev = &pdev->dev; + mv_u3d_phy->plat = pdata; + mv_u3d_phy->base = phy_base; + mv_u3d_phy->phy.dev = mv_u3d_phy->dev; + mv_u3d_phy->phy.label = "mv-u3d-phy"; + mv_u3d_phy->phy.init = mv_u3d_phy_init; + mv_u3d_phy->phy.shutdown = mv_u3d_phy_shutdown; + + ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3); + if (ret) + goto err; + + if (!mv_u3d_phy->clk) + mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy"); + + platform_set_drvdata(pdev, mv_u3d_phy); + + dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n"); +err: + return ret; +} + +static int __exit mv_u3d_phy_remove(struct platform_device *pdev) +{ + struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&mv_u3d_phy->phy); + + if (mv_u3d_phy->clk) { + clk_put(mv_u3d_phy->clk); + mv_u3d_phy->clk = NULL; + } + + return 0; +} + +static struct platform_driver mv_u3d_phy_driver = { + .probe = mv_u3d_phy_probe, + .remove = mv_u3d_phy_remove, + .driver = { + .name = "mv-u3d-phy", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(mv_u3d_phy_driver); +MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller"); +MODULE_AUTHOR("Yu Xu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mv-u3d-phy"); diff --git a/drivers/usb/phy/phy-mv-u3d-usb.h b/drivers/usb/phy/phy-mv-u3d-usb.h new file mode 100644 index 0000000..2a658cb --- /dev/null +++ b/drivers/usb/phy/phy-mv-u3d-usb.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __MV_U3D_PHY_H +#define __MV_U3D_PHY_H + +#define USB3_POWER_PLL_CONTROL 0x1 +#define USB3_KVCO_CALI_CONTROL 0x2 +#define USB3_IMPEDANCE_CALI_CTRL 0x3 +#define USB3_IMPEDANCE_TX_SSC 0x4 +#define USB3_SQUELCH_FFE 0x6 +#define USB3_GEN1_SET0 0xD +#define USB3_GEN2_SET0 0xF +#define USB3_GEN2_SET1 0x10 +#define USB3_DIGITAL_LOOPBACK_EN 0x23 +#define USB3_PHY_ISOLATION_MODE 0x26 +#define USB3_TXDETRX 0x48 +#define USB3_TX_EMPPH 0x5E +#define USB3_RESET_CONTROL 0x90 +#define USB3_PIPE_SM_CTRL 0x91 + +#define USB3_RESET_CONTROL_RESET_PIPE 0x1 +#define USB3_RESET_CONTROL_RESET_PHY 0x2 + +#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK (0x1F << 0) +#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT 0 +#define USB3_PLL_25MHZ 0x2 +#define USB3_PLL_26MHZ 0x5 +#define USB3_POWER_PLL_CONTROL_PHY_MODE_MASK (0x7 << 5) +#define USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT 5 +#define USB3_POWER_PLL_CONTROL_PU_MASK (0xF << 12) +#define USB3_POWER_PLL_CONTROL_PU_SHIFT 12 +#define USB3_POWER_PLL_CONTROL_PU (0xF << 12) + +#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK (0x1 << 12) +#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_SHIFT 12 +#define USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT 14 +#define USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT 15 + +#define USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK 0xF +#define USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT 0 +#define USB3_SQUELCH_FFE_FFE_RES_SEL_MASK (0x7 << 4) +#define USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT 4 +#define USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK (0x1F << 8) +#define USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT 8 + +#define USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK (0x1 << 15) +#define USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT 11 + +#define USB3_GEN2_SET0_G2_TX_AMP_MASK (0x1F << 1) +#define USB3_GEN2_SET0_G2_TX_AMP_SHIFT 1 +#define USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT 6 +#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK (0xF << 7) +#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT 7 +#define USB3_GEN2_SET0_G2_TX_EMPH_EN_MASK (0x1 << 11) +#define USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT 11 +#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK (0x1 << 15) +#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_SHIFT 15 + +#define USB3_GEN2_SET1_G2_RX_SELMUPI_MASK (0x7 << 0) +#define USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT 0 +#define USB3_GEN2_SET1_G2_RX_SELMUPF_MASK (0x7 << 3) +#define USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT 3 +#define USB3_GEN2_SET1_G2_RX_SELMUFI_MASK (0x3 << 6) +#define USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT 6 +#define USB3_GEN2_SET1_G2_RX_SELMUFF_MASK (0x3 << 8) +#define USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT 8 + +#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK (0x3 << 10) +#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT 10 + +#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK (0x7 << 12) +#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT 12 + +#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK (0x3F << 0) +#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT 0 + +#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK 0xF +#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT 0 +#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK (0xF << 4) +#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT 4 +#define USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK (0x1 << 8) + +#define USB3_TXDETRX_VTHSEL_MASK (0x3 << 4) +#define USB3_TXDETRX_VTHSEL_SHIFT 4 + +#define USB3_TX_EMPPH_AMP_MASK (0xF << 0) +#define USB3_TX_EMPPH_AMP_SHIFT 0 +#define USB3_TX_EMPPH_EN_MASK (0x1 << 6) +#define USB3_TX_EMPPH_EN_SHIFT 6 +#define USB3_TX_EMPPH_AMP_FORCE_MASK (0x1 << 7) +#define USB3_TX_EMPPH_AMP_FORCE_SHIFT 7 +#define USB3_TX_EMPPH_PAR1_MASK (0x1F << 8) +#define USB3_TX_EMPPH_PAR1_SHIFT 8 +#define USB3_TX_EMPPH_PAR2_MASK (0x1 << 13) +#define USB3_TX_EMPPH_PAR2_SHIFT 13 + +#define USB3_PIPE_SM_CTRL_PHY_INIT_DONE 15 + +#endif /* __MV_U3D_PHY_H */ diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c new file mode 100644 index 0000000..6872bf0 --- /dev/null +++ b/drivers/usb/phy/phy-mv-usb.c @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie + * Neil Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phy-mv-usb.h" + +#define DRIVER_DESC "Marvell USB OTG transceiver driver" +#define DRIVER_VERSION "Jan 20, 2010" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static const char driver_name[] = "mv-otg"; + +static char *state_string[] = { + "undefined", + "b_idle", + "b_srp_init", + "b_peripheral", + "b_wait_acon", + "b_host", + "a_idle", + "a_wait_vrise", + "a_wait_bcon", + "a_host", + "a_suspend", + "a_peripheral", + "a_wait_vfall", + "a_vbus_err" +}; + +static int mv_otg_set_vbus(struct usb_otg *otg, bool on) +{ + struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy); + if (mvotg->pdata->set_vbus == NULL) + return -ENODEV; + + return mvotg->pdata->set_vbus(on); +} + +static int mv_otg_set_host(struct usb_otg *otg, + struct usb_bus *host) +{ + otg->host = host; + + return 0; +} + +static int mv_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + + return 0; +} + +static void mv_otg_run_state_machine(struct mv_otg *mvotg, + unsigned long delay) +{ + dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n"); + if (!mvotg->qwork) + return; + + queue_delayed_work(mvotg->qwork, &mvotg->work, delay); +} + +static void mv_otg_timer_await_bcon(unsigned long data) +{ + struct mv_otg *mvotg = (struct mv_otg *) data; + + mvotg->otg_ctrl.a_wait_bcon_timeout = 1; + + dev_info(&mvotg->pdev->dev, "B Device No Response!\n"); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } +} + +static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) +{ + struct timer_list *timer; + + if (id >= OTG_TIMER_NUM) + return -EINVAL; + + timer = &mvotg->otg_ctrl.timer[id]; + + if (timer_pending(timer)) + del_timer(timer); + + return 0; +} + +static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, + unsigned long interval, + void (*callback) (unsigned long)) +{ + struct timer_list *timer; + + if (id >= OTG_TIMER_NUM) + return -EINVAL; + + timer = &mvotg->otg_ctrl.timer[id]; + if (timer_pending(timer)) { + dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id); + return -EBUSY; + } + + init_timer(timer); + timer->data = (unsigned long) mvotg; + timer->function = callback; + timer->expires = jiffies + interval; + add_timer(timer); + + return 0; +} + +static int mv_otg_reset(struct mv_otg *mvotg) +{ + unsigned int loops; + u32 tmp; + + /* Stop the controller */ + tmp = readl(&mvotg->op_regs->usbcmd); + tmp &= ~USBCMD_RUN_STOP; + writel(tmp, &mvotg->op_regs->usbcmd); + + /* Reset the controller to get default values */ + writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd); + + loops = 500; + while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) { + if (loops == 0) { + dev_err(&mvotg->pdev->dev, + "Wait for RESET completed TIMEOUT\n"); + return -ETIMEDOUT; + } + loops--; + udelay(20); + } + + writel(0x0, &mvotg->op_regs->usbintr); + tmp = readl(&mvotg->op_regs->usbsts); + writel(tmp, &mvotg->op_regs->usbsts); + + return 0; +} + +static void mv_otg_init_irq(struct mv_otg *mvotg) +{ + u32 otgsc; + + mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID + | OTGSC_INTR_A_VBUS_VALID; + mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID + | OTGSC_INTSTS_A_VBUS_VALID; + + if (mvotg->pdata->vbus == NULL) { + mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID + | OTGSC_INTR_B_SESSION_END; + mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID + | OTGSC_INTSTS_B_SESSION_END; + } + + if (mvotg->pdata->id == NULL) { + mvotg->irq_en |= OTGSC_INTR_USB_ID; + mvotg->irq_status |= OTGSC_INTSTS_USB_ID; + } + + otgsc = readl(&mvotg->op_regs->otgsc); + otgsc |= mvotg->irq_en; + writel(otgsc, &mvotg->op_regs->otgsc); +} + +static void mv_otg_start_host(struct mv_otg *mvotg, int on) +{ +#ifdef CONFIG_USB + struct usb_otg *otg = mvotg->phy.otg; + struct usb_hcd *hcd; + + if (!otg->host) + return; + + dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop"); + + hcd = bus_to_hcd(otg->host); + + if (on) + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + else + usb_remove_hcd(hcd); +#endif /* CONFIG_USB */ +} + +static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) +{ + struct usb_otg *otg = mvotg->phy.otg; + + if (!otg->gadget) + return; + + dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off"); + + if (on) + usb_gadget_vbus_connect(otg->gadget); + else + usb_gadget_vbus_disconnect(otg->gadget); +} + +static void otg_clock_enable(struct mv_otg *mvotg) +{ + unsigned int i; + + for (i = 0; i < mvotg->clknum; i++) + clk_prepare_enable(mvotg->clk[i]); +} + +static void otg_clock_disable(struct mv_otg *mvotg) +{ + unsigned int i; + + for (i = 0; i < mvotg->clknum; i++) + clk_disable_unprepare(mvotg->clk[i]); +} + +static int mv_otg_enable_internal(struct mv_otg *mvotg) +{ + int retval = 0; + + if (mvotg->active) + return 0; + + dev_dbg(&mvotg->pdev->dev, "otg enabled\n"); + + otg_clock_enable(mvotg); + if (mvotg->pdata->phy_init) { + retval = mvotg->pdata->phy_init(mvotg->phy_regs); + if (retval) { + dev_err(&mvotg->pdev->dev, + "init phy error %d\n", retval); + otg_clock_disable(mvotg); + return retval; + } + } + mvotg->active = 1; + + return 0; + +} + +static int mv_otg_enable(struct mv_otg *mvotg) +{ + if (mvotg->clock_gating) + return mv_otg_enable_internal(mvotg); + + return 0; +} + +static void mv_otg_disable_internal(struct mv_otg *mvotg) +{ + if (mvotg->active) { + dev_dbg(&mvotg->pdev->dev, "otg disabled\n"); + if (mvotg->pdata->phy_deinit) + mvotg->pdata->phy_deinit(mvotg->phy_regs); + otg_clock_disable(mvotg); + mvotg->active = 0; + } +} + +static void mv_otg_disable(struct mv_otg *mvotg) +{ + if (mvotg->clock_gating) + mv_otg_disable_internal(mvotg); +} + +static void mv_otg_update_inputs(struct mv_otg *mvotg) +{ + struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; + u32 otgsc; + + otgsc = readl(&mvotg->op_regs->otgsc); + + if (mvotg->pdata->vbus) { + if (mvotg->pdata->vbus->poll() == VBUS_HIGH) { + otg_ctrl->b_sess_vld = 1; + otg_ctrl->b_sess_end = 0; + } else { + otg_ctrl->b_sess_vld = 0; + otg_ctrl->b_sess_end = 1; + } + } else { + otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID); + otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END); + } + + if (mvotg->pdata->id) + otg_ctrl->id = !!mvotg->pdata->id->poll(); + else + otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID); + + if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id) + otg_ctrl->a_bus_req = 1; + + otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID); + otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID); + + dev_dbg(&mvotg->pdev->dev, "%s: ", __func__); + dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id); + dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld); + dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end); + dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld); + dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld); +} + +static void mv_otg_update_state(struct mv_otg *mvotg) +{ + struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; + struct usb_phy *phy = &mvotg->phy; + int old_state = phy->state; + + switch (old_state) { + case OTG_STATE_UNDEFINED: + phy->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + if (otg_ctrl->id == 0) + phy->state = OTG_STATE_A_IDLE; + else if (otg_ctrl->b_sess_vld) + phy->state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_B_PERIPHERAL: + if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0) + phy->state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl->id) + phy->state = OTG_STATE_B_IDLE; + else if (!(otg_ctrl->a_bus_drop) && + (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det)) + phy->state = OTG_STATE_A_WAIT_VRISE; + break; + case OTG_STATE_A_WAIT_VRISE: + if (otg_ctrl->a_vbus_vld) + phy->state = OTG_STATE_A_WAIT_BCON; + break; + case OTG_STATE_A_WAIT_BCON: + if (otg_ctrl->id || otg_ctrl->a_bus_drop + || otg_ctrl->a_wait_bcon_timeout) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_WAIT_VFALL; + otg_ctrl->a_bus_req = 0; + } else if (!otg_ctrl->a_vbus_vld) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_VBUS_ERR; + } else if (otg_ctrl->b_conn) { + mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); + mvotg->otg_ctrl.a_wait_bcon_timeout = 0; + phy->state = OTG_STATE_A_HOST; + } + break; + case OTG_STATE_A_HOST: + if (otg_ctrl->id || !otg_ctrl->b_conn + || otg_ctrl->a_bus_drop) + phy->state = OTG_STATE_A_WAIT_BCON; + else if (!otg_ctrl->a_vbus_vld) + phy->state = OTG_STATE_A_VBUS_ERR; + break; + case OTG_STATE_A_WAIT_VFALL: + if (otg_ctrl->id + || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld) + || otg_ctrl->a_bus_req) + phy->state = OTG_STATE_A_IDLE; + break; + case OTG_STATE_A_VBUS_ERR: + if (otg_ctrl->id || otg_ctrl->a_clr_err + || otg_ctrl->a_bus_drop) { + otg_ctrl->a_clr_err = 0; + phy->state = OTG_STATE_A_WAIT_VFALL; + } + break; + default: + break; + } +} + +static void mv_otg_work(struct work_struct *work) +{ + struct mv_otg *mvotg; + struct usb_phy *phy; + struct usb_otg *otg; + int old_state; + + mvotg = container_of(to_delayed_work(work), struct mv_otg, work); + +run: + /* work queue is single thread, or we need spin_lock to protect */ + phy = &mvotg->phy; + otg = phy->otg; + old_state = phy->state; + + if (!mvotg->active) + return; + + mv_otg_update_inputs(mvotg); + mv_otg_update_state(mvotg); + + if (old_state != phy->state) { + dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", + state_string[old_state], + state_string[phy->state]); + + switch (phy->state) { + case OTG_STATE_B_IDLE: + otg->default_a = 0; + if (old_state == OTG_STATE_B_PERIPHERAL) + mv_otg_start_periphrals(mvotg, 0); + mv_otg_reset(mvotg); + mv_otg_disable(mvotg); + break; + case OTG_STATE_B_PERIPHERAL: + mv_otg_enable(mvotg); + mv_otg_start_periphrals(mvotg, 1); + break; + case OTG_STATE_A_IDLE: + otg->default_a = 1; + mv_otg_enable(mvotg); + if (old_state == OTG_STATE_A_WAIT_VFALL) + mv_otg_start_host(mvotg, 0); + mv_otg_reset(mvotg); + break; + case OTG_STATE_A_WAIT_VRISE: + mv_otg_set_vbus(otg, 1); + break; + case OTG_STATE_A_WAIT_BCON: + if (old_state != OTG_STATE_A_HOST) + mv_otg_start_host(mvotg, 1); + mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, + T_A_WAIT_BCON, + mv_otg_timer_await_bcon); + /* + * Now, we directly enter A_HOST. So set b_conn = 1 + * here. In fact, it need host driver to notify us. + */ + mvotg->otg_ctrl.b_conn = 1; + break; + case OTG_STATE_A_HOST: + break; + case OTG_STATE_A_WAIT_VFALL: + /* + * Now, we has exited A_HOST. So set b_conn = 0 + * here. In fact, it need host driver to notify us. + */ + mvotg->otg_ctrl.b_conn = 0; + mv_otg_set_vbus(otg, 0); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } + goto run; + } +} + +static irqreturn_t mv_otg_irq(int irq, void *dev) +{ + struct mv_otg *mvotg = dev; + u32 otgsc; + + otgsc = readl(&mvotg->op_regs->otgsc); + writel(otgsc, &mvotg->op_regs->otgsc); + + /* + * if we have vbus, then the vbus detection for B-device + * will be done by mv_otg_inputs_irq(). + */ + if (mvotg->pdata->vbus) + if ((otgsc & OTGSC_STS_USB_ID) && + !(otgsc & OTGSC_INTSTS_USB_ID)) + return IRQ_NONE; + + if ((otgsc & mvotg->irq_status) == 0) + return IRQ_NONE; + + mv_otg_run_state_machine(mvotg, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) +{ + struct mv_otg *mvotg = dev; + + /* The clock may disabled at this time */ + if (!mvotg->active) { + mv_otg_enable(mvotg); + mv_otg_init_irq(mvotg); + } + + mv_otg_run_state_machine(mvotg, 0); + + return IRQ_HANDLED; +} + +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + mvotg->otg_ctrl.a_bus_req); +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + /* We will use this interface to change to A device */ + if (mvotg->phy.state != OTG_STATE_B_IDLE + && mvotg->phy.state != OTG_STATE_A_IDLE) + return -1; + + /* The clock may disabled and we need to set irq for ID detected */ + mv_otg_enable(mvotg); + mv_otg_init_irq(mvotg); + + if (buf[0] == '1') { + mvotg->otg_ctrl.a_bus_req = 1; + mvotg->otg_ctrl.a_bus_drop = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_req = 1\n"); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + } + + return count; +} + +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, + set_a_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + if (!mvotg->phy.otg->default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '1') { + mvotg->otg_ctrl.a_clr_err = 1; + dev_dbg(&mvotg->pdev->dev, + "User request: a_clr_err = 1\n"); + } + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + + return count; +} + +static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + mvotg->otg_ctrl.a_bus_drop); +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mv_otg *mvotg = dev_get_drvdata(dev); + if (!mvotg->phy.otg->default_a) + return -1; + + if (count > 2) + return -1; + + if (buf[0] == '0') { + mvotg->otg_ctrl.a_bus_drop = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_drop = 0\n"); + } else if (buf[0] == '1') { + mvotg->otg_ctrl.a_bus_drop = 1; + mvotg->otg_ctrl.a_bus_req = 0; + dev_dbg(&mvotg->pdev->dev, + "User request: a_bus_drop = 1\n"); + dev_dbg(&mvotg->pdev->dev, + "User request: and a_bus_req = 0\n"); + } + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + + return count; +} + +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, + get_a_bus_drop, set_a_bus_drop); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_clr_err.attr, + &dev_attr_a_bus_drop.attr, + NULL, +}; + +static struct attribute_group inputs_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + +int mv_otg_remove(struct platform_device *pdev) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + + sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group); + + if (mvotg->qwork) { + flush_workqueue(mvotg->qwork); + destroy_workqueue(mvotg->qwork); + } + + mv_otg_disable(mvotg); + + usb_remove_phy(&mvotg->phy); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int mv_otg_probe(struct platform_device *pdev) +{ + struct mv_usb_platform_data *pdata = pdev->dev.platform_data; + struct mv_otg *mvotg; + struct usb_otg *otg; + struct resource *r; + int retval = 0, clk_i, i; + size_t size; + + if (pdata == NULL) { + dev_err(&pdev->dev, "failed to get platform data\n"); + return -ENODEV; + } + + size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum; + mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!mvotg) { + dev_err(&pdev->dev, "failed to allocate memory!\n"); + return -ENOMEM; + } + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + platform_set_drvdata(pdev, mvotg); + + mvotg->pdev = pdev; + mvotg->pdata = pdata; + + mvotg->clknum = pdata->clknum; + for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) { + mvotg->clk[clk_i] = devm_clk_get(&pdev->dev, + pdata->clkname[clk_i]); + if (IS_ERR(mvotg->clk[clk_i])) { + retval = PTR_ERR(mvotg->clk[clk_i]); + return retval; + } + } + + mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); + if (!mvotg->qwork) { + dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n"); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&mvotg->work, mv_otg_work); + + /* OTG common part */ + mvotg->pdev = pdev; + mvotg->phy.dev = &pdev->dev; + mvotg->phy.otg = otg; + mvotg->phy.label = driver_name; + mvotg->phy.state = OTG_STATE_UNDEFINED; + + otg->phy = &mvotg->phy; + otg->set_host = mv_otg_set_host; + otg->set_peripheral = mv_otg_set_peripheral; + otg->set_vbus = mv_otg_set_vbus; + + for (i = 0; i < OTG_TIMER_NUM; i++) + init_timer(&mvotg->otg_ctrl.timer[i]); + + r = platform_get_resource_byname(mvotg->pdev, + IORESOURCE_MEM, "phyregs"); + if (r == NULL) { + dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); + retval = -ENODEV; + goto err_destroy_workqueue; + } + + mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (mvotg->phy_regs == NULL) { + dev_err(&pdev->dev, "failed to map phy I/O memory\n"); + retval = -EFAULT; + goto err_destroy_workqueue; + } + + r = platform_get_resource_byname(mvotg->pdev, + IORESOURCE_MEM, "capregs"); + if (r == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + retval = -ENODEV; + goto err_destroy_workqueue; + } + + mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (mvotg->cap_regs == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + retval = -EFAULT; + goto err_destroy_workqueue; + } + + /* we will acces controller register, so enable the udc controller */ + retval = mv_otg_enable_internal(mvotg); + if (retval) { + dev_err(&pdev->dev, "mv otg enable error %d\n", retval); + goto err_destroy_workqueue; + } + + mvotg->op_regs = + (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs + + (readl(mvotg->cap_regs) & CAPLENGTH_MASK)); + + if (pdata->id) { + retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq, + NULL, mv_otg_inputs_irq, + IRQF_ONESHOT, "id", mvotg); + if (retval) { + dev_info(&pdev->dev, + "Failed to request irq for ID\n"); + pdata->id = NULL; + } + } + + if (pdata->vbus) { + mvotg->clock_gating = 1; + retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq, + NULL, mv_otg_inputs_irq, + IRQF_ONESHOT, "vbus", mvotg); + if (retval) { + dev_info(&pdev->dev, + "Failed to request irq for VBUS, " + "disable clock gating\n"); + mvotg->clock_gating = 0; + pdata->vbus = NULL; + } + } + + if (pdata->disable_otg_clock_gating) + mvotg->clock_gating = 0; + + mv_otg_reset(mvotg); + mv_otg_init_irq(mvotg); + + r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + retval = -ENODEV; + goto err_disable_clk; + } + + mvotg->irq = r->start; + if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED, + driver_name, mvotg)) { + dev_err(&pdev->dev, "Request irq %d for OTG failed\n", + mvotg->irq); + mvotg->irq = 0; + retval = -ENODEV; + goto err_disable_clk; + } + + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); + if (retval < 0) { + dev_err(&pdev->dev, "can't register transceiver, %d\n", + retval); + goto err_disable_clk; + } + + retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group); + if (retval < 0) { + dev_dbg(&pdev->dev, + "Can't register sysfs attr group: %d\n", retval); + goto err_remove_phy; + } + + spin_lock_init(&mvotg->wq_lock); + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 2 * HZ); + spin_unlock(&mvotg->wq_lock); + } + + dev_info(&pdev->dev, + "successful probe OTG device %s clock gating.\n", + mvotg->clock_gating ? "with" : "without"); + + return 0; + +err_remove_phy: + usb_remove_phy(&mvotg->phy); +err_disable_clk: + mv_otg_disable_internal(mvotg); +err_destroy_workqueue: + flush_workqueue(mvotg->qwork); + destroy_workqueue(mvotg->qwork); + + platform_set_drvdata(pdev, NULL); + + return retval; +} + +#ifdef CONFIG_PM +static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + + if (mvotg->phy.state != OTG_STATE_B_IDLE) { + dev_info(&pdev->dev, + "OTG state is not B_IDLE, it is %d!\n", + mvotg->phy.state); + return -EAGAIN; + } + + if (!mvotg->clock_gating) + mv_otg_disable_internal(mvotg); + + return 0; +} + +static int mv_otg_resume(struct platform_device *pdev) +{ + struct mv_otg *mvotg = platform_get_drvdata(pdev); + u32 otgsc; + + if (!mvotg->clock_gating) { + mv_otg_enable_internal(mvotg); + + otgsc = readl(&mvotg->op_regs->otgsc); + otgsc |= mvotg->irq_en; + writel(otgsc, &mvotg->op_regs->otgsc); + + if (spin_trylock(&mvotg->wq_lock)) { + mv_otg_run_state_machine(mvotg, 0); + spin_unlock(&mvotg->wq_lock); + } + } + return 0; +} +#endif + +static struct platform_driver mv_otg_driver = { + .probe = mv_otg_probe, + .remove = __exit_p(mv_otg_remove), + .driver = { + .owner = THIS_MODULE, + .name = driver_name, + }, +#ifdef CONFIG_PM + .suspend = mv_otg_suspend, + .resume = mv_otg_resume, +#endif +}; +module_platform_driver(mv_otg_driver); diff --git a/drivers/usb/phy/phy-mv-usb.h b/drivers/usb/phy/phy-mv-usb.h new file mode 100644 index 0000000..8a9e351 --- /dev/null +++ b/drivers/usb/phy/phy-mv-usb.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MV_USB_OTG_CONTROLLER__ +#define __MV_USB_OTG_CONTROLLER__ + +#include + +/* Command Register Bit Masks */ +#define USBCMD_RUN_STOP (0x00000001) +#define USBCMD_CTRL_RESET (0x00000002) + +/* otgsc Register Bit Masks */ +#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 +#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 +#define OTGSC_CTRL_OTG_TERM 0x00000008 +#define OTGSC_CTRL_DATA_PULSING 0x00000010 +#define OTGSC_STS_USB_ID 0x00000100 +#define OTGSC_STS_A_VBUS_VALID 0x00000200 +#define OTGSC_STS_A_SESSION_VALID 0x00000400 +#define OTGSC_STS_B_SESSION_VALID 0x00000800 +#define OTGSC_STS_B_SESSION_END 0x00001000 +#define OTGSC_STS_1MS_TOGGLE 0x00002000 +#define OTGSC_STS_DATA_PULSING 0x00004000 +#define OTGSC_INTSTS_USB_ID 0x00010000 +#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 +#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 +#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 +#define OTGSC_INTSTS_B_SESSION_END 0x00100000 +#define OTGSC_INTSTS_1MS 0x00200000 +#define OTGSC_INTSTS_DATA_PULSING 0x00400000 +#define OTGSC_INTR_USB_ID 0x01000000 +#define OTGSC_INTR_A_VBUS_VALID 0x02000000 +#define OTGSC_INTR_A_SESSION_VALID 0x04000000 +#define OTGSC_INTR_B_SESSION_VALID 0x08000000 +#define OTGSC_INTR_B_SESSION_END 0x10000000 +#define OTGSC_INTR_1MS_TIMER 0x20000000 +#define OTGSC_INTR_DATA_PULSING 0x40000000 + +#define CAPLENGTH_MASK (0xff) + +/* Timer's interval, unit 10ms */ +#define T_A_WAIT_VRISE 100 +#define T_A_WAIT_BCON 2000 +#define T_A_AIDL_BDIS 100 +#define T_A_BIDL_ADIS 20 +#define T_B_ASE0_BRST 400 +#define T_B_SE0_SRP 300 +#define T_B_SRP_FAIL 2000 +#define T_B_DATA_PLS 10 +#define T_B_SRP_INIT 100 +#define T_A_SRP_RSPNS 10 +#define T_A_DRV_RSM 5 + +enum otg_function { + OTG_B_DEVICE = 0, + OTG_A_DEVICE +}; + +enum mv_otg_timer { + A_WAIT_BCON_TIMER = 0, + OTG_TIMER_NUM +}; + +/* PXA OTG state machine */ +struct mv_otg_ctrl { + /* internal variables */ + u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */ + u8 b_srp_done; + u8 b_hnp_en; + + /* OTG inputs */ + u8 a_bus_drop; + u8 a_bus_req; + u8 a_clr_err; + u8 a_bus_resume; + u8 a_bus_suspend; + u8 a_conn; + u8 a_sess_vld; + u8 a_srp_det; + u8 a_vbus_vld; + u8 b_bus_req; /* B-Device Require Bus */ + u8 b_bus_resume; + u8 b_bus_suspend; + u8 b_conn; + u8 b_se0_srp; + u8 b_sess_end; + u8 b_sess_vld; + u8 id; + u8 a_suspend_req; + + /*Timer event */ + u8 a_aidl_bdis_timeout; + u8 b_ase0_brst_timeout; + u8 a_bidl_adis_timeout; + u8 a_wait_bcon_timeout; + + struct timer_list timer[OTG_TIMER_NUM]; +}; + +#define VUSBHS_MAX_PORTS 8 + +struct mv_otg_regs { + u32 usbcmd; /* Command register */ + u32 usbsts; /* Status register */ + u32 usbintr; /* Interrupt enable */ + u32 frindex; /* Frame index */ + u32 reserved1[1]; + u32 deviceaddr; /* Device Address */ + u32 eplistaddr; /* Endpoint List Address */ + u32 ttctrl; /* HOST TT status and control */ + u32 burstsize; /* Programmable Burst Size */ + u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ + u32 reserved[4]; + u32 epnak; /* Endpoint NAK */ + u32 epnaken; /* Endpoint NAK Enable */ + u32 configflag; /* Configured Flag register */ + u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ + u32 otgsc; + u32 usbmode; /* USB Host/Device mode */ + u32 epsetupstat; /* Endpoint Setup Status */ + u32 epprime; /* Endpoint Initialize */ + u32 epflush; /* Endpoint De-initialize */ + u32 epstatus; /* Endpoint Status */ + u32 epcomplete; /* Endpoint Interrupt On Complete */ + u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ + u32 mcr; /* Mux Control */ + u32 isr; /* Interrupt Status */ + u32 ier; /* Interrupt Enable */ +}; + +struct mv_otg { + struct usb_phy phy; + struct mv_otg_ctrl otg_ctrl; + + /* base address */ + void __iomem *phy_regs; + void __iomem *cap_regs; + struct mv_otg_regs __iomem *op_regs; + + struct platform_device *pdev; + int irq; + u32 irq_status; + u32 irq_en; + + struct delayed_work work; + struct workqueue_struct *qwork; + + spinlock_t wq_lock; + + struct mv_usb_platform_data *pdata; + + unsigned int active; + unsigned int clock_gating; + unsigned int clknum; + struct clk *clk[0]; +}; + +#endif diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c new file mode 100644 index 0000000..9d4381e --- /dev/null +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -0,0 +1,220 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs_phy" + +#define HW_USBPHY_PWD 0x00 +#define HW_USBPHY_CTRL 0x30 +#define HW_USBPHY_CTRL_SET 0x34 +#define HW_USBPHY_CTRL_CLR 0x38 + +#define BM_USBPHY_CTRL_SFTRST BIT(31) +#define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) +#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) +#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) + +struct mxs_phy { + struct usb_phy phy; + struct clk *clk; +}; + +#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) + +static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + + stmp_reset_block(base + HW_USBPHY_CTRL); + + /* Power up the PHY */ + writel(0, base + HW_USBPHY_PWD); + + /* enable FS/LS device */ + writel(BM_USBPHY_CTRL_ENUTMILEVEL2 | + BM_USBPHY_CTRL_ENUTMILEVEL3, + base + HW_USBPHY_CTRL_SET); +} + +static int mxs_phy_init(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + clk_prepare_enable(mxs_phy->clk); + mxs_phy_hw_init(mxs_phy); + + return 0; +} + +static void mxs_phy_shutdown(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + + writel(BM_USBPHY_CTRL_CLKGATE, + phy->io_priv + HW_USBPHY_CTRL_SET); + + clk_disable_unprepare(mxs_phy->clk); +} + +static int mxs_phy_suspend(struct usb_phy *x, int suspend) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + + if (suspend) { + writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_SET); + clk_disable_unprepare(mxs_phy->clk); + } else { + clk_prepare_enable(mxs_phy->clk); + writel(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_CLR); + writel(0, x->io_priv + HW_USBPHY_PWD); + } + + return 0; +} + +static int mxs_phy_on_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + dev_dbg(phy->dev, "%s speed device has connected\n", + (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + + if (speed == USB_SPEED_HIGH) + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_SET); + + return 0; +} + +static int mxs_phy_on_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + dev_dbg(phy->dev, "%s speed device has disconnected\n", + (speed == USB_SPEED_HIGH) ? "high" : "non-high"); + + if (speed == USB_SPEED_HIGH) + writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, + phy->io_priv + HW_USBPHY_CTRL_CLR); + + return 0; +} + +static int mxs_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + struct clk *clk; + struct mxs_phy *mxs_phy; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENOENT; + } + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, + "can't get the clock, err=%ld", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); + if (!mxs_phy) { + dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); + return -ENOMEM; + } + + mxs_phy->phy.io_priv = base; + mxs_phy->phy.dev = &pdev->dev; + mxs_phy->phy.label = DRIVER_NAME; + mxs_phy->phy.init = mxs_phy_init; + mxs_phy->phy.shutdown = mxs_phy_shutdown; + mxs_phy->phy.set_suspend = mxs_phy_suspend; + mxs_phy->phy.notify_connect = mxs_phy_on_connect; + mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; + + ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); + + mxs_phy->clk = clk; + + platform_set_drvdata(pdev, &mxs_phy->phy); + + ret = usb_add_phy_dev(&mxs_phy->phy); + if (ret) + return ret; + + return 0; +} + +static int mxs_phy_remove(struct platform_device *pdev) +{ + struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&mxs_phy->phy); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id mxs_phy_dt_ids[] = { + { .compatible = "fsl,imx23-usbphy", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); + +static struct platform_driver mxs_phy_driver = { + .probe = mxs_phy_probe, + .remove = mxs_phy_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mxs_phy_dt_ids, + }, +}; + +static int __init mxs_phy_module_init(void) +{ + return platform_driver_register(&mxs_phy_driver); +} +postcore_initcall(mxs_phy_module_init); + +static void __exit mxs_phy_module_exit(void) +{ + platform_driver_unregister(&mxs_phy_driver); +} +module_exit(mxs_phy_module_exit); + +MODULE_ALIAS("platform:mxs-usb-phy"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_AUTHOR("Richard Zhao "); +MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c new file mode 100644 index 0000000..2b10cc9 --- /dev/null +++ b/drivers/usb/phy/phy-nop.c @@ -0,0 +1,294 @@ +/* + * drivers/usb/otg/nop-usb-xceiv.c + * + * NOP USB transceiver for all USB transceiver which are either built-in + * into USB IP or which are mostly autonomous. + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Ajay Kumar Gupta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nop_usb_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +static struct platform_device *pd; + +void usb_nop_xceiv_register(void) +{ + if (pd) + return; + pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); + if (!pd) { + printk(KERN_ERR "Unable to register usb nop transceiver\n"); + return; + } +} +EXPORT_SYMBOL(usb_nop_xceiv_register); + +void usb_nop_xceiv_unregister(void) +{ + platform_device_unregister(pd); + pd = NULL; +} +EXPORT_SYMBOL(usb_nop_xceiv_unregister); + +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static int nop_init(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } +} + +static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + if (!gadget) { + otg->gadget = NULL; + return -ENODEV; + } + + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + return 0; +} + +static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!host) { + otg->host = NULL; + return -ENODEV; + } + + otg->host = host; + return 0; +} + +static int nop_usb_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct nop_usb_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) + return -ENOMEM; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; + } + + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; + } + + nop->dev = &pdev->dev; + nop->phy.dev = nop->dev; + nop->phy.label = "nop-xceiv"; + nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; + nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; + + nop->phy.otg->phy = &nop->phy; + nop->phy.otg->set_host = nop_set_host; + nop->phy.otg->set_peripheral = nop_set_peripheral; + + err = usb_add_phy_dev(&nop->phy); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_add; + } + + platform_set_drvdata(pdev, nop); + + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + + return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; +} + +static int nop_usb_xceiv_remove(struct platform_device *pdev) +{ + struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); + + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + + usb_remove_phy(&nop->phy); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + +static struct platform_driver nop_usb_xceiv_driver = { + .probe = nop_usb_xceiv_probe, + .remove = nop_usb_xceiv_remove, + .driver = { + .name = "nop_usb_xceiv", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nop_xceiv_dt_ids), + }, +}; + +static int __init nop_usb_xceiv_init(void) +{ + return platform_driver_register(&nop_usb_xceiv_driver); +} +subsys_initcall(nop_usb_xceiv_init); + +static void __exit nop_usb_xceiv_exit(void) +{ + platform_driver_unregister(&nop_usb_xceiv_driver); +} +module_exit(nop_usb_xceiv_exit); + +MODULE_ALIAS("platform:nop_usb_xceiv"); +MODULE_AUTHOR("Texas Instruments Inc"); +MODULE_DESCRIPTION("NOP USB Transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c new file mode 100644 index 0000000..1419ced --- /dev/null +++ b/drivers/usb/phy/phy-omap-control.c @@ -0,0 +1,289 @@ +/* + * omap-control-usb.c - The USB part of control module. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct omap_control_usb *control_usb; + +/** + * omap_get_control_dev - returns the device pointer for this control device + * + * This API should be called to get the device pointer for this control + * module device. This device pointer should be used for called other + * exported API's in this driver. + * + * To be used by PHY driver and glue driver. + */ +struct device *omap_get_control_dev(void) +{ + if (!control_usb) + return ERR_PTR(-ENODEV); + + return control_usb->dev; +} +EXPORT_SYMBOL_GPL(omap_get_control_dev); + +/** + * omap_control_usb3_phy_power - power on/off the serializer using control + * module + * @dev: the control module device + * @on: 0 to off and 1 to on based on powering on or off the PHY + * + * usb3 PHY driver should call this API to power on or off the PHY. + */ +void omap_control_usb3_phy_power(struct device *dev, bool on) +{ + u32 val; + unsigned long rate; + struct omap_control_usb *control_usb = dev_get_drvdata(dev); + + if (control_usb->type != OMAP_CTRL_DEV_TYPE2) + return; + + rate = clk_get_rate(control_usb->sys_clk); + rate = rate/1000000; + + val = readl(control_usb->phy_power); + + if (on) { + val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); + val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << + OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; + val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; + } + + writel(val, control_usb->phy_power); +} +EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power); + +/** + * omap_control_usb_phy_power - power on/off the phy using control module reg + * @dev: the control module device + * @on: 0 or 1, based on powering on or off the PHY + */ +void omap_control_usb_phy_power(struct device *dev, int on) +{ + u32 val; + struct omap_control_usb *control_usb = dev_get_drvdata(dev); + + val = readl(control_usb->dev_conf); + + if (on) + val &= ~OMAP_CTRL_DEV_PHY_PD; + else + val |= OMAP_CTRL_DEV_PHY_PD; + + writel(val, control_usb->dev_conf); +} +EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); + +/** + * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core that a usb + * device has been connected. + */ +static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); + val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high + * impedance + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core that it has been + * connected to a usb host. + */ +static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~OMAP_CTRL_DEV_SESSEND; + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | + OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high + * impedance + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core it's now in + * disconnected state. + */ +static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode + * or device mode or to denote disconnected state + * @dev: the control module device + * @mode: The mode to which usb should be configured + * + * This is an API to write to the mailbox register to notify the usb core that + * a usb device has been connected. + */ +void omap_control_usb_set_mode(struct device *dev, + enum omap_control_usb_mode mode) +{ + struct omap_control_usb *ctrl_usb; + + if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1) + return; + + ctrl_usb = dev_get_drvdata(dev); + + switch (mode) { + case USB_MODE_HOST: + omap_control_usb_host_mode(ctrl_usb); + break; + case USB_MODE_DEVICE: + omap_control_usb_device_mode(ctrl_usb); + break; + case USB_MODE_DISCONNECT: + omap_control_usb_set_sessionend(ctrl_usb); + break; + default: + dev_vdbg(dev, "invalid omap control usb mode\n"); + } +} +EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); + +static int omap_control_usb_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device_node *np = pdev->dev.of_node; + struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data; + + control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), + GFP_KERNEL); + if (!control_usb) { + dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); + return -ENOMEM; + } + + if (np) { + of_property_read_u32(np, "ti,type", &control_usb->type); + } else if (pdata) { + control_usb->type = pdata->type; + } else { + dev_err(&pdev->dev, "no pdata present\n"); + return -EINVAL; + } + + control_usb->dev = &pdev->dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "control_dev_conf"); + control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_usb->dev_conf)) + return PTR_ERR(control_usb->dev_conf); + + if (control_usb->type == OMAP_CTRL_DEV_TYPE1) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "otghs_control"); + control_usb->otghs_control = devm_ioremap_resource( + &pdev->dev, res); + if (IS_ERR(control_usb->otghs_control)) + return PTR_ERR(control_usb->otghs_control); + } + + if (control_usb->type == OMAP_CTRL_DEV_TYPE2) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy_power_usb"); + control_usb->phy_power = devm_ioremap_resource( + &pdev->dev, res); + if (IS_ERR(control_usb->phy_power)) + return PTR_ERR(control_usb->phy_power); + + control_usb->sys_clk = devm_clk_get(control_usb->dev, + "sys_clkin"); + if (IS_ERR(control_usb->sys_clk)) { + pr_err("%s: unable to get sys_clkin\n", __func__); + return -EINVAL; + } + } + + + dev_set_drvdata(control_usb->dev, control_usb); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id omap_control_usb_id_table[] = { + { .compatible = "ti,omap-control-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); +#endif + +static struct platform_driver omap_control_usb_driver = { + .probe = omap_control_usb_probe, + .driver = { + .name = "omap-control-usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_control_usb_id_table), + }, +}; + +static int __init omap_control_usb_init(void) +{ + return platform_driver_register(&omap_control_usb_driver); +} +subsys_initcall(omap_control_usb_init); + +static void __exit omap_control_usb_exit(void) +{ + platform_driver_unregister(&omap_control_usb_driver); +} +module_exit(omap_control_usb_exit); + +MODULE_ALIAS("platform: omap_control_usb"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP Control Module USB Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-omap-usb2.c b/drivers/usb/phy/phy-omap-usb2.c new file mode 100644 index 0000000..844ab68 --- /dev/null +++ b/drivers/usb/phy/phy-omap-usb2.c @@ -0,0 +1,273 @@ +/* + * omap-usb2.c - USB PHY, talking to musb controller in OMAP. + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * omap_usb2_set_comparator - links the comparator present in the sytem with + * this phy + * @comparator - the companion phy(comparator) for this phy + * + * The phy companion driver should call this API passing the phy_companion + * filled with set_vbus and start_srp to be used by usb phy. + * + * For use by phy companion driver + */ +int omap_usb2_set_comparator(struct phy_companion *comparator) +{ + struct omap_usb *phy; + struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); + + if (IS_ERR(x)) + return -ENODEV; + + phy = phy_to_omapusb(x); + phy->comparator = comparator; + return 0; +} +EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); + +static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) +{ + struct omap_usb *phy = phy_to_omapusb(otg->phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->set_vbus(phy->comparator, enabled); +} + +static int omap_usb_start_srp(struct usb_otg *otg) +{ + struct omap_usb *phy = phy_to_omapusb(otg->phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->start_srp(phy->comparator); +} + +static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct usb_phy *phy = otg->phy; + + otg->host = host; + if (!host) + phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct usb_phy *phy = otg->phy; + + otg->gadget = gadget; + if (!gadget) + phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb2_suspend(struct usb_phy *x, int suspend) +{ + u32 ret; + struct omap_usb *phy = phy_to_omapusb(x); + + if (suspend && !phy->is_suspended) { + omap_control_usb_phy_power(phy->control_dev, 0); + pm_runtime_put_sync(phy->dev); + phy->is_suspended = 1; + } else if (!suspend && phy->is_suspended) { + ret = pm_runtime_get_sync(phy->dev); + if (ret < 0) { + dev_err(phy->dev, "get_sync failed with err %d\n", + ret); + return ret; + } + omap_control_usb_phy_power(phy->control_dev, 1); + phy->is_suspended = 0; + } + + return 0; +} + +static int omap_usb2_probe(struct platform_device *pdev) +{ + struct omap_usb *phy; + struct usb_otg *otg; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n"); + return -ENOMEM; + } + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) { + dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n"); + return -ENOMEM; + } + + phy->dev = &pdev->dev; + + phy->phy.dev = phy->dev; + phy->phy.label = "omap-usb2"; + phy->phy.set_suspend = omap_usb2_suspend; + phy->phy.otg = otg; + phy->phy.type = USB_PHY_TYPE_USB2; + + phy->control_dev = omap_get_control_dev(); + if (IS_ERR(phy->control_dev)) { + dev_dbg(&pdev->dev, "Failed to get control device\n"); + return -ENODEV; + } + + phy->is_suspended = 1; + omap_control_usb_phy_power(phy->control_dev, 0); + + otg->set_host = omap_usb_set_host; + otg->set_peripheral = omap_usb_set_peripheral; + otg->set_vbus = omap_usb_set_vbus; + otg->start_srp = omap_usb_start_srp; + otg->phy = &phy->phy; + + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); + return PTR_ERR(phy->wkupclk); + } + clk_prepare(phy->wkupclk); + + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) + dev_vdbg(&pdev->dev, "unable to get refclk960m\n"); + else + clk_prepare(phy->optclk); + + usb_add_phy_dev(&phy->phy); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + + return 0; +} + +static int omap_usb2_remove(struct platform_device *pdev) +{ + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_unprepare(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_unprepare(phy->optclk); + usb_remove_phy(&phy->phy); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int omap_usb2_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_disable(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_disable(phy->optclk); + + return 0; +} + +static int omap_usb2_runtime_resume(struct device *dev) +{ + u32 ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + ret = clk_enable(phy->wkupclk); + if (ret < 0) { + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto err0; + } + + if (!IS_ERR(phy->optclk)) { + ret = clk_enable(phy->optclk); + if (ret < 0) { + dev_err(phy->dev, "Failed to enable optclk %d\n", ret); + goto err1; + } + } + + return 0; + +err1: + clk_disable(phy->wkupclk); + +err0: + return ret; +} + +static const struct dev_pm_ops omap_usb2_pm_ops = { + SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&omap_usb2_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id omap_usb2_id_table[] = { + { .compatible = "ti,omap-usb2" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_usb2_id_table); +#endif + +static struct platform_driver omap_usb2_driver = { + .probe = omap_usb2_probe, + .remove = omap_usb2_remove, + .driver = { + .name = "omap-usb2", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(omap_usb2_id_table), + }, +}; + +module_platform_driver(omap_usb2_driver); + +MODULE_ALIAS("platform: omap_usb2"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP USB2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c new file mode 100644 index 0000000..a6e60b1 --- /dev/null +++ b/drivers/usb/phy/phy-omap-usb3.c @@ -0,0 +1,353 @@ +/* + * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_SYS_CLKS 5 +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 0x9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0x0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 0x1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 0x1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 0x9 +#define SET_PLL_GO 0x1 +#define PLL_TICOPWDN 0x10000 +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +/* + * This is an Empirical value that works, need to confirm the actual + * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status + * to be correctly reflected in the USB3PHY_PLL_STATUS register. + */ +# define PLL_IDLE_TIME 100; + +enum sys_clk_rate { + CLK_RATE_UNDEFINED = -1, + CLK_RATE_12MHZ, + CLK_RATE_16MHZ, + CLK_RATE_19MHZ, + CLK_RATE_26MHZ, + CLK_RATE_38MHZ +}; + +static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = { + {1250, 5, 4, 20, 0}, /* 12 MHz */ + {3125, 20, 4, 20, 0}, /* 16.8 MHz */ + {1172, 8, 4, 20, 65537}, /* 19.2 MHz */ + {1250, 12, 4, 20, 0}, /* 26 MHz */ + {3125, 47, 4, 20, 92843}, /* 38.4 MHz */ +}; + +static int omap_usb3_suspend(struct usb_phy *x, int suspend) +{ + struct omap_usb *phy = phy_to_omapusb(x); + int val; + int timeout = PLL_IDLE_TIME; + + if (suspend && !phy->is_suspended) { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val |= PLL_IDLE; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_TICOPWDN) + break; + udelay(1); + } while (--timeout); + + omap_control_usb3_phy_power(phy->control_dev, 0); + + phy->is_suspended = 1; + } else if (!suspend && phy->is_suspended) { + phy->is_suspended = 0; + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_IDLE; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (!(val & PLL_TICOPWDN)) + break; + udelay(1); + } while (--timeout); + } + + return 0; +} + +static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate) +{ + switch (rate) { + case 12000000: + return CLK_RATE_12MHZ; + case 16800000: + return CLK_RATE_16MHZ; + case 19200000: + return CLK_RATE_19MHZ; + case 26000000: + return CLK_RATE_26MHZ; + case 38400000: + return CLK_RATE_38MHZ; + default: + return CLK_RATE_UNDEFINED; + } +} + +static void omap_usb_dpll_relock(struct omap_usb *phy) +{ + u32 val; + unsigned long timeout; + + omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + timeout = jiffies + msecs_to_jiffies(20); + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + break; + } while (!WARN_ON(time_after(jiffies, timeout))); +} + +static int omap_usb_dpll_lock(struct omap_usb *phy) +{ + u32 val; + unsigned long rate; + enum sys_clk_rate clk_index; + + rate = clk_get_rate(phy->sys_clk); + clk_index = __get_sys_clk_index(rate); + + if (clk_index == CLK_RATE_UNDEFINED) { + pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate); + return -EINVAL; + } + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); + + omap_usb_dpll_relock(phy); + + return 0; +} + +static int omap_usb3_init(struct usb_phy *x) +{ + struct omap_usb *phy = phy_to_omapusb(x); + + omap_usb_dpll_lock(phy); + omap_control_usb3_phy_power(phy->control_dev, 1); + + return 0; +} + +static int omap_usb3_probe(struct platform_device *pdev) +{ + struct omap_usb *phy; + struct resource *res; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n"); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); + phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->pll_ctrl_base)) + return PTR_ERR(phy->pll_ctrl_base); + + phy->dev = &pdev->dev; + + phy->phy.dev = phy->dev; + phy->phy.label = "omap-usb3"; + phy->phy.init = omap_usb3_init; + phy->phy.set_suspend = omap_usb3_suspend; + phy->phy.type = USB_PHY_TYPE_USB3; + + phy->is_suspended = 1; + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); + return PTR_ERR(phy->wkupclk); + } + clk_prepare(phy->wkupclk); + + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) { + dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n"); + return PTR_ERR(phy->optclk); + } + clk_prepare(phy->optclk); + + phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin"); + if (IS_ERR(phy->sys_clk)) { + pr_err("%s: unable to get sys_clkin\n", __func__); + return -EINVAL; + } + + phy->control_dev = omap_get_control_dev(); + if (IS_ERR(phy->control_dev)) { + dev_dbg(&pdev->dev, "Failed to get control device\n"); + return -ENODEV; + } + + omap_control_usb3_phy_power(phy->control_dev, 0); + usb_add_phy_dev(&phy->phy); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + pm_runtime_get(&pdev->dev); + + return 0; +} + +static int omap_usb3_remove(struct platform_device *pdev) +{ + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_unprepare(phy->wkupclk); + clk_unprepare(phy->optclk); + usb_remove_phy(&phy->phy); + if (!pm_runtime_suspended(&pdev->dev)) + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int omap_usb3_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_disable(phy->wkupclk); + clk_disable(phy->optclk); + + return 0; +} + +static int omap_usb3_runtime_resume(struct device *dev) +{ + u32 ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + ret = clk_enable(phy->optclk); + if (ret) { + dev_err(phy->dev, "Failed to enable optclk %d\n", ret); + goto err1; + } + + ret = clk_enable(phy->wkupclk); + if (ret) { + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto err2; + } + + return 0; + +err2: + clk_disable(phy->optclk); + +err1: + return ret; +} + +static const struct dev_pm_ops omap_usb3_pm_ops = { + SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&omap_usb3_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id omap_usb3_id_table[] = { + { .compatible = "ti,omap-usb3" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_usb3_id_table); +#endif + +static struct platform_driver omap_usb3_driver = { + .probe = omap_usb3_probe, + .remove = omap_usb3_remove, + .driver = { + .name = "omap-usb3", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(omap_usb3_id_table), + }, +}; + +module_platform_driver(omap_usb3_driver); + +MODULE_ALIAS("platform: omap_usb3"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP USB3 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c new file mode 100644 index 0000000..a35681b --- /dev/null +++ b/drivers/usb/phy/phy-rcar-usb.c @@ -0,0 +1,220 @@ +/* + * Renesas R-Car USB phy driver + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +/* USBH common register */ +#define USBPCTRL0 0x0800 +#define USBPCTRL1 0x0804 +#define USBST 0x0808 +#define USBEH0 0x080C +#define USBOH0 0x081C +#define USBCTL0 0x0858 +#define EIIBC1 0x0094 +#define EIIBC2 0x009C + +/* USBPCTRL1 */ +#define PHY_RST (1 << 2) +#define PLL_ENB (1 << 1) +#define PHY_ENB (1 << 0) + +/* USBST */ +#define ST_ACT (1 << 31) +#define ST_PLL (1 << 30) + +struct rcar_usb_phy_priv { + struct usb_phy phy; + spinlock_t lock; + + void __iomem *reg0; + void __iomem *reg1; + int counter; +}; + +#define usb_phy_to_priv(p) container_of(p, struct rcar_usb_phy_priv, phy) + + +/* + * USB initial/install operation. + * + * This function setup USB phy. + * The used value and setting order came from + * [USB :: Initial setting] on datasheet. + */ +static int rcar_usb_phy_init(struct usb_phy *phy) +{ + struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy); + struct device *dev = phy->dev; + void __iomem *reg0 = priv->reg0; + void __iomem *reg1 = priv->reg1; + int i; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->counter++ == 0) { + + /* + * USB phy start-up + */ + + /* (1) USB-PHY standby release */ + iowrite32(PHY_ENB, (reg0 + USBPCTRL1)); + + /* (2) start USB-PHY internal PLL */ + iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1)); + + /* (3) USB module status check */ + for (i = 0; i < 1024; i++) { + udelay(10); + val = ioread32(reg0 + USBST); + if (val == (ST_ACT | ST_PLL)) + break; + } + + if (val != (ST_ACT | ST_PLL)) { + dev_err(dev, "USB phy not ready\n"); + goto phy_init_end; + } + + /* (4) USB-PHY reset clear */ + iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1)); + + /* set platform specific port settings */ + iowrite32(0x00000000, (reg0 + USBPCTRL0)); + + /* + * EHCI IP internal buffer setting + * EHCI IP internal buffer enable + * + * These are recommended value of a datasheet + * see [USB :: EHCI internal buffer setting] + */ + iowrite32(0x00ff0040, (reg0 + EIIBC1)); + iowrite32(0x00ff0040, (reg1 + EIIBC1)); + + iowrite32(0x00000001, (reg0 + EIIBC2)); + iowrite32(0x00000001, (reg1 + EIIBC2)); + + /* + * Bus alignment settings + */ + + /* (1) EHCI bus alignment (little endian) */ + iowrite32(0x00000000, (reg0 + USBEH0)); + + /* (1) OHCI bus alignment (little endian) */ + iowrite32(0x00000000, (reg0 + USBOH0)); + } + +phy_init_end: + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void rcar_usb_phy_shutdown(struct usb_phy *phy) +{ + struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy); + void __iomem *reg0 = priv->reg0; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->counter-- == 1) { /* last user */ + iowrite32(0x00000000, (reg0 + USBPCTRL0)); + iowrite32(0x00000000, (reg0 + USBPCTRL1)); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rcar_usb_phy_probe(struct platform_device *pdev) +{ + struct rcar_usb_phy_priv *priv; + struct resource *res0, *res1; + struct device *dev = &pdev->dev; + void __iomem *reg0, *reg1; + int ret; + + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res0 || !res1) { + dev_err(dev, "Not enough platform resources\n"); + return -EINVAL; + } + + /* + * CAUTION + * + * Because this phy address is also mapped under OHCI/EHCI address area, + * this driver can't use devm_request_and_ioremap(dev, res) here + */ + reg0 = devm_ioremap_nocache(dev, res0->start, resource_size(res0)); + reg1 = devm_ioremap_nocache(dev, res1->start, resource_size(res1)); + if (!reg0 || !reg1) { + dev_err(dev, "ioremap error\n"); + return -ENOMEM; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "priv data allocation error\n"); + return -ENOMEM; + } + + priv->reg0 = reg0; + priv->reg1 = reg1; + priv->counter = 0; + priv->phy.dev = dev; + priv->phy.label = dev_name(dev); + priv->phy.init = rcar_usb_phy_init; + priv->phy.shutdown = rcar_usb_phy_shutdown; + spin_lock_init(&priv->lock); + + ret = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); + if (ret < 0) { + dev_err(dev, "usb phy addition error\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + + return ret; +} + +static int rcar_usb_phy_remove(struct platform_device *pdev) +{ + struct rcar_usb_phy_priv *priv = platform_get_drvdata(pdev); + + usb_remove_phy(&priv->phy); + + return 0; +} + +static struct platform_driver rcar_usb_phy_driver = { + .driver = { + .name = "rcar_usb_phy", + }, + .probe = rcar_usb_phy_probe, + .remove = rcar_usb_phy_remove, +}; + +module_platform_driver(rcar_usb_phy_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car USB phy"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c new file mode 100644 index 0000000..967101e --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -0,0 +1,928 @@ +/* linux/drivers/usb/phy/samsung-usbphy.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri + * + * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) + +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) +#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) +#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) + +#define FSEL_CLKSEL_50M (0x7) +#define FSEL_CLKSEL_24M (0x5) +#define FSEL_CLKSEL_20M (0x4) +#define FSEL_CLKSEL_19200K (0x3) +#define FSEL_CLKSEL_12M (0x2) +#define FSEL_CLKSEL_10M (0x1) +#define FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) +#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) +#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) +#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) +#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) +#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) +#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#ifndef KHZ +#define KHZ (1000) +#endif + +#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) +#define S3C64XX_USBPHY_ENABLE (0x1 << 16) +#define EXYNOS_USBPHY_ENABLE (0x1 << 0) +#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, + TYPE_EXYNOS5250, +}; + +/* + * struct samsung_usbphy_drvdata - driver data for various SoC variants + * @cpu_type: machine identifier + * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @hostphy_en_mask: host phy enable mask for PHY CONTROL register + * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from + * mapped address of system controller. + * @hostphy_reg_offset: offset to HOST PHY CONTROL register from + * mapped address of system controller. + * + * Here we have a separate mask for device type phy. + * Having different masks for host and device type phy helps + * in setting independent masks in case of SoCs like S5PV210, + * in which PHY0 and PHY1 enable bits belong to same register + * placed at position 0 and 1 respectively. + * Although for newer SoCs like exynos these bits belong to + * different registers altogether placed at position 0. + */ +struct samsung_usbphy_drvdata { + int cpu_type; + int devphy_en_mask; + int hostphy_en_mask; + u32 devphy_reg_offset; + u32 hostphy_reg_offset; +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy controller registers memory base + * @pmuregs: USB device PHY_CONTROL register memory base + * @sysreg: USB2.0 PHY_CFG register memory base + * @ref_clk_freq: reference clock frequency selection + * @drv_data: driver data available for different SoCs + * @phy_type: Samsung SoCs specific phy types: #HOST + * #DEVICE + * @phy_usage: usage count for phy + * @lock: lock for phy operations + */ +struct samsung_usbphy { + struct usb_phy phy; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + void __iomem *pmuregs; + void __iomem *sysreg; + int ref_clk_freq; + const struct samsung_usbphy_drvdata *drv_data; + enum samsung_usb_phy_type phy_type; + atomic_t phy_usage; + spinlock_t lock; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) + +int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!otg->host) + otg->host = host; + + return 0; +} + +static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) +{ + struct device_node *usbphy_sys; + + /* Getting node for system controller interface for usb-phy */ + usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); + if (!usbphy_sys) { + dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); + return -ENODEV; + } + + sphy->pmuregs = of_iomap(usbphy_sys, 0); + + if (sphy->pmuregs == NULL) { + dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); + goto err0; + } + + sphy->sysreg = of_iomap(usbphy_sys, 1); + + /* + * Not returning error code here, since this situation is not fatal. + * Few SoCs may not have this switch available + */ + if (sphy->sysreg == NULL) + dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); + + of_node_put(usbphy_sys); + + return 0; + +err0: + of_node_put(usbphy_sys); + return -ENXIO; +} + +/* + * Set isolation here for phy. + * Here 'on = true' would mean USB PHY block is isolated, hence + * de-activated and vice-versa. + */ +static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) +{ + void __iomem *reg = NULL; + u32 reg_val; + u32 en_mask = 0; + + if (!sphy->pmuregs) { + dev_warn(sphy->dev, "Can't set pmu isolation\n"); + return; + } + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + /* + * Do nothing: We will add here once S3C64xx goes for DT support + */ + break; + case TYPE_EXYNOS4210: + /* + * Fall through since exynos4210 and exynos5250 have similar + * register architecture: two separate registers for host and + * device phy control with enable bit at position 0. + */ + case TYPE_EXYNOS5250: + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { + reg = sphy->pmuregs + + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { + reg = sphy->pmuregs + + sphy->drv_data->hostphy_reg_offset; + en_mask = sphy->drv_data->hostphy_en_mask; + } + break; + default: + dev_err(sphy->dev, "Invalid SoC type\n"); + return; + } + + reg_val = readl(reg); + + if (on) + reg_val &= ~en_mask; + else + reg_val |= en_mask; + + writel(reg_val, reg); +} + +/* + * Configure the mode of working of usb-phy here: HOST/DEVICE. + */ +static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) +{ + u32 reg; + + if (!sphy->sysreg) { + dev_warn(sphy->dev, "Can't configure specified phy mode\n"); + return; + } + + reg = readl(sphy->sysreg); + + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) + reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; + else if (sphy->phy_type == USB_PHY_TYPE_HOST) + reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; + + writel(reg, sphy->sysreg); +} + +/* + * PHYs are different for USB Device and USB Host. + * This make sure that correct PHY type is selected before + * any operation on PHY. + */ +static int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type) +{ + struct samsung_usbphy *sphy = phy_to_sphy(phy); + + sphy->phy_type = phy_type; + + return 0; +} + +/* + * Returns reference clock frequency selection value + */ +static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +{ + struct clk *ref_clk; + int refclk_freq = 0; + + /* + * In exynos5250 USB host and device PHY use + * external crystal clock XXTI + */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + ref_clk = clk_get(sphy->dev, "ext_xtal"); + else + ref_clk = clk_get(sphy->dev, "xusbxti"); + if (IS_ERR(ref_clk)) { + dev_err(sphy->dev, "Failed to get reference clock\n"); + return PTR_ERR(ref_clk); + } + + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) { + /* set clock frequency for PLL */ + switch (clk_get_rate(ref_clk)) { + case 9600 * KHZ: + refclk_freq = FSEL_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = FSEL_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = FSEL_CLKSEL_12M; + break; + case 19200 * KHZ: + refclk_freq = FSEL_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = FSEL_CLKSEL_20M; + break; + case 50 * MHZ: + refclk_freq = FSEL_CLKSEL_50M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = FSEL_CLKSEL_24M; + break; + } + } else { + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: + refclk_freq = PHYCLK_CLKSEL_24M; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->drv_data->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } + } + clk_put(ref_clk); + + return refclk_freq; +} + +static bool exynos5_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + return !(reg & HOST_CTRL0_SIDDQ); +} + +static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + +static void samsung_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* Configure PHY0 for normal operation*/ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + +static void samsung_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* Disable analog and otg block power */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usbphy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + int ret = 0; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false); + else + samsung_usbphy_set_isolation(sphy, false); + + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + samsung_usbphy_cfg_sel(sphy); + + /* Initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_enable(sphy); + else + samsung_usbphy_enable(sphy); + + spin_unlock_irqrestore(&sphy->lock, flags); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usbphy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* De-initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_disable(sphy); + else + samsung_usbphy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true); + else + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); + + clk_disable_unprepare(sphy->clk); +} + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline const struct samsung_usbphy_drvdata +*samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + return match->data; + } + + return (struct samsung_usbphy_drvdata *) + platform_get_device_id(pdev)->driver_data; +} + +static int samsung_usbphy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct usb_otg *otg; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + const struct samsung_usbphy_drvdata *drv_data; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + int ret; + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + drv_data = samsung_usbphy_get_driver_data(pdev); + + if (drv_data->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->drv_data = drv_data; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usbphy"; + sphy->phy.init = samsung_usbphy_init; + sphy->phy.shutdown = samsung_usbphy_shutdown; + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + sphy->phy.otg = otg; + sphy->phy.otg->phy = &sphy->phy; + sphy->phy.otg->set_host = samsung_usbphy_set_host; + + spin_lock_init(&sphy->lock); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); +} + +static int samsung_usbphy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); + + return 0; +} + +static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { + .cpu_type = TYPE_S3C64XX, + .devphy_en_mask = S3C64XX_USBPHY_ENABLE, +}; + +static const struct samsung_usbphy_drvdata usbphy_exynos4 = { + .cpu_type = TYPE_EXYNOS4210, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +static struct samsung_usbphy_drvdata usbphy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usbphy", + .data = &usbphy_s3c64xx, + }, { + .compatible = "samsung,exynos4210-usbphy", + .data = &usbphy_exynos4, + }, { + .compatible = "samsung,exynos5250-usbphy", + .data = &usbphy_exynos5 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usbphy", + .driver_data = (unsigned long)&usbphy_s3c64xx, + }, { + .name = "exynos4210-usbphy", + .driver_data = (unsigned long)&usbphy_exynos4, + }, { + .name = "exynos5250-usbphy", + .driver_data = (unsigned long)&usbphy_exynos5, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usbphy_driver = { + .probe = samsung_usbphy_probe, + .remove = samsung_usbphy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usbphy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usbphy_driver); + +MODULE_DESCRIPTION("Samsung USB phy controller"); +MODULE_AUTHOR("Praveen Paneri "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usbphy"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c new file mode 100644 index 0000000..5487d38 --- /dev/null +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling + * Benoit Goby + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEGRA_USB_BASE 0xC5000000 +#define TEGRA_USB_SIZE SZ_16K + +#define ULPI_VIEWPORT 0x170 + +#define USB_SUSP_CTRL 0x400 +#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) +#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) +#define USB_SUSP_CLR (1 << 5) +#define USB_PHY_CLK_VALID (1 << 7) +#define UTMIP_RESET (1 << 11) +#define UHSIC_RESET (1 << 11) +#define UTMIP_PHY_ENABLE (1 << 12) +#define ULPI_PHY_ENABLE (1 << 13) +#define USB_SUSP_SET (1 << 14) +#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) + +#define USB1_LEGACY_CTRL 0x410 +#define USB1_NO_LEGACY_MODE (1 << 0) +#define USB1_VBUS_SENSE_CTL_MASK (3 << 1) +#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1) +#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \ + (1 << 1) +#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1) +#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1) + +#define ULPI_TIMING_CTRL_0 0x424 +#define ULPI_OUTPUT_PINMUX_BYP (1 << 10) +#define ULPI_CLKOUT_PINMUX_BYP (1 << 11) + +#define ULPI_TIMING_CTRL_1 0x428 +#define ULPI_DATA_TRIMMER_LOAD (1 << 0) +#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) +#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16) +#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) +#define ULPI_DIR_TRIMMER_LOAD (1 << 24) +#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) + +#define UTMIP_PLL_CFG1 0x804 +#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) + +#define UTMIP_XCVR_CFG0 0x808 +#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) +#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) +#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) +#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) +#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) +#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) +#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) + +#define UTMIP_BIAS_CFG0 0x80c +#define UTMIP_OTGPD (1 << 11) +#define UTMIP_BIASPD (1 << 10) + +#define UTMIP_HSRX_CFG0 0x810 +#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) +#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) + +#define UTMIP_HSRX_CFG1 0x814 +#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UTMIP_TX_CFG0 0x820 +#define UTMIP_FS_PREABMLE_J (1 << 19) +#define UTMIP_HS_DISCON_DISABLE (1 << 8) + +#define UTMIP_MISC_CFG0 0x824 +#define UTMIP_DPDM_OBSERVE (1 << 26) +#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) +#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) +#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) +#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) +#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) + +#define UTMIP_MISC_CFG1 0x828 +#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) +#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) + +#define UTMIP_DEBOUNCE_CFG0 0x82c +#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) + +#define UTMIP_BAT_CHRG_CFG0 0x830 +#define UTMIP_PD_CHRG (1 << 0) + +#define UTMIP_SPARE_CFG0 0x834 +#define FUSE_SETUP_SEL (1 << 3) + +#define UTMIP_XCVR_CFG1 0x838 +#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) +#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) +#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) + +#define UTMIP_BIAS_CFG1 0x83c +#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) + +static DEFINE_SPINLOCK(utmip_pad_lock); +static int utmip_pad_count; + +struct tegra_xtal_freq { + int freq; + u8 enable_delay; + u8 stable_count; + u8 active_delay; + u8 xtal_freq_count; + u16 debounce; +}; + +static const struct tegra_xtal_freq tegra_freq_table[] = { + { + .freq = 12000000, + .enable_delay = 0x02, + .stable_count = 0x2F, + .active_delay = 0x04, + .xtal_freq_count = 0x76, + .debounce = 0x7530, + }, + { + .freq = 13000000, + .enable_delay = 0x02, + .stable_count = 0x33, + .active_delay = 0x05, + .xtal_freq_count = 0x7F, + .debounce = 0x7EF4, + }, + { + .freq = 19200000, + .enable_delay = 0x03, + .stable_count = 0x4B, + .active_delay = 0x06, + .xtal_freq_count = 0xBB, + .debounce = 0xBB80, + }, + { + .freq = 26000000, + .enable_delay = 0x04, + .stable_count = 0x66, + .active_delay = 0x09, + .xtal_freq_count = 0xFE, + .debounce = 0xFDE8, + }, +}; + +static struct tegra_utmip_config utmip_default[] = { + [0] = { + .hssync_start_delay = 9, + .idle_wait_delay = 17, + .elastic_limit = 16, + .term_range_adj = 6, + .xcvr_setup = 9, + .xcvr_lsfslew = 1, + .xcvr_lsrslew = 1, + }, + [2] = { + .hssync_start_delay = 9, + .idle_wait_delay = 17, + .elastic_limit = 16, + .term_range_adj = 6, + .xcvr_setup = 9, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + }, +}; + +static int utmip_pad_open(struct tegra_usb_phy *phy) +{ + phy->pad_clk = clk_get_sys("utmip-pad", NULL); + if (IS_ERR(phy->pad_clk)) { + pr_err("%s: can't get utmip pad clock\n", __func__); + return PTR_ERR(phy->pad_clk); + } + + if (phy->is_legacy_phy) { + phy->pad_regs = phy->regs; + } else { + phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); + if (!phy->pad_regs) { + pr_err("%s: can't remap usb registers\n", __func__); + clk_put(phy->pad_clk); + return -ENOMEM; + } + } + return 0; +} + +static void utmip_pad_close(struct tegra_usb_phy *phy) +{ + if (!phy->is_legacy_phy) + iounmap(phy->pad_regs); + clk_put(phy->pad_clk); +} + +static void utmip_pad_power_on(struct tegra_usb_phy *phy) +{ + unsigned long val, flags; + void __iomem *base = phy->pad_regs; + + clk_prepare_enable(phy->pad_clk); + + spin_lock_irqsave(&utmip_pad_lock, flags); + + if (utmip_pad_count++ == 0) { + val = readl(base + UTMIP_BIAS_CFG0); + val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); + writel(val, base + UTMIP_BIAS_CFG0); + } + + spin_unlock_irqrestore(&utmip_pad_lock, flags); + + clk_disable_unprepare(phy->pad_clk); +} + +static int utmip_pad_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val, flags; + void __iomem *base = phy->pad_regs; + + if (!utmip_pad_count) { + pr_err("%s: utmip pad already powered off\n", __func__); + return -EINVAL; + } + + clk_prepare_enable(phy->pad_clk); + + spin_lock_irqsave(&utmip_pad_lock, flags); + + if (--utmip_pad_count == 0) { + val = readl(base + UTMIP_BIAS_CFG0); + val |= UTMIP_OTGPD | UTMIP_BIASPD; + writel(val, base + UTMIP_BIAS_CFG0); + } + + spin_unlock_irqrestore(&utmip_pad_lock, flags); + + clk_disable_unprepare(phy->pad_clk); + + return 0; +} + +static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) +{ + unsigned long timeout = 2000; + do { + if ((readl(reg) & mask) == result) + return 0; + udelay(1); + timeout--; + } while (timeout); + return -1; +} + +static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + if (phy->is_legacy_phy) { + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + + udelay(10); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + } else + tegra_ehci_set_phcd(&phy->u_phy, true); + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); +} + +static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + if (phy->is_legacy_phy) { + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + + udelay(10); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + } else + tegra_ehci_set_phcd(&phy->u_phy, false); + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID)) + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); +} + +static int utmi_phy_power_on(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_utmip_config *config = phy->config; + + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + if (phy->is_legacy_phy) { + val = readl(base + USB1_LEGACY_CTRL); + val |= USB1_NO_LEGACY_MODE; + writel(val, base + USB1_LEGACY_CTRL); + } + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_FS_PREABMLE_J; + writel(val, base + UTMIP_TX_CFG0); + + val = readl(base + UTMIP_HSRX_CFG0); + val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); + val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); + val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); + writel(val, base + UTMIP_HSRX_CFG0); + + val = readl(base + UTMIP_HSRX_CFG1); + val &= ~UTMIP_HS_SYNC_START_DLY(~0); + val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); + writel(val, base + UTMIP_HSRX_CFG1); + + val = readl(base + UTMIP_DEBOUNCE_CFG0); + val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); + val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); + writel(val, base + UTMIP_DEBOUNCE_CFG0); + + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; + writel(val, base + UTMIP_MISC_CFG0); + + val = readl(base + UTMIP_MISC_CFG1); + val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); + val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | + UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); + writel(val, base + UTMIP_MISC_CFG1); + + val = readl(base + UTMIP_PLL_CFG1); + val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); + writel(val, base + UTMIP_PLL_CFG1); + + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + val = readl(base + USB_SUSP_CTRL); + val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); + writel(val, base + USB_SUSP_CTRL); + } + + utmip_pad_power_on(phy); + + val = readl(base + UTMIP_XCVR_CFG0); + val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | + UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) | + UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | + UTMIP_XCVR_HSSLEW_MSB(~0)); + val |= UTMIP_XCVR_SETUP(config->xcvr_setup); + val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); + val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); + writel(val, base + UTMIP_XCVR_CFG0); + + val = readl(base + UTMIP_XCVR_CFG1); + val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | + UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); + val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); + writel(val, base + UTMIP_XCVR_CFG1); + + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val &= ~UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + + val = readl(base + UTMIP_BIAS_CFG1); + val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); + val |= UTMIP_BIAS_PDTRK_COUNT(0x5); + writel(val, base + UTMIP_BIAS_CFG1); + + if (phy->is_legacy_phy) { + val = readl(base + UTMIP_SPARE_CFG0); + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) + val &= ~FUSE_SETUP_SEL; + else + val |= FUSE_SETUP_SEL; + writel(val, base + UTMIP_SPARE_CFG0); + } else { + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + } + + val = readl(base + USB_SUSP_CTRL); + val &= ~UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + if (phy->is_legacy_phy) { + val = readl(base + USB1_LEGACY_CTRL); + val &= ~USB1_VBUS_SENSE_CTL_MASK; + val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; + writel(val, base + USB1_LEGACY_CTRL); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_SET; + writel(val, base + USB_SUSP_CTRL); + } + + utmi_phy_clk_enable(phy); + + if (!phy->is_legacy_phy) + tegra_ehci_set_pts(&phy->u_phy, 0); + + return 0; +} + +static int utmi_phy_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + utmi_phy_clk_disable(phy); + + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); + val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); + writel(val, base + USB_SUSP_CTRL); + } + + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + + val = readl(base + UTMIP_XCVR_CFG0); + val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | + UTMIP_FORCE_PDZI_POWERDOWN; + writel(val, base + UTMIP_XCVR_CFG0); + + val = readl(base + UTMIP_XCVR_CFG1); + val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | + UTMIP_FORCE_PDDR_POWERDOWN; + writel(val, base + UTMIP_XCVR_CFG1); + + return utmip_pad_power_off(phy); +} + +static void utmi_phy_preresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val |= UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_postresume(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_TX_CFG0); + val &= ~UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); +} + +static void utmi_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) + val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; + else + val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; + writel(val, base + UTMIP_MISC_CFG0); + udelay(1); + + val = readl(base + UTMIP_MISC_CFG0); + val |= UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + udelay(10); +} + +static void utmi_phy_restore_end(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + udelay(10); +} + +static int ulpi_phy_power_on(struct tegra_usb_phy *phy) +{ + int ret; + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + gpio_direction_output(config->reset_gpio, 0); + msleep(5); + gpio_direction_output(config->reset_gpio, 1); + + clk_prepare_enable(phy->clk); + msleep(1); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); + + val = readl(base + USB_SUSP_CTRL); + val |= ULPI_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + + val = 0; + writel(val, base + ULPI_TIMING_CTRL_1); + + val |= ULPI_DATA_TRIMMER_SEL(4); + val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); + val |= ULPI_DIR_TRIMMER_SEL(4); + writel(val, base + ULPI_TIMING_CTRL_1); + udelay(10); + + val |= ULPI_DATA_TRIMMER_LOAD; + val |= ULPI_STPDIRNXT_TRIMMER_LOAD; + val |= ULPI_DIR_TRIMMER_LOAD; + writel(val, base + ULPI_TIMING_CTRL_1); + + /* Fix VbusInvalid due to floating VBUS */ + ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } + + ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } + + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + udelay(100); + + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); + + return 0; +} + +static int ulpi_phy_power_off(struct tegra_usb_phy *phy) +{ + struct tegra_ulpi_config *config = phy->config; + + clk_disable(phy->clk); + return gpio_direction_output(config->reset_gpio, 0); +} + +static int tegra_phy_init(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + struct tegra_ulpi_config *ulpi_config; + int err; + + if (phy->is_ulpi_phy) { + ulpi_config = phy->config; + phy->clk = clk_get_sys(NULL, ulpi_config->clk); + if (IS_ERR(phy->clk)) { + pr_err("%s: can't get ulpi clock\n", __func__); + err = -ENXIO; + goto err1; + } + if (!gpio_is_valid(ulpi_config->reset_gpio)) + ulpi_config->reset_gpio = + of_get_named_gpio(phy->dev->of_node, + "nvidia,phy-reset-gpio", 0); + if (!gpio_is_valid(ulpi_config->reset_gpio)) { + pr_err("%s: invalid reset gpio: %d\n", __func__, + ulpi_config->reset_gpio); + err = -EINVAL; + goto err1; + } + gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); + gpio_direction_output(ulpi_config->reset_gpio, 0); + phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); + phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; + } else { + err = utmip_pad_open(phy); + if (err < 0) + goto err1; + } + return 0; +err1: + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); + return err; +} + +static void tegra_usb_phy_close(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (phy->is_ulpi_phy) + clk_put(phy->clk); + else + utmip_pad_close(phy); + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); + kfree(phy); +} + +static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) +{ + if (phy->is_ulpi_phy) + return ulpi_phy_power_on(phy); + else + return utmi_phy_power_on(phy); +} + +static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) +{ + if (phy->is_ulpi_phy) + return ulpi_phy_power_off(phy); + else + return utmi_phy_power_off(phy); +} + +static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (suspend) + return tegra_usb_phy_power_off(phy); + else + return tegra_usb_phy_power_on(phy); +} + +struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, + void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) +{ + struct tegra_usb_phy *phy; + unsigned long parent_rate; + int i; + int err; + struct device_node *np = dev->of_node; + + phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->instance = instance; + phy->regs = regs; + phy->config = config; + phy->mode = phy_mode; + phy->dev = dev; + phy->is_legacy_phy = + of_property_read_bool(np, "nvidia,has-legacy-mode"); + err = of_property_match_string(np, "phy_type", "ulpi"); + if (err < 0) + phy->is_ulpi_phy = false; + else + phy->is_ulpi_phy = true; + + if (!phy->config) { + if (phy->is_ulpi_phy) { + pr_err("%s: ulpi phy configuration missing", __func__); + err = -EINVAL; + goto err0; + } else { + phy->config = &utmip_default[instance]; + } + } + + phy->pll_u = clk_get_sys(NULL, "pll_u"); + if (IS_ERR(phy->pll_u)) { + pr_err("Can't get pll_u clock\n"); + err = PTR_ERR(phy->pll_u); + goto err0; + } + clk_prepare_enable(phy->pll_u); + + parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); + for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { + if (tegra_freq_table[i].freq == parent_rate) { + phy->freq = &tegra_freq_table[i]; + break; + } + } + if (!phy->freq) { + pr_err("invalid pll_u parent rate %ld\n", parent_rate); + err = -EINVAL; + goto err1; + } + + phy->u_phy.init = tegra_phy_init; + phy->u_phy.shutdown = tegra_usb_phy_close; + phy->u_phy.set_suspend = tegra_usb_phy_suspend; + + return phy; + +err1: + clk_disable_unprepare(phy->pll_u); + clk_put(phy->pll_u); +err0: + kfree(phy); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_open); + +void tegra_usb_phy_preresume(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (!phy->is_ulpi_phy) + utmi_phy_preresume(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); + +void tegra_usb_phy_postresume(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (!phy->is_ulpi_phy) + utmi_phy_postresume(phy); +} +EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); + +void tegra_ehci_phy_restore_start(struct usb_phy *x, + enum tegra_usb_phy_port_speed port_speed) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (!phy->is_ulpi_phy) + utmi_phy_restore_start(phy, port_speed); +} +EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); + +void tegra_ehci_phy_restore_end(struct usb_phy *x) +{ + struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + + if (!phy->is_ulpi_phy) + utmi_phy_restore_end(phy); +} +EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); + diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c new file mode 100644 index 0000000..a994715 --- /dev/null +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -0,0 +1,728 @@ +/* + * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004-2007 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * - HS USB ULPI mode works. + * - 3-pin mode support may be added in future. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register defines */ + +#define MCPC_CTRL 0x30 +#define MCPC_CTRL_RTSOL (1 << 7) +#define MCPC_CTRL_EXTSWR (1 << 6) +#define MCPC_CTRL_EXTSWC (1 << 5) +#define MCPC_CTRL_VOICESW (1 << 4) +#define MCPC_CTRL_OUT64K (1 << 3) +#define MCPC_CTRL_RTSCTSSW (1 << 2) +#define MCPC_CTRL_HS_UART (1 << 0) + +#define MCPC_IO_CTRL 0x33 +#define MCPC_IO_CTRL_MICBIASEN (1 << 5) +#define MCPC_IO_CTRL_CTS_NPU (1 << 4) +#define MCPC_IO_CTRL_RXD_PU (1 << 3) +#define MCPC_IO_CTRL_TXDTYP (1 << 2) +#define MCPC_IO_CTRL_CTSTYP (1 << 1) +#define MCPC_IO_CTRL_RTSTYP (1 << 0) + +#define MCPC_CTRL2 0x36 +#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) + +#define OTHER_FUNC_CTRL 0x80 +#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) +#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) + +#define OTHER_IFC_CTRL 0x83 +#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) +#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) +#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) +#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) +#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) + +#define OTHER_INT_EN_RISE 0x86 +#define OTHER_INT_EN_FALL 0x89 +#define OTHER_INT_STS 0x8C +#define OTHER_INT_LATCH 0x8D +#define OTHER_INT_VB_SESS_VLD (1 << 7) +#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ +#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ +#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ +#define OTHER_INT_MANU (1 << 1) +#define OTHER_INT_ABNORMAL_STRESS (1 << 0) + +#define ID_STATUS 0x96 +#define ID_RES_FLOAT (1 << 4) +#define ID_RES_440K (1 << 3) +#define ID_RES_200K (1 << 2) +#define ID_RES_102K (1 << 1) +#define ID_RES_GND (1 << 0) + +#define POWER_CTRL 0xAC +#define POWER_CTRL_OTG_ENAB (1 << 5) + +#define OTHER_IFC_CTRL2 0xAF +#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) +#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) +#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) + +#define REG_CTRL_EN 0xB2 +#define REG_CTRL_ERROR 0xB5 +#define ULPI_I2C_CONFLICT_INTEN (1 << 0) + +#define OTHER_FUNC_CTRL2 0xB8 +#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) + +/* following registers do not have separate _clr and _set registers */ +#define VBUS_DEBOUNCE 0xC0 +#define ID_DEBOUNCE 0xC1 +#define VBAT_TIMER 0xD3 +#define PHY_PWR_CTRL 0xFD +#define PHY_PWR_PHYPWD (1 << 0) +#define PHY_CLK_CTRL 0xFE +#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) +#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) +#define REQ_PHY_DPLL_CLK (1 << 0) +#define PHY_CLK_CTRL_STS 0xFF +#define PHY_DPLL_CLK (1 << 0) + +/* In module TWL_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x0F + +/* In module TWL_MODULE_PM_RECEIVER */ +#define VUSB_DEDICATED1 0x7D +#define VUSB_DEDICATED2 0x7E +#define VUSB1V5_DEV_GRP 0x71 +#define VUSB1V5_TYPE 0x72 +#define VUSB1V5_REMAP 0x73 +#define VUSB1V8_DEV_GRP 0x74 +#define VUSB1V8_TYPE 0x75 +#define VUSB1V8_REMAP 0x76 +#define VUSB3V1_DEV_GRP 0x77 +#define VUSB3V1_TYPE 0x78 +#define VUSB3V1_REMAP 0x79 + +/* In module TWL4030_MODULE_INTBR */ +#define PMBR1 0x0D +#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) + +struct twl4030_usb { + struct usb_phy phy; + struct device *dev; + + /* TWL4030 internal USB regulator supplies */ + struct regulator *usb1v5; + struct regulator *usb1v8; + struct regulator *usb3v1; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + /* pin configuration */ + enum twl4030_usb_mode usb_mode; + + int irq; + enum omap_musb_vbus_id_status linkstat; + bool vbus_supplied; + u8 asleep; + bool irq_enabled; +}; + +/* internal define on top of container_of */ +#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) + +/*-------------------------------------------------------------------------*/ + +static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, + u8 module, u8 data, u8 address) +{ + u8 check; + + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 1, module, address, check, data); + + /* Failed once: Try again */ + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 2, module, address, check, data); + + /* Failed again: Return error */ + return -EBUSY; +} + +#define twl4030_usb_write_verify(twl, address, data) \ + twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) + +static inline int twl4030_usb_write(struct twl4030_usb *twl, + u8 address, u8 data) +{ + int ret = 0; + + ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); + if (ret < 0) + dev_dbg(twl->dev, + "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) +{ + u8 data; + int ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_dbg(twl->dev, + "TWL4030:readb[0x%x,0x%x] Error %d\n", + module, address, ret); + + return ret; +} + +static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) +{ + return twl4030_readb(twl, TWL_MODULE_USB, address); +} + +/*-------------------------------------------------------------------------*/ + +static inline int +twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_SET(reg), bits); +} + +static inline int +twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_CLR(reg), bits); +} + +/*-------------------------------------------------------------------------*/ + +static enum omap_musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) +{ + int status; + enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; + + twl->vbus_supplied = false; + + /* + * For ID/VBUS sensing, see manual section 15.4.8 ... + * except when using only battery backup power, two + * comparators produce VBUS_PRES and ID_PRES signals, + * which don't match docs elsewhere. But ... BIT(7) + * and BIT(2) of STS_HW_CONDITIONS, respectively, do + * seem to match up. If either is true the USB_PRES + * signal is active, the OTG module is activated, and + * its interrupt may be raised (may wake the system). + */ + status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); + if (status < 0) + dev_err(twl->dev, "USB link status err %d\n", status); + else if (status & (BIT(7) | BIT(2))) { + if (status & (BIT(7))) + twl->vbus_supplied = true; + + if (status & BIT(2)) + linkstat = OMAP_MUSB_ID_GROUND; + else + linkstat = OMAP_MUSB_VBUS_VALID; + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) + linkstat = OMAP_MUSB_VBUS_OFF; + } + + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", + status, status, linkstat); + + /* REVISIT this assumes host and peripheral controllers + * are registered, and that both are active... + */ + + spin_lock_irq(&twl->lock); + twl->linkstat = linkstat; + spin_unlock_irq(&twl->lock); + + return linkstat; +} + +static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) +{ + twl->usb_mode = mode; + + switch (mode) { + case T2_USB_MODE_ULPI: + twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, + ULPI_IFC_CTRL_CARKITMODE); + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, + ULPI_FUNC_CTRL_XCVRSEL_MASK | + ULPI_FUNC_CTRL_OPMODE_MASK); + break; + case -1: + /* FIXME: power on defaults */ + break; + default: + dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", + mode); + break; + }; +} + +static void twl4030_i2c_access(struct twl4030_usb *twl, int on) +{ + unsigned long timeout; + int val = twl4030_usb_read(twl, PHY_CLK_CTRL); + + if (val >= 0) { + if (on) { + /* enable DPLL to access PHY registers over I2C */ + val |= REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + + timeout = jiffies + HZ; + while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK) + && time_before(jiffies, timeout)) + udelay(10); + if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK)) + dev_err(twl->dev, "Timeout setting T2 HSUSB " + "PHY DPLL clock\n"); + } else { + /* let ULPI control the DPLL clock */ + val &= ~REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + } + } +} + +static void __twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); + + if (on) + pwr &= ~PHY_PWR_PHYPWD; + else + pwr |= PHY_PWR_PHYPWD; + + WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); +} + +static void twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + if (on) { + regulator_enable(twl->usb3v1); + regulator_enable(twl->usb1v8); + /* + * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP + * in twl4030) resets the VUSB_DEDICATED2 register. This reset + * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to + * SLEEP. We work around this by clearing the bit after usv3v1 + * is re-activated. This ensures that VUSB3V1 is really active. + */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); + regulator_enable(twl->usb1v5); + __twl4030_phy_power(twl, 1); + twl4030_usb_write(twl, PHY_CLK_CTRL, + twl4030_usb_read(twl, PHY_CLK_CTRL) | + (PHY_CLK_CTRL_CLOCKGATING_EN | + PHY_CLK_CTRL_CLK32K_EN)); + } else { + __twl4030_phy_power(twl, 0); + regulator_disable(twl->usb1v5); + regulator_disable(twl->usb1v8); + regulator_disable(twl->usb3v1); + } +} + +static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) +{ + if (twl->asleep) + return; + + twl4030_phy_power(twl, 0); + twl->asleep = 1; + dev_dbg(twl->dev, "%s\n", __func__); +} + +static void __twl4030_phy_resume(struct twl4030_usb *twl) +{ + twl4030_phy_power(twl, 1); + twl4030_i2c_access(twl, 1); + twl4030_usb_set_mode(twl, twl->usb_mode); + if (twl->usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); +} + +static void twl4030_phy_resume(struct twl4030_usb *twl) +{ + if (!twl->asleep) + return; + __twl4030_phy_resume(twl); + twl->asleep = 0; + dev_dbg(twl->dev, "%s\n", __func__); +} + +static int twl4030_usb_ldo_init(struct twl4030_usb *twl) +{ + /* Enable writing to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); + + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); + + /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ + /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ + + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); + + /* Initialize 3.1V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); + + twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); + if (IS_ERR(twl->usb3v1)) + return -ENODEV; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); + + /* Initialize 1.5V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); + + twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); + if (IS_ERR(twl->usb1v5)) + goto fail1; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); + + /* Initialize 1.8V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); + + twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); + if (IS_ERR(twl->usb1v8)) + goto fail2; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); + + /* disable access to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); + + return 0; + +fail2: + regulator_put(twl->usb1v5); + twl->usb1v5 = NULL; +fail1: + regulator_put(twl->usb3v1); + twl->usb3v1 = NULL; + return -ENODEV; +} + +static ssize_t twl4030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + ret = sprintf(buf, "%s\n", + twl->vbus_supplied ? "on" : "off"); + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); + +static irqreturn_t twl4030_usb_irq(int irq, void *_twl) +{ + struct twl4030_usb *twl = _twl; + enum omap_musb_vbus_id_status status; + + status = twl4030_usb_linkstat(twl); + if (status > 0) { + /* FIXME add a set_power() method so that B-devices can + * configure the charger appropriately. It's not always + * correct to consume VBUS power, and how much current to + * consume is a function of the USB configuration chosen + * by the host. + * + * REVISIT usb_gadget_vbus_connect(...) as needed, ditto + * its disconnect() sibling, when changing to/from the + * USB_LINK_VBUS state. musb_hdrc won't care until it + * starts to handle softconnect right. + */ + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) + twl4030_phy_suspend(twl, 0); + else + twl4030_phy_resume(twl); + + omap_musb_mailbox(twl->linkstat); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static void twl4030_usb_phy_init(struct twl4030_usb *twl) +{ + enum omap_musb_vbus_id_status status; + + status = twl4030_usb_linkstat(twl); + if (status > 0) { + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) { + __twl4030_phy_power(twl, 0); + twl->asleep = 1; + } else { + __twl4030_phy_resume(twl); + twl->asleep = 0; + } + + omap_musb_mailbox(twl->linkstat); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); +} + +static int twl4030_set_suspend(struct usb_phy *x, int suspend) +{ + struct twl4030_usb *twl = phy_to_twl(x); + + if (suspend) + twl4030_phy_suspend(twl, 1); + else + twl4030_phy_resume(twl); + + return 0; +} + +static int twl4030_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + otg->gadget = gadget; + if (!gadget) + otg->phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + otg->host = host; + if (!host) + otg->phy->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_usb_probe(struct platform_device *pdev) +{ + struct twl4030_usb_data *pdata = pdev->dev.platform_data; + struct twl4030_usb *twl; + int status, err; + struct usb_otg *otg; + struct device_node *np = pdev->dev.of_node; + + twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (np) + of_property_read_u32(np, "usb_mode", + (enum twl4030_usb_mode *)&twl->usb_mode); + else if (pdata) + twl->usb_mode = pdata->usb_mode; + else { + dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); + return -EINVAL; + } + + otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); + if (!otg) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq = platform_get_irq(pdev, 0); + twl->vbus_supplied = false; + twl->asleep = 1; + twl->linkstat = OMAP_MUSB_UNKNOWN; + + twl->phy.dev = twl->dev; + twl->phy.label = "twl4030"; + twl->phy.otg = otg; + twl->phy.type = USB_PHY_TYPE_USB2; + twl->phy.set_suspend = twl4030_set_suspend; + + otg->phy = &twl->phy; + otg->set_host = twl4030_set_host; + otg->set_peripheral = twl4030_set_peripheral; + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + err = twl4030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + return err; + } + usb_add_phy_dev(&twl->phy); + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + /* Our job is to use irqs and status from the power module + * to keep the transceiver disabled when nothing's connected. + * + * FIXME we actually shouldn't start enabling it until the + * USB controller drivers have said they're ready, by calling + * set_host() and/or set_peripheral() ... OTG_capable boards + * need both handles, otherwise just one suffices. + */ + twl->irq_enabled = true; + status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "twl4030_usb", twl); + if (status < 0) { + dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq, status); + return status; + } + + /* Power down phy or make it work according to + * current link state. + */ + twl4030_usb_phy_init(twl); + + dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); + return 0; +} + +static int __exit twl4030_usb_remove(struct platform_device *pdev) +{ + struct twl4030_usb *twl = platform_get_drvdata(pdev); + int val; + + free_irq(twl->irq, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + + /* set transceiver mode to power on defaults */ + twl4030_usb_set_mode(twl, -1); + + /* autogate 60MHz ULPI clock, + * clear dpll clock request for i2c access, + * disable 32KHz + */ + val = twl4030_usb_read(twl, PHY_CLK_CTRL); + if (val >= 0) { + val |= PHY_CLK_CTRL_CLOCKGATING_EN; + val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); + twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); + } + + /* disable complete OTG block */ + twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (!twl->asleep) + twl4030_phy_power(twl, 0); + regulator_put(twl->usb1v5); + regulator_put(twl->usb1v8); + regulator_put(twl->usb3v1); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl4030_usb_id_table[] = { + { .compatible = "ti,twl4030-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); +#endif + +static struct platform_driver twl4030_usb_driver = { + .probe = twl4030_usb_probe, + .remove = __exit_p(twl4030_usb_remove), + .driver = { + .name = "twl4030_usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl4030_usb_id_table), + }, +}; + +static int __init twl4030_usb_init(void) +{ + return platform_driver_register(&twl4030_usb_driver); +} +subsys_initcall(twl4030_usb_init); + +static void __exit twl4030_usb_exit(void) +{ + platform_driver_unregister(&twl4030_usb_driver); +} +module_exit(twl4030_usb_exit); + +MODULE_ALIAS("platform:twl4030_usb"); +MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); +MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c new file mode 100644 index 0000000..8cd6cf4 --- /dev/null +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -0,0 +1,446 @@ +/* + * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Hema HK + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* usb register definitions */ +#define USB_VENDOR_ID_LSB 0x00 +#define USB_VENDOR_ID_MSB 0x01 +#define USB_PRODUCT_ID_LSB 0x02 +#define USB_PRODUCT_ID_MSB 0x03 +#define USB_VBUS_CTRL_SET 0x04 +#define USB_VBUS_CTRL_CLR 0x05 +#define USB_ID_CTRL_SET 0x06 +#define USB_ID_CTRL_CLR 0x07 +#define USB_VBUS_INT_SRC 0x08 +#define USB_VBUS_INT_LATCH_SET 0x09 +#define USB_VBUS_INT_LATCH_CLR 0x0A +#define USB_VBUS_INT_EN_LO_SET 0x0B +#define USB_VBUS_INT_EN_LO_CLR 0x0C +#define USB_VBUS_INT_EN_HI_SET 0x0D +#define USB_VBUS_INT_EN_HI_CLR 0x0E +#define USB_ID_INT_SRC 0x0F +#define USB_ID_INT_LATCH_SET 0x10 +#define USB_ID_INT_LATCH_CLR 0x11 + +#define USB_ID_INT_EN_LO_SET 0x12 +#define USB_ID_INT_EN_LO_CLR 0x13 +#define USB_ID_INT_EN_HI_SET 0x14 +#define USB_ID_INT_EN_HI_CLR 0x15 +#define USB_OTG_ADP_CTRL 0x16 +#define USB_OTG_ADP_HIGH 0x17 +#define USB_OTG_ADP_LOW 0x18 +#define USB_OTG_ADP_RISE 0x19 +#define USB_OTG_REVISION 0x1A + +/* to be moved to LDO */ +#define TWL6030_MISC2 0xE5 +#define TWL6030_CFG_LDO_PD2 0xF5 +#define TWL6030_BACKUP_REG 0xFA + +#define STS_HW_CONDITIONS 0x21 + +/* In module TWL6030_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x21 +#define STS_USB_ID BIT(2) + +/* In module TWL6030_MODULE_PM_RECEIVER */ +#define VUSB_CFG_TRANS 0x71 +#define VUSB_CFG_STATE 0x72 +#define VUSB_CFG_VOLTAGE 0x73 + +/* in module TWL6030_MODULE_MAIN_CHARGE */ + +#define CHARGERUSB_CTRL1 0x8 + +#define CONTROLLER_STAT1 0x03 +#define VBUS_DET BIT(2) + +struct twl6030_usb { + struct phy_companion comparator; + struct device *dev; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + struct regulator *usb3v3; + + /* used to set vbus, in atomic path */ + struct work_struct set_vbus_work; + + int irq1; + int irq2; + enum omap_musb_vbus_id_status linkstat; + u8 asleep; + bool irq_enabled; + bool vbus_enable; + const char *regulator; +}; + +#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator) + +/*-------------------------------------------------------------------------*/ + +static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, + u8 data, u8 address) +{ + int ret = 0; + + ret = twl_i2c_write_u8(module, data, address); + if (ret < 0) + dev_err(twl->dev, + "Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) +{ + u8 data, ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_err(twl->dev, + "readb[0x%x,0x%x] Error %d\n", + module, address, ret); + return ret; +} + +static int twl6030_start_srp(struct phy_companion *comparator) +{ + struct twl6030_usb *twl = comparator_to_twl(comparator); + + twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); + twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); + + mdelay(100); + twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); + + return 0; +} + +static int twl6030_usb_ldo_init(struct twl6030_usb *twl) +{ + /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); + + /* Program CFG_LDO_PD2 register and set VUSB bit */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2); + + /* Program MISC2 register and set bit VUSB_IN_VBAT */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); + + twl->usb3v3 = regulator_get(twl->dev, twl->regulator); + if (IS_ERR(twl->usb3v3)) + return -ENODEV; + + /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); + + /* + * Program the USB_ID_CTRL_SET register to enable GND drive + * and the ID comparators + */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); + + return 0; +} + +static ssize_t twl6030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl6030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + + switch (twl->linkstat) { + case OMAP_MUSB_VBUS_VALID: + ret = snprintf(buf, PAGE_SIZE, "vbus\n"); + break; + case OMAP_MUSB_ID_GROUND: + ret = snprintf(buf, PAGE_SIZE, "id\n"); + break; + case OMAP_MUSB_VBUS_OFF: + ret = snprintf(buf, PAGE_SIZE, "none\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + } + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); + +static irqreturn_t twl6030_usb_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; + u8 vbus_state, hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, + CONTROLLER_STAT1); + if (!(hw_state & STS_USB_ID)) { + if (vbus_state & VBUS_DET) { + regulator_enable(twl->usb3v3); + twl->asleep = 1; + status = OMAP_MUSB_VBUS_VALID; + twl->linkstat = status; + omap_musb_mailbox(status); + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) { + status = OMAP_MUSB_VBUS_OFF; + twl->linkstat = status; + omap_musb_mailbox(status); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } + } + } + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; + u8 hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + if (hw_state & STS_USB_ID) { + + regulator_enable(twl->usb3v3); + twl->asleep = 1; + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); + twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); + status = OMAP_MUSB_ID_GROUND; + twl->linkstat = status; + omap_musb_mailbox(status); + } else { + twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); + } + twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); + + return IRQ_HANDLED; +} + +static int twl6030_enable_irq(struct twl6030_usb *twl) +{ + twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); + + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_STS_C); + twl6030_usb_irq(twl->irq2, twl); + twl6030_usbotg_irq(twl->irq1, twl); + + return 0; +} + +static void otg_set_vbus_work(struct work_struct *data) +{ + struct twl6030_usb *twl = container_of(data, struct twl6030_usb, + set_vbus_work); + + /* + * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 + * register. This enables boost mode. + */ + + if (twl->vbus_enable) + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40, + CHARGERUSB_CTRL1); + else + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00, + CHARGERUSB_CTRL1); +} + +static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) +{ + struct twl6030_usb *twl = comparator_to_twl(comparator); + + twl->vbus_enable = enabled; + schedule_work(&twl->set_vbus_work); + + return 0; +} + +static int twl6030_usb_probe(struct platform_device *pdev) +{ + u32 ret; + struct twl6030_usb *twl; + int status, err; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct twl4030_usb_data *pdata = dev->platform_data; + + twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq1 = platform_get_irq(pdev, 0); + twl->irq2 = platform_get_irq(pdev, 1); + twl->linkstat = OMAP_MUSB_UNKNOWN; + + twl->comparator.set_vbus = twl6030_set_vbus; + twl->comparator.start_srp = twl6030_start_srp; + + ret = omap_usb2_set_comparator(&twl->comparator); + if (ret == -ENODEV) { + dev_info(&pdev->dev, "phy not ready, deferring probe"); + return -EPROBE_DEFER; + } + + if (np) { + twl->regulator = "usb"; + } else if (pdata) { + if (pdata->features & TWL6025_SUBCLASS) + twl->regulator = "ldousb"; + else + twl->regulator = "vusb"; + } else { + dev_err(&pdev->dev, "twl6030 initialized without pdata\n"); + return -EINVAL; + } + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + err = twl6030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + return err; + } + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); + + twl->irq_enabled = true; + status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq1, status); + device_remove_file(twl->dev, &dev_attr_vbus); + return status; + } + + status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq2, status); + free_irq(twl->irq1, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + return status; + } + + twl->asleep = 0; + twl6030_enable_irq(twl); + dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); + + return 0; +} + +static int __exit twl6030_usb_remove(struct platform_device *pdev) +{ + struct twl6030_usb *twl = platform_get_drvdata(pdev); + + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_STS_C); + free_irq(twl->irq1, twl); + free_irq(twl->irq2, twl); + regulator_put(twl->usb3v3); + device_remove_file(twl->dev, &dev_attr_vbus); + cancel_work_sync(&twl->set_vbus_work); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl6030_usb_id_table[] = { + { .compatible = "ti,twl6030-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); +#endif + +static struct platform_driver twl6030_usb_driver = { + .probe = twl6030_usb_probe, + .remove = __exit_p(twl6030_usb_remove), + .driver = { + .name = "twl6030_usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(twl6030_usb_id_table), + }, +}; + +static int __init twl6030_usb_init(void) +{ + return platform_driver_register(&twl6030_usb_driver); +} +subsys_initcall(twl6030_usb_init); + +static void __exit twl6030_usb_exit(void) +{ + platform_driver_unregister(&twl6030_usb_driver); +} +module_exit(twl6030_usb_exit); + +MODULE_ALIAS("platform:twl6030_usb"); +MODULE_AUTHOR("Hema HK "); +MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-ulpi-viewport.c b/drivers/usb/phy/phy-ulpi-viewport.c new file mode 100644 index 0000000..c5ba7e5 --- /dev/null +++ b/drivers/usb/phy/phy-ulpi-viewport.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#define ULPI_VIEW_WAKEUP (1 << 31) +#define ULPI_VIEW_RUN (1 << 30) +#define ULPI_VIEW_WRITE (1 << 29) +#define ULPI_VIEW_READ (0 << 29) +#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) +#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) +#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) + +static int ulpi_viewport_wait(void __iomem *view, u32 mask) +{ + unsigned long usec = 2000; + + while (usec--) { + if (!(readl(view) & mask)) + return 0; + + udelay(1); + }; + + return -ETIMEDOUT; +} + +static int ulpi_viewport_read(struct usb_phy *otg, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); + if (ret) + return ret; + + return ULPI_VIEW_DATA_READ(readl(view)); +} + +static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | + ULPI_VIEW_ADDR(reg), view); + + return ulpi_viewport_wait(view, ULPI_VIEW_RUN); +} + +struct usb_phy_io_ops ulpi_viewport_access_ops = { + .read = ulpi_viewport_read, + .write = ulpi_viewport_write, +}; diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c new file mode 100644 index 0000000..217339d --- /dev/null +++ b/drivers/usb/phy/phy-ulpi.c @@ -0,0 +1,283 @@ +/* + * Generic ULPI USB transceiver support + * + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + + +struct ulpi_info { + unsigned int id; + char *name; +}; + +#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) +#define ULPI_INFO(_id, _name) \ + { \ + .id = (_id), \ + .name = (_name), \ + } + +/* ULPI hardcoded IDs, used for probing */ +static struct ulpi_info ulpi_ids[] = { + ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), + ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), +}; + +static int ulpi_set_otg_flags(struct usb_phy *phy) +{ + unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | + ULPI_OTG_CTRL_DM_PULLDOWN; + + if (phy->flags & ULPI_OTG_ID_PULLUP) + flags |= ULPI_OTG_CTRL_ID_PULLUP; + + /* + * ULPI Specification rev.1.1 default + * for Dp/DmPulldown is enabled. + */ + if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; + + if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; + + if (phy->flags & ULPI_OTG_EXTVBUSIND) + flags |= ULPI_OTG_CTRL_EXTVBUSIND; + + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); +} + +static int ulpi_set_fc_flags(struct usb_phy *phy) +{ + unsigned int flags = 0; + + /* + * ULPI Specification rev.1.1 default + * for XcvrSelect is Full Speed. + */ + if (phy->flags & ULPI_FC_HS) + flags |= ULPI_FUNC_CTRL_HIGH_SPEED; + else if (phy->flags & ULPI_FC_LS) + flags |= ULPI_FUNC_CTRL_LOW_SPEED; + else if (phy->flags & ULPI_FC_FS4LS) + flags |= ULPI_FUNC_CTRL_FS4LS; + else + flags |= ULPI_FUNC_CTRL_FULL_SPEED; + + if (phy->flags & ULPI_FC_TERMSEL) + flags |= ULPI_FUNC_CTRL_TERMSELECT; + + /* + * ULPI Specification rev.1.1 default + * for OpMode is Normal Operation. + */ + if (phy->flags & ULPI_FC_OP_NODRV) + flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + else if (phy->flags & ULPI_FC_OP_DIS_NRZI) + flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; + else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) + flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; + else + flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + + /* + * ULPI Specification rev.1.1 default + * for SuspendM is Powered. + */ + flags |= ULPI_FUNC_CTRL_SUSPENDM; + + return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); +} + +static int ulpi_set_ic_flags(struct usb_phy *phy) +{ + unsigned int flags = 0; + + if (phy->flags & ULPI_IC_AUTORESUME) + flags |= ULPI_IFC_CTRL_AUTORESUME; + + if (phy->flags & ULPI_IC_EXTVBUS_INDINV) + flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; + + if (phy->flags & ULPI_IC_IND_PASSTHRU) + flags |= ULPI_IFC_CTRL_PASSTHRU; + + if (phy->flags & ULPI_IC_PROTECT_DIS) + flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; + + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); +} + +static int ulpi_set_flags(struct usb_phy *phy) +{ + int ret; + + ret = ulpi_set_otg_flags(phy); + if (ret) + return ret; + + ret = ulpi_set_ic_flags(phy); + if (ret) + return ret; + + return ulpi_set_fc_flags(phy); +} + +static int ulpi_check_integrity(struct usb_phy *phy) +{ + int ret, i; + unsigned int val = 0x55; + + for (i = 0; i < 2; i++) { + ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); + if (ret < 0) + return ret; + + ret = usb_phy_io_read(phy, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != val) { + pr_err("ULPI integrity check: failed!"); + return -ENODEV; + } + val = val << 1; + } + + pr_info("ULPI integrity check: passed.\n"); + + return 0; +} + +static int ulpi_init(struct usb_phy *phy) +{ + int i, vid, pid, ret; + u32 ulpi_id = 0; + + for (i = 0; i < 4; i++) { + ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); + if (ret < 0) + return ret; + ulpi_id = (ulpi_id << 8) | ret; + } + vid = ulpi_id & 0xffff; + pid = ulpi_id >> 16; + + pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); + + for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) { + if (ulpi_ids[i].id == ULPI_ID(vid, pid)) { + pr_info("Found %s ULPI transceiver.\n", + ulpi_ids[i].name); + break; + } + } + + ret = ulpi_check_integrity(phy); + if (ret) + return ret; + + return ulpi_set_flags(phy); +} + +static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); + + if (!host) { + otg->host = NULL; + return 0; + } + + otg->host = host; + + flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_CARKITMODE); + + if (phy->flags & ULPI_IC_6PIN_SERIAL) + flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; + else if (phy->flags & ULPI_IC_3PIN_SERIAL) + flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; + else if (phy->flags & ULPI_IC_CARKIT) + flags |= ULPI_IFC_CTRL_CARKITMODE; + + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); +} + +static int ulpi_set_vbus(struct usb_otg *otg, bool on) +{ + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); + + flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); + + if (on) { + if (phy->flags & ULPI_OTG_DRVVBUS) + flags |= ULPI_OTG_CTRL_DRVVBUS; + + if (phy->flags & ULPI_OTG_DRVVBUS_EXT) + flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; + } + + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); +} + +struct usb_phy * +otg_ulpi_create(struct usb_phy_io_ops *ops, + unsigned int flags) +{ + struct usb_phy *phy; + struct usb_otg *otg; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; + + otg = kzalloc(sizeof(*otg), GFP_KERNEL); + if (!otg) { + kfree(phy); + return NULL; + } + + phy->label = "ULPI"; + phy->flags = flags; + phy->io_ops = ops; + phy->otg = otg; + phy->init = ulpi_init; + + otg->phy = phy; + otg->set_host = ulpi_set_host; + otg->set_vbus = ulpi_set_vbus; + + return phy; +} +EXPORT_SYMBOL_GPL(otg_ulpi_create); + diff --git a/drivers/usb/phy/rcar-phy.c b/drivers/usb/phy/rcar-phy.c deleted file mode 100644 index a35681b..0000000 --- a/drivers/usb/phy/rcar-phy.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Renesas R-Car USB phy driver - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -/* USBH common register */ -#define USBPCTRL0 0x0800 -#define USBPCTRL1 0x0804 -#define USBST 0x0808 -#define USBEH0 0x080C -#define USBOH0 0x081C -#define USBCTL0 0x0858 -#define EIIBC1 0x0094 -#define EIIBC2 0x009C - -/* USBPCTRL1 */ -#define PHY_RST (1 << 2) -#define PLL_ENB (1 << 1) -#define PHY_ENB (1 << 0) - -/* USBST */ -#define ST_ACT (1 << 31) -#define ST_PLL (1 << 30) - -struct rcar_usb_phy_priv { - struct usb_phy phy; - spinlock_t lock; - - void __iomem *reg0; - void __iomem *reg1; - int counter; -}; - -#define usb_phy_to_priv(p) container_of(p, struct rcar_usb_phy_priv, phy) - - -/* - * USB initial/install operation. - * - * This function setup USB phy. - * The used value and setting order came from - * [USB :: Initial setting] on datasheet. - */ -static int rcar_usb_phy_init(struct usb_phy *phy) -{ - struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy); - struct device *dev = phy->dev; - void __iomem *reg0 = priv->reg0; - void __iomem *reg1 = priv->reg1; - int i; - u32 val; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->counter++ == 0) { - - /* - * USB phy start-up - */ - - /* (1) USB-PHY standby release */ - iowrite32(PHY_ENB, (reg0 + USBPCTRL1)); - - /* (2) start USB-PHY internal PLL */ - iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1)); - - /* (3) USB module status check */ - for (i = 0; i < 1024; i++) { - udelay(10); - val = ioread32(reg0 + USBST); - if (val == (ST_ACT | ST_PLL)) - break; - } - - if (val != (ST_ACT | ST_PLL)) { - dev_err(dev, "USB phy not ready\n"); - goto phy_init_end; - } - - /* (4) USB-PHY reset clear */ - iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1)); - - /* set platform specific port settings */ - iowrite32(0x00000000, (reg0 + USBPCTRL0)); - - /* - * EHCI IP internal buffer setting - * EHCI IP internal buffer enable - * - * These are recommended value of a datasheet - * see [USB :: EHCI internal buffer setting] - */ - iowrite32(0x00ff0040, (reg0 + EIIBC1)); - iowrite32(0x00ff0040, (reg1 + EIIBC1)); - - iowrite32(0x00000001, (reg0 + EIIBC2)); - iowrite32(0x00000001, (reg1 + EIIBC2)); - - /* - * Bus alignment settings - */ - - /* (1) EHCI bus alignment (little endian) */ - iowrite32(0x00000000, (reg0 + USBEH0)); - - /* (1) OHCI bus alignment (little endian) */ - iowrite32(0x00000000, (reg0 + USBOH0)); - } - -phy_init_end: - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static void rcar_usb_phy_shutdown(struct usb_phy *phy) -{ - struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy); - void __iomem *reg0 = priv->reg0; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->counter-- == 1) { /* last user */ - iowrite32(0x00000000, (reg0 + USBPCTRL0)); - iowrite32(0x00000000, (reg0 + USBPCTRL1)); - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int rcar_usb_phy_probe(struct platform_device *pdev) -{ - struct rcar_usb_phy_priv *priv; - struct resource *res0, *res1; - struct device *dev = &pdev->dev; - void __iomem *reg0, *reg1; - int ret; - - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res0 || !res1) { - dev_err(dev, "Not enough platform resources\n"); - return -EINVAL; - } - - /* - * CAUTION - * - * Because this phy address is also mapped under OHCI/EHCI address area, - * this driver can't use devm_request_and_ioremap(dev, res) here - */ - reg0 = devm_ioremap_nocache(dev, res0->start, resource_size(res0)); - reg1 = devm_ioremap_nocache(dev, res1->start, resource_size(res1)); - if (!reg0 || !reg1) { - dev_err(dev, "ioremap error\n"); - return -ENOMEM; - } - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(dev, "priv data allocation error\n"); - return -ENOMEM; - } - - priv->reg0 = reg0; - priv->reg1 = reg1; - priv->counter = 0; - priv->phy.dev = dev; - priv->phy.label = dev_name(dev); - priv->phy.init = rcar_usb_phy_init; - priv->phy.shutdown = rcar_usb_phy_shutdown; - spin_lock_init(&priv->lock); - - ret = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); - if (ret < 0) { - dev_err(dev, "usb phy addition error\n"); - return ret; - } - - platform_set_drvdata(pdev, priv); - - return ret; -} - -static int rcar_usb_phy_remove(struct platform_device *pdev) -{ - struct rcar_usb_phy_priv *priv = platform_get_drvdata(pdev); - - usb_remove_phy(&priv->phy); - - return 0; -} - -static struct platform_driver rcar_usb_phy_driver = { - .driver = { - .name = "rcar_usb_phy", - }, - .probe = rcar_usb_phy_probe, - .remove = rcar_usb_phy_remove, -}; - -module_platform_driver(rcar_usb_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car USB phy"); -MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c deleted file mode 100644 index 967101e..0000000 --- a/drivers/usb/phy/samsung-usbphy.c +++ /dev/null @@ -1,928 +0,0 @@ -/* linux/drivers/usb/phy/samsung-usbphy.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Praveen Paneri - * - * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register definitions */ - -#define SAMSUNG_PHYPWR (0x00) - -#define PHYPWR_NORMAL_MASK (0x19 << 0) -#define PHYPWR_OTG_DISABLE (0x1 << 4) -#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) -#define PHYPWR_FORCE_SUSPEND (0x1 << 1) -/* For Exynos4 */ -#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) -#define PHYPWR_SLEEP_PHY0 (0x1 << 5) - -#define SAMSUNG_PHYCLK (0x04) - -#define PHYCLK_MODE_USB11 (0x1 << 6) -#define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) -#define PHYCLK_ID_PULL (0x1 << 2) -#define PHYCLK_CLKSEL_MASK (0x3 << 0) -#define PHYCLK_CLKSEL_48M (0x0 << 0) -#define PHYCLK_CLKSEL_12M (0x2 << 0) -#define PHYCLK_CLKSEL_24M (0x3 << 0) - -#define SAMSUNG_RSTCON (0x08) - -#define RSTCON_PHYLINK_SWRST (0x1 << 2) -#define RSTCON_HLINK_SWRST (0x1 << 1) -#define RSTCON_SWRST (0x1 << 0) - -/* EXYNOS5 */ -#define EXYNOS5_PHY_HOST_CTRL0 (0x00) - -#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) - -#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) -#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) -#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) -#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) - -#define HOST_CTRL0_FSEL_MASK (0x7 << 16) -#define HOST_CTRL0_FSEL(_x) ((_x) << 16) - -#define FSEL_CLKSEL_50M (0x7) -#define FSEL_CLKSEL_24M (0x5) -#define FSEL_CLKSEL_20M (0x4) -#define FSEL_CLKSEL_19200K (0x3) -#define FSEL_CLKSEL_12M (0x2) -#define FSEL_CLKSEL_10M (0x1) -#define FSEL_CLKSEL_9600K (0x0) - -#define HOST_CTRL0_TESTBURNIN (0x1 << 11) -#define HOST_CTRL0_RETENABLE (0x1 << 10) -#define HOST_CTRL0_COMMONON_N (0x1 << 9) -#define HOST_CTRL0_SIDDQ (0x1 << 6) -#define HOST_CTRL0_FORCESLEEP (0x1 << 5) -#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) -#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) -#define HOST_CTRL0_UTMISWRST (0x1 << 2) -#define HOST_CTRL0_LINKSWRST (0x1 << 1) -#define HOST_CTRL0_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_TUNE0 (0x04) - -#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) - -#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) - -#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) - -#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) - -#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) -#define HSIC_CTRL_REFCLKSEL (0x2 << 23) - -#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) -#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) -#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) -#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) -#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) -#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) -#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) - -#define HSIC_CTRL_SIDDQ (0x1 << 6) -#define HSIC_CTRL_FORCESLEEP (0x1 << 5) -#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) -#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) -#define HSIC_CTRL_UTMISWRST (0x1 << 2) -#define HSIC_CTRL_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) - -#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) -#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) -#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) -#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) - -#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) - -#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) -#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) -#define HOST_OHCICTRL_CNTSEL (0x1 << 1) -#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) - -#define EXYNOS5_PHY_OTG_SYS (0x38) - -#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) -#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) -#define OTG_SYS_PHY0_SWRST (0x1 << 12) - -#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) -#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) -#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) -#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) - -#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) -#define OTG_SYS_COMMON_ON (0x1 << 7) - -#define OTG_SYS_FSEL_MASK (0x7 << 4) -#define OTG_SYS_FSEL(_x) ((_x) << 4) - -#define OTG_SYS_FORCESLEEP (0x1 << 3) -#define OTG_SYS_OTGDISABLE (0x1 << 2) -#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) -#define OTG_SYS_FORCESUSPEND (0x1 << 0) - -#define EXYNOS5_PHY_OTG_TUNE (0x40) - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#ifndef KHZ -#define KHZ (1000) -#endif - -#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) -#define S3C64XX_USBPHY_ENABLE (0x1 << 16) -#define EXYNOS_USBPHY_ENABLE (0x1 << 0) -#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) - -enum samsung_cpu_type { - TYPE_S3C64XX, - TYPE_EXYNOS4210, - TYPE_EXYNOS5250, -}; - -/* - * struct samsung_usbphy_drvdata - driver data for various SoC variants - * @cpu_type: machine identifier - * @devphy_en_mask: device phy enable mask for PHY CONTROL register - * @hostphy_en_mask: host phy enable mask for PHY CONTROL register - * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from - * mapped address of system controller. - * @hostphy_reg_offset: offset to HOST PHY CONTROL register from - * mapped address of system controller. - * - * Here we have a separate mask for device type phy. - * Having different masks for host and device type phy helps - * in setting independent masks in case of SoCs like S5PV210, - * in which PHY0 and PHY1 enable bits belong to same register - * placed at position 0 and 1 respectively. - * Although for newer SoCs like exynos these bits belong to - * different registers altogether placed at position 0. - */ -struct samsung_usbphy_drvdata { - int cpu_type; - int devphy_en_mask; - int hostphy_en_mask; - u32 devphy_reg_offset; - u32 hostphy_reg_offset; -}; - -/* - * struct samsung_usbphy - transceiver driver state - * @phy: transceiver structure - * @plat: platform data - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @regs: usb phy controller registers memory base - * @pmuregs: USB device PHY_CONTROL register memory base - * @sysreg: USB2.0 PHY_CFG register memory base - * @ref_clk_freq: reference clock frequency selection - * @drv_data: driver data available for different SoCs - * @phy_type: Samsung SoCs specific phy types: #HOST - * #DEVICE - * @phy_usage: usage count for phy - * @lock: lock for phy operations - */ -struct samsung_usbphy { - struct usb_phy phy; - struct samsung_usbphy_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *regs; - void __iomem *pmuregs; - void __iomem *sysreg; - int ref_clk_freq; - const struct samsung_usbphy_drvdata *drv_data; - enum samsung_usb_phy_type phy_type; - atomic_t phy_usage; - spinlock_t lock; -}; - -#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) - -int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!otg->host) - otg->host = host; - - return 0; -} - -static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) -{ - struct device_node *usbphy_sys; - - /* Getting node for system controller interface for usb-phy */ - usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); - if (!usbphy_sys) { - dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); - return -ENODEV; - } - - sphy->pmuregs = of_iomap(usbphy_sys, 0); - - if (sphy->pmuregs == NULL) { - dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); - goto err0; - } - - sphy->sysreg = of_iomap(usbphy_sys, 1); - - /* - * Not returning error code here, since this situation is not fatal. - * Few SoCs may not have this switch available - */ - if (sphy->sysreg == NULL) - dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); - - of_node_put(usbphy_sys); - - return 0; - -err0: - of_node_put(usbphy_sys); - return -ENXIO; -} - -/* - * Set isolation here for phy. - * Here 'on = true' would mean USB PHY block is isolated, hence - * de-activated and vice-versa. - */ -static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) -{ - void __iomem *reg = NULL; - u32 reg_val; - u32 en_mask = 0; - - if (!sphy->pmuregs) { - dev_warn(sphy->dev, "Can't set pmu isolation\n"); - return; - } - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - /* - * Do nothing: We will add here once S3C64xx goes for DT support - */ - break; - case TYPE_EXYNOS4210: - /* - * Fall through since exynos4210 and exynos5250 have similar - * register architecture: two separate registers for host and - * device phy control with enable bit at position 0. - */ - case TYPE_EXYNOS5250: - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { - reg = sphy->pmuregs + - sphy->drv_data->devphy_reg_offset; - en_mask = sphy->drv_data->devphy_en_mask; - } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { - reg = sphy->pmuregs + - sphy->drv_data->hostphy_reg_offset; - en_mask = sphy->drv_data->hostphy_en_mask; - } - break; - default: - dev_err(sphy->dev, "Invalid SoC type\n"); - return; - } - - reg_val = readl(reg); - - if (on) - reg_val &= ~en_mask; - else - reg_val |= en_mask; - - writel(reg_val, reg); -} - -/* - * Configure the mode of working of usb-phy here: HOST/DEVICE. - */ -static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) -{ - u32 reg; - - if (!sphy->sysreg) { - dev_warn(sphy->dev, "Can't configure specified phy mode\n"); - return; - } - - reg = readl(sphy->sysreg); - - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) - reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; - else if (sphy->phy_type == USB_PHY_TYPE_HOST) - reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; - - writel(reg, sphy->sysreg); -} - -/* - * PHYs are different for USB Device and USB Host. - * This make sure that correct PHY type is selected before - * any operation on PHY. - */ -static int samsung_usbphy_set_type(struct usb_phy *phy, - enum samsung_usb_phy_type phy_type) -{ - struct samsung_usbphy *sphy = phy_to_sphy(phy); - - sphy->phy_type = phy_type; - - return 0; -} - -/* - * Returns reference clock frequency selection value - */ -static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) -{ - struct clk *ref_clk; - int refclk_freq = 0; - - /* - * In exynos5250 USB host and device PHY use - * external crystal clock XXTI - */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - ref_clk = clk_get(sphy->dev, "ext_xtal"); - else - ref_clk = clk_get(sphy->dev, "xusbxti"); - if (IS_ERR(ref_clk)) { - dev_err(sphy->dev, "Failed to get reference clock\n"); - return PTR_ERR(ref_clk); - } - - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) { - /* set clock frequency for PLL */ - switch (clk_get_rate(ref_clk)) { - case 9600 * KHZ: - refclk_freq = FSEL_CLKSEL_9600K; - break; - case 10 * MHZ: - refclk_freq = FSEL_CLKSEL_10M; - break; - case 12 * MHZ: - refclk_freq = FSEL_CLKSEL_12M; - break; - case 19200 * KHZ: - refclk_freq = FSEL_CLKSEL_19200K; - break; - case 20 * MHZ: - refclk_freq = FSEL_CLKSEL_20M; - break; - case 50 * MHZ: - refclk_freq = FSEL_CLKSEL_50M; - break; - case 24 * MHZ: - default: - /* default reference clock */ - refclk_freq = FSEL_CLKSEL_24M; - break; - } - } else { - switch (clk_get_rate(ref_clk)) { - case 12 * MHZ: - refclk_freq = PHYCLK_CLKSEL_12M; - break; - case 24 * MHZ: - refclk_freq = PHYCLK_CLKSEL_24M; - break; - case 48 * MHZ: - refclk_freq = PHYCLK_CLKSEL_48M; - break; - default: - if (sphy->drv_data->cpu_type == TYPE_S3C64XX) - refclk_freq = PHYCLK_CLKSEL_48M; - else - refclk_freq = PHYCLK_CLKSEL_24M; - break; - } - } - clk_put(ref_clk); - - return refclk_freq; -} - -static bool exynos5_phyhost_is_on(void *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - -static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyclk = sphy->ref_clk_freq; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - u32 ehcictrl; - u32 ohcictrl; - - /* - * phy_usage helps in keeping usage count for phy - * so that the first consumer enabling the phy is also - * the last consumer to disable it. - */ - - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); - return; - } - - /* Host configuration */ - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - /* phy reference clock configuration */ - phyhost &= ~HOST_CTRL0_FSEL_MASK; - phyhost |= HOST_CTRL0_FSEL(phyclk); - - /* host phy reset */ - phyhost &= ~(HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL | - HOST_CTRL0_SIDDQ | - /* Enable normal mode of operation */ - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP); - - /* Link reset */ - phyhost |= (HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST | - /* COMMON Block configuration during suspend */ - HOST_CTRL0_COMMONON_N); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - udelay(10); - phyhost &= ~(HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - /* OTG configuration */ - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - - /* phy reference clock configuration */ - phyotg &= ~OTG_SYS_FSEL_MASK; - phyotg |= OTG_SYS_FSEL(phyclk); - - /* Enable normal mode of operation */ - phyotg &= ~(OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP | - OTG_SYS_REFCLKSEL_MASK | - /* COMMON Block configuration during suspend */ - OTG_SYS_COMMON_ON); - - /* OTG phy & link reset */ - phyotg |= (OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET | - OTG_SYS_OTGDISABLE | - /* Set phy refclk */ - OTG_SYS_REFCLKSEL_CLKCORE); - - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - udelay(10); - phyotg &= ~(OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - - /* HSIC phy configuration */ - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_PHYSWRST); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - udelay(10); - phyhsic &= ~HSIC_CTRL_PHYSWRST; - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - udelay(80); - - /* enable EHCI DMA burst */ - ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); - ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | - HOST_EHCICTRL_ENAINCR4 | - HOST_EHCICTRL_ENAINCR8 | - HOST_EHCICTRL_ENAINCR16); - writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); - - /* set ohci_suspend_on_n */ - ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); - ohcictrl |= HOST_OHCICTRL_SUSPLGCY; - writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); -} - -static void samsung_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - u32 phyclk; - u32 rstcon; - - /* set clock frequency for PLL */ - phyclk = sphy->ref_clk_freq; - phypwr = readl(regs + SAMSUNG_PHYPWR); - rstcon = readl(regs + SAMSUNG_RSTCON); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; - phypwr &= ~PHYPWR_NORMAL_MASK; - rstcon |= RSTCON_SWRST; - break; - case TYPE_EXYNOS4210: - phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; - rstcon |= RSTCON_SWRST; - default: - break; - } - - writel(phyclk, regs + SAMSUNG_PHYCLK); - /* Configure PHY0 for normal operation*/ - writel(phypwr, regs + SAMSUNG_PHYPWR); - /* reset all ports of PHY and Link */ - writel(rstcon, regs + SAMSUNG_RSTCON); - udelay(10); - rstcon &= ~RSTCON_SWRST; - writel(rstcon, regs + SAMSUNG_RSTCON); -} - -static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - - if (atomic_dec_return(&sphy->phy_usage) > 0) { - dev_info(sphy->dev, "still being used\n"); - return; - } - - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_SIDDQ | - HSIC_CTRL_FORCESLEEP | - HSIC_CTRL_FORCESUSPEND); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - phyhost |= (HOST_CTRL0_SIDDQ | - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP | - HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - phyotg |= (OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); -} - -static void samsung_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - - phypwr = readl(regs + SAMSUNG_PHYPWR); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phypwr |= PHYPWR_NORMAL_MASK; - break; - case TYPE_EXYNOS4210: - phypwr |= PHYPWR_NORMAL_MASK_PHY0; - default: - break; - } - - /* Disable analog and otg block power */ - writel(phypwr, regs + SAMSUNG_PHYPWR); -} - -/* - * The function passed to the usb driver for phy initialization - */ -static int samsung_usbphy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* Disable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(false); - else - samsung_usbphy_set_isolation(sphy, false); - - /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ - samsung_usbphy_cfg_sel(sphy); - - /* Initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_enable(sphy); - else - samsung_usbphy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usbphy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* De-initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_disable(sphy); - else - samsung_usbphy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(true); - else - samsung_usbphy_set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static const struct of_device_id samsung_usbphy_dt_match[]; - -static inline const struct samsung_usbphy_drvdata -*samsung_usbphy_get_driver_data(struct platform_device *pdev) -{ - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(samsung_usbphy_dt_match, - pdev->dev.of_node); - return match->data; - } - - return (struct samsung_usbphy_drvdata *) - platform_get_device_id(pdev)->driver_data; -} - -static int samsung_usbphy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct usb_otg *otg; - struct samsung_usbphy_data *pdata = pdev->dev.platform_data; - const struct samsung_usbphy_drvdata *drv_data; - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!phy_mem) { - dev_err(dev, "%s: missing mem resource\n", __func__); - return -ENODEV; - } - - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - drv_data = samsung_usbphy_get_driver_data(pdev); - - if (drv_data->cpu_type == TYPE_EXYNOS5250) - clk = devm_clk_get(dev, "usbhost"); - else - clk = devm_clk_get(dev, "otg"); - - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get otg clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->drv_data = drv_data; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usbphy"; - sphy->phy.init = samsung_usbphy_init; - sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - - sphy->phy.otg = otg; - sphy->phy.otg->phy = &sphy->phy; - sphy->phy.otg->set_host = samsung_usbphy_set_host; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); -} - -static int samsung_usbphy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { - .cpu_type = TYPE_S3C64XX, - .devphy_en_mask = S3C64XX_USBPHY_ENABLE, -}; - -static const struct samsung_usbphy_drvdata usbphy_exynos4 = { - .cpu_type = TYPE_EXYNOS4210, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, -}; - -static struct samsung_usbphy_drvdata usbphy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,s3c64xx-usbphy", - .data = &usbphy_s3c64xx, - }, { - .compatible = "samsung,exynos4210-usbphy", - .data = &usbphy_exynos4, - }, { - .compatible = "samsung,exynos5250-usbphy", - .data = &usbphy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "s3c64xx-usbphy", - .driver_data = (unsigned long)&usbphy_s3c64xx, - }, { - .name = "exynos4210-usbphy", - .driver_data = (unsigned long)&usbphy_exynos4, - }, { - .name = "exynos5250-usbphy", - .driver_data = (unsigned long)&usbphy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usbphy_driver = { - .probe = samsung_usbphy_probe, - .remove = samsung_usbphy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usbphy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usbphy_driver); - -MODULE_DESCRIPTION("Samsung USB phy controller"); -MODULE_AUTHOR("Praveen Paneri "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usbphy"); diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c deleted file mode 100644 index 5487d38..0000000 --- a/drivers/usb/phy/tegra_usb_phy.c +++ /dev/null @@ -1,798 +0,0 @@ -/* - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Erik Gilling - * Benoit Goby - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TEGRA_USB_BASE 0xC5000000 -#define TEGRA_USB_SIZE SZ_16K - -#define ULPI_VIEWPORT 0x170 - -#define USB_SUSP_CTRL 0x400 -#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) -#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) -#define USB_SUSP_CLR (1 << 5) -#define USB_PHY_CLK_VALID (1 << 7) -#define UTMIP_RESET (1 << 11) -#define UHSIC_RESET (1 << 11) -#define UTMIP_PHY_ENABLE (1 << 12) -#define ULPI_PHY_ENABLE (1 << 13) -#define USB_SUSP_SET (1 << 14) -#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) - -#define USB1_LEGACY_CTRL 0x410 -#define USB1_NO_LEGACY_MODE (1 << 0) -#define USB1_VBUS_SENSE_CTL_MASK (3 << 1) -#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1) -#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \ - (1 << 1) -#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1) -#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1) - -#define ULPI_TIMING_CTRL_0 0x424 -#define ULPI_OUTPUT_PINMUX_BYP (1 << 10) -#define ULPI_CLKOUT_PINMUX_BYP (1 << 11) - -#define ULPI_TIMING_CTRL_1 0x428 -#define ULPI_DATA_TRIMMER_LOAD (1 << 0) -#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1) -#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16) -#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17) -#define ULPI_DIR_TRIMMER_LOAD (1 << 24) -#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25) - -#define UTMIP_PLL_CFG1 0x804 -#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) -#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27) - -#define UTMIP_XCVR_CFG0 0x808 -#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0) -#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8) -#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10) -#define UTMIP_FORCE_PD_POWERDOWN (1 << 14) -#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) -#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) -#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) - -#define UTMIP_BIAS_CFG0 0x80c -#define UTMIP_OTGPD (1 << 11) -#define UTMIP_BIASPD (1 << 10) - -#define UTMIP_HSRX_CFG0 0x810 -#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) -#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) - -#define UTMIP_HSRX_CFG1 0x814 -#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) - -#define UTMIP_TX_CFG0 0x820 -#define UTMIP_FS_PREABMLE_J (1 << 19) -#define UTMIP_HS_DISCON_DISABLE (1 << 8) - -#define UTMIP_MISC_CFG0 0x824 -#define UTMIP_DPDM_OBSERVE (1 << 26) -#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27) -#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf) -#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd) -#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc) -#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22) - -#define UTMIP_MISC_CFG1 0x828 -#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18) -#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6) - -#define UTMIP_DEBOUNCE_CFG0 0x82c -#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0) - -#define UTMIP_BAT_CHRG_CFG0 0x830 -#define UTMIP_PD_CHRG (1 << 0) - -#define UTMIP_SPARE_CFG0 0x834 -#define FUSE_SETUP_SEL (1 << 3) - -#define UTMIP_XCVR_CFG1 0x838 -#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) -#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2) -#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4) -#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18) - -#define UTMIP_BIAS_CFG1 0x83c -#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) - -static DEFINE_SPINLOCK(utmip_pad_lock); -static int utmip_pad_count; - -struct tegra_xtal_freq { - int freq; - u8 enable_delay; - u8 stable_count; - u8 active_delay; - u8 xtal_freq_count; - u16 debounce; -}; - -static const struct tegra_xtal_freq tegra_freq_table[] = { - { - .freq = 12000000, - .enable_delay = 0x02, - .stable_count = 0x2F, - .active_delay = 0x04, - .xtal_freq_count = 0x76, - .debounce = 0x7530, - }, - { - .freq = 13000000, - .enable_delay = 0x02, - .stable_count = 0x33, - .active_delay = 0x05, - .xtal_freq_count = 0x7F, - .debounce = 0x7EF4, - }, - { - .freq = 19200000, - .enable_delay = 0x03, - .stable_count = 0x4B, - .active_delay = 0x06, - .xtal_freq_count = 0xBB, - .debounce = 0xBB80, - }, - { - .freq = 26000000, - .enable_delay = 0x04, - .stable_count = 0x66, - .active_delay = 0x09, - .xtal_freq_count = 0xFE, - .debounce = 0xFDE8, - }, -}; - -static struct tegra_utmip_config utmip_default[] = { - [0] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 1, - .xcvr_lsrslew = 1, - }, - [2] = { - .hssync_start_delay = 9, - .idle_wait_delay = 17, - .elastic_limit = 16, - .term_range_adj = 6, - .xcvr_setup = 9, - .xcvr_lsfslew = 2, - .xcvr_lsrslew = 2, - }, -}; - -static int utmip_pad_open(struct tegra_usb_phy *phy) -{ - phy->pad_clk = clk_get_sys("utmip-pad", NULL); - if (IS_ERR(phy->pad_clk)) { - pr_err("%s: can't get utmip pad clock\n", __func__); - return PTR_ERR(phy->pad_clk); - } - - if (phy->is_legacy_phy) { - phy->pad_regs = phy->regs; - } else { - phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); - if (!phy->pad_regs) { - pr_err("%s: can't remap usb registers\n", __func__); - clk_put(phy->pad_clk); - return -ENOMEM; - } - } - return 0; -} - -static void utmip_pad_close(struct tegra_usb_phy *phy) -{ - if (!phy->is_legacy_phy) - iounmap(phy->pad_regs); - clk_put(phy->pad_clk); -} - -static void utmip_pad_power_on(struct tegra_usb_phy *phy) -{ - unsigned long val, flags; - void __iomem *base = phy->pad_regs; - - clk_prepare_enable(phy->pad_clk); - - spin_lock_irqsave(&utmip_pad_lock, flags); - - if (utmip_pad_count++ == 0) { - val = readl(base + UTMIP_BIAS_CFG0); - val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); - writel(val, base + UTMIP_BIAS_CFG0); - } - - spin_unlock_irqrestore(&utmip_pad_lock, flags); - - clk_disable_unprepare(phy->pad_clk); -} - -static int utmip_pad_power_off(struct tegra_usb_phy *phy) -{ - unsigned long val, flags; - void __iomem *base = phy->pad_regs; - - if (!utmip_pad_count) { - pr_err("%s: utmip pad already powered off\n", __func__); - return -EINVAL; - } - - clk_prepare_enable(phy->pad_clk); - - spin_lock_irqsave(&utmip_pad_lock, flags); - - if (--utmip_pad_count == 0) { - val = readl(base + UTMIP_BIAS_CFG0); - val |= UTMIP_OTGPD | UTMIP_BIASPD; - writel(val, base + UTMIP_BIAS_CFG0); - } - - spin_unlock_irqrestore(&utmip_pad_lock, flags); - - clk_disable_unprepare(phy->pad_clk); - - return 0; -} - -static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) -{ - unsigned long timeout = 2000; - do { - if ((readl(reg) & mask) == result) - return 0; - udelay(1); - timeout--; - } while (timeout); - return -1; -} - -static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - if (phy->is_legacy_phy) { - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - - udelay(10); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - } else - tegra_ehci_set_phcd(&phy->u_phy, true); - - if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) - pr_err("%s: timeout waiting for phy to stabilize\n", __func__); -} - -static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - if (phy->is_legacy_phy) { - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - - udelay(10); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - } else - tegra_ehci_set_phcd(&phy->u_phy, false); - - if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, - USB_PHY_CLK_VALID)) - pr_err("%s: timeout waiting for phy to stabilize\n", __func__); -} - -static int utmi_phy_power_on(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - struct tegra_utmip_config *config = phy->config; - - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - if (phy->is_legacy_phy) { - val = readl(base + USB1_LEGACY_CTRL); - val |= USB1_NO_LEGACY_MODE; - writel(val, base + USB1_LEGACY_CTRL); - } - - val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_FS_PREABMLE_J; - writel(val, base + UTMIP_TX_CFG0); - - val = readl(base + UTMIP_HSRX_CFG0); - val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); - val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); - val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); - writel(val, base + UTMIP_HSRX_CFG0); - - val = readl(base + UTMIP_HSRX_CFG1); - val &= ~UTMIP_HS_SYNC_START_DLY(~0); - val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); - writel(val, base + UTMIP_HSRX_CFG1); - - val = readl(base + UTMIP_DEBOUNCE_CFG0); - val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); - val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); - writel(val, base + UTMIP_DEBOUNCE_CFG0); - - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; - writel(val, base + UTMIP_MISC_CFG0); - - val = readl(base + UTMIP_MISC_CFG1); - val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); - val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | - UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); - writel(val, base + UTMIP_MISC_CFG1); - - val = readl(base + UTMIP_PLL_CFG1); - val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | - UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); - writel(val, base + UTMIP_PLL_CFG1); - - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { - val = readl(base + USB_SUSP_CTRL); - val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); - writel(val, base + USB_SUSP_CTRL); - } - - utmip_pad_power_on(phy); - - val = readl(base + UTMIP_XCVR_CFG0); - val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | - UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) | - UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | - UTMIP_XCVR_HSSLEW_MSB(~0)); - val |= UTMIP_XCVR_SETUP(config->xcvr_setup); - val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); - val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); - writel(val, base + UTMIP_XCVR_CFG0); - - val = readl(base + UTMIP_XCVR_CFG1); - val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | - UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); - val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); - writel(val, base + UTMIP_XCVR_CFG1); - - val = readl(base + UTMIP_BAT_CHRG_CFG0); - val &= ~UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); - - val = readl(base + UTMIP_BIAS_CFG1); - val &= ~UTMIP_BIAS_PDTRK_COUNT(~0); - val |= UTMIP_BIAS_PDTRK_COUNT(0x5); - writel(val, base + UTMIP_BIAS_CFG1); - - if (phy->is_legacy_phy) { - val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) - val &= ~FUSE_SETUP_SEL; - else - val |= FUSE_SETUP_SEL; - writel(val, base + UTMIP_SPARE_CFG0); - } else { - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); - } - - val = readl(base + USB_SUSP_CTRL); - val &= ~UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - if (phy->is_legacy_phy) { - val = readl(base + USB1_LEGACY_CTRL); - val &= ~USB1_VBUS_SENSE_CTL_MASK; - val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; - writel(val, base + USB1_LEGACY_CTRL); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_SET; - writel(val, base + USB_SUSP_CTRL); - } - - utmi_phy_clk_enable(phy); - - if (!phy->is_legacy_phy) - tegra_ehci_set_pts(&phy->u_phy, 0); - - return 0; -} - -static int utmi_phy_power_off(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - utmi_phy_clk_disable(phy); - - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); - val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); - writel(val, base + USB_SUSP_CTRL); - } - - val = readl(base + USB_SUSP_CTRL); - val |= UTMIP_RESET; - writel(val, base + USB_SUSP_CTRL); - - val = readl(base + UTMIP_BAT_CHRG_CFG0); - val |= UTMIP_PD_CHRG; - writel(val, base + UTMIP_BAT_CHRG_CFG0); - - val = readl(base + UTMIP_XCVR_CFG0); - val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | - UTMIP_FORCE_PDZI_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG0); - - val = readl(base + UTMIP_XCVR_CFG1); - val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | - UTMIP_FORCE_PDDR_POWERDOWN; - writel(val, base + UTMIP_XCVR_CFG1); - - return utmip_pad_power_off(phy); -} - -static void utmi_phy_preresume(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_TX_CFG0); - val |= UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_postresume(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_restore_start(struct tegra_usb_phy *phy, - enum tegra_usb_phy_port_speed port_speed) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); - if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; - else - val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; - writel(val, base + UTMIP_MISC_CFG0); - udelay(1); - - val = readl(base + UTMIP_MISC_CFG0); - val |= UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); - udelay(10); -} - -static void utmi_phy_restore_end(struct tegra_usb_phy *phy) -{ - unsigned long val; - void __iomem *base = phy->regs; - - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); - udelay(10); -} - -static int ulpi_phy_power_on(struct tegra_usb_phy *phy) -{ - int ret; - unsigned long val; - void __iomem *base = phy->regs; - struct tegra_ulpi_config *config = phy->config; - - gpio_direction_output(config->reset_gpio, 0); - msleep(5); - gpio_direction_output(config->reset_gpio, 1); - - clk_prepare_enable(phy->clk); - msleep(1); - - val = readl(base + USB_SUSP_CTRL); - val |= UHSIC_RESET; - writel(val, base + USB_SUSP_CTRL); - - val = readl(base + ULPI_TIMING_CTRL_0); - val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; - writel(val, base + ULPI_TIMING_CTRL_0); - - val = readl(base + USB_SUSP_CTRL); - val |= ULPI_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); - - val = 0; - writel(val, base + ULPI_TIMING_CTRL_1); - - val |= ULPI_DATA_TRIMMER_SEL(4); - val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); - val |= ULPI_DIR_TRIMMER_SEL(4); - writel(val, base + ULPI_TIMING_CTRL_1); - udelay(10); - - val |= ULPI_DATA_TRIMMER_LOAD; - val |= ULPI_STPDIRNXT_TRIMMER_LOAD; - val |= ULPI_DIR_TRIMMER_LOAD; - writel(val, base + ULPI_TIMING_CTRL_1); - - /* Fix VbusInvalid due to floating VBUS */ - ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; - } - - ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; - } - - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - udelay(100); - - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); - - return 0; -} - -static int ulpi_phy_power_off(struct tegra_usb_phy *phy) -{ - struct tegra_ulpi_config *config = phy->config; - - clk_disable(phy->clk); - return gpio_direction_output(config->reset_gpio, 0); -} - -static int tegra_phy_init(struct usb_phy *x) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - struct tegra_ulpi_config *ulpi_config; - int err; - - if (phy->is_ulpi_phy) { - ulpi_config = phy->config; - phy->clk = clk_get_sys(NULL, ulpi_config->clk); - if (IS_ERR(phy->clk)) { - pr_err("%s: can't get ulpi clock\n", __func__); - err = -ENXIO; - goto err1; - } - if (!gpio_is_valid(ulpi_config->reset_gpio)) - ulpi_config->reset_gpio = - of_get_named_gpio(phy->dev->of_node, - "nvidia,phy-reset-gpio", 0); - if (!gpio_is_valid(ulpi_config->reset_gpio)) { - pr_err("%s: invalid reset gpio: %d\n", __func__, - ulpi_config->reset_gpio); - err = -EINVAL; - goto err1; - } - gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); - gpio_direction_output(ulpi_config->reset_gpio, 0); - phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); - phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; - } else { - err = utmip_pad_open(phy); - if (err < 0) - goto err1; - } - return 0; -err1: - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); - return err; -} - -static void tegra_usb_phy_close(struct usb_phy *x) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - - if (phy->is_ulpi_phy) - clk_put(phy->clk); - else - utmip_pad_close(phy); - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); - kfree(phy); -} - -static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) -{ - if (phy->is_ulpi_phy) - return ulpi_phy_power_on(phy); - else - return utmi_phy_power_on(phy); -} - -static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) -{ - if (phy->is_ulpi_phy) - return ulpi_phy_power_off(phy); - else - return utmi_phy_power_off(phy); -} - -static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - if (suspend) - return tegra_usb_phy_power_off(phy); - else - return tegra_usb_phy_power_on(phy); -} - -struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) -{ - struct tegra_usb_phy *phy; - unsigned long parent_rate; - int i; - int err; - struct device_node *np = dev->of_node; - - phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->instance = instance; - phy->regs = regs; - phy->config = config; - phy->mode = phy_mode; - phy->dev = dev; - phy->is_legacy_phy = - of_property_read_bool(np, "nvidia,has-legacy-mode"); - err = of_property_match_string(np, "phy_type", "ulpi"); - if (err < 0) - phy->is_ulpi_phy = false; - else - phy->is_ulpi_phy = true; - - if (!phy->config) { - if (phy->is_ulpi_phy) { - pr_err("%s: ulpi phy configuration missing", __func__); - err = -EINVAL; - goto err0; - } else { - phy->config = &utmip_default[instance]; - } - } - - phy->pll_u = clk_get_sys(NULL, "pll_u"); - if (IS_ERR(phy->pll_u)) { - pr_err("Can't get pll_u clock\n"); - err = PTR_ERR(phy->pll_u); - goto err0; - } - clk_prepare_enable(phy->pll_u); - - parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); - for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) { - if (tegra_freq_table[i].freq == parent_rate) { - phy->freq = &tegra_freq_table[i]; - break; - } - } - if (!phy->freq) { - pr_err("invalid pll_u parent rate %ld\n", parent_rate); - err = -EINVAL; - goto err1; - } - - phy->u_phy.init = tegra_phy_init; - phy->u_phy.shutdown = tegra_usb_phy_close; - phy->u_phy.set_suspend = tegra_usb_phy_suspend; - - return phy; - -err1: - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); -err0: - kfree(phy); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_open); - -void tegra_usb_phy_preresume(struct usb_phy *x) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_preresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); - -void tegra_usb_phy_postresume(struct usb_phy *x) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_postresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); - -void tegra_ehci_phy_restore_start(struct usb_phy *x, - enum tegra_usb_phy_port_speed port_speed) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_restore_start(phy, port_speed); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); - -void tegra_ehci_phy_restore_end(struct usb_phy *x) -{ - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_restore_end(phy); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); - diff --git a/drivers/usb/phy/twl4030-usb.c b/drivers/usb/phy/twl4030-usb.c deleted file mode 100644 index a994715..0000000 --- a/drivers/usb/phy/twl4030-usb.c +++ /dev/null @@ -1,728 +0,0 @@ -/* - * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller - * - * Copyright (C) 2004-2007 Texas Instruments - * Copyright (C) 2008 Nokia Corporation - * Contact: Felipe Balbi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * - HS USB ULPI mode works. - * - 3-pin mode support may be added in future. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register defines */ - -#define MCPC_CTRL 0x30 -#define MCPC_CTRL_RTSOL (1 << 7) -#define MCPC_CTRL_EXTSWR (1 << 6) -#define MCPC_CTRL_EXTSWC (1 << 5) -#define MCPC_CTRL_VOICESW (1 << 4) -#define MCPC_CTRL_OUT64K (1 << 3) -#define MCPC_CTRL_RTSCTSSW (1 << 2) -#define MCPC_CTRL_HS_UART (1 << 0) - -#define MCPC_IO_CTRL 0x33 -#define MCPC_IO_CTRL_MICBIASEN (1 << 5) -#define MCPC_IO_CTRL_CTS_NPU (1 << 4) -#define MCPC_IO_CTRL_RXD_PU (1 << 3) -#define MCPC_IO_CTRL_TXDTYP (1 << 2) -#define MCPC_IO_CTRL_CTSTYP (1 << 1) -#define MCPC_IO_CTRL_RTSTYP (1 << 0) - -#define MCPC_CTRL2 0x36 -#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) - -#define OTHER_FUNC_CTRL 0x80 -#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) -#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) - -#define OTHER_IFC_CTRL 0x83 -#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) -#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) -#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) -#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) -#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) -#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) - -#define OTHER_INT_EN_RISE 0x86 -#define OTHER_INT_EN_FALL 0x89 -#define OTHER_INT_STS 0x8C -#define OTHER_INT_LATCH 0x8D -#define OTHER_INT_VB_SESS_VLD (1 << 7) -#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ -#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ -#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ -#define OTHER_INT_MANU (1 << 1) -#define OTHER_INT_ABNORMAL_STRESS (1 << 0) - -#define ID_STATUS 0x96 -#define ID_RES_FLOAT (1 << 4) -#define ID_RES_440K (1 << 3) -#define ID_RES_200K (1 << 2) -#define ID_RES_102K (1 << 1) -#define ID_RES_GND (1 << 0) - -#define POWER_CTRL 0xAC -#define POWER_CTRL_OTG_ENAB (1 << 5) - -#define OTHER_IFC_CTRL2 0xAF -#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) -#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) -#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) - -#define REG_CTRL_EN 0xB2 -#define REG_CTRL_ERROR 0xB5 -#define ULPI_I2C_CONFLICT_INTEN (1 << 0) - -#define OTHER_FUNC_CTRL2 0xB8 -#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) - -/* following registers do not have separate _clr and _set registers */ -#define VBUS_DEBOUNCE 0xC0 -#define ID_DEBOUNCE 0xC1 -#define VBAT_TIMER 0xD3 -#define PHY_PWR_CTRL 0xFD -#define PHY_PWR_PHYPWD (1 << 0) -#define PHY_CLK_CTRL 0xFE -#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) -#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) -#define REQ_PHY_DPLL_CLK (1 << 0) -#define PHY_CLK_CTRL_STS 0xFF -#define PHY_DPLL_CLK (1 << 0) - -/* In module TWL_MODULE_PM_MASTER */ -#define STS_HW_CONDITIONS 0x0F - -/* In module TWL_MODULE_PM_RECEIVER */ -#define VUSB_DEDICATED1 0x7D -#define VUSB_DEDICATED2 0x7E -#define VUSB1V5_DEV_GRP 0x71 -#define VUSB1V5_TYPE 0x72 -#define VUSB1V5_REMAP 0x73 -#define VUSB1V8_DEV_GRP 0x74 -#define VUSB1V8_TYPE 0x75 -#define VUSB1V8_REMAP 0x76 -#define VUSB3V1_DEV_GRP 0x77 -#define VUSB3V1_TYPE 0x78 -#define VUSB3V1_REMAP 0x79 - -/* In module TWL4030_MODULE_INTBR */ -#define PMBR1 0x0D -#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) - -struct twl4030_usb { - struct usb_phy phy; - struct device *dev; - - /* TWL4030 internal USB regulator supplies */ - struct regulator *usb1v5; - struct regulator *usb1v8; - struct regulator *usb3v1; - - /* for vbus reporting with irqs disabled */ - spinlock_t lock; - - /* pin configuration */ - enum twl4030_usb_mode usb_mode; - - int irq; - enum omap_musb_vbus_id_status linkstat; - bool vbus_supplied; - u8 asleep; - bool irq_enabled; -}; - -/* internal define on top of container_of */ -#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) - -/*-------------------------------------------------------------------------*/ - -static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, - u8 module, u8 data, u8 address) -{ - u8 check; - - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 1, module, address, check, data); - - /* Failed once: Try again */ - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 2, module, address, check, data); - - /* Failed again: Return error */ - return -EBUSY; -} - -#define twl4030_usb_write_verify(twl, address, data) \ - twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) - -static inline int twl4030_usb_write(struct twl4030_usb *twl, - u8 address, u8 data) -{ - int ret = 0; - - ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); - if (ret < 0) - dev_dbg(twl->dev, - "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); - return ret; -} - -static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) -{ - u8 data; - int ret = 0; - - ret = twl_i2c_read_u8(module, &data, address); - if (ret >= 0) - ret = data; - else - dev_dbg(twl->dev, - "TWL4030:readb[0x%x,0x%x] Error %d\n", - module, address, ret); - - return ret; -} - -static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) -{ - return twl4030_readb(twl, TWL_MODULE_USB, address); -} - -/*-------------------------------------------------------------------------*/ - -static inline int -twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_SET(reg), bits); -} - -static inline int -twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_CLR(reg), bits); -} - -/*-------------------------------------------------------------------------*/ - -static enum omap_musb_vbus_id_status - twl4030_usb_linkstat(struct twl4030_usb *twl) -{ - int status; - enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; - - twl->vbus_supplied = false; - - /* - * For ID/VBUS sensing, see manual section 15.4.8 ... - * except when using only battery backup power, two - * comparators produce VBUS_PRES and ID_PRES signals, - * which don't match docs elsewhere. But ... BIT(7) - * and BIT(2) of STS_HW_CONDITIONS, respectively, do - * seem to match up. If either is true the USB_PRES - * signal is active, the OTG module is activated, and - * its interrupt may be raised (may wake the system). - */ - status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); - if (status < 0) - dev_err(twl->dev, "USB link status err %d\n", status); - else if (status & (BIT(7) | BIT(2))) { - if (status & (BIT(7))) - twl->vbus_supplied = true; - - if (status & BIT(2)) - linkstat = OMAP_MUSB_ID_GROUND; - else - linkstat = OMAP_MUSB_VBUS_VALID; - } else { - if (twl->linkstat != OMAP_MUSB_UNKNOWN) - linkstat = OMAP_MUSB_VBUS_OFF; - } - - dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", - status, status, linkstat); - - /* REVISIT this assumes host and peripheral controllers - * are registered, and that both are active... - */ - - spin_lock_irq(&twl->lock); - twl->linkstat = linkstat; - spin_unlock_irq(&twl->lock); - - return linkstat; -} - -static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) -{ - twl->usb_mode = mode; - - switch (mode) { - case T2_USB_MODE_ULPI: - twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, - ULPI_IFC_CTRL_CARKITMODE); - twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, - ULPI_FUNC_CTRL_XCVRSEL_MASK | - ULPI_FUNC_CTRL_OPMODE_MASK); - break; - case -1: - /* FIXME: power on defaults */ - break; - default: - dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", - mode); - break; - }; -} - -static void twl4030_i2c_access(struct twl4030_usb *twl, int on) -{ - unsigned long timeout; - int val = twl4030_usb_read(twl, PHY_CLK_CTRL); - - if (val >= 0) { - if (on) { - /* enable DPLL to access PHY registers over I2C */ - val |= REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - - timeout = jiffies + HZ; - while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK) - && time_before(jiffies, timeout)) - udelay(10); - if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK)) - dev_err(twl->dev, "Timeout setting T2 HSUSB " - "PHY DPLL clock\n"); - } else { - /* let ULPI control the DPLL clock */ - val &= ~REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - } - } -} - -static void __twl4030_phy_power(struct twl4030_usb *twl, int on) -{ - u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); - - if (on) - pwr &= ~PHY_PWR_PHYPWD; - else - pwr |= PHY_PWR_PHYPWD; - - WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); -} - -static void twl4030_phy_power(struct twl4030_usb *twl, int on) -{ - if (on) { - regulator_enable(twl->usb3v1); - regulator_enable(twl->usb1v8); - /* - * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP - * in twl4030) resets the VUSB_DEDICATED2 register. This reset - * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to - * SLEEP. We work around this by clearing the bit after usv3v1 - * is re-activated. This ensures that VUSB3V1 is really active. - */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); - regulator_enable(twl->usb1v5); - __twl4030_phy_power(twl, 1); - twl4030_usb_write(twl, PHY_CLK_CTRL, - twl4030_usb_read(twl, PHY_CLK_CTRL) | - (PHY_CLK_CTRL_CLOCKGATING_EN | - PHY_CLK_CTRL_CLK32K_EN)); - } else { - __twl4030_phy_power(twl, 0); - regulator_disable(twl->usb1v5); - regulator_disable(twl->usb1v8); - regulator_disable(twl->usb3v1); - } -} - -static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) -{ - if (twl->asleep) - return; - - twl4030_phy_power(twl, 0); - twl->asleep = 1; - dev_dbg(twl->dev, "%s\n", __func__); -} - -static void __twl4030_phy_resume(struct twl4030_usb *twl) -{ - twl4030_phy_power(twl, 1); - twl4030_i2c_access(twl, 1); - twl4030_usb_set_mode(twl, twl->usb_mode); - if (twl->usb_mode == T2_USB_MODE_ULPI) - twl4030_i2c_access(twl, 0); -} - -static void twl4030_phy_resume(struct twl4030_usb *twl) -{ - if (!twl->asleep) - return; - __twl4030_phy_resume(twl); - twl->asleep = 0; - dev_dbg(twl->dev, "%s\n", __func__); -} - -static int twl4030_usb_ldo_init(struct twl4030_usb *twl) -{ - /* Enable writing to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); - - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); - - /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ - /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ - - /* input to VUSB3V1 LDO is from VBAT, not VBUS */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); - - /* Initialize 3.1V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); - - twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); - if (IS_ERR(twl->usb3v1)) - return -ENODEV; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); - - /* Initialize 1.5V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); - - twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); - if (IS_ERR(twl->usb1v5)) - goto fail1; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); - - /* Initialize 1.8V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); - - twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); - if (IS_ERR(twl->usb1v8)) - goto fail2; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); - - /* disable access to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); - - return 0; - -fail2: - regulator_put(twl->usb1v5); - twl->usb1v5 = NULL; -fail1: - regulator_put(twl->usb3v1); - twl->usb3v1 = NULL; - return -ENODEV; -} - -static ssize_t twl4030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct twl4030_usb *twl = dev_get_drvdata(dev); - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&twl->lock, flags); - ret = sprintf(buf, "%s\n", - twl->vbus_supplied ? "on" : "off"); - spin_unlock_irqrestore(&twl->lock, flags); - - return ret; -} -static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); - -static irqreturn_t twl4030_usb_irq(int irq, void *_twl) -{ - struct twl4030_usb *twl = _twl; - enum omap_musb_vbus_id_status status; - - status = twl4030_usb_linkstat(twl); - if (status > 0) { - /* FIXME add a set_power() method so that B-devices can - * configure the charger appropriately. It's not always - * correct to consume VBUS power, and how much current to - * consume is a function of the USB configuration chosen - * by the host. - * - * REVISIT usb_gadget_vbus_connect(...) as needed, ditto - * its disconnect() sibling, when changing to/from the - * USB_LINK_VBUS state. musb_hdrc won't care until it - * starts to handle softconnect right. - */ - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) - twl4030_phy_suspend(twl, 0); - else - twl4030_phy_resume(twl); - - omap_musb_mailbox(twl->linkstat); - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); - - return IRQ_HANDLED; -} - -static void twl4030_usb_phy_init(struct twl4030_usb *twl) -{ - enum omap_musb_vbus_id_status status; - - status = twl4030_usb_linkstat(twl); - if (status > 0) { - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) { - __twl4030_phy_power(twl, 0); - twl->asleep = 1; - } else { - __twl4030_phy_resume(twl); - twl->asleep = 0; - } - - omap_musb_mailbox(twl->linkstat); - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); -} - -static int twl4030_set_suspend(struct usb_phy *x, int suspend) -{ - struct twl4030_usb *twl = phy_to_twl(x); - - if (suspend) - twl4030_phy_suspend(twl, 1); - else - twl4030_phy_resume(twl); - - return 0; -} - -static int twl4030_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - otg->gadget = gadget; - if (!gadget) - otg->phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - otg->host = host; - if (!host) - otg->phy->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int twl4030_usb_probe(struct platform_device *pdev) -{ - struct twl4030_usb_data *pdata = pdev->dev.platform_data; - struct twl4030_usb *twl; - int status, err; - struct usb_otg *otg; - struct device_node *np = pdev->dev.of_node; - - twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); - if (!twl) - return -ENOMEM; - - if (np) - of_property_read_u32(np, "usb_mode", - (enum twl4030_usb_mode *)&twl->usb_mode); - else if (pdata) - twl->usb_mode = pdata->usb_mode; - else { - dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); - return -EINVAL; - } - - otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); - if (!otg) - return -ENOMEM; - - twl->dev = &pdev->dev; - twl->irq = platform_get_irq(pdev, 0); - twl->vbus_supplied = false; - twl->asleep = 1; - twl->linkstat = OMAP_MUSB_UNKNOWN; - - twl->phy.dev = twl->dev; - twl->phy.label = "twl4030"; - twl->phy.otg = otg; - twl->phy.type = USB_PHY_TYPE_USB2; - twl->phy.set_suspend = twl4030_set_suspend; - - otg->phy = &twl->phy; - otg->set_host = twl4030_set_host; - otg->set_peripheral = twl4030_set_peripheral; - - /* init spinlock for workqueue */ - spin_lock_init(&twl->lock); - - err = twl4030_usb_ldo_init(twl); - if (err) { - dev_err(&pdev->dev, "ldo init failed\n"); - return err; - } - usb_add_phy_dev(&twl->phy); - - platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); - - /* Our job is to use irqs and status from the power module - * to keep the transceiver disabled when nothing's connected. - * - * FIXME we actually shouldn't start enabling it until the - * USB controller drivers have said they're ready, by calling - * set_host() and/or set_peripheral() ... OTG_capable boards - * need both handles, otherwise just one suffices. - */ - twl->irq_enabled = true; - status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT, "twl4030_usb", twl); - if (status < 0) { - dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq, status); - return status; - } - - /* Power down phy or make it work according to - * current link state. - */ - twl4030_usb_phy_init(twl); - - dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); - return 0; -} - -static int __exit twl4030_usb_remove(struct platform_device *pdev) -{ - struct twl4030_usb *twl = platform_get_drvdata(pdev); - int val; - - free_irq(twl->irq, twl); - device_remove_file(twl->dev, &dev_attr_vbus); - - /* set transceiver mode to power on defaults */ - twl4030_usb_set_mode(twl, -1); - - /* autogate 60MHz ULPI clock, - * clear dpll clock request for i2c access, - * disable 32KHz - */ - val = twl4030_usb_read(twl, PHY_CLK_CTRL); - if (val >= 0) { - val |= PHY_CLK_CTRL_CLOCKGATING_EN; - val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); - twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); - } - - /* disable complete OTG block */ - twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - - if (!twl->asleep) - twl4030_phy_power(twl, 0); - regulator_put(twl->usb1v5); - regulator_put(twl->usb1v8); - regulator_put(twl->usb3v1); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl4030_usb_id_table[] = { - { .compatible = "ti,twl4030-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); -#endif - -static struct platform_driver twl4030_usb_driver = { - .probe = twl4030_usb_probe, - .remove = __exit_p(twl4030_usb_remove), - .driver = { - .name = "twl4030_usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl4030_usb_id_table), - }, -}; - -static int __init twl4030_usb_init(void) -{ - return platform_driver_register(&twl4030_usb_driver); -} -subsys_initcall(twl4030_usb_init); - -static void __exit twl4030_usb_exit(void) -{ - platform_driver_unregister(&twl4030_usb_driver); -} -module_exit(twl4030_usb_exit); - -MODULE_ALIAS("platform:twl4030_usb"); -MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); -MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/twl6030-usb.c b/drivers/usb/phy/twl6030-usb.c deleted file mode 100644 index 8cd6cf4..0000000 --- a/drivers/usb/phy/twl6030-usb.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. - * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Hema HK - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* usb register definitions */ -#define USB_VENDOR_ID_LSB 0x00 -#define USB_VENDOR_ID_MSB 0x01 -#define USB_PRODUCT_ID_LSB 0x02 -#define USB_PRODUCT_ID_MSB 0x03 -#define USB_VBUS_CTRL_SET 0x04 -#define USB_VBUS_CTRL_CLR 0x05 -#define USB_ID_CTRL_SET 0x06 -#define USB_ID_CTRL_CLR 0x07 -#define USB_VBUS_INT_SRC 0x08 -#define USB_VBUS_INT_LATCH_SET 0x09 -#define USB_VBUS_INT_LATCH_CLR 0x0A -#define USB_VBUS_INT_EN_LO_SET 0x0B -#define USB_VBUS_INT_EN_LO_CLR 0x0C -#define USB_VBUS_INT_EN_HI_SET 0x0D -#define USB_VBUS_INT_EN_HI_CLR 0x0E -#define USB_ID_INT_SRC 0x0F -#define USB_ID_INT_LATCH_SET 0x10 -#define USB_ID_INT_LATCH_CLR 0x11 - -#define USB_ID_INT_EN_LO_SET 0x12 -#define USB_ID_INT_EN_LO_CLR 0x13 -#define USB_ID_INT_EN_HI_SET 0x14 -#define USB_ID_INT_EN_HI_CLR 0x15 -#define USB_OTG_ADP_CTRL 0x16 -#define USB_OTG_ADP_HIGH 0x17 -#define USB_OTG_ADP_LOW 0x18 -#define USB_OTG_ADP_RISE 0x19 -#define USB_OTG_REVISION 0x1A - -/* to be moved to LDO */ -#define TWL6030_MISC2 0xE5 -#define TWL6030_CFG_LDO_PD2 0xF5 -#define TWL6030_BACKUP_REG 0xFA - -#define STS_HW_CONDITIONS 0x21 - -/* In module TWL6030_MODULE_PM_MASTER */ -#define STS_HW_CONDITIONS 0x21 -#define STS_USB_ID BIT(2) - -/* In module TWL6030_MODULE_PM_RECEIVER */ -#define VUSB_CFG_TRANS 0x71 -#define VUSB_CFG_STATE 0x72 -#define VUSB_CFG_VOLTAGE 0x73 - -/* in module TWL6030_MODULE_MAIN_CHARGE */ - -#define CHARGERUSB_CTRL1 0x8 - -#define CONTROLLER_STAT1 0x03 -#define VBUS_DET BIT(2) - -struct twl6030_usb { - struct phy_companion comparator; - struct device *dev; - - /* for vbus reporting with irqs disabled */ - spinlock_t lock; - - struct regulator *usb3v3; - - /* used to set vbus, in atomic path */ - struct work_struct set_vbus_work; - - int irq1; - int irq2; - enum omap_musb_vbus_id_status linkstat; - u8 asleep; - bool irq_enabled; - bool vbus_enable; - const char *regulator; -}; - -#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator) - -/*-------------------------------------------------------------------------*/ - -static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, - u8 data, u8 address) -{ - int ret = 0; - - ret = twl_i2c_write_u8(module, data, address); - if (ret < 0) - dev_err(twl->dev, - "Write[0x%x] Error %d\n", address, ret); - return ret; -} - -static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) -{ - u8 data, ret = 0; - - ret = twl_i2c_read_u8(module, &data, address); - if (ret >= 0) - ret = data; - else - dev_err(twl->dev, - "readb[0x%x,0x%x] Error %d\n", - module, address, ret); - return ret; -} - -static int twl6030_start_srp(struct phy_companion *comparator) -{ - struct twl6030_usb *twl = comparator_to_twl(comparator); - - twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); - twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); - - mdelay(100); - twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); - - return 0; -} - -static int twl6030_usb_ldo_init(struct twl6030_usb *twl) -{ - /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); - - /* Program CFG_LDO_PD2 register and set VUSB bit */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2); - - /* Program MISC2 register and set bit VUSB_IN_VBAT */ - twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); - - twl->usb3v3 = regulator_get(twl->dev, twl->regulator); - if (IS_ERR(twl->usb3v3)) - return -ENODEV; - - /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ - twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); - - /* - * Program the USB_ID_CTRL_SET register to enable GND drive - * and the ID comparators - */ - twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); - - return 0; -} - -static ssize_t twl6030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct twl6030_usb *twl = dev_get_drvdata(dev); - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&twl->lock, flags); - - switch (twl->linkstat) { - case OMAP_MUSB_VBUS_VALID: - ret = snprintf(buf, PAGE_SIZE, "vbus\n"); - break; - case OMAP_MUSB_ID_GROUND: - ret = snprintf(buf, PAGE_SIZE, "id\n"); - break; - case OMAP_MUSB_VBUS_OFF: - ret = snprintf(buf, PAGE_SIZE, "none\n"); - break; - default: - ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); - } - spin_unlock_irqrestore(&twl->lock, flags); - - return ret; -} -static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); - -static irqreturn_t twl6030_usb_irq(int irq, void *_twl) -{ - struct twl6030_usb *twl = _twl; - enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; - u8 vbus_state, hw_state; - - hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); - - vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, - CONTROLLER_STAT1); - if (!(hw_state & STS_USB_ID)) { - if (vbus_state & VBUS_DET) { - regulator_enable(twl->usb3v3); - twl->asleep = 1; - status = OMAP_MUSB_VBUS_VALID; - twl->linkstat = status; - omap_musb_mailbox(status); - } else { - if (twl->linkstat != OMAP_MUSB_UNKNOWN) { - status = OMAP_MUSB_VBUS_OFF; - twl->linkstat = status; - omap_musb_mailbox(status); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; - } - } - } - } - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); - - return IRQ_HANDLED; -} - -static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) -{ - struct twl6030_usb *twl = _twl; - enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; - u8 hw_state; - - hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); - - if (hw_state & STS_USB_ID) { - - regulator_enable(twl->usb3v3); - twl->asleep = 1; - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); - twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); - status = OMAP_MUSB_ID_GROUND; - twl->linkstat = status; - omap_musb_mailbox(status); - } else { - twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); - } - twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); - - return IRQ_HANDLED; -} - -static int twl6030_enable_irq(struct twl6030_usb *twl) -{ - twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); - twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); - twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); - - twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, - REG_INT_MSK_LINE_C); - twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, - REG_INT_MSK_STS_C); - twl6030_usb_irq(twl->irq2, twl); - twl6030_usbotg_irq(twl->irq1, twl); - - return 0; -} - -static void otg_set_vbus_work(struct work_struct *data) -{ - struct twl6030_usb *twl = container_of(data, struct twl6030_usb, - set_vbus_work); - - /* - * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 - * register. This enables boost mode. - */ - - if (twl->vbus_enable) - twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40, - CHARGERUSB_CTRL1); - else - twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00, - CHARGERUSB_CTRL1); -} - -static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) -{ - struct twl6030_usb *twl = comparator_to_twl(comparator); - - twl->vbus_enable = enabled; - schedule_work(&twl->set_vbus_work); - - return 0; -} - -static int twl6030_usb_probe(struct platform_device *pdev) -{ - u32 ret; - struct twl6030_usb *twl; - int status, err; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - struct twl4030_usb_data *pdata = dev->platform_data; - - twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); - if (!twl) - return -ENOMEM; - - twl->dev = &pdev->dev; - twl->irq1 = platform_get_irq(pdev, 0); - twl->irq2 = platform_get_irq(pdev, 1); - twl->linkstat = OMAP_MUSB_UNKNOWN; - - twl->comparator.set_vbus = twl6030_set_vbus; - twl->comparator.start_srp = twl6030_start_srp; - - ret = omap_usb2_set_comparator(&twl->comparator); - if (ret == -ENODEV) { - dev_info(&pdev->dev, "phy not ready, deferring probe"); - return -EPROBE_DEFER; - } - - if (np) { - twl->regulator = "usb"; - } else if (pdata) { - if (pdata->features & TWL6025_SUBCLASS) - twl->regulator = "ldousb"; - else - twl->regulator = "vusb"; - } else { - dev_err(&pdev->dev, "twl6030 initialized without pdata\n"); - return -EINVAL; - } - - /* init spinlock for workqueue */ - spin_lock_init(&twl->lock); - - err = twl6030_usb_ldo_init(twl); - if (err) { - dev_err(&pdev->dev, "ldo init failed\n"); - return err; - } - - platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); - - INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); - - twl->irq_enabled = true; - status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "twl6030_usb", twl); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq1, status); - device_remove_file(twl->dev, &dev_attr_vbus); - return status; - } - - status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "twl6030_usb", twl); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq2, status); - free_irq(twl->irq1, twl); - device_remove_file(twl->dev, &dev_attr_vbus); - return status; - } - - twl->asleep = 0; - twl6030_enable_irq(twl); - dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); - - return 0; -} - -static int __exit twl6030_usb_remove(struct platform_device *pdev) -{ - struct twl6030_usb *twl = platform_get_drvdata(pdev); - - twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, - REG_INT_MSK_LINE_C); - twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, - REG_INT_MSK_STS_C); - free_irq(twl->irq1, twl); - free_irq(twl->irq2, twl); - regulator_put(twl->usb3v3); - device_remove_file(twl->dev, &dev_attr_vbus); - cancel_work_sync(&twl->set_vbus_work); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl6030_usb_id_table[] = { - { .compatible = "ti,twl6030-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); -#endif - -static struct platform_driver twl6030_usb_driver = { - .probe = twl6030_usb_probe, - .remove = __exit_p(twl6030_usb_remove), - .driver = { - .name = "twl6030_usb", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(twl6030_usb_id_table), - }, -}; - -static int __init twl6030_usb_init(void) -{ - return platform_driver_register(&twl6030_usb_driver); -} -subsys_initcall(twl6030_usb_init); - -static void __exit twl6030_usb_exit(void) -{ - platform_driver_unregister(&twl6030_usb_driver); -} -module_exit(twl6030_usb_exit); - -MODULE_ALIAS("platform:twl6030_usb"); -MODULE_AUTHOR("Hema HK "); -MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/ulpi.c b/drivers/usb/phy/ulpi.c deleted file mode 100644 index 217339d..0000000 --- a/drivers/usb/phy/ulpi.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Generic ULPI USB transceiver support - * - * Copyright (C) 2009 Daniel Mack - * - * Based on sources from - * - * Sascha Hauer - * Freescale Semiconductors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - - -struct ulpi_info { - unsigned int id; - char *name; -}; - -#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) -#define ULPI_INFO(_id, _name) \ - { \ - .id = (_id), \ - .name = (_name), \ - } - -/* ULPI hardcoded IDs, used for probing */ -static struct ulpi_info ulpi_ids[] = { - ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"), - ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), -}; - -static int ulpi_set_otg_flags(struct usb_phy *phy) -{ - unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | - ULPI_OTG_CTRL_DM_PULLDOWN; - - if (phy->flags & ULPI_OTG_ID_PULLUP) - flags |= ULPI_OTG_CTRL_ID_PULLUP; - - /* - * ULPI Specification rev.1.1 default - * for Dp/DmPulldown is enabled. - */ - if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) - flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; - - if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) - flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; - - if (phy->flags & ULPI_OTG_EXTVBUSIND) - flags |= ULPI_OTG_CTRL_EXTVBUSIND; - - return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); -} - -static int ulpi_set_fc_flags(struct usb_phy *phy) -{ - unsigned int flags = 0; - - /* - * ULPI Specification rev.1.1 default - * for XcvrSelect is Full Speed. - */ - if (phy->flags & ULPI_FC_HS) - flags |= ULPI_FUNC_CTRL_HIGH_SPEED; - else if (phy->flags & ULPI_FC_LS) - flags |= ULPI_FUNC_CTRL_LOW_SPEED; - else if (phy->flags & ULPI_FC_FS4LS) - flags |= ULPI_FUNC_CTRL_FS4LS; - else - flags |= ULPI_FUNC_CTRL_FULL_SPEED; - - if (phy->flags & ULPI_FC_TERMSEL) - flags |= ULPI_FUNC_CTRL_TERMSELECT; - - /* - * ULPI Specification rev.1.1 default - * for OpMode is Normal Operation. - */ - if (phy->flags & ULPI_FC_OP_NODRV) - flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - else if (phy->flags & ULPI_FC_OP_DIS_NRZI) - flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; - else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) - flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; - else - flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - - /* - * ULPI Specification rev.1.1 default - * for SuspendM is Powered. - */ - flags |= ULPI_FUNC_CTRL_SUSPENDM; - - return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); -} - -static int ulpi_set_ic_flags(struct usb_phy *phy) -{ - unsigned int flags = 0; - - if (phy->flags & ULPI_IC_AUTORESUME) - flags |= ULPI_IFC_CTRL_AUTORESUME; - - if (phy->flags & ULPI_IC_EXTVBUS_INDINV) - flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; - - if (phy->flags & ULPI_IC_IND_PASSTHRU) - flags |= ULPI_IFC_CTRL_PASSTHRU; - - if (phy->flags & ULPI_IC_PROTECT_DIS) - flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; - - return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); -} - -static int ulpi_set_flags(struct usb_phy *phy) -{ - int ret; - - ret = ulpi_set_otg_flags(phy); - if (ret) - return ret; - - ret = ulpi_set_ic_flags(phy); - if (ret) - return ret; - - return ulpi_set_fc_flags(phy); -} - -static int ulpi_check_integrity(struct usb_phy *phy) -{ - int ret, i; - unsigned int val = 0x55; - - for (i = 0; i < 2; i++) { - ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); - if (ret < 0) - return ret; - - ret = usb_phy_io_read(phy, ULPI_SCRATCH); - if (ret < 0) - return ret; - - if (ret != val) { - pr_err("ULPI integrity check: failed!"); - return -ENODEV; - } - val = val << 1; - } - - pr_info("ULPI integrity check: passed.\n"); - - return 0; -} - -static int ulpi_init(struct usb_phy *phy) -{ - int i, vid, pid, ret; - u32 ulpi_id = 0; - - for (i = 0; i < 4; i++) { - ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); - if (ret < 0) - return ret; - ulpi_id = (ulpi_id << 8) | ret; - } - vid = ulpi_id & 0xffff; - pid = ulpi_id >> 16; - - pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); - - for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) { - if (ulpi_ids[i].id == ULPI_ID(vid, pid)) { - pr_info("Found %s ULPI transceiver.\n", - ulpi_ids[i].name); - break; - } - } - - ret = ulpi_check_integrity(phy); - if (ret) - return ret; - - return ulpi_set_flags(phy); -} - -static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct usb_phy *phy = otg->phy; - unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); - - if (!host) { - otg->host = NULL; - return 0; - } - - otg->host = host; - - flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE | - ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | - ULPI_IFC_CTRL_CARKITMODE); - - if (phy->flags & ULPI_IC_6PIN_SERIAL) - flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; - else if (phy->flags & ULPI_IC_3PIN_SERIAL) - flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; - else if (phy->flags & ULPI_IC_CARKIT) - flags |= ULPI_IFC_CTRL_CARKITMODE; - - return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); -} - -static int ulpi_set_vbus(struct usb_otg *otg, bool on) -{ - struct usb_phy *phy = otg->phy; - unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); - - flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); - - if (on) { - if (phy->flags & ULPI_OTG_DRVVBUS) - flags |= ULPI_OTG_CTRL_DRVVBUS; - - if (phy->flags & ULPI_OTG_DRVVBUS_EXT) - flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; - } - - return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); -} - -struct usb_phy * -otg_ulpi_create(struct usb_phy_io_ops *ops, - unsigned int flags) -{ - struct usb_phy *phy; - struct usb_otg *otg; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return NULL; - - otg = kzalloc(sizeof(*otg), GFP_KERNEL); - if (!otg) { - kfree(phy); - return NULL; - } - - phy->label = "ULPI"; - phy->flags = flags; - phy->io_ops = ops; - phy->otg = otg; - phy->init = ulpi_init; - - otg->phy = phy; - otg->set_host = ulpi_set_host; - otg->set_vbus = ulpi_set_vbus; - - return phy; -} -EXPORT_SYMBOL_GPL(otg_ulpi_create); - diff --git a/drivers/usb/phy/ulpi_viewport.c b/drivers/usb/phy/ulpi_viewport.c deleted file mode 100644 index c5ba7e5..0000000 --- a/drivers/usb/phy/ulpi_viewport.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include - -#define ULPI_VIEW_WAKEUP (1 << 31) -#define ULPI_VIEW_RUN (1 << 30) -#define ULPI_VIEW_WRITE (1 << 29) -#define ULPI_VIEW_READ (0 << 29) -#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) -#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) -#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) - -static int ulpi_viewport_wait(void __iomem *view, u32 mask) -{ - unsigned long usec = 2000; - - while (usec--) { - if (!(readl(view) & mask)) - return 0; - - udelay(1); - }; - - return -ETIMEDOUT; -} - -static int ulpi_viewport_read(struct usb_phy *otg, u32 reg) -{ - int ret; - void __iomem *view = otg->io_priv; - - writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); - if (ret) - return ret; - - writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); - if (ret) - return ret; - - return ULPI_VIEW_DATA_READ(readl(view)); -} - -static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) -{ - int ret; - void __iomem *view = otg->io_priv; - - writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); - ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); - if (ret) - return ret; - - writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | - ULPI_VIEW_ADDR(reg), view); - - return ulpi_viewport_wait(view, ULPI_VIEW_RUN); -} - -struct usb_phy_io_ops ulpi_viewport_access_ops = { - .read = ulpi_viewport_read, - .write = ulpi_viewport_write, -}; -- cgit v0.10.2 From 790d3a5ab6e36fb06e99339afe35ecdec4d3b2cb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Mar 2013 13:01:40 +0200 Subject: usb: phy: isp1301: give it a context structure this patch is a small preparation to fix isp1301 driver so that other platforms can use it. We're defining our private data structure to represent this device and adding the PHY to the PHY list. Later patches will come implementing proper PHY API and removing bogus code from ohci_nxp and lpc32xx_udc drivers. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 18dbf7e..5e0f143 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -11,10 +11,19 @@ */ #include +#include #include +#include #define DRV_NAME "isp1301" +struct isp1301 { + struct usb_phy phy; + struct mutex mutex; + + struct i2c_client *client; +}; + static const struct i2c_device_id isp1301_id[] = { { "isp1301", 0 }, { } @@ -25,12 +34,35 @@ static struct i2c_client *isp1301_i2c_client; static int isp1301_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { + struct isp1301 *isp; + struct usb_phy *phy; + + isp = devm_kzalloc(&client->dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->client = client; + mutex_init(&isp->mutex); + + phy = &isp->phy; + phy->label = DRV_NAME; + phy->type = USB_PHY_TYPE_USB2; + + i2c_set_clientdata(client, isp); + usb_add_phy_dev(phy); + isp1301_i2c_client = client; + return 0; } static int isp1301_remove(struct i2c_client *client) { + struct isp1301 *isp = i2c_get_clientdata(client); + + usb_remove_phy(&isp->phy); + isp1301_i2c_client = NULL; + return 0; } -- cgit v0.10.2 From b774212ea5f13911a5e0211a7088e42dad46b4c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Mar 2013 13:22:58 +0200 Subject: usb: phy: introduce ->set_vbus() method this method will be used to enable or disable the charge pump. Whenever we have DRD devices, we need to be able to turn VBUS on or off whenever we want. Note that in the ideal case, this would be controlled by the ID-pin Interrupt, but not all devices have ID-pin properly routed since manufacturers can choose to save that trace if they're building a host-only product out of a DRD IP. This is also useful during debugging where we might not have the proper cable hanging around. Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index b001dc3..b7c2217 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -91,6 +91,9 @@ struct usb_phy { int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); + /* enable/disable VBUS */ + int (*set_vbus)(struct usb_phy *x, int on); + /* effective for B devices, ignored for A-peripheral */ int (*set_power)(struct usb_phy *x, unsigned mA); @@ -160,6 +163,24 @@ usb_phy_shutdown(struct usb_phy *x) x->shutdown(x); } +static inline int +usb_phy_vbus_on(struct usb_phy *x) +{ + if (!x->set_vbus) + return 0; + + return x->set_vbus(x, true); +} + +static inline int +usb_phy_vbus_off(struct usb_phy *x) +{ + if (!x->set_vbus) + return 0; + + return x->set_vbus(x, false); +} + /* for usb host and peripheral controller drivers */ #if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); -- cgit v0.10.2 From c38a4f3f508d47e51c3f28e8946b1482ebf47fee Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Mar 2013 13:25:18 +0200 Subject: usb: phy: isp1301: implement PHY API this patch implements ->init() and ->set_vbus() methods for isp1301 transceiver driver. Later patches can now come in order to remove the hackery from ohci-nxp and lpc32xx udc drivers. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 5e0f143..225ae6c 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -14,6 +14,7 @@ #include #include #include +#include #define DRV_NAME "isp1301" @@ -24,6 +25,8 @@ struct isp1301 { struct i2c_client *client; }; +#define phy_to_isp(p) (container_of((p), struct isp1301, phy)) + static const struct i2c_device_id isp1301_id[] = { { "isp1301", 0 }, { } @@ -31,6 +34,60 @@ static const struct i2c_device_id isp1301_id[] = { static struct i2c_client *isp1301_i2c_client; +static int __isp1301_write(struct isp1301 *isp, u8 reg, u8 value, u8 clear) +{ + return i2c_smbus_write_byte_data(isp->client, reg | clear, value); +} + +static int isp1301_write(struct isp1301 *isp, u8 reg, u8 value) +{ + return __isp1301_write(isp, reg, value, 0); +} + +static int isp1301_clear(struct isp1301 *isp, u8 reg, u8 value) +{ + return __isp1301_write(isp, reg, value, ISP1301_I2C_REG_CLEAR_ADDR); +} + +static int isp1301_phy_init(struct usb_phy *phy) +{ + struct isp1301 *isp = phy_to_isp(phy); + + /* Disable transparent UART mode first */ + isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_UART_EN); + isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, ~MC1_SPEED_REG); + isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_SPEED_REG); + isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_2, ~0); + isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_PSW_EN + | MC2_SPD_SUSP_CTRL)); + + isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, ~0); + isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_DAT_SE0); + isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLDOWN + | OTG1_DP_PULLDOWN)); + isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLUP + | OTG1_DP_PULLUP)); + + /* mask all interrupts */ + isp1301_clear(isp, ISP1301_I2C_INTERRUPT_LATCH, ~0); + isp1301_clear(isp, ISP1301_I2C_INTERRUPT_FALLING, ~0); + isp1301_clear(isp, ISP1301_I2C_INTERRUPT_RISING, ~0); + + return 0; +} + +static int isp1301_phy_set_vbus(struct usb_phy *phy, int on) +{ + struct isp1301 *isp = phy_to_isp(phy); + + if (on) + isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV); + else + isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV); + + return 0; +} + static int isp1301_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { @@ -46,6 +103,8 @@ static int isp1301_probe(struct i2c_client *client, phy = &isp->phy; phy->label = DRV_NAME; + phy->init = isp1301_phy_init; + phy->set_vbus = isp1301_phy_set_vbus; phy->type = USB_PHY_TYPE_USB2; i2c_set_clientdata(client, isp); -- cgit v0.10.2 From 38678f25689c6ee90b443407dba04fb8c0297db3 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 11 Mar 2013 18:28:02 +0800 Subject: usb: gadget: s3c-hsudc: delete outdated comment since commit d93e260 (usb: gadget: s3c-hsudc: use udc_start and udc_stop functions) the 'driver' parameter has been deleted from s3c_hsudc_stop_activity() but its documentation was left outdated. This patch deletes the comment since it makes no sense anymore. Signed-off-by: Chen Gang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index bfe7910..b1f0771 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -283,7 +283,6 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) /** * s3c_hsudc_stop_activity - Stop activity on all endpoints. * @hsudc: Device controller for which EP activity is to be stopped. - * @driver: Reference to the gadget driver which is currently active. * * All the endpoints are stopped and any pending transfer requests if any on * the endpoint are terminated. -- cgit v0.10.2 From 662e9469cbd736e9e0cd72468f3d62e75b159464 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 10:45:02 +0200 Subject: usb: gadget: atmel: remove unused DMA_ADDR_INVALID DMA_ADDR_INVALID isn't (and shouldn't) be used anymore, let's remove it. Acked-by: Nicolas Ferre Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h index 9791259..d65a618 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -216,12 +216,6 @@ #define EP0_EPT_SIZE USBA_EPT_SIZE_64 #define EP0_NR_BANKS 1 -/* - * REVISIT: Try to eliminate this value. Can we rely on req->mapped to - * provide this information? - */ -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - #define FIFO_IOMEM_ID 0 #define CTRL_IOMEM_ID 1 -- cgit v0.10.2 From c36cbfc045bf6e812fa3b9e898603ff45316f369 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 10:45:42 +0200 Subject: usb: gadget: net2272: remove unused DMA_ADDR_INVALID DMA_ADDR_INVALID isn't used anymore, it's safe to remove it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 8dcbe77..03e4104 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -58,7 +58,6 @@ static const char * const ep_name[] = { "ep-a", "ep-b", "ep-c", }; -#define DMA_ADDR_INVALID (~(dma_addr_t)0) #ifdef CONFIG_USB_GADGET_NET2272_DMA /* * use_dma: the NET2272 can use an external DMA controller. @@ -341,7 +340,6 @@ net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; -- cgit v0.10.2 From 853f97b5f3886012d293257db74d65b14f958940 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 10:46:19 +0200 Subject: usb: gadget: net2280: remove unused DMA_ADDR_INVALID DMA_ADDR_INVALID isn't used anymore, it's safe to remove it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index e5f2ef1..691cc65 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -65,7 +65,6 @@ #define DRIVER_DESC "PLX NET228x USB Peripheral Controller" #define DRIVER_VERSION "2005 Sept 27" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) #define EP_DONTUSE 13 /* nonzero */ #define USE_RDK_LEDS /* GPIO pins control three LEDs */ @@ -406,7 +405,6 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); /* this dma descriptor may be swapped with the previous dummy */ @@ -420,7 +418,6 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) return NULL; } td->dmacount = 0; /* not VALID */ - td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID); td->dmadesc = td->dmaaddr; req->td = td; } @@ -2788,7 +2785,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) goto done; } td->dmacount = 0; /* not VALID */ - td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID); td->dmadesc = td->dmaaddr; dev->ep [i].dummy = td; } -- cgit v0.10.2 From 482ef1d2fec5e5d44d97f7e20b4ccf3d3604aaae Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 10:48:02 +0200 Subject: usb: gadget: uvc: remove references to DMA_ADDR_INVALID gadget drivers shouldn't touch req->dma at all, since UDC drivers are the ones required to handle mapping and unmapping of the request buffer. Remove references to DMA_ADDR_INVALID so we don't creat false expectations to gadget driver writers. Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 93b0c11..7e90b1d 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -98,8 +98,6 @@ extern unsigned int uvc_gadget_trace_param; #define DRIVER_VERSION "0.1.0" #define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - #define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 2ca9386..0080d07 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -41,7 +41,6 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) req->length = min_t(unsigned int, uvc->event_length, data->length); req->zero = data->length < uvc->event_length; - req->dma = DMA_ADDR_INVALID; memcpy(req->buf, data->data, data->length); diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index b0e53a8..885c393 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -245,7 +245,6 @@ uvc_video_alloc_requests(struct uvc_video *video) video->req[i]->buf = video->req_buffer[i]; video->req[i]->length = 0; - video->req[i]->dma = DMA_ADDR_INVALID; video->req[i]->complete = uvc_video_complete; video->req[i]->context = video; -- cgit v0.10.2 From 40b8156f9426db48d5d648cdc95bd0789981e9f4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 10:49:13 +0200 Subject: usb: renesas: remove unused DMA_ADDR_INVALID DMA_ADDR_INVALID isn't used anymore, it's safe to remove it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 9538f0f..45b9401 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -32,7 +32,6 @@ */ void usbhs_pkt_init(struct usbhs_pkt *pkt) { - pkt->dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&pkt->node); } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index c31731a..a168a17 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -23,8 +23,6 @@ #include #include "pipe.h" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - struct usbhs_fifo { char *name; u32 port; /* xFIFO */ -- cgit v0.10.2 From d4436c3a6e4ea3000b794eb61e0fc1db46d14175 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Thu, 14 Mar 2013 16:05:24 +0530 Subject: usb: dwc3: core: fix wrong OTG event regitser offset This patch fixes the wrong OTG_EVT,OTG_EVTEN and OTG_STS register offsets. While at that, also add a missing register to debugfs regdump utility. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b42f71c..b69d322 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -154,8 +154,9 @@ /* OTG Registers */ #define DWC3_OCFG 0xcc00 #define DWC3_OCTL 0xcc04 -#define DWC3_OEVTEN 0xcc08 -#define DWC3_OSTS 0xcc0C +#define DWC3_OEVT 0xcc08 +#define DWC3_OEVTEN 0xcc0C +#define DWC3_OSTS 0xcc10 /* Bit fields */ diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 8b23d045..9e9f122 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -372,6 +372,7 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(OCFG), dump_register(OCTL), + dump_register(OEVT), dump_register(OEVTEN), dump_register(OSTS), }; -- cgit v0.10.2 From ddff14f1ab9b55b73ba59126ef4a10966725fc9d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 7 Mar 2013 18:51:43 +0530 Subject: usb: dwc3: set dma_mask for dwc3_omap device *dma_mask* is not set for devices created from dt data. So filled dma_mask for dwc3_omap device here. And dwc3 core will copy the dma_mask from its parent. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 66c0572..c845e70 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -454,6 +454,10 @@ static int dwc3_probe(struct platform_device *pdev) dwc->regs_size = resource_size(res); dwc->dev = dev; + dev->dma_mask = dev->parent->dma_mask; + dev->dma_parms = dev->parent->dma_parms; + dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); + if (!strncmp("super", maximum_speed, 5)) dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; else if (!strncmp("high", maximum_speed, 4)) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 35b9673..546f1fd 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -277,6 +277,8 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00); } +static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32); + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -330,6 +332,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) omap->dev = dev; omap->irq = irq; omap->base = base; + dev->dma_mask = &dwc3_omap_dma_mask; /* * REVISIT if we ever have two instances of the wrapper, we will be -- cgit v0.10.2 From 2ba7943af0f0cca5a069cd3aff807815bc76fff1 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 7 Mar 2013 18:51:44 +0530 Subject: usb: dwc3: dwc3-omap: return -EPROBE_DEFER if probe has not yet executed return -EPROBE_DEFER from dwc3_omap_mailbox in dwc3-omap.c, if the probe of dwc3-omap has not yet been executed or failed. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 546f1fd..2fe9723f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -138,11 +138,14 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value) writel(value, base + offset); } -void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) { u32 val; struct dwc3_omap *omap = _omap; + if (!omap) + return -EPROBE_DEFER; + switch (status) { case OMAP_DWC3_ID_GROUND: dev_dbg(omap->dev, "ID GND\n"); @@ -185,7 +188,7 @@ void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) dev_dbg(omap->dev, "ID float\n"); } - return; + return 0; } EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h index 51eae14..5615f4d 100644 --- a/include/linux/usb/dwc3-omap.h +++ b/include/linux/usb/dwc3-omap.h @@ -19,11 +19,11 @@ enum omap_dwc3_vbus_id_status { }; #if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE)) -extern void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); +extern int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); #else -static inline void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +static inline int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) { - return; + return -ENODEV; } #endif -- cgit v0.10.2 From dc2377d0b0a298ec9d7d232c0d757f462dedcca2 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 15:59:10 +0530 Subject: usb: phy: samsung: Common out the generic stuff Moving register and structure definitions to header file, and keeping the generic functions to be used across multiple PHYs in common phy helper driver under SAMSUNG_USBPHY, and moving USB 2.0 PHY driver under SAMSUNG_USB2PHY. Also allowing samsung PHY drivers be built as modules. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 0331949..96940ab 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -1,20 +1,25 @@ -* Samsung's usb phy transceiver +SAMSUNG USB-PHY controllers -The Samsung's phy transceiver is used for controlling usb phy for -s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers -across Samsung SOCs. +** Samsung's usb 2.0 phy transceiver + +The Samsung's usb 2.0 phy transceiver is used for controlling +usb 2.0 phy for s3c-hsotg as well as ehci-s5p and ohci-exynos +usb controllers across Samsung SOCs. TODO: Adding the PHY binding with controller(s) according to the under developement generic PHY driver. Required properties: Exynos4210: -- compatible : should be "samsung,exynos4210-usbphy" +- compatible : should be "samsung,exynos4210-usb2phy" - reg : base physical address of the phy registers and length of memory mapped region. +- clocks: Clock IDs array as required by the controller. +- clock-names: names of clock correseponding IDs clock property as requested + by the controller driver. Exynos5250: -- compatible : should be "samsung,exynos5250-usbphy" +- compatible : should be "samsung,exynos5250-usb2phy" - reg : base physical address of the phy registers and length of memory mapped region. @@ -44,10 +49,13 @@ Example: usbphy@125B0000 { #address-cells = <1>; #size-cells = <1>; - compatible = "samsung,exynos4210-usbphy"; + compatible = "samsung,exynos4210-usb2phy"; reg = <0x125B0000 0x100>; ranges; + clocks = <&clock 2>, <&clock 305>; + clock-names = "xusbxti", "otg"; + usbphy-sys { /* USB device and host PHY_CONTROL registers */ reg = <0x10020704 0x8>; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 97de6de..e8cd52a 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -86,11 +86,18 @@ config OMAP_USB3 on/off the PHY. config SAMSUNG_USBPHY - bool "Samsung USB PHY controller Driver" - depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS + tristate "Samsung USB PHY Driver" help - Enable this to support Samsung USB phy controller for samsung - SoCs. + Enable this to support Samsung USB phy helper driver for Samsung SoCs. + This driver provides common interface to interact, for Samsung USB 2.0 PHY + driver and later for Samsung USB 3.0 PHY driver. + +config SAMSUNG_USB2PHY + tristate "Samsung USB 2.0 PHY controller Driver" + select SAMSUNG_USBPHY + help + Enable this to support Samsung USB 2.0 (High Speed) PHY controller + driver for Samsung SoCs. config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5fb4a5d..8cd355f 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o +obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 967101e..7b118ee5 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -1,12 +1,13 @@ -/* linux/drivers/usb/phy/samsung-usbphy.c +/* linux/drivers/usb/phy/phy-samsung-usb.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Praveen Paneri * - * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. + * Samsung USB-PHY helper driver with common function calls; + * interacts with Samsung USB 2.0 PHY controller driver and later + * with Samsung USB 3.0 PHY driver. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,233 +22,16 @@ #include #include #include -#include #include #include #include #include #include -#include #include -#include -/* Register definitions */ +#include "phy-samsung-usb.h" -#define SAMSUNG_PHYPWR (0x00) - -#define PHYPWR_NORMAL_MASK (0x19 << 0) -#define PHYPWR_OTG_DISABLE (0x1 << 4) -#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) -#define PHYPWR_FORCE_SUSPEND (0x1 << 1) -/* For Exynos4 */ -#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) -#define PHYPWR_SLEEP_PHY0 (0x1 << 5) - -#define SAMSUNG_PHYCLK (0x04) - -#define PHYCLK_MODE_USB11 (0x1 << 6) -#define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) -#define PHYCLK_ID_PULL (0x1 << 2) -#define PHYCLK_CLKSEL_MASK (0x3 << 0) -#define PHYCLK_CLKSEL_48M (0x0 << 0) -#define PHYCLK_CLKSEL_12M (0x2 << 0) -#define PHYCLK_CLKSEL_24M (0x3 << 0) - -#define SAMSUNG_RSTCON (0x08) - -#define RSTCON_PHYLINK_SWRST (0x1 << 2) -#define RSTCON_HLINK_SWRST (0x1 << 1) -#define RSTCON_SWRST (0x1 << 0) - -/* EXYNOS5 */ -#define EXYNOS5_PHY_HOST_CTRL0 (0x00) - -#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) - -#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) -#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) -#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) -#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) - -#define HOST_CTRL0_FSEL_MASK (0x7 << 16) -#define HOST_CTRL0_FSEL(_x) ((_x) << 16) - -#define FSEL_CLKSEL_50M (0x7) -#define FSEL_CLKSEL_24M (0x5) -#define FSEL_CLKSEL_20M (0x4) -#define FSEL_CLKSEL_19200K (0x3) -#define FSEL_CLKSEL_12M (0x2) -#define FSEL_CLKSEL_10M (0x1) -#define FSEL_CLKSEL_9600K (0x0) - -#define HOST_CTRL0_TESTBURNIN (0x1 << 11) -#define HOST_CTRL0_RETENABLE (0x1 << 10) -#define HOST_CTRL0_COMMONON_N (0x1 << 9) -#define HOST_CTRL0_SIDDQ (0x1 << 6) -#define HOST_CTRL0_FORCESLEEP (0x1 << 5) -#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) -#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) -#define HOST_CTRL0_UTMISWRST (0x1 << 2) -#define HOST_CTRL0_LINKSWRST (0x1 << 1) -#define HOST_CTRL0_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_TUNE0 (0x04) - -#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) - -#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) - -#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) - -#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) - -#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) -#define HSIC_CTRL_REFCLKSEL (0x2 << 23) - -#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) -#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) -#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) -#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) -#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) -#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) -#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) - -#define HSIC_CTRL_SIDDQ (0x1 << 6) -#define HSIC_CTRL_FORCESLEEP (0x1 << 5) -#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) -#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) -#define HSIC_CTRL_UTMISWRST (0x1 << 2) -#define HSIC_CTRL_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) - -#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) -#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) -#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) -#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) - -#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) - -#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) -#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) -#define HOST_OHCICTRL_CNTSEL (0x1 << 1) -#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) - -#define EXYNOS5_PHY_OTG_SYS (0x38) - -#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) -#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) -#define OTG_SYS_PHY0_SWRST (0x1 << 12) - -#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) -#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) -#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) -#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) - -#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) -#define OTG_SYS_COMMON_ON (0x1 << 7) - -#define OTG_SYS_FSEL_MASK (0x7 << 4) -#define OTG_SYS_FSEL(_x) ((_x) << 4) - -#define OTG_SYS_FORCESLEEP (0x1 << 3) -#define OTG_SYS_OTGDISABLE (0x1 << 2) -#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) -#define OTG_SYS_FORCESUSPEND (0x1 << 0) - -#define EXYNOS5_PHY_OTG_TUNE (0x40) - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#ifndef KHZ -#define KHZ (1000) -#endif - -#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) -#define S3C64XX_USBPHY_ENABLE (0x1 << 16) -#define EXYNOS_USBPHY_ENABLE (0x1 << 0) -#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) - -enum samsung_cpu_type { - TYPE_S3C64XX, - TYPE_EXYNOS4210, - TYPE_EXYNOS5250, -}; - -/* - * struct samsung_usbphy_drvdata - driver data for various SoC variants - * @cpu_type: machine identifier - * @devphy_en_mask: device phy enable mask for PHY CONTROL register - * @hostphy_en_mask: host phy enable mask for PHY CONTROL register - * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from - * mapped address of system controller. - * @hostphy_reg_offset: offset to HOST PHY CONTROL register from - * mapped address of system controller. - * - * Here we have a separate mask for device type phy. - * Having different masks for host and device type phy helps - * in setting independent masks in case of SoCs like S5PV210, - * in which PHY0 and PHY1 enable bits belong to same register - * placed at position 0 and 1 respectively. - * Although for newer SoCs like exynos these bits belong to - * different registers altogether placed at position 0. - */ -struct samsung_usbphy_drvdata { - int cpu_type; - int devphy_en_mask; - int hostphy_en_mask; - u32 devphy_reg_offset; - u32 hostphy_reg_offset; -}; - -/* - * struct samsung_usbphy - transceiver driver state - * @phy: transceiver structure - * @plat: platform data - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @regs: usb phy controller registers memory base - * @pmuregs: USB device PHY_CONTROL register memory base - * @sysreg: USB2.0 PHY_CFG register memory base - * @ref_clk_freq: reference clock frequency selection - * @drv_data: driver data available for different SoCs - * @phy_type: Samsung SoCs specific phy types: #HOST - * #DEVICE - * @phy_usage: usage count for phy - * @lock: lock for phy operations - */ -struct samsung_usbphy { - struct usb_phy phy; - struct samsung_usbphy_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *regs; - void __iomem *pmuregs; - void __iomem *sysreg; - int ref_clk_freq; - const struct samsung_usbphy_drvdata *drv_data; - enum samsung_usb_phy_type phy_type; - atomic_t phy_usage; - spinlock_t lock; -}; - -#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) - -int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!otg->host) - otg->host = host; - - return 0; -} - -static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) +int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) { struct device_node *usbphy_sys; @@ -282,13 +66,14 @@ err0: of_node_put(usbphy_sys); return -ENXIO; } +EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); /* * Set isolation here for phy. * Here 'on = true' would mean USB PHY block is isolated, hence * de-activated and vice-versa. */ -static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) +void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) { void __iomem *reg = NULL; u32 reg_val; @@ -336,11 +121,12 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) writel(reg_val, reg); } +EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation); /* * Configure the mode of working of usb-phy here: HOST/DEVICE. */ -static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) +void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) { u32 reg; @@ -358,13 +144,14 @@ static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) writel(reg, sphy->sysreg); } +EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); /* * PHYs are different for USB Device and USB Host. * This make sure that correct PHY type is selected before * any operation on PHY. */ -static int samsung_usbphy_set_type(struct usb_phy *phy, +int samsung_usbphy_set_type(struct usb_phy *phy, enum samsung_usb_phy_type phy_type) { struct samsung_usbphy *sphy = phy_to_sphy(phy); @@ -373,11 +160,12 @@ static int samsung_usbphy_set_type(struct usb_phy *phy, return 0; } +EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); /* * Returns reference clock frequency selection value */ -static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) { struct clk *ref_clk; int refclk_freq = 0; @@ -387,9 +175,9 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) * external crystal clock XXTI */ if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - ref_clk = clk_get(sphy->dev, "ext_xtal"); + ref_clk = devm_clk_get(sphy->dev, "ext_xtal"); else - ref_clk = clk_get(sphy->dev, "xusbxti"); + ref_clk = devm_clk_get(sphy->dev, "xusbxti"); if (IS_ERR(ref_clk)) { dev_err(sphy->dev, "Failed to get reference clock\n"); return PTR_ERR(ref_clk); @@ -445,484 +233,4 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) return refclk_freq; } - -static bool exynos5_phyhost_is_on(void *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - -static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyclk = sphy->ref_clk_freq; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - u32 ehcictrl; - u32 ohcictrl; - - /* - * phy_usage helps in keeping usage count for phy - * so that the first consumer enabling the phy is also - * the last consumer to disable it. - */ - - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); - return; - } - - /* Host configuration */ - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - /* phy reference clock configuration */ - phyhost &= ~HOST_CTRL0_FSEL_MASK; - phyhost |= HOST_CTRL0_FSEL(phyclk); - - /* host phy reset */ - phyhost &= ~(HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL | - HOST_CTRL0_SIDDQ | - /* Enable normal mode of operation */ - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP); - - /* Link reset */ - phyhost |= (HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST | - /* COMMON Block configuration during suspend */ - HOST_CTRL0_COMMONON_N); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - udelay(10); - phyhost &= ~(HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - /* OTG configuration */ - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - - /* phy reference clock configuration */ - phyotg &= ~OTG_SYS_FSEL_MASK; - phyotg |= OTG_SYS_FSEL(phyclk); - - /* Enable normal mode of operation */ - phyotg &= ~(OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP | - OTG_SYS_REFCLKSEL_MASK | - /* COMMON Block configuration during suspend */ - OTG_SYS_COMMON_ON); - - /* OTG phy & link reset */ - phyotg |= (OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET | - OTG_SYS_OTGDISABLE | - /* Set phy refclk */ - OTG_SYS_REFCLKSEL_CLKCORE); - - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - udelay(10); - phyotg &= ~(OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - - /* HSIC phy configuration */ - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_PHYSWRST); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - udelay(10); - phyhsic &= ~HSIC_CTRL_PHYSWRST; - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - udelay(80); - - /* enable EHCI DMA burst */ - ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); - ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | - HOST_EHCICTRL_ENAINCR4 | - HOST_EHCICTRL_ENAINCR8 | - HOST_EHCICTRL_ENAINCR16); - writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); - - /* set ohci_suspend_on_n */ - ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); - ohcictrl |= HOST_OHCICTRL_SUSPLGCY; - writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); -} - -static void samsung_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - u32 phyclk; - u32 rstcon; - - /* set clock frequency for PLL */ - phyclk = sphy->ref_clk_freq; - phypwr = readl(regs + SAMSUNG_PHYPWR); - rstcon = readl(regs + SAMSUNG_RSTCON); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; - phypwr &= ~PHYPWR_NORMAL_MASK; - rstcon |= RSTCON_SWRST; - break; - case TYPE_EXYNOS4210: - phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; - rstcon |= RSTCON_SWRST; - default: - break; - } - - writel(phyclk, regs + SAMSUNG_PHYCLK); - /* Configure PHY0 for normal operation*/ - writel(phypwr, regs + SAMSUNG_PHYPWR); - /* reset all ports of PHY and Link */ - writel(rstcon, regs + SAMSUNG_RSTCON); - udelay(10); - rstcon &= ~RSTCON_SWRST; - writel(rstcon, regs + SAMSUNG_RSTCON); -} - -static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - - if (atomic_dec_return(&sphy->phy_usage) > 0) { - dev_info(sphy->dev, "still being used\n"); - return; - } - - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_SIDDQ | - HSIC_CTRL_FORCESLEEP | - HSIC_CTRL_FORCESUSPEND); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - phyhost |= (HOST_CTRL0_SIDDQ | - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP | - HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - phyotg |= (OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); -} - -static void samsung_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - - phypwr = readl(regs + SAMSUNG_PHYPWR); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phypwr |= PHYPWR_NORMAL_MASK; - break; - case TYPE_EXYNOS4210: - phypwr |= PHYPWR_NORMAL_MASK_PHY0; - default: - break; - } - - /* Disable analog and otg block power */ - writel(phypwr, regs + SAMSUNG_PHYPWR); -} - -/* - * The function passed to the usb driver for phy initialization - */ -static int samsung_usbphy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* Disable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(false); - else - samsung_usbphy_set_isolation(sphy, false); - - /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ - samsung_usbphy_cfg_sel(sphy); - - /* Initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_enable(sphy); - else - samsung_usbphy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usbphy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* De-initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_disable(sphy); - else - samsung_usbphy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(true); - else - samsung_usbphy_set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static const struct of_device_id samsung_usbphy_dt_match[]; - -static inline const struct samsung_usbphy_drvdata -*samsung_usbphy_get_driver_data(struct platform_device *pdev) -{ - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(samsung_usbphy_dt_match, - pdev->dev.of_node); - return match->data; - } - - return (struct samsung_usbphy_drvdata *) - platform_get_device_id(pdev)->driver_data; -} - -static int samsung_usbphy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct usb_otg *otg; - struct samsung_usbphy_data *pdata = pdev->dev.platform_data; - const struct samsung_usbphy_drvdata *drv_data; - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!phy_mem) { - dev_err(dev, "%s: missing mem resource\n", __func__); - return -ENODEV; - } - - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - drv_data = samsung_usbphy_get_driver_data(pdev); - - if (drv_data->cpu_type == TYPE_EXYNOS5250) - clk = devm_clk_get(dev, "usbhost"); - else - clk = devm_clk_get(dev, "otg"); - - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get otg clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->drv_data = drv_data; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usbphy"; - sphy->phy.init = samsung_usbphy_init; - sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - - sphy->phy.otg = otg; - sphy->phy.otg->phy = &sphy->phy; - sphy->phy.otg->set_host = samsung_usbphy_set_host; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); -} - -static int samsung_usbphy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { - .cpu_type = TYPE_S3C64XX, - .devphy_en_mask = S3C64XX_USBPHY_ENABLE, -}; - -static const struct samsung_usbphy_drvdata usbphy_exynos4 = { - .cpu_type = TYPE_EXYNOS4210, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, -}; - -static struct samsung_usbphy_drvdata usbphy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,s3c64xx-usbphy", - .data = &usbphy_s3c64xx, - }, { - .compatible = "samsung,exynos4210-usbphy", - .data = &usbphy_exynos4, - }, { - .compatible = "samsung,exynos5250-usbphy", - .data = &usbphy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "s3c64xx-usbphy", - .driver_data = (unsigned long)&usbphy_s3c64xx, - }, { - .name = "exynos4210-usbphy", - .driver_data = (unsigned long)&usbphy_exynos4, - }, { - .name = "exynos5250-usbphy", - .driver_data = (unsigned long)&usbphy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usbphy_driver = { - .probe = samsung_usbphy_probe, - .remove = samsung_usbphy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usbphy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usbphy_driver); - -MODULE_DESCRIPTION("Samsung USB phy controller"); -MODULE_AUTHOR("Praveen Paneri "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usbphy"); +EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h new file mode 100644 index 0000000..481737d7 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -0,0 +1,247 @@ +/* linux/drivers/usb/phy/phy-samsung-usb.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) + +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) +#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) +#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) + +#define FSEL_CLKSEL_50M (0x7) +#define FSEL_CLKSEL_24M (0x5) +#define FSEL_CLKSEL_20M (0x4) +#define FSEL_CLKSEL_19200K (0x3) +#define FSEL_CLKSEL_12M (0x2) +#define FSEL_CLKSEL_10M (0x1) +#define FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) +#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) +#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) +#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) +#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) +#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) +#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#ifndef KHZ +#define KHZ (1000) +#endif + +#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) +#define S3C64XX_USBPHY_ENABLE (0x1 << 16) +#define EXYNOS_USBPHY_ENABLE (0x1 << 0) +#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, + TYPE_EXYNOS5250, +}; + +/* + * struct samsung_usbphy_drvdata - driver data for various SoC variants + * @cpu_type: machine identifier + * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @hostphy_en_mask: host phy enable mask for PHY CONTROL register + * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from + * mapped address of system controller. + * @hostphy_reg_offset: offset to HOST PHY CONTROL register from + * mapped address of system controller. + * + * Here we have a separate mask for device type phy. + * Having different masks for host and device type phy helps + * in setting independent masks in case of SoCs like S5PV210, + * in which PHY0 and PHY1 enable bits belong to same register + * placed at position 0 and 1 respectively. + * Although for newer SoCs like exynos these bits belong to + * different registers altogether placed at position 0. + */ +struct samsung_usbphy_drvdata { + int cpu_type; + int devphy_en_mask; + int hostphy_en_mask; + u32 devphy_reg_offset; + u32 hostphy_reg_offset; +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy controller registers memory base + * @pmuregs: USB device PHY_CONTROL register memory base + * @sysreg: USB2.0 PHY_CFG register memory base + * @ref_clk_freq: reference clock frequency selection + * @drv_data: driver data available for different SoCs + * @phy_type: Samsung SoCs specific phy types: #HOST + * #DEVICE + * @phy_usage: usage count for phy + * @lock: lock for phy operations + */ +struct samsung_usbphy { + struct usb_phy phy; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + void __iomem *pmuregs; + void __iomem *sysreg; + int ref_clk_freq; + const struct samsung_usbphy_drvdata *drv_data; + enum samsung_usb_phy_type phy_type; + atomic_t phy_usage; + spinlock_t lock; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline const struct samsung_usbphy_drvdata +*samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + return match->data; + } + + return (struct samsung_usbphy_drvdata *) + platform_get_device_id(pdev)->driver_data; +} + +extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy); +extern void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on); +extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy); +extern int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type); +extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy); diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c new file mode 100644 index 0000000..dce9681 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -0,0 +1,509 @@ +/* linux/drivers/usb/phy/phy-samsung-usb2.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri + * + * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-usb.h" + +static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!otg->host) + otg->host = host; + + return 0; +} + +static bool exynos5_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + return !(reg & HOST_CTRL0_SIDDQ); +} + +static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + +static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* Configure PHY0 for normal operation*/ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + +static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* Disable analog and otg block power */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usb2phy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + int ret = 0; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false); + else + samsung_usbphy_set_isolation(sphy, false); + + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + samsung_usbphy_cfg_sel(sphy); + + /* Initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usb2phy_enable(sphy); + else + samsung_usb2phy_enable(sphy); + + spin_unlock_irqrestore(&sphy->lock, flags); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usb2phy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* De-initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usb2phy_disable(sphy); + else + samsung_usb2phy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true); + else + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); + + clk_disable_unprepare(sphy->clk); +} + +static int samsung_usb2phy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct usb_otg *otg; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + const struct samsung_usbphy_drvdata *drv_data; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + int ret; + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + drv_data = samsung_usbphy_get_driver_data(pdev); + + if (drv_data->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->drv_data = drv_data; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usb2phy"; + sphy->phy.init = samsung_usb2phy_init; + sphy->phy.shutdown = samsung_usb2phy_shutdown; + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + sphy->phy.otg = otg; + sphy->phy.otg->phy = &sphy->phy; + sphy->phy.otg->set_host = samsung_usbphy_set_host; + + spin_lock_init(&sphy->lock); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); +} + +static int samsung_usb2phy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); + + return 0; +} + +static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { + .cpu_type = TYPE_S3C64XX, + .devphy_en_mask = S3C64XX_USBPHY_ENABLE, +}; + +static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { + .cpu_type = TYPE_EXYNOS4210, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +static struct samsung_usbphy_drvdata usb2phy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usb2phy", + .data = &usb2phy_s3c64xx, + }, { + .compatible = "samsung,exynos4210-usb2phy", + .data = &usb2phy_exynos4, + }, { + .compatible = "samsung,exynos5250-usb2phy", + .data = &usb2phy_exynos5 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usb2phy", + .driver_data = (unsigned long)&usb2phy_s3c64xx, + }, { + .name = "exynos4210-usb2phy", + .driver_data = (unsigned long)&usb2phy_exynos4, + }, { + .name = "exynos5250-usb2phy", + .driver_data = (unsigned long)&usb2phy_exynos5, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usb2phy_driver = { + .probe = samsung_usb2phy_probe, + .remove = samsung_usb2phy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usb2phy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usb2phy_driver); + +MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); +MODULE_AUTHOR("Praveen Paneri "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usb2phy"); -- cgit v0.10.2 From b52767581765d3d1a1ba7106674791e540574704 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 15:59:11 +0530 Subject: usb: phy: samsung: Add PHY support for USB 3.0 controller Adding PHY driver support for USB 3.0 controller for Samsung's SoCs. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 96940ab..f575302 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -61,3 +61,57 @@ Example: reg = <0x10020704 0x8>; }; }; + + +** Samsung's usb 3.0 phy transceiver + +Starting exynso5250, Samsung's SoC have usb 3.0 phy transceiver +which is used for controlling usb 3.0 phy for dwc3-exynos usb 3.0 +controllers across Samsung SOCs. + +Required properties: + +Exynos5250: +- compatible : should be "samsung,exynos5250-usb3phy" +- reg : base physical address of the phy registers and length of memory mapped + region. +- clocks: Clock IDs array as required by the controller. +- clock-names: names of clocks correseponding to IDs in the clock property + as requested by the controller driver. + +Optional properties: +- #address-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- #size-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- ranges: allows valid translation between child's address space and parent's + address space. + +- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller + interface for usb-phy. It should provide the following information required by + usb-phy controller to control phy. + - reg : base physical address of PHY_CONTROL registers. + The size of this register is the total sum of size of all PHY_CONTROL + registers that the SoC has. For example, the size will be + '0x4' in case we have only one PHY_CONTROL register (e.g. + OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210) + and, '0x8' in case we have two PHY_CONTROL registers (e.g. + USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x). + and so on. + +Example: + usbphy@12100000 { + compatible = "samsung,exynos5250-usb3phy"; + reg = <0x12100000 0x100>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + clocks = <&clock 1>, <&clock 286>; + clock-names = "ext_xtal", "usbdrd30"; + + usbphy-sys { + /* USB device and host PHY_CONTROL registers */ + reg = <0x10040704 0x8>; + }; + }; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index e8cd52a..7e8fe0f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -99,6 +99,13 @@ config SAMSUNG_USB2PHY Enable this to support Samsung USB 2.0 (High Speed) PHY controller driver for Samsung SoCs. +config SAMSUNG_USB3PHY + tristate "Samsung USB 3.0 PHY controller Driver" + select SAMSUNG_USBPHY + help + Enable this to support Samsung USB 3.0 (Super Speed) phy controller + for samsung SoCs. + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 8cd355f..33863c0 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o +obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 481737d7..70a9cae 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -145,6 +145,86 @@ #define EXYNOS5_PHY_OTG_TUNE (0x40) +/* EXYNOS5: USB 3.0 DRD */ +#define EXYNOS5_DRD_LINKSYSTEM (0x04) + +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27) + +#define EXYNOS5_DRD_PHYUTMI (0x08) + +#define PHYUTMI_OTGDISABLE (0x1 << 6) +#define PHYUTMI_FORCESUSPEND (0x1 << 1) +#define PHYUTMI_FORCESLEEP (0x1 << 0) + +#define EXYNOS5_DRD_PHYPIPE (0x0c) + +#define EXYNOS5_DRD_PHYCLKRST (0x10) + +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) + +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) + +#define PHYCLKRST_SSC_EN (0x1 << 20) +#define PHYCLKRST_REF_SSP_EN (0x1 << 19) +#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18) + +#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) + +#define PHYCLKRST_FSEL_MASK (0x3f << 5) +#define PHYCLKRST_FSEL(_x) ((_x) << 5) +#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) +#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) +#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) +#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) + +#define PHYCLKRST_RETENABLEN (0x1 << 4) + +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) + +#define PHYCLKRST_PORTRESET (0x1 << 1) +#define PHYCLKRST_COMMONONN (0x1 << 0) + +#define EXYNOS5_DRD_PHYREG0 (0x14) +#define EXYNOS5_DRD_PHYREG1 (0x18) + +#define EXYNOS5_DRD_PHYPARAM0 (0x1c) + +#define PHYPARAM0_REF_USE_PAD (0x1 << 31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) + +#define EXYNOS5_DRD_PHYPARAM1 (0x20) + +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) + +#define EXYNOS5_DRD_PHYTERM (0x24) + +#define EXYNOS5_DRD_PHYTEST (0x28) + +#define PHYTEST_POWERDOWN_SSP (0x1 << 3) +#define PHYTEST_POWERDOWN_HSP (0x1 << 2) + +#define EXYNOS5_DRD_PHYADP (0x2c) + +#define EXYNOS5_DRD_PHYBATCHG (0x30) + +#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) + +#define EXYNOS5_DRD_PHYRESUME (0x34) +#define EXYNOS5_DRD_LINKPORT (0x44) + #ifndef MHZ #define MHZ (1000*1000) #endif diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c new file mode 100644 index 0000000..54f6418 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -0,0 +1,349 @@ +/* linux/drivers/usb/phy/phy-samsung-usb3.c + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Vivek Gautam + * + * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-usb.h" + +/* + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core. + */ +static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy) +{ + u32 reg; + u32 refclk; + + refclk = sphy->ref_clk_freq; + + reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | + PHYCLKRST_FSEL(refclk); + + switch (refclk) { + case FSEL_CLKSEL_50M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case FSEL_CLKSEL_20M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case FSEL_CLKSEL_19200K: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + case FSEL_CLKSEL_24M: + default: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + } + + return reg; +} + +static int samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyparam0; + u32 phyparam1; + u32 linksystem; + u32 phybatchg; + u32 phytest; + u32 phyclkrst; + + /* Reset USB 3.0 PHY */ + writel(0x0, regs + EXYNOS5_DRD_PHYREG0); + + phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0); + /* Select PHY CLK source */ + phyparam0 &= ~PHYPARAM0_REF_USE_PAD; + /* Set Loss-of-Signal Detector sensitivity */ + phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK; + phyparam0 |= PHYPARAM0_REF_LOSLEVEL; + writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0); + + writel(0x0, regs + EXYNOS5_DRD_PHYRESUME); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20); + writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM); + + phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + phyparam1 |= PHYPARAM1_PCS_TXDEEMPH; + writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1); + + phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG); + phybatchg |= PHYBATCHG_UTMI_CLKSEL; + writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG); + + /* PHYTEST POWERDOWN Control */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest &= ~(PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI); + + phyclkrst = samsung_usb3phy_set_refclk(sphy); + + phyclkrst |= PHYCLKRST_PORTRESET | + /* Digital power supply in normal operating mode */ + PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN; + + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + udelay(10); + + phyclkrst &= ~(PHYCLKRST_PORTRESET); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + return 0; +} + +static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy) +{ + u32 phyutmi; + u32 phyclkrst; + u32 phytest; + void __iomem *regs = sphy->regs; + + phyutmi = PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP; + writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI); + + /* Resetting the PHYCLKRST enable bits to reduce leakage current */ + phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST); + phyclkrst &= ~(PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + /* Control PHYTEST to remove leakage current */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest |= (PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); +} + +static int samsung_usb3phy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + unsigned long flags; + int ret = 0; + + sphy = phy_to_sphy(phy); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + spin_lock_irqsave(&sphy->lock, flags); + + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + + /* Disable phy isolation */ + samsung_usbphy_set_isolation(sphy, false); + + /* Initialize usb phy registers */ + samsung_exynos5_usb3phy_enable(sphy); + + spin_unlock_irqrestore(&sphy->lock, flags); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usb3phy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + unsigned long flags; + + sphy = phy_to_sphy(phy); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + spin_lock_irqsave(&sphy->lock, flags); + + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + + /* De-initialize usb phy registers */ + samsung_exynos5_usb3phy_disable(sphy); + + /* Enable phy isolation */ + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); + + clk_disable_unprepare(sphy->clk); +} + +static int samsung_usb3phy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + int ret; + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_request_and_ioremap(dev, phy_mem); + if (!phy_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + clk = devm_clk_get(dev, "usbdrd30"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get device clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usb3phy"; + sphy->phy.init = samsung_usb3phy_init; + sphy->phy.shutdown = samsung_usb3phy_shutdown; + sphy->drv_data = samsung_usbphy_get_driver_data(pdev); + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + spin_lock_init(&sphy->lock); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB3); +} + +static int samsung_usb3phy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); + + return 0; +} + +static struct samsung_usbphy_drvdata usb3phy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,exynos5250-usb3phy", + .data = &usb3phy_exynos5 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "exynos5250-usb3phy", + .driver_data = (unsigned long)&usb3phy_exynos5, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usb3phy_driver = { + .probe = samsung_usb3phy_probe, + .remove = samsung_usb3phy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usb3phy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usb3phy_driver); + +MODULE_DESCRIPTION("Samsung USB 3.0 phy controller"); +MODULE_AUTHOR("Vivek Gautam "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usb3phy"); -- cgit v0.10.2 From adcf20dcd2629112c467f30a2c057479979ae64c Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 18:09:49 +0530 Subject: usb: dwc3: exynos: Use of_platform API to create dwc3 core pdev Used of_platform_populate() to create dwc3 core platform_device from device tree data. Additionally some cleanup is also done. Signed-off-by: Vivek Gautam CC: Felipe Balbi CC: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index e12e452..f77ec75 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -22,9 +22,9 @@ #include #include #include +#include struct dwc3_exynos { - struct platform_device *dwc3; struct platform_device *usb2_phy; struct platform_device *usb3_phy; struct device *dev; @@ -86,21 +86,30 @@ err1: return ret; } +static int dwc3_exynos_remove_child(struct device *dev, void *unused) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + static u64 dwc3_exynos_dma_mask = DMA_BIT_MASK(32); static int dwc3_exynos_probe(struct platform_device *pdev) { - struct platform_device *dwc3; struct dwc3_exynos *exynos; struct clk *clk; struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; int ret = -ENOMEM; exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); if (!exynos) { dev_err(dev, "not enough memory\n"); - return -ENOMEM; + goto err1; } /* @@ -108,21 +117,15 @@ static int dwc3_exynos_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we move to full device tree support this will vanish off. */ - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &dwc3_exynos_dma_mask; + if (!dev->dma_mask) + dev->dma_mask = &dwc3_exynos_dma_mask; platform_set_drvdata(pdev, exynos); ret = dwc3_exynos_register_phys(exynos); if (ret) { dev_err(dev, "couldn't register PHYs\n"); - return ret; - } - - dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); - if (!dwc3) { - dev_err(dev, "couldn't allocate dwc3 device\n"); - return -ENOMEM; + goto err1; } clk = devm_clk_get(dev, "usbdrd30"); @@ -132,27 +135,20 @@ static int dwc3_exynos_probe(struct platform_device *pdev) goto err1; } - dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask); - - dwc3->dev.parent = dev; - dwc3->dev.dma_mask = dev->dma_mask; - dwc3->dev.dma_parms = dev->dma_parms; - exynos->dwc3 = dwc3; exynos->dev = dev; exynos->clk = clk; clk_enable(exynos->clk); - ret = platform_device_add_resources(dwc3, pdev->resource, - pdev->num_resources); - if (ret) { - dev_err(dev, "couldn't add resources to dwc3 device\n"); - goto err2; - } - - ret = platform_device_add(dwc3); - if (ret) { - dev_err(dev, "failed to register dwc3 device\n"); + if (node) { + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to add dwc3 core\n"); + goto err2; + } + } else { + dev_err(dev, "no device node, failed to add dwc3 core\n"); + ret = -ENODEV; goto err2; } @@ -161,8 +157,6 @@ static int dwc3_exynos_probe(struct platform_device *pdev) err2: clk_disable(clk); err1: - platform_device_put(dwc3); - return ret; } @@ -170,9 +164,9 @@ static int dwc3_exynos_remove(struct platform_device *pdev) { struct dwc3_exynos *exynos = platform_get_drvdata(pdev); - platform_device_unregister(exynos->dwc3); platform_device_unregister(exynos->usb2_phy); platform_device_unregister(exynos->usb3_phy); + device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child); clk_disable(exynos->clk); -- cgit v0.10.2 From ddb5147cea10308fac7d4ea44cbd164929199e03 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 16:14:58 +0530 Subject: usb: dwc3: exynos: use clk_prepare_enable and clk_disable_unprepare Convert clk_enable/clk_disable to clk_prepare_enable/clk_disable_unprepare calls as required by common clock framework. Signed-off-by: Vivek Gautam CC: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index f77ec75..1ea7bd8 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -138,7 +138,7 @@ static int dwc3_exynos_probe(struct platform_device *pdev) exynos->dev = dev; exynos->clk = clk; - clk_enable(exynos->clk); + clk_prepare_enable(exynos->clk); if (node) { ret = of_platform_populate(node, NULL, NULL, dev); @@ -155,7 +155,7 @@ static int dwc3_exynos_probe(struct platform_device *pdev) return 0; err2: - clk_disable(clk); + clk_disable_unprepare(clk); err1: return ret; } @@ -168,7 +168,7 @@ static int dwc3_exynos_remove(struct platform_device *pdev) platform_device_unregister(exynos->usb3_phy); device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child); - clk_disable(exynos->clk); + clk_disable_unprepare(exynos->clk); return 0; } -- cgit v0.10.2 From f9e612002fc50b3ae7cd1349eb2387e5430b44d9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:22 +0100 Subject: usb: gadget: uvc: clarify comment about string descriptors The comment that describes string descriptors allocation isn't clear, fix it. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 92efd6e..dd372ce 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -800,7 +800,10 @@ uvc_bind_config(struct usb_configuration *c, uvc->desc.hs_streaming = hs_streaming; uvc->desc.ss_streaming = ss_streaming; - /* Allocate string descriptor numbers. */ + /* String descriptors are global, we only need to allocate string IDs + * for the first UVC function. UVC functions beyond the first (if any) + * will reuse the same IDs. + */ if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) { ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); if (ret) -- cgit v0.10.2 From 912ca429fc87ceb63ae9ae00eff08212aad890c5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:23 +0100 Subject: usb: gadget: uvc: Rename STATUS_BYTECOUNT to UVC_STATUS_MAX_PACKET_SIZE Descriptive names make the code more readable. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index dd372ce..1851490 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -79,7 +79,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_INTF_VIDEO_CONTROL 0 #define UVC_INTF_VIDEO_STREAMING 1 -#define STATUS_BYTECOUNT 16 /* 16 bytes status */ +#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ static struct usb_interface_assoc_descriptor uvc_iad __initdata = { .bLength = sizeof(uvc_iad), @@ -109,7 +109,7 @@ static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), .bInterval = 8, }; @@ -117,7 +117,7 @@ static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubType = UVC_EP_INTERRUPT, - .wMaxTransferSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { @@ -169,7 +169,7 @@ static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), .bInterval = 8, }; @@ -180,7 +180,7 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { /* the following 3 values can be tweaked if necessary */ /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ - .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), + .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { -- cgit v0.10.2 From ee6a4d870b722a57aa57abe7f12539bac9c01555 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:24 +0100 Subject: usb: gadget: uvc: Fix coding style issues introduced by SS support Let's keep the code consistent, people might want to read it. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 1851490..c13b8b0 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -164,43 +164,43 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { /* super speed support */ static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), - .bInterval = 8, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), + .bInterval = 8, }; static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { - .bLength = sizeof uvc_ss_control_comp, - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bLength = sizeof(uvc_ss_control_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* the following 3 values can be tweaked if necessary */ - /* .bMaxBurst = 0, */ - /* .bmAttributes = 0, */ - .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = 4, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, }; static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { - .bLength = sizeof uvc_ss_streaming_comp, - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bLength = sizeof(uvc_ss_streaming_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* the following 3 values can be tweaked if necessary */ - .bMaxBurst = 0, - .bmAttributes = 0, - .wBytesPerInterval = cpu_to_le16(1024), + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), }; static const struct usb_descriptor_header * const uvc_fs_streaming[] = { @@ -514,13 +514,13 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) } for (src = (const struct usb_descriptor_header **)uvc_control_desc; - *src; ++src) { + *src; ++src) { control_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; } for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; - *src; ++src) { + *src; ++src) { streaming_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; @@ -775,23 +775,23 @@ uvc_bind_config(struct usb_configuration *c, /* Validate the descriptors. */ if (fs_control == NULL || fs_control[0] == NULL || - fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) + fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (ss_control == NULL || ss_control[0] == NULL || - ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) + ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (ss_streaming == NULL || ss_streaming[0] == NULL || - ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; uvc->desc.fs_control = fs_control; diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index c3d258d..ec52752 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -16,12 +16,12 @@ #include #include -extern int uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *hs_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming); +int uvc_bind_config(struct usb_configuration *c, + const struct uvc_descriptor_header * const *fs_control, + const struct uvc_descriptor_header * const *hs_control, + const struct uvc_descriptor_header * const *fs_streaming, + const struct uvc_descriptor_header * const *hs_streaming, + const struct uvc_descriptor_header * const *ss_streaming); #endif /* _F_UVC_H_ */ -- cgit v0.10.2 From 48eee0b41a1c6e979e4b47d75bb3f2493c1b5fb9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:25 +0100 Subject: usb: gadget: uvc: Merge the SS/HS/FS control endpoint descriptors The descriptors are identical, there's no need to have several copies of them. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index c13b8b0..8e4827c 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -104,7 +104,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = { +static struct usb_endpoint_descriptor uvc_control_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -113,6 +113,15 @@ static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = { .bInterval = 8, }; +static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { + .bLength = sizeof(uvc_ss_control_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + /* The following 3 values can be tweaked if necessary. */ + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), +}; + static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, @@ -144,7 +153,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { +static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -153,7 +162,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { .bInterval = 1, }; -static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { +static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -162,27 +171,6 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { .bInterval = 1, }; -/* super speed support */ -static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), - .bInterval = 8, -}; - -static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { - .bLength = sizeof(uvc_ss_control_comp), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - - /* the following 3 values can be tweaked if necessary */ - .bMaxBurst = 0, - .bmAttributes = 0, - .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), -}; - static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -193,11 +181,10 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { .bInterval = 4, }; -static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { +static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - - /* the following 3 values can be tweaked if necessary */ + /* The following 3 values can be tweaked if necessary. */ .bMaxBurst = 0, .bmAttributes = 0, .wBytesPerInterval = cpu_to_le16(1024), @@ -454,7 +441,6 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) const struct uvc_descriptor_header * const *uvc_streaming_cls; const struct usb_descriptor_header * const *uvc_streaming_std; const struct usb_descriptor_header * const *src; - static struct usb_endpoint_descriptor *uvc_control_ep; struct usb_descriptor_header **dst; struct usb_descriptor_header **hdr; unsigned int control_size; @@ -468,14 +454,12 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_desc = uvc->desc.ss_control; uvc_streaming_cls = uvc->desc.ss_streaming; uvc_streaming_std = uvc_ss_streaming; - uvc_control_ep = &uvc_ss_control_ep; break; case USB_SPEED_HIGH: uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.hs_streaming; uvc_streaming_std = uvc_hs_streaming; - uvc_control_ep = &uvc_fs_control_ep; break; case USB_SPEED_FULL: @@ -483,7 +467,6 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.fs_streaming; uvc_streaming_std = uvc_fs_streaming; - uvc_control_ep = &uvc_fs_control_ep; break; } @@ -494,6 +477,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) * Class-specific UVC control descriptors * uvc_control_ep * uvc_control_cs_ep + * uvc_ss_control_comp (for SS only) * uvc_streaming_intf_alt0 * Class-specific UVC streaming descriptors * uvc_{fs|hs}_streaming @@ -503,7 +487,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) control_size = 0; streaming_size = 0; bytes = uvc_iad.bLength + uvc_control_intf.bLength - + uvc_control_ep->bLength + uvc_control_cs_ep.bLength + + uvc_control_ep.bLength + uvc_control_cs_ep.bLength + uvc_streaming_intf_alt0.bLength; if (speed == USB_SPEED_SUPER) { @@ -549,7 +533,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_header->bInCollection = 1; uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; - UVC_COPY_DESCRIPTOR(mem, dst, uvc_control_ep); + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep); if (speed == USB_SPEED_SUPER) UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); @@ -619,7 +603,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_fs_streaming_ep.bInterval = streaming_interval; /* Allocate endpoints. */ - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_control_ep); + ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); if (!ep) { INFO(cdev, "Unable to allocate control EP\n"); goto error; -- cgit v0.10.2 From 20777dde026eb4b915ce577f830231c00c3f9292 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:26 +0100 Subject: usb: gadget: uvc: Merge the streaming maxpacket and mult parameters Compute the multiplier from the maximum packet size based on the speed. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 8e4827c..7189dbe 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -33,19 +33,15 @@ unsigned int uvc_gadget_trace_param; /*-------------------------------------------------------------------------*/ /* module parameters specific to the Video streaming endpoint */ -static unsigned streaming_interval = 1; +static unsigned int streaming_interval = 1; module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(streaming_interval, "1 - 16"); -static unsigned streaming_maxpacket = 1024; +static unsigned int streaming_maxpacket = 1024; module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); -static unsigned streaming_mult; -module_param(streaming_mult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_mult, "0 - 2 (hs/ss only)"); - -static unsigned streaming_maxburst; +static unsigned int streaming_maxburst; module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); @@ -158,8 +154,11 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 1, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ + .wMaxPacketSize = 0, + .bInterval = 0, }; static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { @@ -167,8 +166,11 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = 1, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ + .wMaxPacketSize = 0, + .bInterval = 0, }; static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { @@ -177,8 +179,11 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = 4, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ + .wMaxPacketSize = 0, + .bInterval = 0, }; static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { @@ -579,29 +584,50 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); + unsigned int max_packet_mult; + unsigned int max_packet_size; struct usb_ep *ep; int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); - /* sanity check the streaming endpoint module parameters */ - if (streaming_interval < 1) - streaming_interval = 1; - if (streaming_interval > 16) - streaming_interval = 16; - if (streaming_mult > 2) - streaming_mult = 2; - if (streaming_maxburst > 15) - streaming_maxburst = 15; - - /* - * fill in the FS video streaming specific descriptors from the - * module parameters + /* Sanity check the streaming endpoint module parameters. + */ + streaming_interval = clamp(streaming_interval, 1U, 16U); + streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U); + streaming_maxburst = min(streaming_maxburst, 15U); + + /* Fill in the FS/HS/SS Video Streaming specific descriptors from the + * module parameters. + * + * NOTE: We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. */ - uvc_fs_streaming_ep.wMaxPacketSize = streaming_maxpacket > 1023 ? - 1023 : streaming_maxpacket; + if (streaming_maxpacket <= 1024) { + max_packet_mult = 1; + max_packet_size = streaming_maxpacket; + } else if (streaming_maxpacket <= 2048) { + max_packet_mult = 2; + max_packet_size = streaming_maxpacket / 2; + } else { + max_packet_mult = 3; + max_packet_size = streaming_maxpacket / 3; + } + + uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U); uvc_fs_streaming_ep.bInterval = streaming_interval; + uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); + uvc_hs_streaming_ep.bInterval = streaming_interval; + + uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_ss_streaming_ep.bInterval = streaming_interval; + uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; + uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + max_packet_size * max_packet_mult * streaming_maxburst; + /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); if (!ep) { @@ -619,6 +645,11 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->video.ep = ep; ep->driver_data = uvc; + uvc_hs_streaming_ep.bEndpointAddress = + uvc_fs_streaming_ep.bEndpointAddress; + uvc_ss_streaming_ep.bEndpointAddress = + uvc_fs_streaming_ep.bEndpointAddress; + /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) goto error; @@ -632,37 +663,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; - /* sanity check the streaming endpoint module parameters */ - if (streaming_maxpacket > 1024) - streaming_maxpacket = 1024; - /* - * Fill in the HS descriptors from the module parameters for the Video - * Streaming endpoint. - * NOTE: We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. - */ - uvc_hs_streaming_ep.wMaxPacketSize = streaming_maxpacket; - uvc_hs_streaming_ep.wMaxPacketSize |= streaming_mult << 11; - uvc_hs_streaming_ep.bInterval = streaming_interval; - uvc_hs_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; - - /* - * Fill in the SS descriptors from the module parameters for the Video - * Streaming endpoint. - * NOTE: We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. - */ - uvc_ss_streaming_ep.wMaxPacketSize = streaming_maxpacket; - uvc_ss_streaming_ep.bInterval = streaming_interval; - uvc_ss_streaming_comp.bmAttributes = streaming_mult; - uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; - uvc_ss_streaming_comp.wBytesPerInterval = - streaming_maxpacket * (streaming_mult + 1) * - (streaming_maxburst + 1); - uvc_ss_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; - /* Copy descriptors */ f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); if (gadget_is_dualspeed(cdev->gadget)) -- cgit v0.10.2 From 0485ec0d3ba9ce96ab5064b05a06e672c9d2c973 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 1 Mar 2013 20:46:27 +0100 Subject: usb: gadget: uvc: Configure the streaming endpoint based on the speed Call the appropriate usb_ep_autoconf*() function depending on the device speed, and pass it the corresponding streaming endpoint. Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 7189dbe..87b5306 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -549,8 +549,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) UVC_COPY_DESCRIPTORS(mem, dst, (const struct usb_descriptor_header**)uvc_streaming_cls); uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size); - uvc_streaming_header->bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; + uvc_streaming_header->bEndpointAddress = uvc->video.ep->address; UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std); @@ -637,7 +636,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->control_ep = ep; ep->driver_data = uvc; - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + if (gadget_is_superspeed(c->cdev->gadget)) + ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, + &uvc_ss_streaming_comp); + else if (gadget_is_dualspeed(cdev->gadget)) + ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); + else + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + if (!ep) { INFO(cdev, "Unable to allocate streaming EP\n"); goto error; @@ -645,10 +651,9 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->video.ep = ep; ep->driver_data = uvc; - uvc_hs_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; - uvc_ss_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; + uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; + uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; + uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) -- cgit v0.10.2 From 609a0532a4d713819092a9311ffe89faa6bac617 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Mar 2013 20:46:28 +0100 Subject: usb: gadget: uvc: Add fix for UVC compliance test suite assertion 6.3.90 failure As per UVC compliance test specification's assertion number 6.3.90 related to 'Standard VS Isochronous Video Data Endpoint Descriptor Assertions', the bits D3..2 of 'bmAttributes' field of Standard VS Isochronous Video Data Endpoint Descriptor should be 01 (binary) to indicate that the synchronization type is ASYNCHRONOUS. This mandatory requirement has been captured in section 3.10.1.1 of the UVC Video Class Specification version 1.1 This patch adds a fix for the same. Signed-off-by: Bhupesh Sharma Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 87b5306..76ec10f 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -153,7 +153,8 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ @@ -165,7 +166,8 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ @@ -178,7 +180,8 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ -- cgit v0.10.2 From 43ff05e20c6e2428fe2deb1dc0fed008743e66a3 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Mar 2013 20:46:29 +0100 Subject: usb: gadget: uvc: Add fix for UVC compliance test suite's assertion 6.1.25 failure As per the UVC compliance test suite's assertion 6.1.25, the `iFunction` field of the Interface Association Descriptor (IAD) should the match the `iInterface` field of the standard Video Control (VC) Interface Descriptor for this Video Interface Collection (VIC). This mandatory case is captured in section 3.11 of the USB Video Class Compliance specification revision 1.1 This patch fixes this test assertion's failure and has been tested on Linux FC16, WinXP, WIN7 and WIN8 High speed and Super Speed hosts for successful enumeration. Signed-off-by: Bhupesh Sharma [Merged the association and control string descriptors] Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 76ec10f..49939e4 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -51,13 +51,11 @@ MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); /* string IDs are assigned dynamically */ -#define UVC_STRING_ASSOCIATION_IDX 0 -#define UVC_STRING_CONTROL_IDX 1 -#define UVC_STRING_STREAMING_IDX 2 +#define UVC_STRING_CONTROL_IDX 0 +#define UVC_STRING_STREAMING_IDX 1 static struct usb_string uvc_en_us_strings[] = { - [UVC_STRING_ASSOCIATION_IDX].s = "UVC Camera", - [UVC_STRING_CONTROL_IDX].s = "Video Control", + [UVC_STRING_CONTROL_IDX].s = "UVC Camera", [UVC_STRING_STREAMING_IDX].s = "Video Streaming", { } }; @@ -572,7 +570,7 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) uvc->control_ep->driver_data = NULL; uvc->video.ep->driver_data = NULL; - uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = 0; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -796,12 +794,12 @@ uvc_bind_config(struct usb_configuration *c, * for the first UVC function. UVC functions beyond the first (if any) * will reuse the same IDs. */ - if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) { + if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); if (ret) goto error; uvc_iad.iFunction = - uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; uvc_control_intf.iInterface = uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; -- cgit v0.10.2 From 41837c352fd8d804dbe978c29e57ec8217df1d51 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Mar 2013 20:46:30 +0100 Subject: usb: gadget: uvc: Delay the status stage when setting alternate setting 1 This patch adds the support in UVC webcam gadget design for providing USB_GADGET_DELAYED_STATUS in response to a set_interface(alt setting 1) command issue by the Host. The current UVC webcam gadget design generates a STREAMON event corresponding to a set_interface(alt setting 1) command from the Host. This STREAMON event will eventually be routed to a real V4L2 device. To start video streaming, it may be required to perform some register writes to a camera sensor device over slow external busses like I2C or SPI. So, it makes sense to ensure that we delay the STATUS stage of the set_interface (alt setting 1) command. Otherwise, a lot of ISOC IN tokens sent by the Host will be replied to by zero-length packets by the webcam device. On certain Hosts this may even lead to ISOC URBs been cancelled from the Host side. So, as soon as we finish doing all the "streaming" related stuff on the real V4L2 device, we call a STREAMON ioctl on the UVC side and from here we call the 'usb_composite_setup_continue' function to complete the status stage of the set_interface(alt setting 1) command. Further, we need to ensure that we queue no video buffers on the UVC webcam gadget, until we de-queue a video buffer from the V4L2 device. So, the application should call the STREAMON on UVC side only when it has dequeued sufficient buffers from the V4L2 side and queued them to the UVC gadget. Signed-off-by: Bhupesh Sharma Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 49939e4..38dcedd 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -266,6 +266,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } +void uvc_function_setup_continue(struct uvc_device *uvc) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + + usb_composite_setup_continue(cdev); +} + static int uvc_function_get_alt(struct usb_function *f, unsigned interface) { @@ -328,7 +335,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_CONNECTED; - break; + return 0; case 1: if (uvc->state != UVC_STATE_CONNECTED) @@ -345,15 +352,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(uvc->vdev, &v4l2_event); - - uvc->state = UVC_STATE_STREAMING; - break; + return USB_GADGET_DELAYED_STATUS; default: return -EINVAL; } - - return 0; } static void diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 7e90b1d..817e9e1 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -188,6 +188,7 @@ struct uvc_file_handle * Functions */ +extern void uvc_function_setup_continue(struct uvc_device *uvc); extern void uvc_endpoint_stream(struct uvc_device *dev); extern void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 0080d07..2bb5af8 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -253,7 +253,19 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (*type != video->queue.type) return -EINVAL; - return uvc_video_enable(video, 1); + /* Enable UVC video. */ + ret = uvc_video_enable(video, 1); + if (ret < 0) + return ret; + + /* + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ + uvc_function_setup_continue(uvc); + uvc->state = UVC_STATE_STREAMING; + + return 0; } case VIDIOC_STREAMOFF: -- cgit v0.10.2 From 326b0e613bc858434198120a17d34308f82c27a8 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Fri, 1 Mar 2013 20:46:31 +0100 Subject: usb: gadget: uvc: Make video streaming buffer size comply with USB3.0 SS As per the USB3.0 specs, the bandwidth requirements of a UVC's video streaming endpoint will change to support super-speed. These changes will be dependent on whether the UVC video streaming endpoint is Bulk or Isochronous: - If video streaming endpoint is Isochronous: As per Section 4.4.8.2 (Isochronous Transfer Bandwidth Requirements) of the USB3.0 specs: A SuperSpeed isochronous endpoint can move up to three burst transactions of up to 16 maximum sized packets (3 * 16 * 1024 bytes) per service interval. - If video streaming endpoint is Bulk: As per 4.4.6.1 (Bulk Transfer Data Packet Size) of the USB3.0 specs: An endpoint for bulk transfers shall set the maximum data packet payload size in its endpoint descriptor to 1024 bytes. It also specifies the burst size that the endpoint can accept from or transmit on the SuperSpeed bus. The allowable burst size for a bulk endpoint shall be in the range of 1 to 16. So, in the Isochronous case, we can define the USB request's buffer to be equal to = (Maximum packet size) * (bMaxBurst + 1) * (Mult + 1), so that the UDC driver can try to send out this buffer in one Isochronous service interval. The same computation will hold good for the Bulk case as the Mult value is 0 here and we can have a USB request buffer of maximum 16 * 1024 bytes size, which can be sent out by the UDC driver as per the Bulk bandwidth allocation on the USB3 bus. This patch adds the above-mentioned support and is also USB2.0 backward compliant. Signed-off-by: Bhupesh Sharma Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index 885c393..fac99a9 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -229,13 +229,18 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + unsigned int req_size; unsigned int i; int ret = -ENOMEM; BUG_ON(video->req_size); + req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult + 1); + for (i = 0; i < UVC_NUM_REQUESTS; ++i) { - video->req_buffer[i] = kmalloc(video->ep->maxpacket, GFP_KERNEL); + video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); if (video->req_buffer[i] == NULL) goto error; @@ -251,7 +256,8 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&video->req[i]->list, &video->req_free); } - video->req_size = video->ep->maxpacket; + video->req_size = req_size; + return 0; error: -- cgit v0.10.2 From 6854bcdc6ff92e3a9c24940a3c5ebb446950c974 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Fri, 1 Mar 2013 20:46:32 +0100 Subject: usb: gadget: uvc: Use GFP_ATOMIC under spin lock Found using the following semantic patch: @@ @@ spin_lock_irqsave(...); ... when != spin_unlock_irqrestore(...); * GFP_KERNEL Signed-off-by: Cyril Roelandt Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index fac99a9..ec4bcc4 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -314,7 +314,8 @@ uvc_video_pump(struct uvc_video *video) video->encode(req, video, buf); /* Queue the USB request */ - if ((ret = usb_ep_queue(video->ep, req, GFP_KERNEL)) < 0) { + ret = usb_ep_queue(video->ep, req, GFP_ATOMIC); + if (ret < 0) { printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); -- cgit v0.10.2 From c3ec830d8925d904f8826d52227d7dfb5dee922c Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Fri, 1 Mar 2013 20:46:33 +0100 Subject: usb: gadget: uvc: Use strlcpy instead of strncpy For NULL terminated string, better notice '\0' in the end. Signed-off-by: Chen Gang Signed-off-by: Laurent Pinchart Tested-by: Bhupesh Sharma Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 2bb5af8..a6c728a 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -177,9 +177,9 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_capability *cap = arg; memset(cap, 0, sizeof *cap); - strncpy(cap->driver, "g_uvc", sizeof(cap->driver)); - strncpy(cap->card, cdev->gadget->name, sizeof(cap->card)); - strncpy(cap->bus_info, dev_name(&cdev->gadget->dev), + strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); + strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; -- cgit v0.10.2 From eee44da0453cfe9125f4297e4244fe1d6fb1c653 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 7 Mar 2013 18:51:46 +0530 Subject: usb: musb: omap2430: replace *_* with *-* in property names No functional change. Replace *_* with *-* in property names of otg to follow the general convention. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 1b9f55f..662f0f1 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -8,10 +8,10 @@ OMAP MUSB GLUE and disconnect. - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - - num_eps : Specifies the number of endpoints. This is also a + - num-eps : Specifies the number of endpoints. This is also a MUSB configuration-specific setting. Should be set to "16" - - ram_bits : Specifies the ram address size. Should be set to "12" - - interface_type : This is a board specific setting to describe the type of + - ram-bits : Specifies the ram address size. Should be set to "12" + - interface-type : This is a board specific setting to describe the type of interface between the controller and the phy. It should be "0" or "1" specifying ULPI and UTMI respectively. - mode : Should be "3" to represent OTG. "1" signifies HOST and "2" @@ -29,14 +29,14 @@ usb_otg_hs: usb_otg_hs@4a0ab000 { ti,hwmods = "usb_otg_hs"; ti,has-mailbox; multipoint = <1>; - num_eps = <16>; - ram_bits = <12>; + num-eps = <16>; + ram-bits = <12>; ctrl-module = <&omap_control_usb>; }; Board specific device node entry &usb_otg_hs { - interface_type = <1>; + interface-type = <1>; mode = <3>; power = <50>; }; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 8ba9bb2..e7b5eae 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -526,10 +526,10 @@ static int omap2430_probe(struct platform_device *pdev) } of_property_read_u32(np, "mode", (u32 *)&pdata->mode); - of_property_read_u32(np, "interface_type", + of_property_read_u32(np, "interface-type", (u32 *)&data->interface_type); - of_property_read_u32(np, "num_eps", (u32 *)&config->num_eps); - of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits); + of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); + of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); of_property_read_u32(np, "power", (u32 *)&pdata->power); config->multipoint = of_property_read_bool(np, "multipoint"); pdata->has_mailbox = of_property_read_bool(np, -- cgit v0.10.2 From a33bb2120851407b5703343596d5c2181cfc75b4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 16:00:58 +0200 Subject: usb: dwc3: omap: fix sparse warning our global '_omap' pointer wasn't marked static. This patch solves the following sparse warning: warning: symbol '_omap' was not declared. \ Should it be static? Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 2fe9723f..6de734f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -126,7 +126,7 @@ struct dwc3_omap { u32 dma_status:1; }; -struct dwc3_omap *_omap; +static struct dwc3_omap *_omap; static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset) { -- cgit v0.10.2 From a5eaaa1f33e771fa1651a4a7652b8a5f9fa7f6c1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 14 Mar 2013 11:01:05 +0300 Subject: usb: gadget: uvc: use capped length value "req->length" is a capped version of "data->length". Signed-off-by: Dan Carpenter Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index a6c728a..bb140dd 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -42,7 +42,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) req->length = min_t(unsigned int, uvc->event_length, data->length); req->zero = data->length < uvc->event_length; - memcpy(req->buf, data->data, data->length); + memcpy(req->buf, data->data, req->length); return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL); } -- cgit v0.10.2 From bb467cf5693b59c76c22b73dd383920a87f37b16 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 14 Mar 2013 11:53:56 +0530 Subject: usb: musb: omap: remove the check before calling otg_set_vbus No functional change. otg_set_vbus is already protected so removed the check before calling otg_set_vbus. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e7b5eae..018373d 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -174,8 +174,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) } } - if (otg->set_vbus) - otg_set_vbus(otg, 1); + otg_set_vbus(otg, 1); } else { musb->is_active = 1; otg->default_a = 1; @@ -296,10 +295,9 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) pm_runtime_put_autosuspend(dev); } - if (data->interface_type == MUSB_INTERFACE_UTMI) { - if (musb->xceiv->otg->set_vbus) - otg_set_vbus(musb->xceiv->otg, 0); - } + if (data->interface_type == MUSB_INTERFACE_UTMI) + otg_set_vbus(musb->xceiv->otg, 0); + omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DISCONNECT); break; -- cgit v0.10.2 From 3bf6db9bbe4ad7b08b714c1857a703c1ef1b1e83 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 14 Mar 2013 11:53:58 +0530 Subject: usb: musb: omap: add usb_phy_init in omap2430_musb_init Some PHYs load too early (twl4030) making omap glue to miss cable connect events if the board is booted with cable connected. So adding usb_phy_init in omap2430_musb_init lets PHYs to report events once glue is ready. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 018373d..ec460ea 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -391,6 +391,8 @@ static int omap2430_musb_init(struct musb *musb) if (glue->status != OMAP_MUSB_UNKNOWN) omap_musb_set_mailbox(glue); + usb_phy_init(musb->xceiv); + pm_runtime_put_noidle(musb->controller); return 0; -- cgit v0.10.2 From b7fa5c2aec5be083eb2719b405089703608e9bc6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 17:59:06 +0200 Subject: usb: phy: return -ENXIO when PHY layer isn't enabled in cases where PHY layer isn't enabled, we want to still return an error code (actually an error pointer) so that our users don't need to cope with either error pointer of NULL. This will simplify users as below: - return IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; + return PTR_ERR(phy); Acked-by: Kishon Vijay Abraham I Reported-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index b7c2217..6b5978f 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -197,29 +197,29 @@ extern int usb_bind_phy(const char *dev_name, u8 index, #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, const char *phandle, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline void usb_put_phy(struct usb_phy *x) -- cgit v0.10.2 From 9166902c435b4847c1987188294407b24e76d9e6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 15 Mar 2013 18:58:51 +0530 Subject: usb: phy: twl4030: use devres API for regulator get and request irq Used devres APIs devm_request_threaded_irq and devm_regulator_get for requesting irq and for getting regulator respectively. Signed-off-by: Kishon Vijay Abraham I Tested-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index a994715..e14b03e 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -432,7 +432,7 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl) /* Initialize 3.1V regulator */ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); - twl->usb3v1 = regulator_get(twl->dev, "usb3v1"); + twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); if (IS_ERR(twl->usb3v1)) return -ENODEV; @@ -441,18 +441,18 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl) /* Initialize 1.5V regulator */ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); - twl->usb1v5 = regulator_get(twl->dev, "usb1v5"); + twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); if (IS_ERR(twl->usb1v5)) - goto fail1; + return -ENODEV; twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); /* Initialize 1.8V regulator */ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); - twl->usb1v8 = regulator_get(twl->dev, "usb1v8"); + twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); if (IS_ERR(twl->usb1v8)) - goto fail2; + return -ENODEV; twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); @@ -461,14 +461,6 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl) TWL4030_PM_MASTER_PROTECT_KEY); return 0; - -fail2: - regulator_put(twl->usb1v5); - twl->usb1v5 = NULL; -fail1: - regulator_put(twl->usb3v1); - twl->usb3v1 = NULL; - return -ENODEV; } static ssize_t twl4030_usb_vbus_show(struct device *dev, @@ -640,9 +632,9 @@ static int twl4030_usb_probe(struct platform_device *pdev) * need both handles, otherwise just one suffices. */ twl->irq_enabled = true; - status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT, "twl4030_usb", twl); + status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, + twl4030_usb_irq, IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); if (status < 0) { dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq, status); @@ -663,7 +655,6 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) struct twl4030_usb *twl = platform_get_drvdata(pdev); int val; - free_irq(twl->irq, twl); device_remove_file(twl->dev, &dev_attr_vbus); /* set transceiver mode to power on defaults */ @@ -685,9 +676,6 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) if (!twl->asleep) twl4030_phy_power(twl, 0); - regulator_put(twl->usb1v5); - regulator_put(twl->usb1v8); - regulator_put(twl->usb3v1); return 0; } -- cgit v0.10.2 From 817e5f33d0c12f24bdfebe88c96ca2e968756da4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 15 Mar 2013 18:58:52 +0530 Subject: usb: phy: twl4030: fix cold plug on OMAP3 Having twl4030_usb_phy_init() (detects if a cable is connected before twl4030 is probed) in twl4030 probe makes cable connect events to be missed by musb glue, since it gets loaded after twl4030. Having twl4030_usb_phy_init as a usb_phy ops lets twl4030_usb_phy_init to be called when glue is ready. Signed-off-by: Kishon Vijay Abraham I Tested-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index e14b03e..1986c78 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -510,8 +510,9 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) return IRQ_HANDLED; } -static void twl4030_usb_phy_init(struct twl4030_usb *twl) +static int twl4030_usb_phy_init(struct usb_phy *phy) { + struct twl4030_usb *twl = phy_to_twl(phy); enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); @@ -528,6 +529,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + return 0; } static int twl4030_set_suspend(struct usb_phy *x, int suspend) @@ -604,6 +606,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) twl->phy.otg = otg; twl->phy.type = USB_PHY_TYPE_USB2; twl->phy.set_suspend = twl4030_set_suspend; + twl->phy.init = twl4030_usb_phy_init; otg->phy = &twl->phy; otg->set_host = twl4030_set_host; @@ -641,11 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) return status; } - /* Power down phy or make it work according to - * current link state. - */ - twl4030_usb_phy_init(twl); - dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); return 0; } -- cgit v0.10.2 From d105e7f86f890a530cdefc2a715121345de30dc2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 10:52:08 +0200 Subject: usb: dwc3: fix PHY error handling PHY layer no longer returns NULL. It will return -ENXIO when PHY layer isn't enabled and we can use that to bail out instead of request a probe deferral. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c845e70..e2325ad 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -434,12 +434,32 @@ static int dwc3_probe(struct platform_device *pdev) dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); } - if (IS_ERR_OR_NULL(dwc->usb2_phy)) { + if (IS_ERR(dwc->usb2_phy)) { + ret = PTR_ERR(dwc->usb2_phy); + + /* + * if -ENXIO is returned, it means PHY layer wasn't + * enabled, so it makes no sense to return -EPROBE_DEFER + * in that case, since no PHY driver will ever probe. + */ + if (ret == -ENXIO) + return ret; + dev_err(dev, "no usb2 phy configured\n"); return -EPROBE_DEFER; } - if (IS_ERR_OR_NULL(dwc->usb3_phy)) { + if (IS_ERR(dwc->usb3_phy)) { + ret = PTR_ERR(dwc->usb2_phy); + + /* + * if -ENXIO is returned, it means PHY layer wasn't + * enabled, so it makes no sense to return -EPROBE_DEFER + * in that case, since no PHY driver will ever probe. + */ + if (ret == -ENXIO) + return ret; + dev_err(dev, "no usb3 phy configured\n"); return -EPROBE_DEFER; } -- cgit v0.10.2 From 4dbb71612505de1d3d69d011199554f86273c5e9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 10:54:59 +0200 Subject: usb: gadget: mv_udc_core: fix PHY error handling PHY layer no longer returns NULL. It will return -ENXIO when PHY layer isn't enabled and we can use that to bail out instead of request a probe deferral. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index d550b21..9a68c05 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2127,16 +2127,19 @@ static int mv_udc_probe(struct platform_device *pdev) udc->dev = pdev; -#if IS_ENABLED(CONFIG_USB_PHY) if (pdata->mode == MV_USB_MODE_OTG) { udc->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(udc->transceiver)) { + if (IS_ERR(udc->transceiver)) { + retval = PTR_ERR(udc->transceiver); + + if (retval == -ENXIO) + return retval; + udc->transceiver = NULL; - return -ENODEV; + return -EPROBE_DEFER; } } -#endif udc->clknum = pdata->clknum; for (clk_i = 0; clk_i < udc->clknum; clk_i++) { -- cgit v0.10.2 From f4f5ba5e7d9e087f044fe87f5a5421761274aa48 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 10:56:19 +0200 Subject: usb: gadget: s3c-hsotg: fix PHY error handling PHY laye rno longer return NULL. We need to switch over from IS_ERR_OR_NULL() to IS_ERR(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index f1ceabf..a3cdc32 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3467,7 +3467,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev) } phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(phy)) { + if (IS_ERR(phy)) { /* Fallback for pdata */ plat = pdev->dev.platform_data; if (!plat) { -- cgit v0.10.2 From a90199bb947856c24d7bf78845d24f802e09db0a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 10:57:40 +0200 Subject: usb: musb: omap2430: fix PHY error handling PHY layer no longer returns NULL. It will return -ENXIO when PHY layer isn't enabled and we can use that to bail out instead of request a probe deferral. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index ec460ea..798e029 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -353,7 +353,12 @@ static int omap2430_musb_init(struct musb *musb) else musb->xceiv = devm_usb_get_phy_dev(dev, 0); - if (IS_ERR_OR_NULL(musb->xceiv)) { + if (IS_ERR(musb->xceiv)) { + status = PTR_ERR(musb->xceiv); + + if (status == -ENXIO) + return status; + pr_err("HS USB OTG: no transceiver configured\n"); return -EPROBE_DEFER; } -- cgit v0.10.2 From e299bd93e4fb8e3fa426d30e0a0796b99052a572 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 11:02:56 +0200 Subject: usb: host: ehci-msm: fix PHY error handling PHY layer no longer returns NULL. We must switch from IS_ERR_OR_NULL() to IS_ERR(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 88a49c8..ebf4103 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * management. */ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(phy)) { + if (IS_ERR(phy)) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; goto put_hcd; -- cgit v0.10.2 From 6f3ed4ec182d191d1ba48fbf5aed021d2d00dd37 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 11:03:30 +0200 Subject: usb: host: ehci-mv: fix PHY error handling PHY layer no longer returns NULL. We must switch from IS_ERR_OR_NULL() to IS_ERR(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 9751823..3804820 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -240,12 +240,16 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { -#if IS_ENABLED(CONFIG_USB_PHY) ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(ehci_mv->otg)) { - dev_err(&pdev->dev, - "unable to find transceiver\n"); - retval = -ENODEV; + if (IS_ERR(ehci_mv->otg)) { + retval = PTR_ERR(ehci_mv->otg); + + if (retval == -ENXIO) + dev_info(&pdev->dev, "MV_USB_MODE_OTG " + "must have CONFIG_USB_PHY enabled\n"); + else + dev_err(&pdev->dev, + "unable to find transceiver\n"); goto err_disable_clk; } @@ -258,11 +262,6 @@ static int mv_ehci_probe(struct platform_device *pdev) } /* otg will enable clock before use as host */ mv_ehci_disable(ehci_mv); -#else - dev_info(&pdev->dev, "MV_USB_MODE_OTG " - "must have CONFIG_USB_PHY enabled\n"); - goto err_disable_clk; -#endif } else { if (pdata->set_vbus) pdata->set_vbus(1); -- cgit v0.10.2 From a16283e11d4443430b826c7b131a244e494ac53c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 11:04:15 +0200 Subject: usb: host: ehci-s5p: fix PHY error handling PHY layer no longer returns NULL. We must switch from IS_ERR_OR_NULL() to IS_ERR(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 20ebf6a..867a923 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -139,7 +139,7 @@ static int s5p_ehci_probe(struct platform_device *pdev) return -ENOMEM; phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(phy)) { + if (IS_ERR(phy)) { /* Fallback to pdata */ if (!pdata) { dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); -- cgit v0.10.2 From 9ee1c7fbeab5b671d3b63f2dd33ad48235efcfe8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 11:05:03 +0200 Subject: usb: host: ohci-exynos: fix PHY error handling PHY layer no longer returns NULL. We must switch from IS_ERR_OR_NULL() to IS_ERR(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index e3b7e85..4b469e0 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -128,7 +128,7 @@ static int exynos_ohci_probe(struct platform_device *pdev) return -ENOMEM; phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR_OR_NULL(phy)) { + if (IS_ERR(phy)) { /* Fallback to pdata */ if (!pdata) { dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); -- cgit v0.10.2 From 399e0f4f00adbfdea2122c1daec0d4015f56cc7a Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Fri, 8 Mar 2013 10:27:05 +0800 Subject: usb: musb: ux500_dma: add missing MEM resource check Fix dma_controller_create() fail path in case memory resource is missing. Acked-by: Linus Walleij Signed-off-by: Virupax Sadashivpetimath Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index c3a584c..2df9b7d 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -374,12 +374,17 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba controller = kzalloc(sizeof(*controller), GFP_KERNEL); if (!controller) - return NULL; + goto kzalloc_fail; controller->private_data = musb; /* Save physical address for DMA controller. */ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + dev_err(musb->controller, "no memory resource defined\n"); + goto plat_get_fail; + } + controller->phy_base = (dma_addr_t) iomem->start; controller->controller.start = ux500_dma_controller_start; @@ -391,4 +396,9 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba controller->controller.is_compatible = ux500_dma_is_compatible; return &controller->controller; + +plat_get_fail: + kfree(controller); +kzalloc_fail: + return NULL; } -- cgit v0.10.2 From 996a9d26d37c7dca27b7e9830a49daa74a2603b7 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 8 Mar 2013 10:27:06 +0800 Subject: usb: musb: ux500: implement musb_set_vbus Add ux500_musb_set_vbus() implementation for ux500. This is based on the version originally developed inside ST-Ericsson. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri [ balbi@ti.com: fix a build error due to missing otg_state_string() ] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 13a3929..0332fcd 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -36,6 +36,68 @@ struct ux500_glue { }; #define glue_to_musb(g) platform_get_drvdata(g->musb) +static void ux500_musb_set_vbus(struct musb *musb, int is_on) +{ + u8 devctl; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + /* HDRC controls CPEN, but beware current surges during device + * connect. They can trigger transient overcurrent conditions + * that must be ignored. + */ + + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + if (is_on) { + if (musb->xceiv->state == OTG_STATE_A_IDLE) { + /* start the session */ + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + /* + * Wait for the musb to set as A device to enable the + * VBUS + */ + while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) { + + if (time_after(jiffies, timeout)) { + dev_err(musb->controller, + "configured as A device timeout"); + break; + } + } + + } else { + musb->is_active = 1; + musb->xceiv->otg->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + MUSB_HST_MODE(musb); + } + } else { + musb->is_active = 0; + + /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and jumping + * right to B_IDLE... + */ + musb->xceiv->otg->default_a = 0; + devctl &= ~MUSB_DEVCTL_SESSION; + MUSB_DEV_MODE(musb); + } + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + /* + * Devctl values will be updated after vbus goes below + * session_valid. The time taken depends on the capacitance + * on VBUS line. The max discharge time can be upto 1 sec + * as per the spec. Typically on our platform, it is 200ms + */ + if (!is_on) + mdelay(200); + + dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", + usb_otg_state_string(musb->xceiv->state), + musb_readb(musb->mregs, MUSB_DEVCTL)); +} + static irqreturn_t ux500_musb_interrupt(int irq, void *__hci) { unsigned long flags; @@ -79,6 +141,8 @@ static int ux500_musb_exit(struct musb *musb) static const struct musb_platform_ops ux500_ops = { .init = ux500_musb_init, .exit = ux500_musb_exit, + + .set_vbus = ux500_musb_set_vbus, }; static int ux500_probe(struct platform_device *pdev) -- cgit v0.10.2 From 0135522c48982cf1d456d863272e911fdf8a17da Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 8 Mar 2013 10:27:07 +0800 Subject: usb: musb: ux500: add otg notifier support Add transceiver notifier event handling to the ux500 driver to set vbus on specific transceiver events. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri [ balbi@ti.com: fix build error due to missing otg_state_string() ] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 0332fcd..0ae9472 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -98,6 +98,37 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } +static int musb_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct musb *musb = container_of(nb, struct musb, nb); + + dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n", + event, usb_otg_state_string(musb->xceiv->state)); + + switch (event) { + case USB_EVENT_ID: + dev_dbg(musb->controller, "ID GND\n"); + ux500_musb_set_vbus(musb, 1); + break; + case USB_EVENT_VBUS: + dev_dbg(musb->controller, "VBUS Connect\n"); + ux500_musb_set_vbus(musb, 0); + break; + case USB_EVENT_NONE: + dev_dbg(musb->controller, "VBUS Disconnect\n"); + if (is_host_active(musb)) + ux500_musb_set_vbus(musb, 0); + else + musb->xceiv->state = OTG_STATE_B_IDLE; + break; + default: + dev_dbg(musb->controller, "ID float\n"); + return NOTIFY_DONE; + } + return NOTIFY_OK; +} + static irqreturn_t ux500_musb_interrupt(int irq, void *__hci) { unsigned long flags; @@ -120,12 +151,21 @@ static irqreturn_t ux500_musb_interrupt(int irq, void *__hci) static int ux500_musb_init(struct musb *musb) { + int status; + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -EPROBE_DEFER; } + musb->nb.notifier_call = musb_otg_notifications; + status = usb_register_notifier(musb->xceiv, &musb->nb); + if (status < 0) { + dev_dbg(musb->controller, "notification register failed\n"); + return status; + } + musb->isr = ux500_musb_interrupt; return 0; @@ -133,6 +173,8 @@ static int ux500_musb_init(struct musb *musb) static int ux500_musb_exit(struct musb *musb) { + usb_unregister_notifier(musb->xceiv, &musb->nb); + usb_put_phy(musb->xceiv); return 0; -- cgit v0.10.2 From 73f226cbd79adb5f3f25ee14c18900bb4a9acd15 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 8 Mar 2013 10:27:08 +0800 Subject: usb: otg: ab8500-usb: drop support for ab8500 pre v2.0 AB8500 versions preceding 2.0 were only used internally by ST-Ericsson and are not supported anymore. This patch drops all v1.0 and v1.1 support code. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 2d86f26..9f5e0e4 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -42,10 +42,8 @@ #define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) #define AB8500_BIT_WD_CTRL_KICK (1 << 1) -#define AB8500_V1x_LINK_STAT_WAIT (HZ/10) #define AB8500_WD_KICK_DELAY_US 100 /* usec */ #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ -#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */ /* Usb line status register */ enum ab8500_usb_link_status { @@ -70,16 +68,12 @@ enum ab8500_usb_link_status { struct ab8500_usb { struct usb_phy phy; struct device *dev; - int irq_num_id_rise; - int irq_num_id_fall; - int irq_num_vbus_rise; - int irq_num_vbus_fall; + struct ab8500 *ab8500; int irq_num_link_status; unsigned vbus_draw; struct delayed_work dwork; struct work_struct phy_dis_work; unsigned long link_status_wait; - int rev; }; static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) @@ -102,10 +96,7 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) (AB8500_BIT_WD_CTRL_ENABLE | AB8500_BIT_WD_CTRL_KICK)); - if (ab->rev > 0x10) /* v1.1 v2.0 */ - udelay(AB8500_WD_V11_DISABLE_DELAY_US); - else /* v1.0 */ - msleep(AB8500_WD_V10_DISABLE_DELAY_MS); + udelay(AB8500_WD_V11_DISABLE_DELAY_US); abx500_set_register_interruptible(ab->dev, AB8500_SYS_CTRL2_BLOCK, @@ -225,29 +216,6 @@ static void ab8500_usb_delayed_work(struct work_struct *work) ab8500_usb_link_status_update(ab); } -static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - -static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data) -{ - struct ab8500_usb *ab = (struct ab8500_usb *) data; - - /* Link status will not be updated till phy is disabled. */ - ab8500_usb_peri_phy_dis(ab); - - /* Wait for link status to become stable. */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); - - return IRQ_HANDLED; -} - static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) { struct ab8500_usb *ab = (struct ab8500_usb *) data; @@ -361,86 +329,7 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) static void ab8500_usb_irq_free(struct ab8500_usb *ab) { - if (ab->rev < 0x20) { - free_irq(ab->irq_num_id_rise, ab); - free_irq(ab->irq_num_id_fall, ab); - free_irq(ab->irq_num_vbus_rise, ab); - free_irq(ab->irq_num_vbus_fall, ab); - } else { - free_irq(ab->irq_num_link_status, ab); - } -} - -static int ab8500_usb_v1x_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) -{ - int err; - - ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R"); - if (ab->irq_num_id_rise < 0) { - dev_err(&pdev->dev, "ID rise irq not found\n"); - return ab->irq_num_id_rise; - } - err = request_threaded_irq(ab->irq_num_id_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID rise irq\n"); - goto fail0; - } - - ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); - if (ab->irq_num_id_fall < 0) { - dev_err(&pdev->dev, "ID fall irq not found\n"); - return ab->irq_num_id_fall; - } - err = request_threaded_irq(ab->irq_num_id_fall, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-id-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID fall irq\n"); - goto fail1; - } - - ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R"); - if (ab->irq_num_vbus_rise < 0) { - dev_err(&pdev->dev, "VBUS rise irq not found\n"); - return ab->irq_num_vbus_rise; - } - err = request_threaded_irq(ab->irq_num_vbus_rise, NULL, - ab8500_usb_v1x_common_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-rise", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus rise irq\n"); - goto fail2; - } - - ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F"); - if (ab->irq_num_vbus_fall < 0) { - dev_err(&pdev->dev, "VBUS fall irq not found\n"); - return ab->irq_num_vbus_fall; - } - err = request_threaded_irq(ab->irq_num_vbus_fall, NULL, - ab8500_usb_v1x_vbus_fall_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-vbus-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); - goto fail3; - } - - return 0; -fail3: - free_irq(ab->irq_num_vbus_rise, ab); -fail2: - free_irq(ab->irq_num_id_fall, ab); -fail1: - free_irq(ab->irq_num_id_rise, ab); -fail0: - return err; + free_irq(ab->irq_num_link_status, ab); } static int ab8500_usb_v2_res_setup(struct platform_device *pdev, @@ -471,16 +360,16 @@ static int ab8500_usb_v2_res_setup(struct platform_device *pdev, static int ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; + struct ab8500 *ab8500; struct usb_otg *otg; int err; int rev; + ab8500 = dev_get_drvdata(pdev->dev.parent); rev = abx500_get_chip_id(&pdev->dev); - if (rev < 0) { - dev_err(&pdev->dev, "Chip id read failed\n"); - return rev; - } else if (rev < 0x10) { - dev_err(&pdev->dev, "Unsupported AB8500 chip\n"); + + if (is_ab8500_1p1_or_earlier(ab8500)) { + dev_err(&pdev->dev, "Unsupported AB8500 chip rev=%d\n", rev); return -ENODEV; } @@ -495,7 +384,7 @@ static int ab8500_usb_probe(struct platform_device *pdev) } ab->dev = &pdev->dev; - ab->rev = rev; + ab->ab8500 = ab8500; ab->phy.dev = ab->dev; ab->phy.otg = otg; ab->phy.label = "ab8500"; @@ -519,13 +408,7 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - if (ab->rev < 0x20) { - err = ab8500_usb_v1x_res_setup(pdev, ab); - ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT; - } else { - err = ab8500_usb_v2_res_setup(pdev, ab); - } - + err = ab8500_usb_v2_res_setup(pdev, ab); if (err < 0) goto fail0; @@ -535,7 +418,7 @@ static int ab8500_usb_probe(struct platform_device *pdev) goto fail1; } - dev_info(&pdev->dev, "AB8500 usb driver initialized\n"); + dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); return 0; fail1: -- cgit v0.10.2 From af6882be363d3a7bf0f72dd17ac2a639c4da0059 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 8 Mar 2013 10:27:09 +0800 Subject: usb: phy: ab8500-usb: update irq handling code Update irq handling code to notify all possible link status changes of AB8500 and AB8505 to the ux500-musb glue driver. The additional event codes will be used for pm-runtime implementation, and are defined in a separate ux500-specific header. This also modify the irq registration code to use devm_* helpers and drop all non necessary fail path code. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 0ae9472..88795f5 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "musb_core.h" @@ -107,15 +108,15 @@ static int musb_otg_notifications(struct notifier_block *nb, event, usb_otg_state_string(musb->xceiv->state)); switch (event) { - case USB_EVENT_ID: + case UX500_MUSB_ID: dev_dbg(musb->controller, "ID GND\n"); ux500_musb_set_vbus(musb, 1); break; - case USB_EVENT_VBUS: + case UX500_MUSB_VBUS: dev_dbg(musb->controller, "VBUS Connect\n"); ux500_musb_set_vbus(musb, 0); break; - case USB_EVENT_NONE: + case UX500_MUSB_NONE: dev_dbg(musb->controller, "VBUS Disconnect\n"); if (is_host_active(musb)) ux500_musb_set_vbus(musb, 0); diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 9f5e0e4..351b036 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -31,9 +31,11 @@ #include #include #include +#include #define AB8500_MAIN_WD_CTRL_REG 0x01 #define AB8500_USB_LINE_STAT_REG 0x80 +#define AB8505_USB_LINE_STAT_REG 0x94 #define AB8500_USB_PHY_CTRL_REG 0x8A #define AB8500_BIT_OTG_STAT_ID (1 << 0) @@ -44,36 +46,76 @@ #define AB8500_WD_KICK_DELAY_US 100 /* usec */ #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ +#define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */ /* Usb line status register */ enum ab8500_usb_link_status { - USB_LINK_NOT_CONFIGURED = 0, - USB_LINK_STD_HOST_NC, - USB_LINK_STD_HOST_C_NS, - USB_LINK_STD_HOST_C_S, - USB_LINK_HOST_CHG_NM, - USB_LINK_HOST_CHG_HS, - USB_LINK_HOST_CHG_HS_CHIRP, - USB_LINK_DEDICATED_CHG, - USB_LINK_ACA_RID_A, - USB_LINK_ACA_RID_B, - USB_LINK_ACA_RID_C_NM, - USB_LINK_ACA_RID_C_HS, - USB_LINK_ACA_RID_C_HS_CHIRP, - USB_LINK_HM_IDGND, - USB_LINK_RESERVED, - USB_LINK_NOT_VALID_LINK + USB_LINK_NOT_CONFIGURED_8500 = 0, + USB_LINK_STD_HOST_NC_8500, + USB_LINK_STD_HOST_C_NS_8500, + USB_LINK_STD_HOST_C_S_8500, + USB_LINK_HOST_CHG_NM_8500, + USB_LINK_HOST_CHG_HS_8500, + USB_LINK_HOST_CHG_HS_CHIRP_8500, + USB_LINK_DEDICATED_CHG_8500, + USB_LINK_ACA_RID_A_8500, + USB_LINK_ACA_RID_B_8500, + USB_LINK_ACA_RID_C_NM_8500, + USB_LINK_ACA_RID_C_HS_8500, + USB_LINK_ACA_RID_C_HS_CHIRP_8500, + USB_LINK_HM_IDGND_8500, + USB_LINK_RESERVED_8500, + USB_LINK_NOT_VALID_LINK_8500, +}; + +enum ab8505_usb_link_status { + USB_LINK_NOT_CONFIGURED_8505 = 0, + USB_LINK_STD_HOST_NC_8505, + USB_LINK_STD_HOST_C_NS_8505, + USB_LINK_STD_HOST_C_S_8505, + USB_LINK_CDP_8505, + USB_LINK_RESERVED0_8505, + USB_LINK_RESERVED1_8505, + USB_LINK_DEDICATED_CHG_8505, + USB_LINK_ACA_RID_A_8505, + USB_LINK_ACA_RID_B_8505, + USB_LINK_ACA_RID_C_NM_8505, + USB_LINK_RESERVED2_8505, + USB_LINK_RESERVED3_8505, + USB_LINK_HM_IDGND_8505, + USB_LINK_CHARGERPORT_NOT_OK_8505, + USB_LINK_CHARGER_DM_HIGH_8505, + USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505, + USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505, + USB_LINK_STD_UPSTREAM_8505, + USB_LINK_CHARGER_SE1_8505, + USB_LINK_CARKIT_CHGR_1_8505, + USB_LINK_CARKIT_CHGR_2_8505, + USB_LINK_ACA_DOCK_CHGR_8505, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505, + USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505, + USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505, + USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505, +}; + +enum ab8500_usb_mode { + USB_IDLE = 0, + USB_PERIPHERAL, + USB_HOST, + USB_DEDICATED_CHG }; struct ab8500_usb { struct usb_phy phy; struct device *dev; struct ab8500 *ab8500; - int irq_num_link_status; unsigned vbus_draw; struct delayed_work dwork; struct work_struct phy_dis_work; unsigned long link_status_wait; + enum ab8500_usb_mode mode; + int previous_link_status_state; }; static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) @@ -104,6 +146,17 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) 0); } +static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit) +{ + /* Workaround for v2.0 bug # 31952 */ + if (is_ab8500_2p0(ab->ab8500)) { + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + bit, bit); + udelay(AB8500_V20_31952_DISABLE_DELAY_US); + } +} + static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, bool enable) { @@ -139,92 +192,276 @@ static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, #define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) #define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) -static int ab8500_usb_link_status_update(struct ab8500_usb *ab) +static int ab8505_usb_link_status_update(struct ab8500_usb *ab, + enum ab8505_usb_link_status lsts) { - u8 reg; - enum ab8500_usb_link_status lsts; - void *v = NULL; - enum usb_phy_events event; + enum ux500_musb_vbus_id_status event = 0; - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_LINE_STAT_REG, - ®); + dev_dbg(ab->dev, "ab8505_usb_link_status_update %d\n", lsts); - lsts = (reg >> 3) & 0x0F; + /* + * Spurious link_status interrupts are seen at the time of + * disconnection of a device in RIDA state + */ + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8505 && + (lsts == USB_LINK_STD_HOST_NC_8505)) + return 0; + + ab->previous_link_status_state = lsts; switch (lsts) { - case USB_LINK_NOT_CONFIGURED: - case USB_LINK_RESERVED: - case USB_LINK_NOT_VALID_LINK: - /* TODO: Disable regulators. */ - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - ab->phy.state = OTG_STATE_B_IDLE; + case USB_LINK_ACA_RID_B_8505: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_8505: + case USB_LINK_RESERVED0_8505: + case USB_LINK_RESERVED1_8505: + case USB_LINK_RESERVED2_8505: + case USB_LINK_RESERVED3_8505: + ab->mode = USB_IDLE; ab->phy.otg->default_a = false; ab->vbus_draw = 0; - event = USB_EVENT_NONE; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* + * Fallback to default B_IDLE as nothing + * is connected + */ + ab->phy.state = OTG_STATE_B_IDLE; break; - case USB_LINK_STD_HOST_NC: - case USB_LINK_STD_HOST_C_NS: - case USB_LINK_STD_HOST_C_S: - case USB_LINK_HOST_CHG_NM: - case USB_LINK_HOST_CHG_HS: - case USB_LINK_HOST_CHG_HS_CHIRP: - if (ab->phy.otg->gadget) { - /* TODO: Enable regulators. */ + case USB_LINK_ACA_RID_C_NM_8505: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_8505: + case USB_LINK_STD_HOST_C_NS_8505: + case USB_LINK_STD_HOST_C_S_8505: + case USB_LINK_CDP_8505: + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; ab8500_usb_peri_phy_en(ab); - v = ab->phy.otg->gadget; + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); } - event = USB_EVENT_VBUS; + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; break; - case USB_LINK_HM_IDGND: - if (ab->phy.otg->host) { - /* TODO: Enable regulators. */ + case USB_LINK_ACA_RID_A_8505: + case USB_LINK_ACA_DOCK_CHGR_8505: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_8505: + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; ab8500_usb_host_phy_en(ab); - v = ab->phy.otg->host; + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); } - ab->phy.state = OTG_STATE_A_IDLE; ab->phy.otg->default_a = true; - event = USB_EVENT_ID; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); break; - case USB_LINK_ACA_RID_A: - case USB_LINK_ACA_RID_B: - /* TODO */ - case USB_LINK_ACA_RID_C_NM: - case USB_LINK_ACA_RID_C_HS: - case USB_LINK_ACA_RID_C_HS_CHIRP: - case USB_LINK_DEDICATED_CHG: - /* TODO: vbus_draw */ - event = USB_EVENT_CHARGER; + case USB_LINK_DEDICATED_CHG_8505: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + default: break; } - atomic_notifier_call_chain(&ab->phy.notifier, event, v); + return 0; +} + +static int ab8500_usb_link_status_update(struct ab8500_usb *ab, + enum ab8500_usb_link_status lsts) +{ + enum ux500_musb_vbus_id_status event = 0; + + dev_dbg(ab->dev, "ab8500_usb_link_status_update %d\n", lsts); + + /* + * Spurious link_status interrupts are seen in case of a + * disconnection of a device in IDGND and RIDA stage + */ + if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8500 && + (lsts == USB_LINK_STD_HOST_C_NS_8500 || + lsts == USB_LINK_STD_HOST_NC_8500)) + return 0; + + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8500 && + lsts == USB_LINK_STD_HOST_NC_8500) + return 0; + + ab->previous_link_status_state = lsts; + + switch (lsts) { + case USB_LINK_ACA_RID_B_8500: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_8500: + case USB_LINK_NOT_VALID_LINK_8500: + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* Fallback to default B_IDLE as nothing is connected */ + ab->phy.state = OTG_STATE_B_IDLE; + break; + + case USB_LINK_ACA_RID_C_NM_8500: + case USB_LINK_ACA_RID_C_HS_8500: + case USB_LINK_ACA_RID_C_HS_CHIRP_8500: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_8500: + case USB_LINK_STD_HOST_C_NS_8500: + case USB_LINK_STD_HOST_C_S_8500: + case USB_LINK_HOST_CHG_NM_8500: + case USB_LINK_HOST_CHG_HS_8500: + case USB_LINK_HOST_CHG_HS_CHIRP_8500: + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; + ab8500_usb_peri_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; + break; + + case USB_LINK_ACA_RID_A_8500: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_8500: + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; + ab8500_usb_host_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + ab->phy.otg->default_a = true; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_DEDICATED_CHG_8500: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_RESERVED_8500: + break; + } return 0; } -static void ab8500_usb_delayed_work(struct work_struct *work) +/* + * Connection Sequence: + * 1. Link Status Interrupt + * 2. Enable AB clock + * 3. Enable AB regulators + * 4. Enable USB phy + * 5. Reset the musb controller + * 6. Switch the ULPI GPIO pins to fucntion mode + * 7. Enable the musb Peripheral5 clock + * 8. Restore MUSB context + */ +static int abx500_usb_link_status_update(struct ab8500_usb *ab) { - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - dwork.work); + u8 reg; + int ret = 0; + + if (is_ab8500(ab->ab8500)) { + enum ab8500_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_LINE_STAT_REG, ®); + lsts = (reg >> 3) & 0x0F; + ret = ab8500_usb_link_status_update(ab, lsts); + } else if (is_ab8505(ab->ab8500)) { + enum ab8505_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8505_USB_LINE_STAT_REG, ®); + lsts = (reg >> 3) & 0x1F; + ret = ab8505_usb_link_status_update(ab, lsts); + } + + return ret; +} + +/* + * Disconnection Sequence: + * 1. Disconect Interrupt + * 2. Disable regulators + * 3. Disable AB clock + * 4. Disable the Phy + * 5. Link Status Interrupt + * 6. Disable Musb Clock + */ +static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + enum usb_phy_events event = UX500_MUSB_NONE; + + /* Link status will not be updated till phy is disabled. */ + if (ab->mode == USB_HOST) { + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_host_phy_dis(ab); + ab->mode = USB_IDLE; + } + + if (ab->mode == USB_PERIPHERAL) { + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_peri_phy_dis(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_CLEAN, &ab->vbus_draw); + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + } + + if (is_ab8500_2p0(ab->ab8500)) { + if (ab->mode == USB_DEDICATED_CHG) { + ab8500_usb_wd_linkstatus(ab, + AB8500_BIT_PHY_CTRL_DEVICE_EN); + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_DEVICE_EN, 0); + } + } - ab8500_usb_link_status_update(ab); + return IRQ_HANDLED; } -static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) +static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data) { struct ab8500_usb *ab = (struct ab8500_usb *) data; - ab8500_usb_link_status_update(ab); + abx500_usb_link_status_update(ab); return IRQ_HANDLED; } +static void ab8500_usb_delayed_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + dwork.work); + + abx500_usb_link_status_update(ab); +} + static void ab8500_usb_phy_disable_work(struct work_struct *work) { struct ab8500_usb *ab = container_of(work, struct ab8500_usb, @@ -250,7 +487,7 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) if (mA) atomic_notifier_call_chain(&ab->phy.notifier, - USB_EVENT_ENUMERATED, ab->phy.otg->gadget); + UX500_MUSB_ENUMERATED, ab->phy.otg->gadget); return 0; } @@ -327,30 +564,48 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static void ab8500_usb_irq_free(struct ab8500_usb *ab) -{ - free_irq(ab->irq_num_link_status, ab); -} - -static int ab8500_usb_v2_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) +static int ab8500_usb_irq_setup(struct platform_device *pdev, + struct ab8500_usb *ab) { int err; + int irq; - ab->irq_num_link_status = platform_get_irq_byname(pdev, - "USB_LINK_STATUS"); - if (ab->irq_num_link_status < 0) { + irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS"); + if (irq < 0) { dev_err(&pdev->dev, "Link status irq not found\n"); - return ab->irq_num_link_status; + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_link_status_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-link-status", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for link status irq\n"); + return err; } - err = request_threaded_irq(ab->irq_num_link_status, NULL, - ab8500_usb_v20_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-link-status", ab); + irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); + if (irq < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-id-fall", ab); if (err < 0) { - dev_err(ab->dev, - "request_irq failed for link status irq\n"); + dev_err(ab->dev, "request_irq failed for ID fall irq\n"); + return err; + } + + irq = platform_get_irq_byname(pdev, "VBUS_DET_F"); + if (irq < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-vbus-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); return err; } @@ -408,22 +663,23 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - err = ab8500_usb_v2_res_setup(pdev, ab); + err = ab8500_usb_irq_setup(pdev, ab); if (err < 0) - goto fail0; + goto fail; err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); - goto fail1; + goto fail; } + /* Needed to enable ID detection. */ + ab8500_usb_wd_workaround(ab); + dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); return 0; -fail1: - ab8500_usb_irq_free(ab); -fail0: +fail: kfree(otg); kfree(ab); return err; @@ -433,8 +689,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) { struct ab8500_usb *ab = platform_get_drvdata(pdev); - ab8500_usb_irq_free(ab); - cancel_delayed_work_sync(&ab->dwork); cancel_work_sync(&ab->phy_dis_work); diff --git a/include/linux/usb/musb-ux500.h b/include/linux/usb/musb-ux500.h new file mode 100644 index 0000000..1e2c713 --- /dev/null +++ b/include/linux/usb/musb-ux500.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MUSB_UX500_H__ +#define __MUSB_UX500_H__ + +enum ux500_musb_vbus_id_status { + UX500_MUSB_NONE = 0, + UX500_MUSB_VBUS, + UX500_MUSB_ID, + UX500_MUSB_CHARGER, + UX500_MUSB_ENUMERATED, + UX500_MUSB_RIDA, + UX500_MUSB_RIDB, + UX500_MUSB_RIDC, + UX500_MUSB_PREPARE, + UX500_MUSB_CLEAN, +}; + +#endif /* __MUSB_UX500_H__ */ -- cgit v0.10.2 From 6d5df8976266d8e40603601f7695537f9f3dc9e2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 18 Mar 2013 12:04:54 -0400 Subject: USB: EHCI: decrease schedule-status poll timeout This patch (as1657) decreases the timeout used by ehci-hcd for polling the async and periodic schedule statuses. The timeout is currently set to 20 ms, which is much too high. Controllers should always update the schedule status within one or two ms of being told to do so; if they don't then something is wrong. Furthermore, bug reports have shown that sometimes controllers (particularly those made by VIA) don't update the status bit at all, even when the schedule does change state. When this happens, polling for 20 ms would cause an unnecessarily long delay. The delay is reduced to somewhere between 2 and 4 ms, depending on the slop allowed by the kernel's high-res timers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 20dbdcb..cc9ad58 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -113,8 +113,8 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->ASS_poll_count++ < 20) { + /* Poll again later, but give up after about 2-4 ms */ + if (ehci->ASS_poll_count++ < 2) { ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); return; } @@ -159,8 +159,8 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->PSS_poll_count++ < 20) { + /* Poll again later, but give up after about 2-4 ms */ + if (ehci->PSS_poll_count++ < 2) { ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); return; } -- cgit v0.10.2 From 4dd405a4b0969bfec4dc9959050b46d818b6549b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 18 Mar 2013 12:05:08 -0400 Subject: USB: EHCI: improve use of per-port status-change bits This patch (as1634) simplifies some of the code associated with the per-port change bits added in EHCI-1.1, and in particular it fixes a bug in the logic of ehci_hub_status_data(). Even if the change bit doesn't indicate anything happened on a particular port, we still have to notify the core about changes to the suspend or reset status. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 303b022..fcf8b94 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -758,7 +758,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* remote wakeup [4.3.1] */ if (status & STS_PCD) { unsigned i = HCS_N_PORTS (ehci->hcs_params); - u32 ppcd = 0; + u32 ppcd = ~0; /* kick root hub later */ pcd_status = status; @@ -775,7 +775,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) int pstatus; /* leverage per-port change bits feature */ - if (ehci->has_ppcd && !(ppcd & (1 << i))) + if (!(ppcd & (1 << i))) continue; pstatus = ehci_readl(ehci, &ehci->regs->port_status[i]); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4d3b294..576b735 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -590,7 +590,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) u32 mask; int ports, i, retval = 1; unsigned long flags; - u32 ppcd = 0; + u32 ppcd = ~0; /* init status to no-changes */ buf [0] = 0; @@ -628,9 +628,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) for (i = 0; i < ports; i++) { /* leverage per-port change bits feature */ - if (ehci->has_ppcd && !(ppcd & (1 << i))) - continue; - temp = ehci_readl(ehci, &ehci->regs->port_status [i]); + if (ppcd & (1 << i)) + temp = ehci_readl(ehci, &ehci->regs->port_status[i]); + else + temp = 0; /* * Return status information even for ports with OWNER set. -- cgit v0.10.2 From 60fd4aa742a0c4f01dafeb0d125fed54e91e3657 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 18 Mar 2013 12:05:19 -0400 Subject: USB: EHCI: reorganize ehci_iaa_watchdog() This patch (as1635) rearranges the control-flow logic in ehci_iaa_watchdog() slightly to agree better with the comments. It also changes a verbose-debug message to a regular debug message. Expiration of the IAA watchdog is an unusual event and can lead to problems; we need to know about it if it happens during debugging. It should not be necessary to set a "verbose" compilation option. No behavioral changes other than the debug message. Lots of apparent changes to the source text, though, because the indentation level was decreased. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index cc9ad58..97815d0 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -295,8 +295,7 @@ static void end_free_itds(struct ehci_hcd *ehci) /* Handle lost (or very late) IAA interrupts */ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) { - if (ehci->rh_state != EHCI_RH_RUNNING) - return; + u32 cmd, status; /* * Lost IAA irqs wedge things badly; seen first with a vt8235. @@ -304,34 +303,32 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->async_iaa) { - u32 cmd, status; - - /* If we get here, IAA is *REALLY* late. It's barely - * conceivable that the system is so busy that CMD_IAAD - * is still legitimately set, so let's be sure it's - * clear before we read STS_IAA. (The HC should clear - * CMD_IAAD when it sets STS_IAA.) - */ - cmd = ehci_readl(ehci, &ehci->regs->command); - - /* - * If IAA is set here it either legitimately triggered - * after the watchdog timer expired (_way_ late, so we'll - * still count it as lost) ... or a silicon erratum: - * - VIA seems to set IAA without triggering the IRQ; - * - IAAD potentially cleared without setting IAA. - */ - status = ehci_readl(ehci, &ehci->regs->status); - if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - COUNT(ehci->stats.lost_iaa); - ehci_writel(ehci, STS_IAA, &ehci->regs->status); - } + if (!ehci->async_iaa || ehci->rh_state != EHCI_RH_RUNNING) + return; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = ehci_readl(ehci, &ehci->regs->command); - ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", - status, cmd); - end_unlink_async(ehci); + /* + * If IAA is set here it either legitimately triggered + * after the watchdog timer expired (_way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = ehci_readl(ehci, &ehci->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { + COUNT(ehci->stats.lost_iaa); + ehci_writel(ehci, STS_IAA, &ehci->regs->status); } + + ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd); + end_unlink_async(ehci); } -- cgit v0.10.2 From 24b90814fb133bb7971aef8ea5e642d9f9bc4b0b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 18 Mar 2013 12:05:42 -0400 Subject: USB: EHCI: don't turn on PORT_SUSPEND during port resume This patch (as1637) cleans up the way ehci-hcd handles end-of-resume port signalling. When the PORT_RESUME bit in the port's status and control register is cleared, we shouldn't be setting the PORT_SUSPEND bit at the same time. Not doing this doesn't seem to have hurt so far, but we might as well do the right thing. Also, the patch replaces an estimated value for what the port status should be following a resume with the actual register value. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 576b735..0df45d9 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -464,7 +464,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) while (i--) { temp = ehci_readl(ehci, &ehci->regs->port_status [i]); if (test_bit(i, &resume_needed)) { - temp &= ~(PORT_RWC_BITS | PORT_RESUME); + temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); ehci_writel(ehci, temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } @@ -871,10 +871,9 @@ static int ehci_hub_control ( usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ - temp = ehci_readl(ehci, status_reg); - ehci_writel(ehci, - temp & ~(PORT_RWC_BITS | PORT_RESUME), - status_reg); + temp &= ~(PORT_RWC_BITS | + PORT_SUSPEND | PORT_RESUME); + ehci_writel(ehci, temp, status_reg); clear_bit(wIndex, &ehci->resuming_ports); retval = handshake(ehci, status_reg, PORT_RESUME, 0, 2000 /* 2msec */); @@ -884,7 +883,7 @@ static int ehci_hub_control ( wIndex + 1, retval); goto error; } - temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + temp = ehci_readl(ehci, status_reg); } } -- cgit v0.10.2 From 3f3b55bf7833d81d00c793d722e2af58d3b12963 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Thu, 14 Mar 2013 20:15:37 -0700 Subject: usb: ehci-s5p: Use devm for requesting ehci_vbus_gpio The ehci_vbus_gpio is requested but never freed. This can cause problems with deferred probes and would cause problems if s5p_ehci_remove was ever called. Use devm to fix this. Signed-off-by: Doug Anderson Acked-by: Jingoo Han Tested-by: Vivek Gautam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 20ebf6a..738490e 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -92,20 +92,21 @@ static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci) static void s5p_setup_vbus_gpio(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int err; int gpio; - if (!pdev->dev.of_node) + if (!dev->of_node) return; - gpio = of_get_named_gpio(pdev->dev.of_node, - "samsung,vbus-gpio", 0); + gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0); if (!gpio_is_valid(gpio)) return; - err = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "ehci_vbus_gpio"); + err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, + "ehci_vbus_gpio"); if (err) - dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio); + dev_err(dev, "can't request ehci vbus gpio %d", gpio); } static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); -- cgit v0.10.2 From b64a15930c35c3c1046533aadcfe4f3233e70c20 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 19 Mar 2013 10:14:35 +0200 Subject: usb: phy: samsung: fix sparse warning Fix the following sparse warning: drivers/usb/phy/phy-samsung-usb2.c:50:26: sparse: incorrect type in argument 1 (different address spaces) drivers/usb/phy/phy-samsung-usb2.c:50:26: expected void const volatile [noderef] *addr drivers/usb/phy/phy-samsung-usb2.c:50:26: got void * Cc: Vivek Gautam Cc: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index dce9681..45ffe03 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -43,7 +43,7 @@ static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static bool exynos5_phyhost_is_on(void *regs) +static bool exynos5_phyhost_is_on(void __iomem *regs) { u32 reg; -- cgit v0.10.2 From e8d891fb7b8fe4ee7311820594323d46dbc31d45 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 20 Mar 2013 08:01:53 +0200 Subject: usb: phy: gpio-vbus: don't ignore regulator APIs return value Due to recent changes to regulator API, all users which don't check regulator_{en,dis}able()'s return value will generate compile warnings. Add such checks to gpio-vbus. Cc: Mark Brown Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index a7d4ac5..4c76074 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -61,6 +61,7 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) { struct regulator *vbus_draw = gpio_vbus->vbus_draw; int enabled; + int ret; if (!vbus_draw) return; @@ -69,12 +70,16 @@ static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) if (mA) { regulator_set_current_limit(vbus_draw, 0, 1000 * mA); if (!enabled) { - regulator_enable(vbus_draw); + ret = regulator_enable(vbus_draw); + if (ret < 0) + return; gpio_vbus->vbus_draw_enabled = 1; } } else { if (enabled) { - regulator_disable(vbus_draw); + ret = regulator_disable(vbus_draw); + if (ret < 0) + return; gpio_vbus->vbus_draw_enabled = 0; } } -- cgit v0.10.2 From 75f32ec1de7a4c905ba2007972d981f96c977eb2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 20 Mar 2013 14:18:28 +0200 Subject: arm: tegra: fix Kconfig select clauses USB_ULPI and USB_ULPI_VIEWPORT shouldn't really be selected directly by anyone, but since Tegra still needs some time before turning ulpi viewport into a proper PHY driver, we need to keep the selects in place. This patch just fixes the conditional select so that it will continue to build after merging the latest PHY layer changes. Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index d1c4893..dbc653e 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -18,8 +18,8 @@ config ARCH_TEGRA_2x_SOC select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0 select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB - select USB_ULPI_VIEWPORT if USB_SUPPORT + select USB_ULPI if USB_PHY + select USB_ULPI_VIEWPORT if USB_PHY help Support for NVIDIA Tegra AP20 and T20 processors, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller @@ -37,8 +37,8 @@ config ARCH_TEGRA_3x_SOC select PINCTRL_TEGRA30 select PL310_ERRATA_769419 if CACHE_L2X0 select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB - select USB_ULPI_VIEWPORT if USB_SUPPORT + select USB_ULPI if USB_PHY + select USB_ULPI_VIEWPORT if USB_PHY help Support for NVIDIA Tegra T30 processor family, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ba1347c..1b58587 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -179,6 +179,7 @@ config USB_EHCI_TEGRA boolean "NVIDIA Tegra HCD support" depends on USB_EHCI_HCD && ARCH_TEGRA select USB_EHCI_ROOT_HUB_TT + select USB_PHY help This driver enables support for the internal USB Host Controllers found in NVIDIA Tegra SoCs. The controllers are EHCI compliant. -- cgit v0.10.2 From 29727f3bc2e691d59c521ff09b4d59a743b5d9a3 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Wed, 20 Mar 2013 11:46:10 -0400 Subject: Revert "USB: quatech2: only write to the tty if the port is open." This reverts commit 27b351c5546008c640b3e65152f60ca74b3706f1. Calling tty_flip_buffer_push on an unopened tty is legal, so the driver doesn't need track if port has been opened. Reverting this allows the entire is_open logic to be removed. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index d643a4d..00e6c9b 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -661,9 +661,7 @@ void qt2_process_read_urb(struct urb *urb) __func__); break; } - - if (port_priv->is_open) - tty_flip_buffer_push(&port->port); + tty_flip_buffer_push(&port->port); newport = *(ch + 3); @@ -706,8 +704,7 @@ void qt2_process_read_urb(struct urb *urb) tty_insert_flip_string(&port->port, ch, 1); } - if (port_priv->is_open) - tty_flip_buffer_push(&port->port); + tty_flip_buffer_push(&port->port); } static void qt2_write_bulk_callback(struct urb *urb) -- cgit v0.10.2 From 44a50d088aad18a1a2709091bf25a108f967f8c9 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 17 Mar 2013 20:23:21 +0200 Subject: usb: phy: twl4030-usb: don't enable PHY during init There is no need to do it, otg.set_suspend(false) (which itself comes from runtime_pm OMAP glue calls) will enable it later anyway. This used to be the place where things were enabled if booted with cable connected before runtime_pm conversion, but now can be dropped. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 1986c78..4ad234c 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -515,19 +515,17 @@ static int twl4030_usb_phy_init(struct usb_phy *phy) struct twl4030_usb *twl = phy_to_twl(phy); enum omap_musb_vbus_id_status status; - status = twl4030_usb_linkstat(twl); - if (status > 0) { - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) { - __twl4030_phy_power(twl, 0); - twl->asleep = 1; - } else { - __twl4030_phy_resume(twl); - twl->asleep = 0; - } + /* + * Start in sleep state, we'll get called through set_suspend() + * callback when musb is runtime resumed and it's time to start. + */ + __twl4030_phy_power(twl, 0); + twl->asleep = 1; + status = twl4030_usb_linkstat(twl); + if (status > 0) omap_musb_mailbox(twl->linkstat); - } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); return 0; } -- cgit v0.10.2 From ca4f70ce78de9a2fa09741f80cf7bda2f4256ecc Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 17 Mar 2013 20:23:22 +0200 Subject: usb: phy: twl4030-usb: ignore duplicate events In some rare cases we may get multiple interrupts that will generate duplicate omap_musb_mailbox() calls. This is a problem because each VBUS/ID event generates runtime_pm call in OMAP glue code, causing unbalanced gets or puts and breaking PM. The same goes for initial state, glue already defaults to "no cable" state, so only bother it if we have VBUS or ID. Signed-off-by: Grazvydas Ignotas Reviewed-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 4ad234c..7ff67ce 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -483,9 +483,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; enum omap_musb_vbus_id_status status; + enum omap_musb_vbus_id_status status_prev = twl->linkstat; status = twl4030_usb_linkstat(twl); - if (status > 0) { + if (status > 0 && status != status_prev) { /* FIXME add a set_power() method so that B-devices can * configure the charger appropriately. It's not always * correct to consume VBUS power, and how much current to @@ -523,7 +524,7 @@ static int twl4030_usb_phy_init(struct usb_phy *phy) twl->asleep = 1; status = twl4030_usb_linkstat(twl); - if (status > 0) + if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) omap_musb_mailbox(twl->linkstat); sysfs_notify(&twl->dev->kobj, NULL, "vbus"); -- cgit v0.10.2 From 15460de7f3d9cf0846af1cfdb4a3995d2f270ce7 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 17 Mar 2013 20:23:23 +0200 Subject: usb: phy: twl4030-usb: don't switch the phy on/off needlessly With runtime_pm in place there is no longer need to turn the phy on/off in OTG layer on cable connect/disconnect, OMAP glue does this through otg.set_suspend() callback after it's called through omap_musb_mailbox() on VBUS/ID interrupt. Not doing this will save power when cable is connected but no gadget driver is loaded. This will also have side effect of automatic USB charging no longer working without twl4030_charger driver, because a regulator needed for charging will no longer be enabled, so be sure to enable charger driver if charging is needed. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 7ff67ce..f4ec53a 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -498,12 +498,6 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if (status == OMAP_MUSB_VBUS_OFF || - status == OMAP_MUSB_ID_FLOAT) - twl4030_phy_suspend(twl, 0); - else - twl4030_phy_resume(twl); - omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); -- cgit v0.10.2 From 249751f22380386042056cce6a73c934f5b942a3 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 17 Mar 2013 20:23:24 +0200 Subject: usb: phy: twl4030-usb: poll for ID disconnect On pandora, STS_USB interrupt doesn't arrive on USB host cable disconnect for some reason while VBUS is driven by twl itself, but STS_HW_CONDITIONS is updated correctly. It does work fine when PHY is powered down though. To work around that we have to poll. This patch also moves twl->linkstat update code to callers so that changes can be handled in thread safe way (as polling work can trigger at the same time as real irq now). TI PSP kernels have similar workarounds, so (many?) more boards are likely affected. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index f4ec53a..61fe7e2 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -163,6 +163,8 @@ struct twl4030_usb { bool vbus_supplied; u8 asleep; bool irq_enabled; + + struct delayed_work id_workaround_work; }; /* internal define on top of container_of */ @@ -287,10 +289,6 @@ static enum omap_musb_vbus_id_status * are registered, and that both are active... */ - spin_lock_irq(&twl->lock); - twl->linkstat = linkstat; - spin_unlock_irq(&twl->lock); - return linkstat; } @@ -412,6 +410,16 @@ static void twl4030_phy_resume(struct twl4030_usb *twl) __twl4030_phy_resume(twl); twl->asleep = 0; dev_dbg(twl->dev, "%s\n", __func__); + + /* + * XXX When VBUS gets driven after musb goes to A mode, + * ID_PRES related interrupts no longer arrive, why? + * Register itself is updated fine though, so we must poll. + */ + if (twl->linkstat == OMAP_MUSB_ID_GROUND) { + cancel_delayed_work(&twl->id_workaround_work); + schedule_delayed_work(&twl->id_workaround_work, HZ); + } } static int twl4030_usb_ldo_init(struct twl4030_usb *twl) @@ -483,10 +491,18 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; enum omap_musb_vbus_id_status status; - enum omap_musb_vbus_id_status status_prev = twl->linkstat; + bool status_changed = false; status = twl4030_usb_linkstat(twl); - if (status > 0 && status != status_prev) { + + spin_lock_irq(&twl->lock); + if (status >= 0 && status != twl->linkstat) { + twl->linkstat = status; + status_changed = true; + } + spin_unlock_irq(&twl->lock); + + if (status_changed) { /* FIXME add a set_power() method so that B-devices can * configure the charger appropriately. It's not always * correct to consume VBUS power, and how much current to @@ -498,13 +514,42 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - omap_musb_mailbox(twl->linkstat); + omap_musb_mailbox(status); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); return IRQ_HANDLED; } +static void twl4030_id_workaround_work(struct work_struct *work) +{ + struct twl4030_usb *twl = container_of(work, struct twl4030_usb, + id_workaround_work.work); + enum omap_musb_vbus_id_status status; + bool status_changed = false; + + status = twl4030_usb_linkstat(twl); + + spin_lock_irq(&twl->lock); + if (status >= 0 && status != twl->linkstat) { + twl->linkstat = status; + status_changed = true; + } + spin_unlock_irq(&twl->lock); + + if (status_changed) { + dev_dbg(twl->dev, "handle missing status change to %d\n", + status); + omap_musb_mailbox(status); + } + + /* don't schedule during sleep - irq works right then */ + if (status == OMAP_MUSB_ID_GROUND && !twl->asleep) { + cancel_delayed_work(&twl->id_workaround_work); + schedule_delayed_work(&twl->id_workaround_work, HZ); + } +} + static int twl4030_usb_phy_init(struct usb_phy *phy) { struct twl4030_usb *twl = phy_to_twl(phy); @@ -518,6 +563,8 @@ static int twl4030_usb_phy_init(struct usb_phy *phy) twl->asleep = 1; status = twl4030_usb_linkstat(twl); + twl->linkstat = status; + if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) omap_musb_mailbox(twl->linkstat); @@ -608,6 +655,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) /* init spinlock for workqueue */ spin_lock_init(&twl->lock); + INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); + err = twl4030_usb_ldo_init(twl); if (err) { dev_err(&pdev->dev, "ldo init failed\n"); @@ -646,6 +695,7 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev) struct twl4030_usb *twl = platform_get_drvdata(pdev); int val; + cancel_delayed_work(&twl->id_workaround_work); device_remove_file(twl->dev, &dev_attr_vbus); /* set transceiver mode to power on defaults */ -- cgit v0.10.2 From 4261b8f3538c1cae63e30110a164614ea2f10299 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 15 Mar 2013 11:04:39 +0200 Subject: usb: host: ehci-tegra: fix PHY error handling PHY layer no longer returns NULL, we must switch from IS_ERR_OR_NULL() to IS_ERR(). Acked-by: Stephen Warren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index fafbc81..1d2488c 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -768,14 +768,12 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail; } -#if IS_ENABLED(CONFIG_USB_PHY) if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(tegra->transceiver)) + if (!IS_ERR(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, &hcd->self); } -#endif err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { @@ -794,10 +792,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) return err; fail: -#if IS_ENABLED(CONFIG_USB_PHY) - if (!IS_ERR_OR_NULL(tegra->transceiver)) + if (!IS_ERR(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); -#endif usb_phy_shutdown(hcd->phy); fail_io: clk_disable_unprepare(tegra->clk); @@ -815,10 +811,8 @@ static int tegra_ehci_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); -#if IS_ENABLED(CONFIG_USB_PHY) - if (!IS_ERR_OR_NULL(tegra->transceiver)) + if (!IS_ERR(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); -#endif usb_phy_shutdown(hcd->phy); usb_remove_hcd(hcd); -- cgit v0.10.2 From 417c765af914106f5e76c4e0181dd555fe6a89a0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Mar 2013 12:48:42 -0400 Subject: USB: EHCI: fix up incorrect merge resolution This patch (as1671) fixes up an incorrect resolution of a merge conflict between Greg KH's usb-linus branch and his usb-next branch. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index dca8fc4..e736333 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -297,6 +297,15 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) { u32 cmd, status; + /* + * Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (ehci->rh_state != EHCI_RH_RUNNING) + return; + /* If we get here, IAA is *REALLY* late. It's barely * conceivable that the system is so busy that CMD_IAAD * is still legitimately set, so let's be sure it's -- cgit v0.10.2 From e5615112807c27dd256f0bd8fc8b1f6f5e1b90af Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 10 Mar 2013 02:48:55 +0200 Subject: usb: musb: core: honour initial transceiver state As the usb transceiver driver usually starts first, it should already have default_a variable set according to ID pin state, so don't override it. In case default_a was not changed by trasceiver, it will default to 0 and this code will work as before. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6bd8792..5fbabc0 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1952,9 +1952,13 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_write_ulpi_buscontrol(musb->mregs, busctl); } - MUSB_DEV_MODE(musb); - musb->xceiv->otg->default_a = 0; - musb->xceiv->state = OTG_STATE_B_IDLE; + if (musb->xceiv->otg->default_a) { + MUSB_HST_MODE(musb); + musb->xceiv->state = OTG_STATE_A_IDLE; + } else { + MUSB_DEV_MODE(musb); + musb->xceiv->state = OTG_STATE_B_IDLE; + } status = musb_gadget_setup(musb); -- cgit v0.10.2 From e21de10cd8b1338200d96038aa66fb966fcf6ed0 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 10 Mar 2013 02:49:14 +0200 Subject: usb: musb: gadget: clear gadget_driver when gadget is stopped Some musb glue drivers use gadget_driver pointer to know if any gadget drivers are loaded at some moment and base further decisions on it, like to do runtime suspend/resume or not. Right now the pointer is left alone on stop and OMAP musb glue later does wrong runtime_pm decisions because of it. Clear the gadget_driver pointer on remove, it's invalid after stop anyway. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index e840888..ff6ba3a 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1934,6 +1934,7 @@ static int musb_gadget_stop(struct usb_gadget *g, dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); musb->is_active = 0; + musb->gadget_driver = NULL; musb_platform_try_idle(musb, 0); spin_unlock_irqrestore(&musb->lock, flags); -- cgit v0.10.2 From 54485116cd77b361864c17c40c3280b97b6fbecc Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 10 Mar 2013 02:49:28 +0200 Subject: usb: musb: core: log VBUS error VBUS_ERROR is a serious error that the driver often doesn't recover from in my tests, so we should at least inform the user about it. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5fbabc0..37a261a 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -603,7 +603,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; } - dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller, + "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", usb_otg_state_string(musb->xceiv->state), devctl, ({ char *s; -- cgit v0.10.2 From 4895a2ee5d1bc88d9876c4082062c1cb3df5cdd4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 19 Mar 2013 01:28:56 +0000 Subject: usb: gadget: mv_u3d: make mv_u3d_pm_ops static Make mv_u3d_pm_ops static because mv_u3d_pm_ops is not exported. Also, CONFIG_PM_SLEEP is used to remove unnecessary ifdefs. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index e91281d..49f9da4 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -2002,7 +2002,7 @@ err_pdata: return retval; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mv_u3d_suspend(struct device *dev) { struct mv_u3d *u3d = dev_get_drvdata(dev); @@ -2045,10 +2045,10 @@ static int mv_u3d_resume(struct device *dev) return 0; } - -SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); #endif +static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); + static void mv_u3d_shutdown(struct platform_device *dev) { struct mv_u3d *u3d = dev_get_drvdata(&dev->dev); @@ -2066,9 +2066,7 @@ static struct platform_driver mv_u3d_driver = { .driver = { .owner = THIS_MODULE, .name = "mv-u3d", -#ifdef CONFIG_PM .pm = &mv_u3d_pm_ops, -#endif }, }; -- cgit v0.10.2 From a10713a59af2da6b80d9cad398fdd4f6d23a202c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 09:11:17 +0200 Subject: usb: gadget: mv_u3d: remove unused headers none of those headers are used by the driver, removing them still compiles fine. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 49f9da4..29153bf 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -30,9 +30,6 @@ #include #include #include -#include -#include -#include #include "mv_u3d.h" -- cgit v0.10.2 From af593da481aec9ce1fba8aef3ee1a2b868744968 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 09:12:23 +0200 Subject: usb: gadget: mv_u3d: remove outdated selects USB_GADGET_{DUAL,SUPER}SPEED symbols have been removed by commit 85b8614 (usb: gadget: get rid of USB_GADGET_{DUAL,SUPER}SPEED), for some reason mv_u3d_core was lost. Remove those selects now. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7ad108a..af5cc35 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -325,8 +325,6 @@ config USB_MV_UDC config USB_MV_U3D tristate "MARVELL PXA2128 USB 3.0 controller" depends on CPU_MMP3 - select USB_GADGET_DUALSPEED - select USB_GADGET_SUPERSPEED help MARVELL PXA2128 Processor series include a super speed USB3.0 device controller, which support super speed USB peripheral. -- cgit v0.10.2 From e6667ef7ac27d61dd36b1215da1e9ac719dcb769 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 09:14:25 +0200 Subject: usb: gadget: mv_u3d: fix sparse warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following sparse warnings: drivers/usb/gadget/mv_u3d_core.c: In function ‘mv_u3d_ep_queue’: drivers/usb/gadget/mv_u3d_core.c:812:2: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:822:14: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:823:4: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:823:28: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c: In function ‘mv_u3d_ep_dequeue’: drivers/usb/gadget/mv_u3d_core.c:905:14: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c: In function ‘mv_u3d_probe’: drivers/usb/gadget/mv_u3d_core.c:1840:3: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:1855:51: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:1855:17: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/usb/gadget/mv_u3d_core.c:1858:53: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] drivers/usb/gadget/mv_u3d_core.c:1858:18: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/usb/gadget/mv_u3d_core.c: In function ‘mv_u3d_irq_process_tr_complete’: drivers/usb/gadget/mv_u3d_core.c:163:21: warning: ‘remaining_length’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/usb/gadget/mv_u3d_core.c:125:28: note: ‘remaining_length’ was declared here Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 29153bf..5a6f87c 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -122,7 +122,7 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, struct mv_u3d_trb *curr_trb; dma_addr_t cur_deq_lo; struct mv_u3d_ep_context *curr_ep_context; - int trb_complete, actual, remaining_length; + int trb_complete, actual, remaining_length = 0; int direction, ep_num; int retval = 0; u32 tmp, status, length; @@ -809,19 +809,19 @@ mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return 0; } - dev_dbg(u3d->dev, "%s: %s, req: 0x%x\n", - __func__, _ep->name, (u32)req); + dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n", + __func__, _ep->name, req); /* catch various bogus parameters */ if (!req->req.complete || !req->req.buf || !list_empty(&req->queue)) { dev_err(u3d->dev, - "%s, bad params, _req: 0x%x," - "req->req.complete: 0x%x, req->req.buf: 0x%x," + "%s, bad params, _req: 0x%p," + "req->req.complete: 0x%p, req->req.buf: 0x%p," "list_empty: 0x%x\n", - __func__, (u32)_req, - (u32)req->req.complete, (u32)req->req.buf, - (u32)list_empty(&req->queue)); + __func__, _req, + req->req.complete, req->req.buf, + list_empty(&req->queue)); return -EINVAL; } if (unlikely(!ep->ep.desc)) { @@ -902,7 +902,7 @@ static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct mv_u3d_req, queue); /* Point first TRB of next request to the EP context. */ - iowrite32((u32) next_req->trb_head, + iowrite32((unsigned long) next_req->trb_head, &ep_context->trb_addr_lo); } else { struct mv_u3d_ep_context *ep_context; @@ -1837,8 +1837,9 @@ static int mv_u3d_probe(struct platform_device *dev) retval = -EBUSY; goto err_map_cap_regs; } else { - dev_dbg(&dev->dev, "cap_regs address: 0x%x/0x%x\n", - (unsigned int)r->start, (unsigned int)u3d->cap_regs); + dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n", + (unsigned long) r->start, + (unsigned long) u3d->cap_regs); } /* we will access controller register, so enable the u3d controller */ @@ -1852,10 +1853,10 @@ static int mv_u3d_probe(struct platform_device *dev) } } - u3d->op_regs = (struct mv_u3d_op_regs __iomem *)((u32)u3d->cap_regs + u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs + MV_U3D_USB3_OP_REGS_OFFSET); - u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)((u32)u3d->cap_regs + u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs + ioread32(&u3d->cap_regs->vuoff)); u3d->max_eps = 16; -- cgit v0.10.2 From 303f0847925ece27129487a2bfc05199ab2a0b51 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:53 +0800 Subject: USB: adds comment on suspend callback This patch adds comments on interface driver suspend callback to emphasize that the failure return value is ignored by USB core in system sleep context, so do not try to recover device for this case and let resume/reset_resume callback handle the suspend failure if needed. Also kerneldoc for usb_suspend_both() is updated with the fact. Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d938b2b..eb1d00a 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1196,9 +1196,14 @@ done: * * This is the central routine for suspending USB devices. It calls the * suspend methods for all the interface drivers in @udev and then calls - * the suspend method for @udev itself. If an error occurs at any stage, - * all the interfaces which were suspended are resumed so that they remain - * in the same state as the device. + * the suspend method for @udev itself. When the routine is called in + * autosuspend, if an error occurs at any stage, all the interfaces + * which were suspended are resumed so that they remain in the same + * state as the device, but when called from system sleep, all error + * from suspend methods of interfaces and the non-root-hub device itself + * are simply ignored, so all suspended interfaces are only resumed + * to the device's state when @udev is root-hub and its suspend method + * returns failure. * * Autosuspend requests originating from a child device or an interface * driver may be made without the protection of @udev's device lock, but diff --git a/include/linux/usb.h b/include/linux/usb.h index 52464fb..8d4bc17 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -976,7 +976,12 @@ struct usbdrv_wrap { * the "usbfs" filesystem. This lets devices provide ways to * expose information to user space regardless of where they * do (or don't) show up otherwise in the filesystem. - * @suspend: Called when the device is going to be suspended by the system. + * @suspend: Called when the device is going to be suspended by the + * system either from system sleep or runtime suspend context. The + * return value will be ignored in system sleep context, so do NOT + * try to continue using the device if suspend fails in this case. + * Instead, let the resume or reset-resume routine recover from + * the failure. * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. -- cgit v0.10.2 From 93e4f47f4d1a3483f009202e8a66a3a08de5c4b6 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:54 +0800 Subject: USB: serial: comments on suspend failure If suspend callback fails in system sleep context, usb core will ignore the failure and let system sleep go ahead further, so this patch comments on the case and requires that serial->type->suspend() MUST return 0 in system sleep context. Acked-by: Johan Hovold Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index bbe7f2e..2fc0500 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1137,6 +1137,11 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) serial->suspending = 1; + /* + * serial->type->suspend() MUST return 0 in system sleep context, + * otherwise, the resume callback has to recover device from + * previous suspend failure. + */ if (serial->type->suspend) { r = serial->type->suspend(serial, message); if (r < 0) { -- cgit v0.10.2 From 37093b7017a281b85dc523fdcf99418a564e1cd4 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:55 +0800 Subject: USBHID: don't recover device if suspend fails in system sleep If suspend callback fails in system sleep context, usb core will ignore the failure and let the system sleep go ahead further, so this patch doesn't recover device under this situation, otherwise may cause resume() confused. Acked-by: Jiri Kosina Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 8e0c4bf94..1f9e56b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1493,7 +1493,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; - int status; + int status = 0; bool driver_suspended = false; if (PMSG_IS_AUTO(message)) { @@ -1520,19 +1520,15 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) } } else { - if (hid->driver && hid->driver->suspend) { + /* TODO: resume() might need to handle suspend failure */ + if (hid->driver && hid->driver->suspend) status = hid->driver->suspend(hid, message); - if (status < 0) - return status; - } driver_suspended = true; spin_lock_irq(&usbhid->lock); set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - if (usbhid_wait_io(hid) < 0) { + if (usbhid_wait_io(hid) < 0) status = -EIO; - goto failed; - } } hid_cancel_delayed_stuff(usbhid); @@ -1544,7 +1540,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) goto failed; } dev_dbg(&intf->dev, "suspend\n"); - return 0; + return status; failed: hid_resume_common(hid, driver_suspended); -- cgit v0.10.2 From 9a44312a79786c2fefac8e298a99826e9dc21f17 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:56 +0800 Subject: usbnet: cdc_mbim: comments on suspend failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If suspend callback fails in system sleep context, usb core will ignore the failure and let system sleep go ahead further, so this patch comments on the case and requires that both usbnet_suspend() and subdriver->suspend() MUST return 0 in system sleep context. Acked-by: David S. Miller Acked-by: Bjørn Mork Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 16c8429..673e362 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -323,6 +323,11 @@ static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message) goto error; } + /* + * Both usbnet_suspend() and subdriver->suspend() MUST return 0 + * in system sleep context, otherwise, the resume callback has + * to recover device from previous suspend failure. + */ ret = usbnet_suspend(intf, message); if (ret < 0) goto error; -- cgit v0.10.2 From 81b50be036f91c8502e960c33dca04ac19f96baf Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:57 +0800 Subject: usbnet: qmi_wwan: comments on suspend failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If suspend callback fails in system sleep context, usb core will ignore the failure and let system sleep go ahead further, so this patch comments on the case and requires that both usbnet_suspend() and subdriver->suspend() MUST return 0 in system sleep context. Acked-by: David S. Miller Acked-by: Bjørn Mork Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 968d5d5..bdceb74 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -271,6 +271,11 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) struct qmi_wwan_state *info = (void *)&dev->data; int ret; + /* + * Both usbnet_suspend() and subdriver->suspend() MUST return 0 + * in system sleep context, otherwise, the resume callback has + * to recover device from previous suspend failure. + */ ret = usbnet_suspend(intf, message); if (ret < 0) goto err; -- cgit v0.10.2 From 0d41be53f0a65c52f3099d9a4b4fa6e1bca07be8 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:58 +0800 Subject: usbnet: smsc95xx: don't recover device if suspend fails in system sleep If suspend callback fails in system sleep context, usb core will ignore the failure and let system sleep go ahead further, so this patch doesn't recover device under this situation. Also add comments on the case. Acked-by: David S. Miller Cc: Steve Glendinning Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index e6d2dea..3f38ba8 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1660,7 +1660,11 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ret = smsc95xx_enter_suspend0(dev); done: - if (ret) + /* + * TODO: resume() might need to handle the suspend failure + * in system sleep + */ + if (ret && PMSG_IS_AUTO(message)) usbnet_resume(intf); return ret; } -- cgit v0.10.2 From 5410a473fb7a466020af31e8b400c2d14ed94925 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 15 Mar 2013 12:08:59 +0800 Subject: usbnet: smsc75xx: don't recover device if suspend fails in system sleep If suspend callback fails in system sleep context, usb core will ignore the failure and let system sleep go ahead further, so this patch doesn't recover device under this situation. Also add comments on this case. Acked-by: David S. Miller Cc: Steve Glendinning Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 9abe517..21b607a 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -2011,7 +2011,11 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) ret = smsc75xx_enter_suspend0(dev); done: - if (ret) + /* + * TODO: resume() might need to handle the suspend failure + * in system sleep + */ + if (ret && PMSG_IS_AUTO(message)) usbnet_resume(intf); return ret; } -- cgit v0.10.2 From 769d7368b1727b1b5369d88badf0cbdf0163e079 Mon Sep 17 00:00:00 2001 From: David Linares Date: Mon, 25 Mar 2013 10:50:27 +0000 Subject: USB: hub: Avoid NULL pointer dereference when hub doesn't have any ports Return an error if hub->descriptor->bNbrPorts==0. Without this additional check, we can end up doing a "hub->ports = kzalloc(0, GFP_KERNEL)". This hub->ports pointer will therefore be non-NULL and will be used. Example of dmesg: INIT: usb 1-1: New USB device found, idVendor=0424, idProduct=2512 usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 hub 1-1:1.0: USB hub found version 2.86 bootinghub 1-1:1.0: 0 ports detected Unable to handle kernel NULL pointer dereference at virtual address 00000010 Signed-off-by: David Linares Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5480352..7815462 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1317,6 +1317,10 @@ static int hub_configure(struct usb_hub *hub, message = "hub has too many ports!"; ret = -ENODEV; goto fail; + } else if (hub->descriptor->bNbrPorts == 0) { + message = "hub doesn't have any ports!"; + ret = -ENODEV; + goto fail; } hdev->maxchild = hub->descriptor->bNbrPorts; -- cgit v0.10.2 From 3edce1cf813aa6a087df7730cec0e67d57288300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Sun, 17 Mar 2013 21:00:06 +0100 Subject: USB: cdc-wdm: implement IOCTL_WDM_MAX_COMMAND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace applications need to know the maximum supported message size. The cdc-wdm driver translates between a character device stream and a message based protocol. Each message is transported as a usb control message with no further encapsulation or syncronization. Each read or write on the character device should translate to exactly one usb control message to ensure that message boundaries are kept intact. That means that the userspace application must know the maximum message size supported by the device and driver, making this size a vital part of the cdc-wdm character device API. CDC WDM and CDC MBIM functions export the maximum supported message size through CDC functional descriptors. The cdc-wdm and cdc_mbim drivers will parse these descriptors and use the value chosen by the device. The only current way for a userspace application to retrive the value is by duplicating the descriptor parsing. This is an unnecessary complex task, and application writers are likely to postpone it, using a fixed value and adding a "todo" item. QMI functions have no way to tell the host what message size they support. The qmi_wwan driver use a fixed value based on protocol recommendations and observed device behaviour. Userspace applications must know and hard code the same value. This scheme will break if we ever encounter a QMI device needing a device specific message size quirk. We are currently unable to support such a device because using a non default size would break the implicit userspace API. The message size is currently a hidden attribute of the cdc-wdm userspace API. Retrieving it is unnecessarily complex, increasing the possibility of drivers and applications using different limits. The resulting errors are hard to debug, and can only be replicated on identical hardware. Exporting the maximum message size from the driver simplifies the task for the userspace application, and creates a unified information source independent of device and function class. It also serves to document that the message size is part of the cdc-wdm userspace API. This proposed API extension has been presented for the authors of userspace applications and libraries using the current API: libmbim, libqmi, uqmi, oFono and ModemManager. The replies were: Aleksander Morgado: "We do really need max message size for MBIM; and as you say, it may be good to have the max message size info also for QMI, so the new ioctl seems a good addition. So +1 from my side, for what it's worth." Dan Williams: "Yeah, +1 here. I'd prefer the sysfs file, but the fact that that doesn't work for fd passing pretty much kills it." No negative replies are so far received. Cc: Aleksander Morgado Cc: Dan Williams Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 3210540..237acab 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -131,6 +131,7 @@ Code Seq#(hex) Include File Comments 'H' 40-4F sound/hdspm.h conflict! 'H' 40-4F sound/hdsp.h conflict! 'H' 90 sound/usb/usx2y/usb_stream.h +'H' A0 uapi/linux/usb/cdc-wdm.h 'H' C0-F0 net/bluetooth/hci.h conflict! 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 122d056..8a230f0 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include #include @@ -644,6 +645,22 @@ static int wdm_release(struct inode *inode, struct file *file) return 0; } +static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct wdm_device *desc = file->private_data; + int rv = 0; + + switch (cmd) { + case IOCTL_WDM_MAX_COMMAND: + if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand))) + rv = -EFAULT; + break; + default: + rv = -ENOTTY; + } + return rv; +} + static const struct file_operations wdm_fops = { .owner = THIS_MODULE, .read = wdm_read, @@ -652,6 +669,8 @@ static const struct file_operations wdm_fops = { .flush = wdm_flush, .release = wdm_release, .poll = wdm_poll, + .unlocked_ioctl = wdm_ioctl, + .compat_ioctl = wdm_ioctl, .llseek = noop_llseek, }; diff --git a/include/linux/usb/cdc-wdm.h b/include/linux/usb/cdc-wdm.h index 719c332..0b3f429 100644 --- a/include/linux/usb/cdc-wdm.h +++ b/include/linux/usb/cdc-wdm.h @@ -11,6 +11,8 @@ #ifndef __LINUX_USB_CDC_WDM_H #define __LINUX_USB_CDC_WDM_H +#include + extern struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, int bufsize, diff --git a/include/uapi/linux/usb/cdc-wdm.h b/include/uapi/linux/usb/cdc-wdm.h new file mode 100644 index 0000000..f03134f --- /dev/null +++ b/include/uapi/linux/usb/cdc-wdm.h @@ -0,0 +1,21 @@ +/* + * USB CDC Device Management userspace API definitions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef _UAPI__LINUX_USB_CDC_WDM_H +#define _UAPI__LINUX_USB_CDC_WDM_H + +/* + * This IOCTL is used to retrieve the wMaxCommand for the device, + * defining the message limit for both reading and writing. + * + * For CDC WDM functions this will be the wMaxCommand field of the + * Device Management Functional Descriptor. + */ +#define IOCTL_WDM_MAX_COMMAND _IOR('H', 0xA0, __u16) + +#endif /* _UAPI__LINUX_USB_CDC_WDM_H */ -- cgit v0.10.2 From c79041a44045a40329d9ada3f8679c4b30c5b76b Mon Sep 17 00:00:00 2001 From: Du Xing Date: Wed, 20 Mar 2013 20:47:46 +0800 Subject: USB: usb-skeleton.c: fix blocked forever in skel_read In skel_read,the reader blocked in wait_for_completion before submit bulk in urb. Using processed_urb is for retaining the completion in the case that previous interruptible wait in skel_read was interrupted and complete before next skel_read. Replacing completion with waitqueue can avoid working around the counting nature of completions and fix the bug. Signed-off-by: Du Xing duxing2007@gmail.com Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index ce31017..7ed3b03 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -61,11 +61,10 @@ struct usb_skel { __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ int errors; /* the last request tanked */ bool ongoing_read; /* a read is going on */ - bool processed_urb; /* indicates we haven't processed the urb */ spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ - struct completion bulk_in_completion; /* to wait for an ongoing read */ + wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) @@ -185,7 +184,7 @@ static void skel_read_bulk_callback(struct urb *urb) dev->ongoing_read = 0; spin_unlock(&dev->err_lock); - complete(&dev->bulk_in_completion); + wake_up_interruptible(&dev->bulk_in_wait); } static int skel_do_read_io(struct usb_skel *dev, size_t count) @@ -206,13 +205,16 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count) dev->ongoing_read = 1; spin_unlock_irq(&dev->err_lock); + /* submit bulk in urb, which means no data to deliver */ + dev->bulk_in_filled = 0; + dev->bulk_in_copied = 0; + /* do it */ rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); if (rv < 0) { dev_err(&dev->interface->dev, "%s - failed submitting read urb, error %d\n", __func__, rv); - dev->bulk_in_filled = 0; rv = (rv == -ENOMEM) ? rv : -EIO; spin_lock_irq(&dev->err_lock); dev->ongoing_read = 0; @@ -261,25 +263,9 @@ retry: * IO may take forever * hence wait in an interruptible state */ - rv = wait_for_completion_interruptible(&dev->bulk_in_completion); + rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); if (rv < 0) goto exit; - /* - * by waiting we also semiprocessed the urb - * we must finish now - */ - dev->bulk_in_copied = 0; - dev->processed_urb = 1; - } - - if (!dev->processed_urb) { - /* - * the URB hasn't been processed - * do it now - */ - wait_for_completion(&dev->bulk_in_completion); - dev->bulk_in_copied = 0; - dev->processed_urb = 1; } /* errors must be reported */ @@ -289,8 +275,6 @@ retry: dev->errors = 0; /* to preserve notifications about reset */ rv = (rv == -EPIPE) ? rv : -EIO; - /* no data to deliver */ - dev->bulk_in_filled = 0; /* report it */ goto exit; } @@ -526,7 +510,7 @@ static int skel_probe(struct usb_interface *interface, mutex_init(&dev->io_mutex); spin_lock_init(&dev->err_lock); init_usb_anchor(&dev->submitted); - init_completion(&dev->bulk_in_completion); + init_waitqueue_head(&dev->bulk_in_wait); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; -- cgit v0.10.2 From c1fdb68e3d73741630ca16695cf9176c233be7ed Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:30:43 -0400 Subject: USB: EHCI: changes related to qh_refresh() This patch (as1638) makes several changes to the ehci-hcd driver, all related to the qh_refresh() function. This function must be called whenever an idle QH gets linked back into either the async or the periodic schedule. Change a BUG_ON() in the qh_update routine to a WARN_ON(). Since this code runs in atomic context, a BUG_ON() would immediately freeze the whole system. Remove two unneeded calls to qh_refresh(), one when a QH is initialized and one when a QH becomes idle. Adjust the adjacent comments accordingly. Move the qh_refresh() and qh_link_periodic() calls for new interrupt URBs to after the new TDs have been added. As a result of the previous two changes, qh_refresh() is never called when the qtd_list is empty. The corresponding check in qh_refresh() can be removed, along with an indentation level. These changes should not cause any alteration of behavior. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 23d1369..b824cb6 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -90,7 +90,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) struct ehci_qh_hw *hw = qh->hw; /* writes to an active overlay are unsafe */ - BUG_ON(qh->qh_state != QH_STATE_IDLE); + WARN_ON(qh->qh_state != QH_STATE_IDLE); hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); hw->hw_alt_next = EHCI_LIST_END(ehci); @@ -123,26 +123,19 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) { struct ehci_qtd *qtd; - if (list_empty (&qh->qtd_list)) - qtd = qh->dummy; - else { - qtd = list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list); - /* - * first qtd may already be partially processed. - * If we come here during unlink, the QH overlay region - * might have reference to the just unlinked qtd. The - * qtd is updated in qh_completions(). Update the QH - * overlay here. - */ - if (qh->hw->hw_token & ACTIVE_BIT(ehci)) { - qh->hw->hw_qtd_next = qtd->hw_next; - qtd = NULL; - } - } + qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list); - if (qtd) - qh_update (ehci, qh, qtd); + /* + * first qtd may already be partially processed. + * If we come here during unlink, the QH overlay region + * might have reference to the just unlinked qtd. The + * qtd is updated in qh_completions(). Update the QH + * overlay here. + */ + if (qh->hw->hw_token & ACTIVE_BIT(ehci)) + qh->hw->hw_qtd_next = qtd->hw_next; + else + qh_update(ehci, qh, qtd); } /*-------------------------------------------------------------------------*/ @@ -553,12 +546,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * overlaying the dummy qtd (which reduces DMA chatter). */ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) { - switch (state) { - case QH_STATE_IDLE: - qh_refresh(ehci, qh); - break; - case QH_STATE_LINKED: - /* We won't refresh a QH that's linked (after the HC + if (state == QH_STATE_LINKED) { + /* + * We won't refresh a QH that's linked (after the HC * stopped the queue). That avoids a race: * - HC reads first part of QH; * - CPU updates that first part and the token; @@ -568,13 +558,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * * That should be rare for interrupt transfers, * except maybe high bandwidth ... + * + * Therefore tell the caller to start an unlink. */ - - /* Tell the caller to start an unlink */ qh->needs_rescan = 1; - break; - /* otherwise, unlink already started */ } + /* otherwise, unlink already started */ } return count; @@ -957,14 +946,13 @@ done: /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ - /* init as live, toggle clear, advance to dummy */ + /* init as live, toggle clear */ qh->qh_state = QH_STATE_IDLE; hw = qh->hw; hw->hw_info1 = cpu_to_hc32(ehci, info1); hw->hw_info2 = cpu_to_hc32(ehci, info2); qh->is_out = !is_input; usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); - qh_refresh (ehci, qh); return qh; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b476daf..66259dc 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -792,7 +792,6 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ struct ehci_qh_hw *hw = qh->hw; - qh_refresh(ehci, qh); hw->hw_next = EHCI_LIST_END(ehci); frame = qh->start; @@ -844,8 +843,6 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); - /* stuff into the periodic schedule */ - qh_link_periodic(ehci, qh); done: return status; } @@ -891,6 +888,12 @@ static int intr_submit ( qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv); BUG_ON (qh == NULL); + /* stuff into the periodic schedule */ + if (qh->qh_state == QH_STATE_IDLE) { + qh_refresh(ehci, qh); + qh_link_periodic(ehci, qh); + } + /* ... update usbfs periodic stats */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++; -- cgit v0.10.2 From 79bcf7b02ba3d45bafe81a2753cedb8ef49548e3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:30:56 -0400 Subject: USB: EHCI: change return value of qh_completions() This patch (as1658) cleans up the usage of qh_completions() in ehci-hcd. Currently the function's return value indicates whether any URBs were given back; the idea was that the caller can scan the QH over again to handle any URBs that were dequeued by a completion handler. This is not necessary; when qh_completions() is ready to give back dequeued URBs, it does its own rescanning. Therefore the new return value will be a flag indicating whether the caller needs to unlink the QH. This is more convenient than forcing the caller to check qh->needs_rescan, and it makes a lot more sense -- why should "needs_rescan" imply that an unlink is needed? The callers are also changed to remove the unneeded rescans. Lastly, the check for whether qh->qtd_list is non-empty is removed from the start of qh_completions(). Two of the callers have to make this test anyway, so the same test can simply be added to the other two callers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b824cb6..c95f60d 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -292,8 +292,8 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* * Process and free completed qtds for a qh, returning URBs to drivers. - * Chases up to qh->hw_current. Returns number of completions called, - * indicating how much "real" work we did. + * Chases up to qh->hw_current. Returns nonzero if the caller should + * unlink qh. */ static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) @@ -302,13 +302,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) struct list_head *entry, *tmp; int last_status; int stopped; - unsigned count = 0; u8 state; struct ehci_qh_hw *hw = qh->hw; - if (unlikely (list_empty (&qh->qtd_list))) - return count; - /* completions (or tasks on other cpus) must never clobber HALT * till we've gone through and cleaned everything up, even when * they add urbs to this qh's queue or mark them for unlinking. @@ -345,7 +341,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) if (last) { if (likely (last->urb != urb)) { ehci_urb_done(ehci, last->urb, last_status); - count++; last_status = -EINPROGRESS; } ehci_qtd_free (ehci, last); @@ -519,7 +514,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* last urb's completion might still need calling */ if (likely (last != NULL)) { ehci_urb_done(ehci, last->urb, last_status); - count++; ehci_qtd_free (ehci, last); } @@ -566,7 +560,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* otherwise, unlink already started */ } - return count; + return qh->needs_rescan; } /*-------------------------------------------------------------------------*/ @@ -1254,7 +1248,8 @@ static void end_unlink_async(struct ehci_hcd *ehci) qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; - qh_completions(ehci, qh); + if (!list_empty(&qh->qtd_list)) + qh_completions(ehci, qh); if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async(ehci, qh); @@ -1348,7 +1343,7 @@ static void scan_async (struct ehci_hcd *ehci) while (ehci->qh_scan_next) { qh = ehci->qh_scan_next; ehci->qh_scan_next = qh->qh_next.qh; - rescan: + /* clean any finished work for this qh */ if (!list_empty(&qh->qtd_list)) { int temp; @@ -1361,14 +1356,13 @@ static void scan_async (struct ehci_hcd *ehci) * in single_unlink_async(). */ temp = qh_completions(ehci, qh); - if (qh->needs_rescan) { + if (unlikely(temp)) { start_unlink_async(ehci, qh); } else if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { qh->unlink_cycle = ehci->async_unlink_cycle; check_unlinks_later = true; - } else if (temp != 0) - goto rescan; + } } } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 66259dc..5c82bba 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -649,7 +649,8 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_IDLE; hw->hw_next = EHCI_LIST_END(ehci); - qh_completions(ehci, qh); + if (!list_empty(&qh->qtd_list)) + qh_completions(ehci, qh); /* reschedule QH iff another request is queued */ if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { @@ -914,7 +915,7 @@ static void scan_intr(struct ehci_hcd *ehci) list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list, intr_node) { - rescan: + /* clean any finished work for this qh */ if (!list_empty(&qh->qtd_list)) { int temp; @@ -927,12 +928,9 @@ static void scan_intr(struct ehci_hcd *ehci) * in qh_unlink_periodic(). */ temp = qh_completions(ehci, qh); - if (unlikely(qh->needs_rescan || - (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED))) + if (unlikely(temp || (list_empty(&qh->qtd_list) && + qh->qh_state == QH_STATE_LINKED))) start_unlink_intr(ehci, qh); - else if (temp != 0) - goto rescan; } } } -- cgit v0.10.2 From 7bc782d73c7db042ecc965866b8c1c2b7d6e93d6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:31:11 -0400 Subject: USB: EHCI: split needs_rescan into two flags This patch (as1662) does some more QH-related cleanup in ehci-hcd. The qh->needs_rescan flag is currently used for two different purposes; the patch replaces it with two separate flags for greater clarity: qh->dequeue_during_giveback indicates that a completion handler dequeued an URB (implying that a rescan is needed), and qh->exception indicates that the QH is in an exceptional state requiring an unlink (either it encountered an I/O error or an unlink was requested). The new flags get set where the dequeue, exception, or unlink request occurred, rather than where the unlink is started. This is so that in the future, if we need to, we will be able to tell apart unlinks that truly were required from those that were carried out merely because the QH wasn't being used. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 0c3314c..93f4cd7 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -903,11 +903,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; + qh->exception = 1; switch (qh->qh_state) { case QH_STATE_LINKED: - case QH_STATE_COMPLETING: start_unlink_async(ehci, qh); break; + case QH_STATE_COMPLETING: + qh->dequeue_during_giveback = 1; + break; case QH_STATE_UNLINK: case QH_STATE_UNLINK_WAIT: /* already started */ @@ -923,11 +926,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; + qh->exception = 1; switch (qh->qh_state) { case QH_STATE_LINKED: - case QH_STATE_COMPLETING: start_unlink_intr(ehci, qh); break; + case QH_STATE_COMPLETING: + qh->dequeue_during_giveback = 1; + break; case QH_STATE_IDLE: qh_completions (ehci, qh); break; @@ -984,6 +990,7 @@ rescan: goto done; } + qh->exception = 1; if (ehci->rh_state < EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { @@ -1052,13 +1059,12 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) usb_settoggle(qh->dev, epnum, is_out, 0); if (!list_empty(&qh->qtd_list)) { WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_LINKED || - qh->qh_state == QH_STATE_COMPLETING) { - + } else { /* The toggle value in the QH can't be updated * while the QH is active. Unlink it now; * re-linking will call qh_refresh(). */ + qh->exception = 1; if (eptype == USB_ENDPOINT_XFER_BULK) start_unlink_async(ehci, qh); else diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index c95f60d..fca741d 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -322,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) rescan: last = NULL; last_status = -EINPROGRESS; - qh->needs_rescan = 0; + qh->dequeue_during_giveback = 0; /* remove de-activated QTDs from front of queue. * after faults (including short reads), cleanup this urb @@ -518,18 +518,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) } /* Do we need to rescan for URBs dequeued during a giveback? */ - if (unlikely(qh->needs_rescan)) { + if (unlikely(qh->dequeue_during_giveback)) { /* If the QH is already unlinked, do the rescan now. */ if (state == QH_STATE_IDLE) goto rescan; - /* Otherwise we have to wait until the QH is fully unlinked. - * Our caller will start an unlink if qh->needs_rescan is - * set. But if an unlink has already started, nothing needs - * to be done. - */ - if (state != QH_STATE_LINKED) - qh->needs_rescan = 0; + /* Otherwise the caller must unlink the QH. */ } /* restore original state; caller must unlink or relink */ @@ -538,29 +532,23 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* be sure the hardware's done with the qh before refreshing * it after fault cleanup, or recovering from silicon wrongly * overlaying the dummy qtd (which reduces DMA chatter). + * + * We won't refresh a QH that's linked (after the HC + * stopped the queue). That avoids a race: + * - HC reads first part of QH; + * - CPU updates that first part and the token; + * - HC reads rest of that QH, including token + * Result: HC gets an inconsistent image, and then + * DMAs to/from the wrong memory (corrupting it). + * + * That should be rare for interrupt transfers, + * except maybe high bandwidth ... */ - if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) { - if (state == QH_STATE_LINKED) { - /* - * We won't refresh a QH that's linked (after the HC - * stopped the queue). That avoids a race: - * - HC reads first part of QH; - * - CPU updates that first part and the token; - * - HC reads rest of that QH, including token - * Result: HC gets an inconsistent image, and then - * DMAs to/from the wrong memory (corrupting it). - * - * That should be rare for interrupt transfers, - * except maybe high bandwidth ... - * - * Therefore tell the caller to start an unlink. - */ - qh->needs_rescan = 1; - } - /* otherwise, unlink already started */ - } + if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) + qh->exception = 1; - return qh->needs_rescan; + /* Let the caller know if the QH needs to be unlinked. */ + return qh->exception; } /*-------------------------------------------------------------------------*/ @@ -1002,8 +990,9 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) head->qh_next.qh = qh; head->hw->hw_next = dma; - qh->xacterrs = 0; qh->qh_state = QH_STATE_LINKED; + qh->xacterrs = 0; + qh->exception = 0; /* qtd completions reported later by interrupt */ enable_async(ehci); @@ -1317,16 +1306,9 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { - /* - * If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; + /* If the QH isn't linked then there's nothing we can do. */ + if (qh->qh_state != QH_STATE_LINKED) return; - } single_unlink_async(ehci, qh); start_iaa_cycle(ehci, false); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 5c82bba..e7a2dbd 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -539,6 +539,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_LINKED; qh->xacterrs = 0; + qh->exception = 0; /* update per-qh bandwidth for usbfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period @@ -602,15 +603,9 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) { - /* If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; + /* If the QH isn't linked then there's nothing we can do. */ + if (qh->qh_state != QH_STATE_LINKED) return; - } qh_unlink_periodic (ehci, qh); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 36c3a82..6815209 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -384,7 +384,6 @@ struct ehci_qh { unsigned unlink_cycle; - u8 needs_rescan; /* Dequeue during giveback */ u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ @@ -407,6 +406,9 @@ struct ehci_qh { struct usb_device *dev; /* access to TT */ unsigned is_out:1; /* bulk or intr OUT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ + unsigned dequeue_during_giveback:1; + unsigned exception:1; /* got a fault, or an unlink + was requested */ }; /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 7655e3160c78a18c2ecf7bf4dee0bbfe58575c7f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:31:29 -0400 Subject: USB: EHCI: consolidate code in ehci_urb_dequeue() This patch (as1668) consolidates two nearly identical code paths in ehci_urb_dequeue(). The test for !qh can be removed because it will never succeed; the fact that usb_hcd_check_unlink_urb() returned 0 means that urb must be queued and therefore urb->hcpriv must point to a QH. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 93f4cd7..87fe3da 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -896,17 +896,21 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (rc) goto done; - switch (usb_pipetype (urb->pipe)) { - // case PIPE_CONTROL: - // case PIPE_BULK: - default: + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * We don't expedite dequeue for isochronous URBs. + * Just wait until they complete normally or their + * time slot expires. + */ + } else { qh = (struct ehci_qh *) urb->hcpriv; - if (!qh) - break; qh->exception = 1; switch (qh->qh_state) { case QH_STATE_LINKED: - start_unlink_async(ehci, qh); + if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) + start_unlink_intr(ehci, qh); + else + start_unlink_async(ehci, qh); break; case QH_STATE_COMPLETING: qh->dequeue_during_giveback = 1; @@ -920,36 +924,6 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh_completions(ehci, qh); break; } - break; - - case PIPE_INTERRUPT: - qh = (struct ehci_qh *) urb->hcpriv; - if (!qh) - break; - qh->exception = 1; - switch (qh->qh_state) { - case QH_STATE_LINKED: - start_unlink_intr(ehci, qh); - break; - case QH_STATE_COMPLETING: - qh->dequeue_during_giveback = 1; - break; - case QH_STATE_IDLE: - qh_completions (ehci, qh); - break; - default: - ehci_dbg (ehci, "bogus qh %p state %d\n", - qh, qh->qh_state); - goto done; - } - break; - - case PIPE_ISOCHRONOUS: - // itd or sitd ... - - // wait till next completion, do it then. - // completion irqs can wait up to 1024 msec, - break; } done: spin_unlock_irqrestore (&ehci->lock, flags); -- cgit v0.10.2 From 6e018751a35f6ef7ad04eb8006b5886b6a7c47f5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:31:45 -0400 Subject: USB: EHCI: convert singly-linked lists to list_heads This patch (as1664) converts ehci-hcd's async_unlink, async_iaa, and intr_unlink from singly-linked lists to standard doubly-linked list_heads. Originally it didn't seem necessary to use list_heads, because items are always added to and removed from these lists in FIFO order. But now with more list processing going on, it's easier to use the standard routines than continue with a roll-your-own approach. I don't know if the code ends up being notably shorter, but the patterns will be more familiar to any kernel hacker. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 70b496d..5429d26 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -510,14 +510,16 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) qh_lines (ehci, qh, &next, &size); - if (ehci->async_unlink && size > 0) { + if (!list_empty(&ehci->async_unlink) && size > 0) { temp = scnprintf(next, size, "\nunlink =\n"); size -= temp; next += temp; - for (qh = ehci->async_unlink; size > 0 && qh; - qh = qh->unlink_next) - qh_lines (ehci, qh, &next, &size); + list_for_each_entry(qh, &ehci->async_unlink, unlink_node) { + if (size <= 0) + break; + qh_lines(ehci, qh, &next, &size); + } } spin_unlock_irqrestore (&ehci->lock, flags); @@ -814,9 +816,10 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } } - if (ehci->async_unlink) { + if (!list_empty(&ehci->async_unlink)) { temp = scnprintf(next, size, "async unlink qh %p\n", - ehci->async_unlink); + list_first_entry(&ehci->async_unlink, + struct ehci_qh, unlink_node)); size -= temp; next += temp; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 87fe3da..b32323c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -482,6 +482,9 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->async_unlink); + INIT_LIST_HEAD(&ehci->async_iaa); + INIT_LIST_HEAD(&ehci->intr_unlink); INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list); @@ -749,7 +752,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (ehci->async_iaa) + if (!list_empty(&ehci->async_iaa)) COUNT(ehci->stats.iaa); end_unlink_async(ehci); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index fca741d..4a01367 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -958,8 +958,9 @@ static void disable_async(struct ehci_hcd *ehci) if (--ehci->async_count) return; - /* The async schedule and async_unlink list are supposed to be empty */ - WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink); + /* The async schedule and unlink lists are supposed to be empty */ + WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) || + !list_empty(&ehci->async_iaa)); /* Don't turn off the schedule until ASS is 1 */ ehci_poll_ASS(ehci); @@ -1150,11 +1151,7 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) /* Add to the end of the list of QHs waiting for the next IAAD */ qh->qh_state = QH_STATE_UNLINK_WAIT; - if (ehci->async_unlink) - ehci->async_unlink_last->unlink_next = qh; - else - ehci->async_unlink = qh; - ehci->async_unlink_last = qh; + list_add_tail(&qh->unlink_node, &ehci->async_unlink); /* Unlink it from the schedule */ prev = ehci->async; @@ -1173,15 +1170,14 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) * Do nothing if an IAA cycle is already running or * if one will be started shortly. */ - if (ehci->async_iaa || ehci->async_unlinking) + if (!list_empty(&ehci->async_iaa) || ehci->async_unlinking) return; /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { /* Do all the waiting QHs */ - ehci->async_iaa = ehci->async_unlink; - ehci->async_unlink = NULL; + list_splice_tail_init(&ehci->async_unlink, &ehci->async_iaa); if (!nested) /* Avoid recursion */ end_unlink_async(ehci); @@ -1191,20 +1187,18 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) struct ehci_qh *qh; /* Do only the first waiting QH (nVidia bug?) */ - qh = ehci->async_unlink; + qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, + unlink_node); /* * Intel (?) bug: The HC can write back the overlay region * even after the IAA interrupt occurs. In self-defense, * always go through two IAA cycles for each QH. */ - if (qh->qh_state == QH_STATE_UNLINK_WAIT) { + if (qh->qh_state == QH_STATE_UNLINK_WAIT) qh->qh_state = QH_STATE_UNLINK; - } else { - ehci->async_iaa = qh; - ehci->async_unlink = qh->unlink_next; - qh->unlink_next = NULL; - } + else + list_move_tail(&qh->unlink_node, &ehci->async_iaa); /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1229,10 +1223,10 @@ static void end_unlink_async(struct ehci_hcd *ehci) /* Process the idle QHs */ restart: ehci->async_unlinking = true; - while (ehci->async_iaa) { - qh = ehci->async_iaa; - ehci->async_iaa = qh->unlink_next; - qh->unlink_next = NULL; + while (!list_empty(&ehci->async_iaa)) { + qh = list_first_entry(&ehci->async_iaa, struct ehci_qh, + unlink_node); + list_del(&qh->unlink_node); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; @@ -1247,7 +1241,7 @@ static void end_unlink_async(struct ehci_hcd *ehci) ehci->async_unlinking = false; /* Start a new IAA cycle if any QHs are waiting for it */ - if (ehci->async_unlink) { + if (!list_empty(&ehci->async_unlink)) { start_iaa_cycle(ehci, true); if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) goto restart; @@ -1276,7 +1270,8 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } /* If nothing else is being unlinked, unlink the last empty QH */ - if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { + if (list_empty(&ehci->async_iaa) && list_empty(&ehci->async_unlink) && + qh_to_unlink) { start_unlink_async(ehci, qh_to_unlink); --count; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e7a2dbd..c833feb 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -620,17 +620,13 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->unlink_cycle = ehci->intr_unlink_cycle; /* New entries go at the end of the intr_unlink list */ - if (ehci->intr_unlink) - ehci->intr_unlink_last->unlink_next = qh; - else - ehci->intr_unlink = qh; - ehci->intr_unlink_last = qh; + list_add_tail(&qh->unlink_node, &ehci->intr_unlink); if (ehci->intr_unlinking) ; /* Avoid recursive calls */ else if (ehci->rh_state < EHCI_RH_RUNNING) ehci_handle_intr_unlinks(ehci); - else if (ehci->intr_unlink == qh) { + else if (ehci->intr_unlink.next == &qh->unlink_node) { ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); ++ehci->intr_unlink_cycle; } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index e736333..f63a983 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -229,18 +229,19 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) * process all the QHs on the list. */ ehci->intr_unlinking = true; - while (ehci->intr_unlink) { - struct ehci_qh *qh = ehci->intr_unlink; + while (!list_empty(&ehci->intr_unlink)) { + struct ehci_qh *qh; + qh = list_first_entry(&ehci->intr_unlink, struct ehci_qh, + unlink_node); if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) break; - ehci->intr_unlink = qh->unlink_next; - qh->unlink_next = NULL; + list_del(&qh->unlink_node); end_unlink_intr(ehci, qh); } /* Handle remaining entries later */ - if (ehci->intr_unlink) { + if (!list_empty(&ehci->intr_unlink)) { ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); ++ehci->intr_unlink_cycle; } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 6815209..13f6704 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -128,9 +128,8 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ - struct ehci_qh *async_unlink; - struct ehci_qh *async_unlink_last; - struct ehci_qh *async_iaa; + struct list_head async_unlink; + struct list_head async_iaa; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ @@ -143,8 +142,7 @@ struct ehci_hcd { /* one per controller */ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ - struct ehci_qh *intr_unlink; - struct ehci_qh *intr_unlink_last; + struct list_head intr_unlink; unsigned intr_unlink_cycle; unsigned now_frame; /* frame from HC hardware */ unsigned last_iso_frame; /* last frame scanned for iso */ @@ -380,7 +378,7 @@ struct ehci_qh { struct list_head qtd_list; /* sw qtd list */ struct list_head intr_node; /* list of intr QHs */ struct ehci_qtd *dummy; - struct ehci_qh *unlink_next; /* next on unlink list */ + struct list_head unlink_node; unsigned unlink_cycle; -- cgit v0.10.2 From 214ac7a0771d95d2f66d01bca5afeb2c9e8ac3c8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:31:58 -0400 Subject: USB: EHCI: improve end_unlink_async() This patch (as1665) changes the way ehci-hcd's end_unlink_async() routine works in order to avoid recursive execution and to be more efficient: Now when an IAA cycle ends, a new one gets started up right away (if it is needed) instead of waiting until the just-unlinked QH has been processed. The async_iaa list is renamed to async_idle, which better expresses its new purpose: It is now the list of QHs which are now completely idle and are waiting to be processed by end_unlink_async(). A new flag is added to track whether an IAA cycle is in progress, because the list formerly known as async_iaa no longer stores the QHs waiting for the IAA to finish. The decision about how many QHs to process when an IAA cycle ends is now made at the end of the cycle, when we know the current state of the hardware, rather than at the beginning. This means a bunch of logic got moved from start_iaa_cycle() to end_unlink_async(). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b32323c..037a472 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -483,7 +483,7 @@ static int ehci_init(struct usb_hcd *hcd) */ ehci->periodic_size = DEFAULT_I_TDPS; INIT_LIST_HEAD(&ehci->async_unlink); - INIT_LIST_HEAD(&ehci->async_iaa); + INIT_LIST_HEAD(&ehci->async_idle); INIT_LIST_HEAD(&ehci->intr_unlink); INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->cached_itd_list); @@ -752,7 +752,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* guard against (alleged) silicon errata */ if (cmd & CMD_IAAD) ehci_dbg(ehci, "IAA with IAAD still set?\n"); - if (!list_empty(&ehci->async_iaa)) + if (ehci->iaa_in_progress) COUNT(ehci->stats.iaa); end_unlink_async(ehci); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 4a01367..820583b 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -960,7 +960,7 @@ static void disable_async(struct ehci_hcd *ehci) /* The async schedule and unlink lists are supposed to be empty */ WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) || - !list_empty(&ehci->async_iaa)); + !list_empty(&ehci->async_idle)); /* Don't turn off the schedule until ASS is 1 */ ehci_poll_ASS(ehci); @@ -1164,41 +1164,19 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) ehci->qh_scan_next = qh->qh_next.qh; } -static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) +static void start_iaa_cycle(struct ehci_hcd *ehci) { - /* - * Do nothing if an IAA cycle is already running or - * if one will be started shortly. - */ - if (!list_empty(&ehci->async_iaa) || ehci->async_unlinking) + /* Do nothing if an IAA cycle is already running */ + if (ehci->iaa_in_progress) return; + ehci->iaa_in_progress = true; /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { - - /* Do all the waiting QHs */ - list_splice_tail_init(&ehci->async_unlink, &ehci->async_iaa); - - if (!nested) /* Avoid recursion */ - end_unlink_async(ehci); + end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { - struct ehci_qh *qh; - - /* Do only the first waiting QH (nVidia bug?) */ - qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, - unlink_node); - - /* - * Intel (?) bug: The HC can write back the overlay region - * even after the IAA interrupt occurs. In self-defense, - * always go through two IAA cycles for each QH. - */ - if (qh->qh_state == QH_STATE_UNLINK_WAIT) - qh->qh_state = QH_STATE_UNLINK; - else - list_move_tail(&qh->unlink_node, &ehci->async_iaa); /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1215,16 +1193,59 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) static void end_unlink_async(struct ehci_hcd *ehci) { struct ehci_qh *qh; + bool early_exit; if (ehci->has_synopsys_hc_bug) ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); + /* The current IAA cycle has ended */ + ehci->iaa_in_progress = false; + + if (list_empty(&ehci->async_unlink)) + return; + qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, + unlink_node); /* QH whose IAA cycle just ended */ + + /* + * If async_unlinking is set then this routine is already running, + * either on the stack or on another CPU. + */ + early_exit = ehci->async_unlinking; + + /* If the controller isn't running, process all the waiting QHs */ + if (ehci->rh_state < EHCI_RH_RUNNING) + list_splice_tail_init(&ehci->async_unlink, &ehci->async_idle); + + /* + * Intel (?) bug: The HC can write back the overlay region even + * after the IAA interrupt occurs. In self-defense, always go + * through two IAA cycles for each QH. + */ + else if (qh->qh_state == QH_STATE_UNLINK_WAIT) { + qh->qh_state = QH_STATE_UNLINK; + early_exit = true; + } + + /* Otherwise process only the first waiting QH (NVIDIA bug?) */ + else + list_move_tail(&qh->unlink_node, &ehci->async_idle); + + /* Start a new IAA cycle if any QHs are waiting for it */ + if (!list_empty(&ehci->async_unlink)) + start_iaa_cycle(ehci); + + /* + * Don't allow nesting or concurrent calls, + * or wait for the second IAA cycle for the next QH. + */ + if (early_exit) + return; + /* Process the idle QHs */ - restart: ehci->async_unlinking = true; - while (!list_empty(&ehci->async_iaa)) { - qh = list_first_entry(&ehci->async_iaa, struct ehci_qh, + while (!list_empty(&ehci->async_idle)) { + qh = list_first_entry(&ehci->async_idle, struct ehci_qh, unlink_node); list_del(&qh->unlink_node); @@ -1239,13 +1260,6 @@ static void end_unlink_async(struct ehci_hcd *ehci) disable_async(ehci); } ehci->async_unlinking = false; - - /* Start a new IAA cycle if any QHs are waiting for it */ - if (!list_empty(&ehci->async_unlink)) { - start_iaa_cycle(ehci, true); - if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) - goto restart; - } } static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); @@ -1270,8 +1284,7 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } /* If nothing else is being unlinked, unlink the last empty QH */ - if (list_empty(&ehci->async_iaa) && list_empty(&ehci->async_unlink) && - qh_to_unlink) { + if (list_empty(&ehci->async_unlink) && qh_to_unlink) { start_unlink_async(ehci, qh_to_unlink); --count; } @@ -1293,7 +1306,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) WARN_ON(!list_empty(&qh->qtd_list)); single_unlink_async(ehci, qh); } - start_iaa_cycle(ehci, false); + start_iaa_cycle(ehci); } /* makes sure the async qh will become idle */ @@ -1306,7 +1319,7 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) return; single_unlink_async(ehci, qh); - start_iaa_cycle(ehci, false); + start_iaa_cycle(ehci); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index f63a983..11e5b32 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -304,7 +304,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) * (a) SMP races against real IAA firing and retriggering, and * (b) clean HC shutdown, when IAA watchdog was pending. */ - if (ehci->rh_state != EHCI_RH_RUNNING) + if (!ehci->iaa_in_progress || ehci->rh_state != EHCI_RH_RUNNING) return; /* If we get here, IAA is *REALLY* late. It's barely diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 13f6704..e666999 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -121,6 +121,7 @@ struct ehci_hcd { /* one per controller */ bool scanning:1; bool need_rescan:1; bool intr_unlinking:1; + bool iaa_in_progress:1; bool async_unlinking:1; bool shutdown:1; struct ehci_qh *qh_scan_next; @@ -129,7 +130,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct list_head async_unlink; - struct list_head async_iaa; + struct list_head async_idle; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ -- cgit v0.10.2 From afc2c9a2c359c850cea097c60a5f2b596efbbd88 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Mar 2013 13:32:10 -0400 Subject: USB: EHCI: remove unused variable in unlink_empty_async() This patch (as1669) removes the check_unlinks_later flag in ehci-hcd's unlink_empty_async(). It wasn't being used for anything and should have been removed in an earlier patch, but I forgot about it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 820583b..50787a3 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1268,7 +1268,6 @@ static void unlink_empty_async(struct ehci_hcd *ehci) { struct ehci_qh *qh; struct ehci_qh *qh_to_unlink = NULL; - bool check_unlinks_later = false; int count = 0; /* Find the last async QH which has been empty for a timer cycle */ @@ -1276,9 +1275,7 @@ static void unlink_empty_async(struct ehci_hcd *ehci) if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { ++count; - if (qh->unlink_cycle == ehci->async_unlink_cycle) - check_unlinks_later = true; - else + if (qh->unlink_cycle != ehci->async_unlink_cycle) qh_to_unlink = qh; } } -- cgit v0.10.2 From 4cba98ff877043aeb92e86102b0250b312ddf017 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:18 +0100 Subject: USB: ftdi_sio: remove obsolete port data refcounting Remove the port data refcounting and release the private data explicitly at port remove. The port data refcounting was used to make sure the port data was not freed until the last tty reference was closed. Since moving over to tty ports, the underlying assumptions are no longer valid as close is now called as part of tty port shutdown, which can occur before the final tty reference is dropped on device disconnect. This means that the private port data refcounting is now completely useless, as the last reference will always be dropped on port_remove. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d4809d5..c575738 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -55,7 +55,6 @@ static __u16 vendor = FTDI_VID; static __u16 product; struct ftdi_private { - struct kref kref; enum ftdi_chip_type chip_type; /* type of device, either SIO or FT8U232AM */ int baud_base; /* baud base clock for divisor setting */ @@ -910,7 +909,6 @@ static int ftdi_sio_probe(struct usb_serial *serial, static int ftdi_sio_port_probe(struct usb_serial_port *port); static int ftdi_sio_port_remove(struct usb_serial_port *port); static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port); -static void ftdi_close(struct usb_serial_port *port); static void ftdi_dtr_rts(struct usb_serial_port *port, int on); static void ftdi_process_read_urb(struct urb *urb); static int ftdi_prepare_write_buffer(struct usb_serial_port *port, @@ -950,7 +948,6 @@ static struct usb_serial_driver ftdi_sio_device = { .port_probe = ftdi_sio_port_probe, .port_remove = ftdi_sio_port_remove, .open = ftdi_open, - .close = ftdi_close, .dtr_rts = ftdi_dtr_rts, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, @@ -1687,7 +1684,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) return -ENOMEM; } - kref_init(&priv->kref); mutex_init(&priv->cfg_lock); priv->flags = ASYNC_LOW_LATENCY; @@ -1825,13 +1821,6 @@ static int ftdi_mtxorb_hack_setup(struct usb_serial *serial) return 0; } -static void ftdi_sio_priv_release(struct kref *k) -{ - struct ftdi_private *priv = container_of(k, struct ftdi_private, kref); - - kfree(priv); -} - static int ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1840,7 +1829,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) remove_sysfs_attrs(port); - kref_put(&priv->kref, ftdi_sio_priv_release); + kfree(priv); return 0; } @@ -1850,7 +1839,6 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) struct ktermios dummy; struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - int result; /* No error checking for this (will get errors later anyway) */ /* See ftdi_sio.h for description of what is reset */ @@ -1869,12 +1857,7 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) ftdi_set_termios(tty, port, &dummy); } - /* Start reading from the device */ - result = usb_serial_generic_open(tty, port); - if (!result) - kref_get(&priv->kref); - - return result; + return usb_serial_generic_open(tty, port); } static void ftdi_dtr_rts(struct usb_serial_port *port, int on) @@ -1899,19 +1882,6 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on) clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); } -/* - * usbserial:__serial_close only calls ftdi_close if the point is open - * - * This only gets called when it is the last close - */ -static void ftdi_close(struct usb_serial_port *port) -{ - struct ftdi_private *priv = usb_get_serial_port_data(port); - - usb_serial_generic_close(port); - kref_put(&priv->kref, ftdi_sio_priv_release); -} - /* The SIO requires the first byte to have: * B0 1 * B1 0 -- cgit v0.10.2 From 6f60b34c4db4e5c5004d5d80ae2e1f80ba3fbd77 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:19 +0100 Subject: USB: kl5kusb105: remove unnecessary urb kill on close Remove kill of interrupt-in urb on close as it has never been submitted. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 769d910..57fd001 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -360,9 +360,6 @@ static void klsi_105_close(struct usb_serial_port *port) /* shutdown our bulk reads and writes */ usb_serial_generic_close(port); - - /* wgg - do I need this? I think so. */ - usb_kill_urb(port->interrupt_in_urb); } /* We need to write a complete 64-byte data block and encode the -- cgit v0.10.2 From 83e86c79a61d4098aa3e53bb919adfe0c4c34461 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:20 +0100 Subject: USB: iuu_phoenix: remove unnecessary urb kill on close Remove kill of interrupt-in urb on close as it has never been submitted. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index ff77027..a3bfcb3 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -957,7 +957,6 @@ static void iuu_close(struct usb_serial_port *port) dev_dbg(&port->dev, "%s - shutting down urbs\n", __func__); usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); - usb_kill_urb(port->interrupt_in_urb); iuu_led(port, 0, 0, 0xF000, 0xFF); } } -- cgit v0.10.2 From 7b5789a86890423e1460362798140c26822798b1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:21 +0100 Subject: USB: pl2303: use interface device for debug Use interface rather than usb-serial device for debugging interface related operations. This gives more descriptive messages, such as [ 905.669436] pl2303 1-4.1:1.0: 0x40:0x1:0x8:0x0 0 rather than [ 341.943535] usb 1-4.1: 0x40:0x1:0x8:0x0 0 Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 3b10018..5b2e62f 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -149,7 +149,7 @@ static int pl2303_vendor_read(__u16 value, __u16 index, int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, value, index, buf, 1, 100); - dev_dbg(&serial->dev->dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", + dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, value, index, res, buf[0]); return res; @@ -161,7 +161,7 @@ static int pl2303_vendor_write(__u16 value, __u16 index, int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, 100); - dev_dbg(&serial->dev->dev, "0x%x:0x%x:0x%x:0x%x %d\n", + dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x %d\n", VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, value, index, res); return res; -- cgit v0.10.2 From f45d0a5aa593cdf48a37489fc61c145e16964288 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:22 +0100 Subject: USB: pl2303: make set_control_lines a port operation Pass usb-serial port rather than usb device to set_control_lines, and make sure port device is used for all port related debugging. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 5b2e62f..e7e407b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -248,14 +248,15 @@ static int pl2303_port_remove(struct usb_serial_port *port) return 0; } -static int set_control_lines(struct usb_device *dev, u8 value) +static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) { + struct usb_device *dev = port->serial->dev; int retval; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, value, 0, NULL, 0, 100); - dev_dbg(&dev->dev, "%s - value = %d, retval = %d\n", __func__, + dev_dbg(&port->dev, "%s - value = %d, retval = %d\n", __func__, value, retval); return retval; } @@ -437,7 +438,7 @@ static void pl2303_set_termios(struct tty_struct *tty, if (control != priv->line_control) { control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - set_control_lines(serial->dev, control); + pl2303_set_control_lines(port, control); } else { spin_unlock_irqrestore(&priv->lock, flags); } @@ -480,7 +481,7 @@ static void pl2303_dtr_rts(struct usb_serial_port *port, int on) priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - set_control_lines(port->serial->dev, control); + pl2303_set_control_lines(port, control); } static void pl2303_close(struct usb_serial_port *port) @@ -550,7 +551,7 @@ static int pl2303_tiocmset(struct tty_struct *tty, mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - ret = set_control_lines(serial->dev, control); + ret = pl2303_set_control_lines(port, control); else ret = -ENODEV; mutex_unlock(&serial->disc_mutex); -- cgit v0.10.2 From 395e08da8adfe873c5f79e549bb6b7fa5d9f3832 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:23 +0100 Subject: USB: serial: rename tty-port callbacks Rename the tty-port callbacks using a common prefix to more clearly separate them from the tty and usb driver callbacks. Rename serial_down to serial_port_shutdown to match the callback name. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 2fc0500..3e290fc 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -225,7 +225,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) return retval; } -static int serial_activate(struct tty_port *tport, struct tty_struct *tty) +static int serial_port_activate(struct tty_port *tport, struct tty_struct *tty) { struct usb_serial_port *port = container_of(tport, struct usb_serial_port, port); @@ -254,7 +254,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp) } /** - * serial_down - shut down hardware + * serial_port_shutdown - shut down hardware * @tport: tty port to shut down * * Shut down a USB serial port. Serialized against activate by the @@ -263,7 +263,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp) * * Not called if tty is console. */ -static void serial_down(struct tty_port *tport) +static void serial_port_shutdown(struct tty_port *tport) { struct usb_serial_port *port = container_of(tport, struct usb_serial_port, port); @@ -677,7 +677,7 @@ static struct usb_serial_driver *search_serial_device( return NULL; } -static int serial_carrier_raised(struct tty_port *port) +static int serial_port_carrier_raised(struct tty_port *port) { struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); struct usb_serial_driver *drv = p->serial->type; @@ -688,7 +688,7 @@ static int serial_carrier_raised(struct tty_port *port) return 1; } -static void serial_dtr_rts(struct tty_port *port, int on) +static void serial_port_dtr_rts(struct tty_port *port, int on) { struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); struct usb_serial *serial = p->serial; @@ -708,10 +708,10 @@ static void serial_dtr_rts(struct tty_port *port, int on) } static const struct tty_port_operations serial_port_ops = { - .carrier_raised = serial_carrier_raised, - .dtr_rts = serial_dtr_rts, - .activate = serial_activate, - .shutdown = serial_down, + .carrier_raised = serial_port_carrier_raised, + .dtr_rts = serial_port_dtr_rts, + .activate = serial_port_activate, + .shutdown = serial_port_shutdown, }; static int usb_serial_probe(struct usb_interface *interface, -- cgit v0.10.2 From 9993b42b638ec031a55bfe1bc16317cbb5c69722 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:24 +0100 Subject: USB: serial: remove redundant comments Remove redundant comments and fix some minor coding style issues. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 3e290fc..262beef 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -14,7 +14,6 @@ * * See Documentation/usb/usb-serial.txt for more information on using this * driver - * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -49,7 +48,6 @@ drivers depend on it. */ -/* initially all NULL */ static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; static DEFINE_MUTEX(table_lock); static LIST_HEAD(usb_serial_driver_list); @@ -338,7 +336,6 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf, dev_dbg(tty->dev, "%s - port %d, %d byte(s)\n", __func__, port->number, count); - /* pass on to the driver specific version of this function */ retval = port->serial->type->write(tty, port, buf, count); if (retval < 0) retval = usb_translate_errors(retval); @@ -351,7 +348,7 @@ static int serial_write_room(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - /* pass on to the driver specific version of this function */ + return port->serial->type->write_room(tty); } @@ -381,7 +378,6 @@ static void serial_throttle(struct tty_struct *tty) dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) port->serial->type->throttle(tty); } @@ -392,7 +388,6 @@ static void serial_unthrottle(struct tty_struct *tty) dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) port->serial->type->unthrottle(tty); } @@ -406,12 +401,11 @@ static int serial_ioctl(struct tty_struct *tty, dev_dbg(tty->dev, "%s - port %d, cmd 0x%.4x\n", __func__, port->number, cmd); - /* pass on to the driver specific version of this function - if it is available */ - if (port->serial->type->ioctl) { + if (port->serial->type->ioctl) retval = port->serial->type->ioctl(tty, cmd, arg); - } else + else retval = -ENOIOCTLCMD; + return retval; } @@ -421,8 +415,6 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - /* pass on to the driver specific version of this function - if it is available */ if (port->serial->type->set_termios) port->serial->type->set_termios(tty, port, old); else @@ -435,10 +427,9 @@ static int serial_break(struct tty_struct *tty, int break_state) dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - /* pass on to the driver specific version of this function - if it is available */ if (port->serial->type->break_ctl) port->serial->type->break_ctl(tty, break_state); + return 0; } @@ -1471,7 +1462,6 @@ void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_driver } EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); -/* Module information */ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d12e211d44844930b7460ffab43ff9b078a45369 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:25 +0100 Subject: USB: serial: clean up debug info Remove redundant port number from debug output (already printed as part of device name). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 262beef..fee8d8a 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -247,7 +247,8 @@ static int serial_open(struct tty_struct *tty, struct file *filp) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); + return tty_port_open(&port->port, tty, filp); } @@ -275,7 +276,8 @@ static void serial_hangup(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); + tty_port_hangup(&port->port); } @@ -283,7 +285,8 @@ static void serial_close(struct tty_struct *tty, struct file *filp) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); + tty_port_close(&port->port, tty, filp); } @@ -302,14 +305,14 @@ static void serial_cleanup(struct tty_struct *tty) struct usb_serial *serial; struct module *owner; + dev_dbg(tty->dev, "%s\n", __func__); + /* The console is magical. Do not hang up the console hardware * or there will be tears. */ if (port->port.console) return; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - tty->driver_data = NULL; serial = port->serial; @@ -333,8 +336,7 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf, if (port->serial->dev->state == USB_STATE_NOTATTACHED) goto exit; - dev_dbg(tty->dev, "%s - port %d, %d byte(s)\n", __func__, - port->number, count); + dev_dbg(tty->dev, "%s - %d byte(s)\n", __func__, count); retval = port->serial->type->write(tty, port, buf, count); if (retval < 0) @@ -347,7 +349,7 @@ static int serial_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); return port->serial->type->write_room(tty); } @@ -358,7 +360,7 @@ static int serial_chars_in_buffer(struct tty_struct *tty) struct usb_serial *serial = port->serial; int count = 0; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); mutex_lock(&serial->disc_mutex); /* if the device was unplugged then any remaining characters @@ -376,7 +378,7 @@ static void serial_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->throttle) port->serial->type->throttle(tty); @@ -386,7 +388,7 @@ static void serial_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->unthrottle) port->serial->type->unthrottle(tty); @@ -398,8 +400,7 @@ static int serial_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; - dev_dbg(tty->dev, "%s - port %d, cmd 0x%.4x\n", __func__, - port->number, cmd); + dev_dbg(tty->dev, "%s - cmd 0x%.4x\n", __func__, cmd); if (port->serial->type->ioctl) retval = port->serial->type->ioctl(tty, cmd, arg); @@ -413,7 +414,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->set_termios) port->serial->type->set_termios(tty, port, old); @@ -425,7 +426,7 @@ static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->break_ctl) port->serial->type->break_ctl(tty, break_state); @@ -483,7 +484,7 @@ static int serial_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->tiocmget) return port->serial->type->tiocmget(tty); @@ -495,7 +496,7 @@ static int serial_tiocmset(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->tiocmset) return port->serial->type->tiocmset(tty, set, clear); @@ -507,7 +508,7 @@ static int serial_get_icount(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); if (port->serial->type->get_icount) return port->serial->type->get_icount(tty, icount); @@ -535,7 +536,7 @@ static void usb_serial_port_work(struct work_struct *work) if (!tty) return; - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); + dev_dbg(tty->dev, "%s\n", __func__); tty_wakeup(tty); tty_kref_put(tty); -- cgit v0.10.2 From 6b03f7f79f5610ad119dc99643be20b44095c265 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:26 +0100 Subject: USB: serial: remove redundant allocation error messages Failed allocations already get an OOM message and a stack dump. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index fee8d8a..e7f97b5 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -603,10 +603,8 @@ static struct usb_serial *create_serial(struct usb_device *dev, struct usb_serial *serial; serial = kzalloc(sizeof(*serial), GFP_KERNEL); - if (!serial) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); + if (!serial) return NULL; - } serial->dev = usb_get_dev(dev); serial->type = driver; serial->interface = usb_get_intf(interface); @@ -750,7 +748,6 @@ static int usb_serial_probe(struct usb_interface *interface, serial = create_serial(dev, interface, type); if (!serial) { module_put(type->driver.owner); - dev_err(ddev, "%s - out of memory\n", __func__); return -ENOMEM; } @@ -914,16 +911,12 @@ static int usb_serial_probe(struct usb_interface *interface, for (j = 0; j < ARRAY_SIZE(port->read_urbs); ++j) { set_bit(j, &port->read_urbs_free); port->read_urbs[j] = usb_alloc_urb(0, GFP_KERNEL); - if (!port->read_urbs[j]) { - dev_err(ddev, "No free urbs available\n"); + if (!port->read_urbs[j]) goto probe_error; - } port->bulk_in_buffers[j] = kmalloc(buffer_size, GFP_KERNEL); - if (!port->bulk_in_buffers[j]) { - dev_err(ddev, "Couldn't allocate bulk_in_buffer\n"); + if (!port->bulk_in_buffers[j]) goto probe_error; - } usb_fill_bulk_urb(port->read_urbs[j], dev, usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), @@ -950,16 +943,12 @@ static int usb_serial_probe(struct usb_interface *interface, for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) { set_bit(j, &port->write_urbs_free); port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL); - if (!port->write_urbs[j]) { - dev_err(ddev, "No free urbs available\n"); + if (!port->write_urbs[j]) goto probe_error; - } port->bulk_out_buffers[j] = kmalloc(buffer_size, GFP_KERNEL); - if (!port->bulk_out_buffers[j]) { - dev_err(ddev, "Couldn't allocate bulk_out_buffer\n"); + if (!port->bulk_out_buffers[j]) goto probe_error; - } usb_fill_bulk_urb(port->write_urbs[j], dev, usb_sndbulkpipe(dev, endpoint->bEndpointAddress), @@ -977,19 +966,15 @@ static int usb_serial_probe(struct usb_interface *interface, endpoint = interrupt_in_endpoint[i]; port = serial->port[i]; port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port->interrupt_in_urb) { - dev_err(ddev, "No free urbs available\n"); + if (!port->interrupt_in_urb) goto probe_error; - } buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; port->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!port->interrupt_in_buffer) { - dev_err(ddev, "Couldn't allocate interrupt_in_buffer\n"); + if (!port->interrupt_in_buffer) goto probe_error; - } usb_fill_int_urb(port->interrupt_in_urb, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), @@ -1006,20 +991,16 @@ static int usb_serial_probe(struct usb_interface *interface, endpoint = interrupt_out_endpoint[i]; port = serial->port[i]; port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port->interrupt_out_urb) { - dev_err(ddev, "No free urbs available\n"); + if (!port->interrupt_out_urb) goto probe_error; - } buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_out_size = buffer_size; port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; port->interrupt_out_buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!port->interrupt_out_buffer) { - dev_err(ddev, "Couldn't allocate interrupt_out_buffer\n"); + if (!port->interrupt_out_buffer) goto probe_error; - } usb_fill_int_urb(port->interrupt_out_urb, dev, usb_sndintpipe(dev, endpoint->bEndpointAddress), -- cgit v0.10.2 From 49bd196dc651606e2f97f42957f9fcde3186f615 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:27 +0100 Subject: USB: serial: remove port number from generic-driver debug Remove redundant port number from debug output (already printed as part of device name). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 4c5c23f..927c5d6 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -272,8 +272,7 @@ static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, if (!test_and_clear_bit(index, &port->read_urbs_free)) return 0; - dev_dbg(&port->dev, "%s - port %d, urb %d\n", __func__, - port->number, index); + dev_dbg(&port->dev, "%s - urb %d\n", __func__, index); res = usb_submit_urb(port->read_urbs[index], mem_flags); if (res) { @@ -347,8 +346,8 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) } set_bit(i, &port->read_urbs_free); - dev_dbg(&port->dev, "%s - port %d, urb %d, len %d\n", - __func__, port->number, i, urb->actual_length); + dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, + urb->actual_length); if (urb->status) { dev_dbg(&port->dev, "%s - non-zero urb status: %d\n", @@ -473,8 +472,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, { struct tty_port *port = &usb_port->port; - dev_dbg(&usb_port->dev, "%s - port %d, status %d\n", __func__, - usb_port->number, status); + dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status); if (status) wake_up_interruptible(&port->open_wait); -- cgit v0.10.2 From 5c3275282422dcb895e2e9902c7fba4fd9d2512b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:28 +0100 Subject: USB: ark3116: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 4775f82..3b811fe 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -341,18 +341,15 @@ static void ark3116_close(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - if (serial->dev) { - /* disable DMA */ - ark3116_write_reg(serial, UART_FCR, 0); - - /* deactivate interrupts */ - ark3116_write_reg(serial, UART_IER, 0); + /* disable DMA */ + ark3116_write_reg(serial, UART_FCR, 0); - usb_serial_generic_close(port); - if (serial->num_interrupt_in) - usb_kill_urb(port->interrupt_in_urb); - } + /* deactivate interrupts */ + ark3116_write_reg(serial, UART_IER, 0); + usb_serial_generic_close(port); + if (serial->num_interrupt_in) + usb_kill_urb(port->interrupt_in_urb); } static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v0.10.2 From 1bc77f4df6b11948893cb1427fb6211c94f51364 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:29 +0100 Subject: USB: cyberjack: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 629bd28..de9253d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -166,11 +166,8 @@ static int cyberjack_open(struct tty_struct *tty, static void cyberjack_close(struct usb_serial_port *port) { - if (port->serial->dev) { - /* shutdown any bulk reads that might be going on */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - } + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); } static int cyberjack_write(struct tty_struct *tty, -- cgit v0.10.2 From 28e679ae6fd67023d5e8e61a0092791c5082c21c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:30 +0100 Subject: USB: digi_acceleport: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ebe45fa..76a8c20 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1149,53 +1149,51 @@ static void digi_close(struct usb_serial_port *port) if (port->serial->disconnected) goto exit; - if (port->serial->dev) { - /* FIXME: Transmit idle belongs in the wait_unti_sent path */ - digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT); - - /* disable input flow control */ - buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; - buf[1] = priv->dp_port_num; - buf[2] = DIGI_DISABLE; - buf[3] = 0; - - /* disable output flow control */ - buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; - buf[5] = priv->dp_port_num; - buf[6] = DIGI_DISABLE; - buf[7] = 0; - - /* disable reading modem signals automatically */ - buf[8] = DIGI_CMD_READ_INPUT_SIGNALS; - buf[9] = priv->dp_port_num; - buf[10] = DIGI_DISABLE; - buf[11] = 0; - - /* disable receive */ - buf[12] = DIGI_CMD_RECEIVE_ENABLE; - buf[13] = priv->dp_port_num; - buf[14] = DIGI_DISABLE; - buf[15] = 0; - - /* flush fifos */ - buf[16] = DIGI_CMD_IFLUSH_FIFO; - buf[17] = priv->dp_port_num; - buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; - buf[19] = 0; - - ret = digi_write_oob_command(port, buf, 20, 0); - if (ret != 0) - dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d\n", ret); - - /* wait for final commands on oob port to complete */ - prepare_to_wait(&priv->dp_flush_wait, &wait, - TASK_INTERRUPTIBLE); - schedule_timeout(DIGI_CLOSE_TIMEOUT); - finish_wait(&priv->dp_flush_wait, &wait); - - /* shutdown any outstanding bulk writes */ - usb_kill_urb(port->write_urb); - } + /* FIXME: Transmit idle belongs in the wait_unti_sent path */ + digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT); + + /* disable input flow control */ + buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[1] = priv->dp_port_num; + buf[2] = DIGI_DISABLE; + buf[3] = 0; + + /* disable output flow control */ + buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[5] = priv->dp_port_num; + buf[6] = DIGI_DISABLE; + buf[7] = 0; + + /* disable reading modem signals automatically */ + buf[8] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[9] = priv->dp_port_num; + buf[10] = DIGI_DISABLE; + buf[11] = 0; + + /* disable receive */ + buf[12] = DIGI_CMD_RECEIVE_ENABLE; + buf[13] = priv->dp_port_num; + buf[14] = DIGI_DISABLE; + buf[15] = 0; + + /* flush fifos */ + buf[16] = DIGI_CMD_IFLUSH_FIFO; + buf[17] = priv->dp_port_num; + buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[19] = 0; + + ret = digi_write_oob_command(port, buf, 20, 0); + if (ret != 0) + dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d\n", + ret); + /* wait for final commands on oob port to complete */ + prepare_to_wait(&priv->dp_flush_wait, &wait, + TASK_INTERRUPTIBLE); + schedule_timeout(DIGI_CLOSE_TIMEOUT); + finish_wait(&priv->dp_flush_wait, &wait); + + /* shutdown any outstanding bulk writes */ + usb_kill_urb(port->write_urb); exit: spin_lock_irq(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; -- cgit v0.10.2 From 853127faa43f4971c4e9fc506bb86622208ca935 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:31 +0100 Subject: USB: iuu_phoenix: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index a3bfcb3..8eeefe3 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -951,14 +951,11 @@ static void iuu_close(struct usb_serial_port *port) return; iuu_uart_off(port); - if (serial->dev) { - /* free writebuf */ - /* shutdown our urbs */ - dev_dbg(&port->dev, "%s - shutting down urbs\n", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - iuu_led(port, 0, 0, 0xF000, 0xFF); - } + + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + + iuu_led(port, 0, 0, 0xF000, 0xFF); } static void iuu_init_termios(struct tty_struct *tty) -- cgit v0.10.2 From 80dfe0ceb31d92aca835d94f0255d93288e5ed12 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:32 +0100 Subject: USB: keyspan: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Remove some out-commented bogus code while at it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 1fd1935..6abe8a4 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1115,7 +1115,6 @@ static void keyspan_dtr_rts(struct usb_serial_port *port, int on) static void keyspan_close(struct usb_serial_port *port) { int i; - struct usb_serial *serial = port->serial; struct keyspan_port_private *p_priv; p_priv = usb_get_serial_port_data(port); @@ -1123,28 +1122,17 @@ static void keyspan_close(struct usb_serial_port *port) p_priv->rts_state = 0; p_priv->dtr_state = 0; - if (serial->dev) { - keyspan_send_setup(port, 2); - /* pilot-xfer seems to work best with this delay */ - mdelay(100); - /* keyspan_set_termios(port, NULL); */ - } - - /*while (p_priv->outcont_urb->status == -EINPROGRESS) { - dev_dbg(&port->dev, "%s - urb in progress\n", __func__); - }*/ + keyspan_send_setup(port, 2); + /* pilot-xfer seems to work best with this delay */ + mdelay(100); p_priv->out_flip = 0; p_priv->in_flip = 0; - if (serial->dev) { - /* Stop reading/writing urbs */ - stop_urb(p_priv->inack_urb); - /* stop_urb(p_priv->outcont_urb); */ - for (i = 0; i < 2; i++) { - stop_urb(p_priv->in_urbs[i]); - stop_urb(p_priv->out_urbs[i]); - } + stop_urb(p_priv->inack_urb); + for (i = 0; i < 2; i++) { + stop_urb(p_priv->in_urbs[i]); + stop_urb(p_priv->out_urbs[i]); } } -- cgit v0.10.2 From d7f08452ffd3abbc2dbbf763cdec05496923f3b7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:33 +0100 Subject: USB: keyspan_pda: remove bogus disconnect test from dtr_rts Remove bogus (and unnecessary) test for serial->dev being NULL in dtr_rts. The device is never cleared, and disconnect is handled for dtr_rts in usb-serial core anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 3b17d5d..0d992ba 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -595,12 +595,10 @@ static void keyspan_pda_dtr_rts(struct usb_serial_port *port, int on) { struct usb_serial *serial = port->serial; - if (serial->dev) { - if (on) - keyspan_pda_set_modem_info(serial, (1<<7) | (1<< 2)); - else - keyspan_pda_set_modem_info(serial, 0); - } + if (on) + keyspan_pda_set_modem_info(serial, (1 << 7) | (1 << 2)); + else + keyspan_pda_set_modem_info(serial, 0); } -- cgit v0.10.2 From 75d22b323fad49715068f74af9e65dbaba06614e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:34 +0100 Subject: USB: keyspan_pda: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 0d992ba..da3b29e 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -649,13 +649,8 @@ error: } static void keyspan_pda_close(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - - if (serial->dev) { - /* shutdown our bulk reads and writes */ - usb_kill_urb(port->write_urb); - usb_kill_urb(port->interrupt_in_urb); - } + usb_kill_urb(port->write_urb); + usb_kill_urb(port->interrupt_in_urb); } -- cgit v0.10.2 From bb3529c6a01fce3e23c968982f46fa46a0095345 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:35 +0100 Subject: USB: mos7840: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Simplify urb killing, and remove some related debug and dead code while at it. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 316ad5f..979ef19 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1223,25 +1223,10 @@ static void mos7840_close(struct usb_serial_port *port) } } - /* While closing port, shutdown all bulk read, write * - * and interrupt read if they exists */ - if (serial->dev) { - if (mos7840_port->write_urb) { - dev_dbg(&port->dev, "%s", "Shutdown bulk write\n"); - usb_kill_urb(mos7840_port->write_urb); - } - if (mos7840_port->read_urb) { - dev_dbg(&port->dev, "%s", "Shutdown bulk read\n"); - usb_kill_urb(mos7840_port->read_urb); - mos7840_port->read_urb_busy = false; - } - if ((&mos7840_port->control_urb)) { - dev_dbg(&port->dev, "%s", "Shutdown control read\n"); - /*/ usb_kill_urb (mos7840_port->control_urb); */ - } - } -/* if(mos7840_port->ctrl_buf != NULL) */ -/* kfree(mos7840_port->ctrl_buf); */ + usb_kill_urb(mos7840_port->write_urb); + usb_kill_urb(mos7840_port->read_urb); + mos7840_port->read_urb_busy = false; + port0->open_ports--; dev_dbg(&port->dev, "%s in close%d:in port%d\n", __func__, port0->open_ports, port->number); if (port0->open_ports == 0) { @@ -1330,9 +1315,8 @@ static void mos7840_break(struct tty_struct *tty, int break_state) if (mos7840_port == NULL) return; - if (serial->dev) - /* flush and block until tx is empty */ - mos7840_block_until_chase_response(tty, mos7840_port); + /* flush and block until tx is empty */ + mos7840_block_until_chase_response(tty, mos7840_port); if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; -- cgit v0.10.2 From 9c210bfa29dc3845ae61b544f673d9753ffa3618 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:36 +0100 Subject: USB: sierra: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index c13f6e7..2b06481 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -778,30 +778,25 @@ static void sierra_close(struct usb_serial_port *port) portdata->rts_state = 0; portdata->dtr_state = 0; - if (serial->dev) { - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) { - serial->interface->needs_remote_wakeup = 0; - /* odd error handling due to pm counters */ - if (!usb_autopm_get_interface(serial->interface)) - sierra_send_setup(port); - else - usb_autopm_get_interface_no_resume(serial->interface); - - } - mutex_unlock(&serial->disc_mutex); - spin_lock_irq(&intfdata->susp_lock); - portdata->opened = 0; - spin_unlock_irq(&intfdata->susp_lock); + mutex_lock(&serial->disc_mutex); + if (!serial->disconnected) { + serial->interface->needs_remote_wakeup = 0; + /* odd error handling due to pm counters */ + if (!usb_autopm_get_interface(serial->interface)) + sierra_send_setup(port); + else + usb_autopm_get_interface_no_resume(serial->interface); + } + mutex_unlock(&serial->disc_mutex); + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 0; + spin_unlock_irq(&intfdata->susp_lock); - /* Stop reading urbs */ - sierra_stop_rx_urbs(port); - /* .. and release them */ - for (i = 0; i < portdata->num_in_urbs; i++) { - sierra_release_urb(portdata->in_urbs[i]); - portdata->in_urbs[i] = NULL; - } + sierra_stop_rx_urbs(port); + for (i = 0; i < portdata->num_in_urbs; i++) { + sierra_release_urb(portdata->in_urbs[i]); + portdata->in_urbs[i] = NULL; } } -- cgit v0.10.2 From e6d144bc27da921b81d46eb7cac461be59ae301b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:37 +0100 Subject: USB: usb_wwan: remove bogus disconnect test in close Remove bogus (and unnecessary) test for serial->dev being NULL in close. The device is never cleared, and close is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 571965a..ece326e 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -421,20 +421,19 @@ void usb_wwan_close(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (serial->dev) { - /* Stop reading/writing urbs */ - spin_lock_irq(&intfdata->susp_lock); - portdata->opened = 0; - spin_unlock_irq(&intfdata->susp_lock); + /* Stop reading/writing urbs */ + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 0; + spin_unlock_irq(&intfdata->susp_lock); - for (i = 0; i < N_IN_URB; i++) - usb_kill_urb(portdata->in_urbs[i]); - for (i = 0; i < N_OUT_URB; i++) - usb_kill_urb(portdata->out_urbs[i]); - /* balancing - important as an error cannot be handled*/ - usb_autopm_get_interface_no_resume(serial->interface); - serial->interface->needs_remote_wakeup = 0; - } + for (i = 0; i < N_IN_URB; i++) + usb_kill_urb(portdata->in_urbs[i]); + for (i = 0; i < N_OUT_URB; i++) + usb_kill_urb(portdata->out_urbs[i]); + + /* balancing - important as an error cannot be handled*/ + usb_autopm_get_interface_no_resume(serial->interface); + serial->interface->needs_remote_wakeup = 0; } EXPORT_SYMBOL(usb_wwan_close); -- cgit v0.10.2 From 19c61853831f4f957885665ce36a573cbabf9ba1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:38 +0100 Subject: USB: serial: remove bogus disconnect test in cleanup Remove bogus (and unnecessary) test for serial->dev being NULL in cleanup. The device is never cleared, and cleanup is never called after a completed disconnect anyway. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 927c5d6..131d6cb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -104,24 +104,20 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_open); static void generic_cleanup(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; unsigned long flags; int i; - if (serial->dev) { - /* shutdown any bulk transfers that might be going on */ - if (port->bulk_out_size) { - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) - usb_kill_urb(port->write_urbs[i]); + if (port->bulk_out_size) { + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_kill_urb(port->write_urbs[i]); - spin_lock_irqsave(&port->lock, flags); - kfifo_reset_out(&port->write_fifo); - spin_unlock_irqrestore(&port->lock, flags); - } - if (port->bulk_in_size) { - for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) - usb_kill_urb(port->read_urbs[i]); - } + spin_lock_irqsave(&port->lock, flags); + kfifo_reset_out(&port->write_fifo); + spin_unlock_irqrestore(&port->lock, flags); + } + if (port->bulk_in_size) { + for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) + usb_kill_urb(port->read_urbs[i]); } } -- cgit v0.10.2 From bf8773c6eacc9c3241455fdb7e82050ad2323a0a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:39 +0100 Subject: USB: ssu100: remove explicit initialisation of disconnect The disconnect callback is set to the generic implementation by usb-serial core if NULL. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 4b2a197..97aca3f 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -641,7 +641,6 @@ static struct usb_serial_driver ssu100_device = { .get_icount = ssu100_get_icount, .ioctl = ssu100_ioctl, .set_termios = ssu100_set_termios, - .disconnect = usb_serial_generic_disconnect, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v0.10.2 From 618e183d03a95da200bed48e5277efe428feef26 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:40 +0100 Subject: USB: ssu100: remove custom close operation The generic close operation will be used if the close field is left uninitialised. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 97aca3f..45b8c29 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -315,11 +315,6 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) return usb_serial_generic_open(tty, port); } -static void ssu100_close(struct usb_serial_port *port) -{ - usb_serial_generic_close(port); -} - static int get_serial_info(struct usb_serial_port *port, struct serial_struct __user *retinfo) { @@ -630,7 +625,6 @@ static struct usb_serial_driver ssu100_device = { .id_table = id_table, .num_ports = 1, .open = ssu100_open, - .close = ssu100_close, .attach = ssu100_attach, .port_probe = ssu100_port_probe, .port_remove = ssu100_port_remove, -- cgit v0.10.2 From f8f0ad8621cf01e1e3f8264863b270c2308a9c1a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:41 +0100 Subject: USB: serial: fix generic disconnect implementation There is no need for the generic disconnect callback to stop the read and write urbs a second time as this has already been taken care of by close (which is called from hangup as part of disconnect). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 131d6cb..a6d0ac6 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -102,7 +102,7 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port } EXPORT_SYMBOL_GPL(usb_serial_generic_open); -static void generic_cleanup(struct usb_serial_port *port) +void usb_serial_generic_close(struct usb_serial_port *port) { unsigned long flags; int i; @@ -120,11 +120,6 @@ static void generic_cleanup(struct usb_serial_port *port) usb_kill_urb(port->read_urbs[i]); } } - -void usb_serial_generic_close(struct usb_serial_port *port) -{ - generic_cleanup(port); -} EXPORT_SYMBOL_GPL(usb_serial_generic_close); int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, @@ -507,11 +502,6 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_resume); void usb_serial_generic_disconnect(struct usb_serial *serial) { - int i; - - /* stop reads and writes on all ports */ - for (i = 0; i < serial->num_ports; ++i) - generic_cleanup(serial->port[i]); } EXPORT_SYMBOL_GPL(usb_serial_generic_disconnect); -- cgit v0.10.2 From 0f16cfe39eeef47c91aa3c3bf2b49954d5313a58 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:42 +0100 Subject: USB: serial: remove generic disconnect callback Remove the now empty generic disconnect callback and make the disconnect callback non-mandatory. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index a6d0ac6..4d421f3 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -45,7 +45,6 @@ struct usb_serial_driver usb_serial_generic_device = { }, .id_table = generic_device_ids, .num_ports = 1, - .disconnect = usb_serial_generic_disconnect, .release = usb_serial_generic_release, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, @@ -500,11 +499,6 @@ int usb_serial_generic_resume(struct usb_serial *serial) } EXPORT_SYMBOL_GPL(usb_serial_generic_resume); -void usb_serial_generic_disconnect(struct usb_serial *serial) -{ -} -EXPORT_SYMBOL_GPL(usb_serial_generic_disconnect); - void usb_serial_generic_release(struct usb_serial *serial) { } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index e7f97b5..569b679 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1095,7 +1095,8 @@ static void usb_serial_disconnect(struct usb_interface *interface) device_del(&port->dev); } } - serial->type->disconnect(serial); + if (serial->type->disconnect) + serial->type->disconnect(serial); /* let the last holder of this object cause it to be cleaned up */ usb_serial_put(serial); @@ -1304,7 +1305,6 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, chars_in_buffer); set_to_generic_if_null(device, read_bulk_callback); set_to_generic_if_null(device, write_bulk_callback); - set_to_generic_if_null(device, disconnect); set_to_generic_if_null(device, release); set_to_generic_if_null(device, process_read_urb); set_to_generic_if_null(device, prepare_write_buffer); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 1819b59..437dfd6 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -329,7 +329,6 @@ extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); extern void usb_serial_generic_throttle(struct tty_struct *tty); extern void usb_serial_generic_unthrottle(struct tty_struct *tty); -extern void usb_serial_generic_disconnect(struct usb_serial *serial); extern void usb_serial_generic_release(struct usb_serial *serial); extern int usb_serial_generic_register(void); extern void usb_serial_generic_deregister(void); -- cgit v0.10.2 From 79b80b8a1141ba0605e917a6fc12d44383ab29b8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:43 +0100 Subject: USB: serial: remove generic release callback Remove empty generic release implementation and make the release callback non-mandatory (like attach, probe and disconnect). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 4d421f3..aa71f6e 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -45,7 +45,6 @@ struct usb_serial_driver usb_serial_generic_device = { }, .id_table = generic_device_ids, .num_ports = 1, - .release = usb_serial_generic_release, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, .resume = usb_serial_generic_resume, @@ -498,7 +497,3 @@ int usb_serial_generic_resume(struct usb_serial *serial) return c ? -EIO : 0; } EXPORT_SYMBOL_GPL(usb_serial_generic_resume); - -void usb_serial_generic_release(struct usb_serial *serial) -{ -} diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 569b679..4819fd9 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -137,7 +137,7 @@ static void destroy_serial(struct kref *kref) if (serial->minor != SERIAL_TTY_NO_MINOR) return_serial(serial); - if (serial->attached) + if (serial->attached && serial->type->release) serial->type->release(serial); /* Now that nothing is using the ports, they can be freed */ @@ -1305,7 +1305,6 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, chars_in_buffer); set_to_generic_if_null(device, read_bulk_callback); set_to_generic_if_null(device, write_bulk_callback); - set_to_generic_if_null(device, release); set_to_generic_if_null(device, process_read_urb); set_to_generic_if_null(device, prepare_write_buffer); } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 437dfd6..3f8f5e3 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -329,7 +329,6 @@ extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); extern void usb_serial_generic_throttle(struct tty_struct *tty); extern void usb_serial_generic_unthrottle(struct tty_struct *tty); -extern void usb_serial_generic_release(struct usb_serial *serial); extern int usb_serial_generic_register(void); extern void usb_serial_generic_deregister(void); extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, -- cgit v0.10.2 From c3452f5e444446fad9bb1957d22a25334798f94c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:44 +0100 Subject: USB: serial: clean up generic-operation handling Most USB serial drivers are, and should be, using as much of the generic implementation as possible. Rename the fixup_generic function to a more descriptive name. Reword the related debug message in a more neutral tone (and remember to add the missing newline). Finally, move the operations initialisation to after the initial sanity checks. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4819fd9..3eb4d06 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1291,12 +1291,12 @@ module_exit(usb_serial_exit); do { \ if (!type->function) { \ type->function = usb_serial_generic_##function; \ - pr_debug("Had to override the " #function \ - " usb serial operation with the generic one.");\ - } \ + pr_debug("%s: using generic " #function "\n", \ + type->driver.name); \ + } \ } while (0) -static void fixup_generic(struct usb_serial_driver *device) +static void usb_serial_operations_init(struct usb_serial_driver *device) { set_to_generic_if_null(device, open); set_to_generic_if_null(device, write); @@ -1316,8 +1316,6 @@ static int usb_serial_register(struct usb_serial_driver *driver) if (usb_disabled()) return -ENODEV; - fixup_generic(driver); - if (!driver->description) driver->description = driver->driver.name; if (!driver->usb_driver) { @@ -1326,6 +1324,8 @@ static int usb_serial_register(struct usb_serial_driver *driver) return -EINVAL; } + usb_serial_operations_init(driver); + /* Add this device to our list of devices */ mutex_lock(&table_lock); list_add(&driver->driver_list, &usb_serial_driver_list); -- cgit v0.10.2 From 30d9d42e9a7c205cc139dce743463b8070684df5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:45 +0100 Subject: USB: cyberjack: fix disconnect handling Make sure the interrupt urb submitted in port_probe is killed in port_remove. The interrupt-urb completion handler references the port and may get called after port_remove has returned and the port has been unregistered (although this is currently prevented by usb-serial core as we are using a non-private urb). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index de9253d..7814262 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -51,7 +51,6 @@ #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ -static void cyberjack_disconnect(struct usb_serial *serial); static int cyberjack_port_probe(struct usb_serial_port *port); static int cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, @@ -79,7 +78,6 @@ static struct usb_serial_driver cyberjack_device = { .description = "Reiner SCT Cyberjack USB card reader", .id_table = id_table, .num_ports = 1, - .disconnect = cyberjack_disconnect, .port_probe = cyberjack_port_probe, .port_remove = cyberjack_port_remove, .open = cyberjack_open, @@ -130,20 +128,14 @@ static int cyberjack_port_remove(struct usb_serial_port *port) { struct cyberjack_private *priv; + usb_kill_urb(port->interrupt_in_urb); + priv = usb_get_serial_port_data(port); kfree(priv); return 0; } -static void cyberjack_disconnect(struct usb_serial *serial) -{ - int i; - - for (i = 0; i < serial->num_ports; ++i) - usb_kill_urb(serial->port[i]->interrupt_in_urb); -} - static int cyberjack_open(struct tty_struct *tty, struct usb_serial_port *port) { -- cgit v0.10.2 From a4a83100a1653a389a5efaa68ad32c9b89010910 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:46 +0100 Subject: USB: serial: fix port release We should not call kill_traffic (and usb_kill_urb) once disconnect returns. Any pending urbs are killed at disconnect and new submissions are prevented by usb_unbind_interface (and usb_disable_interface). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 3eb4d06..4567929 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -572,13 +572,6 @@ static void port_release(struct device *dev) dev_dbg(dev, "%s\n", __func__); - /* - * Stop all the traffic before cancelling the work, so that - * nobody will restart it by calling usb_serial_port_softint. - */ - kill_traffic(port); - cancel_work_sync(&port->work); - usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { -- cgit v0.10.2 From 69a3d2125796b3452da1b9fce851af96ac24b3a9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:47 +0100 Subject: USB: serial: rename port release Rename port_release so that all usb_serial_port functions have a common prefix. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4567929..db0b646 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -565,7 +565,7 @@ static void kill_traffic(struct usb_serial_port *port) usb_kill_urb(port->interrupt_out_urb); } -static void port_release(struct device *dev) +static void usb_serial_port_release(struct device *dev) { struct usb_serial_port *port = to_usb_serial_port(dev); int i; @@ -888,7 +888,7 @@ static int usb_serial_probe(struct usb_interface *interface, port->dev.parent = &interface->dev; port->dev.driver = NULL; port->dev.bus = &usb_serial_bus_type; - port->dev.release = &port_release; + port->dev.release = &usb_serial_port_release; device_initialize(&port->dev); } -- cgit v0.10.2 From 68a2bed130a10cffbf68620f41d08a900b1d776b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:48 +0100 Subject: USB: fix urb-poison imbalance The calls to usb_poison_urb and usb_unpoison_urb are expected to be balanced. However, if an urb that has not yet been submitted is poisoned, its reject counter will not be increased as its ep-field is NULL. A consecutive call to unpoison will thus in fact poison the urb as its reject counter will be decremented to a negative value, effectively preventing the urb from being submitted. Note that there are currently no in-kernel drivers affected by this. Cc: Alan Stern Acked-by: Alan Stern Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index e0d9d94..16927fa 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -683,10 +683,13 @@ EXPORT_SYMBOL_GPL(usb_kill_urb); void usb_poison_urb(struct urb *urb) { might_sleep(); - if (!(urb && urb->dev && urb->ep)) + if (!urb) return; atomic_inc(&urb->reject); + if (!urb->dev || !urb->ep) + return; + usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); } -- cgit v0.10.2 From 6a5c821cad1459ec2b5fd5778f46d13c4255a7bf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:49 +0100 Subject: USB: serial: use urb poison to reliably kill traffic Use usb_poison_urb to reliably kill all urbs on disconnect and suspend. This way there will be no question that the urbs cannot be resubmitted by buggy subdrivers. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index db0b646..0b39d01 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -542,27 +542,30 @@ static void usb_serial_port_work(struct work_struct *work) tty_kref_put(tty); } -static void kill_traffic(struct usb_serial_port *port) +static void usb_serial_port_poison_urbs(struct usb_serial_port *port) { int i; for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) - usb_kill_urb(port->read_urbs[i]); + usb_poison_urb(port->read_urbs[i]); for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) - usb_kill_urb(port->write_urbs[i]); - /* - * This is tricky. - * Some drivers submit the read_urb in the - * handler for the write_urb or vice versa - * this order determines the order in which - * usb_kill_urb() must be used to reliably - * kill the URBs. As it is unknown here, - * both orders must be used in turn. - * The call below is not redundant. - */ - usb_kill_urb(port->read_urb); - usb_kill_urb(port->interrupt_in_urb); - usb_kill_urb(port->interrupt_out_urb); + usb_poison_urb(port->write_urbs[i]); + + usb_poison_urb(port->interrupt_in_urb); + usb_poison_urb(port->interrupt_out_urb); +} + +static void usb_serial_port_unpoison_urbs(struct usb_serial_port *port) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) + usb_unpoison_urb(port->read_urbs[i]); + for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) + usb_unpoison_urb(port->write_urbs[i]); + + usb_unpoison_urb(port->interrupt_in_urb); + usb_unpoison_urb(port->interrupt_out_urb); } static void usb_serial_port_release(struct device *dev) @@ -1082,7 +1085,7 @@ static void usb_serial_disconnect(struct usb_interface *interface) tty_vhangup(tty); tty_kref_put(tty); } - kill_traffic(port); + usb_serial_port_poison_urbs(port); cancel_work_sync(&port->work); if (device_is_registered(&port->dev)) device_del(&port->dev); @@ -1120,7 +1123,7 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) - kill_traffic(port); + usb_serial_port_poison_urbs(port); } err_out: @@ -1128,11 +1131,25 @@ err_out: } EXPORT_SYMBOL(usb_serial_suspend); +static void usb_serial_unpoison_port_urbs(struct usb_serial *serial) +{ + struct usb_serial_port *port; + int i; + + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port) + usb_serial_port_unpoison_urbs(port); + } +} + int usb_serial_resume(struct usb_interface *intf) { struct usb_serial *serial = usb_get_intfdata(intf); int rv; + usb_serial_unpoison_port_urbs(serial); + serial->suspending = 0; if (serial->type->resume) rv = serial->type->resume(serial); @@ -1148,6 +1165,8 @@ static int usb_serial_reset_resume(struct usb_interface *intf) struct usb_serial *serial = usb_get_intfdata(intf); int rv; + usb_serial_unpoison_port_urbs(serial); + serial->suspending = 0; if (serial->type->reset_resume) rv = serial->type->reset_resume(serial); -- cgit v0.10.2 From 5cb27dde2e8b7bcbdce6de270c73c021a65caff8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:50 +0100 Subject: USB: serial: clean up usb-serial bus device removal Make sure to unregister the tty-device before calling subdriver port_remove. This way remove will reverse probe, and specifically any port data released in port_remove will be available throughout tty unregister. Note that the order currently does not matter as the tty-layer can make callbacks also after the device has been unregistered. This is handled in usb-serial core using the disconnected flag, which is already set when usb-serial bus device remove is called. Cc: Peter Hurley Reported-by: Peter Hurley Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 37decb1..3c4db6d 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -106,14 +106,15 @@ static int usb_serial_device_remove(struct device *dev) /* make sure suspend/resume doesn't race against port_remove */ usb_autopm_get_interface(port->serial->interface); + minor = port->number; + tty_unregister_device(usb_serial_tty_driver, minor); + device_remove_file(&port->dev, &dev_attr_port_number); driver = port->serial->type; if (driver->port_remove) retval = driver->port_remove(port); - minor = port->number; - tty_unregister_device(usb_serial_tty_driver, minor); dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", driver->description, minor); -- cgit v0.10.2 From 143d9d961608b737d90a813deaaf91affb41c83c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:51 +0100 Subject: USB: serial: add tiocmiwait subdriver operation Add tiocmiwait operation to struct usb_serial_driver. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0b39d01..ada400d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -402,10 +402,17 @@ static int serial_ioctl(struct tty_struct *tty, dev_dbg(tty->dev, "%s - cmd 0x%.4x\n", __func__, cmd); - if (port->serial->type->ioctl) - retval = port->serial->type->ioctl(tty, cmd, arg); - else - retval = -ENOIOCTLCMD; + switch (cmd) { + case TIOCMIWAIT: + if (port->serial->type->tiocmiwait) + retval = port->serial->type->tiocmiwait(tty, arg); + break; + default: + if (port->serial->type->ioctl) + retval = port->serial->type->ioctl(tty, cmd, arg); + else + retval = -ENOIOCTLCMD; + } return retval; } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 3f8f5e3..9c8b53f 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -272,6 +272,7 @@ struct usb_serial_driver { int (*tiocmget)(struct tty_struct *tty); int (*tiocmset)(struct tty_struct *tty, unsigned int set, unsigned int clear); + int (*tiocmiwait)(struct tty_struct *tty, unsigned long arg); int (*get_icount)(struct tty_struct *tty, struct serial_icounter_struct *icount); /* Called by the tty layer for port level work. There may or may not -- cgit v0.10.2 From 980373b7918b8023be6b7df03857f494ae124d0b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:52 +0100 Subject: USB: serial: add generic TIOCMIWAIT implementation Add generic TIOCMIWAIT implementation which correctly handles hangup, USB-device disconnect, does not rely on the deprecated sleep_on functions and hence does not suffer from the races currently affecting several usb-serial drivers. This makes it much easier to add TIOCMIWAIT support to subdrivers as the tricky details related to hangup and disconnect (e.g. atomicity, that the private port data may have been freed when woken up, and waking up processes at disconnect) have been handled once and for all. To add support to a subdriver, simply set the tiocmiwait-port-operation field, update the port icount fields and wake up any process sleeping on the tty-port modem-status-change wait queue on changes. Note that the tty-port initialised flag can be used to detect disconnected as the port will be hung up as part of disconnect (and cannot be reactivated due to the disconnected flag). However, as the tty-port implementation currently wakes up processes before calling port shutdown, the tty-hupping flag must also be checked to detect hangup for now. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index aa71f6e..18bc74e 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -418,6 +418,64 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) } EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); +static bool usb_serial_generic_msr_changed(struct tty_struct *tty, + unsigned long arg, struct async_icount *cprev) +{ + struct usb_serial_port *port = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + bool ret; + + /* + * Use tty-port initialised flag to detect all hangups including the + * one generated at USB-device disconnect. + * + * FIXME: Remove hupping check once tty_port_hangup calls shutdown + * (which clears the initialised flag) before wake up. + */ + if (test_bit(TTY_HUPPING, &tty->flags)) + return true; + if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + return true; + + spin_lock_irqsave(&port->lock, flags); + cnow = port->icount; /* atomic copy*/ + spin_unlock_irqrestore(&port->lock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} + +int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) +{ + struct usb_serial_port *port = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + int ret; + + spin_lock_irqsave(&port->lock, flags); + cnow = port->icount; /* atomic copy */ + spin_unlock_irqrestore(&port->lock, flags); + + ret = wait_event_interruptible(port->port.delta_msr_wait, + usb_serial_generic_msr_changed(tty, arg, &cnow)); + if (!ret) { + if (test_bit(TTY_HUPPING, &tty->flags)) + ret = -EIO; + if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + ret = -EIO; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait); + #ifdef CONFIG_MAGIC_SYSRQ int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) { diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 9c8b53f..47c8d2c 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -61,6 +62,7 @@ * @bulk_out_buffers: pointers to the bulk out buffers for this port * @write_urbs: pointers to the bulk out urbs for this port * @write_urbs_free: status bitmap the for bulk out urbs + * @icount: interrupt counters * @tx_bytes: number of bytes currently in host stack queues * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this * port. @@ -109,6 +111,7 @@ struct usb_serial_port { unsigned long write_urbs_free; __u8 bulk_out_endpointAddress; + struct async_icount icount; int tx_bytes; unsigned long flags; @@ -330,6 +333,8 @@ extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); extern void usb_serial_generic_throttle(struct tty_struct *tty); extern void usb_serial_generic_unthrottle(struct tty_struct *tty); +extern int usb_serial_generic_tiocmiwait(struct tty_struct *tty, + unsigned long arg); extern int usb_serial_generic_register(void); extern void usb_serial_generic_deregister(void); extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, -- cgit v0.10.2 From c371de14b9a23aadb57accab0ca2e5dd28de7f16 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:53 +0100 Subject: USB: serial: wake up MSR-wait queue on disconnect Make sure processes waiting for modem-status changes are woken up at disconnect. This is needed for custom subdriver TIOCMIWAIT-implementations which do not yet handle hangup. Even though processes on the tty-port wait queue are woken up at hangup the wake-up call in usb-serial disconnect is still needed if a woken-up process may go back to sleep (e.g. due to an incomplete TIOCMIWAIT-implementation). If a disconnect occurs after a hangup, any process waiting for changes will not be woken up a second time by the tty-layer as the port will then have been disassociated from the tty. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index ada400d..4568816 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1093,6 +1093,7 @@ static void usb_serial_disconnect(struct usb_interface *interface) tty_kref_put(tty); } usb_serial_port_poison_urbs(port); + wake_up_interruptible(&port->port.delta_msr_wait); cancel_work_sync(&port->work); if (device_is_registered(&port->dev)) device_del(&port->dev); -- cgit v0.10.2 From befefcda4bddc52d29248931801961a72aeef28b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:54 +0100 Subject: USB: serial: add generic get_icount implementation Add generic get_icount implementation that subdrivers relying on the port interrupt counters can use. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 18bc74e..5e55761 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -476,6 +476,33 @@ int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg) } EXPORT_SYMBOL_GPL(usb_serial_generic_tiocmiwait); +int usb_serial_generic_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct usb_serial_port *port = tty->driver_data; + struct async_icount cnow; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + cnow = port->icount; /* atomic copy */ + spin_unlock_irqrestore(&port->lock, flags); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->tx = cnow.tx; + icount->rx = cnow.rx; + icount->frame = cnow.frame; + icount->parity = cnow.parity; + icount->overrun = cnow.overrun; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount); + #ifdef CONFIG_MAGIC_SYSRQ int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) { diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 47c8d2c..c786ee7 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -335,6 +335,8 @@ extern void usb_serial_generic_throttle(struct tty_struct *tty); extern void usb_serial_generic_unthrottle(struct tty_struct *tty); extern int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg); +extern int usb_serial_generic_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); extern int usb_serial_generic_register(void); extern void usb_serial_generic_deregister(void); extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, -- cgit v0.10.2 From cb1676a61a83f9a01517abd496f080bf7d56168e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:55 +0100 Subject: USB: ftdi_sio: use port icount Use the port-data icount for interrupt counters. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c575738..ea7433e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -67,7 +67,6 @@ struct ftdi_private { */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ - struct async_icount icount; char prev_status; /* Used for TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ __u16 interface; /* FT2232C, FT2232H or FT4232H port interface @@ -1909,7 +1908,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); if (!c) break; - priv->icount.tx += c; + port->icount.tx += c; buffer[i] = (c << 2) + 1; count += c + 1; } @@ -1917,7 +1916,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, } else { count = kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); - priv->icount.tx += count; + port->icount.tx += count; } return count; @@ -1946,13 +1945,13 @@ static int ftdi_process_packet(struct usb_serial_port *port, char diff_status = status ^ priv->prev_status; if (diff_status & FTDI_RS0_CTS) - priv->icount.cts++; + port->icount.cts++; if (diff_status & FTDI_RS0_DSR) - priv->icount.dsr++; + port->icount.dsr++; if (diff_status & FTDI_RS0_RI) - priv->icount.rng++; + port->icount.rng++; if (diff_status & FTDI_RS0_RLSD) - priv->icount.dcd++; + port->icount.dcd++; wake_up_interruptible(&port->delta_msr_wait); priv->prev_status = status; @@ -1964,18 +1963,18 @@ static int ftdi_process_packet(struct usb_serial_port *port, * over framing errors */ if (packet[1] & FTDI_RS_BI) { flag = TTY_BREAK; - priv->icount.brk++; + port->icount.brk++; usb_serial_handle_break(port); } else if (packet[1] & FTDI_RS_PE) { flag = TTY_PARITY; - priv->icount.parity++; + port->icount.parity++; } else if (packet[1] & FTDI_RS_FE) { flag = TTY_FRAME; - priv->icount.frame++; + port->icount.frame++; } /* Overrun is special, not associated with a char */ if (packet[1] & FTDI_RS_OE) { - priv->icount.overrun++; + port->icount.overrun++; tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); } } @@ -1989,7 +1988,7 @@ static int ftdi_process_packet(struct usb_serial_port *port, len -= 2; if (!len) return 0; /* status only */ - priv->icount.rx += len; + port->icount.rx += len; ch = packet + 2; if (port->port.console && port->sysrq) { @@ -2357,8 +2356,7 @@ static int ftdi_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); - struct async_icount *ic = &priv->icount; + struct async_icount *ic = &port->icount; icount->cts = ic->cts; icount->dsr = ic->dsr; @@ -2378,7 +2376,6 @@ static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -2404,7 +2401,7 @@ static int ftdi_ioctl(struct tty_struct *tty, * This code is borrowed from linux/drivers/char/serial.c */ case TIOCMIWAIT: - cprev = priv->icount; + cprev = port->icount; for (;;) { interruptible_sleep_on(&port->delta_msr_wait); /* see if a signal did it */ @@ -2414,7 +2411,7 @@ static int ftdi_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; - cnow = priv->icount; + cnow = port->icount; if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || -- cgit v0.10.2 From f307e5cd3f3d2243ec69abc9df890052b88349f5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:56 +0100 Subject: USB: ftdi_sio: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation which does not suffer from the races involved when using the deprecated sleep_on functions. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ea7433e..fa3077f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -954,6 +954,7 @@ static struct usb_serial_driver ftdi_sio_device = { .prepare_write_buffer = ftdi_prepare_write_buffer, .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = ftdi_get_icount, .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, @@ -1824,8 +1825,6 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - wake_up_interruptible(&port->delta_msr_wait); - remove_sysfs_attrs(port); kfree(priv); @@ -1953,7 +1952,7 @@ static int ftdi_process_packet(struct usb_serial_port *port, if (diff_status & FTDI_RS0_RLSD) port->icount.dcd++; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); priv->prev_status = status; } @@ -2376,8 +2375,6 @@ static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; - struct async_icount cnow; - struct async_icount cprev; dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd); @@ -2391,35 +2388,6 @@ static int ftdi_ioctl(struct tty_struct *tty, case TIOCSSERIAL: /* sets serial port data */ return set_serial_info(tty, port, (struct serial_struct __user *) arg); - - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was. - * - * This code is borrowed from linux/drivers/char/serial.c - */ - case TIOCMIWAIT: - cprev = port->icount; - for (;;) { - interruptible_sleep_on(&port->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - cnow = port->icount; - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } case TIOCSERGETLSR: return get_lsr_info(port, (struct serial_struct __user *)arg); break; -- cgit v0.10.2 From 6f86fec9fa38b8f69902f973e0cff612b2a0f0b0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:57 +0100 Subject: USB: ftdi_sio: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fa3077f..1199dc5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -917,8 +917,6 @@ static void ftdi_set_termios(struct tty_struct *tty, static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int ftdi_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount); static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -955,7 +953,7 @@ static struct usb_serial_driver ftdi_sio_device = { .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, - .get_icount = ftdi_get_icount, + .get_icount = usb_serial_generic_get_icount, .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, @@ -2351,26 +2349,6 @@ static int ftdi_tiocmset(struct tty_struct *tty, return update_mctrl(port, set, clear); } -static int ftdi_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct async_icount *ic = &port->icount; - - icount->cts = ic->cts; - icount->dsr = ic->dsr; - icount->rng = ic->rng; - icount->dcd = ic->dcd; - icount->tx = ic->tx; - icount->rx = ic->rx; - icount->frame = ic->frame; - icount->parity = ic->parity; - icount->overrun = ic->overrun; - icount->brk = ic->brk; - icount->buf_overrun = ic->buf_overrun; - return 0; -} - static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { -- cgit v0.10.2 From b4cb7536cc4883fe133de8733430ccdb23e23fa9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:58 +0100 Subject: USB: ark3116: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 3b811fe..ed3f6b8 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -62,7 +62,6 @@ static int is_irda(struct usb_serial *serial) } struct ark3116_private { - struct async_icount icount; int irda; /* 1 for irda device */ /* protects hw register updates */ @@ -402,31 +401,10 @@ err_out: return result; } -static int ark3116_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct ark3116_private *priv = usb_get_serial_port_data(port); - struct async_icount cnow = priv->icount; - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - return 0; -} - static int ark3116_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; - struct ark3116_private *priv = usb_get_serial_port_data(port); struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -450,7 +428,7 @@ static int ark3116_ioctl(struct tty_struct *tty, return 0; case TIOCMIWAIT: for (;;) { - struct async_icount prev = priv->icount; + struct async_icount prev = port->icount; interruptible_sleep_on(&port->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) @@ -459,19 +437,19 @@ static int ark3116_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; - if ((prev.rng == priv->icount.rng) && - (prev.dsr == priv->icount.dsr) && - (prev.dcd == priv->icount.dcd) && - (prev.cts == priv->icount.cts)) + if ((prev.rng == port->icount.rng) && + (prev.dsr == port->icount.dsr) && + (prev.dcd == port->icount.dcd) && + (prev.cts == port->icount.cts)) return -EIO; if ((arg & TIOCM_RNG && - (prev.rng != priv->icount.rng)) || + (prev.rng != port->icount.rng)) || (arg & TIOCM_DSR && - (prev.dsr != priv->icount.dsr)) || + (prev.dsr != port->icount.dsr)) || (arg & TIOCM_CD && - (prev.dcd != priv->icount.dcd)) || + (prev.dcd != port->icount.dcd)) || (arg & TIOCM_CTS && - (prev.cts != priv->icount.cts))) + (prev.cts != port->icount.cts))) return 0; } break; @@ -572,13 +550,13 @@ static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr) if (msr & UART_MSR_ANY_DELTA) { /* update input line counters */ if (msr & UART_MSR_DCTS) - priv->icount.cts++; + port->icount.cts++; if (msr & UART_MSR_DDSR) - priv->icount.dsr++; + port->icount.dsr++; if (msr & UART_MSR_DDCD) - priv->icount.dcd++; + port->icount.dcd++; if (msr & UART_MSR_TERI) - priv->icount.rng++; + port->icount.rng++; wake_up_interruptible(&port->delta_msr_wait); } } @@ -595,13 +573,13 @@ static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr) if (lsr&UART_LSR_BRK_ERROR_BITS) { if (lsr & UART_LSR_BI) - priv->icount.brk++; + port->icount.brk++; if (lsr & UART_LSR_FE) - priv->icount.frame++; + port->icount.frame++; if (lsr & UART_LSR_PE) - priv->icount.parity++; + port->icount.parity++; if (lsr & UART_LSR_OE) - priv->icount.overrun++; + port->icount.overrun++; } } @@ -719,7 +697,7 @@ static struct usb_serial_driver ark3116_device = { .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, .tiocmset = ark3116_tiocmset, - .get_icount = ark3116_get_icount, + .get_icount = usb_serial_generic_get_icount, .open = ark3116_open, .close = ark3116_close, .break_ctl = ark3116_break_ctl, -- cgit v0.10.2 From 7caed7e78c14e53479fea75c132d297257a2423c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:36:59 +0100 Subject: USB: ark3116: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation which does not suffer from the races involved when using the deprecated sleep_on functions. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index ed3f6b8..3b16118 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -426,33 +426,6 @@ static int ark3116_ioctl(struct tty_struct *tty, if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) return -EFAULT; return 0; - case TIOCMIWAIT: - for (;;) { - struct async_icount prev = port->icount; - interruptible_sleep_on(&port->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - if ((prev.rng == port->icount.rng) && - (prev.dsr == port->icount.dsr) && - (prev.dcd == port->icount.dcd) && - (prev.cts == port->icount.cts)) - return -EIO; - if ((arg & TIOCM_RNG && - (prev.rng != port->icount.rng)) || - (arg & TIOCM_DSR && - (prev.dsr != port->icount.dsr)) || - (arg & TIOCM_CD && - (prev.dcd != port->icount.dcd)) || - (arg & TIOCM_CTS && - (prev.cts != port->icount.cts))) - return 0; - } - break; } return -ENOIOCTLCMD; @@ -557,7 +530,7 @@ static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr) port->icount.dcd++; if (msr & UART_MSR_TERI) port->icount.rng++; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); } } @@ -697,6 +670,7 @@ static struct usb_serial_driver ark3116_device = { .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, .tiocmset = ark3116_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .open = ark3116_open, .close = ark3116_close, -- cgit v0.10.2 From 7bb98f0edd1df8687ca9c8ce9492f8ac6b8ec448 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:00 +0100 Subject: USB: ch341: replace custom ioctl operation with tiocmiwait Replace custom ioctl operation with tiocmiwait. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 07d4650..1a97985 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -500,8 +500,9 @@ exit: __func__, status); } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +static int ch341_tiocmiwait(struct tty_struct *tty, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ch341_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 prevstatus; @@ -542,26 +543,6 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int ch341_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - - dev_dbg(&port->dev, "%s (%d) cmd = 0x%04x\n", __func__, port->number, cmd); - - switch (cmd) { - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, port->number); - return wait_modem_info(port, arg); - - default: - dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd); - break; - } - - return -ENOIOCTLCMD; -} - static int ch341_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; @@ -611,11 +592,11 @@ static struct usb_serial_driver ch341_device = { .dtr_rts = ch341_dtr_rts, .carrier_raised = ch341_carrier_raised, .close = ch341_close, - .ioctl = ch341_ioctl, .set_termios = ch341_set_termios, .break_ctl = ch341_break_ctl, .tiocmget = ch341_tiocmget, .tiocmset = ch341_tiocmset, + .tiocmiwait = ch341_tiocmiwait, .read_int_callback = ch341_read_int_callback, .port_probe = ch341_port_probe, .port_remove = ch341_port_remove, -- cgit v0.10.2 From 3f550431925891d2ea4338d298aceeceb1c6cbf1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:01 +0100 Subject: USB: ch341: fix TIOCMIWAIT and disconnect Use tty-port modem-status-change wait queue on which processes are woken up at hangup and disconnect. Currently a process waiting on modem-status changes will not be woken on device disconnect as wake up was only done in dtr_rts which isn't guaranteed to be called (e.g. if HUPCL is not set). Also remove the redundant wake-up call from dtr_rts. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 1a97985..c2a4171 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -296,7 +296,6 @@ static void ch341_dtr_rts(struct usb_serial_port *port, int on) priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR); spin_unlock_irqrestore(&priv->lock, flags); ch341_set_handshake(port->serial->dev, priv->line_control); - wake_up_interruptible(&port->delta_msr_wait); } static void ch341_close(struct usb_serial_port *port) @@ -489,7 +488,7 @@ static void ch341_read_int_callback(struct urb *urb) tty_kref_put(tty); } - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); } exit: @@ -516,7 +515,7 @@ static int ch341_tiocmiwait(struct tty_struct *tty, unsigned long arg) spin_unlock_irqrestore(&priv->lock, flags); while (!multi_change) { - interruptible_sleep_on(&port->delta_msr_wait); + interruptible_sleep_on(&port->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; -- cgit v0.10.2 From 493516e34455cdfcd0a2875bb62ed5d910782958 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:02 +0100 Subject: USB: cypress_m8: replace custom ioctl operation with tiocmiwait Replace custom ioctl operation with tiocmiwait. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index ba7352e..cdbb096 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -129,13 +129,12 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send(struct usb_serial_port *port); static int cypress_write_room(struct tty_struct *tty); -static int cypress_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg); static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int cypress_tiocmget(struct tty_struct *tty); static int cypress_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int cypress_tiocmiwait(struct tty_struct *tty, unsigned long arg); static int cypress_chars_in_buffer(struct tty_struct *tty); static void cypress_throttle(struct tty_struct *tty); static void cypress_unthrottle(struct tty_struct *tty); @@ -158,10 +157,10 @@ static struct usb_serial_driver cypress_earthmate_device = { .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = cypress_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -184,10 +183,10 @@ static struct usb_serial_driver cypress_hidcom_device = { .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = cypress_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -210,10 +209,10 @@ static struct usb_serial_driver cypress_ca42v2_device = { .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, - .ioctl = cypress_ioctl, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, + .tiocmiwait = cypress_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -855,55 +854,43 @@ static int cypress_tiocmset(struct tty_struct *tty, } -static int cypress_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) +static int cypress_tiocmiwait(struct tty_struct *tty, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); - - dev_dbg(&port->dev, "%s - port %d, cmd 0x%.4x\n", __func__, port->number, cmd); - - switch (cmd) { - /* This code comes from drivers/char/serial.c and ftdi_sio.c */ - case TIOCMIWAIT: - for (;;) { - interruptible_sleep_on(&port->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - { - char diff = priv->diff_status; - if (diff == 0) - return -EIO; /* no change => error */ - - /* consume all events */ - priv->diff_status = 0; - - /* return 0 if caller wanted to know about - these bits */ - if (((arg & TIOCM_RNG) && (diff & UART_RI)) || - ((arg & TIOCM_DSR) && (diff & UART_DSR)) || - ((arg & TIOCM_CD) && (diff & UART_CD)) || - ((arg & TIOCM_CTS) && (diff & UART_CTS))) - return 0; - /* otherwise caller can't care less about what - * happened, and so we continue to wait for - * more events. - */ - } - } - return 0; - default: - break; + char diff; + + for (;;) { + interruptible_sleep_on(&port->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + + if (port->serial->disconnected) + return -EIO; + + diff = priv->diff_status; + if (diff == 0) + return -EIO; /* no change => error */ + + /* consume all events */ + priv->diff_status = 0; + + /* return 0 if caller wanted to know about + these bits */ + if (((arg & TIOCM_RNG) && (diff & UART_RI)) || + ((arg & TIOCM_DSR) && (diff & UART_DSR)) || + ((arg & TIOCM_CD) && (diff & UART_CD)) || + ((arg & TIOCM_CTS) && (diff & UART_CTS))) + return 0; + /* otherwise caller can't care less about what + * happened, and so we continue to wait for + * more events. + */ } - dev_dbg(&port->dev, "%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h\n", __func__, cmd); - return -ENOIOCTLCMD; -} /* cypress_ioctl */ + return 0; +} static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) -- cgit v0.10.2 From c648e94ebfc7701f4d0c79dce959134f02f24ce7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:03 +0100 Subject: USB: cypress_m8: fix TIOCMIWAIT and disconnect Use tty-port modem-status-change wait queue on which processes are woken up at hangup and disconnect. Currently a process waiting on modem-status changes will not be woken on device disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index cdbb096..e4a62cf 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -861,7 +861,7 @@ static int cypress_tiocmiwait(struct tty_struct *tty, unsigned long arg) char diff; for (;;) { - interruptible_sleep_on(&port->delta_msr_wait); + interruptible_sleep_on(&port->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -1176,7 +1176,7 @@ static void cypress_read_int_callback(struct urb *urb) if (priv->current_status != priv->prev_status) { priv->diff_status |= priv->current_status ^ priv->prev_status; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); priv->prev_status = priv->current_status; } spin_unlock_irqrestore(&priv->lock, flags); -- cgit v0.10.2 From a7fa57742ea692631a4076254408366a01eb3582 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:04 +0100 Subject: USB: digi_acceleport: remove unused MSR-wait queue Remove unused, private modem-status wait queue from driver. If TIOCMIWAIT is ever implemented it must not rely on a private wait queue which may have been released when woken up. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 76a8c20..32873b4 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -196,7 +196,6 @@ struct digi_port { unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE]; int dp_write_urb_in_use; unsigned int dp_modem_signals; - wait_queue_head_t dp_modem_change_wait; int dp_transmit_idle; wait_queue_head_t dp_transmit_idle_wait; int dp_throttled; @@ -1250,7 +1249,6 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) spin_lock_init(&priv->dp_port_lock); priv->dp_port_num = port_num; - init_waitqueue_head(&priv->dp_modem_change_wait); init_waitqueue_head(&priv->dp_transmit_idle_wait); init_waitqueue_head(&priv->dp_flush_wait); init_waitqueue_head(&priv->dp_close_wait); @@ -1541,7 +1539,6 @@ static int digi_read_oob_callback(struct urb *urb) else priv->dp_modem_signals &= ~TIOCM_CD; - wake_up_interruptible(&priv->dp_modem_change_wait); spin_unlock(&priv->dp_port_lock); } else if (opcode == DIGI_CMD_TRANSMIT_IDLE) { spin_lock(&priv->dp_port_lock); -- cgit v0.10.2 From 4bb4e6384ad633448b3bbd49df63188330bbf740 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:05 +0100 Subject: USB: f81232: add custom tiocmiwait operation Break out TIOCMIWAIT handling from custom ioctl operation and use tiocmiwait operation field instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index a172ad5..d1fdd8d 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -242,8 +242,9 @@ static int f81232_carrier_raised(struct usb_serial_port *port) return 0; } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +static int f81232_tiocmiwait(struct tty_struct *tty, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct f81232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int prevstatus; @@ -302,11 +303,6 @@ static int f81232_ioctl(struct tty_struct *tty, return -EFAULT; return 0; - - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, - port->number); - return wait_modem_info(port, arg); default: dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd); @@ -358,6 +354,7 @@ static struct usb_serial_driver f81232_device = { .set_termios = f81232_set_termios, .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, + .tiocmiwait = f81232_tiocmiwait, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, -- cgit v0.10.2 From 3018bb51583e8b9f9e76574c71d45e43d8299ebc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:06 +0100 Subject: USB: f81232: fix TIOCMIWAIT and disconnect Use tty-port modem-status-change wait queue on which processes are woken up at hangup and disconnect. Currently a process waiting on modem-status changes will not be woken on device disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index d1fdd8d..090b411 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -110,7 +110,7 @@ static void f81232_process_read_urb(struct urb *urb) line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); if (!urb->actual_length) return; @@ -256,7 +256,7 @@ static int f81232_tiocmiwait(struct tty_struct *tty, unsigned long arg) spin_unlock_irqrestore(&priv->lock, flags); while (1) { - interruptible_sleep_on(&port->delta_msr_wait); + interruptible_sleep_on(&port->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; -- cgit v0.10.2 From d36a7712497b547a21bf46c3be517cb06ccb93ee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:07 +0100 Subject: USB: io_edgeport: switch to generic get_icount implementation Switch to the generic get_icount implementation. Note that the interrupt counters will no longer be reset at open which is in accordance with which how the other drivers work. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index efd8b97..16ef8f3 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -111,7 +111,6 @@ struct edgeport_port { wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */ wait_queue_head_t wait_command; /* for handling sleeping while waiting for command to finish */ - struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner of this object */ }; @@ -215,8 +214,6 @@ static void edge_break(struct tty_struct *tty, int break_state); static int edge_tiocmget(struct tty_struct *tty); static int edge_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int edge_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount); static int edge_startup(struct usb_serial *serial); static void edge_disconnect(struct usb_serial *serial); static void edge_release(struct usb_serial *serial); @@ -885,9 +882,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) init_waitqueue_head(&edge_port->wait_chase); init_waitqueue_head(&edge_port->wait_command); - /* initialize our icount structure */ - memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount)); - /* initialize our port settings */ edge_port->txCredits = 0; /* Can't send any data yet */ /* Must always set this bit to enable ints! */ @@ -1314,7 +1308,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, /* decrement the number of credits we have by the number we just sent */ edge_port->txCredits -= count; - edge_port->icount.tx += count; + edge_port->port->icount.tx += count; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { @@ -1326,7 +1320,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, /* revert the credits as something bad happened. */ edge_port->txCredits += count; - edge_port->icount.tx -= count; + edge_port->port->icount.tx -= count; } dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n", __func__, count, edge_port->txCredits, fifo->count); @@ -1588,31 +1582,6 @@ static int edge_tiocmget(struct tty_struct *tty) return result; } -static int edge_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct async_icount cnow; - cnow = edge_port->icount; - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - dev_dbg(&port->dev, "%s (%d) TIOCGICOUNT RX=%d, TX=%d\n", __func__, - port->number, icount->rx, icount->tx); - return 0; -} - static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct __user *retinfo) { @@ -1665,7 +1634,7 @@ static int edge_ioctl(struct tty_struct *tty, case TIOCMIWAIT: dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, port->number); - cprev = edge_port->icount; + cprev = port->icount; while (1) { prepare_to_wait(&port->delta_msr_wait, &wait, TASK_INTERRUPTIBLE); @@ -1678,7 +1647,7 @@ static int edge_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; - cnow = edge_port->icount; + cnow = port->icount; if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -1866,7 +1835,7 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxPort); edge_tty_recv(edge_port->port, buffer, rxLen); - edge_port->icount.rx += rxLen; + edge_port->port->icount.rx += rxLen; } buffer += rxLen; } @@ -2042,7 +2011,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr) if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { - icount = &edge_port->icount; + icount = &edge_port->port->icount; /* update input line counters */ if (newMsr & EDGEPORT_MSR_DELTA_CTS) @@ -2088,7 +2057,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, edge_tty_recv(edge_port->port, &data, 1); /* update input line counters */ - icount = &edge_port->icount; + icount = &edge_port->port->icount; if (newLsr & LSR_BREAK) icount->brk++; if (newLsr & LSR_OVER_ERR) diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index 1511dd0..35fe9ad 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -116,7 +116,7 @@ static struct usb_serial_driver edgeport_2port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, @@ -147,7 +147,7 @@ static struct usb_serial_driver edgeport_4port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, @@ -178,7 +178,7 @@ static struct usb_serial_driver edgeport_8port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, @@ -209,7 +209,7 @@ static struct usb_serial_driver epic_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, -- cgit v0.10.2 From 8b8070d8580c38e15979b2a88f3a4d7b02bd3bde Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:08 +0100 Subject: USB: io_edgeport: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 16ef8f3..ff9a6ef 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1618,8 +1618,6 @@ static int edge_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct async_icount cnow; - struct async_icount cprev; dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd); @@ -1631,37 +1629,6 @@ static int edge_ioctl(struct tty_struct *tty, case TIOCGSERIAL: dev_dbg(&port->dev, "%s (%d) TIOCGSERIAL\n", __func__, port->number); return get_serial_info(edge_port, (struct serial_struct __user *) arg); - - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, port->number); - cprev = port->icount; - while (1) { - prepare_to_wait(&port->delta_msr_wait, - &wait, TASK_INTERRUPTIBLE); - schedule(); - finish_wait(&port->delta_msr_wait, &wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - cnow = port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - break; - } return -ENOIOCTLCMD; } @@ -2022,7 +1989,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr) icount->dcd++; if (newMsr & EDGEPORT_MSR_DELTA_RI) icount->rng++; - wake_up_interruptible(&edge_port->port->delta_msr_wait); + wake_up_interruptible(&edge_port->port->port.delta_msr_wait); } /* Save the new modem status */ diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index 35fe9ad..ae5fac5 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -116,6 +116,7 @@ static struct usb_serial_driver edgeport_2port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, @@ -147,6 +148,7 @@ static struct usb_serial_driver edgeport_4port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, @@ -178,6 +180,7 @@ static struct usb_serial_driver edgeport_8port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, @@ -209,6 +212,7 @@ static struct usb_serial_driver epic_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, -- cgit v0.10.2 From cf9a9d66bd29f32011d271695ffaa289489b3a7d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:09 +0100 Subject: USB: io_ti: switch to generic get_icount implementation Switch to the generic get_icount implementation. Note that the interrupt counters will no longer be reset at open which is in accordance with which how the other drivers work. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7777172..fb9e664 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -86,7 +86,6 @@ struct edgeport_port { int baud_rate; int close_pending; int lsr_event; - struct async_icount icount; struct edgeport_serial *edge_serial; struct usb_serial_port *port; __u8 bUartMode; /* Port type, 0: RS232, etc. */ @@ -1445,7 +1444,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { - icount = &edge_port->icount; + icount = &edge_port->port->icount; /* update input line counters */ if (msr & EDGEPORT_MSR_DELTA_CTS) @@ -1498,7 +1497,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, edge_tty_recv(edge_port->port, &data, 1); /* update input line counters */ - icount = &edge_port->icount; + icount = &edge_port->port->icount; if (new_lsr & LSR_BREAK) icount->brk++; if (new_lsr & LSR_OVER_ERR) @@ -1657,7 +1656,7 @@ static void edge_bulk_in_callback(struct urb *urb) else edge_tty_recv(edge_port->port, data, urb->actual_length); - edge_port->icount.rx += urb->actual_length; + edge_port->port->icount.rx += urb->actual_length; } exit: @@ -1750,8 +1749,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) dev = port->serial->dev; - memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount)); - /* turn off loopback */ status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0); if (status) { @@ -1999,7 +1996,7 @@ static void edge_send(struct tty_struct *tty) edge_port->ep_write_urb_in_use = 0; /* TODO: reschedule edge_send */ } else - edge_port->icount.tx += count; + edge_port->port->icount.tx += count; /* wakeup any process waiting for writes to complete */ /* there is now more room in the buffer for new writes */ @@ -2360,27 +2357,6 @@ static int edge_tiocmget(struct tty_struct *tty) return result; } -static int edge_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct async_icount *ic = &edge_port->icount; - - icount->cts = ic->cts; - icount->dsr = ic->dsr; - icount->rng = ic->rng; - icount->dcd = ic->dcd; - icount->tx = ic->tx; - icount->rx = ic->rx; - icount->frame = ic->frame; - icount->parity = ic->parity; - icount->overrun = ic->overrun; - icount->brk = ic->brk; - icount->buf_overrun = ic->buf_overrun; - return 0; -} - static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct __user *retinfo) { @@ -2428,7 +2404,7 @@ static int edge_ioctl(struct tty_struct *tty, (struct serial_struct __user *) arg); case TIOCMIWAIT: dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__); - cprev = edge_port->icount; + cprev = edge_port->port->icount; while (1) { interruptible_sleep_on(&port->delta_msr_wait); /* see if a signal did it */ @@ -2438,7 +2414,7 @@ static int edge_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; - cnow = edge_port->icount; + cnow = port->icount; if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -2618,7 +2594,7 @@ static struct usb_serial_driver edgeport_1port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, @@ -2649,7 +2625,7 @@ static struct usb_serial_driver edgeport_2port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, - .get_icount = edge_get_icount, + .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, .chars_in_buffer = edge_chars_in_buffer, -- cgit v0.10.2 From 48ee5801381dba2e80187c08f15713e859ab5c0f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:10 +0100 Subject: USB: io_ti: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation which does not suffer from the races involved when using the deprecated sleep_on functions. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index fb9e664..8914002 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -86,6 +86,7 @@ struct edgeport_port { int baud_rate; int close_pending; int lsr_event; + struct edgeport_serial *edge_serial; struct usb_serial_port *port; __u8 bUartMode; /* Port type, 0: RS232, etc. */ @@ -1455,7 +1456,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) icount->dcd++; if (msr & EDGEPORT_MSR_DELTA_RI) icount->rng++; - wake_up_interruptible(&edge_port->port->delta_msr_wait); + wake_up_interruptible(&edge_port->port->port.delta_msr_wait); } /* Save the new modem status */ @@ -2392,8 +2393,6 @@ static int edge_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct async_icount cnow; - struct async_icount cprev; dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd); @@ -2402,32 +2401,6 @@ static int edge_ioctl(struct tty_struct *tty, dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__); return get_serial_info(edge_port, (struct serial_struct __user *) arg); - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__); - cprev = edge_port->port->icount; - while (1) { - interruptible_sleep_on(&port->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - cnow = port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* not reached */ - break; } return -ENOIOCTLCMD; } @@ -2522,7 +2495,6 @@ static int edge_port_remove(struct usb_serial_port *port) struct edgeport_port *edge_port; edge_port = usb_get_serial_port_data(port); - edge_remove_sysfs_attrs(port); kfifo_free(&edge_port->write_fifo); kfree(edge_port); @@ -2594,6 +2566,7 @@ static struct usb_serial_driver edgeport_1port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, @@ -2625,6 +2598,7 @@ static struct usb_serial_driver edgeport_2port_device = { .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, .tiocmset = edge_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .write = edge_write, .write_room = edge_write_room, -- cgit v0.10.2 From 09b0e3957549e7036a66e4597a7167e43861cfd6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:11 +0100 Subject: USB: iuu_phoenix: remove unused MSR-wait queue Remove unused, private modem-status wait queue from driver. If TIOCMIWAIT is ever implemented it must not rely on a private wait queue which may have been released when woken up. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 8eeefe3..1ccf9e4 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -55,7 +55,6 @@ static void read_rxcmd_callback(struct urb *urb); struct iuu_private { spinlock_t lock; /* store irq state */ - wait_queue_head_t delta_msr_wait; u8 line_status; int tiostatus; /* store IUART SIGNAL for tiocmget call */ u8 reset; /* if 1 reset is needed */ @@ -94,7 +93,6 @@ static int iuu_port_probe(struct usb_serial_port *port) priv->vcc = vcc_default; spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->delta_msr_wait); usb_set_serial_port_data(port, priv); -- cgit v0.10.2 From 468c740ee5372b74e9d9bd4d7ec2d81d44e67f2d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:12 +0100 Subject: USB: mct_u232: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 06d5a60..6f4303c 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -59,8 +59,6 @@ static int mct_u232_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int mct_u232_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int mct_u232_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount); static void mct_u232_throttle(struct tty_struct *tty); static void mct_u232_unthrottle(struct tty_struct *tty); @@ -99,7 +97,7 @@ static struct usb_serial_driver mct_u232_device = { .port_probe = mct_u232_port_probe, .port_remove = mct_u232_port_remove, .ioctl = mct_u232_ioctl, - .get_icount = mct_u232_get_icount, + .get_icount = usb_serial_generic_get_icount, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -113,7 +111,6 @@ struct mct_u232_private { unsigned char last_lsr; /* Line Status Register */ unsigned char last_msr; /* Modem Status Register */ unsigned int rx_flags; /* Throttling flags */ - struct async_icount icount; }; #define THROTTLED 0x01 @@ -570,7 +567,7 @@ static void mct_u232_read_int_callback(struct urb *urb) /* Record Control Line states */ mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); - mct_u232_msr_to_icount(&priv->icount, priv->last_msr); + mct_u232_msr_to_icount(&port->icount, priv->last_msr); #if 0 /* Not yet handled. See belkin_sa.c for further information */ @@ -804,7 +801,7 @@ static int mct_u232_ioctl(struct tty_struct *tty, dev_dbg(&port->dev, "%s TIOCMIWAIT", __func__); spin_lock_irqsave(&mct_u232_port->lock, flags); - cprev = mct_u232_port->icount; + cprev = port->icount; spin_unlock_irqrestore(&mct_u232_port->lock, flags); for ( ; ; ) { prepare_to_wait(&port->delta_msr_wait, @@ -819,7 +816,7 @@ static int mct_u232_ioctl(struct tty_struct *tty, return -EIO; spin_lock_irqsave(&mct_u232_port->lock, flags); - cnow = mct_u232_port->icount; + cnow = port->icount; spin_unlock_irqrestore(&mct_u232_port->lock, flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) @@ -837,35 +834,6 @@ static int mct_u232_ioctl(struct tty_struct *tty, return -ENOIOCTLCMD; } -static int mct_u232_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port); - struct async_icount *ic = &mct_u232_port->icount; - unsigned long flags; - - spin_lock_irqsave(&mct_u232_port->lock, flags); - - icount->cts = ic->cts; - icount->dsr = ic->dsr; - icount->rng = ic->rng; - icount->dcd = ic->dcd; - icount->rx = ic->rx; - icount->tx = ic->tx; - icount->frame = ic->frame; - icount->overrun = ic->overrun; - icount->parity = ic->parity; - icount->brk = ic->brk; - icount->buf_overrun = ic->buf_overrun; - - spin_unlock_irqrestore(&mct_u232_port->lock, flags); - - dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n", - __func__, icount->rx, icount->tx); - return 0; -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v0.10.2 From 128434ab5607c6bccf385765e94382cedd3d06fa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:13 +0100 Subject: USB: mct_u232: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 6f4303c..3353c9e 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "mct_u232.h" #define DRIVER_AUTHOR "Wolfgang Grandegger " @@ -57,8 +56,6 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); static int mct_u232_tiocmget(struct tty_struct *tty); static int mct_u232_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int mct_u232_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg); static void mct_u232_throttle(struct tty_struct *tty); static void mct_u232_unthrottle(struct tty_struct *tty); @@ -93,10 +90,10 @@ static struct usb_serial_driver mct_u232_device = { .break_ctl = mct_u232_break_ctl, .tiocmget = mct_u232_tiocmget, .tiocmset = mct_u232_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .attach = mct_u232_startup, .port_probe = mct_u232_port_probe, .port_remove = mct_u232_port_remove, - .ioctl = mct_u232_ioctl, .get_icount = usb_serial_generic_get_icount, }; @@ -595,7 +592,7 @@ static void mct_u232_read_int_callback(struct urb *urb) tty_kref_put(tty); } #endif - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); spin_unlock_irqrestore(&priv->lock, flags); exit: retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -783,57 +780,6 @@ static void mct_u232_unthrottle(struct tty_struct *tty) } } -static int mct_u232_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - DEFINE_WAIT(wait); - struct usb_serial_port *port = tty->driver_data; - struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port); - struct async_icount cnow, cprev; - unsigned long flags; - - dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd); - - switch (cmd) { - - case TIOCMIWAIT: - - dev_dbg(&port->dev, "%s TIOCMIWAIT", __func__); - - spin_lock_irqsave(&mct_u232_port->lock, flags); - cprev = port->icount; - spin_unlock_irqrestore(&mct_u232_port->lock, flags); - for ( ; ; ) { - prepare_to_wait(&port->delta_msr_wait, - &wait, TASK_INTERRUPTIBLE); - schedule(); - finish_wait(&port->delta_msr_wait, &wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&mct_u232_port->lock, flags); - cnow = port->icount; - spin_unlock_irqrestore(&mct_u232_port->lock, flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - - } - return -ENOIOCTLCMD; -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); -- cgit v0.10.2 From 35711578044076d00d396cb6787d3cc593a4c35a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:14 +0100 Subject: USB: mos7720: remove broken get_icount and TIOCMIWAIT Remove broken get_icount and TIOCMIWAIT support. The driver has an icount structure but it is never been updated which makes get_icount rather pointless and causes TIOCMIWAIT to always return -EIO. Note that the TIOCMIWAIT implementation has always been broken and would not work even if icount support was added as it does not wait for the modem status to change (does not use a work queue at all). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e0ebec3..d1e2bf3 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -62,7 +62,6 @@ struct moschip_port { __u8 shadowMCR; /* last MCR value received */ __u8 shadowMSR; /* last MSR value received */ char open; - struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner */ struct urb *write_urb_pool[NUM_URBS]; }; @@ -1075,9 +1074,6 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) dev_err(&port->dev, "%s - Error %d submitting read urb\n", __func__, response); - /* initialize our icount structure */ - memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount)); - /* initialize our port settings */ mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */ @@ -1803,33 +1799,6 @@ static int mos7720_tiocmset(struct tty_struct *tty, return 0; } -static int mos7720_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7720_port; - struct async_icount cnow; - - mos7720_port = usb_get_serial_port_data(port); - cnow = mos7720_port->icount; - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n", __func__, - icount->rx, icount->tx); - return 0; -} - static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, unsigned int __user *value) { @@ -1905,8 +1874,6 @@ static int mos7720_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; - struct async_icount cnow; - struct async_icount cprev; mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) @@ -1931,27 +1898,6 @@ static int mos7720_ioctl(struct tty_struct *tty, dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__); return get_serial_info(mos7720_port, (struct serial_struct __user *)arg); - - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__); - cprev = mos7720_port->icount; - while (1) { - if (signal_pending(current)) - return -ERESTARTSYS; - cnow = mos7720_port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - break; } return -ENOIOCTLCMD; @@ -2107,7 +2053,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .ioctl = mos7720_ioctl, .tiocmget = mos7720_tiocmget, .tiocmset = mos7720_tiocmset, - .get_icount = mos7720_get_icount, .set_termios = mos7720_set_termios, .write = mos7720_write, .write_room = mos7720_write_room, -- cgit v0.10.2 From c9fac85345ca8fd7f6519232a4c0024f648647b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:15 +0100 Subject: USB: mos7840: remove smp barriers from icount handling Remove SMP memory barriers from icount handling and rely on the barriers implied by wait_event, sleep and locks, while using the port lock to guarantee atomicity. This is a step in moving over to the generic icount implementations. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 979ef19..7af3d42 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -406,22 +406,14 @@ static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) icount = &mos7840_port->icount; /* update input line counters */ - if (new_msr & MOS_MSR_DELTA_CTS) { + if (new_msr & MOS_MSR_DELTA_CTS) icount->cts++; - smp_wmb(); - } - if (new_msr & MOS_MSR_DELTA_DSR) { + if (new_msr & MOS_MSR_DELTA_DSR) icount->dsr++; - smp_wmb(); - } - if (new_msr & MOS_MSR_DELTA_CD) { + if (new_msr & MOS_MSR_DELTA_CD) icount->dcd++; - smp_wmb(); - } - if (new_msr & MOS_MSR_DELTA_RI) { + if (new_msr & MOS_MSR_DELTA_RI) icount->rng++; - smp_wmb(); - } mos7840_port->delta_msr_cond = 1; wake_up_interruptible(&port->port->delta_msr_wait); @@ -443,22 +435,14 @@ static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) /* update input line counters */ icount = &port->icount; - if (new_lsr & SERIAL_LSR_BI) { + if (new_lsr & SERIAL_LSR_BI) icount->brk++; - smp_wmb(); - } - if (new_lsr & SERIAL_LSR_OE) { + if (new_lsr & SERIAL_LSR_OE) icount->overrun++; - smp_wmb(); - } - if (new_lsr & SERIAL_LSR_PE) { + if (new_lsr & SERIAL_LSR_PE) icount->parity++; - smp_wmb(); - } - if (new_lsr & SERIAL_LSR_FE) { + if (new_lsr & SERIAL_LSR_FE) icount->frame++; - smp_wmb(); - } } /************************************************************************/ @@ -778,7 +762,6 @@ static void mos7840_bulk_in_callback(struct urb *urb) tty_insert_flip_string(tport, data, urb->actual_length); tty_flip_buffer_push(tport); mos7840_port->icount.rx += urb->actual_length; - smp_wmb(); dev_dbg(&port->dev, "mos7840_port->icount.rx is %d:\n", mos7840_port->icount.rx); } @@ -1507,7 +1490,6 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, } bytes_sent = transfer_size; mos7840_port->icount.tx += transfer_size; - smp_wmb(); dev_dbg(&port->dev, "mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); exit: return bytes_sent; @@ -2133,11 +2115,14 @@ static int mos7840_get_icount(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; struct async_icount cnow; + unsigned long flags; mos7840_port = mos7840_get_port_private(port); + + spin_lock_irqsave(&port->lock, flags); cnow = mos7840_port->icount; + spin_unlock_irqrestore(&port->lock, flags); - smp_rmb(); icount->cts = cnow.cts; icount->dsr = cnow.dsr; icount->rng = cnow.rng; @@ -2166,7 +2151,7 @@ static int mos7840_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - + unsigned long flags; struct async_icount cnow; struct async_icount cprev; @@ -2197,7 +2182,9 @@ static int mos7840_ioctl(struct tty_struct *tty, case TIOCMIWAIT: dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__); + spin_lock_irqsave(&port->lock, flags); cprev = mos7840_port->icount; + spin_unlock_irqrestore(&port->lock, flags); while (1) { /* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */ mos7840_port->delta_msr_cond = 0; @@ -2213,11 +2200,14 @@ static int mos7840_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; + spin_lock_irqsave(&port->lock, flags); cnow = mos7840_port->icount; - smp_rmb(); + spin_unlock_irqrestore(&port->lock, flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || -- cgit v0.10.2 From 8c1a07ff7f28549881db0885074feb3e02b07ddb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:16 +0100 Subject: USB: mos7840: switch to generic get_icount implementation Switch to the generic get_icount implementation. Note that the interrupt counters will no longer be reset at open which is in accordance with which how the other drivers work. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 7af3d42..92feae6 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -220,7 +220,6 @@ struct moschip_port { char open_ports; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ int delta_msr_cond; - struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner of this object */ /* Offsets */ @@ -399,11 +398,10 @@ static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) struct moschip_port *mos7840_port; struct async_icount *icount; mos7840_port = port; - icount = &mos7840_port->icount; if (new_msr & (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI | MOS_MSR_DELTA_CD)) { - icount = &mos7840_port->icount; + icount = &mos7840_port->port->icount; /* update input line counters */ if (new_msr & MOS_MSR_DELTA_CTS) @@ -434,7 +432,7 @@ static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) } /* update input line counters */ - icount = &port->icount; + icount = &port->port->icount; if (new_lsr & SERIAL_LSR_BI) icount->brk++; if (new_lsr & SERIAL_LSR_OE) @@ -761,8 +759,8 @@ static void mos7840_bulk_in_callback(struct urb *urb) struct tty_port *tport = &mos7840_port->port->port; tty_insert_flip_string(tport, data, urb->actual_length); tty_flip_buffer_push(tport); - mos7840_port->icount.rx += urb->actual_length; - dev_dbg(&port->dev, "mos7840_port->icount.rx is %d:\n", mos7840_port->icount.rx); + port->icount.rx += urb->actual_length; + dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx); } if (!mos7840_port->read_urb) { @@ -1113,17 +1111,12 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) /* initialize our wait queues */ init_waitqueue_head(&mos7840_port->wait_chase); - /* initialize our icount structure */ - memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount)); - /* initialize our port settings */ /* Must set to enable ints! */ mos7840_port->shadowMCR = MCR_MASTER_IE; /* send a open port command */ mos7840_port->open = 1; /* mos7840_change_port_settings(mos7840_port,old_termios); */ - mos7840_port->icount.tx = 0; - mos7840_port->icount.rx = 0; return 0; } @@ -1489,8 +1482,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, goto exit; } bytes_sent = transfer_size; - mos7840_port->icount.tx += transfer_size; - dev_dbg(&port->dev, "mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); + port->icount.tx += transfer_size; + dev_dbg(&port->dev, "icount.tx is %d:\n", port->icount.tx); exit: return bytes_sent; @@ -2109,37 +2102,6 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port, return 0; } -static int mos7840_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct moschip_port *mos7840_port; - struct async_icount cnow; - unsigned long flags; - - mos7840_port = mos7840_get_port_private(port); - - spin_lock_irqsave(&port->lock, flags); - cnow = mos7840_port->icount; - spin_unlock_irqrestore(&port->lock, flags); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n", __func__, - icount->rx, icount->tx); - return 0; -} - /***************************************************************************** * SerialIoctl * this function handles any ioctl calls to the driver @@ -2183,7 +2145,7 @@ static int mos7840_ioctl(struct tty_struct *tty, case TIOCMIWAIT: dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__); spin_lock_irqsave(&port->lock, flags); - cprev = mos7840_port->icount; + cprev = port->icount; spin_unlock_irqrestore(&port->lock, flags); while (1) { /* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */ @@ -2201,7 +2163,7 @@ static int mos7840_ioctl(struct tty_struct *tty, return -EIO; spin_lock_irqsave(&port->lock, flags); - cnow = mos7840_port->icount; + cnow = port->icount; spin_unlock_irqrestore(&port->lock, flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && @@ -2565,7 +2527,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .break_ctl = mos7840_break, .tiocmget = mos7840_tiocmget, .tiocmset = mos7840_tiocmset, - .get_icount = mos7840_get_icount, + .get_icount = usb_serial_generic_get_icount, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, -- cgit v0.10.2 From 0c61337138763cb24901f394306715019f3272a6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:17 +0100 Subject: USB: mos7840: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 92feae6..f0b4e5c 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -219,7 +219,6 @@ struct moschip_port { char open; char open_ports; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ - int delta_msr_cond; struct usb_serial_port *port; /* loop back to the owner of this object */ /* Offsets */ @@ -413,8 +412,7 @@ static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) if (new_msr & MOS_MSR_DELTA_RI) icount->rng++; - mos7840_port->delta_msr_cond = 1; - wake_up_interruptible(&port->port->delta_msr_wait); + wake_up_interruptible(&port->port->port.delta_msr_wait); } } @@ -2113,9 +2111,6 @@ static int mos7840_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - unsigned long flags; - struct async_icount cnow; - struct async_icount cprev; if (mos7840_port_paranoia_check(port, __func__)) return -1; @@ -2141,46 +2136,6 @@ static int mos7840_ioctl(struct tty_struct *tty, case TIOCSSERIAL: dev_dbg(&port->dev, "%s TIOCSSERIAL\n", __func__); break; - - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__); - spin_lock_irqsave(&port->lock, flags); - cprev = port->icount; - spin_unlock_irqrestore(&port->lock, flags); - while (1) { - /* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */ - mos7840_port->delta_msr_cond = 0; - wait_event_interruptible(port->delta_msr_wait, - (port->serial->disconnected || - mos7840_port-> - delta_msr_cond == 1)); - - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&port->lock, flags); - cnow = port->icount; - spin_unlock_irqrestore(&port->lock, flags); - - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - break; - default: break; } @@ -2527,6 +2482,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .break_ctl = mos7840_break, .tiocmget = mos7840_tiocmget, .tiocmset = mos7840_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, -- cgit v0.10.2 From 1c9f995363d4a80de72da15af3d66a3dc2f56952 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:18 +0100 Subject: USB: oti6858: replace custom ioctl operation with tiocmiwait Replace custom ioctl operation with tiocmiwait. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 87c71cc..fd5dcb8 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -124,8 +124,6 @@ static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static void oti6858_init_termios(struct tty_struct *tty); -static int oti6858_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); static void oti6858_read_bulk_callback(struct urb *urb); static void oti6858_write_bulk_callback(struct urb *urb); @@ -136,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int oti6858_tiocmiwait(struct tty_struct *tty, unsigned long arg); static int oti6858_port_probe(struct usb_serial_port *port); static int oti6858_port_remove(struct usb_serial_port *port); @@ -150,11 +149,11 @@ static struct usb_serial_driver oti6858_device = { .open = oti6858_open, .close = oti6858_close, .write = oti6858_write, - .ioctl = oti6858_ioctl, .set_termios = oti6858_set_termios, .init_termios = oti6858_init_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, + .tiocmiwait = oti6858_tiocmiwait, .read_bulk_callback = oti6858_read_bulk_callback, .read_int_callback = oti6858_read_int_callback, .write_bulk_callback = oti6858_write_bulk_callback, @@ -650,8 +649,9 @@ static int oti6858_tiocmget(struct tty_struct *tty) return result; } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +static int oti6858_tiocmiwait(struct tty_struct *tty, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int prev, status; @@ -689,24 +689,6 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int oti6858_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - - dev_dbg(&port->dev, "%s(cmd = 0x%04x, arg = 0x%08lx)\n", __func__, cmd, arg); - - switch (cmd) { - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s(): TIOCMIWAIT\n", __func__); - return wait_modem_info(port, arg); - default: - dev_dbg(&port->dev, "%s(): 0x%04x not supported\n", __func__, cmd); - break; - } - return -ENOIOCTLCMD; -} - static void oti6858_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; -- cgit v0.10.2 From 215f6f04668e80543995300c9528ac0143fcda7f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:19 +0100 Subject: USB: oti6858: fix TIOCMIWAIT and disconnect Use tty-port modem-status-change wait queue on which processes are woken up at hangup and disconnect. Currently a process waiting on modem-status changes will not be woken on device disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index fd5dcb8..7e3e078 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -662,7 +662,7 @@ static int oti6858_tiocmiwait(struct tty_struct *tty, unsigned long arg) spin_unlock_irqrestore(&priv->lock, flags); while (1) { - wait_event_interruptible(port->delta_msr_wait, + wait_event_interruptible(port->port.delta_msr_wait, port->serial->disconnected || priv->status.pin_state != prev); if (signal_pending(current)) @@ -747,7 +747,7 @@ static void oti6858_read_int_callback(struct urb *urb) if (!priv->transient) { if (xs->pin_state != priv->status.pin_state) - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE); } -- cgit v0.10.2 From 824d11bef5540062d17380c65ee52d4b2948ca84 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:20 +0100 Subject: USB: pl2303: add custom tiocmiwait operation Break out TIOCMIWAIT handling from custom ioctl operation and use tiocmiwait operation field instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index e7e407b..1f7fd1a 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -593,8 +593,9 @@ static int pl2303_carrier_raised(struct usb_serial_port *port) return 0; } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +static int pl2303_tiocmiwait(struct tty_struct *tty, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int prevstatus; @@ -652,10 +653,6 @@ static int pl2303_ioctl(struct tty_struct *tty, return -EFAULT; return 0; - - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__); - return wait_modem_info(port, arg); default: dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd); break; @@ -836,6 +833,7 @@ static struct usb_serial_driver pl2303_device = { .set_termios = pl2303_set_termios, .tiocmget = pl2303_tiocmget, .tiocmset = pl2303_tiocmset, + .tiocmiwait = pl2303_tiocmiwait, .process_read_urb = pl2303_process_read_urb, .read_int_callback = pl2303_read_int_callback, .attach = pl2303_startup, -- cgit v0.10.2 From 6d0cad657a133818038683120fff9c5c5f8dc751 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:21 +0100 Subject: USB: pl2303: fix TIOCMIWAIT and disconnect Use tty-port modem-status-change wait queue on which processes are woken up at hangup and disconnect. Currently a process waiting on modem-status changes will not be woken on device disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1f7fd1a..997eba4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -607,7 +607,7 @@ static int pl2303_tiocmiwait(struct tty_struct *tty, unsigned long arg) spin_unlock_irqrestore(&priv->lock, flags); while (1) { - interruptible_sleep_on(&port->delta_msr_wait); + interruptible_sleep_on(&port->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -718,7 +718,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); if (priv->line_status & UART_BREAK_ERROR) usb_serial_handle_break(port); - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); tty = tty_port_tty_get(&port->port); if (!tty) @@ -782,7 +782,7 @@ static void pl2303_process_read_urb(struct urb *urb) line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); if (!urb->actual_length) return; -- cgit v0.10.2 From 0a4142fb9ceddb7e8c48570431038a91bbee7c7c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:22 +0100 Subject: USB: quatech2: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 80a8bc3..3506a2c 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -128,8 +128,6 @@ struct qt2_port_private { u8 shadowLSR; u8 shadowMSR; - struct async_icount icount; - struct usb_serial_port *port; }; @@ -501,16 +499,16 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - prev = priv->icount; + prev = port->icount; spin_unlock_irqrestore(&priv->lock, flags); while (1) { wait_event_interruptible(port->delta_msr_wait, (port->serial->disconnected || - (priv->icount.rng != prev.rng) || - (priv->icount.dsr != prev.dsr) || - (priv->icount.dcd != prev.dcd) || - (priv->icount.cts != prev.cts))); + (port->icount.rng != prev.rng) || + (port->icount.dsr != prev.dsr) || + (port->icount.dcd != prev.dcd) || + (port->icount.cts != prev.cts))); if (signal_pending(current)) return -ERESTARTSYS; @@ -519,7 +517,7 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return -EIO; spin_lock_irqsave(&priv->lock, flags); - cur = priv->icount; + cur = port->icount; spin_unlock_irqrestore(&priv->lock, flags); if ((prev.rng == cur.rng) && @@ -537,28 +535,6 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int qt2_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct qt2_port_private *priv = usb_get_serial_port_data(port); - struct async_icount cnow = priv->icount; - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - static int qt2_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -958,16 +934,13 @@ static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) if (newMSR & UART_MSR_ANY_DELTA) { /* update input line counters */ if (newMSR & UART_MSR_DCTS) - port_priv->icount.cts++; - + port->icount.cts++; if (newMSR & UART_MSR_DDSR) - port_priv->icount.dsr++; - + port->icount.dsr++; if (newMSR & UART_MSR_DDCD) - port_priv->icount.dcd++; - + port->icount.dcd++; if (newMSR & UART_MSR_TERI) - port_priv->icount.rng++; + port->icount.rng++; wake_up_interruptible(&port->delta_msr_wait); } @@ -989,7 +962,7 @@ static void qt2_update_lsr(struct usb_serial_port *port, unsigned char *ch) port_priv->shadowLSR = newLSR; spin_unlock_irqrestore(&port_priv->lock, flags); - icount = &port_priv->icount; + icount = &port->icount; if (newLSR & UART_LSR_BRK_ERROR_BITS) { @@ -1099,7 +1072,7 @@ static struct usb_serial_driver qt2_device = { .break_ctl = qt2_break_ctl, .tiocmget = qt2_tiocmget, .tiocmset = qt2_tiocmset, - .get_icount = qt2_get_icount, + .get_icount = usb_serial_generic_get_icount, .ioctl = qt2_ioctl, .set_termios = qt2_set_termios, }; -- cgit v0.10.2 From b59597047ace8531ce9d469e1de0d85792e8cef1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:23 +0100 Subject: USB: quatech2: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 3506a2c..085cad4 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -492,49 +492,6 @@ static int get_serial_info(struct usb_serial_port *port, return 0; } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) -{ - struct qt2_port_private *priv = usb_get_serial_port_data(port); - struct async_icount prev, cur; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - prev = port->icount; - spin_unlock_irqrestore(&priv->lock, flags); - - while (1) { - wait_event_interruptible(port->delta_msr_wait, - (port->serial->disconnected || - (port->icount.rng != prev.rng) || - (port->icount.dsr != prev.dsr) || - (port->icount.dcd != prev.dcd) || - (port->icount.cts != prev.cts))); - - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - cur = port->icount; - spin_unlock_irqrestore(&priv->lock, flags); - - if ((prev.rng == cur.rng) && - (prev.dsr == cur.dsr) && - (prev.dcd == cur.dcd) && - (prev.cts == cur.cts)) - return -EIO; - - if ((arg & TIOCM_RNG && (prev.rng != cur.rng)) || - (arg & TIOCM_DSR && (prev.dsr != cur.dsr)) || - (arg & TIOCM_CD && (prev.dcd != cur.dcd)) || - (arg & TIOCM_CTS && (prev.cts != cur.cts))) - return 0; - } - return 0; -} - static int qt2_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -544,10 +501,6 @@ static int qt2_ioctl(struct tty_struct *tty, case TIOCGSERIAL: return get_serial_info(port, (struct serial_struct __user *)arg); - - case TIOCMIWAIT: - return wait_modem_info(port, arg); - default: break; } @@ -942,7 +895,7 @@ static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) if (newMSR & UART_MSR_TERI) port->icount.rng++; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); } } @@ -1072,6 +1025,7 @@ static struct usb_serial_driver qt2_device = { .break_ctl = qt2_break_ctl, .tiocmget = qt2_tiocmget, .tiocmset = qt2_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .ioctl = qt2_ioctl, .set_termios = qt2_set_termios, -- cgit v0.10.2 From f65f9a50ddc6db4deebd01541bea09fb1672ce40 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:24 +0100 Subject: USB: spcp8x5: remove broken TIOCMIWAIT support Remove broken TIOCMIWAIT support. This drivers appears to implement TIOCMIWAIT but has no means of receiving modem-status interrupts. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 549ef68..e24d23b 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -472,8 +472,6 @@ static void spcp8x5_process_read_urb(struct urb *urb) status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - /* wake up the wait for termios */ - wake_up_interruptible(&port->delta_msr_wait); if (!urb->actual_length) return; @@ -509,71 +507,6 @@ static void spcp8x5_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -static int spcp8x5_wait_modem_info(struct usb_serial_port *port, - unsigned int arg) -{ - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int prevstatus; - unsigned int status; - unsigned int changed; - - spin_lock_irqsave(&priv->lock, flags); - prevstatus = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - while (1) { - /* wake up in bulk read */ - interruptible_sleep_on(&port->delta_msr_wait); - - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prevstatus^status; - - if (((arg & TIOCM_RNG) && (changed & MSR_STATUS_LINE_RI)) || - ((arg & TIOCM_DSR) && (changed & MSR_STATUS_LINE_DSR)) || - ((arg & TIOCM_CD) && (changed & MSR_STATUS_LINE_DCD)) || - ((arg & TIOCM_CTS) && (changed & MSR_STATUS_LINE_CTS))) - return 0; - - prevstatus = status; - } - /* NOTREACHED */ - return 0; -} - -static int spcp8x5_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - - dev_dbg(&port->dev, "%s (%d) cmd = 0x%04x\n", __func__, - port->number, cmd); - - switch (cmd) { - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, - port->number); - return spcp8x5_wait_modem_info(port, arg); - - default: - dev_dbg(&port->dev, "%s not supported = 0x%04x", __func__, - cmd); - break; - } - - return -ENOIOCTLCMD; -} - static int spcp8x5_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { @@ -634,7 +567,6 @@ static struct usb_serial_driver spcp8x5_device = { .carrier_raised = spcp8x5_carrier_raised, .set_termios = spcp8x5_set_termios, .init_termios = spcp8x5_init_termios, - .ioctl = spcp8x5_ioctl, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, .port_probe = spcp8x5_port_probe, -- cgit v0.10.2 From 23bd364dcf90de36f470e5a29f1684e128b53f38 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:25 +0100 Subject: USB: spcp8x5: remove broken uart-error handling Remove broken uart-error handling. This driver appears to implement uart-error handling but does not receive status interrupts or status information with bulk in transfers. Instead status was retrieved at open and used to flag only the first bulk in transfer. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index e24d23b..f34930c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -456,57 +456,6 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) return usb_serial_generic_open(tty, port); } -static void spcp8x5_process_read_urb(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct spcp8x5_private *priv = usb_get_serial_port_data(port); - unsigned char *data = urb->transfer_buffer; - unsigned long flags; - u8 status; - char tty_flag; - - /* get tty_flag from status */ - tty_flag = TTY_NORMAL; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - priv->line_status &= ~UART_STATE_TRANSIENT_MASK; - spin_unlock_irqrestore(&priv->lock, flags); - - if (!urb->actual_length) - return; - - - if (status & UART_STATE_TRANSIENT_MASK) { - /* break takes precedence over parity, which takes precedence - * over framing errors */ - if (status & UART_BREAK_ERROR) - tty_flag = TTY_BREAK; - else if (status & UART_PARITY_ERROR) - tty_flag = TTY_PARITY; - else if (status & UART_FRAME_ERROR) - tty_flag = TTY_FRAME; - dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - - /* overrun is special, not associated with a char */ - if (status & UART_OVERRUN_ERROR) - tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); - - if (status & UART_DCD) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) { - usb_serial_handle_dcd_change(port, tty, - priv->line_status & MSR_STATUS_LINE_DCD); - tty_kref_put(tty); - } - } - } - - tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag, - urb->actual_length); - tty_flip_buffer_push(&port->port); -} - static int spcp8x5_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { @@ -571,7 +520,6 @@ static struct usb_serial_driver spcp8x5_device = { .tiocmset = spcp8x5_tiocmset, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, - .process_read_urb = spcp8x5_process_read_urb, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v0.10.2 From 2d816ac6f5e0a0dc03be752c1599ed9588b403d2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:26 +0100 Subject: USB: spcp8x5: clean up code Clean up this driver somewhat. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index f34930c..a5c3a36 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -13,8 +13,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * */ #include #include @@ -28,7 +26,7 @@ #include #include -#define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" +#define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" #define SPCP8x5_007_VID 0x04FC #define SPCP8x5_007_PID 0x0201 @@ -52,7 +50,7 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); struct spcp8x5_usb_ctrl_arg { - u8 type; + u8 type; u8 cmd; u8 cmd_type; u16 value; @@ -147,10 +145,10 @@ enum spcp8x5_type { }; struct spcp8x5_private { - spinlock_t lock; + spinlock_t lock; enum spcp8x5_type type; - u8 line_control; - u8 line_status; + u8 line_control; + u8 line_status; }; static int spcp8x5_port_probe(struct usb_serial_port *port) @@ -180,7 +178,7 @@ static int spcp8x5_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); priv->type = type; - usb_set_serial_port_data(port , priv); + usb_set_serial_port_data(port, priv); return 0; } @@ -195,13 +193,16 @@ static int spcp8x5_port_remove(struct usb_serial_port *port) return 0; } -/* set the modem control line of the device. - * NOTE spcp825-007 not supported this */ -static int spcp8x5_set_ctrlLine(struct usb_device *dev, u8 value, +/* + * Set the modem control line of the device. + * + * NOTE: not supported by spcp825-007 + */ +static int spcp8x5_set_ctrl_line(struct usb_device *dev, u8 value, enum spcp8x5_type type) { int retval; - u8 mcr = 0 ; + u8 mcr = 0; if (type == SPCP825_007_TYPE) return -EPERM; @@ -215,8 +216,11 @@ static int spcp8x5_set_ctrlLine(struct usb_device *dev, u8 value, return retval; } -/* get the modem status register of the device - * NOTE spcp825-007 not supported this */ +/* + * Get the modem status register of the device. + * + * NOTE: not supported by spcp825-007 + */ static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, enum spcp8x5_type type) { @@ -249,9 +253,12 @@ static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, return ret; } -/* select the work mode. - * NOTE this function not supported by spcp825-007 */ -static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, +/* + * Select the work mode. + * + * NOTE: not supported by spcp825-007 + */ +static void spcp8x5_set_work_mode(struct usb_device *dev, u16 value, u16 index, enum spcp8x5_type type) { int ret; @@ -273,8 +280,10 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, static int spcp8x5_carrier_raised(struct usb_serial_port *port) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); + if (priv->line_status & MSR_STATUS_LINE_DCD) return 1; + return 0; } @@ -293,20 +302,17 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) | MCR_CONTROL_LINE_RTS); control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); + spcp8x5_set_ctrl_line(port->serial->dev, control, priv->type); } static void spcp8x5_init_termios(struct tty_struct *tty) { - /* for the 1st time call this function */ tty->termios = tty_std_termios; tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; tty->termios.c_ispeed = 115200; tty->termios.c_ospeed = 115200; } -/* set the serial param for transfer. we should check if we really need to - * transfer. if we set flow control we should do this too. */ static void spcp8x5_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -321,7 +327,6 @@ static void spcp8x5_set_termios(struct tty_struct *tty, int i; u8 control; - /* check that they really want us to change something */ if (!tty_termios_hw_change(&tty->termios, old_termios)) return; @@ -337,7 +342,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, if (control != priv->line_control) { control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrlLine(serial->dev, control , priv->type); + spcp8x5_set_ctrl_line(serial->dev, control , priv->type); } else { spin_unlock_irqrestore(&priv->lock, flags); } @@ -397,9 +402,9 @@ static void spcp8x5_set_termios(struct tty_struct *tty, if (cflag & PARENB) { buf[1] |= (cflag & PARODD) ? SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ; - } else + } else { buf[1] |= SET_UART_FORMAT_PAR_NONE; - + } uartdata = buf[0] | buf[1]<<8; i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), @@ -412,13 +417,11 @@ static void spcp8x5_set_termios(struct tty_struct *tty, if (cflag & CRTSCTS) { /* enable hardware flow control */ - spcp8x5_set_workMode(serial->dev, 0x000a, + spcp8x5_set_work_mode(serial->dev, 0x000a, SET_WORKING_MODE_U2C, priv->type); } } -/* open the serial port. do some usb system call. set termios and get the line - * status of the device. */ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) { struct ktermios tmp_termios; @@ -438,7 +441,7 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) if (ret) return ret; - spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); + spcp8x5_set_ctrl_line(serial->dev, priv->line_control, priv->type); /* Setup termios */ if (tty) @@ -476,7 +479,7 @@ static int spcp8x5_tiocmset(struct tty_struct *tty, control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); + return spcp8x5_set_ctrl_line(port->serial->dev, control, priv->type); } static int spcp8x5_tiocmget(struct tty_struct *tty) @@ -503,7 +506,6 @@ static int spcp8x5_tiocmget(struct tty_struct *tty) return result; } -/* All of the device info needed for the spcp8x5 SIO serial converter */ static struct usb_serial_driver spcp8x5_device = { .driver = { .owner = THIS_MODULE, @@ -511,13 +513,13 @@ static struct usb_serial_driver spcp8x5_device = { }, .id_table = id_table, .num_ports = 1, - .open = spcp8x5_open, + .open = spcp8x5_open, .dtr_rts = spcp8x5_dtr_rts, .carrier_raised = spcp8x5_carrier_raised, - .set_termios = spcp8x5_set_termios, + .set_termios = spcp8x5_set_termios, .init_termios = spcp8x5_init_termios, - .tiocmget = spcp8x5_tiocmget, - .tiocmset = spcp8x5_tiocmset, + .tiocmget = spcp8x5_tiocmget, + .tiocmset = spcp8x5_tiocmset, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; -- cgit v0.10.2 From 8413d2fd80a8e1f09a37c371610a89618980b08f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:27 +0100 Subject: USB: spcp8x5: pass usb-serial port to control functions Pass usb-serial port to the control functions for uart status and work mode. Use port device for debugging and use dev_err to report errors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index a5c3a36..5779dd8 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -198,21 +198,22 @@ static int spcp8x5_port_remove(struct usb_serial_port *port) * * NOTE: not supported by spcp825-007 */ -static int spcp8x5_set_ctrl_line(struct usb_device *dev, u8 value, - enum spcp8x5_type type) +static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) { + struct spcp8x5_private *priv = usb_get_serial_port_data(port); + struct usb_device *dev = port->serial->dev; int retval; - u8 mcr = 0; - if (type == SPCP825_007_TYPE) + if (priv->type == SPCP825_007_TYPE) return -EPERM; - mcr = (unsigned short)value; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_UART_STATUS_TYPE, SET_UART_STATUS, mcr, 0x04, NULL, 0, 100); - if (retval != 0) - dev_dbg(&dev->dev, "usb_control_msg return %#x\n", retval); + if (retval != 0) { + dev_err(&port->dev, "failed to set control lines: %d\n", + retval); + } return retval; } @@ -221,15 +222,16 @@ static int spcp8x5_set_ctrl_line(struct usb_device *dev, u8 value, * * NOTE: not supported by spcp825-007 */ -static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, - enum spcp8x5_type type) +static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) { + struct spcp8x5_private *priv = usb_get_serial_port_data(port); + struct usb_device *dev = port->serial->dev; u8 *status_buffer; int ret; /* I return Permited not support here but seem inval device * is more fix */ - if (type == SPCP825_007_TYPE) + if (priv->type == SPCP825_007_TYPE) return -EPERM; if (status == NULL) return -EINVAL; @@ -243,10 +245,10 @@ static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, status_buffer, 1, 100); if (ret < 0) - dev_dbg(&dev->dev, "Get MSR = 0x%p failed (error = %d)", - status_buffer, ret); + dev_err(&port->dev, "failed to get modem status: %d", ret); + + dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *status_buffer); - dev_dbg(&dev->dev, "0xc0:0x22:0:6 %d - 0x%p ", ret, status_buffer); status[0] = status_buffer[0]; kfree(status_buffer); @@ -258,23 +260,24 @@ static int spcp8x5_get_msr(struct usb_device *dev, u8 *status, * * NOTE: not supported by spcp825-007 */ -static void spcp8x5_set_work_mode(struct usb_device *dev, u16 value, - u16 index, enum spcp8x5_type type) +static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, + u16 index) { + struct spcp8x5_private *priv = usb_get_serial_port_data(port); + struct usb_device *dev = port->serial->dev; int ret; /* I return Permited not support here but seem inval device * is more fix */ - if (type == SPCP825_007_TYPE) + if (priv->type == SPCP825_007_TYPE) return; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_WORKING_MODE_TYPE, SET_WORKING_MODE, value, index, NULL, 0, 100); - dev_dbg(&dev->dev, "value = %#x , index = %#x\n", value, index); + dev_dbg(&port->dev, "value = %#x , index = %#x\n", value, index); if (ret < 0) - dev_dbg(&dev->dev, - "RTSCTS usb_control_msg(enable flowctrl) = %d\n", ret); + dev_err(&port->dev, "failed to set work mode: %d\n", ret); } static int spcp8x5_carrier_raised(struct usb_serial_port *port) @@ -302,7 +305,7 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) | MCR_CONTROL_LINE_RTS); control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrl_line(port->serial->dev, control, priv->type); + spcp8x5_set_ctrl_line(port, control); } static void spcp8x5_init_termios(struct tty_struct *tty) @@ -342,7 +345,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, if (control != priv->line_control) { control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - spcp8x5_set_ctrl_line(serial->dev, control , priv->type); + spcp8x5_set_ctrl_line(port, control); } else { spin_unlock_irqrestore(&priv->lock, flags); } @@ -417,8 +420,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, if (cflag & CRTSCTS) { /* enable hardware flow control */ - spcp8x5_set_work_mode(serial->dev, 0x000a, - SET_WORKING_MODE_U2C, priv->type); + spcp8x5_set_work_mode(port, 0x000a, SET_WORKING_MODE_U2C); } } @@ -441,13 +443,13 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) if (ret) return ret; - spcp8x5_set_ctrl_line(serial->dev, priv->line_control, priv->type); + spcp8x5_set_ctrl_line(port, priv->line_control); /* Setup termios */ if (tty) spcp8x5_set_termios(tty, port, &tmp_termios); - spcp8x5_get_msr(serial->dev, &status, priv->type); + spcp8x5_get_msr(port, &status); /* may be we should update uart status here but now we did not do */ spin_lock_irqsave(&priv->lock, flags); @@ -479,7 +481,7 @@ static int spcp8x5_tiocmset(struct tty_struct *tty, control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - return spcp8x5_set_ctrl_line(port->serial->dev, control, priv->type); + return spcp8x5_set_ctrl_line(port, control); } static int spcp8x5_tiocmget(struct tty_struct *tty) -- cgit v0.10.2 From 0a967f62e6623a2a7bb3a5707352b6667c0c649e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:28 +0100 Subject: USB: spcp8x5: clean up modem status retrieval Clean up modem status retrieval. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5779dd8..e0093dd 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -226,31 +226,27 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct usb_device *dev = port->serial->dev; - u8 *status_buffer; + u8 *buf; int ret; /* I return Permited not support here but seem inval device * is more fix */ if (priv->type == SPCP825_007_TYPE) return -EPERM; - if (status == NULL) - return -EINVAL; - status_buffer = kmalloc(1, GFP_KERNEL); - if (!status_buffer) + buf = kzalloc(1, GFP_KERNEL); + if (!buf) return -ENOMEM; - status_buffer[0] = status[0]; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_UART_STATUS, GET_UART_STATUS_TYPE, - 0, GET_UART_STATUS_MSR, status_buffer, 1, 100); + 0, GET_UART_STATUS_MSR, buf, 1, 100); if (ret < 0) dev_err(&port->dev, "failed to get modem status: %d", ret); - dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *status_buffer); - - status[0] = status_buffer[0]; - kfree(status_buffer); + dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x", ret, *buf); + *status = *buf; + kfree(buf); return ret; } -- cgit v0.10.2 From 4d63143db1426342f186cfe258db73571bf57897 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:29 +0100 Subject: USB: spcp8x5: reimplement device type detection Reimplement device type detection using the device id table and quirks. Device type was used to detect one device type which did not support to control functions. Add quirks to the device table and store them in the private port data at probe instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index e0093dd..a454a3f 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -28,6 +28,9 @@ #define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" +#define SPCP825_QUIRK_NO_UART_STATUS 0x01 +#define SPCP825_QUIRK_NO_WORK_MODE 0x02 + #define SPCP8x5_007_VID 0x04FC #define SPCP8x5_007_PID 0x0201 #define SPCP8x5_008_VID 0x04fc @@ -44,7 +47,9 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)}, { USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)}, { USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)}, - { USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID)}, + { USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID), + .driver_info = SPCP825_QUIRK_NO_UART_STATUS | + SPCP825_QUIRK_NO_WORK_MODE }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -136,47 +141,32 @@ struct spcp8x5_usb_ctrl_arg { #define UART_OVERRUN_ERROR 0x40 #define UART_CTS 0x80 -enum spcp8x5_type { - SPCP825_007_TYPE, - SPCP825_008_TYPE, - SPCP825_PHILIP_TYPE, - SPCP825_INTERMATIC_TYPE, - SPCP835_TYPE, -}; - struct spcp8x5_private { + unsigned quirks; spinlock_t lock; - enum spcp8x5_type type; u8 line_control; u8 line_status; }; +static int spcp8x5_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + usb_set_serial_data(serial, (void *)id); + + return 0; +} + static int spcp8x5_port_probe(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; + const struct usb_device_id *id = usb_get_serial_data(port->serial); struct spcp8x5_private *priv; - enum spcp8x5_type type = SPCP825_007_TYPE; - u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); - - if (product == 0x0201) - type = SPCP825_007_TYPE; - else if (product == 0x0231) - type = SPCP835_TYPE; - else if (product == 0x0235) - type = SPCP825_008_TYPE; - else if (product == 0x0204) - type = SPCP825_INTERMATIC_TYPE; - else if (product == 0x0471 && - serial->dev->descriptor.idVendor == cpu_to_le16(0x081e)) - type = SPCP825_PHILIP_TYPE; - dev_dbg(&serial->dev->dev, "device type = %d\n", (int)type); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; spin_lock_init(&priv->lock); - priv->type = type; + priv->quirks = id->driver_info; usb_set_serial_port_data(port, priv); @@ -193,18 +183,13 @@ static int spcp8x5_port_remove(struct usb_serial_port *port) return 0; } -/* - * Set the modem control line of the device. - * - * NOTE: not supported by spcp825-007 - */ static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct usb_device *dev = port->serial->dev; int retval; - if (priv->type == SPCP825_007_TYPE) + if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) return -EPERM; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -217,11 +202,6 @@ static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) return retval; } -/* - * Get the modem status register of the device. - * - * NOTE: not supported by spcp825-007 - */ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); @@ -229,9 +209,7 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) u8 *buf; int ret; - /* I return Permited not support here but seem inval device - * is more fix */ - if (priv->type == SPCP825_007_TYPE) + if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) return -EPERM; buf = kzalloc(1, GFP_KERNEL); @@ -251,11 +229,6 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) return ret; } -/* - * Select the work mode. - * - * NOTE: not supported by spcp825-007 - */ static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, u16 index) { @@ -263,9 +236,7 @@ static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, struct usb_device *dev = port->serial->dev; int ret; - /* I return Permited not support here but seem inval device - * is more fix */ - if (priv->type == SPCP825_007_TYPE) + if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE) return; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -518,6 +489,7 @@ static struct usb_serial_driver spcp8x5_device = { .init_termios = spcp8x5_init_termios, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, + .probe = spcp8x5_probe, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; -- cgit v0.10.2 From e1ed212d8593f8d5036709ae257e0e019dbecd4f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:30 +0100 Subject: USB: spcp8x5: add proper modem-status support Fetch modem status on carrier_raised and tiocmget. This driver appeared to support modem-status but only read the modem status registers once at open and then used that cached value for all further enquires. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index a454a3f..cf3df79 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -1,7 +1,7 @@ /* * spcp8x5 USB to serial adaptor driver * - * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) + * Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com) * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn) * Copyright (C) 2006 S1 Corp. * @@ -145,7 +145,6 @@ struct spcp8x5_private { unsigned quirks; spinlock_t lock; u8 line_control; - u8 line_status; }; static int spcp8x5_probe(struct usb_serial *serial, @@ -249,9 +248,11 @@ static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, static int spcp8x5_carrier_raised(struct usb_serial_port *port) { - struct spcp8x5_private *priv = usb_get_serial_port_data(port); + u8 msr; + int ret; - if (priv->line_status & MSR_STATUS_LINE_DCD) + ret = spcp8x5_get_msr(port, &msr); + if (ret || msr & MSR_STATUS_LINE_DCD) return 1; return 0; @@ -397,9 +398,6 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int ret; - unsigned long flags; - u8 status = 0x30; - /* status 0x30 means DSR and CTS = 1 other CDC RI and delta = 0 */ usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); @@ -412,17 +410,9 @@ static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) spcp8x5_set_ctrl_line(port, priv->line_control); - /* Setup termios */ if (tty) spcp8x5_set_termios(tty, port, &tmp_termios); - spcp8x5_get_msr(port, &status); - - /* may be we should update uart status here but now we did not do */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = status & 0xf0 ; - spin_unlock_irqrestore(&priv->lock, flags); - port->port.drain_delay = 256; return usb_serial_generic_open(tty, port); @@ -457,12 +447,15 @@ static int spcp8x5_tiocmget(struct tty_struct *tty) struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; - unsigned int status; + u8 status; unsigned int result; + result = spcp8x5_get_msr(port, &status); + if (result) + return result; + spin_lock_irqsave(&priv->lock, flags); mcr = priv->line_control; - status = priv->line_status; spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) -- cgit v0.10.2 From 31ecdb6befeab20d93b49b8dd640c081b48912d0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:31 +0100 Subject: USB: ssu100: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 45b8c29..8d94a5a 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -61,7 +61,6 @@ struct ssu100_port_private { spinlock_t status_lock; u8 shadowLSR; u8 shadowMSR; - struct async_icount icount; }; static inline int ssu100_control_msg(struct usb_device *dev, @@ -345,16 +344,16 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) unsigned long flags; spin_lock_irqsave(&priv->status_lock, flags); - prev = priv->icount; + prev = port->icount; spin_unlock_irqrestore(&priv->status_lock, flags); while (1) { wait_event_interruptible(port->delta_msr_wait, (port->serial->disconnected || - (priv->icount.rng != prev.rng) || - (priv->icount.dsr != prev.dsr) || - (priv->icount.dcd != prev.dcd) || - (priv->icount.cts != prev.cts))); + (port->icount.rng != prev.rng) || + (port->icount.dsr != prev.dsr) || + (port->icount.dcd != prev.dcd) || + (port->icount.cts != prev.cts))); if (signal_pending(current)) return -ERESTARTSYS; @@ -363,7 +362,7 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return -EIO; spin_lock_irqsave(&priv->status_lock, flags); - cur = priv->icount; + cur = port->icount; spin_unlock_irqrestore(&priv->status_lock, flags); if ((prev.rng == cur.rng) && @@ -381,30 +380,6 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int ssu100_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct ssu100_port_private *priv = usb_get_serial_port_data(port); - struct async_icount cnow = priv->icount; - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - - - static int ssu100_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -527,13 +502,13 @@ static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) if (msr & UART_MSR_ANY_DELTA) { /* update input line counters */ if (msr & UART_MSR_DCTS) - priv->icount.cts++; + port->icount.cts++; if (msr & UART_MSR_DDSR) - priv->icount.dsr++; + port->icount.dsr++; if (msr & UART_MSR_DDCD) - priv->icount.dcd++; + port->icount.dcd++; if (msr & UART_MSR_TERI) - priv->icount.rng++; + port->icount.rng++; wake_up_interruptible(&port->delta_msr_wait); } } @@ -553,22 +528,22 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr, /* we always want to update icount, but we only want to * update tty_flag for one case */ if (lsr & UART_LSR_BI) { - priv->icount.brk++; + port->icount.brk++; *tty_flag = TTY_BREAK; usb_serial_handle_break(port); } if (lsr & UART_LSR_PE) { - priv->icount.parity++; + port->icount.parity++; if (*tty_flag == TTY_NORMAL) *tty_flag = TTY_PARITY; } if (lsr & UART_LSR_FE) { - priv->icount.frame++; + port->icount.frame++; if (*tty_flag == TTY_NORMAL) *tty_flag = TTY_FRAME; } if (lsr & UART_LSR_OE){ - priv->icount.overrun++; + port->icount.overrun++; if (*tty_flag == TTY_NORMAL) *tty_flag = TTY_OVERRUN; } @@ -632,7 +607,7 @@ static struct usb_serial_driver ssu100_device = { .process_read_urb = ssu100_process_read_urb, .tiocmget = ssu100_tiocmget, .tiocmset = ssu100_tiocmset, - .get_icount = ssu100_get_icount, + .get_icount = usb_serial_generic_get_icount, .ioctl = ssu100_ioctl, .set_termios = ssu100_set_termios, }; -- cgit v0.10.2 From c24c838e8effd55ba330d1966d9846400af4bec8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:32 +0100 Subject: USB: ssu100: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 8d94a5a..5b62dbb 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -337,49 +337,6 @@ static int get_serial_info(struct usb_serial_port *port, return 0; } -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) -{ - struct ssu100_port_private *priv = usb_get_serial_port_data(port); - struct async_icount prev, cur; - unsigned long flags; - - spin_lock_irqsave(&priv->status_lock, flags); - prev = port->icount; - spin_unlock_irqrestore(&priv->status_lock, flags); - - while (1) { - wait_event_interruptible(port->delta_msr_wait, - (port->serial->disconnected || - (port->icount.rng != prev.rng) || - (port->icount.dsr != prev.dsr) || - (port->icount.dcd != prev.dcd) || - (port->icount.cts != prev.cts))); - - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->status_lock, flags); - cur = port->icount; - spin_unlock_irqrestore(&priv->status_lock, flags); - - if ((prev.rng == cur.rng) && - (prev.dsr == cur.dsr) && - (prev.dcd == cur.dcd) && - (prev.cts == cur.cts)) - return -EIO; - - if ((arg & TIOCM_RNG && (prev.rng != cur.rng)) || - (arg & TIOCM_DSR && (prev.dsr != cur.dsr)) || - (arg & TIOCM_CD && (prev.dcd != cur.dcd)) || - (arg & TIOCM_CTS && (prev.cts != cur.cts))) - return 0; - } - return 0; -} - static int ssu100_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -391,10 +348,6 @@ static int ssu100_ioctl(struct tty_struct *tty, case TIOCGSERIAL: return get_serial_info(port, (struct serial_struct __user *) arg); - - case TIOCMIWAIT: - return wait_modem_info(port, arg); - default: break; } @@ -509,7 +462,7 @@ static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) port->icount.dcd++; if (msr & UART_MSR_TERI) port->icount.rng++; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); } } @@ -607,6 +560,7 @@ static struct usb_serial_driver ssu100_device = { .process_read_urb = ssu100_process_read_urb, .tiocmget = ssu100_tiocmget, .tiocmset = ssu100_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .ioctl = ssu100_ioctl, .set_termios = ssu100_set_termios, -- cgit v0.10.2 From 783ca3557b83b915d1b7a47240e7ab6acca0aac8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:33 +0100 Subject: USB: ti_usb_3410_5052: switch to generic get_icount implementation Switch to the generic get_icount implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 73deb02..9997725 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -73,7 +73,6 @@ struct ti_port { unsigned int tp_uart_base_addr; int tp_flags; int tp_closing_wait;/* in .01 secs */ - struct async_icount tp_icount; wait_queue_head_t tp_write_wait; struct ti_device *tp_tdev; struct usb_serial_port *tp_port; @@ -108,8 +107,6 @@ static void ti_throttle(struct tty_struct *tty); static void ti_unthrottle(struct tty_struct *tty); static int ti_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static int ti_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount); static void ti_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static int ti_tiocmget(struct tty_struct *tty); @@ -235,7 +232,7 @@ static struct usb_serial_driver ti_1port_device = { .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, - .get_icount = ti_get_icount, + .get_icount = usb_serial_generic_get_icount, .break_ctl = ti_break, .read_int_callback = ti_interrupt_callback, .read_bulk_callback = ti_bulk_in_callback, @@ -265,7 +262,7 @@ static struct usb_serial_driver ti_2port_device = { .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, - .get_icount = ti_get_icount, + .get_icount = usb_serial_generic_get_icount, .break_ctl = ti_break, .read_int_callback = ti_interrupt_callback, .read_bulk_callback = ti_bulk_in_callback, @@ -480,8 +477,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) port_number = port->number - port->serial->minor; - memset(&(tport->tp_icount), 0x00, sizeof(tport->tp_icount)); - tport->tp_msr = 0; tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR); @@ -731,31 +726,6 @@ static void ti_unthrottle(struct tty_struct *tty) } } -static int ti_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct usb_serial_port *port = tty->driver_data; - struct ti_port *tport = usb_get_serial_port_data(port); - struct async_icount cnow = tport->tp_icount; - - dev_dbg(&port->dev, "%s - TIOCGICOUNT RX=%d, TX=%d\n", __func__, - cnow.rx, cnow.tx); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - icount->rx = cnow.rx; - icount->tx = cnow.tx; - icount->frame = cnow.frame; - icount->overrun = cnow.overrun; - icount->parity = cnow.parity; - icount->brk = cnow.brk; - icount->buf_overrun = cnow.buf_overrun; - - return 0; -} - static int ti_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -780,7 +750,7 @@ static int ti_ioctl(struct tty_struct *tty, (struct serial_struct __user *)arg); case TIOCMIWAIT: dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__); - cprev = tport->tp_icount; + cprev = port->icount; while (1) { interruptible_sleep_on(&port->delta_msr_wait); if (signal_pending(current)) @@ -789,7 +759,7 @@ static int ti_ioctl(struct tty_struct *tty, if (port->serial->disconnected) return -EIO; - cnow = tport->tp_icount; + cnow = port->icount; if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -1156,7 +1126,7 @@ static void ti_bulk_in_callback(struct urb *urb) else ti_recv(port, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); - tport->tp_icount.rx += urb->actual_length; + port->icount.rx += urb->actual_length; spin_unlock(&tport->tp_lock); } @@ -1265,7 +1235,7 @@ static void ti_send(struct ti_port *tport) /* TODO: reschedule ti_send */ } else { spin_lock_irqsave(&tport->tp_lock, flags); - tport->tp_icount.tx += count; + port->icount.tx += count; spin_unlock_irqrestore(&tport->tp_lock, flags); } @@ -1385,7 +1355,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) if (msr & TI_MSR_DELTA_MASK) { spin_lock_irqsave(&tport->tp_lock, flags); - icount = &tport->tp_icount; + icount = &tport->tp_port->icount; if (msr & TI_MSR_DELTA_CTS) icount->cts++; if (msr & TI_MSR_DELTA_DSR) -- cgit v0.10.2 From 6c75e26067c8b8a17aea1bb54b9a8dc9d229c5e8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:34 +0100 Subject: USB: ti_usb_3410_5052: switch to generic TIOCMIWAIT implementation Switch to the generic TIOCMIWAIT implementation which does not suffer from the races involved when using the deprecated sleep_on functions. This also fixes the issue with processes waiting for modem-status-changes not being woken up at disconnect. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 9997725..0726859 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -232,6 +232,7 @@ static struct usb_serial_driver ti_1port_device = { .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .break_ctl = ti_break, .read_int_callback = ti_interrupt_callback, @@ -262,6 +263,7 @@ static struct usb_serial_driver ti_2port_device = { .set_termios = ti_set_termios, .tiocmget = ti_tiocmget, .tiocmset = ti_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, .break_ctl = ti_break, .read_int_callback = ti_interrupt_callback, @@ -731,8 +733,6 @@ static int ti_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct async_icount cnow; - struct async_icount cprev; dev_dbg(&port->dev, "%s - cmd = 0x%04X\n", __func__, cmd); @@ -748,29 +748,6 @@ static int ti_ioctl(struct tty_struct *tty, dev_dbg(&port->dev, "%s - TIOCSSERIAL\n", __func__); return ti_set_serial_info(tty, tport, (struct serial_struct __user *)arg); - case TIOCMIWAIT: - dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__); - cprev = port->icount; - while (1) { - interruptible_sleep_on(&port->delta_msr_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - cnow = port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) - return 0; - cprev = cnow; - } - break; } return -ENOIOCTLCMD; } @@ -1364,7 +1341,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) icount->dcd++; if (msr & TI_MSR_DELTA_RI) icount->rng++; - wake_up_interruptible(&tport->tp_port->delta_msr_wait); + wake_up_interruptible(&tport->tp_port->port.delta_msr_wait); spin_unlock_irqrestore(&tport->tp_lock, flags); } -- cgit v0.10.2 From 53ab34dc50ad99366257d34cdb8a84f24250d611 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:35 +0100 Subject: USB: serial: remove unused MSR-wait queue Remove the port MSR-wait queue now that all drivers have been migrated to the tty-port queue. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c786ee7..b9b0f7b4 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -68,7 +68,6 @@ * port. * @flags: usb serial port flags * @write_wait: a wait_queue_head_t used by the port. - * @delta_msr_wait: modem-status-change wait queue * @work: work queue entry for the line discipline waking up. * @throttled: nonzero if the read urb is inactive to throttle the device * @throttle_req: nonzero if the tty wants to throttle us @@ -116,7 +115,6 @@ struct usb_serial_port { unsigned long flags; wait_queue_head_t write_wait; - wait_queue_head_t delta_msr_wait; struct work_struct work; char throttled; char throttle_req; -- cgit v0.10.2 From ecb9eb8064ba705e9182772db28809a755c920e8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:36 +0100 Subject: USB: cp210x: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4747d1c..2c65955 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -462,11 +462,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) static void cp210x_close(struct usb_serial_port *port) { usb_serial_generic_close(port); - - mutex_lock(&port->serial->disc_mutex); - if (!port->serial->disconnected) - cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); - mutex_unlock(&port->serial->disc_mutex); + cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); } /* -- cgit v0.10.2 From 87ddf4dc16d93db92431d57c4b2fcd640f428228 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:37 +0100 Subject: USB: cypress_m8: remove bogus disconnect test from close Remove disconnected test from close which did not protect any device IO at all. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e4a62cf..d341555 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -632,12 +632,6 @@ static void cypress_close(struct usb_serial_port *port) struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; - /* writing is potentially harmful, lock must be taken */ - mutex_lock(&port->serial->disc_mutex); - if (port->serial->disconnected) { - mutex_unlock(&port->serial->disc_mutex); - return; - } spin_lock_irqsave(&priv->lock, flags); kfifo_reset_out(&priv->write_fifo); spin_unlock_irqrestore(&priv->lock, flags); @@ -649,7 +643,6 @@ static void cypress_close(struct usb_serial_port *port) if (stats) dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", priv->bytes_in, priv->bytes_out, priv->cmd_count); - mutex_unlock(&port->serial->disc_mutex); } /* cypress_close */ -- cgit v0.10.2 From bca87e9efb5258749f016a63b2abed40229c68c9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:38 +0100 Subject: USB: io_ti: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 8914002..0ccc422 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1907,21 +1907,10 @@ static void edge_close(struct usb_serial_port *port) kfifo_reset_out(&edge_port->write_fifo); spin_unlock_irqrestore(&edge_port->ep_lock, flags); - /* assuming we can still talk to the device, - * send a close port command to it */ dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__); port_number = port->number - port->serial->minor; - - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) { - send_cmd(serial->dev, - UMPC_CLOSE_PORT, - (__u8)(UMPM_UART1_PORT + port_number), - 0, - NULL, - 0); - } - mutex_unlock(&serial->disc_mutex); + send_cmd(serial->dev, UMPC_CLOSE_PORT, + (__u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0); mutex_lock(&edge_serial->es_lock); --edge_port->edge_serial->num_ports_open; -- cgit v0.10.2 From 9fcb2e6e7e34e3ca6a6f364a1d65279e44749e07 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:39 +0100 Subject: USB: kl5kusb105: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 57fd001..1b4054f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -341,22 +341,17 @@ static void klsi_105_close(struct usb_serial_port *port) { int rc; - mutex_lock(&port->serial->disc_mutex); - if (!port->serial->disconnected) { - /* send READ_OFF */ - rc = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - KL5KUSB105A_SIO_CONFIGURE, - USB_TYPE_VENDOR | USB_DIR_OUT, - KL5KUSB105A_SIO_CONFIGURE_READ_OFF, - 0, /* index */ - NULL, 0, - KLSI_TIMEOUT); - if (rc < 0) - dev_err(&port->dev, - "Disabling read failed (error = %d)\n", rc); - } - mutex_unlock(&port->serial->disc_mutex); + /* send READ_OFF */ + rc = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); + if (rc < 0) + dev_err(&port->dev, "failed to disable read: %d\n", rc); /* shutdown our bulk reads and writes */ usb_serial_generic_close(port); -- cgit v0.10.2 From 5813f281bba6603bed52d6005a9d6a9f23f5c061 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:40 +0100 Subject: USB: metro-usb: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index bf3c7a2..47e2477 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -177,10 +177,7 @@ static void metrousb_cleanup(struct usb_serial_port *port) usb_unlink_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_in_urb); - mutex_lock(&port->serial->disc_mutex); - if (!port->serial->disconnected) - metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); - mutex_unlock(&port->serial->disc_mutex); + metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); } static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v0.10.2 From cf41aa9e19052d467b54786090e1d3ba9104e394 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:41 +0100 Subject: USB: mos7720: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index d1e2bf3..fc506bb 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1140,16 +1140,9 @@ static void mos7720_close(struct usb_serial_port *port) usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); - mutex_lock(&serial->disc_mutex); - /* these commands must not be issued if the device has - * been disconnected */ - if (!serial->disconnected) { - write_mos_reg(serial, port->number - port->serial->minor, - MCR, 0x00); - write_mos_reg(serial, port->number - port->serial->minor, - IER, 0x00); - } - mutex_unlock(&serial->disc_mutex); + write_mos_reg(serial, port->number - port->serial->minor, MCR, 0x00); + write_mos_reg(serial, port->number - port->serial->minor, IER, 0x00); + mos7720_port->open = 0; } -- cgit v0.10.2 From 94c51dca2ce9a3dd0c52ec6f57df4fb3e81e3ec7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:42 +0100 Subject: USB: opticon: fix return value of tiocmset Make sure we return 0 or a negative error number appropriate for userspace on errors. Currently 1 rather than 0 is returned on successful operation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index e13e1a4..6af5bb8 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -120,7 +120,10 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype, 0, 0, buffer, 1, 0); kfree(buffer); - return retval; + if (retval < 0) + return retval; + + return 0; } static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) @@ -329,10 +332,13 @@ static int opticon_tiocmset(struct tty_struct *tty, /* Send the new RTS state to the connected device */ mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) + if (!serial->disconnected) { ret = send_control_msg(port, CONTROL_RTS, !rts); - else + if (ret) + ret = usb_translate_errors(ret); + } else { ret = -ENODEV; + } mutex_unlock(&serial->disc_mutex); return ret; -- cgit v0.10.2 From 94bcef624548fd2bcf94cb7f9fcbaedbd96d5742 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:43 +0100 Subject: USB: opticon: remove disconnect test from tiocmset Remove unnecessary disconnect test in tiocmset. No ioctls will be made after disconnect returns. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 6af5bb8..5f4b0cd 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -309,7 +309,6 @@ static int opticon_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; struct opticon_private *priv = usb_get_serial_port_data(port); unsigned long flags; bool rts; @@ -330,18 +329,11 @@ static int opticon_tiocmset(struct tty_struct *tty, if (!changed) return 0; - /* Send the new RTS state to the connected device */ - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) { - ret = send_control_msg(port, CONTROL_RTS, !rts); - if (ret) - ret = usb_translate_errors(ret); - } else { - ret = -ENODEV; - } - mutex_unlock(&serial->disc_mutex); + ret = send_control_msg(port, CONTROL_RTS, !rts); + if (ret) + return usb_translate_errors(ret); - return ret; + return 0; } static int get_serial_info(struct usb_serial_port *port, -- cgit v0.10.2 From aff5b323b319758257a838cdc45f494c6674447c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:44 +0100 Subject: USB: pl2303: fix return value of tiocmset Make sure we return 0 or a negative error number appropriate for userspace on errors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 997eba4..4dff179 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -550,10 +550,13 @@ static int pl2303_tiocmset(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) + if (!serial->disconnected) { ret = pl2303_set_control_lines(port, control); - else + if (ret) + ret = usb_translate_errors(ret); + } else { ret = -ENODEV; + } mutex_unlock(&serial->disc_mutex); return ret; -- cgit v0.10.2 From 5ddbb26b8b571231cace9f013b2d0ae66229b316 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:45 +0100 Subject: USB: pl2303: remove disconnect test from tiocmset Remove unnecessary disconnect test in tiocmset. No ioctls will be made after disconnect returns. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 4dff179..7151659 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -531,7 +531,6 @@ static int pl2303_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -549,17 +548,11 @@ static int pl2303_tiocmset(struct tty_struct *tty, control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) { - ret = pl2303_set_control_lines(port, control); - if (ret) - ret = usb_translate_errors(ret); - } else { - ret = -ENODEV; - } - mutex_unlock(&serial->disc_mutex); + ret = pl2303_set_control_lines(port, control); + if (ret) + return usb_translate_errors(ret); - return ret; + return 0; } static int pl2303_tiocmget(struct tty_struct *tty) -- cgit v0.10.2 From 81ae1b3c31a91358950011fee16698226a1bda45 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:46 +0100 Subject: USB: quatech2: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 085cad4..3c27852 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -422,12 +422,6 @@ static void qt2_close(struct usb_serial_port *port) port_priv->urb_in_use = false; spin_unlock_irqrestore(&port_priv->urb_lock, flags); - mutex_lock(&port->serial->disc_mutex); - if (port->serial->disconnected) { - mutex_unlock(&port->serial->disc_mutex); - return; - } - /* flush the port transmit buffer */ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), @@ -458,8 +452,6 @@ static void qt2_close(struct usb_serial_port *port) if (i < 0) dev_err(&port->dev, "%s - close port failed %i\n", __func__, i); - - mutex_unlock(&port->serial->disc_mutex); } static void qt2_disconnect(struct usb_serial *serial) -- cgit v0.10.2 From 7620c33afb643451e1cbdc7fa666842440f8af63 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:47 +0100 Subject: USB: visor: always disable uart on close Always try to disable the uart on close. Since the switch to tty ports, close will be called as part of shutdown before disconnect returns. Hence there is no need to check the disconnected flag, and we can put devices in disabled states also on driver unbind. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 1129aa7..7573ec8 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -257,24 +257,18 @@ static void visor_close(struct usb_serial_port *port) { unsigned char *transfer_buffer; - /* shutdown our urbs */ usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); - mutex_lock(&port->serial->disc_mutex); - if (!port->serial->disconnected) { - /* Try to send shutdown message, unless the device is gone */ - transfer_buffer = kmalloc(0x12, GFP_KERNEL); - if (transfer_buffer) { - usb_control_msg(port->serial->dev, + transfer_buffer = kmalloc(0x12, GFP_KERNEL); + if (!transfer_buffer) + return; + usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), VISOR_CLOSE_NOTIFICATION, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); - kfree(transfer_buffer); - } - } - mutex_unlock(&port->serial->disc_mutex); + kfree(transfer_buffer); } static void visor_read_int_callback(struct urb *urb) -- cgit v0.10.2 From 91972724525829a9399a2cb41e60cad853b09229 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:48 +0100 Subject: USB: garmin_gps: remove bogus disconnect test in close Remove bogus disconnect test for serial device being NULL in close. This can never happen as close is guaranteed to be called before the last tty reference is dropped (and port->serial is cleared). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 81caf56..1ade6cf2 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -946,16 +946,12 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) static void garmin_close(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); dev_dbg(&port->dev, "%s - port %d - mode=%d state=%d flags=0x%X\n", __func__, port->number, garmin_data_p->mode, garmin_data_p->state, garmin_data_p->flags); - if (!serial) - return; - garmin_clear(garmin_data_p); /* shutdown our urbs */ -- cgit v0.10.2 From 5dbabace4f8514500c729d3e4fe10b2811a35f15 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:49 +0100 Subject: USB: garmin_gps: remove bogus disconnect test in bulk callback Remove bogus disconnect test for serial device being NULL in read bulk callback. This can never happen as the port read urb is killed (and poisoned) at close, which in turn is guaranteed to be called before the last tty reference is dropped (and port->serial is cleared). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 1ade6cf2..b110c57 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1181,17 +1181,11 @@ static void garmin_read_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; - struct usb_serial *serial = port->serial; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; int status = urb->status; int retval; - if (!serial) { - dev_dbg(&urb->dev->dev, "%s - bad serial pointer, exiting\n", __func__); - return; - } - if (status) { dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", __func__, status); -- cgit v0.10.2 From 8976f1df91aa36e0cea312ed4e547831db57261a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:50 +0100 Subject: USB: iuu_phoenix: remove bogus disconnect test in close Remove bogus disconnect test for serial device being NULL in close. This can never happen as close is guaranteed to be called before the last tty reference is dropped (and port->serial is cleared). Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 1ccf9e4..9d74c27 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -942,11 +942,6 @@ static void iuu_set_termios(struct tty_struct *tty, static void iuu_close(struct usb_serial_port *port) { /* iuu_led (port,255,0,0,0); */ - struct usb_serial *serial; - - serial = port->serial; - if (!serial) - return; iuu_uart_off(port); -- cgit v0.10.2 From 659597b77493959c699b4e99841a2c778eaa70bc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 Mar 2013 12:37:51 +0100 Subject: USB: serial: update copyright information Update copyright information. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 1199dc5..9a5295e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1,7 +1,7 @@ /* * USB FTDI SIO driver * - * Copyright (C) 2009 - 2010 + * Copyright (C) 2009 - 2013 * Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 5e55761..297665f 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -1,7 +1,7 @@ /* * USB Serial Converter Generic functions * - * Copyright (C) 2010 - 2011 Johan Hovold (jhovold@gmail.com) + * Copyright (C) 2010 - 2013 Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4568816..5eb96df 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1,6 +1,7 @@ /* * USB Serial Converter driver * + * Copyright (C) 2009 - 2013 Johan Hovold (jhovold@gmail.com) * Copyright (C) 1999 - 2012 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2000 Peter Berger (pberger@brimson.com) * Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com) -- cgit v0.10.2 From 60630c2eabd40fb119a1b88af364003d2915b370 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 09:15:45 +0200 Subject: usb: gadget: mv_u3d: drop ARCH dependency this driver compiles fine everywhere which means we can use linux-next to compile it for us frequently. By dropping the arch dependency, we also ensure driver writers don't add virtual arch-depdencies to the driver by e.g. using the wrong headers. While at that, fix Marvell's USB3 PHY dependency, that's the driver which depends on CPU_MM3, not mv_u3d_core. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index af5cc35..261b1e3 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -324,7 +324,6 @@ config USB_MV_UDC config USB_MV_U3D tristate "MARVELL PXA2128 USB 3.0 controller" - depends on CPU_MMP3 help MARVELL PXA2128 Processor series include a super speed USB3.0 device controller, which support super speed USB peripheral. diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7e8fe0f..372db48 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -45,7 +45,7 @@ config ISP1301_OMAP config MV_U3D_PHY bool "Marvell USB 3.0 PHY controller Driver" - depends on USB_MV_U3D + depends on CPU_MMP3 help Enable this to support Marvell USB 3.0 phy controller for Marvell SoC. -- cgit v0.10.2 From 69147121abf569bf3655a2000c6511f1f016d9f8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 21 Mar 2013 12:19:31 +0200 Subject: usb: gadget: net2272: fix sparse warnings Fix the following sparse warnings: drivers/usb/gadget/net2272.c:916:13: warning: Using plain integer as NULL pointer drivers/usb/gadget/net2272.c:1624:9: warning: Using plain integer as NULL pointer drivers/usb/gadget/net2272.c:1552:30: warning: right shift by bigger than source value Note that the last warning is an actual bug, since ep->dma is a one bit value which is shifted by one bit in code. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 03e4104..ce450a1 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -911,7 +911,7 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } } } - if (likely(req != 0)) + if (likely(req)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue))) @@ -1538,7 +1538,7 @@ net2272_handle_dma(struct net2272_ep *ep) | (ep->dev->dma_eot_polarity << EOT_POLARITY) | (ep->dev->dma_dack_polarity << DACK_POLARITY) | (ep->dev->dma_dreq_polarity << DREQ_POLARITY) - | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT)); + | (ep->dma << DMA_ENDPOINT_SELECT)); ep->dev->dma_busy = 0; @@ -1611,7 +1611,7 @@ net2272_handle_ep(struct net2272_ep *ep) ep->irqs++; dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n", - ep->ep.name, stat0, stat1, req ? &req->req : 0); + ep->ep.name, stat0, stat1, req ? &req->req : NULL); net2272_ep_write(ep, EP_STAT0, stat0 & ~((1 << NAK_OUT_PACKETS) -- cgit v0.10.2 From ad303db62d280fb8e5b4174273f4fb8b03b64747 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 16:47:52 +0200 Subject: usb: gadget: net2280: fix sparse warnings fix the following sparse warnings: drivers/usb/gadget/net2280.c:2063:13: warning: Using plain integer as NULL pointer drivers/usb/gadget/net2280.c:2321:68: warning: Using plain integer as NULL pointer drivers/usb/gadget/net2280.c:2349:68: warning: Using plain integer as NULL pointer drivers/usb/gadget/net2280.c:2371:68: warning: Using plain integer as NULL pointer Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 691cc65..e869188 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2060,7 +2060,7 @@ static void handle_ep_small (struct net2280_ep *ep) return; /* manual DMA queue advance after short OUT */ - if (likely (ep->dma != 0)) { + if (likely (ep->dma)) { if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { u32 count; int stopped = ep->stopped; @@ -2318,7 +2318,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device and interface status */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) goto delegate; - if ((e = get_ep_by_addr (dev, w_index)) == 0 + if ((e = get_ep_by_addr (dev, w_index)) == NULL || w_length > 2) goto do_stall; @@ -2346,7 +2346,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == NULL) goto do_stall; if (e->wedged) { VDEBUG(dev, "%s wedged, halt not cleared\n", @@ -2368,7 +2368,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == NULL) goto do_stall; if (e->ep.name == ep0name) goto do_stall; -- cgit v0.10.2 From 814cf212f9d9e8a270e8cded8452c1c992fb21da Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 16:50:47 +0200 Subject: usb: gadget: dummy: fix sparse warning fix the following sparse warning: drivers/usb/gadget/dummy_hcd.c:1912:12: warning: symbol 'usb3_bos_desc' was not declared. Should it be static? Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index ce75155..a792e32 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1906,7 +1906,7 @@ done: } /* usb 3.0 root hub device descriptor */ -struct { +static struct { struct usb_bos_descriptor bos; struct usb_ss_cap_descriptor ss_cap; } __packed usb3_bos_desc = { -- cgit v0.10.2 From 3f8b620124d2f331b71936a8d17e0fa493182632 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 16:54:01 +0200 Subject: usb: gadget: pch_udc: fix sparse warnings fix the following sparse warnings: drivers/usb/gadget/pch_udc.c:1483:9: warning: context imbalance in 'complete_req' - unexpected unlock drivers/usb/gadget/pch_udc.c:2408:28: warning: context imbalance in 'pch_udc_svc_control_out' - unexpected unlock Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 44aacf7..24174e1 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -1440,6 +1440,8 @@ static void pch_vbus_gpio_free(struct pch_udc_dev *dev) */ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, int status) + __releases(&dev->lock) + __acquires(&dev->lock) { struct pch_udc_dev *dev; unsigned halted = ep->halted; @@ -2381,6 +2383,8 @@ static void pch_udc_svc_control_in(struct pch_udc_dev *dev) * @dev: Reference to the device structure */ static void pch_udc_svc_control_out(struct pch_udc_dev *dev) + __releases(&dev->lock) + __acquires(&dev->lock) { u32 stat; int setup_supported; -- cgit v0.10.2 From 94a06018dead7fc5f92238ececf31ed601ee528c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 16:58:18 +0200 Subject: usb: gadget: mv_udc_core: fix sparse warnings fix the following sparse warnings: drivers/usb/gadget/mv_udc_core.c:1850:6: warning: symbol 'irq_process_reset' was not declared. Should it be static? drivers/usb/gadget/mv_udc_core.c:247:9: warning: context imbalance in 'done' - unexpected unlock drivers/usb/gadget/mv_udc_core.c:1692:25: warning: context imbalance in 'handle_setup_packet' - unexpected unlock Note that there are still other sparse warnings which aren't trivial to fix, so I left them out: drivers/usb/gadget/mv_udc_core.c:848:34: warning: incorrect type in argument 1 (different address spaces) drivers/usb/gadget/mv_udc_core.c:848:34: expected void const volatile [noderef] *addr drivers/usb/gadget/mv_udc_core.c:848:34: got unsigned int * drivers/usb/gadget/mv_udc_core.c:849:42: warning: incorrect type in argument 2 (different address spaces) drivers/usb/gadget/mv_udc_core.c:849:42: expected void volatile [noderef] *addr drivers/usb/gadget/mv_udc_core.c:849:42: got unsigned int * Acked-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 9a68c05..88be74e 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -212,6 +212,8 @@ static int process_ep_req(struct mv_udc *udc, int index, * request is still in progress. */ static void done(struct mv_ep *ep, struct mv_req *req, int status) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { struct mv_udc *udc = NULL; unsigned char stopped = ep->stopped; @@ -1651,6 +1653,8 @@ out: static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, struct usb_ctrlrequest *setup) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { bool delegate = false; @@ -1847,7 +1851,7 @@ static void irq_process_tr_complete(struct mv_udc *udc) } } -void irq_process_reset(struct mv_udc *udc) +static void irq_process_reset(struct mv_udc *udc) { u32 tmp; unsigned int loops; -- cgit v0.10.2 From 6fbb2f7daeeeb0865fc9b7799607f4cb5481a96e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 17:01:42 +0200 Subject: usb: gadget: mv_u3d: fix sparse warnings fix the following sparse warnings: drivers/usb/gadget/mv_u3d_core.c:223:20: warning: context imbalance in 'mv_u3d_done' - unexpected unlock drivers/usb/gadget/mv_u3d_core.c:1562:25: warning: context imbalance in 'mv_u3d_handle_setup_packet' - unexpected unlock Note that the non-trivial sparse warnings are left out of this commit due to lack of HW to test: drivers/usb/gadget/mv_u3d_core.c:906:42: warning: incorrect type in argument 2 (different address spaces) drivers/usb/gadget/mv_u3d_core.c:906:42: expected void [noderef] * drivers/usb/gadget/mv_u3d_core.c:906:42: got unsigned int * Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 5a6f87c..9675227 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -186,6 +186,8 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, */ static void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d; @@ -1520,6 +1522,8 @@ static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num, struct usb_ctrlrequest *setup) + __releases(&u3c->lock) + __acquires(&u3c->lock) { bool delegate = false; -- cgit v0.10.2 From 6b0cfc656f8a649fbfbe11e76e0aa301ee26879e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 22 Mar 2013 17:03:32 +0200 Subject: usb: musb: ux500_dma: fix sparse warning fix the following sparse warning: drivers/usb/musb/ux500_dma.c:60:6: warning: symbol 'ux500_dma_callback' was not declared. Should it be static? Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 2df9b7d..f417b7e 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -57,7 +57,7 @@ struct ux500_dma_controller { }; /* Work function invoked from DMA callback to handle rx transfers. */ -void ux500_dma_callback(void *private_data) +static void ux500_dma_callback(void *private_data) { struct dma_channel *channel = private_data; struct ux500_dma_channel *ux500_channel = channel->private_data; -- cgit v0.10.2 From b13379758f17e66d60f1f288bf917762c54ebd2f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 28 Mar 2013 11:02:24 -0700 Subject: Revert "USB: serial: fix hang when opening port" This reverts commit eba0e3c3a0ba7b96f01cbe997680f6a4401a0bfc. When merged together (usb-linus and usb-next), this fix isn't needed and causes a build error. Revert the commit to solve the build issue. Reported-by: Stephen Rothwell Cc: Ming Lei Cc: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 57e6f53..5eb96df 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -892,7 +892,6 @@ static int usb_serial_probe(struct usb_interface *interface, port->port.ops = &serial_port_ops; port->serial = serial; spin_lock_init(&port->lock); - init_waitqueue_head(&port->delta_msr_wait); /* Keep this for private driver use for the moment but should probably go away */ INIT_WORK(&port->work, usb_serial_port_work); -- cgit v0.10.2 From e9e88fb7bca9f527ccdf4166a240a9023ba6ee73 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 27 Mar 2013 16:14:01 -0400 Subject: USB: avoid error messages when a device is disconnected This patch (as1673) reduces the amount of log spew from the hub driver by removing a bunch of error messages in the case where the device in question is already known to have been disconnected. Since the disconnect event itself appears in the log, there's no need for other error messages. Signed-off-by: Alan Stern Tested-by: Jenya Y Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 271e761..acbfeb0 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -169,7 +169,7 @@ static int generic_probe(struct usb_device *udev) c = usb_choose_configuration(udev); if (c >= 0) { err = usb_set_configuration(udev, c); - if (err) { + if (err && err != -ENODEV) { dev_err(&udev->dev, "can't set config #%d, error %d\n", c, err); /* This need not be fatal. The user can try to diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7815462..669da9e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -555,8 +555,9 @@ static int hub_port_status(struct usb_hub *hub, int port1, mutex_lock(&hub->status_mutex); ret = get_port_status(hub->hdev, port1, &hub->status->port); if (ret < 4) { - dev_err(hub->intfdev, - "%s failed (err = %d)\n", __func__, ret); + if (ret != -ENODEV) + dev_err(hub->intfdev, + "%s failed (err = %d)\n", __func__, ret); if (ret >= 0) ret = -EIO; } else { @@ -699,7 +700,7 @@ static void hub_tt_work(struct work_struct *work) /* drop lock so HCD can concurrently report other TT errors */ spin_unlock_irqrestore (&hub->tt.lock, flags); status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); - if (status) + if (status && status != -ENODEV) dev_err (&hdev->dev, "clear tt %d (%04x) error %d\n", clear->tt, clear->devinfo, status); @@ -837,10 +838,11 @@ static int hub_hub_status(struct usb_hub *hub, mutex_lock(&hub->status_mutex); ret = get_hub_status(hub->hdev, &hub->status->hub); - if (ret < 0) - dev_err (hub->intfdev, - "%s failed (err = %d)\n", __func__, ret); - else { + if (ret < 0) { + if (ret != -ENODEV) + dev_err(hub->intfdev, + "%s failed (err = %d)\n", __func__, ret); + } else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; @@ -877,11 +879,8 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1) return -EINVAL; ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); - if (ret) { - dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", - port1, ret); + if (ret) return ret; - } /* Wait for the link to enter the disabled state. */ for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { @@ -918,7 +917,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); } - if (ret) + if (ret && ret != -ENODEV) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", port1, ret); return ret; @@ -2196,8 +2195,9 @@ static int usb_enumerate_device(struct usb_device *udev) if (udev->config == NULL) { err = usb_get_configuration(udev); if (err < 0) { - dev_err(&udev->dev, "can't read configurations, error %d\n", - err); + if (err != -ENODEV) + dev_err(&udev->dev, "can't read configurations, error %d\n", + err); return err; } } @@ -2644,14 +2644,16 @@ static int hub_port_reset(struct usb_hub *hub, int port1, status = set_port_feature(hub->hdev, port1, (warm ? USB_PORT_FEAT_BH_PORT_RESET : USB_PORT_FEAT_RESET)); - if (status) { + if (status == -ENODEV) { + ; /* The hub is gone */ + } else if (status) { dev_err(hub->intfdev, "cannot %sreset port %d (err = %d)\n", warm ? "warm " : "", port1, status); } else { status = hub_port_wait_reset(hub, port1, udev, delay, warm); - if (status && status != -ENOTCONN) + if (status && status != -ENOTCONN && status != -ENODEV) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", status); @@ -4094,9 +4096,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } if (r) { - dev_err(&udev->dev, - "device descriptor read/64, error %d\n", - r); + if (r != -ENODEV) + dev_err(&udev->dev, "device descriptor read/64, error %d\n", + r); retval = -EMSGSIZE; continue; } @@ -4116,9 +4118,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, msleep(200); } if (retval < 0) { - dev_err(&udev->dev, - "device not accepting address %d, error %d\n", - devnum, retval); + if (retval != -ENODEV) + dev_err(&udev->dev, "device not accepting address %d, error %d\n", + devnum, retval); goto fail; } if (udev->speed == USB_SPEED_SUPER) { @@ -4140,7 +4142,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, retval = usb_get_device_descriptor(udev, 8); if (retval < 8) { - dev_err(&udev->dev, + if (retval != -ENODEV) + dev_err(&udev->dev, "device descriptor read/8, error %d\n", retval); if (retval >= 0) @@ -4194,8 +4197,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { - dev_err(&udev->dev, "device descriptor read/all, error %d\n", - retval); + if (retval != -ENODEV) + dev_err(&udev->dev, "device descriptor read/all, error %d\n", + retval); if (retval >= 0) retval = -ENOMSG; goto fail; @@ -4377,7 +4381,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce_be_stable(hub, port1); if (status < 0) { - if (printk_ratelimit()) + if (status != -ENODEV && printk_ratelimit()) dev_err(hub_dev, "connect-debounce failed, " "port %d disabled\n", port1); portstatus &= ~USB_PORT_STAT_CONNECTION; @@ -4406,6 +4410,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, else unit_load = 100; + status = 0; for (i = 0; i < SET_CONFIG_TRIES; i++) { /* reallocate for each attempt, since references @@ -4530,9 +4535,11 @@ loop: } if (hub->hdev->parent || !hcd->driver->port_handed_over || - !(hcd->driver->port_handed_over)(hcd, port1)) - dev_err(hub_dev, "unable to enumerate USB device on port %d\n", - port1); + !(hcd->driver->port_handed_over)(hcd, port1)) { + if (status != -ENOTCONN && status != -ENODEV) + dev_err(hub_dev, "unable to enumerate USB device on port %d\n", + port1); + } done: hub_port_disable(hub, port1, 1); -- cgit v0.10.2 From 0aa2832dd0d9d8609fd8f15139bc7572541a1215 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 27 Mar 2013 16:14:19 -0400 Subject: USB: use "global suspend" for system sleep on USB-2 buses This patch (as1674) speeds up system sleep transitions by not suspending each individual device on a USB-1.1 or USB-2 bus. The devices will automatically go into suspend when their root hubs are suspended (i.e., stop sending out Start-Of-Frame packets) -- this is what the USB spec calls "global suspend". Since this is what we do already when CONFIG_USB_SUSPEND isn't enabled, it shouldn't cause any problems. Signed-off-by: Alan Stern CC: Peter Chen Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 669da9e..443d5cc 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2886,9 +2886,11 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev) * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. * - * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when - * the root hub for their bus goes into global suspend ... so we don't - * (falsely) update the device power state to say it suspended. + * If CONFIG_USB_SUSPEND isn't enabled, non-SuperSpeed devices really get + * suspended only when their bus goes into global suspend (i.e., the root + * hub is suspended). Nevertheless, we change @udev->state to + * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual + * upstream port setting is stored in @udev->port_is_suspended. * * Returns 0 on success, else negative errno. */ @@ -2899,6 +2901,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) enum pm_qos_flags_status pm_qos_stat; int port1 = udev->portnum; int status; + bool really_suspend = true; /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). @@ -2955,9 +2958,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); - else + else if (PMSG_IS_AUTO(msg)) status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); + /* + * For system suspend, we do not need to enable the suspend feature + * on individual USB-2 ports. The devices will automatically go + * into suspend a few ms after the root hub stops sending packets. + * The USB 2.0 spec calls this "global suspend". + */ + else { + really_suspend = false; + status = 0; + } if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); @@ -2993,8 +3006,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) (PMSG_IS_AUTO(msg) ? "auto-" : ""), udev->do_remote_wakeup); usb_set_device_state(udev, USB_STATE_SUSPENDED); - udev->port_is_suspended = 1; - msleep(10); + if (really_suspend) { + udev->port_is_suspended = 1; + msleep(10); + } } /* -- cgit v0.10.2 From 84ebc10294a3d7be4c66f51070b7aedbaa24de9b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 27 Mar 2013 16:14:46 -0400 Subject: USB: remove CONFIG_USB_SUSPEND option This patch (as1675) removes the CONFIG_USB_SUSPEND option, essentially replacing it everywhere with CONFIG_PM_RUNTIME (except for one place in hub.c, where it is replaced with CONFIG_PM because the code needs to be used in both runtime and system PM). The net result is code shrinkage and simplification. There's very little point in keeping CONFIG_USB_SUSPEND because almost everybody enables it. The few that don't will find that the usbcore module has gotten somewhat bigger and they will have to take active measures if they want to prevent hubs from being runtime suspended. Signed-off-by: Alan Stern CC: Peter Chen Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index f70c1a1..175701a 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -38,22 +38,6 @@ config USB_DYNAMIC_MINORS If you are unsure about this, say N here. -config USB_SUSPEND - bool "USB runtime power management (autosuspend) and wakeup" - depends on USB && PM_RUNTIME - help - If you say Y here, you can use driver calls or the sysfs - "power/control" file to enable or disable autosuspend for - individual USB peripherals (see - Documentation/usb/power-management.txt for more details). - - Also, USB "remote wakeup" signaling is supported, whereby some - USB devices (like keyboards and network adapters) can wake up - their parent hub. That wakeup cascades up the USB tree, and - could wake the system from states like suspend-to-RAM. - - If you are unsure about this, say N here. - config USB_OTG bool "OTG support" depends on USB diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index eb1d00a..84d2b05 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1412,7 +1412,7 @@ int usb_resume(struct device *dev, pm_message_t msg) #endif /* CONFIG_PM */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME /** * usb_enable_autosuspend - allow a USB device to be autosuspended @@ -1780,7 +1780,7 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) return ret; } -#endif /* CONFIG_USB_SUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ struct bus_type usb_bus_type = { .name = "usb", diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index f9ec44c..d53547d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2125,7 +2125,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) #endif /* CONFIG_PM */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME /* Workqueue routine for root-hub remote wakeup */ static void hcd_resume_work(struct work_struct *work) @@ -2160,7 +2160,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); -#endif /* CONFIG_USB_SUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ /*-------------------------------------------------------------------------*/ @@ -2336,7 +2336,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif @@ -2590,7 +2590,7 @@ error_create_attr_group: hcd->rh_registered = 0; spin_unlock_irq(&hcd_root_hub_lock); -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME cancel_work_sync(&hcd->wakeup_work); #endif mutex_lock(&usb_bus_list_lock); @@ -2645,7 +2645,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME cancel_work_sync(&hcd->wakeup_work); #endif diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 443d5cc..feef935 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2827,7 +2827,7 @@ void usb_enable_ltm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_enable_ltm); -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM /* * usb_disable_function_remotewakeup - disable usb3.0 * device's function remote wakeup @@ -2886,7 +2886,7 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev) * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. * - * If CONFIG_USB_SUSPEND isn't enabled, non-SuperSpeed devices really get + * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get * suspended only when their bus goes into global suspend (i.e., the root * hub is suspended). Nevertheless, we change @udev->state to * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual @@ -3247,6 +3247,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) return status; } +#endif /* CONFIG_PM */ + +#ifdef CONFIG_PM_RUNTIME + /* caller has locked udev */ int usb_remote_wakeup(struct usb_device *udev) { @@ -3263,38 +3267,6 @@ int usb_remote_wakeup(struct usb_device *udev) return status; } -#else /* CONFIG_USB_SUSPEND */ - -/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */ - -int usb_port_suspend(struct usb_device *udev, pm_message_t msg) -{ - return 0; -} - -/* However we may need to do a reset-resume */ - -int usb_port_resume(struct usb_device *udev, pm_message_t msg) -{ - struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); - int port1 = udev->portnum; - int status; - u16 portchange, portstatus; - - status = hub_port_status(hub, port1, &portstatus, &portchange); - status = check_port_resume_type(udev, - hub, port1, status, portchange, portstatus); - - if (status) { - dev_dbg(&udev->dev, "can't resume, status %d\n", status); - hub_port_logical_disconnect(hub, port1); - } else if (udev->reset_resume) { - dev_dbg(&udev->dev, "reset-resume\n"); - status = usb_reset_and_verify_device(udev); - } - return status; -} - #endif static int check_ports_changed(struct usb_hub *hub) @@ -4356,7 +4328,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (portstatus & USB_PORT_STAT_ENABLE) { status = 0; /* Nothing to do */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { /* For a suspended device, treat this as a diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 797f9d5..06c4894 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -71,7 +71,7 @@ static void usb_port_device_release(struct device *dev) kfree(port_dev); } -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME static int usb_port_runtime_resume(struct device *dev) { struct usb_port *port_dev = to_usb_port(dev); @@ -139,7 +139,7 @@ static int usb_port_runtime_suspend(struct device *dev) #endif static const struct dev_pm_ops usb_port_pm_ops = { -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME .runtime_suspend = usb_port_runtime_suspend, .runtime_resume = usb_port_runtime_resume, .runtime_idle = pm_generic_runtime_idle, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 3f81a3d..aa38db4 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -338,7 +338,7 @@ static void remove_persist_attributes(struct device *dev) #endif /* CONFIG_PM */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME static ssize_t show_connected_duration(struct device *dev, struct device_attribute *attr, @@ -544,7 +544,7 @@ static void remove_power_attributes(struct device *dev) #define add_power_attributes(dev) 0 #define remove_power_attributes(dev) do {} while (0) -#endif /* CONFIG_USB_SUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ /* Descriptor fields */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f81b925..03eb7ae 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -49,7 +49,7 @@ const char *usbcore_name = "usbcore"; static bool nousb; /* Disable USB when built into kernel image */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME static int usb_autosuspend_delay = 2; /* Default delay value, * in seconds */ module_param_named(autosuspend, usb_autosuspend_delay, int, 0644); @@ -307,7 +307,7 @@ static const struct dev_pm_ops usb_device_pm_ops = { .thaw = usb_dev_thaw, .poweroff = usb_dev_poweroff, .restore = usb_dev_restore, -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME .runtime_suspend = usb_runtime_suspend, .runtime_resume = usb_runtime_resume, .runtime_idle = usb_runtime_idle, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index a7f20bd..8238577 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -93,7 +93,7 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) #endif -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME extern void usb_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 170b939..a573d5f 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -292,17 +292,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } } -#ifdef CONFIG_USB_SUSPEND - /* REVISIT: the controller works fine for wakeup iff the root hub - * itself is "globally" suspended, but usbcore currently doesn't - * understand such things. - * - * System suspend currently expects to be able to suspend the entire - * device tree, device-at-a-time. If we failed selective suspend - * reports, system suspend would fail; so the root hub code must claim - * success. That's lying to usbcore, and it matters for runtime - * PM scenarios with selective suspend and remote wakeup... - */ +#ifdef CONFIG_PM_RUNTIME if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev)) ehci_warn(ehci, "selective suspend/wakeup unavailable\n"); #endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index db09dae..60ff422 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -580,14 +580,8 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port) /* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling, * not necessarily continuous ... to guard against resume signaling. - * The short timeout is safe for non-root hubs, and is backward-compatible - * with earlier Linux hosts. */ -#ifdef CONFIG_USB_SUSPEND #define PORT_RESET_MSEC 50 -#else -#define PORT_RESET_MSEC 10 -#endif /* this timer value might be vendor-specific ... */ #define PORT_RESET_HW_MSEC 10 diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index d62f040..15ed7e8 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1755,7 +1755,7 @@ sl811h_probe(struct platform_device *dev) /* for this device there's no useful distinction between the controller * and its root hub, except that the root hub only gets direct PM calls - * when CONFIG_USB_SUSPEND is enabled. + * when CONFIG_PM_RUNTIME is enabled. */ static int diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 5efdffe..5c124bf 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3141,10 +3141,11 @@ static int u132_probe(struct platform_device *pdev) #ifdef CONFIG_PM -/* for this device there's no useful distinction between the controller -* and its root hub, except that the root hub only gets direct PM calls -* when CONFIG_USB_SUSPEND is enabled. -*/ +/* + * for this device there's no useful distinction between the controller + * and its root hub, except that the root hub only gets direct PM calls + * when CONFIG_PM_RUNTIME is enabled. + */ static int u132_suspend(struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd = platform_get_drvdata(pdev); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 6891442..187a3ec 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1075,7 +1075,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) set_bit(port_index, &bus_state->bus_suspended); } /* USB core sets remote wake mask for USB 3.0 hubs, - * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND + * including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME * is enabled, so also enable remote wake here. */ if (hcd->self.root_hub->do_remote_wakeup) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 53b8f89..5156b72 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3801,7 +3801,7 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1) return raw_port; } -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME /* BESL to HIRD Encoding array for USB2 LPM */ static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, @@ -4051,7 +4051,7 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } -#endif /* CONFIG_USB_SUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ /*---------------------- USB 3.0 Link PM functions ------------------------*/ diff --git a/include/linux/usb.h b/include/linux/usb.h index 8d4bc17..a0bee5a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -617,7 +617,7 @@ static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index) #endif /* USB autosuspend and autoresume */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME extern void usb_enable_autosuspend(struct usb_device *udev); extern void usb_disable_autosuspend(struct usb_device *udev); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 59694b5..f5f5c7d 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -84,7 +84,7 @@ struct usb_hcd { struct timer_list rh_timer; /* drives root-hub polling */ struct urb *status_urb; /* the current status urb */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME struct work_struct wakeup_work; /* for remote wakeup */ #endif @@ -593,14 +593,14 @@ extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg); extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg); #endif /* CONFIG_PM */ -#ifdef CONFIG_USB_SUSPEND +#ifdef CONFIG_PM_RUNTIME extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { return; } -#endif /* CONFIG_USB_SUSPEND */ +#endif /* CONFIG_PM_RUNTIME */ /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 1c3a46de66577b4ff53597addb977aa377e61d8c Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 27 Mar 2013 15:09:03 +0900 Subject: usb: ehci-sh: Fix build error due to comma has been deleted By commit 39d3568 (USB: remove incorrect __exit markups), comma following ehci_hcd_sh_remove has been deleted. This fixes the error by the correction. Signed-off-by: Nobuhiro Iwamatsu CC: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index e30e396..b0f2268 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -196,7 +196,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove = ehci_hcd_sh_remove + .remove = ehci_hcd_sh_remove, .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", -- cgit v0.10.2 From e7231be8571c7a9604867bbfdf7f6036422e13f6 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Tue, 26 Mar 2013 17:29:28 +0900 Subject: usb: echi-sh: Remove driver variable which is not used Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index b0f2268..b44d716 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -77,7 +77,6 @@ static const struct hc_driver ehci_sh_hc_driver = { static int ehci_hcd_sh_probe(struct platform_device *pdev) { - const struct hc_driver *driver = &ehci_sh_hc_driver; struct resource *res; struct ehci_sh_priv *priv; struct ehci_sh_platdata *pdata; -- cgit v0.10.2 From ab1f046a1908d60d2bad20c3ac0f9e71db72318f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 26 Mar 2013 15:48:08 +0900 Subject: USB: ehci-spear: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. drivers/usb/host/ehci-spear.c:82:12: warning: 'ehci_spear_drv_suspend' defined but not used [-Wunused-function] drivers/usb/host/ehci-spear.c:90:12: warning: 'ehci_spear_drv_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 466c1bb..210bb67 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -78,7 +78,7 @@ static const struct hc_driver ehci_spear_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ehci_spear_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -94,7 +94,7 @@ static int ehci_spear_drv_resume(struct device *dev) ehci_resume(hcd, false); return 0; } -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend, ehci_spear_drv_resume); -- cgit v0.10.2 From 4d053fdac3c44ec8c7a78c810292a156abf84971 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 27 Mar 2013 18:28:00 +1300 Subject: usb: ehci: unlink_empty_async_suspended() only used with CONFIG_PM Compiling with !CONFIG_PM generates an unused function warning on unlink_empty_async_suspended(). Enclose the function in a #ifdef CONFIG_PM Signed-off-by: Tony Prisk Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 50787a3..7562d76 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1293,6 +1293,7 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } } +#ifdef CONFIG_PM /* The root hub is suspended; unlink all the async QHs */ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) { @@ -1305,6 +1306,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) } start_iaa_cycle(ehci); } +#endif /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ -- cgit v0.10.2 From f3bc64d6d1f21c1b92d75f233a37b75d77af6963 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 27 Mar 2013 21:44:22 +0000 Subject: USB: EHCI: DT support for generic bus glue This lets us use the ehci-platform driver on platforms without special requirements for their ehci controllers. In particular, this is true for the vt8500/wm8x50 platforms, which currently have a separate driver that causes problems with multiplatform configurations. Tested-by: Tony Prisk Tested-by: Peter Vasil Acked-by: Alan Stern Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 037a472..b12b97d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1269,11 +1269,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif -#ifdef CONFIG_ARCH_VT8500 -#include "ehci-vt8500.c" -#define PLATFORM_DRIVER vt8500_ehci_driver -#endif - #ifdef CONFIG_PLAT_SPEAR #include "ehci-spear.c" #define PLATFORM_DRIVER spear_ehci_hcd_driver diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index ca75063..cda0fa9 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -18,11 +18,13 @@ * * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include #include #include #include #include +#include #include #include #include @@ -62,22 +64,32 @@ static const struct ehci_driver_overrides platform_overrides __initdata = { .reset = ehci_platform_reset, }; +static struct usb_ehci_pdata ehci_platform_defaults; + static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; struct resource *res_mem; - struct usb_ehci_pdata *pdata = dev->dev.platform_data; + struct usb_ehci_pdata *pdata; int irq; int err = -ENOMEM; - if (!pdata) { - WARN_ON(1); - return -ENODEV; - } - if (usb_disabled()) return -ENODEV; + /* + * use reasonable defaults so platforms don't have to provide these. + * with DT probing on ARM, none of these are set. + */ + if (!dev->dev.platform_data) + dev->dev.platform_data = &ehci_platform_defaults; + if (!dev->dev.dma_mask) + dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + if (!dev->dev.coherent_dma_mask) + dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + pdata = dev->dev.platform_data; + irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "no irq provided"); @@ -139,6 +151,9 @@ static int ehci_platform_remove(struct platform_device *dev) if (pdata->power_off) pdata->power_off(dev); + if (pdata == &ehci_platform_defaults) + dev->dev.platform_data = NULL; + return 0; } @@ -183,6 +198,12 @@ static int ehci_platform_resume(struct device *dev) #define ehci_platform_resume NULL #endif /* CONFIG_PM */ +static const struct of_device_id vt8500_ehci_ids[] = { + { .compatible = "via,vt8500-ehci", }, + { .compatible = "wm,prizm-ehci", }, + {} +}; + static const struct platform_device_id ehci_platform_table[] = { { "ehci-platform", 0 }, { } @@ -203,6 +224,7 @@ static struct platform_driver ehci_platform_driver = { .owner = THIS_MODULE, .name = "ehci-platform", .pm = &ehci_platform_pm_ops, + .of_match_table = of_match_ptr(vt8500_ehci_ids), } }; diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c deleted file mode 100644 index 7ecf709..0000000 --- a/drivers/usb/host/ehci-vt8500.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * drivers/usb/host/ehci-vt8500.c - * - * Copyright (C) 2010 Alexey Charkov - * - * Based on ehci-au1xxx.c - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include - -static const struct hc_driver vt8500_ehci_hc_driver = { - .description = hcd_name, - .product_desc = "VT8500 EHCI", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .reset = ehci_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - -static u64 vt8500_ehci_dma_mask = DMA_BIT_MASK(32); - -static int vt8500_ehci_drv_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct ehci_hcd *ehci; - struct resource *res; - int ret; - - if (usb_disabled()) - return -ENODEV; - - /* - * Right now device-tree probed devices don't get dma_mask set. - * Since shared usb code relies on it, set it here for now. - * Once we have dma capability bindings this can go away. - */ - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &vt8500_ehci_dma_mask; - - if (pdev->resource[1].flags != IORESOURCE_IRQ) { - pr_debug("resource[1] is not IORESOURCE_IRQ"); - return -ENOMEM; - } - hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500"); - if (!hcd) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - - hcd->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto err1; - } - - ehci = hcd_to_ehci(hcd); - ehci->caps = hcd->regs; - - ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_SHARED); - if (ret == 0) { - platform_set_drvdata(pdev, hcd); - return ret; - } - -err1: - usb_put_hcd(hcd); - return ret; -} - -static int vt8500_ehci_drv_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - usb_remove_hcd(hcd); - usb_put_hcd(hcd); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static const struct of_device_id vt8500_ehci_ids[] = { - { .compatible = "via,vt8500-ehci", }, - { .compatible = "wm,prizm-ehci", }, - {} -}; - -static struct platform_driver vt8500_ehci_driver = { - .probe = vt8500_ehci_drv_probe, - .remove = vt8500_ehci_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "vt8500-ehci", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(vt8500_ehci_ids), - } -}; - -MODULE_ALIAS("platform:vt8500-ehci"); -MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); -- cgit v0.10.2 From 4e9c8e5c5883c910926296e699c2f4e4d9f847cb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Mar 2013 15:31:13 -0400 Subject: USB: remove CONFIG_USB_SUSPEND from Documentation An earlier patch removed the CONFIG_USB_SUSPEND symbol but forgot to update the Documentation files. This patch (as1676) rectifies that omission. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index c8baaf5..f093e59 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -32,7 +32,7 @@ Date: January 2008 KernelVersion: 2.6.25 Contact: Sarah Sharp Description: - If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file + If CONFIG_PM_RUNTIME is enabled then this file is present. When read, it returns the total time (in msec) that the USB device has been connected to the machine. This file is read-only. @@ -45,7 +45,7 @@ Date: January 2008 KernelVersion: 2.6.25 Contact: Sarah Sharp Description: - If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file + If CONFIG_PM_RUNTIME is enabled then this file is present. When read, it returns the total time (in msec) that the USB device has been active, i.e. not in a suspended state. This file is read-only. @@ -187,7 +187,7 @@ What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm Date: September 2011 Contact: Andiry Xu Description: - If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device + If CONFIG_PM_RUNTIME is set and a USB 2.0 lpm-capable device is plugged in to a xHCI host which support link PM, it will perform a LPM test; if the test is passed and host supports USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index 4204eb0..1392b61 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -33,6 +33,10 @@ built with CONFIG_USB_SUSPEND enabled (which depends on CONFIG_PM_RUNTIME). System PM support is present only if the kernel was built with CONFIG_SUSPEND or CONFIG_HIBERNATION enabled. +(Starting with the 3.10 kernel release, dynamic PM support for USB is +present whenever the kernel was built with CONFIG_PM_RUNTIME enabled. +The CONFIG_USB_SUSPEND option has been eliminated.) + What is Remote Wakeup? ---------------------- @@ -206,10 +210,8 @@ initialized to 5. (The idle-delay values for already existing devices will not be affected.) Setting the initial default idle-delay to -1 will prevent any -autosuspend of any USB device. This is a simple alternative to -disabling CONFIG_USB_SUSPEND and rebuilding the kernel, and it has the -added benefit of allowing you to enable autosuspend for selected -devices. +autosuspend of any USB device. This has the benefit of allowing you +then to enable autosuspend for selected devices. Warnings -- cgit v0.10.2 From 05768918b9a122ce21bd55950df5054ff6c57f28 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Mar 2013 15:04:45 -0400 Subject: USB: improve port transitions when EHCI starts up It seems to be getting more common recently for EHCI host controllers to be probed after their companion UHCI or OHCI controllers. This may be caused partly by splitting the ehci-pci driver out from ehci-hcd, or it may be caused by changes in the way the kernel does driver probing. Regardless, it has a tendency to cause problems. When an EHCI controller is initialized, it takes ownership of all the ports away from the companions. In effect, it forcefully disconnects all the USB devices that may already be using a companion controller. This patch (as1672b) tries to make the transition more orderly by deconfiguring the root hubs for all the companion controllers before initializing the EHCI controller, and reconfiguring them afterward. The result is a soft disconnect rather than a hard one. Internally, the patch refactors the code involved in associating EHCI controllers with their companions. The old approach, in which a single function is called with an argument telling it what to do (the companion_action enum), has been replaced with a scheme using multiple callback functions, each performing a single task. This patch won't solve all the problems people encounter when their EHCI controllers start up, but it will at least reduce the number of error messages generated by the unexpected disconnections. Signed-off-by: Alan Stern Tested-by: Jenya Y Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 2b487d4..caeb8d6 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -37,119 +37,123 @@ /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ -#ifdef CONFIG_PM_SLEEP - -/* Coordinate handoffs between EHCI and companion controllers - * during system resume +/* + * Coordinate handoffs between EHCI and companion controllers + * during EHCI probing and system resume. */ -static DEFINE_MUTEX(companions_mutex); +static DECLARE_RWSEM(companions_rwsem); #define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI #define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI #define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI -enum companion_action { - SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS -}; +static inline int is_ohci_or_uhci(struct pci_dev *pdev) +{ + return pdev->class == CL_OHCI || pdev->class == CL_UHCI; +} + +typedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd); -static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, - enum companion_action action) +/* Iterate over PCI devices in the same slot as pdev and call fn for each */ +static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd, + companion_fn fn) { struct pci_dev *companion; struct usb_hcd *companion_hcd; unsigned int slot = PCI_SLOT(pdev->devfn); - /* Iterate through other PCI functions in the same slot. - * If pdev is OHCI or UHCI then we are looking for EHCI, and - * vice versa. + /* + * Iterate through other PCI functions in the same slot. + * If the function's drvdata isn't set then it isn't bound to + * a USB host controller driver, so skip it. */ companion = NULL; for_each_pci_dev(companion) { if (companion->bus != pdev->bus || PCI_SLOT(companion->devfn) != slot) continue; - companion_hcd = pci_get_drvdata(companion); if (!companion_hcd) continue; - - /* For SET_HS_COMPANION, store a pointer to the EHCI bus in - * the OHCI/UHCI companion bus structure. - * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus - * in the OHCI/UHCI companion bus structure. - * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI - * companion controllers have fully resumed. - */ - - if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) && - companion->class == CL_EHCI) { - /* action must be SET_HS_COMPANION */ - dev_dbg(&companion->dev, "HS companion for %s\n", - dev_name(&pdev->dev)); - hcd->self.hs_companion = &companion_hcd->self; - - } else if (pdev->class == CL_EHCI && - (companion->class == CL_OHCI || - companion->class == CL_UHCI)) { - switch (action) { - case SET_HS_COMPANION: - dev_dbg(&pdev->dev, "HS companion for %s\n", - dev_name(&companion->dev)); - companion_hcd->self.hs_companion = &hcd->self; - break; - case CLEAR_HS_COMPANION: - companion_hcd->self.hs_companion = NULL; - break; - case WAIT_FOR_COMPANIONS: - device_pm_wait_for_dev(&pdev->dev, - &companion->dev); - break; - } - } + fn(pdev, hcd, companion, companion_hcd); } } -static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) +/* + * We're about to add an EHCI controller, which will unceremoniously grab + * all the port connections away from its companions. To prevent annoying + * error messages, lock the companion's root hub and gracefully unconfigure + * it beforehand. Leave it locked until the EHCI controller is all set. + */ +static void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd) { - mutex_lock(&companions_mutex); - dev_set_drvdata(&pdev->dev, hcd); - companion_common(pdev, hcd, SET_HS_COMPANION); - mutex_unlock(&companions_mutex); + struct usb_device *udev; + + if (is_ohci_or_uhci(companion)) { + udev = companion_hcd->self.root_hub; + usb_lock_device(udev); + usb_set_configuration(udev, 0); + } } -static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) +/* + * Adding the EHCI controller has either succeeded or failed. Set the + * companion pointer accordingly, and in either case, reconfigure and + * unlock the root hub. + */ +static void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd) { - mutex_lock(&companions_mutex); - dev_set_drvdata(&pdev->dev, NULL); + struct usb_device *udev; - /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */ - if (pdev->class == CL_OHCI || pdev->class == CL_UHCI) - hcd->self.hs_companion = NULL; + if (is_ohci_or_uhci(companion)) { + if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */ + dev_dbg(&pdev->dev, "HS companion for %s\n", + dev_name(&companion->dev)); + companion_hcd->self.hs_companion = &hcd->self; + } + udev = companion_hcd->self.root_hub; + usb_set_configuration(udev, 1); + usb_unlock_device(udev); + } +} - /* Otherwise search for companion buses and clear their pointers */ - else - companion_common(pdev, hcd, CLEAR_HS_COMPANION); - mutex_unlock(&companions_mutex); +/* + * We just added a non-EHCI controller. Find the EHCI controller to + * which it is a companion, and store a pointer to the bus structure. + */ +static void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd) +{ + if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) { + dev_dbg(&pdev->dev, "FS/LS companion for %s\n", + dev_name(&companion->dev)); + hcd->self.hs_companion = &companion_hcd->self; + } } -static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd) +/* We are removing an EHCI controller. Clear the companions' pointers. */ +static void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd) { - /* Only EHCI controllers need to wait. - * No locking is needed because a controller cannot be resumed - * while one of its companions is getting unbound. - */ - if (pdev->class == CL_EHCI) - companion_common(pdev, hcd, WAIT_FOR_COMPANIONS); + if (is_ohci_or_uhci(companion)) + companion_hcd->self.hs_companion = NULL; } -#else /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM -static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} -static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} -static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {} +/* An EHCI controller must wait for its companions before resuming. */ +static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, + struct pci_dev *companion, struct usb_hcd *companion_hcd) +{ + if (is_ohci_or_uhci(companion)) + device_pm_wait_for_dev(&pdev->dev, &companion->dev); +} -#endif /* !CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM */ /*-------------------------------------------------------------------------*/ @@ -217,7 +221,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) driver->description)) { dev_dbg(&dev->dev, "controller already in use\n"); retval = -EBUSY; - goto clear_companion; + goto put_hcd; } hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (hcd->regs == NULL) { @@ -244,16 +248,35 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (region == PCI_ROM_RESOURCE) { dev_dbg(&dev->dev, "no i/o regions available\n"); retval = -EBUSY; - goto clear_companion; + goto put_hcd; } } pci_set_master(dev); - retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); + /* Note: dev_set_drvdata must be called while holding the rwsem */ + if (dev->class == CL_EHCI) { + down_write(&companions_rwsem); + dev_set_drvdata(&dev->dev, hcd); + for_each_companion(dev, hcd, ehci_pre_add); + retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); + if (retval != 0) + dev_set_drvdata(&dev->dev, NULL); + for_each_companion(dev, hcd, ehci_post_add); + up_write(&companions_rwsem); + } else { + down_read(&companions_rwsem); + dev_set_drvdata(&dev->dev, hcd); + retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); + if (retval != 0) + dev_set_drvdata(&dev->dev, NULL); + else + for_each_companion(dev, hcd, non_ehci_add); + up_read(&companions_rwsem); + } + if (retval != 0) goto unmap_registers; - set_hs_companion(dev, hcd); if (pci_dev_run_wake(dev)) pm_runtime_put_noidle(&dev->dev); @@ -266,8 +289,7 @@ release_mem_region: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); } else release_region(hcd->rsrc_start, hcd->rsrc_len); -clear_companion: - clear_hs_companion(dev, hcd); +put_hcd: usb_put_hcd(hcd); disable_pci: pci_disable_device(dev); @@ -310,14 +332,29 @@ void usb_hcd_pci_remove(struct pci_dev *dev) usb_hcd_irq(0, hcd); local_irq_enable(); - usb_remove_hcd(hcd); + /* Note: dev_set_drvdata must be called while holding the rwsem */ + if (dev->class == CL_EHCI) { + down_write(&companions_rwsem); + for_each_companion(dev, hcd, ehci_remove); + usb_remove_hcd(hcd); + dev_set_drvdata(&dev->dev, NULL); + up_write(&companions_rwsem); + } else { + /* Not EHCI; just clear the companion pointer */ + down_read(&companions_rwsem); + hcd->self.hs_companion = NULL; + usb_remove_hcd(hcd); + dev_set_drvdata(&dev->dev, NULL); + up_read(&companions_rwsem); + } + if (hcd->driver->flags & HCD_MEMORY) { iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); } else { release_region(hcd->rsrc_start, hcd->rsrc_len); } - clear_hs_companion(dev, hcd); + usb_put_hcd(hcd); pci_disable_device(dev); } @@ -463,8 +500,15 @@ static int resume_common(struct device *dev, int event) pci_set_master(pci_dev); if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { - if (event != PM_EVENT_AUTO_RESUME) - wait_for_companions(pci_dev, hcd); + + /* + * Only EHCI controllers have to wait for their companions. + * No locking is needed because PCI controller drivers do not + * get unbound during system resume. + */ + if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) + for_each_companion(pci_dev, hcd, + ehci_wait_for_companions); retval = hcd->driver->pci_resume(hcd, event == PM_EVENT_RESTORE); -- cgit v0.10.2 From 70b55c2ad0e5c8bbbb36cedf6a37f9d029cfd403 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 28 Mar 2013 21:09:46 +0000 Subject: usb: ehci: mark unlink_empty_async_suspended() as __maybe_unused Patch 4d053fdac3 "usb: ehci: unlink_empty_async_suspended() only used with CONFIG_PM" tried to hide the unlink_empty_async_suspended function inside of an #ifdef to work around an unused function warning. Unfortunately that had the effect of introducing a new warning: drivers/usb/host/ehci-q.c:1297:13: warning: 'unlink_empty_async_suspended' declared 'static' but never defined [-Wunused-function] While we could add another #ifdef around the function declaration to avoid this, a nicer solution is to mark it as __maybe_unused, which will let gcc silently drop the function definition when it is not needed. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7562d76..d34b399 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1293,9 +1293,8 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } } -#ifdef CONFIG_PM /* The root hub is suspended; unlink all the async QHs */ -static void unlink_empty_async_suspended(struct ehci_hcd *ehci) +static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci) { struct ehci_qh *qh; @@ -1306,7 +1305,6 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) } start_iaa_cycle(ehci); } -#endif /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ -- cgit v0.10.2 From 4f48203881ce947a0cbd8ae7b1a1a1b04aaa3766 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Wed, 13 Mar 2013 15:57:31 -0700 Subject: usb: Make USB persist default configurable Commit 9214d1d8 set the USB persist flag as a default for all devices. This might be desirable for some distributions, but it certainly has its trade-offs... most importantly, it can significantly increase system resume time, because the kernel blocks on resuming (and sometimes resetting) USB devices before it unfreezes userspace. This patch introduces a new config option CONFIG_USB_DEFAULT_PERSIST, which allows distributions to make this decision on their own without the need to carry a custom patch or revert the kernel's setting in userspace. [edited the Kconfig help text a bit - gregkh] Signed-off-by: Julius Werner Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 175701a..7b7305e 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -27,6 +27,22 @@ config USB_ANNOUNCE_NEW_DEVICES comment "Miscellaneous USB options" depends on USB +config USB_DEFAULT_PERSIST + bool "Enable USB persist by default" + depends on USB + default y + help + Say N here if you don't want USB power session persistance + enabled by default. If you say N it will make suspended USB + devices that lose power get reenumerated as if they had been + unplugged, causing any mounted filesystems to be lost. The + persist feature can still be enabled for individual devices + through the power/persist sysfs node. See + Documentation/usb/persist.txt for more info. + + If you have any questions about this, say Y here, only say N + if you know exactly what you are doing. + config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation" depends on USB diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 3113c1d..ab5638d 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -201,20 +201,14 @@ void usb_detect_quirks(struct usb_device *udev) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", udev->quirks); - /* For the present, all devices default to USB-PERSIST enabled */ -#if 0 /* was: #ifdef CONFIG_PM */ - /* Hubs are automatically enabled for USB-PERSIST */ - if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) +#ifdef CONFIG_USB_DEFAULT_PERSIST + if (!(udev->quirks & USB_QUIRK_RESET)) udev->persist_enabled = 1; - #else - /* In the absence of PM, we can safely enable USB-PERSIST - * for all devices. It will affect things like hub resets - * and EMF-related port disables. - */ - if (!(udev->quirks & USB_QUIRK_RESET)) + /* Hubs are automatically enabled for USB-PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) udev->persist_enabled = 1; -#endif /* CONFIG_PM */ +#endif /* CONFIG_USB_DEFAULT_PERSIST */ } void usb_detect_interface_quirks(struct usb_device *udev) -- cgit v0.10.2 From 3c37bb685d8d668f00102502e753cccc426cc954 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 02:46:17 +0200 Subject: usb: chipidea: drop redundant includes debug.c is carrying a lot of includes that aren't needed there, although they implicitly include the ones that are actually needed. Replace the former with the latter. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index a62c4a4..e6cc45e 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -1,20 +1,9 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include +#include +#include +#include #include #include -#include -#include #include "ci.h" #include "udc.h" -- cgit v0.10.2 From 5b08319f33f199fd5b3ee5360ece8b6ae2890819 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 02:46:18 +0200 Subject: usb: chipidea: trim include list in udc code Some headers included in udc core code are not actually needed, remove them and add irqreturn.h, which was implicitly included via irq.h. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index f64fbea..3dc5093 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -13,14 +13,8 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include -#include +#include #include #include #include -- cgit v0.10.2 From bb36668d5fe784600621d8fdce80f7c280f37a8a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 02:46:19 +0200 Subject: usb: chipidea: trim include list in the core Some headers included in the chipidea controller core are not needed, remove them. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 57cae1f..77963b6 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -51,15 +51,12 @@ */ #include #include -#include #include -#include #include #include #include #include #include -#include #include #include #include -- cgit v0.10.2 From 19290816c5fa3f289465d24bfbac9d669cc2f061 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 30 Mar 2013 02:46:27 +0200 Subject: usb: chipidea: core: switch over to devm_ioremap_resource switch over to the newly added devm_ioremap_resource which provides more consistent error messages. Signed-off-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 77963b6..42f2249 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -407,11 +407,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } - base = devm_request_and_ioremap(dev, res); - if (!base) { - dev_err(dev, "can't request and ioremap resource\n"); - return -ENOMEM; - } + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); if (!ci) { -- cgit v0.10.2 From 571bb7abac4ed02cf7d5094b4a04a8bdca3783ed Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Sat, 30 Mar 2013 02:46:43 +0200 Subject: Revert "USB: chipidea: add vbus detect for udc" There are several problems with this patch: + in introduces a sparse warning for a condition that's always negative, + and because of that, it actually doesn't do anything useful, + and vbus detection belongs to otg, not device function anyway. This reverts commit 8c4fc031954b4eb72daf13d3c907a985a3eee208. Signed-off-by: Peter Chen [Alex: amended the above text] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index e25d126..d738603 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -139,7 +139,6 @@ struct ci13xxx { enum ci_role role; bool is_otg; struct work_struct work; - struct work_struct vbus_work; struct workqueue_struct *wq; struct dma_pool *qh_pool; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3dc5093..86db1de 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -299,18 +299,6 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) return reg; } -static void hw_enable_vbus_intr(struct ci13xxx *ci) -{ - hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS); - hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE); - queue_work(ci->wq, &ci->vbus_work); -} - -static void hw_disable_vbus_intr(struct ci13xxx *ci) -{ - hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0); -} - /** * hw_test_and_clear_setup_guard: test & clear setup guard (execute without * interruption) @@ -377,16 +365,6 @@ static int hw_usb_reset(struct ci13xxx *ci) return 0; } -static void vbus_work(struct work_struct *work) -{ - struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work); - - if (hw_read(ci, OP_OTGSC, OTGSC_AVV)) - usb_gadget_vbus_connect(&ci->gadget); - else - usb_gadget_vbus_disconnect(&ci->gadget); -} - /****************************************************************************** * UTIL block *****************************************************************************/ @@ -1386,7 +1364,6 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) if (is_active) { pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci, USBMODE_CM_DC); - hw_enable_vbus_intr(ci); hw_device_state(ci, ci->ep0out->qh.dma); } else { hw_device_state(ci, 0); @@ -1561,10 +1538,8 @@ static int ci13xxx_start(struct usb_gadget *gadget, pm_runtime_get_sync(&ci->gadget.dev); if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { if (ci->vbus_active) { - if (ci->platdata->flags & CI13XXX_REGS_SHARED) { + if (ci->platdata->flags & CI13XXX_REGS_SHARED) hw_device_reset(ci, USBMODE_CM_DC); - hw_enable_vbus_intr(ci); - } } else { pm_runtime_put_sync(&ci->gadget.dev); goto done; @@ -1670,13 +1645,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) } else { retval = IRQ_NONE; } - - intr = hw_read(ci, OP_OTGSC, ~0); - hw_write(ci, OP_OTGSC, ~0, intr); - - if (intr & (OTGSC_AVVIE & OTGSC_AVVIS)) - queue_work(ci->wq, &ci->vbus_work); - spin_unlock(&ci->lock); return retval; @@ -1752,7 +1720,6 @@ static int udc_start(struct ci13xxx *ci) retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; - hw_enable_vbus_intr(ci); } retval = device_register(&ci->gadget.dev); @@ -1815,9 +1782,6 @@ static void udc_stop(struct ci13xxx *ci) if (ci == NULL) return; - hw_disable_vbus_intr(ci); - cancel_work_sync(&ci->vbus_work); - usb_del_gadget_udc(&ci->gadget); destroy_eps(ci); @@ -1858,7 +1822,6 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci) rdrv->irq = udc_irq; rdrv->name = "gadget"; ci->roles[CI_ROLE_GADGET] = rdrv; - INIT_WORK(&ci->vbus_work, vbus_work); return 0; } -- cgit v0.10.2 From 69b7e8d34f12a5770d57ccd38926d373e4599561 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 12:53:50 +0200 Subject: usb: chipidea: remove home-grown tracing facility As part of the legacy from the original driver design, we retain home-grown tracing infrastructure, complete with own ring buffer and timestamps, which among other things has a performance penalty. This patch removes it. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index e6cc45e..898aca5 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -10,46 +10,6 @@ #include "bits.h" #include "debug.h" -/* Interrupt statistics */ -#define ISR_MASK 0x1F -static struct isr_statistics { - u32 test; - u32 ui; - u32 uei; - u32 pci; - u32 uri; - u32 sli; - u32 none; - struct { - u32 cnt; - u32 buf[ISR_MASK+1]; - u32 idx; - } hndl; -} isr_statistics; - -void dbg_interrupt(u32 intmask) -{ - if (!intmask) { - isr_statistics.none++; - return; - } - - isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intmask; - isr_statistics.hndl.idx &= ISR_MASK; - isr_statistics.hndl.cnt++; - - if (USBi_URI & intmask) - isr_statistics.uri++; - if (USBi_PCI & intmask) - isr_statistics.pci++; - if (USBi_UEI & intmask) - isr_statistics.uei++; - if (USBi_UI & intmask) - isr_statistics.ui++; - if (USBi_SLI & intmask) - isr_statistics.sli++; -} - /** * hw_register_read: reads all device registers (execute without interruption) * @buf: destination buffer @@ -196,312 +156,6 @@ static ssize_t show_driver(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); -/* Maximum event message length */ -#define DBG_DATA_MSG 64UL - -/* Maximum event messages */ -#define DBG_DATA_MAX 128UL - -/* Event buffer descriptor */ -static struct { - char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ - unsigned idx; /* index */ - unsigned tty; /* print to console? */ - rwlock_t lck; /* lock */ -} dbg_data = { - .idx = 0, - .tty = 0, - .lck = __RW_LOCK_UNLOCKED(dbg_data.lck) -}; - -/** - * dbg_dec: decrements debug event index - * @idx: buffer index - */ -static void dbg_dec(unsigned *idx) -{ - *idx = (*idx - 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_inc: increments debug event index - * @idx: buffer index - */ -static void dbg_inc(unsigned *idx) -{ - *idx = (*idx + 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_print: prints the common part of the event - * @addr: endpoint address - * @name: event name - * @status: status - * @extra: extra information - */ -static void dbg_print(u8 addr, const char *name, int status, const char *extra) -{ - struct timeval tval; - unsigned int stamp; - unsigned long flags; - - write_lock_irqsave(&dbg_data.lck, flags); - - do_gettimeofday(&tval); - stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ - stamp = stamp * 1000000 + tval.tv_usec; - - scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, - "%04X\t? %02X %-7.7s %4i ?\t%s\n", - stamp, addr, name, status, extra); - - dbg_inc(&dbg_data.idx); - - write_unlock_irqrestore(&dbg_data.lck, flags); - - if (dbg_data.tty != 0) - pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n", - stamp, addr, name, status, extra); -} - -/** - * dbg_done: prints a DONE event - * @addr: endpoint address - * @td: transfer descriptor - * @status: status - */ -void dbg_done(u8 addr, const u32 token, int status) -{ - char msg[DBG_DATA_MSG]; - - scnprintf(msg, sizeof(msg), "%d %02X", - (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), - (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); - dbg_print(addr, "DONE", status, msg); -} - -/** - * dbg_event: prints a generic event - * @addr: endpoint address - * @name: event name - * @status: status - */ -void dbg_event(u8 addr, const char *name, int status) -{ - if (name != NULL) - dbg_print(addr, name, status, ""); -} - -/* - * dbg_queue: prints a QUEUE event - * @addr: endpoint address - * @req: USB request - * @status: status - */ -void dbg_queue(u8 addr, const struct usb_request *req, int status) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%d %d", !req->no_interrupt, req->length); - dbg_print(addr, "QUEUE", status, msg); - } -} - -/** - * dbg_setup: prints a SETUP event - * @addr: endpoint address - * @req: setup request - */ -void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%02X %02X %04X %04X %d", req->bRequestType, - req->bRequest, le16_to_cpu(req->wValue), - le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); - dbg_print(addr, "SETUP", 0, msg); - } -} - -/** - * show_events: displays the event buffer - * - * Check "device.h" for details - */ -static ssize_t show_events(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned long flags; - unsigned i, j, n = 0; - - if (attr == NULL || buf == NULL) { - dev_err(dev->parent, "[%s] EINVAL\n", __func__); - return 0; - } - - read_lock_irqsave(&dbg_data.lck, flags); - - i = dbg_data.idx; - for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { - n += strlen(dbg_data.buf[i]); - if (n >= PAGE_SIZE) { - n -= strlen(dbg_data.buf[i]); - break; - } - } - for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) - j += scnprintf(buf + j, PAGE_SIZE - j, - "%s", dbg_data.buf[i]); - - read_unlock_irqrestore(&dbg_data.lck, flags); - - return n; -} - -/** - * store_events: configure if events are going to be also printed to console - * - * Check "device.h" for details - */ -static ssize_t store_events(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned tty; - - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { - dev_err(dev, "<1|0>: enable|disable console log\n"); - goto done; - } - - dbg_data.tty = tty; - dev_info(dev, "tty = %u", dbg_data.tty); - - done: - return count; -} -static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); - -/** - * show_inters: interrupt status, enable status and historic - * - * Check "device.h" for details - */ -static ssize_t show_inters(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 intr; - unsigned i, j, n = 0; - - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(&ci->lock, flags); - - /*n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status(ci)); - n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable(ci));*/ - - n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", - isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n", - isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n", - isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n", - isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n", - isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n", - isr_statistics.sli); - n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", - isr_statistics.none); - n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", - isr_statistics.hndl.cnt); - - for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { - i &= ISR_MASK; - intr = isr_statistics.hndl.buf[i]; - - if (USBi_UI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); - intr &= ~USBi_UI; - if (USBi_UEI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); - intr &= ~USBi_UEI; - if (USBi_PCI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); - intr &= ~USBi_PCI; - if (USBi_URI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); - intr &= ~USBi_URI; - if (USBi_SLI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); - intr &= ~USBi_SLI; - if (intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); - if (isr_statistics.hndl.buf[i]) - n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); - } - - spin_unlock_irqrestore(&ci->lock, flags); - - return n; -} - -/** - * store_inters: enable & force or disable an individual interrutps - * (to be used for test purposes only) - * - * Check "device.h" for details - */ -static ssize_t store_inters(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned en, bit; - - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "EINVAL\n"); - goto done; - } - - if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(ci->dev, "<1|0> : enable|disable interrupt\n"); - goto done; - } - - spin_lock_irqsave(&ci->lock, flags); - if (en) { - if (hw_intr_force(ci, bit)) - dev_err(dev, "invalid bit number\n"); - else - isr_statistics.test++; - } else { - if (hw_intr_clear(ci, bit)) - dev_err(dev, "invalid bit number\n"); - } - spin_unlock_irqrestore(&ci->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); - /** * show_port_test: reads port test mode * @@ -730,15 +384,9 @@ int dbg_create_files(struct device *dev) retval = device_create_file(dev, &dev_attr_driver); if (retval) goto rm_device; - retval = device_create_file(dev, &dev_attr_events); - if (retval) - goto rm_driver; - retval = device_create_file(dev, &dev_attr_inters); - if (retval) - goto rm_events; retval = device_create_file(dev, &dev_attr_port_test); if (retval) - goto rm_inters; + goto rm_driver; retval = device_create_file(dev, &dev_attr_qheads); if (retval) goto rm_port_test; @@ -756,10 +404,6 @@ int dbg_create_files(struct device *dev) device_remove_file(dev, &dev_attr_qheads); rm_port_test: device_remove_file(dev, &dev_attr_port_test); - rm_inters: - device_remove_file(dev, &dev_attr_inters); - rm_events: - device_remove_file(dev, &dev_attr_events); rm_driver: device_remove_file(dev, &dev_attr_driver); rm_device: @@ -782,8 +426,6 @@ int dbg_remove_files(struct device *dev) device_remove_file(dev, &dev_attr_registers); device_remove_file(dev, &dev_attr_qheads); device_remove_file(dev, &dev_attr_port_test); - device_remove_file(dev, &dev_attr_inters); - device_remove_file(dev, &dev_attr_events); device_remove_file(dev, &dev_attr_driver); device_remove_file(dev, &dev_attr_device); return 0; diff --git a/drivers/usb/chipidea/debug.h b/drivers/usb/chipidea/debug.h index 80d9686..425f1ff 100644 --- a/drivers/usb/chipidea/debug.h +++ b/drivers/usb/chipidea/debug.h @@ -14,34 +14,9 @@ #define __DRIVERS_USB_CHIPIDEA_DEBUG_H #ifdef CONFIG_USB_CHIPIDEA_DEBUG -void dbg_interrupt(u32 intmask); -void dbg_done(u8 addr, const u32 token, int status); -void dbg_event(u8 addr, const char *name, int status); -void dbg_queue(u8 addr, const struct usb_request *req, int status); -void dbg_setup(u8 addr, const struct usb_ctrlrequest *req); int dbg_create_files(struct device *dev); int dbg_remove_files(struct device *dev); #else -static inline void dbg_interrupt(u32 intmask) -{ -} - -static inline void dbg_done(u8 addr, const u32 token, int status) -{ -} - -static inline void dbg_event(u8 addr, const char *name, int status) -{ -} - -static inline void dbg_queue(u8 addr, const struct usb_request *req, int status) -{ -} - -static inline void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) -{ -} - static inline int dbg_create_files(struct device *dev) { return 0; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 86db1de..ed498cb 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -601,8 +601,6 @@ __acquires(ci->lock) { int retval; - dbg_event(0xFF, "BUS RST", 0); - spin_unlock(&ci->lock); retval = _gadget_stop_activity(&ci->gadget); if (retval) @@ -773,7 +771,6 @@ __acquires(mEp->lock) if (retval < 0) break; list_del_init(&mReq->queue); - dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && @@ -786,8 +783,6 @@ __acquires(mEp->lock) if (retval == -EBUSY) retval = 0; - if (retval < 0) - dbg_event(_usb_addr(mEp), "DONE", retval); return retval; } @@ -819,8 +814,6 @@ __acquires(ci->lock) if (err > 0) /* needs status phase */ err = isr_setup_status_phase(ci); if (err < 0) { - dbg_event(_usb_addr(mEp), - "ERROR", err); spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) dev_err(ci->dev, @@ -856,8 +849,6 @@ __acquires(ci->lock) ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; - dbg_setup(_usb_addr(mEp), &req); - switch (req.bRequest) { case USB_REQ_CLEAR_FEATURE: if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && @@ -969,8 +960,6 @@ delegate: } if (err < 0) { - dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) dev_err(ci->dev, "error: ep_set_halt\n"); @@ -1012,8 +1001,6 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.maxpacket = usb_endpoint_maxp(desc); - dbg_event(_usb_addr(mEp), "ENABLE", 0); - mEp->qh.ptr->cap = 0; if (mEp->type == USB_ENDPOINT_XFER_CONTROL) @@ -1060,8 +1047,6 @@ static int ep_disable(struct usb_ep *ep) direction = mEp->dir; do { - dbg_event(_usb_addr(mEp), "DISABLE", 0); - retval |= _ep_nuke(mEp); retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir); @@ -1101,8 +1086,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) } } - dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); - return (mReq == NULL) ? NULL : &mReq->req; } @@ -1130,8 +1113,6 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma); kfree(mReq); - dbg_event(_usb_addr(mEp), "FREE", 0); - spin_unlock_irqrestore(mEp->lock, flags); } @@ -1179,18 +1160,14 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, dev_warn(mEp->ci->dev, "request length truncated\n"); } - dbg_queue(_usb_addr(mEp), req, retval); - /* push request */ mReq->req.status = -EINPROGRESS; mReq->req.actual = 0; retval = _hardware_enqueue(mEp, mReq); - if (retval == -EALREADY) { - dbg_event(_usb_addr(mEp), "QUEUE", retval); + if (retval == -EALREADY) retval = 0; - } if (!retval) list_add_tail(&mReq->queue, &mEp->qh.queue); @@ -1217,8 +1194,6 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) spin_lock_irqsave(mEp->lock, flags); - dbg_event(_usb_addr(mEp), "DEQUEUE", 0); - hw_ep_flush(mEp->ci, mEp->num, mEp->dir); /* pop request */ @@ -1265,7 +1240,6 @@ static int ep_set_halt(struct usb_ep *ep, int value) direction = mEp->dir; do { - dbg_event(_usb_addr(mEp), "HALT", value); retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value); if (!value) @@ -1294,10 +1268,7 @@ static int ep_set_wedge(struct usb_ep *ep) return -EINVAL; spin_lock_irqsave(mEp->lock, flags); - - dbg_event(_usb_addr(mEp), "WEDGE", 0); mEp->wedge = 1; - spin_unlock_irqrestore(mEp->lock, flags); return usb_ep_set_halt(ep); @@ -1320,7 +1291,6 @@ static void ep_fifo_flush(struct usb_ep *ep) spin_lock_irqsave(mEp->lock, flags); - dbg_event(_usb_addr(mEp), "FFLUSH", 0); hw_ep_flush(mEp->ci, mEp->num, mEp->dir); spin_unlock_irqrestore(mEp->lock, flags); @@ -1611,7 +1581,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci) } } intr = hw_test_and_clear_intr_active(ci); - dbg_interrupt(intr); if (intr) { /* order defines priority - do NOT change it */ -- cgit v0.10.2 From 2d6512892c106556f07e502939005e73cdc6e2cc Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 12:53:51 +0200 Subject: usb: chipidea: convert debug entries in sysfs to debugfs Currently, we have a bunch of files in sysfs that display all sorts of debugging information for the device controller, so they have to move to debugfs where they belong. The "registers" interface have been removed, since it doesn't fit into the current driver design as is and it's hardly a good idea to touch the registers from userspace anyway. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index d738603..c7d4622 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -129,6 +129,7 @@ struct hw_bank { * @vbus_active: is VBUS active * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver + * @debugfs: root dentry for this controller in debugfs */ struct ci13xxx { struct device *dev; @@ -164,6 +165,7 @@ struct ci13xxx { bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; + struct dentry *debugfs; }; static inline struct ci_role_driver *ci_role(struct ci13xxx *ci) diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 898aca5..057ae09 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #include #include @@ -11,223 +14,113 @@ #include "debug.h" /** - * hw_register_read: reads all device registers (execute without interruption) - * @buf: destination buffer - * @size: buffer size - * - * This function returns number of registers read + * ci_device_show: prints information about device capabilities and status */ -static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size) +static int ci_device_show(struct seq_file *s, void *data) { - unsigned i; - - if (size > ci->hw_bank.size) - size = ci->hw_bank.size; - - for (i = 0; i < size; i++) - buf[i] = hw_read(ci, i * sizeof(u32), ~0); - - return size; -} - -/** - * hw_register_write: writes to register - * @addr: register address - * @data: register value - * - * This function returns an error code - */ -static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data) -{ - /* align */ - addr /= sizeof(u32); + struct ci13xxx *ci = s->private; + struct usb_gadget *gadget = &ci->gadget; - if (addr >= ci->hw_bank.size) - return -EINVAL; + seq_printf(s, "speed = %d\n", gadget->speed); + seq_printf(s, "max_speed = %d\n", gadget->max_speed); + seq_printf(s, "is_otg = %d\n", gadget->is_otg); + seq_printf(s, "is_a_peripheral = %d\n", gadget->is_a_peripheral); + seq_printf(s, "b_hnp_enable = %d\n", gadget->b_hnp_enable); + seq_printf(s, "a_hnp_support = %d\n", gadget->a_hnp_support); + seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support); + seq_printf(s, "name = %s\n", + (gadget->name ? gadget->name : "")); + + if (!ci->driver) + return 0; - /* align */ - addr *= sizeof(u32); + seq_printf(s, "gadget function = %s\n", + (ci->driver->function ? ci->driver->function : "")); + seq_printf(s, "gadget max speed = %d\n", ci->driver->max_speed); - hw_write(ci, addr, ~0, data); return 0; } -/** - * hw_intr_clear: disables interrupt & clears interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_clear(struct ci13xxx *ci, int n) +static int ci_device_open(struct inode *inode, struct file *file) { - if (n >= REG_BITS) - return -EINVAL; - - hw_write(ci, OP_USBINTR, BIT(n), 0); - hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); - return 0; + return single_open(file, ci_device_show, inode->i_private); } -/** - * hw_intr_force: enables interrupt & forces interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_force(struct ci13xxx *ci, int n) -{ - if (n >= REG_BITS) - return -EINVAL; - - hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_write(ci, OP_USBINTR, BIT(n), BIT(n)); - hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); - hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0); - return 0; -} - -/** - * show_device: prints information about device capabilities and status - * - * Check "device.h" for details - */ -static ssize_t show_device(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &ci->gadget; - int n = 0; - - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", - gadget->speed); - n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n", - gadget->max_speed); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", - gadget->is_otg); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", - gadget->is_a_peripheral); - n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", - gadget->b_hnp_enable); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", - gadget->a_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", - gadget->a_alt_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", - (gadget->name ? gadget->name : "")); - - return n; -} -static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); - -/** - * show_driver: prints information about attached gadget (if any) - * - * Check "device.h" for details - */ -static ssize_t show_driver(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = ci->driver; - int n = 0; - - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - if (driver == NULL) - return scnprintf(buf, PAGE_SIZE, - "There is no gadget attached!\n"); - - n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", - (driver->function ? driver->function : "")); - n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", - driver->max_speed); - - return n; -} -static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); +static const struct file_operations ci_device_fops = { + .open = ci_device_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /** - * show_port_test: reads port test mode - * - * Check "device.h" for details + * ci_port_test_show: reads port test mode */ -static ssize_t show_port_test(struct device *dev, - struct device_attribute *attr, char *buf) +static int ci_port_test_show(struct seq_file *s, void *data) { - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = s->private; unsigned long flags; unsigned mode; - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "EINVAL\n"); - return 0; - } - spin_lock_irqsave(&ci->lock, flags); mode = hw_port_test_get(ci); spin_unlock_irqrestore(&ci->lock, flags); - return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); + seq_printf(s, "mode = %u\n", mode); + + return 0; } /** - * store_port_test: writes port test mode - * - * Check "device.h" for details + * ci_port_test_write: writes port test mode */ -static ssize_t store_port_test(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) { - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct seq_file *s = file->private_data; + struct ci13xxx *ci = s->private; unsigned long flags; unsigned mode; + char buf[32]; + int ret; - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); - goto done; - } + if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; - if (sscanf(buf, "%u", &mode) != 1) { - dev_err(ci->dev, ": set port test mode"); - goto done; - } + if (sscanf(buf, "%u", &mode) != 1) + return -EINVAL; spin_lock_irqsave(&ci->lock, flags); - if (hw_port_test_set(ci, mode)) - dev_err(ci->dev, "invalid mode\n"); + ret = hw_port_test_set(ci, mode); spin_unlock_irqrestore(&ci->lock, flags); - done: - return count; + return ret ? ret : count; } -static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, - show_port_test, store_port_test); + +static int ci_port_test_open(struct inode *inode, struct file *file) +{ + return single_open(file, ci_port_test_show, inode->i_private); +} + +static const struct file_operations ci_port_test_fops = { + .open = ci_port_test_open, + .write = ci_port_test_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /** - * show_qheads: DMA contents of all queue heads - * - * Check "device.h" for details + * ci_qheads_show: DMA contents of all queue heads */ -static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, - char *buf) +static int ci_qheads_show(struct seq_file *s, void *data) { - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = s->private; unsigned long flags; - unsigned i, j, n = 0; + unsigned i, j; - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); + if (ci->role != CI_ROLE_GADGET) { + seq_printf(s, "not in gadget mode\n"); return 0; } @@ -236,197 +129,119 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i]; struct ci13xxx_ep *mEpTx = &ci->ci13xxx_ep[i + ci->hw_ep_max/2]; - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: RX=%08X TX=%08X\n", - i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); - for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X %08X\n", j, - *((u32 *)mEpRx->qh.ptr + j), - *((u32 *)mEpTx->qh.ptr + j)); - } + seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n", + i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); + for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) + seq_printf(s, " %04X: %08X %08X\n", j, + *((u32 *)mEpRx->qh.ptr + j), + *((u32 *)mEpTx->qh.ptr + j)); } spin_unlock_irqrestore(&ci->lock, flags); - return n; + return 0; } -static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); -/** - * show_registers: dumps all registers - * - * Check "device.h" for details - */ -#define DUMP_ENTRIES 512 -static ssize_t show_registers(struct device *dev, - struct device_attribute *attr, char *buf) +static int ci_qheads_open(struct inode *inode, struct file *file) { - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 *dump; - unsigned i, k, n = 0; - - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); - return 0; - } - - dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); - if (!dump) { - dev_err(ci->dev, "%s: out of memory\n", __func__); - return 0; - } - - spin_lock_irqsave(&ci->lock, flags); - k = hw_register_read(ci, dump, DUMP_ENTRIES); - spin_unlock_irqrestore(&ci->lock, flags); - - for (i = 0; i < k; i++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - "reg[0x%04X] = 0x%08X\n", - i * (unsigned)sizeof(u32), dump[i]); - } - kfree(dump); - - return n; + return single_open(file, ci_qheads_show, inode->i_private); } -/** - * store_registers: writes value to register address - * - * Check "device.h" for details - */ -static ssize_t store_registers(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long addr, data, flags; - - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(ci->dev, - " : write data to register address\n"); - goto done; - } - - spin_lock_irqsave(&ci->lock, flags); - if (hw_register_write(ci, addr, data)) - dev_err(ci->dev, "invalid address range\n"); - spin_unlock_irqrestore(&ci->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, - show_registers, store_registers); +static const struct file_operations ci_qheads_fops = { + .open = ci_qheads_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /** - * show_requests: DMA contents of all requests currently queued (all endpts) - * - * Check "device.h" for details + * ci_requests_show: DMA contents of all requests currently queued (all endpts) */ -static ssize_t show_requests(struct device *dev, struct device_attribute *attr, - char *buf) +static int ci_requests_show(struct seq_file *s, void *data) { - struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = s->private; unsigned long flags; struct list_head *ptr = NULL; struct ci13xxx_req *req = NULL; - unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + unsigned i, j, qsize = sizeof(struct ci13xxx_td)/sizeof(u32); - if (attr == NULL || buf == NULL) { - dev_err(ci->dev, "[%s] EINVAL\n", __func__); + if (ci->role != CI_ROLE_GADGET) { + seq_printf(s, "not in gadget mode\n"); return 0; } spin_lock_irqsave(&ci->lock, flags); for (i = 0; i < ci->hw_ep_max; i++) - list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) - { + list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) { req = list_entry(ptr, struct ci13xxx_req, queue); - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: TD=%08X %s\n", - i % ci->hw_ep_max/2, (u32)req->dma, - ((i < ci->hw_ep_max/2) ? "RX" : "TX")); + seq_printf(s, "EP=%02i: TD=%08X %s\n", + i % ci->hw_ep_max/2, (u32)req->dma, + ((i < ci->hw_ep_max/2) ? "RX" : "TX")); - for (j = 0; j < qSize; j++) - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X\n", j, - *((u32 *)req->ptr + j)); + for (j = 0; j < qsize; j++) + seq_printf(s, " %04X: %08X\n", j, + *((u32 *)req->ptr + j)); } spin_unlock_irqrestore(&ci->lock, flags); - return n; + return 0; +} + +static int ci_requests_open(struct inode *inode, struct file *file) +{ + return single_open(file, ci_requests_show, inode->i_private); } -static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); + +static const struct file_operations ci_requests_fops = { + .open = ci_requests_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; /** * dbg_create_files: initializes the attribute interface - * @dev: device + * @ci: device * * This function returns an error code */ -int dbg_create_files(struct device *dev) +int dbg_create_files(struct ci13xxx *ci) { - int retval = 0; - - if (dev == NULL) - return -EINVAL; - retval = device_create_file(dev, &dev_attr_device); - if (retval) - goto done; - retval = device_create_file(dev, &dev_attr_driver); - if (retval) - goto rm_device; - retval = device_create_file(dev, &dev_attr_port_test); - if (retval) - goto rm_driver; - retval = device_create_file(dev, &dev_attr_qheads); - if (retval) - goto rm_port_test; - retval = device_create_file(dev, &dev_attr_registers); - if (retval) - goto rm_qheads; - retval = device_create_file(dev, &dev_attr_requests); - if (retval) - goto rm_registers; - return 0; - - rm_registers: - device_remove_file(dev, &dev_attr_registers); - rm_qheads: - device_remove_file(dev, &dev_attr_qheads); - rm_port_test: - device_remove_file(dev, &dev_attr_port_test); - rm_driver: - device_remove_file(dev, &dev_attr_driver); - rm_device: - device_remove_file(dev, &dev_attr_device); - done: - return retval; + struct dentry *dent; + + ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL); + if (!ci->debugfs) + return -ENOMEM; + + dent = debugfs_create_file("device", S_IRUGO, ci->debugfs, ci, + &ci_device_fops); + if (!dent) + goto err; + + dent = debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs, + ci, &ci_port_test_fops); + if (!dent) + goto err; + + dent = debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci, + &ci_qheads_fops); + if (!dent) + goto err; + + dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci, + &ci_requests_fops); + if (dent) + return 0; +err: + debugfs_remove_recursive(ci->debugfs); + return -ENOMEM; } /** * dbg_remove_files: destroys the attribute interface - * @dev: device - * - * This function returns an error code + * @ci: device */ -int dbg_remove_files(struct device *dev) +void dbg_remove_files(struct ci13xxx *ci) { - if (dev == NULL) - return -EINVAL; - device_remove_file(dev, &dev_attr_requests); - device_remove_file(dev, &dev_attr_registers); - device_remove_file(dev, &dev_attr_qheads); - device_remove_file(dev, &dev_attr_port_test); - device_remove_file(dev, &dev_attr_driver); - device_remove_file(dev, &dev_attr_device); - return 0; + debugfs_remove_recursive(ci->debugfs); } diff --git a/drivers/usb/chipidea/debug.h b/drivers/usb/chipidea/debug.h index 425f1ff..7ca6ca0 100644 --- a/drivers/usb/chipidea/debug.h +++ b/drivers/usb/chipidea/debug.h @@ -14,17 +14,16 @@ #define __DRIVERS_USB_CHIPIDEA_DEBUG_H #ifdef CONFIG_USB_CHIPIDEA_DEBUG -int dbg_create_files(struct device *dev); -int dbg_remove_files(struct device *dev); +int dbg_create_files(struct ci13xxx *ci); +void dbg_remove_files(struct ci13xxx *ci); #else -static inline int dbg_create_files(struct device *dev) +static inline int dbg_create_files(struct ci13xxx *ci) { return 0; } -static inline int dbg_remove_files(struct device *dev) +static inline void dbg_remove_files(struct ci13xxx *ci) { - return 0; } #endif diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ed498cb..d945391 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1697,7 +1697,7 @@ static int udc_start(struct ci13xxx *ci) goto put_transceiver; } - retval = dbg_create_files(ci->dev); + retval = dbg_create_files(ci); if (retval) goto unreg_device; @@ -1726,7 +1726,7 @@ remove_trans: dev_err(dev, "error = %i\n", retval); remove_dbg: - dbg_remove_files(ci->dev); + dbg_remove_files(ci); unreg_device: device_unregister(&ci->gadget.dev); put_transceiver: @@ -1763,7 +1763,7 @@ static void udc_stop(struct ci13xxx *ci) if (ci->global_phy) usb_put_phy(ci->transceiver); } - dbg_remove_files(ci->dev); + dbg_remove_files(ci); device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ memset(&ci->gadget, 0, sizeof(ci->gadget)); -- cgit v0.10.2 From c8e333a3b17050800a5aa536feb628f18d2a01a2 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 12:53:52 +0200 Subject: usb: chipidea: move role to debugfs Manual role switching function is there for debugging purposes, so has to move to debugfs. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 42f2249..5270156 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -280,38 +280,6 @@ static void ci_role_work(struct work_struct *work) } } -static ssize_t show_role(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *ci = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", ci_role(ci)->name); -} - -static ssize_t store_role(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *ci = dev_get_drvdata(dev); - enum ci_role role; - int ret; - - for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) - if (ci->roles[role] && !strcmp(buf, ci->roles[role]->name)) - break; - - if (role == CI_ROLE_END || role == ci->role) - return -EINVAL; - - ci_role_stop(ci); - ret = ci_role_start(ci, role); - if (ret) - return ret; - - return count; -} - -static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role); - static irqreturn_t ci_irq(int irq, void *data) { struct ci13xxx *ci = data; @@ -484,17 +452,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; - ret = device_create_file(dev, &dev_attr_role); - if (ret) - goto rm_attr; - if (ci->is_otg) hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE); return ret; -rm_attr: - device_remove_file(dev, &dev_attr_role); stop: ci_role_stop(ci); rm_wq: @@ -510,7 +472,6 @@ static int ci_hdrc_remove(struct platform_device *pdev) flush_workqueue(ci->wq); destroy_workqueue(ci->wq); - device_remove_file(ci->dev, &dev_attr_role); free_irq(ci->irq, ci); ci_role_stop(ci); diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 057ae09..5738079 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -199,6 +199,55 @@ static const struct file_operations ci_requests_fops = { .release = single_release, }; +static int ci_role_show(struct seq_file *s, void *data) +{ + struct ci13xxx *ci = s->private; + + seq_printf(s, "%s\n", ci_role(ci)->name); + + return 0; +} + +static ssize_t ci_role_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct ci13xxx *ci = s->private; + enum ci_role role; + char buf[8]; + int ret; + + if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) + if (ci->roles[role] && + !strncmp(buf, ci->roles[role]->name, + strlen(ci->roles[role]->name))) + break; + + if (role == CI_ROLE_END || role == ci->role) + return -EINVAL; + + ci_role_stop(ci); + ret = ci_role_start(ci, role); + + return ret ? ret : count; +} + +static int ci_role_open(struct inode *inode, struct file *file) +{ + return single_open(file, ci_role_show, inode->i_private); +} + +static const struct file_operations ci_role_fops = { + .open = ci_role_open, + .write = ci_role_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** * dbg_create_files: initializes the attribute interface * @ci: device @@ -230,6 +279,11 @@ int dbg_create_files(struct ci13xxx *ci) dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci, &ci_requests_fops); + if (!dent) + goto err; + + dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci, + &ci_role_fops); if (dent) return 0; err: -- cgit v0.10.2 From adf0f735e61aae5ff615bb0301d9fff29b589a5c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 12:53:53 +0200 Subject: usb: chipidea: move debug files creation/removal to the core Create and remove debugfs entries in hdrc probe/remove instead of start/stop of the device controller. Gadget specific will not export anything while the controller is in host mode. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5270156..a7ce1b8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -455,8 +455,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ci->is_otg) hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE); - return ret; + ret = dbg_create_files(ci); + if (!ret) + return 0; + free_irq(ci->irq, ci); stop: ci_role_stop(ci); rm_wq: @@ -470,6 +473,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) { struct ci13xxx *ci = platform_get_drvdata(pdev); + dbg_remove_files(ci); flush_workqueue(ci->wq); destroy_workqueue(ci->wq); free_irq(ci->irq, ci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index d945391..28c31ba 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1697,15 +1697,11 @@ static int udc_start(struct ci13xxx *ci) goto put_transceiver; } - retval = dbg_create_files(ci); - if (retval) - goto unreg_device; - if (!IS_ERR_OR_NULL(ci->transceiver)) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); if (retval) - goto remove_dbg; + goto unreg_device; } retval = usb_add_gadget_udc(dev, &ci->gadget); @@ -1725,8 +1721,6 @@ remove_trans: } dev_err(dev, "error = %i\n", retval); -remove_dbg: - dbg_remove_files(ci); unreg_device: device_unregister(&ci->gadget.dev); put_transceiver: @@ -1763,7 +1757,6 @@ static void udc_stop(struct ci13xxx *ci) if (ci->global_phy) usb_put_phy(ci->transceiver); } - dbg_remove_files(ci); device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ memset(&ci->gadget, 0, sizeof(ci->gadget)); -- cgit v0.10.2 From ba8618e067907407ec84001adc11bc1f184d19a6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 30 Mar 2013 12:53:54 +0200 Subject: usb: chipidea: fix precedence bug in ci_requests_show() The intent here was to have parenthesis around the (ci->hw_ep_max / 2) so that it counts like "0 1 2 0 1 2". In the current code, the mod operation happens first so it counts like "0 0 1 1 2 2". Signed-off-by: Dan Carpenter [rebased on top of debug.c changes] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 5738079..36a7063 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -175,7 +175,7 @@ static int ci_requests_show(struct seq_file *s, void *data) req = list_entry(ptr, struct ci13xxx_req, queue); seq_printf(s, "EP=%02i: TD=%08X %s\n", - i % ci->hw_ep_max/2, (u32)req->dma, + i % (ci->hw_ep_max / 2), (u32)req->dma, ((i < ci->hw_ep_max/2) ? "RX" : "TX")); for (j = 0; j < qsize; j++) -- cgit v0.10.2 From 727b4ddb48dcd8c9ca81b58ca58191233bdf75aa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 30 Mar 2013 12:53:55 +0200 Subject: usb: chipidea: don't redefine __ffs() chipidea's ffs_nr() is pretty much what __ffs() does. Use that one instead. Signed-off-by: Felipe Balbi [rebased on top of debug infrastructure rework] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index c7d4622..68577d1 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -235,19 +235,6 @@ enum ci13xxx_regs { }; /** - * ffs_nr: find first (least significant) bit set - * @x: the word to search - * - * This function returns bit number (instead of position) - */ -static inline int ffs_nr(u32 x) -{ - int n = ffs(x); - - return n ? n-1 : 32; -} - -/** * hw_read: reads from a hw register * @reg: register index * @mask: bitfield mask @@ -305,7 +292,7 @@ static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 val = hw_read(ci, reg, ~0); hw_write(ci, reg, mask, data); - return (val & mask) >> ffs_nr(mask); + return (val & mask) >> __ffs(mask); } int hw_device_reset(struct ci13xxx *ci, u32 mode); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a7ce1b8..114d4c4 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -155,7 +155,7 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode) if (mode > TEST_MODE_MAX) return -EINVAL; - hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); + hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); return 0; } @@ -166,7 +166,7 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode) */ u8 hw_port_test_get(struct ci13xxx *ci) { - return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); + return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); } static int hw_device_init(struct ci13xxx *ci, void __iomem *base) @@ -182,7 +182,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) hw_alloc_regmap(ci, false); reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> - ffs_nr(HCCPARAMS_LEN); + __ffs(HCCPARAMS_LEN); ci->hw_bank.lpm = reg; hw_alloc_regmap(ci, !!reg); ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; @@ -190,7 +190,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ci->hw_bank.size /= sizeof(u32); reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> - ffs_nr(DCCPARAMS_DEN); + __ffs(DCCPARAMS_DEN); ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ if (ci->hw_ep_max > ENDPT_MAX) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 28c31ba..32e6c99 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -140,7 +140,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) if (dir) { mask = ENDPTCTRL_TXT; /* type */ - data = type << ffs_nr(mask); + data = type << __ffs(mask); mask |= ENDPTCTRL_TXS; /* unstall */ mask |= ENDPTCTRL_TXR; /* reset data toggle */ @@ -149,7 +149,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) data |= ENDPTCTRL_TXE; } else { mask = ENDPTCTRL_RXT; /* type */ - data = type << ffs_nr(mask); + data = type << __ffs(mask); mask |= ENDPTCTRL_RXS; /* unstall */ mask |= ENDPTCTRL_RXR; /* reset data toggle */ @@ -331,7 +331,7 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *ci) static void hw_usb_set_address(struct ci13xxx *ci, u8 value) { hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, - value << ffs_nr(DEVICEADDR_USBADR)); + value << __ffs(DEVICEADDR_USBADR)); } /** @@ -418,7 +418,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) * TODO - handle requests which spawns into several TDs */ memset(mReq->ptr, 0, sizeof(*mReq->ptr)); - mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES); + mReq->ptr->token = length << __ffs(TD_TOTAL_BYTES); mReq->ptr->token &= TD_TOTAL_BYTES; mReq->ptr->token |= TD_STATUS_ACTIVE; if (mReq->zptr) { @@ -504,7 +504,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->req.status = -1; mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; - mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); + mReq->req.actual >>= __ffs(TD_TOTAL_BYTES); mReq->req.actual = mReq->req.length - mReq->req.actual; mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; @@ -1011,7 +1011,7 @@ static int ep_enable(struct usb_ep *ep, mEp->qh.ptr->cap &= ~QH_ZLT; mEp->qh.ptr->cap |= - (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; + (mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ /* -- cgit v0.10.2 From a7bc2fdf003c55f8e00e1e7e6fff51a4876779ef Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:53:56 +0200 Subject: usb: chipidea: usbmisc: rename file, struct and functions to usbmisc_imx This driver will be used for every Freescale SoC which has this misc memory layout to control the basic usb handling. So better name this driver, function and struct names in a more generic way. Reported-by: Fabio Estevam Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index d92ca32..4ab83e9 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -17,5 +17,5 @@ ifneq ($(CONFIG_PCI),) endif ifneq ($(CONFIG_OF_DEVICE),) - obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx6q.o + obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx.o endif diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c new file mode 100644 index 0000000..3c42446 --- /dev/null +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -0,0 +1,162 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include + +#include "ci13xxx_imx.h" + +#define USB_DEV_MAX 4 + +#define BM_OVER_CUR_DIS BIT(7) + +struct imx_usbmisc { + void __iomem *base; + spinlock_t lock; + struct clk *clk; + struct usbmisc_usb_device usbdev[USB_DEV_MAX]; +}; + +static struct imx_usbmisc *usbmisc; + +static struct usbmisc_usb_device *get_usbdev(struct device *dev) +{ + int i, ret; + + for (i = 0; i < USB_DEV_MAX; i++) { + if (usbmisc->usbdev[i].dev == dev) + return &usbmisc->usbdev[i]; + else if (!usbmisc->usbdev[i].dev) + break; + } + + if (i >= USB_DEV_MAX) + return ERR_PTR(-EBUSY); + + ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); + if (ret) + return ERR_PTR(ret); + + return &usbmisc->usbdev[i]; +} + +static int usbmisc_imx6q_init(struct device *dev) +{ + + struct usbmisc_usb_device *usbdev; + unsigned long flags; + u32 reg; + + usbdev = get_usbdev(dev); + if (IS_ERR(usbdev)) + return PTR_ERR(usbdev); + + if (usbdev->disable_oc) { + spin_lock_irqsave(&usbmisc->lock, flags); + reg = readl(usbmisc->base + usbdev->index * 4); + writel(reg | BM_OVER_CUR_DIS, + usbmisc->base + usbdev->index * 4); + spin_unlock_irqrestore(&usbmisc->lock, flags); + } + + return 0; +} + +static const struct usbmisc_ops imx6q_usbmisc_ops = { + .init = usbmisc_imx6q_init, +}; + +static const struct of_device_id usbmisc_imx_dt_ids[] = { + { .compatible = "fsl,imx6q-usbmisc"}, + { /* sentinel */ } +}; + +static int usbmisc_imx_probe(struct platform_device *pdev) +{ + struct resource *res; + struct imx_usbmisc *data; + int ret; + + if (usbmisc) + return -EBUSY; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) { + dev_err(&pdev->dev, + "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); + return PTR_ERR(data->clk); + } + + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed, err=%d\n", ret); + return ret; + } + + ret = usbmisc_set_ops(&imx6q_usbmisc_ops); + if (ret) { + clk_disable_unprepare(data->clk); + return ret; + } + + usbmisc = data; + + return 0; +} + +static int usbmisc_imx_remove(struct platform_device *pdev) +{ + usbmisc_unset_ops(&imx6q_usbmisc_ops); + clk_disable_unprepare(usbmisc->clk); + return 0; +} + +static struct platform_driver usbmisc_imx_driver = { + .probe = usbmisc_imx_probe, + .remove = usbmisc_imx_remove, + .driver = { + .name = "usbmisc_imx", + .owner = THIS_MODULE, + .of_match_table = usbmisc_imx_dt_ids, + }, +}; + +int usbmisc_imx_drv_init(void) +{ + return platform_driver_register(&usbmisc_imx_driver); +} +subsys_initcall(usbmisc_imx_drv_init); + +void usbmisc_imx_drv_exit(void) +{ + platform_driver_unregister(&usbmisc_imx_driver); +} +module_exit(usbmisc_imx_drv_exit); + +MODULE_ALIAS("platform:usbmisc-imx"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("driver for imx usb non-core registers"); +MODULE_AUTHOR("Richard Zhao "); diff --git a/drivers/usb/chipidea/usbmisc_imx6q.c b/drivers/usb/chipidea/usbmisc_imx6q.c deleted file mode 100644 index 113fcea..0000000 --- a/drivers/usb/chipidea/usbmisc_imx6q.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include - -#include "ci13xxx_imx.h" - -#define USB_DEV_MAX 4 - -#define BM_OVER_CUR_DIS BIT(7) - -struct imx6q_usbmisc { - void __iomem *base; - spinlock_t lock; - struct clk *clk; - struct usbmisc_usb_device usbdev[USB_DEV_MAX]; -}; - -static struct imx6q_usbmisc *usbmisc; - -static struct usbmisc_usb_device *get_usbdev(struct device *dev) -{ - int i, ret; - - for (i = 0; i < USB_DEV_MAX; i++) { - if (usbmisc->usbdev[i].dev == dev) - return &usbmisc->usbdev[i]; - else if (!usbmisc->usbdev[i].dev) - break; - } - - if (i >= USB_DEV_MAX) - return ERR_PTR(-EBUSY); - - ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); - if (ret) - return ERR_PTR(ret); - - return &usbmisc->usbdev[i]; -} - -static int usbmisc_imx6q_init(struct device *dev) -{ - - struct usbmisc_usb_device *usbdev; - unsigned long flags; - u32 reg; - - usbdev = get_usbdev(dev); - if (IS_ERR(usbdev)) - return PTR_ERR(usbdev); - - if (usbdev->disable_oc) { - spin_lock_irqsave(&usbmisc->lock, flags); - reg = readl(usbmisc->base + usbdev->index * 4); - writel(reg | BM_OVER_CUR_DIS, - usbmisc->base + usbdev->index * 4); - spin_unlock_irqrestore(&usbmisc->lock, flags); - } - - return 0; -} - -static const struct usbmisc_ops imx6q_usbmisc_ops = { - .init = usbmisc_imx6q_init, -}; - -static const struct of_device_id usbmisc_imx6q_dt_ids[] = { - { .compatible = "fsl,imx6q-usbmisc"}, - { /* sentinel */ } -}; - -static int usbmisc_imx6q_probe(struct platform_device *pdev) -{ - struct resource *res; - struct imx6q_usbmisc *data; - int ret; - - if (usbmisc) - return -EBUSY; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - spin_lock_init(&data->lock); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } - - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "clk_prepare_enable failed, err=%d\n", ret); - return ret; - } - - ret = usbmisc_set_ops(&imx6q_usbmisc_ops); - if (ret) { - clk_disable_unprepare(data->clk); - return ret; - } - - usbmisc = data; - - return 0; -} - -static int usbmisc_imx6q_remove(struct platform_device *pdev) -{ - usbmisc_unset_ops(&imx6q_usbmisc_ops); - clk_disable_unprepare(usbmisc->clk); - return 0; -} - -static struct platform_driver usbmisc_imx6q_driver = { - .probe = usbmisc_imx6q_probe, - .remove = usbmisc_imx6q_remove, - .driver = { - .name = "usbmisc_imx6q", - .owner = THIS_MODULE, - .of_match_table = usbmisc_imx6q_dt_ids, - }, -}; - -static int __init usbmisc_imx6q_drv_init(void) -{ - return platform_driver_register(&usbmisc_imx6q_driver); -} -subsys_initcall(usbmisc_imx6q_drv_init); - -static void __exit usbmisc_imx6q_drv_exit(void) -{ - platform_driver_unregister(&usbmisc_imx6q_driver); -} -module_exit(usbmisc_imx6q_drv_exit); - -MODULE_ALIAS("platform:usbmisc-imx6q"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("driver for imx6q usb non-core registers"); -MODULE_AUTHOR("Richard Zhao "); -- cgit v0.10.2 From d48a24dbc0d3f21cbd594bcc2553d40cc5ed4fd9 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Sat, 30 Mar 2013 12:53:57 +0200 Subject: usb: chipidea: usbmisc: unset global varibale usbmisc on driver remove The probe function checks usbmisc to be NULL in the beginning. Without this patch the can only be loaded once. Signed-off-by: Marc Kleine-Budde Signed-off-by: Michael Grzeschik Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 3c42446..fd4d339 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -131,6 +131,7 @@ static int usbmisc_imx_remove(struct platform_device *pdev) { usbmisc_unset_ops(&imx6q_usbmisc_ops); clk_disable_unprepare(usbmisc->clk); + usbmisc = NULL; return 0; } -- cgit v0.10.2 From 00b9a1f97dbdfbdc1d268bf8d878937b150ce2d4 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Sat, 30 Mar 2013 12:53:58 +0200 Subject: usb: chipidea: usbmisc: fix a potential race condition This fixes a potential race condition where the ci13xxx_imx glue code could be fast enough to call one of the usbmisc_ops before he got a valid value on the static usbmisc pointer. To fix that we first set usbmisc, then call usbmisc_set_ops(). Signed-off-by: Marc Kleine-Budde Signed-off-by: Michael Grzeschik Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index fd4d339..d77e712 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -116,14 +116,14 @@ static int usbmisc_imx_probe(struct platform_device *pdev) return ret; } + usbmisc = data; ret = usbmisc_set_ops(&imx6q_usbmisc_ops); if (ret) { + usbmisc = NULL; clk_disable_unprepare(data->clk); return ret; } - usbmisc = data; - return 0; } -- cgit v0.10.2 From e609108a5ba70ecf3b1b6a7e09e5a56244e92926 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Sat, 30 Mar 2013 12:53:59 +0200 Subject: usb: chipidea: usbmisc: prepare driver to handle more than one soc This attaches the usbmisc_ops to the of_device_id data and makes it possible to define special functions per soc. Signed-off-by: Marc Kleine-Budde Signed-off-by: Michael Grzeschik [Alex: fixed one case of line-too-long and one bogus cast to void ptr] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index d77e712..08b046f 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -19,13 +19,14 @@ #define USB_DEV_MAX 4 -#define BM_OVER_CUR_DIS BIT(7) +#define MX6_BM_OVER_CUR_DIS BIT(7) struct imx_usbmisc { void __iomem *base; spinlock_t lock; struct clk *clk; struct usbmisc_usb_device usbdev[USB_DEV_MAX]; + const struct usbmisc_ops *ops; }; static struct imx_usbmisc *usbmisc; @@ -65,7 +66,7 @@ static int usbmisc_imx6q_init(struct device *dev) if (usbdev->disable_oc) { spin_lock_irqsave(&usbmisc->lock, flags); reg = readl(usbmisc->base + usbdev->index * 4); - writel(reg | BM_OVER_CUR_DIS, + writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base + usbdev->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); } @@ -78,7 +79,10 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { }; static const struct of_device_id usbmisc_imx_dt_ids[] = { - { .compatible = "fsl,imx6q-usbmisc"}, + { + .compatible = "fsl,imx6q-usbmisc", + .data = &imx6q_usbmisc_ops, + }, { /* sentinel */ } }; @@ -87,6 +91,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev) struct resource *res; struct imx_usbmisc *data; int ret; + struct of_device_id *tmp_dev; if (usbmisc) return -EBUSY; @@ -116,8 +121,11 @@ static int usbmisc_imx_probe(struct platform_device *pdev) return ret; } + tmp_dev = (struct of_device_id *) + of_match_device(usbmisc_imx_dt_ids, &pdev->dev); + data->ops = (const struct usbmisc_ops *)tmp_dev->data; usbmisc = data; - ret = usbmisc_set_ops(&imx6q_usbmisc_ops); + ret = usbmisc_set_ops(data->ops); if (ret) { usbmisc = NULL; clk_disable_unprepare(data->clk); @@ -129,7 +137,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev) static int usbmisc_imx_remove(struct platform_device *pdev) { - usbmisc_unset_ops(&imx6q_usbmisc_ops); + usbmisc_unset_ops(usbmisc->ops); clk_disable_unprepare(usbmisc->clk); usbmisc = NULL; return 0; -- cgit v0.10.2 From f0c910b63cc273c239964776fae1aaa58ed4ad2b Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:00 +0200 Subject: usb: chipidea: usbmisc: add mx53 support This adds mx53 as the next user of the usbmisc driver and makes it possible to disable the overcurrent-detection of the internal phy. Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde [Alex: fixed another set of line-too-long and void pointer cast] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 08b046f..746013d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -19,6 +19,13 @@ #define USB_DEV_MAX 4 +#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 +#define MX53_USB_UH2_CTRL_OFFSET 0x14 +#define MX53_USB_UH3_CTRL_OFFSET 0x18 +#define MX53_BM_OVER_CUR_DIS_H1 BIT(5) +#define MX53_BM_OVER_CUR_DIS_OTG BIT(8) +#define MX53_BM_OVER_CUR_DIS_UHx BIT(30) + #define MX6_BM_OVER_CUR_DIS BIT(7) struct imx_usbmisc { @@ -52,6 +59,45 @@ static struct usbmisc_usb_device *get_usbdev(struct device *dev) return &usbmisc->usbdev[i]; } +static int usbmisc_imx53_init(struct device *dev) +{ + struct usbmisc_usb_device *usbdev; + void __iomem *reg = NULL; + unsigned long flags; + u32 val = 0; + + usbdev = get_usbdev(dev); + if (IS_ERR(usbdev)) + return PTR_ERR(usbdev); + + if (usbdev->disable_oc) { + spin_lock_irqsave(&usbmisc->lock, flags); + switch (usbdev->index) { + case 0: + reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; + val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; + break; + case 1: + reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; + val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1; + break; + case 2: + reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET; + val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; + break; + case 3: + reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET; + val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; + break; + } + if (reg && val) + writel(val, reg); + spin_unlock_irqrestore(&usbmisc->lock, flags); + } + + return 0; +} + static int usbmisc_imx6q_init(struct device *dev) { @@ -74,12 +120,20 @@ static int usbmisc_imx6q_init(struct device *dev) return 0; } +static const struct usbmisc_ops imx53_usbmisc_ops = { + .init = usbmisc_imx53_init, +}; + static const struct usbmisc_ops imx6q_usbmisc_ops = { .init = usbmisc_imx6q_init, }; static const struct of_device_id usbmisc_imx_dt_ids[] = { { + .compatible = "fsl,imx53-usbmisc", + .data = &imx53_usbmisc_ops, + }, + { .compatible = "fsl,imx6q-usbmisc", .data = &imx6q_usbmisc_ops, }, -- cgit v0.10.2 From a068533079a0a1be53c78c89e65adfbd3c687591 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:01 +0200 Subject: usb: chipidea: usbmisc: add post handling and errata fix for mx25 This adds a post handling routine which is called after ci13xxx_add_device was called. The first user is the mx25, which has to disable the external-vbus-divider after the udc has started. Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde [Alex: also fixed a signed one-bit bitfield a whitespace error and yet another set of line-too-long and void pointer casting errors] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt index 5778b9c..1c04a4c 100644 --- a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt +++ b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt @@ -11,6 +11,7 @@ Optional properties: that indicate usb controller index - vbus-supply: regulator for vbus - disable-over-current: disable over current detect +- external-vbus-divider: enables off-chip resistor divider for Vbus Examples: usb@02184000 { /* USB OTG */ @@ -20,4 +21,5 @@ usb@02184000 { /* USB OTG */ fsl,usbphy = <&usbphy1>; fsl,usbmisc = <&usbmisc 0>; disable-over-current; + external-vbus-divider; }; diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c index 8c29122..8faec9d 100644 --- a/drivers/usb/chipidea/ci13xxx_imx.c +++ b/drivers/usb/chipidea/ci13xxx_imx.c @@ -79,6 +79,9 @@ int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) if (of_find_property(np, "disable-over-current", NULL)) usbdev->disable_oc = 1; + if (of_find_property(np, "external-vbus-divider", NULL)) + usbdev->evdo = 1; + return 0; } EXPORT_SYMBOL_GPL(usbmisc_get_init_data); @@ -202,6 +205,15 @@ static int ci13xxx_imx_probe(struct platform_device *pdev) goto err; } + if (usbmisc_ops && usbmisc_ops->post) { + ret = usbmisc_ops->post(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "usbmisc post failed, ret=%d\n", ret); + goto put_np; + } + } + data->ci_pdev = plat_ci; platform_set_drvdata(pdev, data); diff --git a/drivers/usb/chipidea/ci13xxx_imx.h b/drivers/usb/chipidea/ci13xxx_imx.h index 9cd2e91..550bfa4 100644 --- a/drivers/usb/chipidea/ci13xxx_imx.h +++ b/drivers/usb/chipidea/ci13xxx_imx.h @@ -13,6 +13,8 @@ struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct device *dev); + /* It's called once after adding a usb device */ + int (*post)(struct device *dev); }; struct usbmisc_usb_device { @@ -20,6 +22,7 @@ struct usbmisc_usb_device { int index; unsigned int disable_oc:1; /* over current detect disabled */ + unsigned int evdo:1; /* set external vbus divider option */ }; int usbmisc_set_ops(const struct usbmisc_ops *ops); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 746013d..714a6bd 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -14,11 +14,15 @@ #include #include #include +#include #include "ci13xxx_imx.h" #define USB_DEV_MAX 4 +#define MX25_USB_PHY_CTRL_OFFSET 0x08 +#define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) + #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 #define MX53_USB_UH2_CTRL_OFFSET 0x14 #define MX53_USB_UH3_CTRL_OFFSET 0x18 @@ -59,6 +63,30 @@ static struct usbmisc_usb_device *get_usbdev(struct device *dev) return &usbmisc->usbdev[i]; } +static int usbmisc_imx25_post(struct device *dev) +{ + struct usbmisc_usb_device *usbdev; + void __iomem *reg; + unsigned long flags; + u32 val; + + usbdev = get_usbdev(dev); + if (IS_ERR(usbdev)) + return PTR_ERR(usbdev); + + reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; + + if (usbdev->evdo) { + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(reg); + writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); + spin_unlock_irqrestore(&usbmisc->lock, flags); + usleep_range(5000, 10000); /* needed to stabilize voltage */ + } + + return 0; +} + static int usbmisc_imx53_init(struct device *dev) { struct usbmisc_usb_device *usbdev; @@ -120,6 +148,10 @@ static int usbmisc_imx6q_init(struct device *dev) return 0; } +static const struct usbmisc_ops imx25_usbmisc_ops = { + .post = usbmisc_imx25_post, +}; + static const struct usbmisc_ops imx53_usbmisc_ops = { .init = usbmisc_imx53_init, }; @@ -130,6 +162,10 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { static const struct of_device_id usbmisc_imx_dt_ids[] = { { + .compatible = "fsl,imx25-usbmisc", + .data = &imx25_usbmisc_ops, + }, + { .compatible = "fsl,imx53-usbmisc", .data = &imx53_usbmisc_ops, }, -- cgit v0.10.2 From efccca4ff59e672a6b50e99f0f4cb61b60d09ec8 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Sat, 30 Mar 2013 12:54:02 +0200 Subject: usb: chipidea: make pci platform datas static PCI chipideas' platform datas are not static as all such things should be. Fix it. Reported-by: Marc Kleine-Budde Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index 9b227e3..4e1fc61 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -23,17 +23,17 @@ /****************************************************************************** * PCI block *****************************************************************************/ -struct ci13xxx_platform_data pci_platdata = { +static struct ci13xxx_platform_data pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = DEF_CAPOFFSET, }; -struct ci13xxx_platform_data langwell_pci_platdata = { +static struct ci13xxx_platform_data langwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, }; -struct ci13xxx_platform_data penwell_pci_platdata = { +static struct ci13xxx_platform_data penwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, .power_budget = 200, -- cgit v0.10.2 From 938d323f14480ca8dcb9dbbe48add35a09246d09 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Sat, 30 Mar 2013 12:54:03 +0200 Subject: usb: chipidea: big-endian support Convert between big-endian and little-endian format when accessing the usb controller structures which are little-endian by specification. Fix cases where the little-endian memory layout is taken for granted. The patch doesn't have any effect on the already supported little-endian architectures. Signed-off-by: Svetoslav Neykov [Alex: minor cosmetic fixes] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 114d4c4..450107e 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -178,7 +178,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ci->hw_bank.cap = ci->hw_bank.abs; ci->hw_bank.cap += ci->platdata->capoffset; - ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); + ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); hw_alloc_regmap(ci, false); reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 32e6c99..ff393e1 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -404,10 +404,10 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) return -ENOMEM; memset(mReq->zptr, 0, sizeof(*mReq->zptr)); - mReq->zptr->next = TD_TERMINATE; - mReq->zptr->token = TD_STATUS_ACTIVE; + mReq->zptr->next = cpu_to_le32(TD_TERMINATE); + mReq->zptr->token = cpu_to_le32(TD_STATUS_ACTIVE); if (!mReq->req.no_interrupt) - mReq->zptr->token |= TD_IOC; + mReq->zptr->token |= cpu_to_le32(TD_IOC); } ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir); if (ret) @@ -418,32 +418,35 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) * TODO - handle requests which spawns into several TDs */ memset(mReq->ptr, 0, sizeof(*mReq->ptr)); - mReq->ptr->token = length << __ffs(TD_TOTAL_BYTES); - mReq->ptr->token &= TD_TOTAL_BYTES; - mReq->ptr->token |= TD_STATUS_ACTIVE; + mReq->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES)); + mReq->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES); + mReq->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE); if (mReq->zptr) { - mReq->ptr->next = mReq->zdma; + mReq->ptr->next = cpu_to_le32(mReq->zdma); } else { - mReq->ptr->next = TD_TERMINATE; + mReq->ptr->next = cpu_to_le32(TD_TERMINATE); if (!mReq->req.no_interrupt) - mReq->ptr->token |= TD_IOC; + mReq->ptr->token |= cpu_to_le32(TD_IOC); + } + mReq->ptr->page[0] = cpu_to_le32(mReq->req.dma); + for (i = 1; i < 5; i++) { + u32 page = mReq->req.dma + i * CI13XXX_PAGE_SIZE; + page &= ~TD_RESERVED_MASK; + mReq->ptr->page[i] = cpu_to_le32(page); } - mReq->ptr->page[0] = mReq->req.dma; - for (i = 1; i < 5; i++) - mReq->ptr->page[i] = - (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; if (!list_empty(&mEp->qh.queue)) { struct ci13xxx_req *mReqPrev; int n = hw_ep_bit(mEp->num, mEp->dir); int tmp_stat; + u32 next = mReq->dma & TD_ADDR_MASK; mReqPrev = list_entry(mEp->qh.queue.prev, struct ci13xxx_req, queue); if (mReqPrev->zptr) - mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK; + mReqPrev->zptr->next = cpu_to_le32(next); else - mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK; + mReqPrev->ptr->next = cpu_to_le32(next); wmb(); if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; @@ -457,9 +460,9 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) } /* QH configuration */ - mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */ - mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */ - mEp->qh.ptr->cap |= QH_ZLT; + mEp->qh.ptr->td.next = cpu_to_le32(mReq->dma); /* TERMINATE = 0 */ + mEp->qh.ptr->td.token &= cpu_to_le32(~TD_STATUS); /* clear status */ + mEp->qh.ptr->cap |= cpu_to_le32(QH_ZLT); wmb(); /* synchronize before ep prime */ @@ -481,11 +484,11 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (mReq->req.status != -EALREADY) return -EINVAL; - if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0) + if ((cpu_to_le32(TD_STATUS_ACTIVE) & mReq->ptr->token) != 0) return -EBUSY; if (mReq->zptr) { - if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0) + if ((cpu_to_le32(TD_STATUS_ACTIVE) & mReq->zptr->token) != 0) return -EBUSY; dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma); mReq->zptr = NULL; @@ -495,7 +498,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); - mReq->req.status = mReq->ptr->token & TD_STATUS; + mReq->req.status = le32_to_cpu(mReq->ptr->token) & TD_STATUS; if ((TD_STATUS_HALTED & mReq->req.status) != 0) mReq->req.status = -1; else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) @@ -503,7 +506,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) mReq->req.status = -1; - mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; + mReq->req.actual = le32_to_cpu(mReq->ptr->token) & TD_TOTAL_BYTES; mReq->req.actual >>= __ffs(TD_TOTAL_BYTES); mReq->req.actual = mReq->req.length - mReq->req.actual; mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; @@ -1004,15 +1007,15 @@ static int ep_enable(struct usb_ep *ep, mEp->qh.ptr->cap = 0; if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->qh.ptr->cap |= QH_IOS; + mEp->qh.ptr->cap |= cpu_to_le32(QH_IOS); else if (mEp->type == USB_ENDPOINT_XFER_ISOC) - mEp->qh.ptr->cap &= ~QH_MULT; + mEp->qh.ptr->cap &= cpu_to_le32(~QH_MULT); else - mEp->qh.ptr->cap &= ~QH_ZLT; + mEp->qh.ptr->cap &= cpu_to_le32(~QH_ZLT); - mEp->qh.ptr->cap |= - (mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; - mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ + mEp->qh.ptr->cap |= cpu_to_le32((mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) + & QH_MAX_PKT); + mEp->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ /* * Enable endpoints in the HW other than ep0 as ep0 -- cgit v0.10.2 From 080ff5f4719e21c78f8ec96eea4f82b2fd047fa4 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:04 +0200 Subject: usb: chipidea: udc: only clear active and halted bits in qhead The datasheet of the synopsys core describes only to overwrite the active and halted bits in the qhead before priming any endpoint. Signed-off-by: Michael Grzeschik Reviewed-by: Felipe Balbi [Alex: fixed a case of line-too-long] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ff393e1..0e11172 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -461,7 +461,8 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) /* QH configuration */ mEp->qh.ptr->td.next = cpu_to_le32(mReq->dma); /* TERMINATE = 0 */ - mEp->qh.ptr->td.token &= cpu_to_le32(~TD_STATUS); /* clear status */ + mEp->qh.ptr->td.token &= + cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE)); mEp->qh.ptr->cap |= cpu_to_le32(QH_ZLT); wmb(); /* synchronize before ep prime */ -- cgit v0.10.2 From 1cd12a9cf055e72b3094ef38ac4d9afff2e9520e Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:05 +0200 Subject: usb: chipidea: udc: rework ep_enable cap setting This patch reworks the cap value from several read and write operations to one single operation. Signed-off-by: Michael Grzeschik Reviewed-by: Felipe Balbi [Alex: removed useless isoc-related bit of code] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0e11172..3abd1ad 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -986,6 +986,7 @@ static int ep_enable(struct usb_ep *ep, struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); int retval = 0; unsigned long flags; + u32 cap = 0; if (ep == NULL || desc == NULL) return -EINVAL; @@ -1005,17 +1006,12 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.maxpacket = usb_endpoint_maxp(desc); - mEp->qh.ptr->cap = 0; - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->qh.ptr->cap |= cpu_to_le32(QH_IOS); - else if (mEp->type == USB_ENDPOINT_XFER_ISOC) - mEp->qh.ptr->cap &= cpu_to_le32(~QH_MULT); - else - mEp->qh.ptr->cap &= cpu_to_le32(~QH_ZLT); + cap |= QH_IOS; + cap |= (mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; + + mEp->qh.ptr->cap = cpu_to_le32(cap); - mEp->qh.ptr->cap |= cpu_to_le32((mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) - & QH_MAX_PKT); mEp->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ /* -- cgit v0.10.2 From 776ffc16b74a5c19285135cfbc6dd02e1c733f25 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:06 +0200 Subject: usb: chipidea: udc: move ZLT flag change to ep_enable Its not necessary and also not specified in the datasheet to change the ZLT flag before every ep_prime. This patch moves this to the ep_enable and applies it only for non configuration endpoints. Signed-off-by: Michael Grzeschik Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3abd1ad..94af020 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -463,7 +463,6 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mEp->qh.ptr->td.next = cpu_to_le32(mReq->dma); /* TERMINATE = 0 */ mEp->qh.ptr->td.token &= cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE)); - mEp->qh.ptr->cap |= cpu_to_le32(QH_ZLT); wmb(); /* synchronize before ep prime */ @@ -1008,6 +1007,8 @@ static int ep_enable(struct usb_ep *ep, if (mEp->type == USB_ENDPOINT_XFER_CONTROL) cap |= QH_IOS; + if (mEp->num) + cap |= QH_ZLT; cap |= (mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; mEp->qh.ptr->cap = cpu_to_le32(cap); -- cgit v0.10.2 From 9e5064384a69e6dac15e3ba8590355ec844e47b5 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:07 +0200 Subject: usb: chipidea: udc: read status of td only once in hardware_dequeue This patch changes the read of the td status to one atomic operation to analyse coherent bits. Signed-off-by: Michael Grzeschik [Alex: fixed backwards endianness conversion] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 94af020..20a5b79 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -481,10 +481,12 @@ done: */ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) { + u32 tmptoken = le32_to_cpu(mReq->ptr->token); + if (mReq->req.status != -EALREADY) return -EINVAL; - if ((cpu_to_le32(TD_STATUS_ACTIVE) & mReq->ptr->token) != 0) + if ((TD_STATUS_ACTIVE & tmptoken) != 0) return -EBUSY; if (mReq->zptr) { @@ -498,7 +500,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); - mReq->req.status = le32_to_cpu(mReq->ptr->token) & TD_STATUS; + mReq->req.status = tmptoken & TD_STATUS; if ((TD_STATUS_HALTED & mReq->req.status) != 0) mReq->req.status = -1; else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) @@ -506,7 +508,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) mReq->req.status = -1; - mReq->req.actual = le32_to_cpu(mReq->ptr->token) & TD_TOTAL_BYTES; + mReq->req.actual = tmptoken & TD_TOTAL_BYTES; mReq->req.actual >>= __ffs(TD_TOTAL_BYTES); mReq->req.actual = mReq->req.length - mReq->req.actual; mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; -- cgit v0.10.2 From 24dcade163753259ddcbf77018b1244d7d90ed6b Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:08 +0200 Subject: usb: chipidea: udc: don't truncate requests to single tds It is not safe to truncate requests to the maximum possible size the controller can handle with one td and to keep working. That patch fixes that with proper error handling instead. Reported-by: Felipe Balbi Signed-off-by: Michael Grzeschik Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 20a5b79..0531532 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1158,9 +1158,9 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, } if (req->length > 4 * CI13XXX_PAGE_SIZE) { - req->length = 4 * CI13XXX_PAGE_SIZE; retval = -EMSGSIZE; - dev_warn(mEp->ci->dev, "request length truncated\n"); + dev_err(mEp->ci->dev, "request bigger than one td\n"); + goto done; } /* push request */ -- cgit v0.10.2 From dd064e9d36b32616e72dc9c1871d7e667cd970ce Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:09 +0200 Subject: usb: chipidea: udc: move _ep_queue into an unlocked function There is no need to call ep_queue unlocked inside the own driver. We move its functionionality into an unlocked version. This patch removes potential unlocked timeslots inside isr_setup_status_phase and isr_get_status_response, in which the lock got released just before acquired again inside usb_ep_queue. Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde Reviewed-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0531532..ec218b0 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -643,6 +643,59 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) } /** + * _ep_queue: queues (submits) an I/O request to an endpoint + * + * Caller must hold lock + */ +static int _ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t __maybe_unused gfp_flags) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + struct ci13xxx *ci = mEp->ci; + int retval = 0; + + if (ep == NULL || req == NULL || mEp->ep.desc == NULL) + return -EINVAL; + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { + if (req->length) + mEp = (ci->ep0_dir == RX) ? + ci->ep0out : ci->ep0in; + if (!list_empty(&mEp->qh.queue)) { + _ep_nuke(mEp); + retval = -EOVERFLOW; + dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", + _usb_addr(mEp)); + } + } + + /* first nuke then test link, e.g. previous status has not sent */ + if (!list_empty(&mReq->queue)) { + dev_err(mEp->ci->dev, "request already in queue\n"); + return -EBUSY; + } + + if (req->length > 4 * CI13XXX_PAGE_SIZE) { + dev_err(mEp->ci->dev, "request bigger than one td\n"); + return -EMSGSIZE; + } + + /* push request */ + mReq->req.status = -EINPROGRESS; + mReq->req.actual = 0; + + retval = _hardware_enqueue(mEp, mReq); + + if (retval == -EALREADY) + retval = 0; + if (!retval) + list_add_tail(&mReq->queue, &mEp->qh.queue); + + return retval; +} + +/** * isr_get_status_response: get_status request response * @ci: ci struct * @setup: setup request packet @@ -689,9 +742,7 @@ __acquires(mEp->lock) } /* else do nothing; reserved for future use */ - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, req, gfp_flags); - spin_lock(mEp->lock); + retval = _ep_queue(&mEp->ep, req, gfp_flags); if (retval) goto err_free_buf; @@ -738,8 +789,6 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) * This function returns an error code */ static int isr_setup_status_phase(struct ci13xxx *ci) -__releases(mEp->lock) -__acquires(mEp->lock) { int retval; struct ci13xxx_ep *mEp; @@ -748,9 +797,7 @@ __acquires(mEp->lock) ci->status->context = ci; ci->status->complete = isr_setup_status_complete; - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); - spin_lock(mEp->lock); + retval = _ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); return retval; } @@ -1128,8 +1175,6 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t __maybe_unused gfp_flags) { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - struct ci13xxx *ci = mEp->ci; int retval = 0; unsigned long flags; @@ -1137,44 +1182,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, return -EINVAL; spin_lock_irqsave(mEp->lock, flags); - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { - if (req->length) - mEp = (ci->ep0_dir == RX) ? - ci->ep0out : ci->ep0in; - if (!list_empty(&mEp->qh.queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", - _usb_addr(mEp)); - } - } - - /* first nuke then test link, e.g. previous status has not sent */ - if (!list_empty(&mReq->queue)) { - retval = -EBUSY; - dev_err(mEp->ci->dev, "request already in queue\n"); - goto done; - } - - if (req->length > 4 * CI13XXX_PAGE_SIZE) { - retval = -EMSGSIZE; - dev_err(mEp->ci->dev, "request bigger than one td\n"); - goto done; - } - - /* push request */ - mReq->req.status = -EINPROGRESS; - mReq->req.actual = 0; - - retval = _hardware_enqueue(mEp, mReq); - - if (retval == -EALREADY) - retval = 0; - if (!retval) - list_add_tail(&mReq->queue, &mEp->qh.queue); - - done: + retval = _ep_queue(ep, req, gfp_flags); spin_unlock_irqrestore(mEp->lock, flags); return retval; } -- cgit v0.10.2 From b983e51a73b60b358e2c15a64c0c489cb266fe67 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:10 +0200 Subject: usb: chipidea: udc: add the define TD_PAGE_COUNT and fix all users A static count of transfer descriptors was used everywhere in the driver with the fixed number 5. This patch adds a define, named TD_PAGE_COUNT, and replaces all users of this value. This way its possible to have only one parameter to change and limit the amount of buffer pointers per TD. Signed-off-by: Michael Grzeschik Reviewed-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 68577d1..b0a6bce 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -21,6 +21,7 @@ /****************************************************************************** * DEFINE *****************************************************************************/ +#define TD_PAGE_COUNT 5 #define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX 32 diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ec218b0..e502e48 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -429,7 +429,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->ptr->token |= cpu_to_le32(TD_IOC); } mReq->ptr->page[0] = cpu_to_le32(mReq->req.dma); - for (i = 1; i < 5; i++) { + for (i = 1; i < TD_PAGE_COUNT; i++) { u32 page = mReq->req.dma + i * CI13XXX_PAGE_SIZE; page &= ~TD_RESERVED_MASK; mReq->ptr->page[i] = cpu_to_le32(page); @@ -676,7 +676,7 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, return -EBUSY; } - if (req->length > 4 * CI13XXX_PAGE_SIZE) { + if (req->length > (TD_PAGE_COUNT - 1) * CI13XXX_PAGE_SIZE) { dev_err(mEp->ci->dev, "request bigger than one td\n"); return -EMSGSIZE; } -- cgit v0.10.2 From 8e277d7cfae470b76eb955f6ee9aa32d111bf5ae Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Fri, 29 Mar 2013 17:22:44 -0700 Subject: usb: PS3 EHCI remove unneeded ehci_shutdown Remove an unneeded call to ehci_shutdown() in ps3_ehci_remove(). This removal will allow for a loadable ehci driver. Cc: Arnd Bergmann Signed-off-by: Geoff Levand Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index df5925a..fd98377 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -221,7 +221,6 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev) tmp = hcd->irq; - ehci_shutdown(hcd); usb_remove_hcd(hcd); ps3_system_bus_set_drvdata(dev, NULL); -- cgit v0.10.2 From 505bdbc79d20f8f2a56c02498f079bb23b0ef756 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 1 Apr 2013 13:04:08 +0800 Subject: USB: driver.c: processing failure, maching resume condition with suspend condition when suspend, it need check 'udev->actconfig'. so when process failure, also need check it. Signed-off-by: Chen Gang Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 84d2b05..6eab440 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1253,10 +1253,12 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { - msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); - while (++i < n) { - intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, msg, 0); + if (udev->actconfig) { + msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); + while (++i < n) { + intf = udev->actconfig->interface[i]; + usb_resume_interface(udev, intf, msg, 0); + } } /* If the suspend succeeded then prevent any more URB submissions -- cgit v0.10.2 From f65f4f40fb23b64a59adbe8629e8e7e6fea279cf Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 24 Mar 2013 17:36:53 +0200 Subject: usb: phy: twl4030-usb: check if vbus is driven by twl itself At least on pandora, STS_VBUS gets set even when VBUS is driven by twl itself. Reporting VBUS in this case confuses OMAP musb glue and charger driver, so check if OTG VBUS charge pump is on before reporting VBUS event to avoid this problem. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 61fe7e2..3f9858f 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -248,6 +248,25 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ +static bool twl4030_is_driving_vbus(struct twl4030_usb *twl) +{ + int ret; + + ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); + if (ret < 0 || !(ret & PHY_DPLL_CLK)) + /* + * if clocks are off, registers are not updated, + * but we can assume we don't drive VBUS in this case + */ + return false; + + ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); + if (ret < 0) + return false; + + return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; +} + static enum omap_musb_vbus_id_status twl4030_usb_linkstat(struct twl4030_usb *twl) { @@ -270,13 +289,19 @@ static enum omap_musb_vbus_id_status if (status < 0) dev_err(twl->dev, "USB link status err %d\n", status); else if (status & (BIT(7) | BIT(2))) { - if (status & (BIT(7))) - twl->vbus_supplied = true; + if (status & BIT(7)) { + if (twl4030_is_driving_vbus(twl)) + status &= ~BIT(7); + else + twl->vbus_supplied = true; + } if (status & BIT(2)) linkstat = OMAP_MUSB_ID_GROUND; - else + else if (status & BIT(7)) linkstat = OMAP_MUSB_VBUS_VALID; + else + linkstat = OMAP_MUSB_VBUS_OFF; } else { if (twl->linkstat != OMAP_MUSB_UNKNOWN) linkstat = OMAP_MUSB_VBUS_OFF; -- cgit v0.10.2 From 2c1fe89db1cfa9826c94e848c14dc68de0e2b0d5 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 24 Mar 2013 17:36:54 +0200 Subject: usb: musb: omap2430: turn off vbus on cable disconnect On USB_EVENT_ID event the musb glue enables VBUS by calling omap2430_musb_set_vbus(musb, 1) that sets the session bit, but on USB_EVENT_NONE reverse action is never made, and that breaks PM. Disable VBUS on USB_EVENT_NONE to be sure musb session is ended on cable unplug so that PM works. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 798e029..3551f1a 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -291,6 +291,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) musb->xceiv->last_event = USB_EVENT_NONE; if (musb->gadget_driver) { + omap2430_musb_set_vbus(musb, 0); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } -- cgit v0.10.2 From b65ae0f1a7b98c370967cf161ee87011e3ecdd77 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 24 Mar 2013 17:36:55 +0200 Subject: usb: musb: gadget: use platform callback to enable vbus On some platform configurations (like OMAP3+twl4030) it's the platform code that enables VBUS, not OTG transceiver, so call vbus platform callback instead, it will then call the transceiver if needed. This fixes a use case where USB cable is plugged first and gadget driver is loaded later after that. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index ff6ba3a..4be4865 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1848,9 +1848,8 @@ static int musb_gadget_start(struct usb_gadget *g, goto err; } - if ((musb->xceiv->last_event == USB_EVENT_ID) - && otg->set_vbus) - otg_set_vbus(otg, 1); + if (musb->xceiv->last_event == USB_EVENT_ID) + musb_platform_set_vbus(musb, 1); hcd->self.uses_pio_for_control = 1; -- cgit v0.10.2 From 1919811fe635de4689b4f31a20da3d50196cf43f Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:52 -0400 Subject: usb: gadget: mv_udc_core: remove unused clock The origianl understanding of clock is wrong. The UDC controller only have one clock input. Passing clock name by pdata is wrong. The clock is defined by device iteself. Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 9073436..be77f20 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -222,8 +222,7 @@ struct mv_udc { struct mv_usb_platform_data *pdata; /* some SOC has mutiple clock sources for USB*/ - unsigned int clknum; - struct clk *clk[0]; + struct clk *clk; }; /* endpoint data structure */ diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 88be74e..c2a5702 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -974,18 +974,12 @@ static struct usb_ep_ops mv_ep_ops = { static void udc_clock_enable(struct mv_udc *udc) { - unsigned int i; - - for (i = 0; i < udc->clknum; i++) - clk_prepare_enable(udc->clk[i]); + clk_prepare_enable(udc->clk); } static void udc_clock_disable(struct mv_udc *udc) { - unsigned int i; - - for (i = 0; i < udc->clknum; i++) - clk_disable_unprepare(udc->clk[i]); + clk_disable_unprepare(udc->clk); } static void udc_stop(struct mv_udc *udc) @@ -2109,7 +2103,6 @@ static int mv_udc_probe(struct platform_device *pdev) struct mv_usb_platform_data *pdata = pdev->dev.platform_data; struct mv_udc *udc; int retval = 0; - int clk_i = 0; struct resource *r; size_t size; @@ -2118,8 +2111,7 @@ static int mv_udc_probe(struct platform_device *pdev) return -ENODEV; } - size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum; - udc = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); if (udc == NULL) { dev_err(&pdev->dev, "failed to allocate memory for udc\n"); return -ENOMEM; @@ -2145,15 +2137,10 @@ static int mv_udc_probe(struct platform_device *pdev) } } - udc->clknum = pdata->clknum; - for (clk_i = 0; clk_i < udc->clknum; clk_i++) { - udc->clk[clk_i] = devm_clk_get(&pdev->dev, - pdata->clkname[clk_i]); - if (IS_ERR(udc->clk[clk_i])) { - retval = PTR_ERR(udc->clk[clk_i]); - return retval; - } - } + /* udc only have one sysclk. */ + udc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(udc->clk)) + return PTR_ERR(udc->clk); r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); if (r == NULL) { -- cgit v0.10.2 From df18fedae5f7870529a0a79193e24e67f56d995e Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:53 -0400 Subject: usb: otg: mv_otg: remove unused clock The origianl understanding of clock is wrong. The OTG controller only have one clock input. Passing clock name by pdata is wrong. The clock is defined by device iteself. Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 6872bf0..c987bbe 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -237,18 +237,12 @@ static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) static void otg_clock_enable(struct mv_otg *mvotg) { - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_prepare_enable(mvotg->clk[i]); + clk_prepare_enable(mvotg->clk); } static void otg_clock_disable(struct mv_otg *mvotg) { - unsigned int i; - - for (i = 0; i < mvotg->clknum; i++) - clk_disable_unprepare(mvotg->clk[i]); + clk_disable_unprepare(mvotg->clk); } static int mv_otg_enable_internal(struct mv_otg *mvotg) @@ -684,16 +678,14 @@ static int mv_otg_probe(struct platform_device *pdev) struct mv_otg *mvotg; struct usb_otg *otg; struct resource *r; - int retval = 0, clk_i, i; - size_t size; + int retval = 0, i; if (pdata == NULL) { dev_err(&pdev->dev, "failed to get platform data\n"); return -ENODEV; } - size = sizeof(*mvotg) + sizeof(struct clk *) * pdata->clknum; - mvotg = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL); if (!mvotg) { dev_err(&pdev->dev, "failed to allocate memory!\n"); return -ENOMEM; @@ -708,15 +700,9 @@ static int mv_otg_probe(struct platform_device *pdev) mvotg->pdev = pdev; mvotg->pdata = pdata; - mvotg->clknum = pdata->clknum; - for (clk_i = 0; clk_i < mvotg->clknum; clk_i++) { - mvotg->clk[clk_i] = devm_clk_get(&pdev->dev, - pdata->clkname[clk_i]); - if (IS_ERR(mvotg->clk[clk_i])) { - retval = PTR_ERR(mvotg->clk[clk_i]); - return retval; - } - } + mvotg->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mvotg->clk)) + return PTR_ERR(mvotg->clk); mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); if (!mvotg->qwork) { diff --git a/drivers/usb/phy/phy-mv-usb.h b/drivers/usb/phy/phy-mv-usb.h index 8a9e351..551da6e 100644 --- a/drivers/usb/phy/phy-mv-usb.h +++ b/drivers/usb/phy/phy-mv-usb.h @@ -158,8 +158,7 @@ struct mv_otg { unsigned int active; unsigned int clock_gating; - unsigned int clknum; - struct clk *clk[0]; + struct clk *clk; }; #endif -- cgit v0.10.2 From b7e159c29548955a61439d217181d67409bf8bc7 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:54 -0400 Subject: usb: ehci: mv_ehci: remove unused clock The origianl understanding of clock is wrong. The EHCI controller only have one clock input. Passing clock name by pdata is wrong. The clock is defined by device iteself. Acked-by: Alan Stern Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 3804820..6bad41a 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -33,25 +33,17 @@ struct ehci_hcd_mv { struct mv_usb_platform_data *pdata; - /* clock source and total clock number */ - unsigned int clknum; - struct clk *clk[0]; + struct clk *clk; }; static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) { - unsigned int i; - - for (i = 0; i < ehci_mv->clknum; i++) - clk_prepare_enable(ehci_mv->clk[i]); + clk_prepare_enable(ehci_mv->clk); } static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) { - unsigned int i; - - for (i = 0; i < ehci_mv->clknum; i++) - clk_disable_unprepare(ehci_mv->clk[i]); + clk_disable_unprepare(ehci_mv->clk); } static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) @@ -144,9 +136,8 @@ static int mv_ehci_probe(struct platform_device *pdev) struct ehci_hcd *ehci; struct ehci_hcd_mv *ehci_mv; struct resource *r; - int clk_i, retval = -ENODEV; + int retval = -ENODEV; u32 offset; - size_t size; if (!pdata) { dev_err(&pdev->dev, "missing platform_data\n"); @@ -160,8 +151,7 @@ static int mv_ehci_probe(struct platform_device *pdev) if (!hcd) return -ENOMEM; - size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum; - ehci_mv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL); if (ehci_mv == NULL) { dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n"); retval = -ENOMEM; @@ -172,16 +162,11 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->pdata = pdata; ehci_mv->hcd = hcd; - ehci_mv->clknum = pdata->clknum; - for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) { - ehci_mv->clk[clk_i] = - devm_clk_get(&pdev->dev, pdata->clkname[clk_i]); - if (IS_ERR(ehci_mv->clk[clk_i])) { - dev_err(&pdev->dev, "error get clck \"%s\"\n", - pdata->clkname[clk_i]); - retval = PTR_ERR(ehci_mv->clk[clk_i]); - goto err_clear_drvdata; - } + ehci_mv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ehci_mv->clk)) { + dev_err(&pdev->dev, "error getting clock\n"); + retval = PTR_ERR(ehci_mv->clk); + goto err_clear_drvdata; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); -- cgit v0.10.2 From 8f75d400984bb0bf538e1df7ec483076b83e7a62 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:55 -0400 Subject: arm: mmp: remove clock from usb pdata for aspenite The clock name will directly get by driver. Removing the name from pdata. Acked-by: Haojian Zhuang Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c index 9f64d56..76901f4 100644 --- a/arch/arm/mach-mmp/aspenite.c +++ b/arch/arm/mach-mmp/aspenite.c @@ -223,13 +223,7 @@ static struct pxa27x_keypad_platform_data aspenite_keypad_info __initdata = { }; #if defined(CONFIG_USB_EHCI_MV) -static char *pxa168_sph_clock_name[] = { - [0] = "PXA168-USBCLK", -}; - static struct mv_usb_platform_data pxa168_sph_pdata = { - .clknum = 1, - .clkname = pxa168_sph_clock_name, .mode = MV_USB_MODE_HOST, .phy_init = pxa_usb_phy_init, .phy_deinit = pxa_usb_phy_deinit, -- cgit v0.10.2 From 1805e811f00623465900238d4863fc9433e0b99e Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:56 -0400 Subject: arm: mmp: remove clock name from usb pdata for ttc The clock name will directly get by driver. Removing the name from pdata. Acked-by: Haojian Zhuang Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c index 22a9058..6528a5f 100644 --- a/arch/arm/mach-mmp/ttc_dkb.c +++ b/arch/arm/mach-mmp/ttc_dkb.c @@ -162,13 +162,7 @@ static struct i2c_board_info ttc_dkb_i2c_info[] = { #ifdef CONFIG_USB_SUPPORT #if defined(CONFIG_USB_MV_UDC) || defined(CONFIG_USB_EHCI_MV_U2O) -static char *pxa910_usb_clock_name[] = { - [0] = "U2OCLK", -}; - static struct mv_usb_platform_data ttc_usb_pdata = { - .clknum = 1, - .clkname = pxa910_usb_clock_name, .vbus = NULL, .mode = MV_USB_MODE_OTG, .otg_force_a_bus_req = 1, -- cgit v0.10.2 From 65cd3f2b877ed11faa0899524746d27ed2d5aa75 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Apr 2013 11:12:11 +0300 Subject: usb: gadget: mv_u3d_core: remove unused clock The origianl understanding of clock is wrong. The UDC controller only have one clock input. Passing clock name by pdata is wrong. The clock is defined by device iteself. Cc: Chao Xie Cc: Yu Xu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 9675227..58288e9 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1821,7 +1821,7 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->dev = &dev->dev; u3d->vbus = pdata->vbus; - u3d->clk = clk_get(&dev->dev, pdata->clkname[0]); + u3d->clk = clk_get(&dev->dev, NULL); if (IS_ERR(u3d->clk)) { retval = PTR_ERR(u3d->clk); goto err_get_clk; -- cgit v0.10.2 From ef096542642874de10909f02686447a96a66ad14 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Mon, 25 Mar 2013 03:06:57 -0400 Subject: usb: mv_usb: remove clock name from pdata Using pdata to pass clock name is not correct. Directly get clock from usb drivers. Signed-off-by: Chao Xie Signed-off-by: Felipe Balbi diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h index 944b01d..98b7925 100644 --- a/include/linux/platform_data/mv_usb.h +++ b/include/linux/platform_data/mv_usb.h @@ -34,8 +34,6 @@ struct mv_usb_addon_irq { }; struct mv_usb_platform_data { - unsigned int clknum; - char **clkname; struct mv_usb_addon_irq *id; /* Only valid for OTG. ID pin change*/ struct mv_usb_addon_irq *vbus; /* valid for OTG/UDC. VBUS change*/ -- cgit v0.10.2 From 225da3e3cb1f0db9e4cb7fa2a7dc3a360d1cf788 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 31 Mar 2013 18:34:43 -0700 Subject: usb: renesas_usbhs: fixup sparse errors for common.c This patch fixup below sparse errors CHECK ${RENESAS_USB}/common.c ${RENESAS_USB}/common.c:313:17: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:322:17: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:384:17: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:524:9: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:545:9: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:574:9: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/common.c:606:9: error: incompatible types in conditional expression (different base types) ${RENESAS_USB}/mod_gadget.c:233:28: warning: symbol 'req_clear_feature' was not declared. Should it be static? ${RENESAS_USB}/mod_gadget.c:274:28: warning: symbol 'req_set_feature' was not declared. Should it be static? ${RENESAS_USB}/mod_gadget.c:375:28: warning: symbol 'req_get_status' was not declared. Should it be static? [ balbi@ti.com : added three sparse fixes to mod_gadget.c ] Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index f2ec077..ff8b7ba 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -169,7 +169,7 @@ static int usbhsf_get_id(struct platform_device *pdev) return USBHS_GADGET; } -static void usbhsf_power_ctrl(struct platform_device *pdev, +static int usbhsf_power_ctrl(struct platform_device *pdev, void __iomem *base, int enable) { struct usbhsf_private *priv = usbhsf_get_priv(pdev); @@ -223,6 +223,8 @@ static void usbhsf_power_ctrl(struct platform_device *pdev, clk_disable(priv->pci); /* usb work around */ clk_disable(priv->usb24); /* usb work around */ } + + return 0; } static int usbhsf_get_vbus(struct platform_device *pdev) @@ -239,7 +241,7 @@ static irqreturn_t usbhsf_interrupt(int irq, void *data) return IRQ_HANDLED; } -static void usbhsf_hardware_exit(struct platform_device *pdev) +static int usbhsf_hardware_exit(struct platform_device *pdev) { struct usbhsf_private *priv = usbhsf_get_priv(pdev); @@ -264,6 +266,8 @@ static void usbhsf_hardware_exit(struct platform_device *pdev) priv->usbh_base = NULL; free_irq(IRQ7, pdev); + + return 0; } static int usbhsf_hardware_init(struct platform_device *pdev) diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index 7f3a6b7..a385f57 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -155,12 +155,14 @@ static int usbhs_get_vbus(struct platform_device *pdev) return !((1 << 7) & __raw_readw(priv->cr2)); } -static void usbhs_phy_reset(struct platform_device *pdev) +static int usbhs_phy_reset(struct platform_device *pdev) { struct usbhs_private *priv = usbhs_get_priv(pdev); /* init phy */ __raw_writew(0x8a0a, priv->cr2); + + return 0; } static int usbhs_get_id(struct platform_device *pdev) @@ -202,7 +204,7 @@ static int usbhs_hardware_init(struct platform_device *pdev) return 0; } -static void usbhs_hardware_exit(struct platform_device *pdev) +static int usbhs_hardware_exit(struct platform_device *pdev) { struct usbhs_private *priv = usbhs_get_priv(pdev); @@ -210,6 +212,8 @@ static void usbhs_hardware_exit(struct platform_device *pdev) __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy); free_irq(IRQ15, pdev); + + return 0; } static u32 usbhs_pipe_cfg[] = { diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index db968a5..979237c 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -596,12 +596,14 @@ static int usbhs_get_vbus(struct platform_device *pdev) return usbhs_is_connected(usbhs_get_priv(pdev)); } -static void usbhs_phy_reset(struct platform_device *pdev) +static int usbhs_phy_reset(struct platform_device *pdev) { struct usbhs_private *priv = usbhs_get_priv(pdev); /* init phy */ __raw_writew(0x8a0a, priv->usbcrcaddr); + + return 0; } static int usbhs0_get_id(struct platform_device *pdev) @@ -628,11 +630,13 @@ static int usbhs0_hardware_init(struct platform_device *pdev) return 0; } -static void usbhs0_hardware_exit(struct platform_device *pdev) +static int usbhs0_hardware_exit(struct platform_device *pdev) { struct usbhs_private *priv = usbhs_get_priv(pdev); cancel_delayed_work_sync(&priv->work); + + return 0; } static struct usbhs_private usbhs0_private = { @@ -735,7 +739,7 @@ static int usbhs1_hardware_init(struct platform_device *pdev) return 0; } -static void usbhs1_hardware_exit(struct platform_device *pdev) +static int usbhs1_hardware_exit(struct platform_device *pdev) { struct usbhs_private *priv = usbhs_get_priv(pdev); @@ -743,6 +747,8 @@ static void usbhs1_hardware_exit(struct platform_device *pdev) __raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr); free_irq(IRQ8, pdev); + + return 0; } static int usbhs1_get_id(struct platform_device *pdev) diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index aaff767..764530c 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -254,11 +254,13 @@ static int usbhs_get_id(struct platform_device *pdev) return gpio_get_value(GPIO_PTB3); } -static void usbhs_phy_reset(struct platform_device *pdev) +static int usbhs_phy_reset(struct platform_device *pdev) { /* enable vbus if HOST */ if (!gpio_get_value(GPIO_PTB3)) gpio_set_value(GPIO_PTB5, 1); + + return 0; } static struct renesas_usbhs_platform_info usbhs_info = { diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index c2781bc..ed4949f 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -230,7 +230,7 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, return 0; } -struct usbhsg_recip_handle req_clear_feature = { +static struct usbhsg_recip_handle req_clear_feature = { .name = "clear feature", .device = usbhsg_recip_handler_std_control_done, .interface = usbhsg_recip_handler_std_control_done, @@ -271,7 +271,7 @@ static int usbhsg_recip_handler_std_set_endpoint(struct usbhs_priv *priv, return 0; } -struct usbhsg_recip_handle req_set_feature = { +static struct usbhsg_recip_handle req_set_feature = { .name = "set feature", .device = usbhsg_recip_handler_std_set_device, .interface = usbhsg_recip_handler_std_control_done, @@ -372,7 +372,7 @@ static int usbhsg_recip_handler_std_get_endpoint(struct usbhs_priv *priv, return 0; } -struct usbhsg_recip_handle req_get_status = { +static struct usbhsg_recip_handle req_get_status = { .name = "get status", .device = usbhsg_recip_handler_std_get_device, .interface = usbhsg_recip_handler_std_get_interface, diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index c5d36c6..e452ba6 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -62,14 +62,14 @@ struct renesas_usbhs_platform_callback { * Hardware exit function for platform. * it is called when driver was removed */ - void (*hardware_exit)(struct platform_device *pdev); + int (*hardware_exit)(struct platform_device *pdev); /* * option: * * for board specific clock control */ - void (*power_ctrl)(struct platform_device *pdev, + int (*power_ctrl)(struct platform_device *pdev, void __iomem *base, int enable); /* @@ -77,7 +77,7 @@ struct renesas_usbhs_platform_callback { * * Phy reset for platform */ - void (*phy_reset)(struct platform_device *pdev); + int (*phy_reset)(struct platform_device *pdev); /* * get USB ID function -- cgit v0.10.2 From d692522577c051058efbe9e3c8aef68a4c36e4f7 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 28 Mar 2013 15:11:52 +0530 Subject: usb: gadget/uvc: Port UVC webcam gadget to use videobuf2 framework This patch reworks the videobuffer management logic present in the UVC webcam gadget and ports it to use the "more apt" videobuf2 framework for video buffer management. To support routing video data captured from a real V4L2 video capture device with a "zero copy" operation on videobuffers (as they pass from the V4L2 domain to UVC domain via a user-space application), we need to support USER_PTR IO method at the UVC gadget side. So the V4L2 capture device driver can still continue to use MMAP IO method and now the user-space application can just pass a pointer to the video buffers being dequeued from the V4L2 device side while queueing them at the UVC gadget end. This ensures that we have a "zero-copy" design as the videobuffers pass from the V4L2 capture device to the UVC gadget. Note that there will still be a need to apply UVC specific payload headers on top of each UVC payload data, which will still require a copy operation to be performed in the 'encode' routines of the UVC gadget. This patch also addresses one issue found out while porting the UVC gadget to videobuf2 framework: - In case the usb requests queued by the gadget get completed with a status of -ESHUTDOWN (disconnected from host), the queue of videobuf2 should be cancelled to ensure that the application space daemon is not left in a state waiting for a vb2 to be successfully absorbed at the USB side. Signed-off-by: Bhupesh Sharma Signed-off-by: Laurent Pinchart diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 261b1e3..1deadde 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -949,6 +949,7 @@ config USB_G_WEBCAM tristate "USB Webcam Gadget" depends on VIDEO_DEV select USB_LIBCOMPOSITE + select VIDEOBUF2_VMALLOC help The Webcam Gadget acts as a composite USB Audio and Video Class device. It provides a userspace API to process UVC control requests diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 104ae9c..3139795 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -10,6 +10,7 @@ * (at your option) any later version. */ +#include #include #include #include @@ -18,7 +19,8 @@ #include #include #include -#include + +#include #include "uvc.h" @@ -28,330 +30,175 @@ * Video queues is initialized by uvc_queue_init(). The function performs * basic initialization of the uvc_video_queue struct and never fails. * - * Video buffer allocation and freeing are performed by uvc_alloc_buffers and - * uvc_free_buffers respectively. The former acquires the video queue lock, - * while the later must be called with the lock held (so that allocation can - * free previously allocated buffers). Trying to free buffers that are mapped - * to user space will return -EBUSY. - * - * Video buffers are managed using two queues. However, unlike most USB video - * drivers that use an in queue and an out queue, we use a main queue to hold - * all queued buffers (both 'empty' and 'done' buffers), and an irq queue to - * hold empty buffers. This design (copied from video-buf) minimizes locking - * in interrupt, as only one queue is shared between interrupt and user - * contexts. - * - * Use cases - * --------- - * - * Unless stated otherwise, all operations that modify the irq buffers queue - * are protected by the irq spinlock. - * - * 1. The user queues the buffers, starts streaming and dequeues a buffer. - * - * The buffers are added to the main and irq queues. Both operations are - * protected by the queue lock, and the later is protected by the irq - * spinlock as well. - * - * The completion handler fetches a buffer from the irq queue and fills it - * with video data. If no buffer is available (irq queue empty), the handler - * returns immediately. - * - * When the buffer is full, the completion handler removes it from the irq - * queue, marks it as ready (UVC_BUF_STATE_DONE) and wakes its wait queue. - * At that point, any process waiting on the buffer will be woken up. If a - * process tries to dequeue a buffer after it has been marked ready, the - * dequeing will succeed immediately. - * - * 2. Buffers are queued, user is waiting on a buffer and the device gets - * disconnected. - * - * When the device is disconnected, the kernel calls the completion handler - * with an appropriate status code. The handler marks all buffers in the - * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so - * that any process waiting on a buffer gets woken up. - * - * Waking up up the first buffer on the irq list is not enough, as the - * process waiting on the buffer might restart the dequeue operation - * immediately. - * + * Video buffers are managed by videobuf2. The driver uses a mutex to protect + * the videobuf2 queue operations by serializing calls to videobuf2 and a + * spinlock to protect the IRQ queue that holds the buffers to be processed by + * the driver. */ -static void -uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) -{ - mutex_init(&queue->mutex); - spin_lock_init(&queue->irqlock); - INIT_LIST_HEAD(&queue->mainqueue); - INIT_LIST_HEAD(&queue->irqqueue); - queue->type = type; -} - -/* - * Free the video buffers. - * - * This function must be called with the queue lock held. +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations */ -static int uvc_free_buffers(struct uvc_video_queue *queue) + +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - unsigned int i; + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); - for (i = 0; i < queue->count; ++i) { - if (queue->buffer[i].vma_use_count != 0) - return -EBUSY; - } + if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) + *nbuffers = UVC_MAX_VIDEO_BUFFERS; - if (queue->count) { - vfree(queue->mem); - queue->count = 0; - } + *nplanes = 1; + + sizes[0] = video->imagesize; return 0; } -/* - * Allocate the video buffers. - * - * Pages are reserved to make sure they will not be swapped, as they will be - * filled in the URB completion handler. - * - * Buffers will be individually mapped, so they must all be page aligned. - */ -static int -uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, - unsigned int buflength) +static int uvc_buffer_prepare(struct vb2_buffer *vb) { - unsigned int bufsize = PAGE_ALIGN(buflength); - unsigned int i; - void *mem = NULL; - int ret; + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); - if (nbuffers > UVC_MAX_VIDEO_BUFFERS) - nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); + return -EINVAL; + } - mutex_lock(&queue->mutex); + if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) + return -ENODEV; - if ((ret = uvc_free_buffers(queue)) < 0) - goto done; + buf->state = UVC_BUF_STATE_QUEUED; + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + buf->bytesused = 0; + else + buf->bytesused = vb2_get_plane_payload(vb, 0); - /* Bail out if no buffers should be allocated. */ - if (nbuffers == 0) - goto done; + return 0; +} - /* Decrement the number of buffers until allocation succeeds. */ - for (; nbuffers > 0; --nbuffers) { - mem = vmalloc_32(nbuffers * bufsize); - if (mem != NULL) - break; - } +static void uvc_buffer_queue(struct vb2_buffer *vb) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + unsigned long flags; - if (mem == NULL) { - ret = -ENOMEM; - goto done; - } + spin_lock_irqsave(&queue->irqlock, flags); - for (i = 0; i < nbuffers; ++i) { - memset(&queue->buffer[i], 0, sizeof queue->buffer[i]); - queue->buffer[i].buf.index = i; - queue->buffer[i].buf.m.offset = i * bufsize; - queue->buffer[i].buf.length = buflength; - queue->buffer[i].buf.type = queue->type; - queue->buffer[i].buf.sequence = 0; - queue->buffer[i].buf.field = V4L2_FIELD_NONE; - queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; - queue->buffer[i].buf.flags = 0; - init_waitqueue_head(&queue->buffer[i].wait); + if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { + list_add_tail(&buf->queue, &queue->irqqueue); + } else { + /* If the device is disconnected return the buffer to userspace + * directly. The next QBUF call will fail with -ENODEV. + */ + buf->state = UVC_BUF_STATE_ERROR; + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); } - queue->mem = mem; - queue->count = nbuffers; - queue->buf_size = bufsize; - ret = nbuffers; - -done: - mutex_unlock(&queue->mutex); - return ret; + spin_unlock_irqrestore(&queue->irqlock, flags); } -static void __uvc_query_buffer(struct uvc_buffer *buf, - struct v4l2_buffer *v4l2_buf) -{ - memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); - - if (buf->vma_use_count) - v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED; - - switch (buf->state) { - case UVC_BUF_STATE_ERROR: - case UVC_BUF_STATE_DONE: - v4l2_buf->flags |= V4L2_BUF_FLAG_DONE; - break; - case UVC_BUF_STATE_QUEUED: - case UVC_BUF_STATE_ACTIVE: - v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; - break; - case UVC_BUF_STATE_IDLE: - default: - break; - } -} +static struct vb2_ops uvc_queue_qops = { + .queue_setup = uvc_queue_setup, + .buf_prepare = uvc_buffer_prepare, + .buf_queue = uvc_buffer_queue, +}; -static int -uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf) +static int uvc_queue_init(struct uvc_video_queue *queue, + enum v4l2_buf_type type) { - int ret = 0; + int ret; - mutex_lock(&queue->mutex); - if (v4l2_buf->index >= queue->count) { - ret = -EINVAL; - goto done; - } + queue->queue.type = type; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.drv_priv = queue; + queue->queue.buf_struct_size = sizeof(struct uvc_buffer); + queue->queue.ops = &uvc_queue_qops; + queue->queue.mem_ops = &vb2_vmalloc_memops; + ret = vb2_queue_init(&queue->queue); + if (ret) + return ret; + + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = 0; - __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf); + return 0; +} -done: +/* + * Free the video buffers. + */ +static void uvc_free_buffers(struct uvc_video_queue *queue) +{ + mutex_lock(&queue->mutex); + vb2_queue_release(&queue->queue); mutex_unlock(&queue->mutex); - return ret; } /* - * Queue a video buffer. Attempting to queue a buffer that has already been - * queued will return -EINVAL. + * Allocate the video buffers. */ -static int -uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf) +static int uvc_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) { - struct uvc_buffer *buf; - unsigned long flags; - int ret = 0; + int ret; - uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); + mutex_lock(&queue->mutex); + ret = vb2_reqbufs(&queue->queue, rb); + mutex_unlock(&queue->mutex); - if (v4l2_buf->type != queue->type || - v4l2_buf->memory != V4L2_MEMORY_MMAP) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " - "and/or memory (%u).\n", v4l2_buf->type, - v4l2_buf->memory); - return -EINVAL; - } + return ret ? ret : rb->count; +} - mutex_lock(&queue->mutex); - if (v4l2_buf->index >= queue->count) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n"); - ret = -EINVAL; - goto done; - } +static int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + int ret; - buf = &queue->buffer[v4l2_buf->index]; - if (buf->state != UVC_BUF_STATE_IDLE) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state " - "(%u).\n", buf->state); - ret = -EINVAL; - goto done; - } + mutex_lock(&queue->mutex); + ret = vb2_querybuf(&queue->queue, buf); + mutex_unlock(&queue->mutex); - if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - v4l2_buf->bytesused > buf->buf.length) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); - ret = -EINVAL; - goto done; - } + return ret; +} - if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - buf->buf.bytesused = 0; - else - buf->buf.bytesused = v4l2_buf->bytesused; +static int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + unsigned long flags; + int ret; + mutex_lock(&queue->mutex); + ret = vb2_qbuf(&queue->queue, buf); spin_lock_irqsave(&queue->irqlock, flags); - if (queue->flags & UVC_QUEUE_DISCONNECTED) { - spin_unlock_irqrestore(&queue->irqlock, flags); - ret = -ENODEV; - goto done; - } - buf->state = UVC_BUF_STATE_QUEUED; - ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; queue->flags &= ~UVC_QUEUE_PAUSED; - - list_add_tail(&buf->stream, &queue->mainqueue); - list_add_tail(&buf->queue, &queue->irqqueue); spin_unlock_irqrestore(&queue->irqlock, flags); - -done: mutex_unlock(&queue->mutex); - return ret; -} - -static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking) -{ - if (nonblocking) { - return (buf->state != UVC_BUF_STATE_QUEUED && - buf->state != UVC_BUF_STATE_ACTIVE) - ? 0 : -EAGAIN; - } - return wait_event_interruptible(buf->wait, - buf->state != UVC_BUF_STATE_QUEUED && - buf->state != UVC_BUF_STATE_ACTIVE); + return ret; } /* * Dequeue a video buffer. If nonblocking is false, block until a buffer is * available. */ -static int -uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf, - int nonblocking) +static int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf, int nonblocking) { - struct uvc_buffer *buf; - int ret = 0; - - if (v4l2_buf->type != queue->type || - v4l2_buf->memory != V4L2_MEMORY_MMAP) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " - "and/or memory (%u).\n", v4l2_buf->type, - v4l2_buf->memory); - return -EINVAL; - } + int ret; mutex_lock(&queue->mutex); - if (list_empty(&queue->mainqueue)) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n"); - ret = -EINVAL; - goto done; - } - - buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); - if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0) - goto done; - - uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n", - buf->buf.index, buf->state, buf->buf.bytesused); - - switch (buf->state) { - case UVC_BUF_STATE_ERROR: - uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data " - "(transmission error).\n"); - ret = -EIO; - case UVC_BUF_STATE_DONE: - buf->state = UVC_BUF_STATE_IDLE; - break; - - case UVC_BUF_STATE_IDLE: - case UVC_BUF_STATE_QUEUED: - case UVC_BUF_STATE_ACTIVE: - default: - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u " - "(driver bug?).\n", buf->state); - ret = -EINVAL; - goto done; - } - - list_del(&buf->stream); - __uvc_query_buffer(buf, v4l2_buf); - -done: + ret = vb2_dqbuf(&queue->queue, buf, nonblocking); mutex_unlock(&queue->mutex); + return ret; } @@ -361,103 +208,27 @@ done: * This function implements video queue polling and is intended to be used by * the device poll handler. */ -static unsigned int -uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, - poll_table *wait) +static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait) { - struct uvc_buffer *buf; - unsigned int mask = 0; + unsigned int ret; mutex_lock(&queue->mutex); - if (list_empty(&queue->mainqueue)) - goto done; - - buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); - - poll_wait(file, &buf->wait, wait); - if (buf->state == UVC_BUF_STATE_DONE || - buf->state == UVC_BUF_STATE_ERROR) - mask |= POLLOUT | POLLWRNORM; - -done: + ret = vb2_poll(&queue->queue, file, wait); mutex_unlock(&queue->mutex); - return mask; -} -/* - * VMA operations. - */ -static void uvc_vm_open(struct vm_area_struct *vma) -{ - struct uvc_buffer *buffer = vma->vm_private_data; - buffer->vma_use_count++; -} - -static void uvc_vm_close(struct vm_area_struct *vma) -{ - struct uvc_buffer *buffer = vma->vm_private_data; - buffer->vma_use_count--; + return ret; } -static struct vm_operations_struct uvc_vm_ops = { - .open = uvc_vm_open, - .close = uvc_vm_close, -}; - -/* - * Memory-map a buffer. - * - * This function implements video buffer memory mapping and is intended to be - * used by the device mmap handler. - */ -static int -uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) +static int uvc_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma) { - struct uvc_buffer *uninitialized_var(buffer); - struct page *page; - unsigned long addr, start, size; - unsigned int i; - int ret = 0; - - start = vma->vm_start; - size = vma->vm_end - vma->vm_start; + int ret; mutex_lock(&queue->mutex); - - for (i = 0; i < queue->count; ++i) { - buffer = &queue->buffer[i]; - if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) - break; - } - - if (i == queue->count || size != queue->buf_size) { - ret = -EINVAL; - goto done; - } - - /* - * VM_IO marks the area as being an mmaped region for I/O to a - * device. It also prevents the region from being core dumped. - */ - vma->vm_flags |= VM_IO; - - addr = (unsigned long)queue->mem + buffer->buf.m.offset; - while (size > 0) { - page = vmalloc_to_page((void *)addr); - if ((ret = vm_insert_page(vma, start, page)) < 0) - goto done; - - start += PAGE_SIZE; - addr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &uvc_vm_ops; - vma->vm_private_data = buffer; - uvc_vm_open(vma); - -done: + ret = vb2_mmap(&queue->queue, vma); mutex_unlock(&queue->mutex); + return ret; } @@ -484,7 +255,7 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) queue); list_del(&buf->queue); buf->state = UVC_BUF_STATE_ERROR; - wake_up(&buf->wait); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); } /* This must be protected by the irqlock spinlock to avoid race * conditions between uvc_queue_buffer and the disconnection event that @@ -516,26 +287,33 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) */ static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) { - unsigned int i; + unsigned long flags; int ret = 0; mutex_lock(&queue->mutex); if (enable) { - if (uvc_queue_streaming(queue)) { - ret = -EBUSY; + ret = vb2_streamon(&queue->queue, queue->queue.type); + if (ret < 0) goto done; - } + queue->sequence = 0; - queue->flags |= UVC_QUEUE_STREAMING; queue->buf_used = 0; } else { - uvc_queue_cancel(queue, 0); - INIT_LIST_HEAD(&queue->mainqueue); + ret = vb2_streamoff(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; - for (i = 0; i < queue->count; ++i) - queue->buffer[i].state = UVC_BUF_STATE_IDLE; + spin_lock_irqsave(&queue->irqlock, flags); + INIT_LIST_HEAD(&queue->irqqueue); - queue->flags &= ~UVC_QUEUE_STREAMING; + /* + * FIXME: We need to clear the DISCONNECTED flag to ensure that + * applications will be able to queue buffers for the next + * streaming run. However, clearing it here doesn't guarantee + * that the device will be reconnected in the meantime. + */ + queue->flags &= ~UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); } done: @@ -544,15 +322,15 @@ done: } /* called with &queue_irqlock held.. */ -static struct uvc_buffer * -uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) +static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && - buf->buf.length != buf->buf.bytesused) { + buf->length != buf->bytesused) { buf->state = UVC_BUF_STATE_QUEUED; - buf->buf.bytesused = 0; + vb2_set_plane_payload(&buf->buf, 0, 0); return buf; } @@ -563,10 +341,18 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) else nextbuf = NULL; - buf->buf.sequence = queue->sequence++; - do_gettimeofday(&buf->buf.timestamp); + /* + * FIXME: with videobuf2, the sequence number or timestamp fields + * are valid only for video capture devices and the UVC gadget usually + * is a video output device. Keeping these until the specs are clear on + * this aspect. + */ + buf->buf.v4l2_buf.sequence = queue->sequence++; + do_gettimeofday(&buf->buf.v4l2_buf.timestamp); + + vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); - wake_up(&buf->wait); return nextbuf; } diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h index 1812a8e..8e76ce9 100644 --- a/drivers/usb/gadget/uvc_queue.h +++ b/drivers/usb/gadget/uvc_queue.h @@ -6,6 +6,7 @@ #include #include #include +#include /* Maximum frame size in bytes, for sanity checking. */ #define UVC_MAX_FRAME_SIZE (16*1024*1024) @@ -25,42 +26,35 @@ enum uvc_buffer_state { }; struct uvc_buffer { - unsigned long vma_use_count; - struct list_head stream; - - /* Touched by interrupt handler. */ - struct v4l2_buffer buf; + struct vb2_buffer buf; struct list_head queue; - wait_queue_head_t wait; + enum uvc_buffer_state state; + void *mem; + unsigned int length; + unsigned int bytesused; }; -#define UVC_QUEUE_STREAMING (1 << 0) -#define UVC_QUEUE_DISCONNECTED (1 << 1) -#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) -#define UVC_QUEUE_PAUSED (1 << 3) +#define UVC_QUEUE_DISCONNECTED (1 << 0) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1) +#define UVC_QUEUE_PAUSED (1 << 2) struct uvc_video_queue { - enum v4l2_buf_type type; + struct vb2_queue queue; + struct mutex mutex; /* Protects queue */ - void *mem; unsigned int flags; __u32 sequence; - unsigned int count; - unsigned int buf_size; unsigned int buf_used; - struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; - struct mutex mutex; /* protects buffers and mainqueue */ - spinlock_t irqlock; /* protects irqqueue */ - struct list_head mainqueue; + spinlock_t irqlock; /* Protects flags and irqqueue */ struct list_head irqqueue; }; static inline int uvc_queue_streaming(struct uvc_video_queue *queue) { - return queue->flags & UVC_QUEUE_STREAMING; + return vb2_is_streaming(&queue->queue); } #endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index bb140dd..c058867 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -147,16 +147,13 @@ uvc_v4l2_release(struct file *file) uvc_function_disconnect(uvc); uvc_video_enable(video, 0); - mutex_lock(&video->queue.mutex); - if (uvc_free_buffers(&video->queue) < 0) - printk(KERN_ERR "uvc_v4l2_release: Unable to free " - "buffers.\n"); - mutex_unlock(&video->queue.mutex); + uvc_free_buffers(&video->queue); file->private_data = NULL; v4l2_fh_del(&handle->vfh); v4l2_fh_exit(&handle->vfh); kfree(handle); + return 0; } @@ -191,7 +188,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_format *fmt = arg; - if (fmt->type != video->queue.type) + if (fmt->type != video->queue.queue.type) return -EINVAL; return uvc_v4l2_get_format(video, fmt); @@ -201,7 +198,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_format *fmt = arg; - if (fmt->type != video->queue.type) + if (fmt->type != video->queue.queue.type) return -EINVAL; return uvc_v4l2_set_format(video, fmt); @@ -212,16 +209,13 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_requestbuffers *rb = arg; - if (rb->type != video->queue.type || - rb->memory != V4L2_MEMORY_MMAP) + if (rb->type != video->queue.queue.type) return -EINVAL; - ret = uvc_alloc_buffers(&video->queue, rb->count, - video->imagesize); + ret = uvc_alloc_buffers(&video->queue, rb); if (ret < 0) return ret; - rb->count = ret; ret = 0; break; } @@ -230,9 +224,6 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_buffer *buf = arg; - if (buf->type != video->queue.type) - return -EINVAL; - return uvc_query_buffer(&video->queue, buf); } @@ -250,7 +241,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->queue.type) + if (*type != video->queue.queue.type) return -EINVAL; /* Enable UVC video. */ @@ -272,14 +263,14 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->queue.type) + if (*type != video->queue.queue.type) return -EINVAL; return uvc_video_enable(video, 0); } /* Events */ - case VIDIOC_DQEVENT: + case VIDIOC_DQEVENT: { struct v4l2_event *event = arg; @@ -344,16 +335,8 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - unsigned int mask = 0; - - poll_wait(file, &handle->vfh.wait, wait); - if (v4l2_event_pending(&handle->vfh)) - mask |= POLLPRI; - - mask |= uvc_queue_poll(&uvc->video.queue, file, wait); - return mask; + return uvc_queue_poll(&uvc->video.queue, file, wait); } static struct v4l2_file_operations uvc_v4l2_fops = { diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index ec4bcc4..71e896d 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -32,7 +32,7 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, data[0] = 2; data[1] = UVC_STREAM_EOH | video->fid; - if (buf->buf.bytesused - video->queue.buf_used <= len - 2) + if (buf->bytesused - video->queue.buf_used <= len - 2) data[1] |= UVC_STREAM_EOF; return 2; @@ -47,8 +47,8 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, void *mem; /* Copy video data to the USB buffer. */ - mem = queue->mem + buf->buf.m.offset + queue->buf_used; - nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); + mem = buf->mem + queue->buf_used; + nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); memcpy(data, mem, nbytes); queue->buf_used += nbytes; @@ -82,7 +82,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, req->length = video->req_size - len; req->zero = video->payload_size == video->max_payload_size; - if (buf->buf.bytesused == video->queue.buf_used) { + if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; uvc_queue_next_buffer(&video->queue, buf); @@ -92,7 +92,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } if (video->payload_size == video->max_payload_size || - buf->buf.bytesused == video->queue.buf_used) + buf->bytesused == video->queue.buf_used) video->payload_size = 0; } @@ -115,7 +115,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, req->length = video->req_size - len; - if (buf->buf.bytesused == video->queue.buf_used) { + if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; uvc_queue_next_buffer(&video->queue, buf); @@ -161,6 +161,7 @@ static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { struct uvc_video *video = req->context; + struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *buf; unsigned long flags; int ret; @@ -169,13 +170,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) case 0: break; - case -ESHUTDOWN: + case -ESHUTDOWN: /* disconnect from host. */ printk(KERN_INFO "VS request cancelled.\n"); + uvc_queue_cancel(queue, 1); goto requeue; default: printk(KERN_INFO "VS request completed with status %d.\n", req->status); + uvc_queue_cancel(queue, 0); goto requeue; } -- cgit v0.10.2 From 2f1d57069338b14fcf4765ae2c25fc377da45b1f Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 28 Mar 2013 15:11:53 +0530 Subject: usb: gadget/uvc: Add support for 'get_unmapped_area' for MMUless architectures This patch adds the support for 'get_unmapped_area' in UVC gadget which is called when the 'mmap' system call is executed on MMUless architectures. Signed-off-by: Bhupesh Sharma Signed-off-by: Laurent Pinchart diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 3139795..7ce27e3 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -232,6 +232,24 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue, return ret; } +#ifndef CONFIG_MMU +/* + * Get unmapped area. + * + * NO-MMU arch need this function to make mmap() work correctly. + */ +static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + /* * Cancel the video buffers queue. * diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index c058867..ad48e81 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -339,6 +339,18 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) return uvc_queue_poll(&uvc->video.queue, file, wait); } +#ifndef CONFIG_MMU +static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + + return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff); +} +#endif + static struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, @@ -346,5 +358,8 @@ static struct v4l2_file_operations uvc_v4l2_fops = { .ioctl = uvc_v4l2_ioctl, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, +#ifndef CONFIG_MMU + .get_unmapped_area = uvc_v4l2_get_unmapped_area, +#endif }; -- cgit v0.10.2 From 8408fd1d83e39bf856d31a36b70bcc53527702fd Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Fri, 29 Mar 2013 19:15:21 +0200 Subject: usb: musb: implement (un)map_urb_for_dma hooks MUSB controller cannot work in DMA mode with misaligned buffers, switching in PIO mode. HCD core has hooks that allow to override the default DMA mapping and unmapping routines for host controllers that have special DMA requirements, such as alignment constraints. It is observed that work in PIO mode is slow and it's better to align buffers properly before passing them to MUSB This increased throughput 80->120 MBits/s over musb@omap4 with USB Gigabit Ethernet adapter attached. Some ideas are taken from ehci-tegra.c Signed-off-by: Ruslan Bilovol Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 51e9e8a..8914dec 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2465,6 +2465,118 @@ static int musb_bus_resume(struct usb_hcd *hcd) return 0; } + +#ifndef CONFIG_MUSB_PIO_ONLY + +#define MUSB_USB_DMA_ALIGN 4 + +struct musb_temp_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void musb_free_temp_buffer(struct urb *urb) +{ + enum dma_data_direction dir; + struct musb_temp_buffer *temp; + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + temp = container_of(urb->transfer_buffer, struct musb_temp_buffer, + data); + + if (dir == DMA_FROM_DEVICE) { + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); + } + urb->transfer_buffer = temp->old_xfer_buffer; + kfree(temp->kmalloc_ptr); + + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; +} + +static int musb_alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) +{ + enum dma_data_direction dir; + struct musb_temp_buffer *temp; + void *kmalloc_ptr; + size_t kmalloc_size; + + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (MUSB_USB_DMA_ALIGN - 1))) + return 0; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct musb_temp_buffer) + MUSB_USB_DMA_ALIGN - 1; + + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + if (!kmalloc_ptr) + return -ENOMEM; + + /* Position our struct temp_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr, MUSB_USB_DMA_ALIGN); + + + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + if (dir == DMA_TO_DEVICE) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->data; + + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; +} + +static int musb_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct musb *musb = hcd_to_musb(hcd); + int ret; + + /* + * The DMA engine in RTL1.8 and above cannot handle + * DMA addresses that are not aligned to a 4 byte boundary. + * For such engine implemented (un)map_urb_for_dma hooks. + * Do not use these hooks for RTL<1.8 + */ + if (musb->hwvers < MUSB_HWVERS_1800) + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + + ret = musb_alloc_temp_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + if (ret) + musb_free_temp_buffer(urb); + + return ret; +} + +static void musb_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + struct musb *musb = hcd_to_musb(hcd); + + usb_hcd_unmap_urb_for_dma(hcd, urb); + + /* Do not use this hook for RTL<1.8 (see description above) */ + if (musb->hwvers < MUSB_HWVERS_1800) + return; + + musb_free_temp_buffer(urb); +} +#endif /* !CONFIG_MUSB_PIO_ONLY */ + const struct hc_driver musb_hc_driver = { .description = "musb-hcd", .product_desc = "MUSB HDRC host driver", @@ -2484,6 +2596,11 @@ const struct hc_driver musb_hc_driver = { .urb_dequeue = musb_urb_dequeue, .endpoint_disable = musb_h_disable, +#ifndef CONFIG_MUSB_PIO_ONLY + .map_urb_for_dma = musb_map_urb_for_dma, + .unmap_urb_for_dma = musb_unmap_urb_for_dma, +#endif + .hub_status_data = musb_hub_status_data, .hub_control = musb_hub_control, .bus_suspend = musb_bus_suspend, -- cgit v0.10.2 From 96449f097e3874af0e8ddd721d3ebeed2ec389da Mon Sep 17 00:00:00 2001 From: Ravi Babu Date: Tue, 2 Apr 2013 13:21:54 +0530 Subject: usb: musb: dsps: print babble message only when musb is active host The musb controller uses single bit defintion for both reset and babble events. The babble event is valid only when controller is active a-host, and hence print the babble message only when the controller is active a-host. Signed-off-by: Ravi Babu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 1ea553d..3a18e44 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -335,7 +335,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. * Also, DRVVBUS pulses for SRP (but not at 5V) ... */ - if (usbintr & MUSB_INTR_BABBLE) + if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) pr_info("CAUTION: musb: Babble Interrupt Occurred\n"); if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) { -- cgit v0.10.2 From 538bf07d897008e87eca49cde5970545f3814ac7 Mon Sep 17 00:00:00 2001 From: Ravi Babu Date: Tue, 2 Apr 2013 13:22:42 +0530 Subject: usb: musb: gadget: read ep0 fifo only if rxcount is non zero avoid reading fifo rxcount is zero of fifo is empty, hence read fifo only if rxcount is non-zero Signed-off-by: Ravi Babu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index c9c1ac4..2af45a0 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -505,8 +505,10 @@ static void ep0_rxstate(struct musb *musb) req->status = -EOVERFLOW; count = len; } - musb_read_fifo(&musb->endpoints[0], count, buf); - req->actual += count; + if (count > 0) { + musb_read_fifo(&musb->endpoints[0], count, buf); + req->actual += count; + } csr = MUSB_CSR0_P_SVDRXPKTRDY; if (count < 64 || req->actual == req->length) { musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; -- cgit v0.10.2 From 19fda7cd59eac8e537f63a4d9bd5973bf78a8c38 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 26 Mar 2013 01:52:48 +0000 Subject: usb: dwc3: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SET_SYSTEM_SLEEP_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. Unnecessary CONFIG_PM ifdefs are removed. drivers/usb/dwc3/core.c:682:12: warning: 'dwc3_suspend' defined but not used [-Wunused-function] drivers/usb/dwc3/core.c:709:12: warning: 'dwc3_resume' defined but not used [-Wunused-function] drivers/usb/dwc3/dwc3-omap.c:430:12: warning: 'dwc3_omap_suspend' defined but not used [-Wunused-function] drivers/usb/dwc3/dwc3-omap.c:440:12: warning: 'dwc3_omap_resume' defined but not used [-Wunused-function] drivers/usb/dwc3/dwc3-exynos.c:185:12: warning: 'dwc3_exynos_suspend' defined but not used [-Wunused-function] drivers/usb/dwc3/dwc3-exynos.c:194:12: warning: 'dwc3_exynos_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e2325ad..c35d49d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -634,7 +634,7 @@ static int dwc3_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int dwc3_prepare(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 1ea7bd8..a8afe6e 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -181,7 +181,7 @@ static const struct of_device_id exynos_dwc3_match[] = { MODULE_DEVICE_TABLE(of, exynos_dwc3_match); #endif -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int dwc3_exynos_suspend(struct device *dev) { struct dwc3_exynos *exynos = dev_get_drvdata(dev); @@ -212,7 +212,7 @@ static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = { #define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops) #else #define DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 6de734f..34638b9 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -410,7 +410,7 @@ static const struct of_device_id of_dwc3_match[] = { }; MODULE_DEVICE_TABLE(of, of_dwc3_match); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int dwc3_omap_prepare(struct device *dev) { struct dwc3_omap *omap = dev_get_drvdata(dev); @@ -461,7 +461,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { #define DEV_PM_OPS (&dwc3_omap_dev_pm_ops) #else #define DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, -- cgit v0.10.2 From 9770a16fd9d9c5a91378d53732be1d2ec599bfcf Mon Sep 17 00:00:00 2001 From: Yuan-Hsin Chen Date: Tue, 2 Apr 2013 11:15:28 +0000 Subject: usb: gadget: fusb300_udc: add FUSB300_EPSET0_STL_CLR for clearing EP0 stall The final version of fusb300 controller adds EPSET0_STL_CLR for clearing EP0 stall and also removes EPSET0_EPn_TX0BYTE. fusb300_udc driver is tested on FARADAY platform a369 with FUSB300 FPGA v1.8 Signed-off-by: Yuan-Hsin Chen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index d053553..db0d291 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -394,7 +394,7 @@ static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep) if (reg & FUSB300_EPSET0_STL) { printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep); - reg &= ~FUSB300_EPSET0_STL; + reg |= FUSB300_EPSET0_STL_CLR; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); } } diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h index 6ba444a..ae811d8 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/fusb300_udc.h @@ -111,8 +111,8 @@ /* * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 ) * */ +#define FUSB300_EPSET0_STL_CLR (1 << 3) #define FUSB300_EPSET0_CLRSEQNUM (1 << 2) -#define FUSB300_EPSET0_EPn_TX0BYTE (1 << 1) #define FUSB300_EPSET0_STL (1 << 0) /* -- cgit v0.10.2 From 1cb5e9ee033f89d3241013cfff8300cc0195a62d Mon Sep 17 00:00:00 2001 From: Yuan-Hsin Chen Date: Tue, 2 Apr 2013 11:18:08 +0000 Subject: usb: gadget: fusb300_udc: bug fix of not doing idma reset for each time Enter IDMA_RESET only when the controller has been reset or the device has been plugged in to or out from a host. In IDMA_RESET, we should disable the corresponding PRD interrupt. Also there is a redundant space eliminated. fusb300_udc driver is tested on FARADAY platform a369 with FUSB300 FPGA v1.8 Signed-off-by: Yuan-Hsin Chen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index db0d291..cec8871 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -930,12 +930,15 @@ static void fusb300_wait_idma_finished(struct fusb300_ep *ep) fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0, FUSB300_IGR0_EPn_PRD_INT(ep->epnum)); + return; + IDMA_RESET: - fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGER0, - FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0); + reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum); + iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0); } -static void fusb300_set_idma(struct fusb300_ep *ep, +static void fusb300_set_idma(struct fusb300_ep *ep, struct fusb300_request *req) { int ret; -- cgit v0.10.2 From c5dbc220b327dc1ff5e0b1d829c091347511ed83 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Apr 2013 17:06:28 +0300 Subject: usb: gadget: udc-core: prevent a memory leak udc-core would leak 'udc' memory in some error cases. Fix it by kfree()ing udc on error path. Reported-by: Dan Carpenter Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 7999cc6..c6c2b56 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -240,6 +240,7 @@ err3: err2: put_device(&gadget->dev); + kfree(udc); err1: return ret; -- cgit v0.10.2 From 50757b24944ede7ab07165cdb57c995456b6e916 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Apr 2013 17:13:40 +0300 Subject: usb: gadget: pxa27x_udc: fix Section Mismatches Remove all section annotations to fix the following section mismatches: >> WARNING: drivers/usb/gadget/pxa27x_udc.o(.text+0x597c): Section mismatch in reference from the function .pxa_udc_probe() to the function .init.text:.udc_init_data.constprop.11() The function .pxa_udc_probe() references the function __init .udc_init_data.constprop.11(). This is often because .pxa_udc_probe lacks a __init annotation or the annotation of .udc_init_data.constprop.11 is wrong. -- >> WARNING: drivers/usb/gadget/pxa27x_udc.o(.text+0x5a04): Section mismatch in reference from the function .pxa_udc_probe() to the function .init.text:.pxa_ep_setup() The function .pxa_udc_probe() references the function __init .pxa_ep_setup(). This is often because .pxa_udc_probe lacks a __init annotation or the annotation of .pxa_ep_setup is wrong. Reported-by: Fengguang Wu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index ad954c4..6b4c7d9 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -609,7 +609,7 @@ static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) * * Find the physical pxa27x ep, and setup its UDCCR */ -static __init void pxa_ep_setup(struct pxa_ep *ep) +static void pxa_ep_setup(struct pxa_ep *ep) { u32 new_udccr; @@ -631,7 +631,7 @@ static __init void pxa_ep_setup(struct pxa_ep *ep) * * Setup all pxa physical endpoints, except ep0 */ -static __init void pxa_eps_setup(struct pxa_udc *dev) +static void pxa_eps_setup(struct pxa_udc *dev) { unsigned int i; @@ -1716,7 +1716,7 @@ static void udc_disable(struct pxa_udc *udc) * Initializes gadget endpoint list, endpoints locks. No action is taken * on the hardware. */ -static __init void udc_init_data(struct pxa_udc *dev) +static void udc_init_data(struct pxa_udc *dev) { int i; struct pxa_ep *ep; @@ -2490,7 +2490,7 @@ err_clk: * pxa_udc_remove - removes the udc device driver * @_dev: platform device */ -static int __exit pxa_udc_remove(struct platform_device *_dev) +static int pxa_udc_remove(struct platform_device *_dev) { struct pxa_udc *udc = platform_get_drvdata(_dev); int gpio = udc->mach->gpio_pullup; @@ -2609,7 +2609,7 @@ static struct platform_driver udc_driver = { .owner = THIS_MODULE, }, .probe = pxa_udc_probe, - .remove = __exit_p(pxa_udc_remove), + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, -- cgit v0.10.2 From f4362b1dd7262e3c81f295154b601065f8810186 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 26 Mar 2013 14:44:03 +0530 Subject: usb: phy: samsung: convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index 54f6418..133f3d0 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -244,11 +244,9 @@ static int samsung_usb3phy_probe(struct platform_device *pdev) return -ENODEV; } - phy_base = devm_request_and_ioremap(dev, phy_mem); - if (!phy_base) { - dev_err(dev, "%s: register mapping failed\n", __func__); - return -ENXIO; - } + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); if (!sphy) -- cgit v0.10.2 From 3ee1f2e6e51581d5cb9c312161a9a9b2f18f25bf Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:02 +0200 Subject: usb: musb: ux500_dma: drop references to U5500 Drop references to deprecated U5500 platform in driver comments. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index f417b7e..3381206 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -1,7 +1,7 @@ /* * drivers/usb/musb/ux500_dma.c * - * U8500 and U5500 DMA support code + * U8500 DMA support code * * Copyright (C) 2009 STMicroelectronics * Copyright (C) 2011 ST-Ericsson SA -- cgit v0.10.2 From 81ef6724302e0c7ba3f087403de24760601b1839 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:03 +0200 Subject: usb: phy: ab8500-usb: convert to devm_kzalloc Convert local data allocation to devm_kzalloc and drop unnecessary fail path code. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 351b036..ab6dd07 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -628,15 +628,13 @@ static int ab8500_usb_probe(struct platform_device *pdev) return -ENODEV; } - ab = kzalloc(sizeof *ab, GFP_KERNEL); + ab = devm_kzalloc(&pdev->dev, sizeof(*ab), GFP_KERNEL); if (!ab) return -ENOMEM; - otg = kzalloc(sizeof *otg, GFP_KERNEL); - if (!otg) { - kfree(ab); + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) return -ENOMEM; - } ab->dev = &pdev->dev; ab->ab8500 = ab8500; @@ -665,12 +663,12 @@ static int ab8500_usb_probe(struct platform_device *pdev) err = ab8500_usb_irq_setup(pdev, ab); if (err < 0) - goto fail; + return err; err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); - goto fail; + return err; } /* Needed to enable ID detection. */ @@ -679,10 +677,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); return 0; -fail: - kfree(otg); - kfree(ab); - return err; } static int ab8500_usb_remove(struct platform_device *pdev) @@ -700,9 +694,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - kfree(ab->phy.otg); - kfree(ab); - return 0; } -- cgit v0.10.2 From 7124631aa892712fc8b317ff34d25c14dee6f63d Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Wed, 3 Apr 2013 10:45:04 +0200 Subject: usb: phy: ab8500-usb: set phy tuning values Set phy tuning values proposed by the hardware teams for AB8500 and AB8505 to improve USB eye diagram performances. Signed-off-by: Sakethram Bommisetti Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index ab6dd07..5b92a59 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -33,11 +33,22 @@ #include #include +/* Bank AB8500_SYS_CTRL2_BLOCK */ #define AB8500_MAIN_WD_CTRL_REG 0x01 + +/* Bank AB8500_USB */ #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8505_USB_LINE_STAT_REG 0x94 #define AB8500_USB_PHY_CTRL_REG 0x8A +/* Bank AB8500_DEVELOPMENT */ +#define AB8500_BANK12_ACCESS 0x00 + +/* Bank AB8500_DEBUG */ +#define AB8500_USB_PHY_TUNE1 0x05 +#define AB8500_USB_PHY_TUNE2 0x06 +#define AB8500_USB_PHY_TUNE3 0x07 + #define AB8500_BIT_OTG_STAT_ID (1 << 0) #define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) #define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) @@ -671,6 +682,82 @@ static int ab8500_usb_probe(struct platform_device *pdev) return err; } + /* Phy tuning values for AB8500 */ + if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + /* Enable the PBT/Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); + if (err < 0) + dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + err); + + /* Switch to normal mode/disable Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", + err); + } + + /* Phy tuning values for AB8505 */ + if (is_ab8505(ab->ab8500)) { + /* Enable the PBT/Bank 0x12 access */ + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, + 0x01, 0x01); + if (err < 0) + dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE1, + 0xC8, 0xC8); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE2, + 0x60, 0x60); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE3, + 0xFC, 0x80); + + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + err); + + /* Switch to normal mode/disable Bank 0x12 access */ + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, + 0x00, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", + err); + } + /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); -- cgit v0.10.2 From c2a0ab6bd5ccf031f87bc678152fb70befea5786 Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Wed, 3 Apr 2013 10:45:05 +0200 Subject: usb: phy: ab8500-usb: fix eye diagram for ab8500 v2.0 AB8500 v2.0 has eye diagram issues when drawing more than 100mA from VBUS. Force charging current to 100mA in case of standard host. Signed-off-by: Sakethram Bommisetti Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 5b92a59..441b295 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -485,6 +485,19 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work) ab8500_usb_peri_phy_dis(ab); } +static unsigned ab8500_eyediagram_workaroud(struct ab8500_usb *ab, unsigned mA) +{ + /* + * AB8500 V2 has eye diagram issues when drawing more than 100mA from + * VBUS. Set charging current to 100mA in case of standard host + */ + if (is_ab8500_2p0_or_earlier(ab->ab8500)) + if (mA > 100) + mA = 100; + + return mA; +} + static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) { struct ab8500_usb *ab; @@ -494,6 +507,8 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) ab = phy_to_ab(phy); + mA = ab8500_eyediagram_workaroud(ab, mA); + ab->vbus_draw = mA; if (mA) -- cgit v0.10.2 From e65b36c02613764aa703ef0be0a3c2c57ea91625 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:06 +0200 Subject: usb: phy: ab8500-usb: add regulator support Add initial regulator support to ab8500-usb by introducing necessary devm_regulator_get(). Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 441b295..1bc24d4 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Bank AB8500_SYS_CTRL2_BLOCK */ #define AB8500_MAIN_WD_CTRL_REG 0x01 @@ -126,6 +127,9 @@ struct ab8500_usb { struct work_struct phy_dis_work; unsigned long link_status_wait; enum ab8500_usb_mode mode; + struct regulator *v_ape; + struct regulator *v_musb; + struct regulator *v_ulpi; int previous_link_status_state; }; @@ -590,6 +594,34 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } +static int ab8500_usb_regulator_get(struct ab8500_usb *ab) +{ + int err; + + ab->v_ape = devm_regulator_get(ab->dev, "v-ape"); + if (IS_ERR(ab->v_ape)) { + dev_err(ab->dev, "Could not get v-ape supply\n"); + err = PTR_ERR(ab->v_ape); + return err; + } + + ab->v_ulpi = devm_regulator_get(ab->dev, "vddulpivio18"); + if (IS_ERR(ab->v_ulpi)) { + dev_err(ab->dev, "Could not get vddulpivio18 supply\n"); + err = PTR_ERR(ab->v_ulpi); + return err; + } + + ab->v_musb = devm_regulator_get(ab->dev, "musb_1v8"); + if (IS_ERR(ab->v_musb)) { + dev_err(ab->dev, "Could not get musb_1v8 supply\n"); + err = PTR_ERR(ab->v_musb); + return err; + } + + return 0; +} + static int ab8500_usb_irq_setup(struct platform_device *pdev, struct ab8500_usb *ab) { @@ -687,6 +719,10 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); + err = ab8500_usb_regulator_get(ab); + if (err) + return err; + err = ab8500_usb_irq_setup(pdev, ab); if (err < 0) return err; -- cgit v0.10.2 From c0ea70646ad66a83f09562621babae4700c2f322 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:07 +0200 Subject: usb: phy: ab8500-usb: split ab8500_usb_phy_ctrl Split ab8500_usb_phy_ctrl into separate enable/disable functions to make the code more linear and readable. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 1bc24d4..58b194b 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -172,40 +172,37 @@ static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit) } } -static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, - bool enable) +static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) { - u8 ctrl_reg; - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - &ctrl_reg); - if (sel_host) { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN; - } else { - if (enable) - ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN; - else - ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN; - } + u8 bit; + bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : + AB8500_BIT_PHY_CTRL_DEVICE_EN; - abx500_set_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_PHY_CTRL_REG, - ctrl_reg); + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + bit, bit); +} - /* Needed to enable the phy.*/ - if (enable) - ab8500_usb_wd_workaround(ab); +static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) +{ + u8 bit; + bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : + AB8500_BIT_PHY_CTRL_DEVICE_EN; + + ab8500_usb_wd_linkstatus(ab, bit); + + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + bit, 0); + + /* Needed to disable the phy.*/ + ab8500_usb_wd_workaround(ab); } -#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true) -#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false) -#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) -#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) +#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_enable(ab, true) +#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_disable(ab, true) +#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false) +#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false) static int ab8505_usb_link_status_update(struct ab8500_usb *ab, enum ab8505_usb_link_status lsts) -- cgit v0.10.2 From 54dfbb08051c2fa666bd4ce6cb7626b9e2a80655 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:08 +0200 Subject: usb: phy: ab8500-usb: enable/disable regulator on phy events Add ab8500_usb_regulator_{enable,disable} functions to control USB phy regulators on corresponding ab8500_usb_phy_{enable,disable} events. This contains some workaround and optimization for specific AB8500 versions. Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Sakethram Bommisetti Signed-off-by: Praveena Nadahally Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 58b194b..96b4854 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -130,6 +130,7 @@ struct ab8500_usb { struct regulator *v_ape; struct regulator *v_musb; struct regulator *v_ulpi; + int saved_v_ulpi; int previous_link_status_state; }; @@ -161,6 +162,67 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) 0); } +static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) +{ + int ret, volt; + + regulator_enable(ab->v_ape); + + if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + ab->saved_v_ulpi = regulator_get_voltage(ab->v_ulpi); + if (ab->saved_v_ulpi < 0) + dev_err(ab->dev, "Failed to get v_ulpi voltage\n"); + + ret = regulator_set_voltage(ab->v_ulpi, 1300000, 1350000); + if (ret < 0) + dev_err(ab->dev, "Failed to set the Vintcore to 1.3V, ret=%d\n", + ret); + + ret = regulator_set_optimum_mode(ab->v_ulpi, 28000); + if (ret < 0) + dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n", + ret); + } + + regulator_enable(ab->v_ulpi); + + if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + volt = regulator_get_voltage(ab->v_ulpi); + if ((volt != 1300000) && (volt != 1350000)) + dev_err(ab->dev, "Vintcore is not set to 1.3V volt=%d\n", + volt); + } + + regulator_enable(ab->v_musb); +} + +static void ab8500_usb_regulator_disable(struct ab8500_usb *ab) +{ + int ret; + + regulator_disable(ab->v_musb); + + regulator_disable(ab->v_ulpi); + + /* USB is not the only consumer of Vintcore, restore old settings */ + if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + if (ab->saved_v_ulpi > 0) { + ret = regulator_set_voltage(ab->v_ulpi, + ab->saved_v_ulpi, ab->saved_v_ulpi); + if (ret < 0) + dev_err(ab->dev, "Failed to set the Vintcore to %duV, ret=%d\n", + ab->saved_v_ulpi, ret); + } + + ret = regulator_set_optimum_mode(ab->v_ulpi, 0); + if (ret < 0) + dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n", + ret); + } + + regulator_disable(ab->v_ape); +} + static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit) { /* Workaround for v2.0 bug # 31952 */ @@ -178,6 +240,8 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : AB8500_BIT_PHY_CTRL_DEVICE_EN; + ab8500_usb_regulator_enable(ab); + abx500_mask_and_set_register_interruptible(ab->dev, AB8500_USB, AB8500_USB_PHY_CTRL_REG, bit, bit); @@ -197,6 +261,8 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) /* Needed to disable the phy.*/ ab8500_usb_wd_workaround(ab); + + ab8500_usb_regulator_disable(ab); } #define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_enable(ab, true) @@ -544,7 +610,6 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg, */ if (!gadget) { - /* TODO: Disable regulators. */ otg->gadget = NULL; schedule_work(&ab->phy_dis_work); } else { @@ -576,7 +641,6 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) */ if (!host) { - /* TODO: Disable regulators. */ otg->host = NULL; schedule_work(&ab->phy_dis_work); } else { -- cgit v0.10.2 From f5ef7b42823945d979ebd957e79bf66dc6a5e3d1 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 3 Apr 2013 10:45:09 +0200 Subject: usb: phy: ab8500-usb: fix unbalanced clock and regulator disable warnings To prevent clock and regulator frameworks from complaining, only disable the host or peripheral phy if they were enabled. Reported-by: Sakethram Bommisetti Signed-off-by: Mian Yousaf Kaukab Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 96b4854..fec260e 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -888,8 +888,10 @@ static int ab8500_usb_remove(struct platform_device *pdev) usb_remove_phy(&ab->phy); - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); + if (ab->mode == USB_HOST) + ab8500_usb_host_phy_dis(ab); + else if (ab->mode == USB_PERIPHERAL) + ab8500_usb_peri_phy_dis(ab); platform_set_drvdata(pdev, NULL); -- cgit v0.10.2 From 77f4396ecb3396e518da60f915bbd4726732fd70 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 10:45:10 +0200 Subject: usb: phy: ab8500-usb: fix last notifier arguments Fix last ab->phy.notifier call to use vbus_draw as notifier argument, as that's used in ab8500_charger to control charging current. Also drop a related TODO comment, and the additional ux500_musb_set_vbus(musb, 0), as with this patch it was causing an erratic behaviour of gadget ep0 state machine. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 88795f5..2c80004 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -114,7 +114,6 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case UX500_MUSB_VBUS: dev_dbg(musb->controller, "VBUS Connect\n"); - ux500_musb_set_vbus(musb, 0); break; case UX500_MUSB_NONE: dev_dbg(musb->controller, "VBUS Disconnect\n"); diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index fec260e..888dad6 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -578,16 +578,12 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) ab->vbus_draw = mA; - if (mA) - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_ENUMERATED, ab->phy.otg->gadget); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_VBUS, &ab->vbus_draw); + return 0; } -/* TODO: Implement some way for charging or other drivers to read - * ab->vbus_draw. - */ - static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) { /* TODO */ -- cgit v0.10.2 From 899f0f561b51506f40eb78c9c4aaeeabf97cf35c Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 3 Apr 2013 10:45:11 +0200 Subject: usb: phy: ab8500-usb: adopt pinctrl support Amend the ab8500-usb driver to optionally take a pin control handle and set the state of the pins to "default" on ab8500_usb_phy_enable and to "sleep" on ab8500_usb_phy_disable. The pinctrl handle is released on ab8500_usb_phy_disable because USB pins are shared with ab8505_micro_usb_iddet driver. Signed-off-by: Patrice Chotard Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 888dad6..90a3278 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -33,6 +33,7 @@ #include #include #include +#include /* Bank AB8500_SYS_CTRL2_BLOCK */ #define AB8500_MAIN_WD_CTRL_REG 0x01 @@ -132,6 +133,8 @@ struct ab8500_usb { struct regulator *v_ulpi; int saved_v_ulpi; int previous_link_status_state; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_sleep; }; static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) @@ -240,6 +243,11 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : AB8500_BIT_PHY_CTRL_DEVICE_EN; + /* mux and configure USB pins to DEFAULT state */ + ab->pinctrl = pinctrl_get_select(ab->dev, PINCTRL_STATE_DEFAULT); + if (IS_ERR(ab->pinctrl)) + dev_err(ab->dev, "could not get/set default pinstate\n"); + ab8500_usb_regulator_enable(ab); abx500_mask_and_set_register_interruptible(ab->dev, @@ -263,6 +271,22 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) ab8500_usb_wd_workaround(ab); ab8500_usb_regulator_disable(ab); + + if (!IS_ERR(ab->pinctrl)) { + /* configure USB pins to SLEEP state */ + ab->pins_sleep = pinctrl_lookup_state(ab->pinctrl, + PINCTRL_STATE_SLEEP); + + if (IS_ERR(ab->pins_sleep)) + dev_dbg(ab->dev, "could not get sleep pinstate\n"); + else if (pinctrl_select_state(ab->pinctrl, ab->pins_sleep)) + dev_err(ab->dev, "could not set pins to sleep state\n"); + + /* as USB pins are shared with idddet, release them to allow + * iddet to request them + */ + pinctrl_put(ab->pinctrl); + } } #define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_enable(ab, true) -- cgit v0.10.2 From 8db12231bccc5ebf414b267af68c5a8c1e4432dd Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Wed, 3 Apr 2013 10:45:12 +0200 Subject: usb: phy: ab8500-usb: drop link status delayed work ab8500_usb_delayed_work was implemented as a workaroud for the internal only and now unsupported v1.0 version of AB850. This patch removes the delayed work and just leave a link status update call at probe time. Signed-off-by: Sakethram Bommisetti Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 90a3278..a1c103f 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -124,9 +124,7 @@ struct ab8500_usb { struct device *dev; struct ab8500 *ab8500; unsigned vbus_draw; - struct delayed_work dwork; struct work_struct phy_dis_work; - unsigned long link_status_wait; enum ab8500_usb_mode mode; struct regulator *v_ape; struct regulator *v_musb; @@ -556,14 +554,6 @@ static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data) return IRQ_HANDLED; } -static void ab8500_usb_delayed_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - dwork.work); - - abx500_usb_link_status_update(ab); -} - static void ab8500_usb_phy_disable_work(struct work_struct *work) { struct ab8500_usb *ab = container_of(work, struct ab8500_usb, @@ -635,12 +625,6 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg, } else { otg->gadget = gadget; otg->phy->state = OTG_STATE_B_IDLE; - - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); } return 0; @@ -665,11 +649,6 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) schedule_work(&ab->phy_dis_work); } else { otg->host = host; - /* Phy will not be enabled if cable is already - * plugged-in. Schedule to enable phy. - * Use same delay to avoid any race condition. - */ - schedule_delayed_work(&ab->dwork, ab->link_status_wait); } return 0; @@ -792,11 +771,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); - /* v1: Wait for link status to become stable. - * all: Updates form set_host and set_peripheral as they are atomic. - */ - INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work); - /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); @@ -893,6 +867,8 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); + abx500_usb_link_status_update(ab); + dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); return 0; @@ -902,8 +878,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) { struct ab8500_usb *ab = platform_get_drvdata(pdev); - cancel_delayed_work_sync(&ab->dwork); - cancel_work_sync(&ab->phy_dis_work); usb_remove_phy(&ab->phy); -- cgit v0.10.2 From 588233733804aeaf16335a32904aaa4d15b9bddd Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Wed, 3 Apr 2013 10:45:13 +0200 Subject: usb: phy: ab8500-usb: call phy_dis_work only when necessary Modify ab8500_usb_set_peripheral() and ab8500_usb_set_host() code to schedule phy_dis_work only when necessary in order to prevent regulator count mismatch during reboot/shutdown. Signed-off-by: Sakethram Bommisetti Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index a1c103f..749614c 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -614,17 +614,16 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg, ab = phy_to_ab(otg->phy); + ab->phy.otg->gadget = gadget; + /* Some drivers call this function in atomic context. * Do not update ab8500 registers directly till this * is fixed. */ - if (!gadget) { - otg->gadget = NULL; + if ((ab->mode != USB_IDLE) && (!gadget)) { + ab->mode = USB_IDLE; schedule_work(&ab->phy_dis_work); - } else { - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; } return 0; @@ -639,16 +638,16 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) ab = phy_to_ab(otg->phy); + ab->phy.otg->host = host; + /* Some drivers call this function in atomic context. * Do not update ab8500 registers directly till this * is fixed. */ - if (!host) { - otg->host = NULL; + if ((ab->mode != USB_IDLE) && (!host)) { + ab->mode = USB_IDLE; schedule_work(&ab->phy_dis_work); - } else { - otg->host = host; } return 0; -- cgit v0.10.2 From 15761826eecda192f4d1527e08c32193a03e94b7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 25 Jan 2013 14:09:17 +0100 Subject: usb: gadget: nokia: use function framework for ACM This patch converts the acm_ms gadget to make use of the function framework to request the ACM function. The "old" include interface for acm is now removed since nokia was the last user of it (for ACM). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1deadde..e1d3e08 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -831,6 +831,7 @@ config USB_G_NOKIA depends on PHONET select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_F_ACM help The Nokia composite gadget provides support for acm, obex and phonet in only one composite gadget driver. diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 1ae180b..61b33d2 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -715,72 +715,6 @@ fail: return status; } -static struct f_acm *acm_alloc_basic_func(void) -{ - struct f_acm *acm; - - acm = kzalloc(sizeof(*acm), GFP_KERNEL); - if (!acm) - return NULL; - - spin_lock_init(&acm->lock); - - acm->port.connect = acm_connect; - acm->port.disconnect = acm_disconnect; - acm->port.send_break = acm_send_break; - - acm->port.func.name = "acm"; - /* descriptors are per-instance copies */ - acm->port.func.bind = acm_bind; - acm->port.func.set_alt = acm_set_alt; - acm->port.func.setup = acm_setup; - acm->port.func.disable = acm_disable; - - return acm; -} - -#ifdef USB_FACM_INCLUDED -static void -acm_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_acm *acm = func_to_acm(f); - - usb_free_all_descriptors(f); - if (acm->notify_req) - gs_free_req(acm->notify, acm->notify_req); - kfree(acm); -} - -/** - * acm_bind_config - add a CDC ACM function to a configuration - * @c: the configuration to support the CDC ACM instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - */ -int acm_bind_config(struct usb_configuration *c, u8 port_num) -{ - struct f_acm *acm; - int status; - - /* allocate and initialize one new instance */ - acm = acm_alloc_basic_func(); - if (!acm) - return -ENOMEM; - - acm->port_num = port_num; - acm->port.func.unbind = acm_old_unbind; - - status = usb_add_function(c, &acm->port.func); - if (status) - kfree(acm); - return status; -} - -#else - static void acm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_acm *acm = func_to_acm(f); @@ -803,10 +737,24 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) struct f_serial_opts *opts; struct f_acm *acm; - acm = acm_alloc_basic_func(); + acm = kzalloc(sizeof(*acm), GFP_KERNEL); if (!acm) return ERR_PTR(-ENOMEM); + spin_lock_init(&acm->lock); + + acm->port.connect = acm_connect; + acm->port.disconnect = acm_disconnect; + acm->port.send_break = acm_send_break; + + acm->port.func.name = "acm"; + acm->port.func.strings = acm_strings; + /* descriptors are per-instance copies */ + acm->port.func.bind = acm_bind; + acm->port.func.set_alt = acm_set_alt; + acm->port.func.setup = acm_setup; + acm->port.func.disable = acm_disable; + opts = container_of(fi, struct f_serial_opts, func_inst); acm->port_num = opts->port_num; acm->port.func.unbind = acm_unbind; @@ -835,4 +783,3 @@ static struct usb_function_instance *acm_alloc_instance(void) } DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); MODULE_LICENSE("GPL"); -#endif diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index def3740..c3ad777 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,8 +37,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#define USB_FACM_INCLUDED -#include "f_acm.c" #include "f_ecm.c" #include "f_obex.c" #include "f_serial.c" @@ -98,7 +96,8 @@ MODULE_AUTHOR("Felipe Balbi"); MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ - +static struct usb_function *f_acm_cfg1; +static struct usb_function *f_acm_cfg2; static u8 hostaddr[ETH_ALEN]; enum { @@ -110,8 +109,27 @@ enum { static unsigned char tty_lines[TTY_PORTS_MAX]; +static struct usb_configuration nokia_config_500ma_driver = { + .label = "Bus Powered", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .MaxPower = 500, +}; + +static struct usb_configuration nokia_config_100ma_driver = { + .label = "Self Powered", + .bConfigurationValue = 2, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .MaxPower = 100, +}; + +static struct usb_function_instance *fi_acm; + static int __init nokia_bind_config(struct usb_configuration *c) { + struct usb_function *f_acm; int status = 0; status = phonet_bind_config(c); @@ -126,36 +144,36 @@ static int __init nokia_bind_config(struct usb_configuration *c) if (status) printk(KERN_DEBUG "could not bind obex config %d\n", 0); - status = acm_bind_config(c, tty_lines[TTY_PORT_ACM]); + f_acm = usb_get_function(fi_acm); + if (IS_ERR(f_acm)) + return PTR_ERR(f_acm); + + status = usb_add_function(c, f_acm); if (status) - printk(KERN_DEBUG "could not bind acm config\n"); + goto err_conf; status = ecm_bind_config(c, hostaddr); - if (status) - printk(KERN_DEBUG "could not bind ecm config\n"); + if (status) { + pr_debug("could not bind ecm config %d\n", status); + goto err_ecm; + } + if (c == &nokia_config_500ma_driver) + f_acm_cfg1 = f_acm; + else + f_acm_cfg2 = f_acm; return status; +err_ecm: + usb_remove_function(c, f_acm); +err_conf: + usb_put_function(f_acm); + return status; } -static struct usb_configuration nokia_config_500ma_driver = { - .label = "Bus Powered", - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_ONE, - .MaxPower = 500, -}; - -static struct usb_configuration nokia_config_100ma_driver = { - .label = "Self Powered", - .bConfigurationValue = 2, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .MaxPower = 100, -}; - static int __init nokia_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct f_serial_opts *opts; int status; int cur_line; @@ -185,22 +203,32 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) if (!gadget_supports_altsettings(gadget)) goto err_usb; + fi_acm = usb_get_function_instance("acm"); + if (IS_ERR(fi_acm)) + goto err_usb; + opts = container_of(fi_acm, struct f_serial_opts, func_inst); + opts->port_num = tty_lines[TTY_PORT_ACM]; + /* finally register the configuration */ status = usb_add_config(cdev, &nokia_config_500ma_driver, nokia_bind_config); if (status < 0) - goto err_usb; + goto err_acm_inst; status = usb_add_config(cdev, &nokia_config_100ma_driver, nokia_bind_config); if (status < 0) - goto err_usb; + goto err_put_cfg1; usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME); return 0; +err_put_cfg1: + usb_put_function(f_acm_cfg1); +err_acm_inst: + usb_put_function_instance(fi_acm); err_usb: gether_cleanup(); err_ether: @@ -217,6 +245,9 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) { int i; + usb_put_function(f_acm_cfg1); + usb_put_function(f_acm_cfg2); + usb_put_function_instance(fi_acm); gphonet_cleanup(); for (i = 0; i < TTY_PORTS_MAX; i++) @@ -247,4 +278,3 @@ static void __exit nokia_cleanup(void) usb_composite_unregister(&nokia_driver); } module_exit(nokia_cleanup); - diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index 66ce73a..c20210c 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -65,7 +65,6 @@ int gserial_connect(struct gserial *, u8 port_num); void gserial_disconnect(struct gserial *); /* functions are bound to configurations by a config or gadget driver */ -int acm_bind_config(struct usb_configuration *c, u8 port_num); int gser_bind_config(struct usb_configuration *c, u8 port_num); int obex_bind_config(struct usb_configuration *c, u8 port_num); -- cgit v0.10.2 From d6a0143985489e470a118605352f4b18df0ce142 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:12 +0100 Subject: usb: gadget: move the global the_dev variable to their users the u_ether.c file has a global variable named the_dev which keeps a pointer to the network device after it has been created via gether_setup_name(). It is only used internally by u_ether. This patches moves the variable to its users and passes it via the port.ioport where it is saved later anyway. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index a7d6f70..61023aa 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -103,7 +103,7 @@ static struct usb_gadget_strings *dev_strings[] = { }; static u8 hostaddr[ETH_ALEN]; - +static struct eth_dev *the_dev; /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm; static struct usb_function_instance *fi_serial; @@ -122,7 +122,7 @@ static int __init cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - status = ecm_bind_config(c, hostaddr); + status = ecm_bind_config(c, hostaddr, the_dev); if (status < 0) return status; @@ -169,9 +169,9 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) } /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + the_dev = gether_setup(cdev->gadget, hostaddr); + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); /* set up serial link layer */ status = gserial_alloc_line(&tty_line); @@ -202,7 +202,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) fail1: gserial_free_line(tty_line); fail0: - gether_cleanup(); + gether_cleanup(the_dev); return status; } @@ -211,7 +211,7 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev) usb_put_function(f_acm); usb_put_function_instance(fi_serial); gserial_free_line(tty_line); - gether_cleanup(); + gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 18c3f42..56c8eca 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -207,7 +207,7 @@ static struct usb_gadget_strings *dev_strings[] = { }; static u8 hostaddr[ETH_ALEN]; - +static struct eth_dev *the_dev; /*-------------------------------------------------------------------------*/ /* @@ -224,7 +224,7 @@ static int __init rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return rndis_bind_config(c, hostaddr); + return rndis_bind_config(c, hostaddr, the_dev); } static struct usb_configuration rndis_config_driver = { @@ -257,11 +257,11 @@ static int __init eth_do_config(struct usb_configuration *c) } if (use_eem) - return eem_bind_config(c); + return eem_bind_config(c, the_dev); else if (can_support_ecm(c->cdev->gadget)) - return ecm_bind_config(c, hostaddr); + return ecm_bind_config(c, hostaddr, the_dev); else - return geth_bind_config(c, hostaddr); + return geth_bind_config(c, hostaddr, the_dev); } static struct usb_configuration eth_config_driver = { @@ -279,9 +279,9 @@ static int __init eth_bind(struct usb_composite_dev *cdev) int status; /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + the_dev = gether_setup(cdev->gadget, hostaddr); + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); /* set up main config label and device descriptor */ if (use_eem) { @@ -338,13 +338,13 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; fail: - gether_cleanup(); + gether_cleanup(the_dev); return status; } static int __exit eth_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(); + gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 83420a3..d893d69 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -824,7 +824,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f) * for calling @gether_cleanup() before module unload. */ int -ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev) { struct f_ecm *ecm; int status; @@ -852,6 +853,7 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr); ecm_string_defs[1].s = ecm->ethaddr; + ecm->port.ioport = dev; ecm->port.cdc_filter = DEFAULT_FILTER; ecm->port.func.name = "cdc_ethernet"; diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index cf0ebee8..f4e0bbe 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -528,7 +528,7 @@ error: * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int __init eem_bind_config(struct usb_configuration *c) +int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev) { struct f_eem *eem; int status; @@ -549,6 +549,7 @@ int __init eem_bind_config(struct usb_configuration *c) if (!eem) return -ENOMEM; + eem->port.ioport = dev; eem->port.cdc_filter = DEFAULT_FILTER; eem->port.func.name = "cdc_eem"; diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 5e7557e..ee19bc8 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1287,7 +1287,8 @@ ncm_unbind(struct usb_configuration *c, struct usb_function *f) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev) { struct f_ncm *ncm; int status; @@ -1321,6 +1322,7 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) spin_lock_init(&ncm->lock); ncm_reset_values(ncm); + ncm->port.ioport = dev; ncm->port.is_fixed = true; ncm->port.func.name = "cdc_network"; diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 71beeb8..970c905 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -814,7 +814,7 @@ static inline bool can_support_rndis(struct usb_configuration *c) int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer) + u32 vendorID, const char *manufacturer, struct eth_dev *dev) { struct f_rndis *rndis; int status; @@ -847,6 +847,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis->vendorID = vendorID; rndis->manufacturer = manufacturer; + rndis->port.ioport = dev; /* RNDIS activates when the host changes this filter */ rndis->port.cdc_filter = 0; diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index f172bd1..185d6f5 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -380,7 +380,8 @@ geth_unbind(struct usb_configuration *c, struct usb_function *f) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev) { struct f_gether *geth; int status; @@ -406,6 +407,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr); geth_string_defs[1].s = geth->ethaddr; + geth->port.ioport = dev; geth->port.cdc_filter = DEFAULT_FILTER; geth->port.func.name = "cdc_subset"; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 3953dd4..a07dd17 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -13,7 +13,6 @@ #define pr_fmt(fmt) "g_ffs: " fmt #include - /* * kbuild is not very cooperative with respect to linking separately * compiled library objects into one module. So for now we won't use @@ -38,13 +37,16 @@ # include "u_ether.c" static u8 gfs_hostaddr[ETH_ALEN]; +static struct eth_dev *the_dev; # ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev); # endif #else -# define gether_cleanup() do { } while (0) -# define gether_setup(gadget, hostaddr) ((int)0) +# define the_dev NULL +# define gether_cleanup(dev) do { } while (0) # define gfs_hostaddr NULL +struct eth_dev; #endif #include "f_fs.c" @@ -137,7 +139,8 @@ static struct usb_gadget_strings *gfs_dev_strings[] = { struct gfs_configuration { struct usb_configuration c; - int (*eth)(struct usb_configuration *c, u8 *ethaddr); + int (*eth)(struct usb_configuration *c, u8 *ethaddr, + struct eth_dev *dev); } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { @@ -346,10 +349,13 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (missing_funcs) return -ENODEV; - - ret = gether_setup(cdev->gadget, gfs_hostaddr); - if (unlikely(ret < 0)) +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + the_dev = gether_setup(cdev->gadget, gfs_hostaddr); +#endif + if (IS_ERR(the_dev)) { + ret = PTR_ERR(the_dev); goto error_quick; + } gfs_ether_setup = true; ret = usb_string_ids_tab(cdev, gfs_strings); @@ -386,7 +392,7 @@ error_unbind: for (i = 0; i < func_num; i++) functionfs_unbind(ffs_tab[i].ffs_data); error: - gether_cleanup(); + gether_cleanup(the_dev); error_quick: gfs_ether_setup = false; return ret; @@ -410,7 +416,7 @@ static int gfs_unbind(struct usb_composite_dev *cdev) * do...? */ if (gfs_ether_setup) - gether_cleanup(); + gether_cleanup(the_dev); gfs_ether_setup = false; for (i = func_num; --i; ) @@ -440,7 +446,7 @@ static int gfs_do_config(struct usb_configuration *c) } if (gc->eth) { - ret = gc->eth(c, gfs_hostaddr); + ret = gc->eth(c, gfs_hostaddr, the_dev); if (unlikely(ret < 0)) return ret; } @@ -469,11 +475,12 @@ static int gfs_do_config(struct usb_configuration *c) #ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev) { return can_support_ecm(c->cdev->gadget) - ? ecm_bind_config(c, ethaddr) - : geth_bind_config(c, ethaddr); + ? ecm_bind_config(c, ethaddr, dev) + : geth_bind_config(c, ethaddr, dev); } #endif diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 20bbbf9..3442755 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -137,6 +137,7 @@ static u8 hostaddr[ETH_ALEN]; static unsigned char tty_line; static struct usb_function_instance *fi_acm; +static struct eth_dev *the_dev; /********** RNDIS **********/ @@ -152,7 +153,7 @@ static __init int rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = rndis_bind_config(c, hostaddr); + ret = rndis_bind_config(c, hostaddr, the_dev); if (ret < 0) return ret; @@ -214,7 +215,7 @@ static __init int cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = ecm_bind_config(c, hostaddr); + ret = ecm_bind_config(c, hostaddr, the_dev); if (ret < 0) return ret; @@ -279,9 +280,9 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) } /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + the_dev = gether_setup(cdev->gadget, hostaddr); + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); /* set up serial link layer */ status = gserial_alloc_line(&tty_line); @@ -337,7 +338,7 @@ fail1: fail0dot5: gserial_free_line(tty_line); fail0: - gether_cleanup(); + gether_cleanup(the_dev); return status; } @@ -351,7 +352,7 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev) #endif usb_put_function_instance(fi_acm); gserial_free_line(tty_line); - gether_cleanup(); + gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index a22ad9a..3b02fd4 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -111,6 +111,7 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +struct eth_dev *the_dev; static u8 hostaddr[ETH_ALEN]; /*-------------------------------------------------------------------------*/ @@ -124,7 +125,7 @@ static int __init ncm_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return ncm_bind_config(c, hostaddr); + return ncm_bind_config(c, hostaddr, the_dev); } static struct usb_configuration ncm_config_driver = { @@ -143,9 +144,9 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) int status; /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + the_dev = gether_setup(cdev->gadget, hostaddr); + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. @@ -168,13 +169,13 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) return 0; fail: - gether_cleanup(); + gether_cleanup(the_dev); return status; } static int __exit gncm_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(); + gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index c3ad777..0c13ddd 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -99,6 +99,7 @@ MODULE_LICENSE("GPL"); static struct usb_function *f_acm_cfg1; static struct usb_function *f_acm_cfg2; static u8 hostaddr[ETH_ALEN]; +static struct eth_dev *the_dev; enum { TTY_PORT_OBEX0, @@ -152,7 +153,7 @@ static int __init nokia_bind_config(struct usb_configuration *c) if (status) goto err_conf; - status = ecm_bind_config(c, hostaddr); + status = ecm_bind_config(c, hostaddr, the_dev); if (status) { pr_debug("could not bind ecm config %d\n", status); goto err_ecm; @@ -187,9 +188,11 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) goto err_ether; } - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) + the_dev = gether_setup(cdev->gadget, hostaddr); + if (IS_ERR(the_dev)) { + status = PTR_ERR(the_dev); goto err_ether; + } status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) @@ -230,7 +233,7 @@ err_put_cfg1: err_acm_inst: usb_put_function_instance(fi_acm); err_usb: - gether_cleanup(); + gether_cleanup(the_dev); err_ether: cur_line--; while (cur_line >= 0) @@ -253,7 +256,7 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) for (i = 0; i < TTY_PORTS_MAX; i++) gserial_free_line(tty_lines[i]); - gether_cleanup(); + gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index a0aa721..4b76124 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -50,7 +50,6 @@ struct eth_dev { /* lock is held while accessing port_usb - * or updating its backlink port_usb->ioport */ spinlock_t lock; struct gether *port_usb; @@ -729,8 +728,6 @@ static int get_ether_addr(const char *str, u8 *dev_addr) return 1; } -static struct eth_dev *the_dev; - static const struct net_device_ops eth_netdev_ops = { .ndo_open = eth_open, .ndo_stop = eth_stop, @@ -758,19 +755,16 @@ static struct device_type gadget_type = { * * Returns negative errno, or zero on success */ -int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], +struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], const char *netname) { struct eth_dev *dev; struct net_device *net; int status; - if (the_dev) - return -EBUSY; - net = alloc_etherdev(sizeof *dev); if (!net) - return -ENOMEM; + return ERR_PTR(-ENOMEM); dev = netdev_priv(net); spin_lock_init(&dev->lock); @@ -807,12 +801,11 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], if (status < 0) { dev_dbg(&g->dev, "register_netdev failed, %d\n", status); free_netdev(net); + dev = ERR_PTR(status); } else { INFO(dev, "MAC %pM\n", net->dev_addr); INFO(dev, "HOST MAC %pM\n", dev->host_mac); - the_dev = dev; - /* two kinds of host-initiated state changes: * - iff DATA transfer is active, carrier is "on" * - tx queueing enabled if open *and* carrier is "on" @@ -820,7 +813,7 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], netif_carrier_off(net); } - return status; + return dev; } /** @@ -829,19 +822,16 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], * * This is called to free all resources allocated by @gether_setup(). */ -void gether_cleanup(void) +void gether_cleanup(struct eth_dev *dev) { - if (!the_dev) + if (!dev) return; - unregister_netdev(the_dev->net); - flush_work(&the_dev->work); - free_netdev(the_dev->net); - - the_dev = NULL; + unregister_netdev(dev->net); + flush_work(&dev->work); + free_netdev(dev->net); } - /** * gether_connect - notify network layer that USB link is active * @link: the USB link, set up with endpoints, descriptors matching @@ -860,7 +850,7 @@ void gether_cleanup(void) */ struct net_device *gether_connect(struct gether *link) { - struct eth_dev *dev = the_dev; + struct eth_dev *dev = link->ioport; int result = 0; if (!dev) @@ -895,7 +885,6 @@ struct net_device *gether_connect(struct gether *link) spin_lock(&dev->lock); dev->port_usb = link; - link->ioport = dev; if (netif_running(dev->net)) { if (link->open) link->open(link); @@ -989,6 +978,5 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->lock); dev->port_usb = NULL; - link->ioport = NULL; spin_unlock(&dev->lock); } diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 6f4a162..0252233 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -21,6 +21,7 @@ #include "gadget_chips.h" +struct eth_dev; /* * This represents the USB side of an "ethernet" link, managed by a USB @@ -70,7 +71,7 @@ struct gether { |USB_CDC_PACKET_TYPE_DIRECTED) /* variant of gether_setup that allows customizing network device name */ -int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], +struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], const char *netname); /* netdev setup/teardown as directed by the gadget driver */ @@ -86,12 +87,13 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], * * Returns negative errno, or zero on success */ -static inline int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +static inline struct eth_dev *gether_setup(struct usb_gadget *g, + u8 ethaddr[ETH_ALEN]) { return gether_setup_name(g, ethaddr, "usb"); } -void gether_cleanup(void); +void gether_cleanup(struct eth_dev *dev); /* connect/disconnect is handled by individual functions */ struct net_device *gether_connect(struct gether *); @@ -111,21 +113,24 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) } /* each configuration may bind one instance of an ethernet link */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int eem_bind_config(struct usb_configuration *c); +int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev); +int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev); +int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], + struct eth_dev *dev); +int eem_bind_config(struct usb_configuration *c, struct eth_dev *dev); #ifdef USB_ETH_RNDIS int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer); + u32 vendorID, const char *manufacturer, struct eth_dev *dev); #else static inline int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer) + u32 vendorID, const char *manufacturer, struct eth_dev *dev) { return 0; } @@ -145,9 +150,9 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], * for calling @gether_cleanup() before module unload. */ static inline int rndis_bind_config(struct usb_configuration *c, - u8 ethaddr[ETH_ALEN]) + u8 ethaddr[ETH_ALEN], struct eth_dev *dev) { - return rndis_bind_config_vendor(c, ethaddr, 0, NULL); + return rndis_bind_config_vendor(c, ethaddr, 0, NULL, dev); } -- cgit v0.10.2 From c4ed4ac198495895fd1620cba15184c3b2d399dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:18 +0100 Subject: usb: gadget: push tty port allocation from gadget into f_acm It possible to allocate the tty port number within the "instance" structure of the function and there is no need to expose this information within the gadget and therefore it is removed here. This patch converts only f_acm and all its users. The other gadgets will follow once the function is converted to the function interface. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 8f2b0e3..4b947bb 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -109,7 +109,6 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); static struct fsg_common fsg_common; /*-------------------------------------------------------------------------*/ -static unsigned char tty_line; static struct usb_function *f_acm; static struct usb_function_instance *f_acm_inst; /* @@ -117,7 +116,6 @@ static struct usb_function_instance *f_acm_inst; */ static int __init acm_ms_do_config(struct usb_configuration *c) { - struct f_serial_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -129,9 +127,6 @@ static int __init acm_ms_do_config(struct usb_configuration *c) if (IS_ERR(f_acm_inst)) return PTR_ERR(f_acm_inst); - opts = container_of(f_acm_inst, struct f_serial_opts, func_inst); - opts->port_num = tty_line; - f_acm = usb_get_function(f_acm_inst); if (IS_ERR(f_acm)) { status = PTR_ERR(f_acm); @@ -171,16 +166,11 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) int status; void *retp; - /* set up serial link layer */ - status = gserial_alloc_line(&tty_line); - if (status < 0) - return status; - /* set up mass storage function */ retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); if (IS_ERR(retp)) { status = PTR_ERR(retp); - goto fail0; + return PTR_ERR(retp); } /* @@ -207,8 +197,6 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) /* error recovery */ fail1: fsg_common_put(&fsg_common); -fail0: - gserial_free_line(tty_line); return status; } @@ -216,7 +204,6 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) { usb_put_function(f_acm); usb_put_function_instance(f_acm_inst); - gserial_free_line(tty_line); return 0; } diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 61023aa..c6ee6f1 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -108,13 +108,11 @@ static struct eth_dev *the_dev; static struct usb_function *f_acm; static struct usb_function_instance *fi_serial; -static unsigned char tty_line; /* * We _always_ have both CDC ECM and CDC ACM functions. */ static int __init cdc_do_config(struct usb_configuration *c) { - struct f_serial_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -130,9 +128,6 @@ static int __init cdc_do_config(struct usb_configuration *c) if (IS_ERR(fi_serial)) return PTR_ERR(fi_serial); - opts = container_of(fi_serial, struct f_serial_opts, func_inst); - opts->port_num = tty_line; - f_acm = usb_get_function(fi_serial); if (IS_ERR(f_acm)) goto err_func_acm; @@ -173,11 +168,6 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) if (IS_ERR(the_dev)) return PTR_ERR(the_dev); - /* set up serial link layer */ - status = gserial_alloc_line(&tty_line); - if (status < 0) - goto fail0; - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ @@ -200,8 +190,6 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return 0; fail1: - gserial_free_line(tty_line); -fail0: gether_cleanup(the_dev); return status; } @@ -210,7 +198,6 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev) { usb_put_function(f_acm); usb_put_function_instance(fi_serial); - gserial_free_line(tty_line); gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 61b33d2..ba7daaa 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -768,17 +768,24 @@ static void acm_free_instance(struct usb_function_instance *fi) struct f_serial_opts *opts; opts = container_of(fi, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); kfree(opts); } static struct usb_function_instance *acm_alloc_instance(void) { struct f_serial_opts *opts; + int ret; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = acm_free_instance; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); + } return &opts->func_inst; } DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 3442755..a74ebef 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -135,7 +135,6 @@ static struct fsg_common fsg_common; static u8 hostaddr[ETH_ALEN]; -static unsigned char tty_line; static struct usb_function_instance *fi_acm; static struct eth_dev *the_dev; @@ -270,7 +269,6 @@ static int cdc_config_register(struct usb_composite_dev *cdev) static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; - struct f_serial_opts *opts; int status; if (!can_support_ecm(cdev->gadget)) { @@ -285,19 +283,12 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) return PTR_ERR(the_dev); /* set up serial link layer */ - status = gserial_alloc_line(&tty_line); - if (status < 0) - goto fail0; - fi_acm = usb_get_function_instance("acm"); if (IS_ERR(fi_acm)) { status = PTR_ERR(fi_acm); - goto fail0dot5; + goto fail0; } - opts = container_of(fi_acm, struct f_serial_opts, func_inst); - opts->port_num = tty_line; - /* set up mass storage function */ { void *retp; @@ -335,8 +326,6 @@ fail2: fsg_common_put(&fsg_common); fail1: usb_put_function_instance(fi_acm); -fail0dot5: - gserial_free_line(tty_line); fail0: gether_cleanup(the_dev); return status; @@ -351,7 +340,6 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev) usb_put_function(f_acm_rndis); #endif usb_put_function_instance(fi_acm); - gserial_free_line(tty_line); gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 0c13ddd..b5fbf1a 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -104,7 +104,6 @@ static struct eth_dev *the_dev; enum { TTY_PORT_OBEX0, TTY_PORT_OBEX1, - TTY_PORT_ACM, TTY_PORTS_MAX, }; @@ -174,7 +173,6 @@ err_conf: static int __init nokia_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; - struct f_serial_opts *opts; int status; int cur_line; @@ -209,8 +207,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) fi_acm = usb_get_function_instance("acm"); if (IS_ERR(fi_acm)) goto err_usb; - opts = container_of(fi_acm, struct f_serial_opts, func_inst); - opts->port_num = tty_lines[TTY_PORT_ACM]; /* finally register the configuration */ status = usb_add_config(cdev, &nokia_config_500ma_driver, diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 68d7bb0..c48ca1e 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -169,15 +169,12 @@ static int serial_register_ports(struct usb_composite_dev *cdev, goto out; for (i = 0; i < n_ports; i++) { - struct f_serial_opts *opts; fi_serial[i] = usb_get_function_instance(f_name); if (IS_ERR(fi_serial[i])) { ret = PTR_ERR(fi_serial[i]); goto fail; } - opts = container_of(fi_serial[i], struct f_serial_opts, func_inst); - opts->port_num = tty_lines[i]; f_serial[i] = usb_get_function(fi_serial[i]); if (IS_ERR(f_serial[i])) { @@ -212,12 +209,14 @@ out: static int __init gs_bind(struct usb_composite_dev *cdev) { int status; - int cur_line; + int cur_line = 0; - for (cur_line = 0; cur_line < n_ports; cur_line++) { - status = gserial_alloc_line(&tty_lines[cur_line]); - if (status) - goto fail; + if (!use_acm) { + for (cur_line = 0; cur_line < n_ports; cur_line++) { + status = gserial_alloc_line(&tty_lines[cur_line]); + if (status) + goto fail; + } } /* Allocate string descriptor numbers ... note that string @@ -258,7 +257,7 @@ static int __init gs_bind(struct usb_composite_dev *cdev) fail: cur_line--; - while (cur_line >= 0) + while (cur_line >= 0 && !use_acm) gserial_free_line(tty_lines[cur_line--]); return status; } @@ -270,7 +269,8 @@ static int gs_unbind(struct usb_composite_dev *cdev) for (i = 0; i < n_ports; i++) { usb_put_function(f_serial[i]); usb_put_function_instance(fi_serial[i]); - gserial_free_line(tty_lines[i]); + if (!use_acm) + gserial_free_line(tty_lines[i]); } return 0; } -- cgit v0.10.2 From 88af8bbe4ef781031ad3370847553f3b42ba0076 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:24 +0100 Subject: usb: gadget: the start of the configfs interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit |# modprobe dummy_hcd num=2 |# modprobe libcomposite |# lsmod |Module Size Used by |libcomposite 31648 0 |dummy_hcd 19871 0 |# mkdir /sys/kernel/config/usb_gadget/oha |# cd /sys/kernel/config/usb_gadget/oha |# mkdir configs/def.1 |# mkdir configs/def.2 |# mkdir functions/acm.ttyS1 |# mkdir strings/0x1 |mkdir: cannot create directory `strings/0x1': Invalid argument |# mkdir strings/0x409 |# mkdir strings/1033 |mkdir: cannot create directory `strings/1033': File exists |# mkdir strings/1032 |# mkdir configs/def.1/strings/0x409 |# mkdir configs/def.2/strings/0x409 |#find . -ls | 975 0 drwxr-xr-x 5 root root 0 Dec 23 17:40 . | 978 0 drwxr-xr-x 4 root root 0 Dec 23 17:43 ./strings | 4100 0 drwxr-xr-x 2 root root 0 Dec 23 17:43 ./strings/1032 | 995 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/1032/serialnumber | 996 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/1032/product | 997 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/1032/manufacturer | 2002 0 drwxr-xr-x 2 root root 0 Dec 23 17:41 ./strings/0x409 | 998 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/0x409/serialnumber | 999 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/0x409/product | 1000 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./strings/0x409/manufacturer | 977 0 drwxr-xr-x 4 root root 0 Dec 23 17:41 ./configs | 4081 0 drwxr-xr-x 3 root root 0 Dec 23 17:41 ./configs/def.2 | 4082 0 drwxr-xr-x 3 root root 0 Dec 23 17:42 ./configs/def.2/strings | 2016 0 drwxr-xr-x 2 root root 0 Dec 23 17:42 ./configs/def.2/strings/0x409 | 1001 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.2/strings/0x409/configuration | 1002 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.2/bmAttributes | 1003 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.2/MaxPower | 979 0 drwxr-xr-x 3 root root 0 Dec 23 17:42 ./configs/def.1 | 980 0 drwxr-xr-x 3 root root 0 Dec 23 17:42 ./configs/def.1/strings | 5122 0 drwxr-xr-x 2 root root 0 Dec 23 17:42 ./configs/def.1/strings/0x409 | 1004 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.1/strings/0x409/configuration | 1005 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.1/bmAttributes | 1006 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./configs/def.1/MaxPower | 976 0 drwxr-xr-x 3 root root 0 Dec 23 17:41 ./functions | 981 0 drwxr-xr-x 2 root root 0 Dec 23 17:41 ./functions/acm.ttyS1 | 1007 0 -r--r--r-- 1 root root 4096 Dec 23 17:43 ./functions/acm.ttyS1/port_num | 1008 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./UDC | 1009 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bcdUSB | 1010 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bcdDevice | 1011 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./idProduct | 1012 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./idVendor | 1013 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bMaxPacketSize0 | 1014 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bDeviceProtocol | 1015 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bDeviceSubClass | 1016 0 -rw-r--r-- 1 root root 4096 Dec 23 17:43 ./bDeviceClass |# cat functions/acm.ttyS1/port_num |0 |# ls -lah /dev/ttyGS* |crw-rw---T 1 root dialout 252, 0 Dec 23 17:41 /dev/ttyGS0 | |# echo 0x1234 > idProduct |# echo 0xabcd > idVendor |# echo 1122 > strings/0x409/serialnumber |# echo "The manufacturer" > strings/0x409/manufacturer |# echo 1 > strings/1032/manufacturer |# echo 1sa > strings/1032/product |# echo tada > strings/1032/serialnumber |echo "Primary configuration" > configs/def.1/strings/0x409/configuration |# echo "Secondary configuration" > configs/def.2/strings/0x409/configuration |# ln -s functions/acm.ttyS1 configs/def.1/ |# ln -s functions/acm.ttyS1 configs/def.2/ |find configs/def.1/ -ls | 979 0 drwxr-xr-x 3 root root 0 Dec 23 17:49 configs/def.1/ | 6264 0 lrwxrwxrwx 1 root root 0 Dec 23 17:48 configs/def.1/acm.ttyS1 -> ../../../../usb_gadget/oha/functions/acm.ttyS1 | 980 0 drwxr-xr-x 3 root root 0 Dec 23 17:42 configs/def.1/strings | 5122 0 drwxr-xr-x 2 root root 0 Dec 23 17:49 configs/def.1/strings/0x409 | 6284 0 -rw-r--r-- 1 root root 4096 Dec 23 17:47 configs/def.1/strings/0x409/configuration | 6285 0 -rw-r--r-- 1 root root 4096 Dec 23 17:49 configs/def.1/bmAttributes | 6286 0 -rw-r--r-- 1 root root 4096 Dec 23 17:49 configs/def.1/MaxPower | |echo 120 > configs/def.1/MaxPower | |# ls -lh /sys/class/udc/ |total 0 |lrwxrwxrwx 1 root root 0 Dec 23 17:50 dummy_udc.0 -> ../../devices/platform/dummy_udc.0/udc/dummy_udc.0 |lrwxrwxrwx 1 root root 0 Dec 23 17:50 dummy_udc.1 -> ../../devices/platform/dummy_udc.1/udc/dummy_udc.1 |# echo dummy_udc.0 > UDC |# lsusb |Bus 001 Device 002: ID abcd:1234 Unknown | |lsusb -d abcd:1234 -v |Device Descriptor: … | idVendor 0xabcd Unknown | idProduct 0x1234 | bcdDevice 3.06 | iManufacturer 1 The manufacturer | iProduct 2 | iSerial 3 1122 | bNumConfigurations 2 … |echo "" > UDC v5…v6 - wired up strings with usb_gstrings_attach() - add UDC attribe. Write "udc-name" will bind the gadget. Write an empty string (it should contain \n since 0 bytes write get optimzed away) will unbind the UDC from the gadget. The name of available UDCs can be obtained from /sys/class/udc/ v4…v5 - string rework. This will add a strings folder incl. language code like strings/409/manufacturer as suggested by Alan. - rebased ontop reworked functions.c which has usb_function_instance which is used prior after "mkdir acm.instance" and can be directly used for configuration via configfs. v3…v4 - moved functions from the root folde down to the gadget as suggested by Michał - configs have now their own configs folder as suggested by Michał. The folder is still name.bConfigurationValue where name becomes the sConfiguration. Is this usefull should we just stilc configs/bConfigurationValue/ ? - added configfs support to the ACM function. The port_num attribute is exported by f_acm. An argument has been added to the USB alloc function to distinguish between "old" (use facm_configure() to configure and configfs interface (expose a config_node). The port_num is currently a dumb counter. It will require some function re-work to make it work. scheduled for v5: - sym linking function into config. v2…v3 - replaced one ifndef by ifdef as suggested by Micahał - strstr()/strchr() function_make as suggested by Micahł - replace [iSerialNumber|iProduct|iManufacturer] with [sSerialNumber|sProduct|sManufacturer] as suggested by Alan - added creation of config descriptors v1…v2 - moved gadgets from configfs' root directory into /udcs/ within our "usb_gadget" folder. Requested by Andrzej & Michał - use a dot as a delimiter between function's name and its instance's name as suggested by Michał - renamed all config_item_type, configfs_group_operations, make_group, drop_item as suggested by suggested by Andrzej to remain consisten within this file and within other configfs users - Since configfs.c and functions.c are now part of the udc-core module, the module itself is now called udc. Also added a tiny ifdef around init code becuase udc-core is subsys init and this is too early for configfs in the built-in case. In the module case, we can only have one init function. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index e1d3e08..74a29de 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -493,6 +493,7 @@ endmenu # composite based drivers config USB_LIBCOMPOSITE tristate + select CONFIGFS_FS depends on USB_GADGET config USB_F_ACM diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 82fb225..96e7243 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o -libcomposite-y += composite.o functions.o +libcomposite-y += composite.o functions.o configfs.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index c0d62b2..55f4df6 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1637,6 +1637,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) kfree(cdev->req->buf); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } + cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); } diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c new file mode 100644 index 0000000..a34633a --- /dev/null +++ b/drivers/usb/gadget/configfs.c @@ -0,0 +1,1003 @@ +#include +#include +#include +#include +#include +#include + +int check_user_usb_string(const char *name, + struct usb_gadget_strings *stringtab_dev) +{ + unsigned primary_lang; + unsigned sub_lang; + u16 num; + int ret; + + ret = kstrtou16(name, 0, &num); + if (ret) + return ret; + + primary_lang = num & 0x3ff; + sub_lang = num >> 10; + + /* simple sanity check for valid langid */ + switch (primary_lang) { + case 0: + case 0x62 ... 0xfe: + case 0x100 ... 0x3ff: + return -EINVAL; + } + if (!sub_lang) + return -EINVAL; + + stringtab_dev->language = num; + return 0; +} + +#define MAX_NAME_LEN 40 +#define MAX_USB_STRING_LANGS 2 + +struct gadget_info { + struct config_group group; + struct config_group functions_group; + struct config_group configs_group; + struct config_group strings_group; + struct config_group *default_groups[4]; + + struct mutex lock; + struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; + struct list_head string_list; + struct list_head available_func; + + const char *udc_name; +#ifdef CONFIG_USB_OTG + struct usb_otg_descriptor otg; +#endif + struct usb_composite_driver composite; + struct usb_composite_dev cdev; +}; + +struct config_usb_cfg { + struct config_group group; + struct config_group strings_group; + struct config_group *default_groups[2]; + struct list_head string_list; + struct usb_configuration c; + struct list_head func_list; + struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; +}; + +struct gadget_strings { + struct usb_gadget_strings stringtab_dev; + struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; + char *manufacturer; + char *product; + char *serialnumber; + + struct config_group group; + struct list_head list; +}; + +struct gadget_config_name { + struct usb_gadget_strings stringtab_dev; + struct usb_string strings; + char *configuration; + + struct config_group group; + struct list_head list; +}; + +static int usb_string_copy(const char *s, char **s_copy) +{ + int ret; + char *str; + char *copy = *s_copy; + ret = strlen(s); + if (ret > 126) + return -EOVERFLOW; + + str = kstrdup(s, GFP_KERNEL); + if (!str) + return -ENOMEM; + if (str[ret - 1] == '\n') + str[ret - 1] = '\0'; + kfree(copy); + *s_copy = str; + return 0; +} + +CONFIGFS_ATTR_STRUCT(gadget_info); +CONFIGFS_ATTR_STRUCT(config_usb_cfg); + +#define GI_DEVICE_DESC_ITEM_ATTR(name) \ + static struct gadget_info_attribute gadget_cdev_desc_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_dev_desc_##name##_show, \ + gadget_dev_desc_##name##_store) + +#define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ + char *page) \ +{ \ + return sprintf(page, "0x%02x\n", gi->cdev.desc.__name); \ +} + +#define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ + char *page) \ +{ \ + return sprintf(page, "0x%04x\n", le16_to_cpup(&gi->cdev.desc.__name)); \ +} + + +#define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u8 val; \ + int ret; \ + ret = kstrtou8(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->cdev.desc._name = val; \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u16 val; \ + int ret; \ + ret = kstrtou16(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->cdev.desc._name = cpu_to_le16p(&val); \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ + GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ + GI_DEVICE_DESC_SIMPLE_W_##_type(_name) + +GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); +GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); +GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); +GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); +GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); + +static ssize_t is_valid_bcd(u16 bcd_val) +{ + if ((bcd_val & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 4) & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 8) & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 12) & 0xf) > 9) + return -EINVAL; + return 0; +} + +static ssize_t gadget_dev_desc_bcdDevice_store(struct gadget_info *gi, + const char *page, size_t len) +{ + u16 bcdDevice; + int ret; + + ret = kstrtou16(page, 0, &bcdDevice); + if (ret) + return ret; + ret = is_valid_bcd(bcdDevice); + if (ret) + return ret; + + gi->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); + return len; +} + +static ssize_t gadget_dev_desc_bcdUSB_store(struct gadget_info *gi, + const char *page, size_t len) +{ + u16 bcdUSB; + int ret; + + ret = kstrtou16(page, 0, &bcdUSB); + if (ret) + return ret; + ret = is_valid_bcd(bcdUSB); + if (ret) + return ret; + + gi->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); + return len; +} + +static ssize_t gadget_dev_desc_UDC_show(struct gadget_info *gi, char *page) +{ + return sprintf(page, "%s\n", gi->udc_name ?: ""); +} + +static int unregister_gadget(struct gadget_info *gi) +{ + int ret; + + if (!gi->udc_name) + return -ENODEV; + + ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); + if (ret) + return ret; + kfree(gi->udc_name); + gi->udc_name = NULL; + return 0; +} + +static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, + const char *page, size_t len) +{ + char *name; + int ret; + + name = kstrdup(page, GFP_KERNEL); + if (!name) + return -ENOMEM; + if (name[len - 1] == '\n') + name[len - 1] = '\0'; + + mutex_lock(&gi->lock); + + if (!strlen(name)) { + ret = unregister_gadget(gi); + if (ret) + goto err; + } else { + if (gi->udc_name) { + ret = -EBUSY; + goto err; + } + ret = udc_attach_driver(name, &gi->composite.gadget_driver); + if (ret) + goto err; + gi->udc_name = name; + } + mutex_unlock(&gi->lock); + return len; +err: + kfree(name); + mutex_unlock(&gi->lock); + return ret; +} + +GI_DEVICE_DESC_ITEM_ATTR(bDeviceClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceSubClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceProtocol); +GI_DEVICE_DESC_ITEM_ATTR(bMaxPacketSize0); +GI_DEVICE_DESC_ITEM_ATTR(idVendor); +GI_DEVICE_DESC_ITEM_ATTR(idProduct); +GI_DEVICE_DESC_ITEM_ATTR(bcdDevice); +GI_DEVICE_DESC_ITEM_ATTR(bcdUSB); +GI_DEVICE_DESC_ITEM_ATTR(UDC); + +static struct configfs_attribute *gadget_root_attrs[] = { + &gadget_cdev_desc_bDeviceClass.attr, + &gadget_cdev_desc_bDeviceSubClass.attr, + &gadget_cdev_desc_bDeviceProtocol.attr, + &gadget_cdev_desc_bMaxPacketSize0.attr, + &gadget_cdev_desc_idVendor.attr, + &gadget_cdev_desc_idProduct.attr, + &gadget_cdev_desc_bcdDevice.attr, + &gadget_cdev_desc_bcdUSB.attr, + &gadget_cdev_desc_UDC.attr, + NULL, +}; + +static inline struct gadget_info *to_gadget_info(struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_info, group); +} + +static inline struct gadget_strings *to_gadget_strings(struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_strings, + group); +} + +static inline struct gadget_config_name *to_gadget_config_name( + struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_config_name, + group); +} + +static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) +{ + return container_of(to_config_group(item), struct config_usb_cfg, + group); +} + +static inline struct usb_function_instance *to_usb_function_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), + struct usb_function_instance, group); +} + +static void gadget_info_attr_release(struct config_item *item) +{ + struct gadget_info *gi = to_gadget_info(item); + + WARN_ON(!list_empty(&gi->cdev.configs)); + WARN_ON(!list_empty(&gi->string_list)); + WARN_ON(!list_empty(&gi->available_func)); + kfree(gi->composite.gadget_driver.function); + kfree(gi); +} + +CONFIGFS_ATTR_OPS(gadget_info); + +static struct configfs_item_operations gadget_root_item_ops = { + .release = gadget_info_attr_release, + .show_attribute = gadget_info_attr_show, + .store_attribute = gadget_info_attr_store, +}; + +static void gadget_config_attr_release(struct config_item *item) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(item); + + WARN_ON(!list_empty(&cfg->c.functions)); + list_del(&cfg->c.list); + kfree(cfg->c.label); + kfree(cfg); +} + +static int config_usb_cfg_link( + struct config_item *usb_cfg_ci, + struct config_item *usb_func_ci) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); + struct usb_composite_dev *cdev = cfg->c.cdev; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + struct config_group *group = to_config_group(usb_func_ci); + struct usb_function_instance *fi = container_of(group, + struct usb_function_instance, group); + struct usb_function_instance *a_fi; + struct usb_function *f; + int ret; + + mutex_lock(&gi->lock); + /* + * Make sure this function is from within our _this_ gadget and not + * from another gadget or a random directory. + * Also a function instance can only be linked once. + */ + list_for_each_entry(a_fi, &gi->available_func, cfs_list) { + if (a_fi == fi) + break; + } + if (a_fi != fi) { + ret = -EINVAL; + goto out; + } + + list_for_each_entry(f, &cfg->func_list, list) { + if (f->fi == fi) { + ret = -EEXIST; + goto out; + } + } + + f = usb_get_function(fi); + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto out; + } + + /* stash the function until we bind it to the gadget */ + list_add_tail(&f->list, &cfg->func_list); + ret = 0; +out: + mutex_unlock(&gi->lock); + return ret; +} + +static int config_usb_cfg_unlink( + struct config_item *usb_cfg_ci, + struct config_item *usb_func_ci) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); + struct usb_composite_dev *cdev = cfg->c.cdev; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + struct config_group *group = to_config_group(usb_func_ci); + struct usb_function_instance *fi = container_of(group, + struct usb_function_instance, group); + struct usb_function *f; + + /* + * ideally I would like to forbid to unlink functions while a gadget is + * bound to an UDC. Since this isn't possible at the moment, we simply + * force an unbind, the function is available here and then we can + * remove the function. + */ + mutex_lock(&gi->lock); + if (gi->udc_name) + unregister_gadget(gi); + WARN_ON(gi->udc_name); + + list_for_each_entry(f, &cfg->func_list, list) { + if (f->fi == fi) { + list_del(&f->list); + usb_put_function(f); + mutex_unlock(&gi->lock); + return 0; + } + } + mutex_unlock(&gi->lock); + __WARN_printf("Unable to locate function to unbind\n"); + return 0; +} + +CONFIGFS_ATTR_OPS(config_usb_cfg); + +static struct configfs_item_operations gadget_config_item_ops = { + .release = gadget_config_attr_release, + .show_attribute = config_usb_cfg_attr_show, + .store_attribute = config_usb_cfg_attr_store, + .allow_link = config_usb_cfg_link, + .drop_link = config_usb_cfg_unlink, +}; + + +static ssize_t gadget_config_desc_MaxPower_show(struct config_usb_cfg *cfg, + char *page) +{ + return sprintf(page, "%u\n", cfg->c.MaxPower); +} + +static ssize_t gadget_config_desc_MaxPower_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u16 val; + int ret; + ret = kstrtou16(page, 0, &val); + if (ret) + return ret; + if (DIV_ROUND_UP(val, 8) > 0xff) + return -ERANGE; + cfg->c.MaxPower = val; + return len; +} + +static ssize_t gadget_config_desc_bmAttributes_show(struct config_usb_cfg *cfg, + char *page) +{ + return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); +} + +static ssize_t gadget_config_desc_bmAttributes_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u8 val; + int ret; + ret = kstrtou8(page, 0, &val); + if (ret) + return ret; + if (!(val & USB_CONFIG_ATT_ONE)) + return -EINVAL; + if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | + USB_CONFIG_ATT_WAKEUP)) + return -EINVAL; + cfg->c.bmAttributes = val; + return len; +} + +#define CFG_CONFIG_DESC_ITEM_ATTR(name) \ + static struct config_usb_cfg_attribute gadget_usb_cfg_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_config_desc_##name##_show, \ + gadget_config_desc_##name##_store) + +CFG_CONFIG_DESC_ITEM_ATTR(MaxPower); +CFG_CONFIG_DESC_ITEM_ATTR(bmAttributes); + +static struct configfs_attribute *gadget_config_attrs[] = { + &gadget_usb_cfg_MaxPower.attr, + &gadget_usb_cfg_bmAttributes.attr, + NULL, +}; + +static struct config_item_type gadget_config_type = { + .ct_item_ops = &gadget_config_item_ops, + .ct_attrs = gadget_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item_type gadget_root_type = { + .ct_item_ops = &gadget_root_item_ops, + .ct_attrs = gadget_root_attrs, + .ct_owner = THIS_MODULE, +}; + +static void composite_init_dev(struct usb_composite_dev *cdev) +{ + spin_lock_init(&cdev->lock); + INIT_LIST_HEAD(&cdev->configs); + INIT_LIST_HEAD(&cdev->gstrings); +} + +static struct config_group *function_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct usb_function_instance *fi; + char buf[MAX_NAME_LEN]; + char *func_name; + char *instance_name; + int ret; + + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + func_name = buf; + instance_name = strchr(func_name, '.'); + if (!instance_name) { + pr_err("Unable to locate . in FUNC.INSTANCE\n"); + return ERR_PTR(-EINVAL); + } + *instance_name = '\0'; + instance_name++; + + fi = usb_get_function_instance(func_name); + if (IS_ERR(fi)) + return ERR_PTR(PTR_ERR(fi)); + + ret = config_item_set_name(&fi->group.cg_item, name); + if (ret) { + usb_put_function_instance(fi); + return ERR_PTR(ret); + } + + gi = container_of(group, struct gadget_info, functions_group); + + mutex_lock(&gi->lock); + list_add_tail(&fi->cfs_list, &gi->available_func); + mutex_unlock(&gi->lock); + return &fi->group; +} + +static void function_drop( + struct config_group *group, + struct config_item *item) +{ + struct usb_function_instance *fi = to_usb_function_instance(item); + struct gadget_info *gi; + + gi = container_of(group, struct gadget_info, functions_group); + + mutex_lock(&gi->lock); + list_del(&fi->cfs_list); + mutex_unlock(&gi->lock); + config_item_put(item); +} + +static struct configfs_group_operations functions_ops = { + .make_group = &function_make, + .drop_item = &function_drop, +}; + +static struct config_item_type functions_type = { + .ct_group_ops = &functions_ops, + .ct_owner = THIS_MODULE, +}; + +CONFIGFS_ATTR_STRUCT(gadget_config_name); +GS_STRINGS_RW(gadget_config_name, configuration); + +static struct configfs_attribute *gadget_config_name_langid_attrs[] = { + &gadget_config_name_configuration.attr, + NULL, +}; + +static void gadget_config_name_attr_release(struct config_item *item) +{ + struct gadget_config_name *cn = to_gadget_config_name(item); + + kfree(cn->configuration); + + list_del(&cn->list); + kfree(cn); +} + +USB_CONFIG_STRING_RW_OPS(gadget_config_name); +USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); + +static struct config_group *config_desc_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct config_usb_cfg *cfg; + char buf[MAX_NAME_LEN]; + char *num_str; + u8 num; + int ret; + + gi = container_of(group, struct gadget_info, configs_group); + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + num_str = strchr(buf, '.'); + if (!num_str) { + pr_err("Unable to locate . in name.bConfigurationValue\n"); + return ERR_PTR(-EINVAL); + } + + *num_str = '\0'; + num_str++; + + if (!strlen(buf)) + return ERR_PTR(-EINVAL); + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return ERR_PTR(-ENOMEM); + cfg->c.label = kstrdup(buf, GFP_KERNEL); + if (!cfg->c.label) { + ret = -ENOMEM; + goto err; + } + cfg->c.bConfigurationValue = num; + cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; + cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; + INIT_LIST_HEAD(&cfg->string_list); + INIT_LIST_HEAD(&cfg->func_list); + + cfg->group.default_groups = cfg->default_groups; + cfg->default_groups[0] = &cfg->strings_group; + + config_group_init_type_name(&cfg->group, name, + &gadget_config_type); + config_group_init_type_name(&cfg->strings_group, "strings", + &gadget_config_name_strings_type); + + ret = usb_add_config_only(&gi->cdev, &cfg->c); + if (ret) + goto err; + + return &cfg->group; +err: + kfree(cfg->c.label); + kfree(cfg); + return ERR_PTR(ret); +} + +static void config_desc_drop( + struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations config_desc_ops = { + .make_group = &config_desc_make, + .drop_item = &config_desc_drop, +}; + +static struct config_item_type config_desc_type = { + .ct_group_ops = &config_desc_ops, + .ct_owner = THIS_MODULE, +}; + +CONFIGFS_ATTR_STRUCT(gadget_strings); +GS_STRINGS_RW(gadget_strings, manufacturer); +GS_STRINGS_RW(gadget_strings, product); +GS_STRINGS_RW(gadget_strings, serialnumber); + +static struct configfs_attribute *gadget_strings_langid_attrs[] = { + &gadget_strings_manufacturer.attr, + &gadget_strings_product.attr, + &gadget_strings_serialnumber.attr, + NULL, +}; + +static void gadget_strings_attr_release(struct config_item *item) +{ + struct gadget_strings *gs = to_gadget_strings(item); + + kfree(gs->manufacturer); + kfree(gs->product); + kfree(gs->serialnumber); + + list_del(&gs->list); + kfree(gs); +} + +USB_CONFIG_STRING_RW_OPS(gadget_strings); +USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); + +static int configfs_do_nothing(struct usb_composite_dev *cdev) +{ + __WARN(); + return -EINVAL; +} + +int composite_dev_prepare(struct usb_composite_driver *composite, + struct usb_composite_dev *dev); + +static void purge_configs_funcs(struct gadget_info *gi) +{ + struct usb_configuration *c; + + list_for_each_entry(c, &gi->cdev.configs, list) { + struct usb_function *f, *tmp; + struct config_usb_cfg *cfg; + + cfg = container_of(c, struct config_usb_cfg, c); + + list_for_each_entry_safe(f, tmp, &c->functions, list) { + + list_move_tail(&f->list, &cfg->func_list); + if (f->unbind) { + dev_err(&gi->cdev.gadget->dev, "unbind function" + " '%s'/%p\n", f->name, f); + f->unbind(c, f); + } + } + c->next_interface_id = 0; + c->superspeed = 0; + c->highspeed = 0; + c->fullspeed = 0; + } +} + +static int configfs_composite_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *gdriver) +{ + struct usb_composite_driver *composite = to_cdriver(gdriver); + struct gadget_info *gi = container_of(composite, + struct gadget_info, composite); + struct usb_composite_dev *cdev = &gi->cdev; + struct usb_configuration *c; + struct usb_string *s; + unsigned i; + int ret; + + /* the gi->lock is hold by the caller */ + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + ret = composite_dev_prepare(composite, cdev); + if (ret) + return ret; + /* and now the gadget bind */ + ret = -EINVAL; + + if (list_empty(&gi->cdev.configs)) { + pr_err("Need atleast one configuration in %s.\n", + gi->composite.name); + goto err_comp_cleanup; + } + + + list_for_each_entry(c, &gi->cdev.configs, list) { + struct config_usb_cfg *cfg; + + cfg = container_of(c, struct config_usb_cfg, c); + if (list_empty(&cfg->func_list)) { + pr_err("Config %s/%d of %s needs atleast one function.\n", + c->label, c->bConfigurationValue, + gi->composite.name); + goto err_comp_cleanup; + } + } + + /* init all strings */ + if (!list_empty(&gi->string_list)) { + struct gadget_strings *gs; + + i = 0; + list_for_each_entry(gs, &gi->string_list, list) { + + gi->gstrings[i] = &gs->stringtab_dev; + gs->stringtab_dev.strings = gs->strings; + gs->strings[USB_GADGET_MANUFACTURER_IDX].s = + gs->manufacturer; + gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; + gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; + i++; + } + gi->gstrings[i] = NULL; + s = usb_gstrings_attach(&gi->cdev, gi->gstrings, + USB_GADGET_FIRST_AVAIL_IDX); + if (IS_ERR(s)) + goto err_comp_cleanup; + + gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; + gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; + gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; + } + + /* Go through all configs, attach all functions */ + list_for_each_entry(c, &gi->cdev.configs, list) { + struct config_usb_cfg *cfg; + struct usb_function *f; + struct usb_function *tmp; + struct gadget_config_name *cn; + + cfg = container_of(c, struct config_usb_cfg, c); + if (!list_empty(&cfg->string_list)) { + i = 0; + list_for_each_entry(cn, &cfg->string_list, list) { + cfg->gstrings[i] = &cn->stringtab_dev; + cn->stringtab_dev.strings = &cn->strings; + cn->strings.s = cn->configuration; + i++; + } + cfg->gstrings[i] = NULL; + s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); + if (IS_ERR(s)) + goto err_comp_cleanup; + c->iConfiguration = s[0].id; + } + + list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { + list_del(&f->list); + ret = usb_add_function(c, f); + if (ret) + goto err_purge_funcs; + } + usb_ep_autoconfig_reset(cdev->gadget); + } + usb_ep_autoconfig_reset(cdev->gadget); + return 0; + +err_purge_funcs: + purge_configs_funcs(gi); +err_comp_cleanup: + composite_dev_cleanup(cdev); + return ret; +} + +static void configfs_composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + + /* the gi->lock is hold by the caller */ + + cdev = get_gadget_data(gadget); + gi = container_of(cdev, struct gadget_info, cdev); + + purge_configs_funcs(gi); + composite_dev_cleanup(cdev); + usb_ep_autoconfig_reset(cdev->gadget); + cdev->gadget = NULL; + set_gadget_data(gadget, NULL); +} + +static const struct usb_gadget_driver configfs_driver_template = { + .bind = configfs_composite_bind, + .unbind = configfs_composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .max_speed = USB_SPEED_SUPER, + .driver = { + .owner = THIS_MODULE, + .name = "configfs-gadget", + }, +}; + +static struct config_group *gadgets_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + + gi = kzalloc(sizeof(*gi), GFP_KERNEL); + if (!gi) + return ERR_PTR(-ENOMEM); + + gi->group.default_groups = gi->default_groups; + gi->group.default_groups[0] = &gi->functions_group; + gi->group.default_groups[1] = &gi->configs_group; + gi->group.default_groups[2] = &gi->strings_group; + + config_group_init_type_name(&gi->functions_group, "functions", + &functions_type); + config_group_init_type_name(&gi->configs_group, "configs", + &config_desc_type); + config_group_init_type_name(&gi->strings_group, "strings", + &gadget_strings_strings_type); + + gi->composite.bind = configfs_do_nothing; + gi->composite.unbind = configfs_do_nothing; + gi->composite.suspend = NULL; + gi->composite.resume = NULL; + gi->composite.max_speed = USB_SPEED_SUPER; + + mutex_init(&gi->lock); + INIT_LIST_HEAD(&gi->string_list); + INIT_LIST_HEAD(&gi->available_func); + + composite_init_dev(&gi->cdev); + gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; + gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; + gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); + + gi->composite.gadget_driver = configfs_driver_template; + + gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); + gi->composite.name = gi->composite.gadget_driver.function; + + if (!gi->composite.gadget_driver.function) + goto err; + +#ifdef CONFIG_USB_OTG + gi->otg.bLength = sizeof(struct usb_otg_descriptor); + gi->otg.bDescriptorType = USB_DT_OTG; + gi->otg.bmAttributes = USB_OTG_SRP | USB_OTG_HNP; +#endif + + config_group_init_type_name(&gi->group, name, + &gadget_root_type); + return &gi->group; +err: + kfree(gi); + return ERR_PTR(-ENOMEM); +} + +static void gadgets_drop(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations gadgets_ops = { + .make_group = &gadgets_make, + .drop_item = &gadgets_drop, +}; + +static struct config_item_type gadgets_type = { + .ct_group_ops = &gadgets_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gadget_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "usb_gadget", + .ci_type = &gadgets_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), +}; + +static int __init gadget_cfs_init(void) +{ + int ret; + + config_group_init(&gadget_subsys.su_group); + + ret = configfs_register_subsystem(&gadget_subsys); + return ret; +} +module_init(gadget_cfs_init); + +static void __exit gadget_cfs_exit(void) +{ + configfs_unregister_subsystem(&gadget_subsys); +} +module_exit(gadget_cfs_exit); diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index ba7daaa..4b7e33e 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -763,6 +763,59 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) return &acm->port.func; } +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_acm_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; + + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); + return ret; +} + +static void acm_attr_release(struct config_item *item) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations acm_item_ops = { + .release = acm_attr_release, + .show_attribute = f_acm_attr_show, +}; + +static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_acm_port_num = + __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show); + + +static struct configfs_attribute *acm_attrs[] = { + &f_acm_port_num.attr, + NULL, +}; + +static struct config_item_type acm_func_type = { + .ct_item_ops = &acm_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + static void acm_free_instance(struct usb_function_instance *fi) { struct f_serial_opts *opts; @@ -786,6 +839,8 @@ static struct usb_function_instance *acm_alloc_instance(void) kfree(opts); return ERR_PTR(ret); } + config_group_init_type_name(&opts->func_inst.group, "", + &acm_func_type); return &opts->func_inst; } DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 8860594..5e61589 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -39,6 +39,7 @@ #include #include #include +#include /* * USB function drivers should return USB_GADGET_DELAYED_STATUS if they @@ -464,6 +465,8 @@ struct usb_function_driver { }; struct usb_function_instance { + struct config_group group; + struct list_head cfs_list; struct usb_function_driver *fd; void (*free_func_inst)(struct usb_function_instance *inst); }; diff --git a/include/linux/usb/gadget_configfs.h b/include/linux/usb/gadget_configfs.h new file mode 100644 index 0000000..d74c0ae --- /dev/null +++ b/include/linux/usb/gadget_configfs.h @@ -0,0 +1,110 @@ +#ifndef __GADGET_CONFIGFS__ +#define __GADGET_CONFIGFS__ + +#include + +int check_user_usb_string(const char *name, + struct usb_gadget_strings *stringtab_dev); + +#define GS_STRINGS_W(__struct, __name) \ + static ssize_t __struct##_##__name##_store(struct __struct *gs, \ + const char *page, size_t len) \ +{ \ + int ret; \ + \ + ret = usb_string_copy(page, &gs->__name); \ + if (ret) \ + return ret; \ + return len; \ +} + +#define GS_STRINGS_R(__struct, __name) \ + static ssize_t __struct##_##__name##_show(struct __struct *gs, \ + char *page) \ +{ \ + return sprintf(page, "%s\n", gs->__name ?: ""); \ +} + +#define GS_STRING_ITEM_ATTR(struct_name, name) \ + static struct struct_name##_attribute struct_name##_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + struct_name##_##name##_show, \ + struct_name##_##name##_store) + +#define GS_STRINGS_RW(struct_name, _name) \ + GS_STRINGS_R(struct_name, _name) \ + GS_STRINGS_W(struct_name, _name) \ + GS_STRING_ITEM_ATTR(struct_name, _name) + +#define USB_CONFIG_STRING_RW_OPS(struct_in) \ + CONFIGFS_ATTR_OPS(struct_in); \ + \ +static struct configfs_item_operations struct_in##_langid_item_ops = { \ + .release = struct_in##_attr_release, \ + .show_attribute = struct_in##_attr_show, \ + .store_attribute = struct_in##_attr_store, \ +}; \ + \ +static struct config_item_type struct_in##_langid_type = { \ + .ct_item_ops = &struct_in##_langid_item_ops, \ + .ct_attrs = struct_in##_langid_attrs, \ + .ct_owner = THIS_MODULE, \ +} + +#define USB_CONFIG_STRINGS_LANG(struct_in, struct_member) \ + static struct config_group *struct_in##_strings_make( \ + struct config_group *group, \ + const char *name) \ + { \ + struct struct_member *gi; \ + struct struct_in *gs; \ + struct struct_in *new; \ + int langs = 0; \ + int ret; \ + \ + new = kzalloc(sizeof(*new), GFP_KERNEL); \ + if (!new) \ + return ERR_PTR(-ENOMEM); \ + \ + ret = check_user_usb_string(name, &new->stringtab_dev); \ + if (ret) \ + goto err; \ + config_group_init_type_name(&new->group, name, \ + &struct_in##_langid_type); \ + \ + gi = container_of(group, struct struct_member, strings_group); \ + ret = -EEXIST; \ + list_for_each_entry(gs, &gi->string_list, list) { \ + if (gs->stringtab_dev.language == new->stringtab_dev.language) \ + goto err; \ + langs++; \ + } \ + ret = -EOVERFLOW; \ + if (langs >= MAX_USB_STRING_LANGS) \ + goto err; \ + \ + list_add_tail(&new->list, &gi->string_list); \ + return &new->group; \ +err: \ + kfree(new); \ + return ERR_PTR(ret); \ +} \ + \ +static void struct_in##_strings_drop( \ + struct config_group *group, \ + struct config_item *item) \ +{ \ + config_item_put(item); \ +} \ + \ +static struct configfs_group_operations struct_in##_strings_ops = { \ + .make_group = &struct_in##_strings_make, \ + .drop_item = &struct_in##_strings_drop, \ +}; \ + \ +static struct config_item_type struct_in##_strings_type = { \ + .ct_group_ops = &struct_in##_strings_ops, \ + .ct_owner = THIS_MODULE, \ +} + +#endif -- cgit v0.10.2 From 4f47a1be0fb2ce2ee166faeb3f08f22d70e09109 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 7 Feb 2013 14:36:27 +0100 Subject: usb: gadget: use consistent naming scheme for usb function modules In order to convert to configfs the USB functions need to be converted to the new interface from Sebastian, which also requires turning them into separate modules. Some of these modules will consist of just one object file, e.g. f_ncm.o. But some of the modules will eventually consist of more than one object file, e.g. for mass storage there will be f_mass_storage.o and storage_common.o. The resulting module cannot be called f_mass_storage.ko due to cyclic dependency. This patch introduces a naming scheme for the said resulting modules: usb_f_xxxxxx.ko e.g. usb_f_mass_storage.ko, usb_f_ss_lb.ko, usb_f_rndis.ko etc. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 96e7243..b938e06 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -36,9 +36,10 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o # USB Functions -obj-$(CONFIG_USB_F_ACM) += f_acm.o -f_ss_lb-y := f_loopback.o f_sourcesink.o -obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o +usb_f_acm-y := f_acm.o +obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +usb_f_ss_lb-y := f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o # -- cgit v0.10.2 From 81f886cb7a5157d1b6562084a2ed96328e39f288 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 14 Mar 2013 16:11:57 +0100 Subject: usb: gadget: nokia: remove unused include f_serial.c isn't necessary in nokia.c, we can safely remove it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index b5fbf1a..f829a80 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -39,7 +39,6 @@ */ #include "f_ecm.c" #include "f_obex.c" -#include "f_serial.c" #include "f_phonet.c" #include "u_ether.c" -- cgit v0.10.2 From 60540ea2c51fa4feb475f689adce56297cb52010 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 18 Mar 2013 09:52:57 +0100 Subject: usb: gadget: f_serial: convert to new function interface with backward compatibility Converting f_serial to the new function interface requires converting the f_serial's function code and its users. This patch converts the f_serial.c to the new function interface. The file is now compiled into a separate usb_f_serial.ko module. The old function interface is provided by means of preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 74a29de..8a08efb 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -505,6 +505,9 @@ config USB_F_SS_LB config USB_U_SERIAL tristate +config USB_F_SERIAL + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b938e06..34bb49a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -41,6 +41,8 @@ obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o usb_f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o +usb_f_serial-y := f_serial.o +obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o # # USB gadget drivers diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index da33cfb..465789b 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -12,6 +12,7 @@ #include #include +#include #include #include "u_serial.h" @@ -42,7 +43,7 @@ static inline struct f_gser *func_to_gser(struct usb_function *f) /* interface descriptor: */ -static struct usb_interface_descriptor gser_interface_desc __initdata = { +static struct usb_interface_descriptor gser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -55,21 +56,21 @@ static struct usb_interface_descriptor gser_interface_desc __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *gser_fs_function[] __initdata = { +static struct usb_descriptor_header *gser_fs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_fs_in_desc, (struct usb_descriptor_header *) &gser_fs_out_desc, @@ -78,47 +79,47 @@ static struct usb_descriptor_header *gser_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *gser_hs_function[] __initdata = { +static struct usb_descriptor_header *gser_hs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_hs_in_desc, (struct usb_descriptor_header *) &gser_hs_out_desc, NULL, }; -static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_ss_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_ss_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = { +static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = { .bLength = sizeof gser_ss_bulk_comp_desc, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; -static struct usb_descriptor_header *gser_ss_function[] __initdata = { +static struct usb_descriptor_header *gser_ss_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_ss_in_desc, (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, @@ -183,14 +184,25 @@ static void gser_disable(struct usb_function *f) /* serial function driver setup/binding */ -static int __init -gser_bind(struct usb_configuration *c, struct usb_function *f) +static int gser_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_gser *gser = func_to_gser(f); int status; struct usb_ep *ep; + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string ID */ + if (gser_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + gser_string_defs[0].id = status; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -246,8 +258,10 @@ fail: return status; } +#ifdef USB_FSERIAL_INCLUDED + static void -gser_unbind(struct usb_configuration *c, struct usb_function *f) +gser_old_unbind(struct usb_configuration *c, struct usb_function *f) { usb_free_all_descriptors(f); kfree(func_to_gser(f)); @@ -266,18 +280,6 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) struct f_gser *gser; int status; - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ - - /* maybe allocate device-global string ID */ - if (gser_string_defs[0].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - gser_string_defs[0].id = status; - } - /* allocate and initialize one new instance */ gser = kzalloc(sizeof *gser, GFP_KERNEL); if (!gser) @@ -288,7 +290,7 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) gser->port.func.name = "gser"; gser->port.func.strings = gser_strings; gser->port.func.bind = gser_bind; - gser->port.func.unbind = gser_unbind; + gser->port.func.unbind = gser_old_unbind; gser->port.func.set_alt = gser_set_alt; gser->port.func.disable = gser_disable; @@ -297,3 +299,78 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) kfree(gser); return status; } + +#else + +static void gser_free_inst(struct usb_function_instance *f) +{ + struct f_serial_opts *opts; + + opts = container_of(f, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); + kfree(opts); +} + +static struct usb_function_instance *gser_alloc_inst(void) +{ + struct f_serial_opts *opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = gser_free_inst; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); + } + + return &opts->func_inst; +} + +static void gser_free(struct usb_function *f) +{ + struct f_gser *serial; + + serial = func_to_gser(f); + kfree(serial); +} + +static void gser_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +struct usb_function *gser_alloc(struct usb_function_instance *fi) +{ + struct f_gser *gser; + struct f_serial_opts *opts; + + /* allocate and initialize one new instance */ + gser = kzalloc(sizeof(*gser), GFP_KERNEL); + if (!gser) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_serial_opts, func_inst); + + gser->port_num = opts->port_num; + + gser->port.func.name = "gser"; + gser->port.func.strings = gser_strings; + gser->port.func.bind = gser_bind; + gser->port.func.unbind = gser_unbind; + gser->port.func.set_alt = gser_set_alt; + gser->port.func.disable = gser_disable; + gser->port.func.free_func = gser_free; + + return &gser->port.func; +} + +DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index c48ca1e..07c1e80 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -37,6 +37,7 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_obex.c" +#define USB_FSERIAL_INCLUDED #include "f_serial.c" /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 70cc3c024640a3c60e91d04789f9574a371a2db5 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 14 Mar 2013 16:02:12 +0100 Subject: usb: gadget: serial: convert to new interface of f_serial Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8a08efb..93217fc 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -762,6 +762,7 @@ config USB_G_SERIAL depends on TTY select USB_U_SERIAL select USB_F_ACM + select USB_F_SERIAL select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 07c1e80..9d215c4 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -37,8 +37,6 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_obex.c" -#define USB_FSERIAL_INCLUDED -#include "f_serial.c" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -139,16 +137,6 @@ static int __init serial_bind_obex_config(struct usb_configuration *c) return status; } -static int __init serial_bind_gser_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) - status = gser_bind_config(c, tty_lines[i]); - return status; -} - static struct usb_configuration serial_config_driver = { /* .label = f(use_acm) */ /* .bConfigurationValue = f(use_acm) */ @@ -212,7 +200,7 @@ static int __init gs_bind(struct usb_composite_dev *cdev) int status; int cur_line = 0; - if (!use_acm) { + if (use_obex) { for (cur_line = 0; cur_line < n_ports; cur_line++) { status = gserial_alloc_line(&tty_lines[cur_line]); if (status) @@ -245,9 +233,10 @@ static int __init gs_bind(struct usb_composite_dev *cdev) } else if (use_obex) status = usb_add_config(cdev, &serial_config_driver, serial_bind_obex_config); - else - status = usb_add_config(cdev, &serial_config_driver, - serial_bind_gser_config); + else { + status = serial_register_ports(cdev, &serial_config_driver, + "gser"); + } if (status < 0) goto fail; @@ -258,7 +247,7 @@ static int __init gs_bind(struct usb_composite_dev *cdev) fail: cur_line--; - while (cur_line >= 0 && !use_acm) + while (cur_line >= 0 && use_obex) gserial_free_line(tty_lines[cur_line--]); return status; } @@ -270,7 +259,7 @@ static int gs_unbind(struct usb_composite_dev *cdev) for (i = 0; i < n_ports; i++) { usb_put_function(f_serial[i]); usb_put_function_instance(fi_serial[i]); - if (!use_acm) + if (use_obex) gserial_free_line(tty_lines[i]); } return 0; -- cgit v0.10.2 From 9786b4561228099f579ad88912aa305812526ea1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 14 Mar 2013 16:14:26 +0100 Subject: usb: gadget: f_serial: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 465789b..49c3d14 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -258,50 +258,6 @@ fail: return status; } -#ifdef USB_FSERIAL_INCLUDED - -static void -gser_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - usb_free_all_descriptors(f); - kfree(func_to_gser(f)); -} - -/** - * gser_bind_config - add a generic serial function to a configuration - * @c: the configuration to support the serial instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -int __init gser_bind_config(struct usb_configuration *c, u8 port_num) -{ - struct f_gser *gser; - int status; - - /* allocate and initialize one new instance */ - gser = kzalloc(sizeof *gser, GFP_KERNEL); - if (!gser) - return -ENOMEM; - - gser->port_num = port_num; - - gser->port.func.name = "gser"; - gser->port.func.strings = gser_strings; - gser->port.func.bind = gser_bind; - gser->port.func.unbind = gser_old_unbind; - gser->port.func.set_alt = gser_set_alt; - gser->port.func.disable = gser_disable; - - status = usb_add_function(c, &gser->port.func); - if (status) - kfree(gser); - return status; -} - -#else - static void gser_free_inst(struct usb_function_instance *f) { struct f_serial_opts *opts; @@ -372,5 +328,3 @@ DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Al Borchers"); MODULE_AUTHOR("David Brownell"); - -#endif -- cgit v0.10.2 From 0b6a1e6ff7e62a1ab5a8924bffb12c17c183308a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 27 Mar 2013 09:12:03 +0100 Subject: usb: gadget: f_serial: add configfs support this patch implements the new configfs based interface on f_serial function driver. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 49c3d14..981113c 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -258,6 +258,59 @@ fail: return status; } +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_serial_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; + + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); + + return ret; +} + +static void serial_attr_release(struct config_item *item) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations serial_item_ops = { + .release = serial_attr_release, + .show_attribute = f_serial_attr_show, +}; + +static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_serial_port_num = + __CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show); + +static struct configfs_attribute *acm_attrs[] = { + &f_serial_port_num.attr, + NULL, +}; + +static struct config_item_type serial_func_type = { + .ct_item_ops = &serial_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + static void gser_free_inst(struct usb_function_instance *f) { struct f_serial_opts *opts; @@ -282,6 +335,8 @@ static struct usb_function_instance *gser_alloc_inst(void) kfree(opts); return ERR_PTR(ret); } + config_group_init_type_name(&opts->func_inst.group, "", + &serial_func_type); return &opts->func_inst; } -- cgit v0.10.2 From 1d8fc2518c1cddef0902b0a2c51946732a0982fc Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 21 Mar 2013 15:33:42 +0100 Subject: usb: gadget: f_obex: convert to new function interface with backward compatibility Converting f_obex to the new function interface requires converting the f_obex's function code and its users. This patch converts the f_obex.c to the new function interface. The file is now compiled into a separate usb_f_obex.ko module. The old function interface is provided by means of preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 93217fc..a5f67e1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -508,6 +508,9 @@ config USB_U_SERIAL config USB_F_SERIAL tristate +config USB_F_OBEX + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 34bb49a..6afd166 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -43,6 +43,8 @@ obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o usb_f_serial-y := f_serial.o obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o +usb_f_obex-y := f_obex.o +obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o # # USB gadget drivers diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 36a0045..439b666 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -72,7 +72,7 @@ static struct usb_gadget_strings *obex_strings[] = { /*-------------------------------------------------------------------------*/ -static struct usb_interface_descriptor obex_control_intf __initdata = { +static struct usb_interface_descriptor obex_control_intf = { .bLength = sizeof(obex_control_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, @@ -83,7 +83,7 @@ static struct usb_interface_descriptor obex_control_intf __initdata = { .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, }; -static struct usb_interface_descriptor obex_data_nop_intf __initdata = { +static struct usb_interface_descriptor obex_data_nop_intf = { .bLength = sizeof(obex_data_nop_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 1, @@ -93,7 +93,7 @@ static struct usb_interface_descriptor obex_data_nop_intf __initdata = { .bInterfaceClass = USB_CLASS_CDC_DATA, }; -static struct usb_interface_descriptor obex_data_intf __initdata = { +static struct usb_interface_descriptor obex_data_intf = { .bLength = sizeof(obex_data_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 2, @@ -103,14 +103,14 @@ static struct usb_interface_descriptor obex_data_intf __initdata = { .bInterfaceClass = USB_CLASS_CDC_DATA, }; -static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = { +static struct usb_cdc_header_desc obex_cdc_header_desc = { .bLength = sizeof(obex_cdc_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, .bcdCDC = cpu_to_le16(0x0120), }; -static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { +static struct usb_cdc_union_desc obex_cdc_union_desc = { .bLength = sizeof(obex_cdc_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -118,7 +118,7 @@ static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { .bSlaveInterface0 = 2, }; -static struct usb_cdc_obex_desc obex_desc __initdata = { +static struct usb_cdc_obex_desc obex_desc = { .bLength = sizeof(obex_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_OBEX_TYPE, @@ -127,7 +127,7 @@ static struct usb_cdc_obex_desc obex_desc __initdata = { /* High-Speed Support */ -static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { +static struct usb_endpoint_descriptor obex_hs_ep_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -136,7 +136,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { +static struct usb_endpoint_descriptor obex_hs_ep_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -145,7 +145,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *hs_function[] __initdata = { +static struct usb_descriptor_header *hs_function[] = { (struct usb_descriptor_header *) &obex_control_intf, (struct usb_descriptor_header *) &obex_cdc_header_desc, (struct usb_descriptor_header *) &obex_desc, @@ -160,7 +160,7 @@ static struct usb_descriptor_header *hs_function[] __initdata = { /* Full-Speed Support */ -static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { +static struct usb_endpoint_descriptor obex_fs_ep_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -168,7 +168,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { +static struct usb_endpoint_descriptor obex_fs_ep_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -176,7 +176,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *fs_function[] __initdata = { +static struct usb_descriptor_header *fs_function[] = { (struct usb_descriptor_header *) &obex_control_intf, (struct usb_descriptor_header *) &obex_cdc_header_desc, (struct usb_descriptor_header *) &obex_desc, @@ -290,14 +290,43 @@ static void obex_disconnect(struct gserial *g) /*-------------------------------------------------------------------------*/ -static int __init -obex_bind(struct usb_configuration *c, struct usb_function *f) +/* Some controllers can't support CDC OBEX ... */ +static inline bool can_support_obex(struct usb_configuration *c) +{ + /* Since the first interface is a NOP, we can ignore the + * issue of multi-interface support on most controllers. + * + * Altsettings are mandatory, however... + */ + if (!gadget_supports_altsettings(c->cdev->gadget)) + return false; + + /* everything else is *probably* fine ... */ + return true; +} + +static int obex_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_obex *obex = func_to_obex(f); int status; struct usb_ep *ep; + if (!can_support_obex(c)) + return -EINVAL; + + if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { + status = usb_string_ids_tab(c->cdev, obex_string_defs); + if (status < 0) + return status; + obex_control_intf.iInterface = + obex_string_defs[OBEX_CTRL_IDX].id; + + status = obex_string_defs[OBEX_DATA_IDX].id; + obex_data_nop_intf.iInterface = status; + obex_data_intf.iInterface = status; + } + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); @@ -376,29 +405,16 @@ fail: return status; } +#ifdef USBF_OBEX_INCLUDED + static void -obex_unbind(struct usb_configuration *c, struct usb_function *f) +obex_old_unbind(struct usb_configuration *c, struct usb_function *f) { obex_string_defs[OBEX_CTRL_IDX].id = 0; usb_free_all_descriptors(f); kfree(func_to_obex(f)); } -/* Some controllers can't support CDC OBEX ... */ -static inline bool can_support_obex(struct usb_configuration *c) -{ - /* Since the first interface is a NOP, we can ignore the - * issue of multi-interface support on most controllers. - * - * Altsettings are mandatory, however... - */ - if (!gadget_supports_altsettings(c->cdev->gadget)) - return false; - - /* everything else is *probably* fine ... */ - return true; -} - /** * obex_bind_config - add a CDC OBEX function to a configuration * @c: the configuration to support the CDC OBEX instance @@ -412,21 +428,6 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) struct f_obex *obex; int status; - if (!can_support_obex(c)) - return -EINVAL; - - if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { - status = usb_string_ids_tab(c->cdev, obex_string_defs); - if (status < 0) - return status; - obex_control_intf.iInterface = - obex_string_defs[OBEX_CTRL_IDX].id; - - status = obex_string_defs[OBEX_DATA_IDX].id; - obex_data_nop_intf.iInterface = status; - obex_data_intf.iInterface = status; - } - /* allocate and initialize one new instance */ obex = kzalloc(sizeof *obex, GFP_KERNEL); if (!obex) @@ -441,7 +442,7 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) obex->port.func.strings = obex_strings; /* descriptors are per-instance copies */ obex->port.func.bind = obex_bind; - obex->port.func.unbind = obex_unbind; + obex->port.func.unbind = obex_old_unbind; obex->port.func.set_alt = obex_set_alt; obex->port.func.get_alt = obex_get_alt; obex->port.func.disable = obex_disable; @@ -453,5 +454,83 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) return status; } +#else + +static void obex_free_inst(struct usb_function_instance *f) +{ + struct f_serial_opts *opts; + + opts = container_of(f, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); + kfree(opts); +} + +static struct usb_function_instance *obex_alloc_inst(void) +{ + struct f_serial_opts *opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = obex_free_inst; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); + } + + return &opts->func_inst; +} + +static void obex_free(struct usb_function *f) +{ + struct f_obex *obex; + + obex = func_to_obex(f); + kfree(obex); +} + +static void obex_unbind(struct usb_configuration *c, struct usb_function *f) +{ + obex_string_defs[OBEX_CTRL_IDX].id = 0; + usb_free_all_descriptors(f); +} + +struct usb_function *obex_alloc(struct usb_function_instance *fi) +{ + struct f_obex *obex; + struct f_serial_opts *opts; + + /* allocate and initialize one new instance */ + obex = kzalloc(sizeof(*obex), GFP_KERNEL); + if (!obex) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_serial_opts, func_inst); + + obex->port_num = opts->port_num; + + obex->port.connect = obex_connect; + obex->port.disconnect = obex_disconnect; + + obex->port.func.name = "obex"; + obex->port.func.strings = obex_strings; + /* descriptors are per-instance copies */ + obex->port.func.bind = obex_bind; + obex->port.func.unbind = obex_unbind; + obex->port.func.set_alt = obex_set_alt; + obex->port.func.get_alt = obex_get_alt; + obex->port.func.disable = obex_disable; + obex->port.func.free_func = obex_free; + + return &obex->port.func; +} + +DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); + +#endif + MODULE_AUTHOR("Felipe Balbi"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index f829a80..eebdd0e0 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,7 +37,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "f_ecm.c" +#define USBF_OBEX_INCLUDED #include "f_obex.c" #include "f_phonet.c" #include "u_ether.c" diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 9d215c4..664da64 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -36,6 +36,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ +#define USBF_OBEX_INCLUDED #include "f_obex.c" /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From d1412794b33ab0cb3dcc4c820e9595354b7e6e59 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 21 Mar 2013 09:22:30 +0100 Subject: usb: gadget: serial: convert to new interface of f_obex f_obex is now a self-contained module. We need to teach serial.c about it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a5f67e1..86d5d80 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -766,6 +766,7 @@ config USB_G_SERIAL select USB_U_SERIAL select USB_F_ACM select USB_F_SERIAL + select USB_F_OBEX select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 664da64..1f5f978 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -28,18 +29,6 @@ #define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR /*-------------------------------------------------------------------------*/ - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#define USBF_OBEX_INCLUDED -#include "f_obex.c" - -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); /* Thanks to NetChip Technologies for donating this product ID. @@ -126,17 +115,6 @@ module_param(n_ports, uint, 0); MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ -static unsigned char tty_lines[MAX_U_SERIAL_PORTS]; - -static int __init serial_bind_obex_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) - status = obex_bind_config(c, tty_lines[i]); - return status; -} static struct usb_configuration serial_config_driver = { /* .label = f(use_acm) */ @@ -199,15 +177,6 @@ out: static int __init gs_bind(struct usb_composite_dev *cdev) { int status; - int cur_line = 0; - - if (use_obex) { - for (cur_line = 0; cur_line < n_ports; cur_line++) { - status = gserial_alloc_line(&tty_lines[cur_line]); - if (status) - goto fail; - } - } /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. @@ -232,8 +201,8 @@ static int __init gs_bind(struct usb_composite_dev *cdev) "acm"); usb_ep_autoconfig_reset(cdev->gadget); } else if (use_obex) - status = usb_add_config(cdev, &serial_config_driver, - serial_bind_obex_config); + status = serial_register_ports(cdev, &serial_config_driver, + "obex"); else { status = serial_register_ports(cdev, &serial_config_driver, "gser"); @@ -247,9 +216,6 @@ static int __init gs_bind(struct usb_composite_dev *cdev) return 0; fail: - cur_line--; - while (cur_line >= 0 && use_obex) - gserial_free_line(tty_lines[cur_line--]); return status; } @@ -260,8 +226,6 @@ static int gs_unbind(struct usb_composite_dev *cdev) for (i = 0; i < n_ports; i++) { usb_put_function(f_serial[i]); usb_put_function_instance(fi_serial[i]); - if (use_obex) - gserial_free_line(tty_lines[i]); } return 0; } -- cgit v0.10.2 From ecfd3f7bb3d5dceb8e7ad1c117303c774cb90234 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 27 Mar 2013 12:13:25 +0100 Subject: usb: gadget: f_obex: add configfs support f_obex learns about our new configfs-based interface, which will allow gadgets to be bound to controllers through userland. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 439b666..29a348a 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -456,6 +456,59 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) #else +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_obex_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; + + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); + + return ret; +} + +static void obex_attr_release(struct config_item *item) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations obex_item_ops = { + .release = obex_attr_release, + .show_attribute = f_obex_attr_show, +}; + +static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_obex_port_num = + __CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show); + +static struct configfs_attribute *acm_attrs[] = { + &f_obex_port_num.attr, + NULL, +}; + +static struct config_item_type obex_func_type = { + .ct_item_ops = &obex_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + static void obex_free_inst(struct usb_function_instance *f) { struct f_serial_opts *opts; @@ -480,6 +533,8 @@ static struct usb_function_instance *obex_alloc_inst(void) kfree(opts); return ERR_PTR(ret); } + config_group_init_type_name(&opts->func_inst.group, "", + &obex_func_type); return &opts->func_inst; } -- cgit v0.10.2 From 88b1c78db32782041d9548e3c4ab405f9e9eab59 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 16:02:25 +0200 Subject: usb: phy: ab8500-usb: check regulator_enable return value Since regulator_enable() is going to be marked as __must_check in the next merge window, always check regulator_enable() return value and print a warning if it fails. Cc: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 749614c..4acef26 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -167,7 +167,9 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) { int ret, volt; - regulator_enable(ab->v_ape); + ret = regulator_enable(ab->v_ape); + if (ret) + dev_err(ab->dev, "Failed to enable v-ape\n"); if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { ab->saved_v_ulpi = regulator_get_voltage(ab->v_ulpi); @@ -185,7 +187,9 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) ret); } - regulator_enable(ab->v_ulpi); + ret = regulator_enable(ab->v_ulpi); + if (ret) + dev_err(ab->dev, "Failed to enable vddulpivio18\n"); if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { volt = regulator_get_voltage(ab->v_ulpi); @@ -194,7 +198,9 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) volt); } - regulator_enable(ab->v_musb); + ret = regulator_enable(ab->v_musb); + if (ret) + dev_err(ab->dev, "Failed to enable musb_1v8\n"); } static void ab8500_usb_regulator_disable(struct ab8500_usb *ab) -- cgit v0.10.2 From 9ec602ecb199653a16e1eb909551b7ba77d79922 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 16:02:26 +0200 Subject: usb: phy: twl4030-usb: check regulator_enable return value Since regulator_enable() is going to be marked as __must_check in the next merge window, always check regulator_enable() return value and print a warning if it fails. Reviewed-by: Kalle Jokiniemi Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl4030-usb.c b/drivers/usb/phy/phy-twl4030-usb.c index 3f9858f..13e17ae 100644 --- a/drivers/usb/phy/phy-twl4030-usb.c +++ b/drivers/usb/phy/phy-twl4030-usb.c @@ -384,9 +384,17 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on) static void twl4030_phy_power(struct twl4030_usb *twl, int on) { + int ret; + if (on) { - regulator_enable(twl->usb3v1); - regulator_enable(twl->usb1v8); + ret = regulator_enable(twl->usb3v1); + if (ret) + dev_err(twl->dev, "Failed to enable usb3v1\n"); + + ret = regulator_enable(twl->usb1v8); + if (ret) + dev_err(twl->dev, "Failed to enable usb1v8\n"); + /* * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP * in twl4030) resets the VUSB_DEDICATED2 register. This reset @@ -395,7 +403,11 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on) * is re-activated. This ensures that VUSB3V1 is really active. */ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); - regulator_enable(twl->usb1v5); + + ret = regulator_enable(twl->usb1v5); + if (ret) + dev_err(twl->dev, "Failed to enable usb1v5\n"); + __twl4030_phy_power(twl, 1); twl4030_usb_write(twl, PHY_CLK_CTRL, twl4030_usb_read(twl, PHY_CLK_CTRL) | -- cgit v0.10.2 From bb54542cfaac0e7949925b774b04b7ea069b80d1 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 3 Apr 2013 16:02:27 +0200 Subject: usb: phy: twl6030-usb: check regulator_enable return value Since regulator_enable() is going to be marked as __must_check in the next merge window, always check regulator_enable() return value and print a warning if it fails. Cc: Hema HK Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 8cd6cf4..e841474 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -211,6 +211,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) struct twl6030_usb *twl = _twl; enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 vbus_state, hw_state; + int ret; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -218,7 +219,10 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) CONTROLLER_STAT1); if (!(hw_state & STS_USB_ID)) { if (vbus_state & VBUS_DET) { - regulator_enable(twl->usb3v3); + ret = regulator_enable(twl->usb3v3); + if (ret) + dev_err(twl->dev, "Failed to enable usb3v3\n"); + twl->asleep = 1; status = OMAP_MUSB_VBUS_VALID; twl->linkstat = status; @@ -245,12 +249,15 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) struct twl6030_usb *twl = _twl; enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 hw_state; + int ret; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); if (hw_state & STS_USB_ID) { + ret = regulator_enable(twl->usb3v3); + if (ret) + dev_err(twl->dev, "Failed to enable usb3v3\n"); - regulator_enable(twl->usb3v3); twl->asleep = 1; twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); -- cgit v0.10.2 From 6a5d6943fe0e4b1e4c43c3aa1919360f68ae2f9a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 28 Mar 2013 18:48:28 +0000 Subject: xhci: Use ilog2() rather than __ffs() for calculating SEGMENT_SHIFT Use ilog2() rather than __ffs() for calculating SEGMENT_SHIFT as ilog2() can be worked out at compile time, whereas __ffs() must be calculated at runtime. Signed-off-by: David Howells cc: Sarah Sharp cc: Greg Kroah-Hartman cc: linux-usb@vger.kernel.org Signed-off-by: Sarah Sharp diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6358271..ca550dd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1239,7 +1239,7 @@ union xhci_trb { /* Allow two commands + a link TRB, along with any reserved command TRBs */ #define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) #define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) -#define SEGMENT_SHIFT (__ffs(SEGMENT_SIZE)) +#define SEGMENT_SHIFT (ilog2(SEGMENT_SIZE)) /* TRB buffer pointers can't cross 64KB boundaries */ #define TRB_MAX_BUFF_SHIFT 16 #define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) -- cgit v0.10.2 From eb8ccd2b481123e5fe0872a83e07ea8a6e2c4026 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 28 Mar 2013 18:48:35 +0000 Subject: xhci: Rename SEGMENT_SIZE and SEGMENT_SHIFT as the former is used in a.out.h Rename SEGMENT_SIZE and SEGMENT_SHIFT as the former is used in a.out.h. Signed-off-by: David Howells cc: Sarah Sharp cc: Greg Kroah-Hartman cc: linux-usb@vger.kernel.org Signed-off-by: Sarah Sharp diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6dc238c..965b539 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -51,7 +51,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, return NULL; } - memset(seg->trbs, 0, SEGMENT_SIZE); + memset(seg->trbs, 0, TRB_SEGMENT_SIZE); /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ if (cycle_state == 0) { for (i = 0; i < TRBS_PER_SEGMENT; i++) @@ -467,7 +467,7 @@ struct xhci_ring *xhci_dma_to_transfer_ring( { if (ep->ep_state & EP_HAS_STREAMS) return radix_tree_lookup(&ep->stream_info->trb_address_map, - address >> SEGMENT_SHIFT); + address >> TRB_SEGMENT_SHIFT); return ep->ring; } @@ -478,7 +478,7 @@ static struct xhci_ring *dma_to_stream_ring( u64 address) { return radix_tree_lookup(&stream_info->trb_address_map, - address >> SEGMENT_SHIFT); + address >> TRB_SEGMENT_SHIFT); } #endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */ @@ -514,7 +514,7 @@ static int xhci_test_radix_tree(struct xhci_hcd *xhci, cur_ring = stream_info->stream_rings[cur_stream]; for (addr = cur_ring->first_seg->dma; - addr < cur_ring->first_seg->dma + SEGMENT_SIZE; + addr < cur_ring->first_seg->dma + TRB_SEGMENT_SIZE; addr += trb_size) { mapped_ring = dma_to_stream_ring(stream_info, addr); if (cur_ring != mapped_ring) { @@ -662,7 +662,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, cur_stream, (unsigned long long) addr); key = (unsigned long) - (cur_ring->first_seg->dma >> SEGMENT_SHIFT); + (cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT); ret = radix_tree_insert(&stream_info->trb_address_map, key, cur_ring); if (ret) { @@ -693,7 +693,7 @@ cleanup_rings: if (cur_ring) { addr = cur_ring->first_seg->dma; radix_tree_delete(&stream_info->trb_address_map, - addr >> SEGMENT_SHIFT); + addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } @@ -764,7 +764,7 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, if (cur_ring) { addr = cur_ring->first_seg->dma; radix_tree_delete(&stream_info->trb_address_map, - addr >> SEGMENT_SHIFT); + addr >> TRB_SEGMENT_SHIFT); xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; } @@ -2305,7 +2305,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * so we pick the greater alignment need. */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, - SEGMENT_SIZE, 64, xhci->page_size); + TRB_SEGMENT_SIZE, 64, xhci->page_size); /* See Table 46 and Note on Figure 55 */ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ca550dd..29c978e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1238,8 +1238,8 @@ union xhci_trb { #define TRBS_PER_SEGMENT 64 /* Allow two commands + a link TRB, along with any reserved command TRBs */ #define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) -#define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) -#define SEGMENT_SHIFT (ilog2(SEGMENT_SIZE)) +#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT*16) +#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE)) /* TRB buffer pointers can't cross 64KB boundaries */ #define TRB_MAX_BUFF_SHIFT 16 #define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) -- cgit v0.10.2 From 9b192de60b5a584ee4ed967fb6758773c75e4643 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 3 Apr 2013 21:01:44 +0300 Subject: usb: gadget: nokia: include f_ecm.c recent changes to gadget framework have exposed a flaw in nokia.c where it uses f_ecm.c but it wasn't including it like other gadget drivers. This is a temporary patch until all other function drivers are converted to new API where each function driver becomes a module and binding is done through configfs. Solves a linking error when building g_nokia.ko Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index eebdd0e0..3b344b4 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -38,6 +38,7 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #define USBF_OBEX_INCLUDED +#include "f_ecm.c" #include "f_obex.c" #include "f_phonet.c" #include "u_ether.c" -- cgit v0.10.2 From e7d3b6e22c871ba36d052ca99bc8ceca4d546a60 Mon Sep 17 00:00:00 2001 From: Ben Jencks Date: Tue, 2 Apr 2013 00:35:08 -0400 Subject: usb/misc/appledisplay: Add 24" LED Cinema display Add the Apple 24" LED Cinema display to the supported devices. Signed-off-by: Ben Jencks Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 0fc6e5f..ba6a5d6 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -63,6 +63,7 @@ static const struct usb_device_id appledisplay_table[] = { { APPLEDISPLAY_DEVICE(0x9219) }, { APPLEDISPLAY_DEVICE(0x921c) }, { APPLEDISPLAY_DEVICE(0x921d) }, + { APPLEDISPLAY_DEVICE(0x9236) }, /* Terminating entry */ { } -- cgit v0.10.2 From e6604a7fd71f9bd2890e07800e191167d7f5751b Mon Sep 17 00:00:00 2001 From: Christian Engelmayer Date: Wed, 3 Apr 2013 12:18:51 +0200 Subject: EHCI: Quirk flag for port power handling on overcurrent. Commit 756aa6b3d536afe85e151138cb03a293998887b3 (ehci-hub: improved over-current recovery) added port power cycling on overcurrent indications as needed by the MPC8349 USB controller after resolving of the overcurrent situation in order to have the host state machine assert the correct port status again. Commit 81463c1d707186adbbe534016cd1249edeab0dac (EHCI: only power off port if over-current is active) solved a thus resulting issue of endless overcurrent changes in combination with the MAX4967 USB power supply chip that signals overcurrent when power is not enabled by only powering off a port if the overcurrent is currently active. Added quirks flag need_oc_pp_cycle in order to specify the needed behaviour as there is no common behaviour that can comply with both requirements. Activated the quirks handling for Freescale 83xx based boards. Signed-off-by: Christian Engelmayer Acked-by: Alan Stern Acked-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index d81d2fc..3be3df2 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -370,6 +370,15 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; +#ifdef CONFIG_PPC_83xx + /* + * Deal with MPC834X that need port power to be cycled after the power + * fault condition is removed. Otherwise the state machine does not + * reflect PORTSC[CSC] correctly. + */ + ehci->need_oc_pp_cycle = 1; +#endif + hcd->has_tt = 1; retval = ehci_setup(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 7b04ca9..9ab4a4d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -840,7 +840,8 @@ static int ehci_hub_control ( * power switching; they're allowed to just limit the * current. khubd will turn the power back on. */ - if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) { + if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) + && HCS_PPC(ehci->hcs_params)) { ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_POWER), status_reg); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e666999..7c978b2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -199,6 +199,7 @@ struct ehci_hcd { /* one per controller */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ + unsigned need_oc_pp_cycle:1; /* MPC834X port power */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) -- cgit v0.10.2 From eb5369edca4f4307a6035dc6f6813698b0762906 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Wed, 3 Apr 2013 16:11:12 +0530 Subject: usb: host: tegra: Reset Tegra USB controller before init To clear any configurations made by U-Boot on Tegra USB controller, reset it before init in probe. Signed-off-by: Venu Byravarasu Acked-by: Alan Stern reviewed-by: Stephen Warren Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 568aecc..83d190a 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -28,6 +28,7 @@ #include #include #include +#include #define TEGRA_USB_BASE 0xC5000000 #define TEGRA_USB2_BASE 0xC5004000 @@ -691,6 +692,10 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (err) goto fail_clk; + tegra_periph_reset_assert(tegra->clk); + udelay(1); + tegra_periph_reset_deassert(tegra->clk); + tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, "nvidia,needs-double-reset"); -- cgit v0.10.2 From 369a9a9d2af702bac3ccd2ade86ddcb2594eb3b1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 3 Apr 2013 21:57:57 +0200 Subject: usb: host: ehci-tegra: Fix oops in error cleanup The cleanup path checks whether the transceiver was properly initialized using IS_ERR(). However it can also happen that the cleanup path is run before the transceiver was initialized (or the operating mode isn't set to TEGRA_USB_OTG) and is therefore NULL. Add a separate label for error unwinding and initialize the transceiver field to ERR_PTR(-ENODEV) when the operating mode isn't TEGRA_USB_OTG to allow for consistent checking. Signed-off-by: Thierry Reding Acked-by: Stephen Warren Acked-by: Venu Byravarasu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 83d190a..4f3cfb8 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -760,7 +760,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) err = usb_phy_set_suspend(hcd->phy, 0); if (err) { dev_err(&pdev->dev, "Failed to power on the phy\n"); - goto fail; + goto fail_phy; } tegra->host_resumed = 1; @@ -770,7 +770,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (!irq) { dev_err(&pdev->dev, "Failed to get IRQ\n"); err = -ENODEV; - goto fail; + goto fail_phy; } #ifdef CONFIG_USB_OTG_UTILS @@ -779,6 +779,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, &hcd->self); + } else { + tegra->transceiver = ERR_PTR(-ENODEV); } #endif @@ -803,6 +805,7 @@ fail: if (!IS_ERR_OR_NULL(tegra->transceiver)) otg_set_host(tegra->transceiver->otg, NULL); #endif +fail_phy: usb_phy_shutdown(hcd->phy); fail_io: clk_disable_unprepare(tegra->clk); -- cgit v0.10.2 From a9c174302b1590ef3ead485d804a303c5f89174b Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Thu, 4 Apr 2013 13:13:46 +0300 Subject: usb: chipidea: udc: fix memory access of shared memory on armv5 machines The udc uses an shared dma memory space between hard and software. This memory layout is described in ci13xxx_qh and ci13xxx_td which are marked with the attribute ((packed)). The compiler currently does not know about the alignment of the memory layout, and will create strb and ldrb operations. The Datasheet of the synopsys core describes, that some operations on the mapped memory need to be atomic double word operations. I.e. the next pointer addressing in the qhead, as otherwise the hardware will read wrong data and totally stuck. This is also possible while working with the current active td queue, and preparing the td->ptr.next in software while the hardware is still working with the current active td which is supposed to be changed: writeb(0xde, &td->ptr.next + 0x0); /* strb */ writeb(0xad, &td->ptr.next + 0x1); /* strb */ <----- hardware reads value of td->ptr.next and get stuck! writeb(0xbe, &td->ptr.next + 0x2); /* strb */ writeb(0xef, &td->ptr.next + 0x3); /* strb */ This appeares on armv5 machines where the hardware does not support unaligned 32bit operations. This patch adds the attribute ((aligned(4))) to the structures to tell the compiler to use 32bit operations. It also adds an wmb() for the prepared TD data before it gets enqueued into the qhead. Cc: stable # v3.5 Signed-off-by: Michael Grzeschik Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e502e48..b4cac44 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -435,6 +435,8 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->ptr->page[i] = cpu_to_le32(page); } + wmb(); + if (!list_empty(&mEp->qh.queue)) { struct ci13xxx_req *mReqPrev; int n = hw_ep_bit(mEp->num, mEp->dir); diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 4ff2384d..d12e8b5 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -40,7 +40,7 @@ struct ci13xxx_td { #define TD_CURR_OFFSET (0x0FFFUL << 0) #define TD_FRAME_NUM (0x07FFUL << 0) #define TD_RESERVED_MASK (0x0FFFUL << 0) -} __attribute__ ((packed)); +} __attribute__ ((packed, aligned(4))); /* DMA layout of queue heads */ struct ci13xxx_qh { @@ -57,7 +57,7 @@ struct ci13xxx_qh { /* 9 */ u32 RESERVED; struct usb_ctrlrequest setup; -} __attribute__ ((packed)); +} __attribute__ ((packed, aligned(4))); /** * struct ci13xxx_req - usb request representation -- cgit v0.10.2 From 7ca2cd291fd84ae499390f227a255ccba2780a81 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Thu, 4 Apr 2013 13:13:47 +0300 Subject: usb: chipidea: udc: fix memory leak in _ep_nuke In hardware_enqueue code adds one extra td with dma_pool_alloc if mReq->req.zero is true. When _ep_nuke will be called for that endpoint, dma_pool_free will not be called to free that memory again. That patch fixes this. Cc: stable # v3.5 Signed-off-by: Michael Grzeschik Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index b4cac44..3d90e61 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -540,6 +540,12 @@ __acquires(mEp->lock) struct ci13xxx_req *mReq = \ list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue); + + if (mReq->zptr) { + dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma); + mReq->zptr = NULL; + } + list_del_init(&mReq->queue); mReq->req.status = -ESHUTDOWN; -- cgit v0.10.2 From 6a3ae8412f9e9cee0e8647954f4f7f2c50664ca2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 5 Apr 2013 08:42:41 +0300 Subject: USB: keyspan: pull in one indent level We can remove the "if (urb->actual_length) {" check because checking for "while (i < urb->actual_length) {" is sufficient. This lets us pull the code in one indent level. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 6abe8a4..3d92394 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -726,45 +726,44 @@ static void usa49wg_indat_callback(struct urb *urb) i = 0; len = 0; - if (urb->actual_length) { - while (i < urb->actual_length) { + while (i < urb->actual_length) { - /* Check port number from message*/ - if (data[i] >= serial->num_ports) { - dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", - __func__, data[i]); - return; - } - port = serial->port[data[i++]]; - len = data[i++]; + /* Check port number from message */ + if (data[i] >= serial->num_ports) { + dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", + __func__, data[i]); + return; + } + port = serial->port[data[i++]]; + len = data[i++]; - /* 0x80 bit is error flag */ - if ((data[i] & 0x80) == 0) { - /* no error on any byte */ - i++; - for (x = 1; x < len ; ++x) - tty_insert_flip_char(&port->port, - data[i++], 0); - } else { - /* - * some bytes had errors, every byte has status - */ - for (x = 0; x + 1 < len; x += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; - /* XXX should handle break (0x10) */ - tty_insert_flip_char(&port->port, - data[i+1], flag); - i += 2; - } + /* 0x80 bit is error flag */ + if ((data[i] & 0x80) == 0) { + /* no error on any byte */ + i++; + for (x = 1; x < len ; ++x) + tty_insert_flip_char(&port->port, + data[i++], 0); + } else { + /* + * some bytes had errors, every byte has status + */ + for (x = 0; x + 1 < len; x += 2) { + int stat = data[i], flag = 0; + + if (stat & RXERROR_OVERRUN) + flag |= TTY_OVERRUN; + if (stat & RXERROR_FRAMING) + flag |= TTY_FRAME; + if (stat & RXERROR_PARITY) + flag |= TTY_PARITY; + /* XXX should handle break (0x10) */ + tty_insert_flip_char(&port->port, data[i+1], + flag); + i += 2; } - tty_flip_buffer_push(&port->port); } + tty_flip_buffer_push(&port->port); } /* Resubmit urb so we continue receiving */ -- cgit v0.10.2 From 01a60e76b6392547ad3dca3ac05b9c886fa5da45 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 5 Apr 2013 08:43:20 +0300 Subject: USB: keyspan: add a sanity test on "len" "len" comes from the USB transfer and it's probably correct. The thing is that we already have similar checks like: if (data[i] >= serial->num_ports) { So adding a sanity test here matches the rest of the code and is a good idea. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 3d92394..025310b 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -741,14 +741,15 @@ static void usa49wg_indat_callback(struct urb *urb) if ((data[i] & 0x80) == 0) { /* no error on any byte */ i++; - for (x = 1; x < len ; ++x) + for (x = 1; x < len && i < urb->actual_length; ++x) tty_insert_flip_char(&port->port, data[i++], 0); } else { /* * some bytes had errors, every byte has status */ - for (x = 0; x + 1 < len; x += 2) { + for (x = 0; x + 1 < len && + i + 1 < urb->actual_length; x += 2) { int stat = data[i], flag = 0; if (stat & RXERROR_OVERRUN) -- cgit v0.10.2 From ae3759c2573031f1306496c6f9f32f20e86f03aa Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 4 Apr 2013 22:41:01 +0200 Subject: USB: io_ti, stop dereferencing potential NULL tty_port_tty_get might return a tty which is NULL. But it is dereferenced unconditionally in edge_send. Stop dereferencing that by sending usb_serial_port pointer around. Signed-off-by: Jiri Slaby Cc: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 0ccc422..f2a1601 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -206,7 +206,7 @@ static int restart_read(struct edgeport_port *edge_port); static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); -static void edge_send(struct tty_struct *tty); +static void edge_send(struct usb_serial_port *port, struct tty_struct *tty); /* sysfs attributes */ static int edge_create_sysfs_attrs(struct usb_serial_port *port); @@ -1712,7 +1712,7 @@ static void edge_bulk_out_callback(struct urb *urb) /* send any buffered data */ tty = tty_port_tty_get(&port->port); - edge_send(tty); + edge_send(port, tty); tty_kref_put(tty); } @@ -1940,14 +1940,13 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, count = kfifo_in_locked(&edge_port->write_fifo, data, count, &edge_port->ep_lock); - edge_send(tty); + edge_send(port, tty); return count; } -static void edge_send(struct tty_struct *tty) +static void edge_send(struct usb_serial_port *port, struct tty_struct *tty) { - struct usb_serial_port *port = tty->driver_data; int count, result; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned long flags; -- cgit v0.10.2 From 58b1d7999e15e61f314c2ff746ffb9dd8e07eba6 Mon Sep 17 00:00:00 2001 From: Tony Camuso Date: Fri, 5 Apr 2013 14:27:07 -0400 Subject: xhci - clarify compliance mode debug messages There are no functional changes in this patch. However, because the compliance mode timer can be deleted in more than one function, it seemed expedient to include the function name in the debug strings. Also limited the use of capitals to the first word in the compliance mode debug messages, except after a function name where all words start with lower case, in keeping with the style prevalent elsewhere in xhci.c. Signed-off-by: Tony Camuso Signed-off-by: Sarah Sharp diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5156b72..b4aa79d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -417,9 +417,9 @@ static void compliance_mode_recovery(unsigned long arg) * Compliance Mode Detected. Letting USB Core * handle the Warm Reset */ - xhci_dbg(xhci, "Compliance Mode Detected->Port %d!\n", + xhci_dbg(xhci, "Compliance mode detected->port %d\n", i + 1); - xhci_dbg(xhci, "Attempting Recovery routine!\n"); + xhci_dbg(xhci, "Attempting compliance mode recovery\n"); hcd = xhci->shared_hcd; if (hcd->state == HC_STATE_SUSPENDED) @@ -457,7 +457,7 @@ static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci) set_timer_slack(&xhci->comp_mode_recovery_timer, msecs_to_jiffies(COMP_MODE_RCVRY_MSECS)); add_timer(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "Compliance Mode Recovery Timer Initialized.\n"); + xhci_dbg(xhci, "Compliance mode recovery timer initialized\n"); } /* @@ -733,8 +733,11 @@ void xhci_stop(struct usb_hcd *hcd) /* Deleting Compliance Mode Recovery Timer */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && - (!(xhci_all_ports_seen_u0(xhci)))) + (!(xhci_all_ports_seen_u0(xhci)))) { del_timer_sync(&xhci->comp_mode_recovery_timer); + xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n", + __func__); + } if (xhci->quirks & XHCI_AMD_PLL_FIX) usb_amd_dev_put(); @@ -930,7 +933,8 @@ int xhci_suspend(struct xhci_hcd *xhci) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { del_timer_sync(&xhci->comp_mode_recovery_timer); - xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted!\n"); + xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n", + __func__); } /* step 5: remove core well power */ -- cgit v0.10.2 From 197c58f624bfb98d3f7f381e57b5d09b2360266f Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Mon, 8 Apr 2013 12:20:49 +0800 Subject: USB: fix an incorrect table index in comment Signed-off-by: Huang Rui Signed-off-by: Greg Kroah-Hartman diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index f738e25..aa33fd1 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -138,7 +138,7 @@ /* * New Feature Selectors as added by USB 3.0 - * See USB 3.0 spec Table 9-6 + * See USB 3.0 spec Table 9-7 */ #define USB_DEVICE_U1_ENABLE 48 /* dev may initiate U1 transition */ #define USB_DEVICE_U2_ENABLE 49 /* dev may initiate U2 transition */ @@ -147,7 +147,7 @@ #define USB_INTR_FUNC_SUSPEND_OPT_MASK 0xFF00 /* - * Suspend Options, Table 9-7 USB 3.0 spec + * Suspend Options, Table 9-8 USB 3.0 spec */ #define USB_INTRF_FUNC_SUSPEND_LP (1 << (8 + 0)) #define USB_INTRF_FUNC_SUSPEND_RW (1 << (8 + 1)) -- cgit v0.10.2 From 75bfe23a36fc47dd95c5216b48caaf1fa9c55a65 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Sun, 7 Apr 2013 14:11:47 -0700 Subject: usb, gadget: use appropriate warning accessors Use the appropriate WARN() and WARN_ON() accessors to avoid a build error when CONFIG_BUG=n: drivers/usb/gadget/configfs.c: In function 'config_usb_cfg_unlink': drivers/usb/gadget/configfs.c:442:2: error: implicit declaration of function '__WARN_printf' drivers/usb/gadget/configfs.c: In function 'configfs_do_nothing': drivers/usb/gadget/configfs.c:733:2: error: implicit declaration of function '__WARN' Reported-by: Randy Dunlap Signed-off-by: David Rientjes Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index a34633a..3d5cfc9 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -439,7 +439,7 @@ static int config_usb_cfg_unlink( } } mutex_unlock(&gi->lock); - __WARN_printf("Unable to locate function to unbind\n"); + WARN(1, "Unable to locate function to unbind\n"); return 0; } @@ -730,7 +730,7 @@ USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); static int configfs_do_nothing(struct usb_composite_dev *cdev) { - __WARN(); + WARN_ON(1); return -EINVAL; } -- cgit v0.10.2 From 58f8b6c4fa5a13cb2ddb400e26e9e65766d71e38 Mon Sep 17 00:00:00 2001 From: Stefani Seibold Date: Sun, 7 Apr 2013 12:08:55 +0200 Subject: USB: add ftdi_sio USB ID for GDM Boost V1.x This patch add a missing usb device id for the GDMBoost V1.x device The patch is against 3.9-rc5 Signed-off-by: Stefani Seibold Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 778c54d..fc7bd14 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -187,6 +187,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_BOOST_PID) }, { USB_DEVICE(NEWPORT_VID, NEWPORT_AGILIS_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index e79861e..3c00351 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -74,6 +74,7 @@ #define FTDI_OPENDCC_THROTTLE_PID 0xBFDA #define FTDI_OPENDCC_GATEWAY_PID 0xBFDB #define FTDI_OPENDCC_GBM_PID 0xBFDC +#define FTDI_OPENDCC_GBM_BOOST_PID 0xBFDD /* NZR SEM 16+ USB (http://www.nzr.de) */ #define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */ -- cgit v0.10.2 From a76dd463c58efa5dfd72c3dd41f5a76b196f7ab1 Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Tue, 2 Apr 2013 18:23:59 +0200 Subject: USB: EHCI: make ehci-orion a separate driver Separate the Orion host controller driver from ehci-hcd host code into its own driver module because of following reason. With the multiplatform changes in arm-soc tree, it becomes possible to enable the mvebu platform (which uses ehci-orion) at the same time as other platforms that require a conflicting EHCI bus glue. At the moment, this results in a warning like drivers/usb/host/ehci-hcd.c:1297:0: warning: "PLATFORM_DRIVER" redefined [enabled by default] drivers/usb/host/ehci-hcd.c:1277:0: note: this is the location of the previous definition drivers/usb/host/ehci-orion.c:334:31: warning: 'ehci_orion_driver' defined but not used [-Wunused-variable] and an ehci driver that only works on one of them. With the infrastructure added by Alan Stern in patch 3e0232039 "USB: EHCI: prepare to make ehci-hcd a library module", we can avoid this problem by turning a bus glue into a separate module, as we do here for the orion bus glue. An earlier version of this patch was included in 3.9 but caused a regression there, which has subsequently been fixed. While we are here, use the opportunity to disabiguate the two Marvell EHCI controller implementations in Kconfig. In V4 (arnd): - Improve Kconfig text In V3: - More detail provided in commit message regarding this patch. - Replaced hcd_name string "ehci-orion" into "orion-ehci". - MODULE_LICENSE is GPL v2. - In ehci_init_driver calling second argument passed as NULL instead of ehci_orion_overrides because ehci_orion_overrides is removed. In V2: - Tegra patch related changes removed from this patch. Signed-off-by: Manjunath Goudar Signed-off-by: Arnd Bergmann Acked-by: Jason Cooper Tested-by: Andrew Lunn Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f7af098..c49a0b5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -163,6 +163,17 @@ config USB_EHCI_HCD_OMAP Enables support for the on-chip EHCI controller on OMAP3 and later chips. +config USB_EHCI_HCD_ORION + tristate "Support for Marvell EBU on-chip EHCI USB controller" + depends on USB_EHCI_HCD && PLAT_ORION + default y + ---help--- + Enables support for the on-chip EHCI controller on Marvell's + embedded ARM SoCs, including Orion, Kirkwood, Dove, Armada XP, + Armada 370. This is different from the EHCI implementation + on Marvell's mobile PXA and MMP SoC, see "EHCI support for + Marvell PXA/MMP USB controller" for those. + config USB_EHCI_MSM bool "Support for MSM on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MSM @@ -207,13 +218,17 @@ config USB_EHCI_S5P Enable support for the S5P SOC's on-chip EHCI controller. config USB_EHCI_MV - bool "EHCI support for Marvell on-chip controller" + bool "EHCI support for Marvell PXA/MMP USB controller" depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP) select USB_EHCI_ROOT_HUB_TT ---help--- Enables support for Marvell (including PXA and MMP series) on-chip USB SPH and OTG controller. SPH is a single port host, and it can only be EHCI host. OTG is controller that can switch to host mode. + Note that this driver will not work on Marvell's other EHCI + controller used by the EBU-type SoCs including Orion, Kirkwood, + Dova, Armada 370 and Armada XP. See "Support for Marvell EBU + on-chip EHCI USB controller" for those. config USB_W90X900_EHCI bool "W90X900(W90P910) EHCI support" diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 56de410..9492f50 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o +obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b12b97d..1f97268 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1249,11 +1249,6 @@ MODULE_LICENSE ("GPL"); #define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver #endif -#ifdef CONFIG_PLAT_ORION -#include "ehci-orion.c" -#define PLATFORM_DRIVER ehci_orion_driver -#endif - #ifdef CONFIG_USB_W90X900_EHCI #include "ehci-w90x900.c" #define PLATFORM_DRIVER ehci_hcd_w90x900_driver @@ -1319,6 +1314,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \ !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \ + !IS_ENABLED(CONFIG_USB_EHCI_HCD_ORION) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 38c45fb..54c5794 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -17,6 +17,12 @@ #include #include #include +#include +#include +#include +#include + +#include "ehci.h" #define rdl(off) __raw_readl(hcd->regs + (off)) #define wrl(off, val) __raw_writel((val), hcd->regs + (off)) @@ -34,6 +40,12 @@ #define USB_PHY_IVREF_CTRL 0x440 #define USB_PHY_TST_GRP_CTRL 0x450 +#define DRIVER_DESC "EHCI orion driver" + +static const char hcd_name[] = "ehci-orion"; + +static struct hc_driver __read_mostly ehci_orion_hc_driver; + /* * Implement Orion USB controller specification guidelines */ @@ -104,51 +116,6 @@ static void orion_usb_phy_v1_setup(struct usb_hcd *hcd) wrl(USB_MODE, 0x13); } -static const struct hc_driver ehci_orion_hc_driver = { - .description = hcd_name, - .product_desc = "Marvell Orion EHCI", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .reset = ehci_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - static void ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, const struct mbus_dram_target_info *dram) @@ -323,8 +290,6 @@ static int ehci_orion_drv_remove(struct platform_device *pdev) return 0; } -MODULE_ALIAS("platform:orion-ehci"); - static const struct of_device_id ehci_orion_dt_ids[] = { { .compatible = "marvell,orion-ehci", }, {}, @@ -341,3 +306,26 @@ static struct platform_driver ehci_orion_driver = { .of_match_table = of_match_ptr(ehci_orion_dt_ids), }, }; + +static int __init ehci_orion_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_orion_hc_driver, NULL); + return platform_driver_register(&ehci_orion_driver); +} +module_init(ehci_orion_init); + +static void __exit ehci_orion_cleanup(void) +{ + platform_driver_unregister(&ehci_orion_driver); +} +module_exit(ehci_orion_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:orion-ehci"); +MODULE_AUTHOR("Tzachi Perelstein"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 7675d6ba436f8439fc524ee0b42dc562cb1bc74e Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Tue, 2 Apr 2013 18:24:00 +0200 Subject: USB: EHCI: make ehci-spear a separate driver Separate the SPEAr host controller driver from ehci-hcd host code so that it can be built as a separate driver module. This work is part of enabling multi-platform kernels on ARM; however, note that other changes are still needed before SPEAr can be booted with a multi-platform kernel, but they are queued in the arm-soc tree for 3.10. With the infrastructure added by Alan Stern in patch 3e0232039 "USB: EHCI: prepare to make ehci-hcd a library module", we can avoid this problem by turning a bus glue into a separate module, as we do here for the SPEAr bus glue. In V4 (arnd): - renamed all 'struct spear_ehci' pointers from 'ehci' to the less ambiguous 'sehci'. - folded trivial spear_start_ehci/spear_stop_ehci functions into callers. - brought back initialization of ehci->caps. In V3: - Detailed commit message added here about why this patch is required. - Eliminated ehci_spear_setup routine because hcd registers can be directly set in the spear_ehci_hcd_drv_probe function. - spear_overrides struct initialized. - Converted to using .extra_priv_size for allocating spear_ehci, and updated all users of that structure. - to_spear_ehci() macro modified for spear_ehci. In V2: - Replaced spear as SPEAr everywhere, leaving functions/variables/config options. Signed-off-by: Deepak Saxena Signed-off-by: Manjunath Goudar Signed-off-by: Arnd Bergmann Acked-by: Viresh Kumar Acked-by: Alan Stern Cc: Shiraz Hashim Cc: spear-devel@list.st.com Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c49a0b5..ae2c747 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -174,6 +174,14 @@ config USB_EHCI_HCD_ORION on Marvell's mobile PXA and MMP SoC, see "EHCI support for Marvell PXA/MMP USB controller" for those. +config USB_EHCI_HCD_SPEAR + tristate "Support for ST SPEAr on-chip EHCI USB controller" + depends on USB_EHCI_HCD && PLAT_SPEAR + default y + ---help--- + Enables support for the on-chip EHCI controller on + ST SPEAr chips. + config USB_EHCI_MSM bool "Support for MSM on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MSM diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 9492f50..3e02471 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o +obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1f97268..c8c70a1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1264,11 +1264,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif -#ifdef CONFIG_PLAT_SPEAR -#include "ehci-spear.c" -#define PLATFORM_DRIVER spear_ehci_hcd_driver -#endif - #ifdef CONFIG_USB_EHCI_MSM #include "ehci-msm.c" #define PLATFORM_DRIVER ehci_msm_driver @@ -1315,6 +1310,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_ORION) && \ + !IS_ENABLED(CONFIG_USB_EHCI_HCD_SPEAR) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 210bb67..61ecfb3 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -1,5 +1,5 @@ /* -* Driver for EHCI HCD on SPEAR SOC +* Driver for EHCI HCD on SPEAr SOC * * Copyright (C) 2010 ST Micro Electronics, * Deepak Sikri @@ -12,71 +12,30 @@ */ #include +#include +#include #include +#include +#include #include #include #include +#include +#include -struct spear_ehci { - struct ehci_hcd ehci; - struct clk *clk; -}; - -#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd) +#include "ehci.h" -static void spear_start_ehci(struct spear_ehci *ehci) -{ - clk_prepare_enable(ehci->clk); -} +#define DRIVER_DESC "EHCI SPEAr driver" -static void spear_stop_ehci(struct spear_ehci *ehci) -{ - clk_disable_unprepare(ehci->clk); -} +static const char hcd_name[] = "SPEAr-ehci"; -static int ehci_spear_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - /* registers start at offset 0x0 */ - ehci->caps = hcd->regs; +struct spear_ehci { + struct clk *clk; +}; - return ehci_setup(hcd); -} +#define to_spear_ehci(hcd) (struct spear_ehci *)(hcd_to_ehci(hcd)->priv) -static const struct hc_driver ehci_spear_hc_driver = { - .description = hcd_name, - .product_desc = "SPEAr EHCI", - .hcd_priv_size = sizeof(struct spear_ehci), - - /* generic hardware linkage */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* basic lifecycle operations */ - .reset = ehci_spear_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* managing i/o requests and associated device resources */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* scheduling support */ - .get_frame_number = ehci_get_frame, - - /* root hub support */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; +static struct hc_driver __read_mostly ehci_spear_hc_driver; #ifdef CONFIG_PM_SLEEP static int ehci_spear_drv_suspend(struct device *dev) @@ -104,7 +63,7 @@ static u64 spear_ehci_dma_mask = DMA_BIT_MASK(32); static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) { struct usb_hcd *hcd ; - struct spear_ehci *ehci; + struct spear_ehci *sehci; struct resource *res; struct clk *usbh_clk; const struct hc_driver *driver = &ehci_spear_hc_driver; @@ -161,10 +120,13 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) goto err_put_hcd; } - ehci = (struct spear_ehci *)hcd_to_ehci(hcd); - ehci->clk = usbh_clk; + sehci = to_spear_ehci(hcd); + sehci->clk = usbh_clk; + + /* registers start at offset 0x0 */ + hcd_to_ehci(hcd)->caps = hcd->regs; - spear_start_ehci(ehci); + clk_prepare_enable(sehci->clk); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err_stop_ehci; @@ -172,7 +134,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) return retval; err_stop_ehci: - spear_stop_ehci(ehci); + clk_disable_unprepare(sehci->clk); err_put_hcd: usb_put_hcd(hcd); fail: @@ -184,7 +146,7 @@ fail: static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct spear_ehci *ehci_p = to_spear_ehci(hcd); + struct spear_ehci *sehci = to_spear_ehci(hcd); if (!hcd) return 0; @@ -192,8 +154,8 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) BUG(); usb_remove_hcd(hcd); - if (ehci_p->clk) - spear_stop_ehci(ehci_p); + if (sehci->clk) + clk_disable_unprepare(sehci->clk); usb_put_hcd(hcd); return 0; @@ -216,4 +178,29 @@ static struct platform_driver spear_ehci_hcd_driver = { } }; +static const struct ehci_driver_overrides spear_overrides __initdata = { + .extra_priv_size = sizeof(struct spear_ehci), +}; + +static int __init ehci_spear_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides); + return platform_driver_register(&spear_ehci_hcd_driver); +} +module_init(ehci_spear_init); + +static void __exit ehci_spear_cleanup(void) +{ + platform_driver_unregister(&spear_ehci_hcd_driver); +} +module_exit(ehci_spear_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_ALIAS("platform:spear-ehci"); +MODULE_AUTHOR("Deepak Sikri"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 7edb3daf78e5124b64a39b6eb264ec2a487e295a Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Tue, 2 Apr 2013 18:24:01 +0200 Subject: USB: EHCI: make ehci-s5p a separate driver Separate the Samsung S5P/EXYNOS host controller driver from ehci-hcd host code so that it can be built as a separate driver module. This work is part of enabling multi-platform kernels on ARM; however, note that other changes are still needed before S5P/EXYNOS can be booted with a multi-platform kernel. We currently expect those to get merged for 3.10. With the infrastructure added by Alan Stern in patch 3e0232039 "USB: EHCI: prepare to make ehci-hcd a library module", we can avoid this problem by turning a bus glue into a separate module, as we do here for the s5p bus glue. In V4 (arnd) - revert some of the pointless changes. - fix allocation of s5p specific data structure. In V3: - Detailed commit message added here, why this patch is required. - MODULE_LICENSE is GPL v2. - Added .extra_priv_size to eliminate the separate allocation of the s5p_ehci_hcd structure and removed .reset function pointer initialization. - Arranged #include's in alphabetical order. - After using extra_priv_size initialization, struct usb_hcd *hcd is redundant and can be removed from the probe function. - Eliminated s5p_ehci_phy_enable,contents of statements moved into the s5p_ehci_probe - Eliminated s5p_ehci_phy_disable, contents of statements moved into the s5p_ehci_remove. In V2: - Tegra patch related changes removed from this patch. Signed-off-by: Manjunath Goudar Acked-by: Jingoo Han Acked-by: Alan Stern Cc: Kukjin Kim Cc: Kyungmin Park Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ae2c747..5d4f42a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -220,10 +220,11 @@ config USB_EHCI_SH If you use the PCI EHCI controller, this option is not necessary. config USB_EHCI_S5P - boolean "S5P EHCI support" + tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" depends on USB_EHCI_HCD && PLAT_S5P help - Enable support for the S5P SOC's on-chip EHCI controller. + Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's + on-chip EHCI controller. config USB_EHCI_MV bool "EHCI support for Marvell PXA/MMP USB controller" diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 3e02471..3d895b5 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o +obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c8c70a1..8f1f4b4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1284,11 +1284,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER tegra_ehci_driver #endif -#ifdef CONFIG_USB_EHCI_S5P -#include "ehci-s5p.c" -#define PLATFORM_DRIVER s5p_ehci_driver -#endif - #ifdef CONFIG_SPARC_LEON #include "ehci-grlib.c" #define PLATFORM_DRIVER ehci_grlib_driver @@ -1311,6 +1306,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_ORION) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_SPEAR) && \ + !IS_ENABLED(CONFIG_USB_EHCI_S5P) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 43a2a16..d8cb0ca 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -13,13 +13,24 @@ */ #include +#include +#include +#include +#include #include -#include #include +#include #include #include #include #include +#include +#include +#include + +#include "ehci.h" + +#define DRIVER_DESC "EHCI s5p driver" #define EHCI_INSNREG00(base) (base + 0x90) #define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) @@ -30,65 +41,17 @@ (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \ EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN) +static const char hcd_name[] = "ehci-s5p"; +static struct hc_driver __read_mostly s5p_ehci_hc_driver; + struct s5p_ehci_hcd { - struct device *dev; - struct usb_hcd *hcd; struct clk *clk; struct usb_phy *phy; struct usb_otg *otg; struct s5p_ehci_platdata *pdata; }; -static const struct hc_driver s5p_ehci_hc_driver = { - .description = hcd_name, - .product_desc = "S5P EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - .reset = ehci_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - .get_frame_number = ehci_get_frame, - - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - -static void s5p_ehci_phy_enable(struct s5p_ehci_hcd *s5p_ehci) -{ - struct platform_device *pdev = to_platform_device(s5p_ehci->dev); - - if (s5p_ehci->phy) - usb_phy_init(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_init) - s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); -} - -static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci) -{ - struct platform_device *pdev = to_platform_device(s5p_ehci->dev); - - if (s5p_ehci->phy) - usb_phy_shutdown(s5p_ehci->phy); - else if (s5p_ehci->pdata->phy_exit) - s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); -} +#define to_s5p_ehci(hcd) (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv) static void s5p_setup_vbus_gpio(struct platform_device *pdev) { @@ -134,11 +97,13 @@ static int s5p_ehci_probe(struct platform_device *pdev) s5p_setup_vbus_gpio(pdev); - s5p_ehci = devm_kzalloc(&pdev->dev, sizeof(struct s5p_ehci_hcd), - GFP_KERNEL); - if (!s5p_ehci) + hcd = usb_create_hcd(&s5p_ehci_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; - + } + s5p_ehci = to_s5p_ehci(hcd); phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { /* Fallback to pdata */ @@ -153,16 +118,6 @@ static int s5p_ehci_probe(struct platform_device *pdev) s5p_ehci->otg = phy->otg; } - s5p_ehci->dev = &pdev->dev; - - hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, - dev_name(&pdev->dev)); - if (!hcd) { - dev_err(&pdev->dev, "Unable to create HCD\n"); - return -ENOMEM; - } - - s5p_ehci->hcd = hcd; s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost"); if (IS_ERR(s5p_ehci->clk)) { @@ -199,9 +154,12 @@ static int s5p_ehci_probe(struct platform_device *pdev) } if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); - s5p_ehci_phy_enable(s5p_ehci); + if (s5p_ehci->phy) + usb_phy_init(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_init) + s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; @@ -220,7 +178,10 @@ static int s5p_ehci_probe(struct platform_device *pdev) return 0; fail_add_hcd: - s5p_ehci_phy_disable(s5p_ehci); + if (s5p_ehci->phy) + usb_phy_shutdown(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_exit) + s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); fail_io: clk_disable_unprepare(s5p_ehci->clk); fail_clk: @@ -230,15 +191,18 @@ fail_clk: static int s5p_ehci_remove(struct platform_device *pdev) { - struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); - struct usb_hcd *hcd = s5p_ehci->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); usb_remove_hcd(hcd); if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); - s5p_ehci_phy_disable(s5p_ehci); + if (s5p_ehci->phy) + usb_phy_shutdown(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_exit) + s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(s5p_ehci->clk); @@ -249,8 +213,7 @@ static int s5p_ehci_remove(struct platform_device *pdev) static void s5p_ehci_shutdown(struct platform_device *pdev) { - struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); - struct usb_hcd *hcd = s5p_ehci->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); @@ -259,17 +222,22 @@ static void s5p_ehci_shutdown(struct platform_device *pdev) #ifdef CONFIG_PM static int s5p_ehci_suspend(struct device *dev) { - struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); - struct usb_hcd *hcd = s5p_ehci->hcd; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); + struct platform_device *pdev = to_platform_device(dev); + bool do_wakeup = device_may_wakeup(dev); int rc; rc = ehci_suspend(hcd, do_wakeup); if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); - s5p_ehci_phy_disable(s5p_ehci); + if (s5p_ehci->phy) + usb_phy_shutdown(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_exit) + s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(s5p_ehci->clk); @@ -278,15 +246,19 @@ static int s5p_ehci_suspend(struct device *dev) static int s5p_ehci_resume(struct device *dev) { - struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); - struct usb_hcd *hcd = s5p_ehci->hcd; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); + struct platform_device *pdev = to_platform_device(dev); clk_prepare_enable(s5p_ehci->clk); if (s5p_ehci->otg) - s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); - s5p_ehci_phy_enable(s5p_ehci); + if (s5p_ehci->phy) + usb_phy_init(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_init) + s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); @@ -323,5 +295,29 @@ static struct platform_driver s5p_ehci_driver = { .of_match_table = of_match_ptr(exynos_ehci_match), } }; +static const struct ehci_driver_overrides s5p_overrides __initdata = { + .extra_priv_size = sizeof(struct s5p_ehci_hcd), +}; + +static int __init ehci_s5p_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides); + return platform_driver_register(&s5p_ehci_driver); +} +module_init(ehci_s5p_init); + +static void __exit ehci_s5p_cleanup(void) +{ + platform_driver_unregister(&s5p_ehci_driver); +} +module_exit(ehci_s5p_cleanup); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_ALIAS("platform:s5p-ehci"); +MODULE_AUTHOR("Jingoo Han"); +MODULE_AUTHOR("Joonyoung Shim"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 9773696105534dd5193576adfe4a0117a6489c64 Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Tue, 2 Apr 2013 18:24:02 +0200 Subject: USB: EHCI: make ehci-atmel a separate driver Separate the Atmel host controller driver from ehci-hcd host code so that it can be built as a separate driver module. This work is part of enabling multi-platform kernels on ARM; however, note that other changes are still needed before Atmel can be booted with a multi-platform kernel. This is currently planned for Linux-3.11. With the infrastructure added by Alan Stern in patch 3e0232039 "USB: EHCI: prepare to make ehci-hcd a library module", we can avoid this problem by turning a bus glue into a separate module, as we do here for the Atmel bus glue. In V4 (arnd): - reordered #include statements. - removed call to ehci_shutdown and the corresponding export In V3: - Detailed commit message added here about why this patch is required. - Replaced hcd_name string "ehci-atmel" to "atmel-ehci". - Inserted blank line in the Makefile to separate the EHCI drivers from the following non-EHCI drivers. - Exported ehci_shutdown symbol as it is needed by the Atmel driver. - Eliminated ehci_atmel_setup routine because hcd registers can be directly set in the ehci_atmel_drv_probe function. In V2: Resolved below compiler error. drivers/usb/host/ehci-atmel.c: In function 'ehci_atmel_drv_remove': drivers/usb/host/ehci-atmel.c:167: error: implicit declaration of function 'ehci_shutdown' Signed-off-by: Manjunath Goudar Acked-by: Alan Stern Acked-by: Nicolas Ferre Cc: Andrew Victor Cc: Jean-Christophe Plagniol-Villard Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5d4f42a..c7a6866 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -182,6 +182,14 @@ config USB_EHCI_HCD_SPEAR Enables support for the on-chip EHCI controller on ST SPEAr chips. +config USB_EHCI_HCD_AT91 + tristate "Support for Atmel on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_AT91 + default y + ---help--- + Enables support for the on-chip EHCI controller on + Atmel chips. + config USB_EHCI_MSM bool "Support for MSM on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MSM diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 3d895b5..368d3eb 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o +obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index f3beac4..6642009 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -12,9 +12,22 @@ */ #include -#include +#include +#include +#include +#include #include #include +#include +#include +#include + +#include "ehci.h" + +#define DRIVER_DESC "EHCI Atmel driver" + +static const char hcd_name[] = "ehci-atmel"; +static struct hc_driver __read_mostly ehci_atmel_hc_driver; /* interface and function clocks */ static struct clk *iclk, *fclk; @@ -50,51 +63,6 @@ static void atmel_stop_ehci(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -static int ehci_atmel_setup(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - /* registers start at offset 0x0 */ - ehci->caps = hcd->regs; - - return ehci_setup(hcd); -} - -static const struct hc_driver ehci_atmel_hc_driver = { - .description = hcd_name, - .product_desc = "Atmel EHCI UHP HS", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* generic hardware linkage */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* basic lifecycle operations */ - .reset = ehci_atmel_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* managing i/o requests and associated device resources */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* scheduling support */ - .get_frame_number = ehci_get_frame, - - /* root hub support */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - static u64 at91_ehci_dma_mask = DMA_BIT_MASK(32); static int ehci_atmel_drv_probe(struct platform_device *pdev) @@ -102,6 +70,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) struct usb_hcd *hcd; const struct hc_driver *driver = &ehci_atmel_hc_driver; struct resource *res; + struct ehci_hcd *ehci; int irq; int retval; @@ -162,6 +131,10 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) goto fail_request_resource; } + ehci = hcd_to_ehci(hcd); + /* registers start at offset 0x0 */ + ehci->caps = hcd->regs; + atmel_start_ehci(pdev); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); @@ -185,7 +158,6 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - ehci_shutdown(hcd); usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -213,3 +185,25 @@ static struct platform_driver ehci_atmel_driver = { .of_match_table = of_match_ptr(atmel_ehci_dt_ids), }, }; + +static int __init ehci_atmel_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ehci_init_driver(&ehci_atmel_hc_driver, NULL); + return platform_driver_register(&ehci_atmel_driver); +} +module_init(ehci_atmel_init); + +static void __exit ehci_atmel_cleanup(void) +{ + platform_driver_unregister(&ehci_atmel_driver); +} +module_exit(ehci_atmel_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:atmel-ehci"); +MODULE_AUTHOR("Nicolas Ferre"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8f1f4b4..ccc78ab 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1254,11 +1254,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_w90x900_driver #endif -#ifdef CONFIG_ARCH_AT91 -#include "ehci-atmel.c" -#define PLATFORM_DRIVER ehci_atmel_driver -#endif - #ifdef CONFIG_USB_OCTEON_EHCI #include "ehci-octeon.c" #define PLATFORM_DRIVER ehci_octeon_driver @@ -1307,6 +1302,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_EHCI_HCD_ORION) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_SPEAR) && \ !IS_ENABLED(CONFIG_USB_EHCI_S5P) && \ + !IS_ENABLED(CONFIG_USB_EHCI_HCD_AT91) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ -- cgit v0.10.2 From 8c68e84f3a0361bf6053a055643a6ff18c7d86d4 Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Tue, 2 Apr 2013 18:24:03 +0200 Subject: USB: EHCI: make ehci-msm a separate driver Separate the Qualcomm QSD/MSM on-chip host controller driver from ehci-hcd host code so that it can be built as a separate driver module. This work is part of enabling multi-platform kernels on ARM; however, note that other changes are still needed before Qualcomm QSD/MSM can be booted with a multi-platform kernel, which is not expected before 3.11. With the infrastructure added by Alan Stern in patch 3e0232039 "USB: EHCI: prepare to make ehci-hcd a library module", we can avoid this problem by turning a bus glue into a separate module, as we do here for the msm bus glue. In V5 (arnd): - add FIXME about missing usb_add_hcd() or usb_remove_hcd() calls In V3: - Detailed commit message added here describing why this patch is required. - Arranged #include's in alphabetical order. - driver.name initialized hcd_name[] = "ehci-msm" in platform_driver structure initialization instead of "msm-ehci", which was the reason why it broke in EHCI USB testing In V2: Tegra patch related changes removed from this patch. Signed-off-by: Manjunath Goudar Acked-by: Alan Stern Acked-by: David Brown Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c7a6866..dbeac41 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -191,7 +191,7 @@ config USB_EHCI_HCD_AT91 Atmel chips. config USB_EHCI_MSM - bool "Support for MSM on-chip EHCI USB controller" + tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MSM select USB_EHCI_ROOT_HUB_TT select USB_MSM_OTG diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 368d3eb..4fb73c1 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o +obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ccc78ab..2e0c2bd 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1259,11 +1259,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif -#ifdef CONFIG_USB_EHCI_MSM -#include "ehci-msm.c" -#define PLATFORM_DRIVER ehci_msm_driver -#endif - #ifdef CONFIG_TILE_USB #include "ehci-tilegx.c" #define PLATFORM_DRIVER ehci_hcd_tilegx_driver @@ -1303,6 +1298,7 @@ MODULE_LICENSE ("GPL"); !IS_ENABLED(CONFIG_USB_EHCI_HCD_SPEAR) && \ !IS_ENABLED(CONFIG_USB_EHCI_S5P) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_AT91) && \ + !IS_ENABLED(CONFIG_USB_EHCI_MSM) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index ebf4103..0f717dc 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -22,16 +22,26 @@ * along with this program; if not, you can find it at http://www.fsf.org */ -#include #include #include +#include +#include +#include +#include #include - #include #include +#include +#include + +#include "ehci.h" #define MSM_USB_BASE (hcd->regs) +#define DRIVER_DESC "Qualcomm On-Chip EHCI Host Controller" + +static const char hcd_name[] = "ehci-msm"; +static struct hc_driver __read_mostly msm_hc_driver; static struct usb_phy *phy; static int ehci_msm_reset(struct usb_hcd *hcd) @@ -56,52 +66,6 @@ static int ehci_msm_reset(struct usb_hcd *hcd) return 0; } -static struct hc_driver msm_hc_driver = { - .description = hcd_name, - .product_desc = "Qualcomm On-Chip EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, - - .reset = ehci_msm_reset, - .start = ehci_run, - - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - /* - * PM support - */ - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, -}; - static int ehci_msm_probe(struct platform_device *pdev) { struct usb_hcd *hcd; @@ -165,6 +129,8 @@ static int ehci_msm_probe(struct platform_device *pdev) pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); + /* FIXME: need to call usb_add_hcd() here? */ + return 0; put_hcd: @@ -183,6 +149,8 @@ static int ehci_msm_remove(struct platform_device *pdev) otg_set_host(phy->otg, NULL); + /* FIXME: need to call usb_remove_hcd() here? */ + usb_put_hcd(hcd); return 0; @@ -226,3 +194,28 @@ static struct platform_driver ehci_msm_driver = { .pm = &ehci_msm_dev_pm_ops, }, }; + +static const struct ehci_driver_overrides msm_overrides __initdata = { + .reset = ehci_msm_reset, +}; + +static int __init ehci_msm_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + ehci_init_driver(&msm_hc_driver, &msm_overrides); + return platform_driver_register(&ehci_msm_driver); +} +module_init(ehci_msm_init); + +static void __exit ehci_msm_cleanup(void) +{ + platform_driver_unregister(&ehci_msm_driver); +} +module_exit(ehci_msm_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:msm-ehci"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From bbcd85a0e624a6825e2b4477a5ad2a7873288809 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 9 Apr 2013 18:42:11 +0530 Subject: usb: ehci-s5p: fix: Fix null pointer dereferencing 7edb3da: (USB: EHCI: make ehci-s5p a separate driver) raised an issue with ehci-s5p's driver data. Now that 's5p_ehci_hcd' doesn't maintain pointer to 'usb_hcd' and s5p_ehci is nothing but a pointer to hcd->priv; add hcd to the driver data rather than s5p_ehci. This fixes issues with null pointer dereferencing in s5p_ehci_shutdown(), s5p_ehci_suspend(), s5p_ehci_resume(). Signed-off-by: Vivek Gautam CC: Manjunath Goudar CC: Arnd Bergmann CC: Jingoo Han Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index d8cb0ca..580548a 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -173,7 +173,7 @@ static int s5p_ehci_probe(struct platform_device *pdev) goto fail_add_hcd; } - platform_set_drvdata(pdev, s5p_ehci); + platform_set_drvdata(pdev, hcd); return 0; -- cgit v0.10.2 From a2a2d6c7f93e160b52a4ad0164db1f43f743ae0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 9 Apr 2013 11:26:02 +0200 Subject: USB: option: add a D-Link DWM-156 variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding support for a Mediatek based device labelled as D-Link Model: DWM-156, H/W Ver: A7 Also adding two other device IDs found in the Debian(!) packages included on the embedded device driver CD. This is a composite MBIM + serial ports + card reader device: T: Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 14 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=2001 ProdID=7d01 Rev= 3.00 S: Manufacturer=D-Link,Inc S: Product=D-Link DWM-156 C:* #Ifs= 7 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=88(I) Atr=03(Int.) MxPS= 64 Ivl=125us I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=02 Prot=01 Driver=option E: Ad=87(I) Atr=03(Int.) MxPS= 64 Ivl=500us E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 6 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Cc: stable Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 558adfc..e1bff4b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1350,6 +1350,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x02, 0x01) }, /* D-Link DWM-156 (variant) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d01, 0xff, 0x00, 0x00) }, /* D-Link DWM-156 (variant) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v0.10.2 From 25e11ec4fe5271c4895265ecbb69531e6b0c0dd5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Apr 2013 14:29:25 +0200 Subject: USB: regroup all depends on USB within an if USB block This patch removes the depends on USB from all config symbols in drivers/usb/host/Kconfig and replace that with an if USB / endif block as suggested by Alan Stern. Some source ... Kconfig lines have been shuffled around to permit a better regroupment of the Kconfig files depending on "config USB" item. No functionnal change is introduced. Acked-by: Alan Stern Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2c481b8..92e1dc9 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -122,9 +122,9 @@ config USB To compile this driver as a module, choose M here: the module will be called usbcore. -source "drivers/usb/core/Kconfig" +if USB -source "drivers/usb/dwc3/Kconfig" +source "drivers/usb/core/Kconfig" source "drivers/usb/mon/Kconfig" @@ -134,8 +134,6 @@ source "drivers/usb/host/Kconfig" source "drivers/usb/musb/Kconfig" -source "drivers/usb/chipidea/Kconfig" - source "drivers/usb/renesas_usbhs/Kconfig" source "drivers/usb/class/Kconfig" @@ -144,12 +142,19 @@ source "drivers/usb/storage/Kconfig" source "drivers/usb/image/Kconfig" +endif + +source "drivers/usb/dwc3/Kconfig" + +source "drivers/usb/chipidea/Kconfig" + comment "USB port drivers" - depends on USB + +if USB config USB_USS720 tristate "USS720 parport driver" - depends on USB && PARPORT + depends on PARPORT select PARPORT_NOT_PC ---help--- This driver is for USB parallel port adapters that use the Lucent @@ -180,10 +185,12 @@ source "drivers/usb/serial/Kconfig" source "drivers/usb/misc/Kconfig" -source "drivers/usb/phy/Kconfig" - source "drivers/usb/atm/Kconfig" +endif # USB + +source "drivers/usb/phy/Kconfig" + source "drivers/usb/gadget/Kconfig" endif # USB_SUPPORT diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig index be0b8da..0f92294 100644 --- a/drivers/usb/atm/Kconfig +++ b/drivers/usb/atm/Kconfig @@ -4,7 +4,7 @@ menuconfig USB_ATM tristate "USB DSL modem support" - depends on USB && ATM + depends on ATM select CRC32 default n help diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 316aac8..bb8b736 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -2,11 +2,10 @@ # USB Class driver configuration # comment "USB Device Class drivers" - depends on USB config USB_ACM tristate "USB Modem (CDC ACM) support" - depends on USB && TTY + depends on TTY ---help--- This driver supports USB modems and ISDN adapters which support the Communication Device Class Abstract Control Model interface. @@ -21,7 +20,6 @@ config USB_ACM config USB_PRINTER tristate "USB Printer support" - depends on USB help Say Y here if you want to connect a USB printer to your computer's USB port. @@ -31,7 +29,6 @@ config USB_PRINTER config USB_WDM tristate "USB Wireless Device Management support" - depends on USB ---help--- This driver supports the WMC Device Management functionality of cell phones compliant to the CDC WMC specification. You can use @@ -42,7 +39,6 @@ config USB_WDM config USB_TMC tristate "USB Test and Measurement Class support" - depends on USB help Say Y here if you want to connect a USB device that follows the USB.org specification for USB Test and Measurement devices diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 7b7305e..8772b36 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -3,7 +3,6 @@ # config USB_DEBUG bool "USB verbose debug messages" - depends on USB help Say Y here if you want the USB core & hub drivers to produce a bunch of debug messages to the system log. Select this if you are having a @@ -11,7 +10,6 @@ config USB_DEBUG config USB_ANNOUNCE_NEW_DEVICES bool "USB announce new devices" - depends on USB default N help Say Y here if you want the USB core to always announce the @@ -25,11 +23,9 @@ config USB_ANNOUNCE_NEW_DEVICES log, or have any doubts about this, say N here. comment "Miscellaneous USB options" - depends on USB config USB_DEFAULT_PERSIST bool "Enable USB persist by default" - depends on USB default y help Say N here if you don't want USB power session persistance @@ -45,7 +41,6 @@ config USB_DEFAULT_PERSIST config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation" - depends on USB help If you say Y here, the USB subsystem will use dynamic minor allocation for any device that uses the USB major number. @@ -56,7 +51,6 @@ config USB_DYNAMIC_MINORS config USB_OTG bool "OTG support" - depends on USB depends on USB_SUSPEND default n help diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index dbeac41..1714c6d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -2,11 +2,9 @@ # USB Host Controller Drivers # comment "USB Host Controller Drivers" - depends on USB config USB_C67X00_HCD tristate "Cypress C67x00 HCD support" - depends on USB help The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role host/peripheral/OTG USB controllers. @@ -19,7 +17,7 @@ config USB_C67X00_HCD config USB_XHCI_HCD tristate "xHCI HCD (USB 3.0) support" - depends on USB && USB_ARCH_HAS_XHCI + depends on USB_ARCH_HAS_XHCI ---help--- The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0 "SuperSpeed" host controller hardware. @@ -43,7 +41,7 @@ config USB_XHCI_HCD_DEBUGGING config USB_EHCI_HCD tristate "EHCI HCD (USB 2.0) support" - depends on USB && USB_ARCH_HAS_EHCI + depends on USB_ARCH_HAS_EHCI ---help--- The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. @@ -280,7 +278,7 @@ config USB_EHCI_ATH79 config USB_OXU210HP_HCD tristate "OXU210HP HCD support" - depends on USB && GENERIC_HARDIRQS + depends on GENERIC_HARDIRQS ---help--- The OXU210HP is an USB host/OTG/device controller. Enable this option if your board has this chip. If unsure, say N. @@ -293,7 +291,6 @@ config USB_OXU210HP_HCD config USB_ISP116X_HCD tristate "ISP116X HCD support" - depends on USB ---help--- The ISP1160 and ISP1161 chips are USB host controllers. Enable this option if your board has this chip. If unsure, say N. @@ -305,7 +302,6 @@ config USB_ISP116X_HCD config USB_ISP1760_HCD tristate "ISP 1760 HCD support" - depends on USB ---help--- The ISP1760 chip is a USB 2.0 host controller. @@ -320,7 +316,6 @@ config USB_ISP1760_HCD config USB_ISP1362_HCD tristate "ISP1362 HCD support" - depends on USB default N ---help--- Supports the Philips ISP1362 chip as a host controller @@ -332,7 +327,7 @@ config USB_ISP1362_HCD config USB_OHCI_HCD tristate "OHCI HCD support" - depends on USB && USB_ARCH_HAS_OHCI + depends on USB_ARCH_HAS_OHCI select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 depends on USB_ISP1301 || !ARCH_LPC32XX ---help--- @@ -490,7 +485,7 @@ config USB_OHCI_LITTLE_ENDIAN config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" - depends on USB && (PCI || SPARC_LEON || ARCH_VT8500) + depends on PCI || SPARC_LEON || ARCH_VT8500 ---help--- The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB @@ -530,7 +525,7 @@ config USB_UHCI_BIG_ENDIAN_DESC config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" - depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE + depends on OF_GPIO && QE_GPIO && QUICC_ENGINE select FSL_GTM select QE_USB help @@ -547,7 +542,7 @@ config FHCI_DEBUG config USB_U132_HCD tristate "Elan U132 Adapter Host Controller" - depends on USB && USB_FTDI_ELAN + depends on USB_FTDI_ELAN default M help The U132 adapter is a USB to CardBus adapter specifically designed @@ -575,7 +570,6 @@ config USB_U132_HCD config USB_SL811_HCD tristate "SL811HS HCD support" - depends on USB help The SL811HS is a single-port USB controller that supports either host side or peripheral side roles. Enable this option if your @@ -607,7 +601,6 @@ config USB_SL811_CS config USB_R8A66597_HCD tristate "R8A66597 HCD support" - depends on USB help The R8A66597 is a USB 2.0 host and peripheral controller. @@ -619,7 +612,6 @@ config USB_R8A66597_HCD config USB_RENESAS_USBHS_HCD tristate "Renesas USBHS HCD support" - depends on USB depends on USB_RENESAS_USBHS help The Renesas USBHS is a USB 2.0 host and peripheral controller. @@ -644,7 +636,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver" - depends on USB && UWB + depends on UWB select USB_WUSB select UWB_HWA help @@ -658,7 +650,7 @@ config USB_HWA_HCD config USB_IMX21_HCD tristate "i.MX21 HCD support" - depends on USB && ARM && ARCH_MXC + depends on ARM && ARCH_MXC help This driver enables support for the on-chip USB host in the i.MX21 processor. @@ -668,7 +660,7 @@ config USB_IMX21_HCD config USB_OCTEON_EHCI bool "Octeon on-chip EHCI support" - depends on USB && USB_EHCI_HCD && CPU_CAVIUM_OCTEON + depends on USB_EHCI_HCD && CPU_CAVIUM_OCTEON default n select USB_EHCI_BIG_ENDIAN_MMIO help @@ -679,7 +671,7 @@ config USB_OCTEON_EHCI config USB_OCTEON_OHCI bool "Octeon on-chip OHCI support" - depends on USB && USB_OHCI_HCD && CPU_CAVIUM_OCTEON + depends on USB_OHCI_HCD && CPU_CAVIUM_OCTEON default USB_OCTEON_EHCI select USB_OHCI_BIG_ENDIAN_MMIO select USB_OHCI_LITTLE_ENDIAN diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig index 33350f9..320d368 100644 --- a/drivers/usb/image/Kconfig +++ b/drivers/usb/image/Kconfig @@ -2,11 +2,9 @@ # USB Imaging devices configuration # comment "USB Imaging devices" - depends on USB config USB_MDC800 tristate "USB Mustek MDC800 Digital Camera support" - depends on USB ---help--- Say Y here if you want to connect this type of still camera to your computer's USB port. This driver can be used with gphoto 0.4.3 @@ -19,7 +17,7 @@ config USB_MDC800 config USB_MICROTEK tristate "Microtek X6USB scanner support" - depends on USB && SCSI + depends on SCSI help Say Y here if you want support for the Microtek X6USB and possibly the Phantom 336CX, Phantom C6 and ScanMaker V6U(S)L. diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 3b1a3f4..a51e7d6 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -2,11 +2,9 @@ # USB Miscellaneous driver configuration # comment "USB Miscellaneous drivers" - depends on USB config USB_EMI62 tristate "EMI 6|2m USB Audio interface support" - depends on USB ---help--- This driver loads firmware to Emagic EMI 6|2m low latency USB Audio and Midi interface. @@ -21,7 +19,6 @@ config USB_EMI62 config USB_EMI26 tristate "EMI 2|6 USB Audio interface support" - depends on USB ---help--- This driver loads firmware to Emagic EMI 2|6 low latency USB Audio interface. @@ -34,7 +31,6 @@ config USB_EMI26 config USB_ADUTUX tristate "ADU devices from Ontrak Control Systems" - depends on USB help Say Y if you want to use an ADU device from Ontrak Control Systems. @@ -44,7 +40,6 @@ config USB_ADUTUX config USB_SEVSEG tristate "USB 7-Segment LED Display" - depends on USB help Say Y here if you have a USB 7-Segment Display by Delcom @@ -53,7 +48,6 @@ config USB_SEVSEG config USB_RIO500 tristate "USB Diamond Rio500 support" - depends on USB help Say Y here if you want to connect a USB Rio500 mp3 player to your computer's USB port. Please read @@ -64,7 +58,6 @@ config USB_RIO500 config USB_LEGOTOWER tristate "USB Lego Infrared Tower support" - depends on USB help Say Y here if you want to connect a USB Lego Infrared Tower to your computer's USB port. @@ -77,7 +70,6 @@ config USB_LEGOTOWER config USB_LCD tristate "USB LCD driver support" - depends on USB help Say Y here if you want to connect an USBLCD to your computer's USB port. The USBLCD is a small USB interface board for @@ -89,7 +81,6 @@ config USB_LCD config USB_LED tristate "USB LED driver support" - depends on USB help Say Y here if you want to connect an USBLED device to your computer's USB port. @@ -99,7 +90,6 @@ config USB_LED config USB_CYPRESS_CY7C63 tristate "Cypress CY7C63xxx USB driver support" - depends on USB help Say Y here if you want to connect a Cypress CY7C63xxx micro controller to your computer's USB port. Currently this @@ -113,7 +103,6 @@ config USB_CYPRESS_CY7C63 config USB_CYTHERM tristate "Cypress USB thermometer driver support" - depends on USB help Say Y here if you want to connect a Cypress USB thermometer device to your computer's USB port. This device is also known @@ -126,7 +115,6 @@ config USB_CYTHERM config USB_IDMOUSE tristate "Siemens ID USB Mouse Fingerprint sensor support" - depends on USB help Say Y here if you want to use the fingerprint sensor on the Siemens ID Mouse. There is also a Siemens ID Mouse @@ -140,7 +128,6 @@ config USB_IDMOUSE config USB_FTDI_ELAN tristate "Elan PCMCIA CardBus Adapter USB Client" - depends on USB default M help ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters. @@ -164,7 +151,6 @@ config USB_FTDI_ELAN config USB_APPLEDISPLAY tristate "Apple Cinema Display support" - depends on USB select BACKLIGHT_LCD_SUPPORT select BACKLIGHT_CLASS_DEVICE help @@ -175,7 +161,6 @@ source "drivers/usb/misc/sisusbvga/Kconfig" config USB_LD tristate "USB LD driver" - depends on USB help This driver is for generic USB devices that use interrupt transfers, like LD Didactic's USB devices. @@ -185,7 +170,6 @@ config USB_LD config USB_TRANCEVIBRATOR tristate "PlayStation 2 Trance Vibrator driver support" - depends on USB help Say Y here if you want to connect a PlayStation 2 Trance Vibrator device to your computer's USB port. @@ -195,7 +179,6 @@ config USB_TRANCEVIBRATOR config USB_IOWARRIOR tristate "IO Warrior driver support" - depends on USB help Say Y here if you want to support the IO Warrior devices from Code Mercenaries. This includes support for the following devices: @@ -209,7 +192,6 @@ config USB_IOWARRIOR config USB_TEST tristate "USB testing driver" - depends on USB help This driver is for testing host controller software. It is used with specialized device firmware for regression and stress testing, @@ -220,7 +202,6 @@ config USB_TEST config USB_ISIGHTFW tristate "iSight firmware loading support" - depends on USB select FW_LOADER help This driver loads firmware for USB Apple iSight cameras, allowing @@ -233,7 +214,6 @@ config USB_ISIGHTFW config USB_YUREX tristate "USB YUREX driver support" - depends on USB help Say Y here if you want to connect a YUREX to your computer's USB port. The YUREX is a leg-shakes sensor. See @@ -246,7 +226,6 @@ config USB_YUREX config USB_EZUSB_FX2 tristate "Functions for loading firmware on EZUSB chips" - depends on USB help Say Y here if you need EZUSB device support. (Cypress FX/FX2/FX2LP microcontrollers) diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 30ea7ca..0d03a52 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -1,7 +1,7 @@ config USB_SISUSBVGA tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)" - depends on USB && (USB_MUSB_HDRC || USB_EHCI_HCD) + depends on (USB_MUSB_HDRC || USB_EHCI_HCD) ---help--- Say Y here if you intend to attach a USB2VGA dongle based on a Net2280 and a SiS315 chip. diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index 635745f..5c6ffa2 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -4,7 +4,6 @@ config USB_MON tristate "USB Monitor" - depends on USB help If you select this option, a component which captures the USB traffic between peripheral-specific drivers and HC drivers will be built. diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 47442d3..06f8d29 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -6,7 +6,7 @@ # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' - depends on USB && USB_GADGET + depends on USB_GADGET help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 29feb00..019bf7e 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -4,7 +4,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' - depends on USB && USB_GADGET && GENERIC_HARDIRQS + depends on USB_GADGET && GENERIC_HARDIRQS default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 17b7f9a..bf37336 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -4,7 +4,7 @@ menuconfig USB_SERIAL tristate "USB Serial Converter support" - depends on USB && TTY + depends on TTY ---help--- Say Y here if you have a USB device that provides normal serial ports, or acts like a serial device, and you want to connect it to diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index eab04a6..8470e1b 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -4,11 +4,10 @@ comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may" comment "also be needed; see USB_STORAGE Help for more info" - depends on USB config USB_STORAGE tristate "USB Mass Storage support" - depends on USB && SCSI + depends on SCSI ---help--- Say Y here if you want to connect USB mass storage devices to your computer's USB port. This is the driver you need for USB @@ -188,7 +187,7 @@ config USB_STORAGE_CYPRESS_ATACB config USB_STORAGE_ENE_UB6250 tristate "USB ENE card reader support" - depends on USB && SCSI + depends on SCSI depends on USB_STORAGE ---help--- Say Y here if you wish to control a ENE SD/MS Card reader. @@ -203,7 +202,7 @@ config USB_STORAGE_ENE_UB6250 config USB_UAS tristate "USB Attached SCSI" - depends on USB && SCSI && BROKEN + depends on SCSI && BROKEN help The USB Attached SCSI protocol is supported by some USB storage devices. It permits higher performance by supporting diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig index 8bf1976..0e17b96 100644 --- a/drivers/usb/wusbcore/Kconfig +++ b/drivers/usb/wusbcore/Kconfig @@ -3,7 +3,6 @@ # config USB_WUSB tristate "Enable Wireless USB extensions" - depends on USB depends on PCI depends on UWB select CRYPTO @@ -19,7 +18,6 @@ config USB_WUSB config USB_WUSB_CBAF tristate "Support WUSB Cable Based Association (CBA)" - depends on USB help Some WUSB devices support Cable Based Association. It's used to enable the secure communication between the host and the -- cgit v0.10.2 From 9296d94d83649e1c2f25c87dc4ead9c2ab073305 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Apr 2013 14:29:26 +0200 Subject: USB: remove USB_EHCI_BIG_ENDIAN_{DESC,MMIO} depends on architecture symbol Just like the OHCI counter part we just can remove the architecture specific symbols which prevent these configuration symbols from being selected by platforms/architectures requiring it. The original implementation did not scale at all since it required each and every single architecture to be added for these configuration symbols to be selected. Now it is up to the EHCI driver and/or platform to select these configuration symbols accordingly. Acked-by: Alan Stern Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1cacda4..bbddefe 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -549,6 +549,8 @@ config ARCH_IXP4XX select GENERIC_CLOCKEVENTS select MIGHT_HAVE_PCI select NEED_MACH_IO_H + select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_DESC help Support for Intel's IXP4XX (XScale) family of processors. diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 51244bf..3a7b395 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -404,6 +404,8 @@ config PMC_MSP select IRQ_CPU select SERIAL_8250 select SERIAL_8250_CONSOLE + select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_DESC help This adds support for the PMC-Sierra family of Multi-Service Processor System-On-A-Chips. These parts include a number @@ -1433,6 +1435,7 @@ config CPU_CAVIUM_OCTEON select CPU_SUPPORTS_HUGEPAGES select LIBFDT select USE_OF + select USB_EHCI_BIG_ENDIAN_MMIO help The Cavium Octeon processor is a highly integrated chip containing many ethernet hardware widgets for networking tasks. The processor diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 0effe9f..7be9336 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -274,6 +274,8 @@ config 440EPX select IBM_EMAC_EMAC4 select IBM_EMAC_RGMII select IBM_EMAC_ZMII + select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_DESC config 440GRX bool diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig index c169998..381a592 100644 --- a/arch/powerpc/platforms/512x/Kconfig +++ b/arch/powerpc/platforms/512x/Kconfig @@ -7,6 +7,8 @@ config PPC_MPC512x select PPC_PCI_CHOICE select FSL_PCI if PCI select ARCH_WANT_OPTIONAL_GPIOLIB + select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_DESC config MPC5121_ADS bool "Freescale MPC5121E ADS" diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 3d361f2..66dc562 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -407,6 +407,8 @@ config SERIAL_CONSOLE config SPARC_LEON bool "Sparc Leon processor family" depends on SPARC32 + select USB_EHCI_BIG_ENDIAN_MMIO + select USB_EHCI_BIG_ENDIAN_DESC ---help--- If you say Y here if you are running on a SPARC-LEON processor. The LEON processor is a synthesizable VHDL model of the diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1714c6d..436b682 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -110,18 +110,11 @@ config USB_EHCI_HCD_PMC_MSP config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ - ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ - PPC_MPC512x || CPU_CAVIUM_OCTEON || \ - PMC_MSP || SPARC_LEON || MIPS_SEAD3) - default y + depends on USB_EHCI_HCD config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ - PPC_MPC512x || PMC_MSP || SPARC_LEON || \ - MIPS_SEAD3) - default y + depends on USB_EHCI_HCD config XPS_USB_HCD_XILINX bool "Use Xilinx usb host EHCI controller core" -- cgit v0.10.2 From 42443dc44b0b0e29ea80d320882d4ab85f9340b3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Apr 2013 14:29:27 +0200 Subject: USB: enclose EHCI HCD drivers within an if USB_EHCI_HCD block Thist patch removes the depends on USB_EHCI_HCD that the various USB EHCI HCD drivers use and encloses every driver within an if USB_EHCI_HCD / endif block. The EHCI HCD platform and Octeon drivers have been moved around to remain enclosed within this block. Acked-by: Alan Stern Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 436b682..bc9123c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,14 +93,19 @@ config USB_EHCI_TT_NEWSCHED If unsure, say Y. +config USB_FSL_MPH_DR_OF + tristate + +if USB_EHCI_HCD + config USB_EHCI_PCI tristate - depends on USB_EHCI_HCD && PCI + depends on PCI default y config USB_EHCI_HCD_PMC_MSP tristate "EHCI support for on-chip PMC MSP71xx USB controller" - depends on USB_EHCI_HCD && MSP_HAS_USB + depends on MSP_HAS_USB default n select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO @@ -110,15 +115,13 @@ config USB_EHCI_HCD_PMC_MSP config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD config XPS_USB_HCD_XILINX bool "Use Xilinx usb host EHCI controller core" - depends on USB_EHCI_HCD && (PPC32 || MICROBLAZE) + depends on (PPC32 || MICROBLAZE) select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_MMIO ---help--- @@ -127,12 +130,9 @@ config XPS_USB_HCD_XILINX support both high speed and full speed devices, or high speed devices only. -config USB_FSL_MPH_DR_OF - tristate - config USB_EHCI_FSL bool "Support for Freescale PPC on-chip EHCI USB controller" - depends on USB_EHCI_HCD && FSL_SOC + depends on FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF ---help--- @@ -140,14 +140,14 @@ config USB_EHCI_FSL config USB_EHCI_MXC tristate "Support for Freescale i.MX on-chip EHCI USB controller" - depends on USB_EHCI_HCD && ARCH_MXC + depends on ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- Variation of ARC USB block used in some Freescale chips. config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" - depends on USB_EHCI_HCD && ARCH_OMAP + depends on ARCH_OMAP select NOP_USB_XCEIV default y ---help--- @@ -183,7 +183,7 @@ config USB_EHCI_HCD_AT91 config USB_EHCI_MSM tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller" - depends on USB_EHCI_HCD && ARCH_MSM + depends on ARCH_MSM select USB_EHCI_ROOT_HUB_TT select USB_MSM_OTG ---help--- @@ -196,7 +196,7 @@ config USB_EHCI_MSM config USB_EHCI_TEGRA boolean "NVIDIA Tegra HCD support" - depends on USB_EHCI_HCD && ARCH_TEGRA + depends on ARCH_TEGRA select USB_EHCI_ROOT_HUB_TT select USB_PHY help @@ -205,7 +205,7 @@ config USB_EHCI_TEGRA config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" - depends on USB_EHCI_HCD && PPC_OF + depends on PPC_OF default y ---help--- Enables support for the USB controller present on the PowerPC @@ -213,21 +213,21 @@ config USB_EHCI_HCD_PPC_OF config USB_EHCI_SH bool "EHCI support for SuperH USB controller" - depends on USB_EHCI_HCD && SUPERH + depends on SUPERH ---help--- Enables support for the on-chip EHCI controller on the SuperH. If you use the PCI EHCI controller, this option is not necessary. config USB_EHCI_S5P tristate "EHCI support for Samsung S5P/EXYNOS SoC Series" - depends on USB_EHCI_HCD && PLAT_S5P + depends on PLAT_S5P help Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's on-chip EHCI controller. config USB_EHCI_MV bool "EHCI support for Marvell PXA/MMP USB controller" - depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP) + depends on (ARCH_PXA || ARCH_MMP) select USB_EHCI_ROOT_HUB_TT ---help--- Enables support for Marvell (including PXA and MMP series) on-chip @@ -240,13 +240,13 @@ config USB_EHCI_MV config USB_W90X900_EHCI bool "W90X900(W90P910) EHCI support" - depends on USB_EHCI_HCD && ARCH_W90X900 + depends on ARCH_W90X900 ---help--- Enables support for the W90X900 USB controller config USB_CNS3XXX_EHCI bool "Cavium CNS3XXX EHCI Module (DEPRECATED)" - depends on USB_EHCI_HCD && ARCH_CNS3XXX + depends on ARCH_CNS3XXX select USB_EHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use @@ -258,7 +258,7 @@ config USB_CNS3XXX_EHCI config USB_EHCI_ATH79 bool "EHCI support for AR7XXX/AR9XXX SoCs (DEPRECATED)" - depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X) + depends on (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X) select USB_EHCI_ROOT_HUB_TT select USB_EHCI_HCD_PLATFORM default y @@ -269,6 +269,28 @@ config USB_EHCI_ATH79 Enables support for the built-in EHCI controller present on the Atheros AR7XXX/AR9XXX SoCs. +config USB_EHCI_HCD_PLATFORM + tristate "Generic EHCI driver for a platform device" + default n + ---help--- + Adds an EHCI host driver for a generic platform device, which + provides a memory space and an irq. + + If unsure, say N. + +config USB_OCTEON_EHCI + bool "Octeon on-chip EHCI support" + depends on CPU_CAVIUM_OCTEON + default n + select USB_EHCI_BIG_ENDIAN_MMIO + help + Enable support for the Octeon II SOC's on-chip EHCI + controller. It is needed for high-speed (480Mbit/sec) + USB 2.0 device support. All CN6XXX based chips with USB are + supported. + +endif # USB_EHCI_HCD + config USB_OXU210HP_HCD tristate "OXU210HP HCD support" depends on GENERIC_HARDIRQS @@ -450,15 +472,6 @@ config USB_OHCI_HCD_PLATFORM If unsure, say N. -config USB_EHCI_HCD_PLATFORM - tristate "Generic EHCI driver for a platform device" - depends on USB_EHCI_HCD - default n - ---help--- - Adds an EHCI host driver for a generic platform device, which - provides a memory space and an irq. - - If unsure, say N. config USB_OHCI_BIG_ENDIAN_DESC bool @@ -651,16 +664,6 @@ config USB_IMX21_HCD To compile this driver as a module, choose M here: the module will be called "imx21-hcd". -config USB_OCTEON_EHCI - bool "Octeon on-chip EHCI support" - depends on USB_EHCI_HCD && CPU_CAVIUM_OCTEON - default n - select USB_EHCI_BIG_ENDIAN_MMIO - help - Enable support for the Octeon II SOC's on-chip EHCI - controller. It is needed for high-speed (480Mbit/sec) - USB 2.0 device support. All CN6XXX based chips with USB are - supported. config USB_OCTEON_OHCI bool "Octeon on-chip OHCI support" -- cgit v0.10.2 From 3ad3ca056eefb75a587434bbcb2460864377f17c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Apr 2013 14:29:28 +0200 Subject: USB: enclose all depends on USB_OHCI_HCD within an if USB_OHCI_HCD block This patch removes the various depends on USB_OHCI_HCD from the OHCI HCD drivers and enclose them within an if USB_OHCI_HCD / endif block. The Octeon OHCI HCD driver has been moved around to remain in this block. Acked-by: Alan Stern Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index bc9123c..76f8bbc 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -358,16 +358,18 @@ config USB_OHCI_HCD To compile this driver as a module, choose M here: the module will be called ohci-hcd. +if USB_OHCI_HCD + config USB_OHCI_HCD_OMAP1 bool "OHCI support for OMAP1/2 chips" - depends on USB_OHCI_HCD && ARCH_OMAP1 + depends on ARCH_OMAP1 default y ---help--- Enables support for the OHCI controller on OMAP1/2 chips. config USB_OHCI_HCD_OMAP3 bool "OHCI support for OMAP3 and later chips" - depends on USB_OHCI_HCD && (ARCH_OMAP3 || ARCH_OMAP4) + depends on (ARCH_OMAP3 || ARCH_OMAP4) default y ---help--- Enables support for the on-chip OHCI controller on @@ -375,7 +377,7 @@ config USB_OHCI_HCD_OMAP3 config USB_OHCI_ATH79 bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs (DEPRECATED)" - depends on USB_OHCI_HCD && (SOC_AR71XX || SOC_AR724X) + depends on (SOC_AR71XX || SOC_AR724X) select USB_OHCI_HCD_PLATFORM default y help @@ -387,7 +389,7 @@ config USB_OHCI_ATH79 config USB_OHCI_HCD_PPC_OF_BE bool "OHCI support for OF platform bus (big endian)" - depends on USB_OHCI_HCD && PPC_OF + depends on PPC_OF select USB_OHCI_BIG_ENDIAN_DESC select USB_OHCI_BIG_ENDIAN_MMIO ---help--- @@ -396,7 +398,7 @@ config USB_OHCI_HCD_PPC_OF_BE config USB_OHCI_HCD_PPC_OF_LE bool "OHCI support for OF platform bus (little endian)" - depends on USB_OHCI_HCD && PPC_OF + depends on PPC_OF select USB_OHCI_LITTLE_ENDIAN ---help--- Enables support for little-endian USB controllers present on the @@ -404,12 +406,12 @@ config USB_OHCI_HCD_PPC_OF_LE config USB_OHCI_HCD_PPC_OF bool - depends on USB_OHCI_HCD && PPC_OF + depends on PPC_OF default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE config USB_OHCI_HCD_PCI bool "OHCI support for PCI-bus USB controllers" - depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx || USB_OHCI_HCD_PPC_OF) + depends on PCI && (STB03xxx || PPC_MPC52xx || USB_OHCI_HCD_PPC_OF) default y select USB_OHCI_LITTLE_ENDIAN ---help--- @@ -418,7 +420,7 @@ config USB_OHCI_HCD_PCI config USB_OHCI_HCD_SSB bool "OHCI support for Broadcom SSB OHCI core (DEPRECATED)" - depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) + depends on (SSB = y || SSB = USB_OHCI_HCD) select USB_HCD_SSB select USB_OHCI_HCD_PLATFORM default n @@ -436,7 +438,7 @@ config USB_OHCI_HCD_SSB config USB_OHCI_SH bool "OHCI support for SuperH USB controller (DEPRECATED)" - depends on USB_OHCI_HCD && SUPERH + depends on SUPERH select USB_OHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use @@ -447,13 +449,13 @@ config USB_OHCI_SH config USB_OHCI_EXYNOS boolean "OHCI support for Samsung EXYNOS SoC Series" - depends on USB_OHCI_HCD && ARCH_EXYNOS + depends on ARCH_EXYNOS help Enable support for the Samsung Exynos SOC's on-chip OHCI controller. config USB_CNS3XXX_OHCI bool "Cavium CNS3XXX OHCI Module (DEPRECATED)" - depends on USB_OHCI_HCD && ARCH_CNS3XXX + depends on ARCH_CNS3XXX select USB_OHCI_HCD_PLATFORM ---help--- This option is deprecated now and the driver was removed, use @@ -464,7 +466,6 @@ config USB_CNS3XXX_OHCI config USB_OHCI_HCD_PLATFORM bool "Generic OHCI driver for a platform device" - depends on USB_OHCI_HCD default n ---help--- Adds an OHCI host driver for a generic platform device, which @@ -472,23 +473,33 @@ config USB_OHCI_HCD_PLATFORM If unsure, say N. +config USB_OCTEON_OHCI + bool "Octeon on-chip OHCI support" + depends on CPU_CAVIUM_OCTEON + default USB_OCTEON_EHCI + select USB_OHCI_BIG_ENDIAN_MMIO + select USB_OHCI_LITTLE_ENDIAN + help + Enable support for the Octeon II SOC's on-chip OHCI + controller. It is needed for low-speed USB 1.0 device + support. All CN6XXX based chips with USB are supported. + config USB_OHCI_BIG_ENDIAN_DESC bool - depends on USB_OHCI_HCD default n config USB_OHCI_BIG_ENDIAN_MMIO bool - depends on USB_OHCI_HCD default n config USB_OHCI_LITTLE_ENDIAN bool - depends on USB_OHCI_HCD default n if STB03xxx || PPC_MPC52xx default y +endif # USB_OHCI_HCD + config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" depends on PCI || SPARC_LEON || ARCH_VT8500 @@ -665,16 +676,6 @@ config USB_IMX21_HCD module will be called "imx21-hcd". -config USB_OCTEON_OHCI - bool "Octeon on-chip OHCI support" - depends on USB_OHCI_HCD && CPU_CAVIUM_OCTEON - default USB_OCTEON_EHCI - select USB_OHCI_BIG_ENDIAN_MMIO - select USB_OHCI_LITTLE_ENDIAN - help - Enable support for the Octeon II SOC's on-chip OHCI - controller. It is needed for low-speed USB 1.0 device - support. All CN6XXX based chips with USB are supported. config USB_OCTEON2_COMMON bool -- cgit v0.10.2 From 0fcb99898633e5def627833613cf262fe7f30ac4 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Apr 2013 14:29:29 +0200 Subject: USB: enclose USB_XHCI_HCD related symbols within a if USB_XHCI_HCD block This patch encloses all symbols depending on USB_XHCI_HCD within an if USB_XHCI_HCD / endif block. Acked-by: Alan Stern Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 76f8bbc..c0be25c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -25,13 +25,13 @@ config USB_XHCI_HCD To compile this driver as a module, choose M here: the module will be called xhci-hcd. +if USB_XHCI_HCD + config USB_XHCI_PLATFORM tristate - depends on USB_XHCI_HCD config USB_XHCI_HCD_DEBUGGING bool "Debugging for the xHCI host controller" - depends on USB_XHCI_HCD ---help--- Say 'Y' to turn on debugging for the xHCI host controller driver. This will spew debugging output, even in interrupt context. @@ -39,6 +39,8 @@ config USB_XHCI_HCD_DEBUGGING If unsure, say N. +endif # USB_XHCI_HCD + config USB_EHCI_HCD tristate "EHCI HCD (USB 2.0) support" depends on USB_ARCH_HAS_EHCI -- cgit v0.10.2 From 1157f69bee295987952bf0cbbcbc419d497eb51c Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Thu, 11 Apr 2013 15:08:20 +0200 Subject: usb-serial: add support for USB Wishbone-serial adapters Wishbone is an open hardware SoC bus commonly used in FPGA designs. Bus access can be serialized using the Etherbone protocol . This driver is intended to be used with devices which attach their internal Wishbone bus to a USB serial interface using the Etherbone protocol. A userspace library is required to speak the protocol made available by this driver as ttyUSBx. Signed-off-by: Wesley W. Terpstra Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index bf37336..1d55762 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -667,6 +667,23 @@ config USB_SERIAL_ZIO To compile this driver as a module, choose M here: the module will be called zio. +config USB_SERIAL_WISHBONE + tristate "USB-Wishbone adapter interface driver" + help + Say Y here if you want to use a USB attached Wishbone bus. + + Wishbone is an open hardware SoC bus commonly used in FPGA + designs. Bus access can be serialized using the Etherbone + protocol . + + This driver is intended to be used with devices which attach + their internal Wishbone bus to a USB serial interface using + the Etherbone protocol. A userspace library is required to + speak the protocol made available by this driver as ttyUSBx. + + To compile this driver as a module, choose M here: the + module will be called wishbone-serial. + config USB_SERIAL_ZTE tristate "ZTE USB serial driver" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index eaf5ca1..cec63fa 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o +obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL) += vivopay-serial.o diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c new file mode 100644 index 0000000..481ec66 --- /dev/null +++ b/drivers/usb/serial/wishbone-serial.c @@ -0,0 +1,95 @@ +/* + * USB Wishbone-Serial adapter driver + * + * Copyright (C) 2013 Wesley W. Terpstra + * Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GSI_VENDOR_OPENCLOSE 0xB0 + +static const struct usb_device_id id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x1D50, 0x6062, 0xFF, 0xFF, 0xFF) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Etherbone must be told that a new stream has begun before data arrives. + * This is necessary to restart the negotiation of Wishbone bus parameters. + * Similarly, when the stream ends, Etherbone must be told so that the cycle + * line can be driven low in the case that userspace failed to do so. + */ +static int usb_gsi_openclose(struct usb_serial_port *port, int value) +{ + struct usb_device *dev = port->serial->dev; + + return usb_control_msg( + dev, + usb_sndctrlpipe(dev, 0), /* Send to EP0OUT */ + GSI_VENDOR_OPENCLOSE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + value, /* wValue = device is open(1) or closed(0) */ + port->serial->interface->cur_altsetting->desc.bInterfaceNumber, + 0, 0, /* There is no data stage */ + 5000); /* Timeout till operation fails */ +} + +static int wishbone_serial_open(struct tty_struct *tty, + struct usb_serial_port *port) +{ + int retval; + + retval = usb_gsi_openclose(port, 1); + if (retval) { + dev_err(&port->serial->dev->dev, + "Could not mark device as open (%d)\n", + retval); + return retval; + } + + retval = usb_serial_generic_open(tty, port); + if (retval) + usb_gsi_openclose(port, 0); + + return retval; +} + +static void wishbone_serial_close(struct usb_serial_port *port) +{ + usb_serial_generic_close(port); + usb_gsi_openclose(port, 0); +} + +static struct usb_serial_driver wishbone_serial_device = { + .driver = { + .owner = THIS_MODULE, + .name = "wishbone_serial", + }, + .id_table = id_table, + .num_ports = 1, + .open = &wishbone_serial_open, + .close = &wishbone_serial_close, +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &wishbone_serial_device, NULL +}; + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_AUTHOR("Wesley W. Terpstra "); +MODULE_DESCRIPTION("USB Wishbone-Serial adapter"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b8a261b59c8c6014d7dc984811ec81f14ef6f767 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 11 Apr 2013 08:45:34 -0700 Subject: USB: serial: wishbone-serial: fix up minor sparse warning This fixes a sparse warning where we should be using NULL instead of 0 Cc: Wesley W. Terpstra Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index 481ec66..100573c 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -43,7 +43,7 @@ static int usb_gsi_openclose(struct usb_serial_port *port, int value) USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, value, /* wValue = device is open(1) or closed(0) */ port->serial->interface->cur_altsetting->desc.bInterfaceNumber, - 0, 0, /* There is no data stage */ + NULL, 0, /* There is no data stage */ 5000); /* Timeout till operation fails */ } -- cgit v0.10.2 From 2871782ae1cbca372eabf599576d611d861310e6 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Thu, 11 Apr 2013 17:12:30 +0530 Subject: usb: ohci-exynos: skip phy setup for Exynos5440 based platforms Exynos5440 does not require any explict USB phy configuration. So skip the USB phy configuration for Exynos5440 based platforms. Signed-off-by: Thomas Abraham Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 4b469e0..509fa51 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -34,7 +34,7 @@ static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) if (exynos_ohci->phy) usb_phy_init(exynos_ohci->phy); - else if (exynos_ohci->pdata->phy_init) + else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_init) exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); } @@ -44,7 +44,7 @@ static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) if (exynos_ohci->phy) usb_phy_shutdown(exynos_ohci->phy); - else if (exynos_ohci->pdata->phy_exit) + else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_exit) exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); } @@ -127,6 +127,10 @@ static int exynos_ohci_probe(struct platform_device *pdev) if (!exynos_ohci) return -ENOMEM; + if (of_device_is_compatible(pdev->dev.of_node, + "samsung,exynos5440-ohci")) + goto skip_phy; + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { /* Fallback to pdata */ @@ -141,6 +145,8 @@ static int exynos_ohci_probe(struct platform_device *pdev) exynos_ohci->otg = phy->otg; } +skip_phy: + exynos_ohci->dev = &pdev->dev; hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, @@ -311,6 +317,7 @@ static const struct dev_pm_ops exynos_ohci_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id exynos_ohci_match[] = { { .compatible = "samsung,exynos4210-ohci" }, + { .compatible = "samsung,exynos5440-ohci" }, {}, }; MODULE_DEVICE_TABLE(of, exynos_ohci_match); -- cgit v0.10.2 From cccfc53626bb74f5b9fbacfdcd3c475f0e1f359b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Apr 2013 22:49:39 +0200 Subject: usb: exynos: do not include plat/usb-phy.h The definitions have moved to include/linux/usb/samsung-usb-phy.h, and plat/usb-phy.h is unavailable from drivers in a multiplatform configuration. Also fix up the plat/usb-phy.h header file to use the definitions from the new header instead of providing a separate copy. Signed-off-by: Arnd Bergmann Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c index b81cc56..6af4066 100644 --- a/arch/arm/mach-exynos/setup-usb-phy.c +++ b/arch/arm/mach-exynos/setup-usb-phy.c @@ -204,9 +204,9 @@ static int exynos4210_usb_phy1_exit(struct platform_device *pdev) int s5p_usb_phy_init(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return exynos4210_usb_phy0_init(pdev); - else if (type == S5P_USB_PHY_HOST) + else if (type == USB_PHY_TYPE_HOST) return exynos4210_usb_phy1_init(pdev); return -EINVAL; @@ -214,9 +214,9 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type) int s5p_usb_phy_exit(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return exynos4210_usb_phy0_exit(pdev); - else if (type == S5P_USB_PHY_HOST) + else if (type == USB_PHY_TYPE_HOST) return exynos4210_usb_phy1_exit(pdev); return -EINVAL; diff --git a/arch/arm/mach-s3c64xx/setup-usb-phy.c b/arch/arm/mach-s3c64xx/setup-usb-phy.c index c8174d9..ca960bd 100644 --- a/arch/arm/mach-s3c64xx/setup-usb-phy.c +++ b/arch/arm/mach-s3c64xx/setup-usb-phy.c @@ -76,7 +76,7 @@ static int s3c_usb_otgphy_exit(struct platform_device *pdev) int s5p_usb_phy_init(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return s3c_usb_otgphy_init(pdev); return -EINVAL; @@ -84,7 +84,7 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type) int s5p_usb_phy_exit(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return s3c_usb_otgphy_exit(pdev); return -EINVAL; diff --git a/arch/arm/mach-s5pv210/setup-usb-phy.c b/arch/arm/mach-s5pv210/setup-usb-phy.c index 356a090..b2ee533 100644 --- a/arch/arm/mach-s5pv210/setup-usb-phy.c +++ b/arch/arm/mach-s5pv210/setup-usb-phy.c @@ -80,7 +80,7 @@ static int s5pv210_usb_otgphy_exit(struct platform_device *pdev) int s5p_usb_phy_init(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return s5pv210_usb_otgphy_init(pdev); return -EINVAL; @@ -88,7 +88,7 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type) int s5p_usb_phy_exit(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) + if (type == USB_PHY_TYPE_DEVICE) return s5pv210_usb_otgphy_exit(pdev); return -EINVAL; diff --git a/arch/arm/plat-samsung/include/plat/usb-phy.h b/arch/arm/plat-samsung/include/plat/usb-phy.h index 959bcdb..ab34dfa 100644 --- a/arch/arm/plat-samsung/include/plat/usb-phy.h +++ b/arch/arm/plat-samsung/include/plat/usb-phy.h @@ -11,10 +11,7 @@ #ifndef __PLAT_SAMSUNG_USB_PHY_H #define __PLAT_SAMSUNG_USB_PHY_H __FILE__ -enum s5p_usb_phy_type { - S5P_USB_PHY_DEVICE, - S5P_USB_PHY_HOST, -}; +#include extern int s5p_usb_phy_init(struct platform_device *pdev, int type); extern int s5p_usb_phy_exit(struct platform_device *pdev, int type); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 580548a..6357752 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 509fa51..114583a 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -17,7 +17,6 @@ #include #include #include -#include struct exynos_ohci_hcd { struct device *dev; -- cgit v0.10.2 From 61ac6ac8d662ac7ac67c864954d39d1b19948354 Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Wed, 17 Apr 2013 18:03:46 +0800 Subject: usb: remove redundant tdi_reset We remove the redundant tdi_reset in ehci_setup since there is already it in ehci_reset. It was observed that the duplicated tdi_reset was causing the PHY_CLK_VALID bit unstable. Reported-by: Michael Braun Signed-off-by: Shengzhou Liu Acked-by: Alan Stern Cc: stable # 3.6+ Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2e0c2bd..312fc10 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -673,9 +673,6 @@ int ehci_setup(struct usb_hcd *hcd) if (retval) return retval; - if (ehci_is_TDI(ehci)) - tdi_reset(ehci); - ehci_reset(ehci); return 0; -- cgit v0.10.2 From 75b9130e8af64c6878a1aa396a8446f3ce2cfb49 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 16 Apr 2013 22:44:07 -0700 Subject: usb: storage: Add usb_stor_dbg, reduce object size Reduce the size of the objects by consolidating the duplicated USB_STORAGE into a single function. Add function usb_stor_dbg to emit debugging messages. Always validate the format and arguments. Reduce the number of uses of CONFIG_USB_STORAGE_DEBUG. Reduces size of objects ~7KB when CONFIG_USB_STORAGE_DEBUG is set. $ size drivers/usb/storage/built-in.o* text data bss dec hex filename 140133 55296 70312 265741 40e0d drivers/usb/storage/built-in.o.new 147494 55248 70296 273038 42a8e drivers/usb/storage/built-in.o.old Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index a2b5526..b428129 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -150,7 +150,7 @@ void usb_stor_show_command(struct scsi_cmnd *srb) default: what = "(unknown command)"; break; } US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len); - US_DEBUGP(""); + US_DEBUGP("bytes: "); for (i = 0; i < srb->cmd_len && i < 16; i++) US_DEBUGPX(" %02x", srb->cmnd[i]); US_DEBUGPX("\n"); @@ -175,3 +175,21 @@ void usb_stor_show_sense( US_DEBUGPX(what, ascq); US_DEBUGPX("\n"); } + +int usb_stor_dbg(const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + r = printk(KERN_DEBUG USB_STORAGE "%pV", &vaf); + + va_end(args); + + return r; +} diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index dbb985d..d4280e1 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -50,12 +50,16 @@ void usb_stor_show_command(struct scsi_cmnd *srb); void usb_stor_show_sense( unsigned char key, unsigned char asc, unsigned char ascq ); -#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x ) -#define US_DEBUGPX(x...) printk( x ) -#define US_DEBUG(x) x +__printf(1, 2) int usb_stor_dbg(const char *fmt, ...); + +#define US_DEBUGP(fmt, ...) usb_stor_dbg(fmt, ##__VA_ARGS__) +#define US_DEBUGPX(fmt, ...) printk(fmt, ##__VA_ARGS__) +#define US_DEBUG(x) x #else -#define US_DEBUGP(x...) -#define US_DEBUGPX(x...) +#define US_DEBUGP(fmt, ...) \ + do { if (0) printk(fmt, ##__VA_ARGS__); } while (0) +#define US_DEBUGPX(fmt, ...) \ + do { if (0) printk(fmt, ##__VA_ARGS__); } while (0) #define US_DEBUG(x) #endif diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 06a3d22..55571ae 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -926,10 +926,6 @@ static int isd200_try_enum(struct us_data *us, unsigned char master_slave, /* loop until we detect !BSY or timeout */ while(1) { -#ifdef CONFIG_USB_STORAGE_DEBUG - char* mstr = master_slave == ATA_ADDRESS_DEVHEAD_STD ? - "Master" : "Slave"; -#endif status = isd200_action( us, ACTION_ENUM, NULL, master_slave ); if ( status != ISD200_GOOD ) @@ -942,9 +938,13 @@ static int isd200_try_enum(struct us_data *us, unsigned char master_slave, if (!detect) { if (regs[ATA_REG_STATUS_OFFSET] & ATA_BUSY) { - US_DEBUGP(" %s status is still BSY, try again...\n",mstr); + US_DEBUGP(" %s status is still BSY, try again...\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); } else { - US_DEBUGP(" %s status !BSY, continue with next operation\n",mstr); + US_DEBUGP(" %s status !BSY, continue with next operation\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); break; } } -- cgit v0.10.2 From 1361bf4b9f9ef45e628a5b89e0fd9bedfdcb7104 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Apr 2013 11:08:33 +0200 Subject: usbfs: Always allow ctrl requests with USB_RECIP_ENDPOINT on the ctrl ep When usbfs receives a ctrl-request from userspace it calls check_ctrlrecip, which for a request with USB_RECIP_ENDPOINT tries to map this to an interface to see if this interface is claimed, except for ctrl-requests with a type of USB_TYPE_VENDOR. When trying to use this device: http://www.akaipro.com/eiepro redirected to a Windows vm running on qemu on top of Linux. The windows driver makes a ctrl-req with USB_TYPE_CLASS and USB_RECIP_ENDPOINT with index 0, and the mapping of the endpoint (0) to the interface fails since ep 0 is the ctrl endpoint and thus never is part of an interface. This patch fixes this ctrl-req failing by skipping the checkintf call for USB_RECIP_ENDPOINT ctrl-reqs on the ctrl endpoint. Reported-by: Dave Stikkolorum Tested-by: Dave Stikkolorum Signed-off-by: Hans de Goede Acked-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 8823e98..caefc80 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -739,6 +739,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: + if ((index & ~USB_DIR_IN) == 0) + return 0; ret = findintfep(ps->dev, index); if (ret >= 0) ret = checkintf(ps, ret); -- cgit v0.10.2 From 7c4ebe68ecdeaa6b2846863c29e248615c366e19 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 15 Apr 2013 14:50:34 +0300 Subject: USB: ehci-omap: Don't select any PHY driver Don't select NOP_USB_XCEIV. Instead, board config must select USB_PHY and the appropriate PHY driver. Also add a hint in Kconfig so that users enabling this driver manually enable the right PHY drivers as well. Gets rid of the below warnings when USB_EHCI_HCD_OMAP is enabled. warning: (USB_EHCI_HCD_OMAP) selects NOP_USB_XCEIV which has unmet direct dependencies (USB_SUPPORT && USB_PHY) warning: (USB_EHCI_HCD_OMAP) selects NOP_USB_XCEIV which has unmet direct dependencies (USB_SUPPORT && USB_PHY) Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c0be25c..c558472 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -150,11 +150,13 @@ config USB_EHCI_MXC config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on ARCH_OMAP - select NOP_USB_XCEIV default y ---help--- Enables support for the on-chip EHCI controller on OMAP3 and later chips. + If your system uses a PHY on the USB port, you will need to + enable USB_PHY and the appropriate PHY driver as well. Most + boards need the NOP_USB_XCEIV PHY driver. config USB_EHCI_HCD_ORION tristate "Support for Marvell EBU on-chip EHCI USB controller" -- cgit v0.10.2 From 2bef64b89ae315e7935067ce8db901783deaf857 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 17 Apr 2013 11:24:25 +0300 Subject: USB: ehci-omap: Improve PHY error handling As the USB PHY layer never returns NULL we don't need to check for that condition. Signed-off-by: Roger Quadros Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 5de3e43..3d1491b 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -175,12 +175,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) phy = devm_usb_get_phy_by_phandle(dev, "phys", i); else phy = devm_usb_get_phy_dev(dev, i); - if (IS_ERR(phy) || !phy) { + if (IS_ERR(phy)) { /* Don't bail out if PHY is not absolutely necessary */ if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) continue; - ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; + ret = PTR_ERR(phy); dev_err(dev, "Can't get PHY device for port %d: %d\n", i, ret); goto err_phy; -- cgit v0.10.2 From 81f58c67b1d71b3a1a6c2bb077844249790807c8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:19 +0200 Subject: USB: omninet: use kzalloc for private data Make sure the port private data, which contains the write sequence number, is cleared at allocation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 1e1cafe..ea1105c 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -112,7 +112,7 @@ static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; - od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL); + od = kzalloc(sizeof(*od), GFP_KERNEL); if (!od) return -ENOMEM; -- cgit v0.10.2 From b42abbcde44a0a58e7dbaa4e3f0adc4cf5b74684 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:20 +0200 Subject: USB: omninet: clean up protocol description Clean up and fix typos in protocol comment. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ea1105c..ec16b92 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -74,29 +74,28 @@ static struct usb_serial_driver * const serial_drivers[] = { }; -/* The protocol. +/* + * The protocol. * * The omni.net always exchange 64 bytes of data with the host. The first - * four bytes are the control header, you can see it in the above structure. + * four bytes are the control header. * * oh_seq is a sequence number. Don't know if/how it's used. * oh_len is the length of the data bytes in the packet. * oh_xxx Bit-mapped, related to handshaking and status info. - * I normally set it to 0x03 in trasmitted frames. + * I normally set it to 0x03 in transmitted frames. * 7: Active when the TA is in a CONNECTed state. * 6: unknown * 5: handshaking, unknown * 4: handshaking, unknown * 3: unknown, usually 0 * 2: unknown, usually 0 - * 1: handshaking, unknown, usually set to 1 in trasmitted frames - * 0: handshaking, unknown, usually set to 1 in trasmitted frames + * 1: handshaking, unknown, usually set to 1 in transmitted frames + * 0: handshaking, unknown, usually set to 1 in transmitted frames * oh_pad Probably a pad byte. * * After the header you will find data bytes if oh_len was greater than zero. - * */ - struct omninet_header { __u8 oh_seq; __u8 oh_len; -- cgit v0.10.2 From d91641b161594672242b3c5d8656ad3b2ef58f34 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:21 +0200 Subject: USB: omninet: clean up protocol defines Remove redundant data-offset define, which was really just the header length. Add payload-size define and use the bulk-out size define for the actual bulk-out size. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ec16b92..9dcaa77 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -154,9 +154,9 @@ static void omninet_close(struct usb_serial_port *port) } -#define OMNINET_DATAOFFSET 0x04 -#define OMNINET_HEADERLEN sizeof(struct omninet_header) -#define OMNINET_BULKOUTSIZE (64 - OMNINET_HEADERLEN) +#define OMNINET_HEADERLEN 4 +#define OMNINET_BULKOUTSIZE 64 +#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) static void omninet_read_bulk_callback(struct urb *urb) { @@ -173,7 +173,7 @@ static void omninet_read_bulk_callback(struct urb *urb) } if (urb->actual_length && header->oh_len) { - tty_insert_flip_string(&port->port, data + OMNINET_DATAOFFSET, + tty_insert_flip_string(&port->port, data + OMNINET_HEADERLEN, header->oh_len); tty_flip_buffer_push(&port->port); } @@ -208,9 +208,9 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, return 0; } - count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; + count = (count > OMNINET_PAYLOADSIZE) ? OMNINET_PAYLOADSIZE : count; - memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, + memcpy(wport->write_urb->transfer_buffer + OMNINET_HEADERLEN, buf, count); usb_serial_debug_data(&port->dev, __func__, count, @@ -222,7 +222,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, header->oh_pad = 0x00; /* send the data out the bulk port, always 64 bytes */ - wport->write_urb->transfer_buffer_length = 64; + wport->write_urb->transfer_buffer_length = OMNINET_BULKOUTSIZE; result = usb_submit_urb(wport->write_urb, GFP_ATOMIC); if (result) { -- cgit v0.10.2 From a6c042f95031afbeb0b0fb77643bc9211a3f2e2e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:22 +0200 Subject: USB: omninet: refactor read-urb processing Refactor read-urb processing, and add sanity checks on header and data lengths. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 9dcaa77..7aaf969 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -158,11 +158,26 @@ static void omninet_close(struct usb_serial_port *port) #define OMNINET_BULKOUTSIZE 64 #define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) +static void omninet_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + const struct omninet_header *hdr = urb->transfer_buffer; + const unsigned char *data; + size_t data_len; + + if (urb->actual_length <= OMNINET_HEADERLEN || !hdr->oh_len) + return; + + data = (char *)urb->transfer_buffer + OMNINET_HEADERLEN; + data_len = min_t(size_t, urb->actual_length - OMNINET_HEADERLEN, + hdr->oh_len); + tty_insert_flip_string(&port->port, data, data_len); + tty_flip_buffer_push(&port->port); +} + static void omninet_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - unsigned char *data = urb->transfer_buffer; - struct omninet_header *header = (struct omninet_header *) &data[0]; int status = urb->status; int result; @@ -172,11 +187,7 @@ static void omninet_read_bulk_callback(struct urb *urb) return; } - if (urb->actual_length && header->oh_len) { - tty_insert_flip_string(&port->port, data + OMNINET_HEADERLEN, - header->oh_len); - tty_flip_buffer_push(&port->port); - } + omninet_process_read_urb(urb); /* Continue trying to always read */ result = usb_submit_urb(urb, GFP_ATOMIC); -- cgit v0.10.2 From fbf947736968cbb0e55ec4b5d861d31a4a106c99 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:23 +0200 Subject: USB: omninet: switch to generic read implementation Switch to the more efficient generic read implementation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 7aaf969..5739bf6 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -33,8 +33,7 @@ /* function prototypes */ static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port); -static void omninet_close(struct usb_serial_port *port); -static void omninet_read_bulk_callback(struct urb *urb); +static void omninet_process_read_urb(struct urb *urb); static void omninet_write_bulk_callback(struct urb *urb); static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -61,11 +60,10 @@ static struct usb_serial_driver zyxel_omninet_device = { .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, .open = omninet_open, - .close = omninet_close, .write = omninet_write, .write_room = omninet_write_room, - .read_bulk_callback = omninet_read_bulk_callback, .write_bulk_callback = omninet_write_bulk_callback, + .process_read_urb = omninet_process_read_urb, .disconnect = omninet_disconnect, }; @@ -134,26 +132,13 @@ static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; - int result = 0; wport = serial->port[1]; tty_port_tty_set(&wport->port, tty); - /* Start reading from the device */ - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - return result; -} - -static void omninet_close(struct usb_serial_port *port) -{ - usb_kill_urb(port->read_urb); + return usb_serial_generic_open(tty, port); } - #define OMNINET_HEADERLEN 4 #define OMNINET_BULKOUTSIZE 64 #define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) @@ -175,28 +160,6 @@ static void omninet_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -static void omninet_read_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - int status = urb->status; - int result; - - if (status) { - dev_dbg(&port->dev, "%s - nonzero read bulk status received: %d\n", - __func__, status); - return; - } - - omninet_process_read_urb(urb); - - /* Continue trying to always read */ - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); -} - static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { -- cgit v0.10.2 From 8e34c6c2ee9ed4cafe01e760e0c4bc1c8f7e9cb7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:24 +0200 Subject: USB: kobil_sct: fix broken debug code Replace broken and commented-out debug code with usb_serial_debug_data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 903d938..da1a372 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -333,24 +333,8 @@ static void kobil_read_int_callback(struct urb *urb) } if (urb->actual_length) { - - /* BEGIN DEBUG */ - /* - char *dbg_data; - - dbg_data = kzalloc((3 * purb->actual_length + 10) - * sizeof(char), GFP_KERNEL); - if (! dbg_data) { - return; - } - for (i = 0; i < purb->actual_length; i++) { - sprintf(dbg_data +3*i, "%02X ", data[i]); - } - dev_dbg(&port->dev, " <-- %s\n", dbg_data); - kfree(dbg_data); - */ - /* END DEBUG */ - + usb_serial_debug_data(&port->dev, __func__, urb->actual_length, + data); tty_insert_flip_string(&port->port, data, urb->actual_length); tty_flip_buffer_push(&port->port); } -- cgit v0.10.2 From 9c0343aa6cbbb62b8b829c87b15839f35bdebbf2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:25 +0200 Subject: USB: kobil_sct: remove unused endpoint address Remove unused interrupt-in endpoint address from private data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index da1a372..5bcfd57 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -107,7 +107,6 @@ static struct usb_serial_driver * const serial_drivers[] = { struct kobil_private { int write_int_endpoint_address; - int read_int_endpoint_address; unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */ int filled; /* index of the last char in buf */ int cur_pos; /* index of the next char to send in buf */ @@ -166,13 +165,6 @@ static int kobil_port_probe(struct usb_serial_port *port) priv->write_int_endpoint_address = endpoint->desc.bEndpointAddress; } - if (usb_endpoint_is_int_in(&endpoint->desc)) { - dev_dbg(&serial->dev->dev, - "%s Found interrupt in endpoint. Address: %d\n", - __func__, endpoint->desc.bEndpointAddress); - priv->read_int_endpoint_address = - endpoint->desc.bEndpointAddress; - } } return 0; } -- cgit v0.10.2 From feb0a36a523b9fd07275b12f76b344901f884253 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:26 +0200 Subject: USB: kobil_sct: use port interrupt-out urb Use the port interrupt-out urb rather than abusing the port write_urb pointer and allocating a new urb at every open (but the first...). Note that the write_urb abuse would have led to a double free should there ever be interfaces with a bulk-out endpoint. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 5bcfd57..78b48c3 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -65,7 +65,7 @@ static int kobil_tiocmget(struct tty_struct *tty); static int kobil_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void kobil_read_int_callback(struct urb *urb); -static void kobil_write_callback(struct urb *purb); +static void kobil_write_int_callback(struct urb *urb); static void kobil_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static void kobil_init_termios(struct tty_struct *tty); @@ -99,6 +99,7 @@ static struct usb_serial_driver kobil_device = { .write = kobil_write, .write_room = kobil_write_room, .read_int_callback = kobil_read_int_callback, + .write_int_callback = kobil_write_int_callback, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -106,7 +107,6 @@ static struct usb_serial_driver * const serial_drivers[] = { }; struct kobil_private { - int write_int_endpoint_address; unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */ int filled; /* index of the last char in buf */ int cur_pos; /* index of the next char to send in buf */ @@ -116,14 +116,8 @@ struct kobil_private { static int kobil_port_probe(struct usb_serial_port *port) { - int i; struct usb_serial *serial = port->serial; struct kobil_private *priv; - struct usb_device *pdev; - struct usb_host_config *actconfig; - struct usb_interface *interface; - struct usb_host_interface *altsetting; - struct usb_host_endpoint *endpoint; priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL); if (!priv) @@ -149,23 +143,6 @@ static int kobil_port_probe(struct usb_serial_port *port) } usb_set_serial_port_data(port, priv); - /* search for the necessary endpoints */ - pdev = serial->dev; - actconfig = pdev->actconfig; - interface = actconfig->interface[0]; - altsetting = interface->cur_altsetting; - endpoint = altsetting->endpoint; - - for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { - endpoint = &altsetting->endpoint[i]; - if (usb_endpoint_is_int_out(&endpoint->desc)) { - dev_dbg(&serial->dev->dev, - "%s Found interrupt out endpoint. Address: %d\n", - __func__, endpoint->desc.bEndpointAddress); - priv->write_int_endpoint_address = - endpoint->desc.bEndpointAddress; - } - } return 0; } @@ -197,7 +174,6 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) struct kobil_private *priv; unsigned char *transfer_buffer; int transfer_buffer_length = 8; - int write_urb_transfer_buffer_length = 8; priv = usb_get_serial_port_data(port); @@ -206,27 +182,6 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) if (!transfer_buffer) return -ENOMEM; - /* allocate write_urb */ - if (!port->write_urb) { - dev_dbg(dev, "%s - Allocating port->write_urb\n", __func__); - port->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!port->write_urb) { - dev_dbg(dev, "%s - usb_alloc_urb failed\n", __func__); - kfree(transfer_buffer); - return -ENOMEM; - } - } - - /* allocate memory for write_urb transfer buffer */ - port->write_urb->transfer_buffer = - kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL); - if (!port->write_urb->transfer_buffer) { - kfree(transfer_buffer); - usb_free_urb(port->write_urb); - port->write_urb = NULL; - return -ENOMEM; - } - /* get hardware version */ result = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), @@ -302,12 +257,7 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) static void kobil_close(struct usb_serial_port *port) { /* FIXME: Add rts/dtr methods */ - if (port->write_urb) { - usb_poison_urb(port->write_urb); - kfree(port->write_urb->transfer_buffer); - usb_free_urb(port->write_urb); - port->write_urb = NULL; - } + usb_kill_urb(port->interrupt_out_urb); usb_kill_urb(port->interrupt_in_urb); } @@ -336,7 +286,7 @@ static void kobil_read_int_callback(struct urb *urb) } -static void kobil_write_callback(struct urb *purb) +static void kobil_write_int_callback(struct urb *urb) { } @@ -379,23 +329,14 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, while (todo > 0) { /* max 8 byte in one urb (endpoint size) */ - length = (todo < 8) ? todo : 8; + length = min(todo, port->interrupt_out_size); /* copy data to transfer buffer */ - memcpy(port->write_urb->transfer_buffer, + memcpy(port->interrupt_out_buffer, priv->buf + priv->cur_pos, length); - usb_fill_int_urb(port->write_urb, - port->serial->dev, - usb_sndintpipe(port->serial->dev, - priv->write_int_endpoint_address), - port->write_urb->transfer_buffer, - length, - kobil_write_callback, - port, - 8 - ); + port->interrupt_out_urb->transfer_buffer_length = length; priv->cur_pos = priv->cur_pos + length; - result = usb_submit_urb(port->write_urb, GFP_NOIO); + result = usb_submit_urb(port->interrupt_out_urb, GFP_NOIO); dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result); todo = priv->filled - priv->cur_pos; -- cgit v0.10.2 From 35807187e4b35200b12edad319a36ee7a0167ba7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:27 +0200 Subject: USB: mct_u232: clean up read implementation The device uses the second interrupt-in endpoint of the interface for reading. Stop abusing the port read urb and store a pointer to the second interrupt-in urb as port-private data instead. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 3353c9e..6a15adf 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -43,7 +43,6 @@ /* * Function prototypes */ -static int mct_u232_startup(struct usb_serial *serial); static int mct_u232_port_probe(struct usb_serial_port *port); static int mct_u232_port_remove(struct usb_serial_port *remove); static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); @@ -91,7 +90,6 @@ static struct usb_serial_driver mct_u232_device = { .tiocmget = mct_u232_tiocmget, .tiocmset = mct_u232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, - .attach = mct_u232_startup, .port_probe = mct_u232_port_probe, .port_remove = mct_u232_port_remove, .get_icount = usb_serial_generic_get_icount, @@ -102,6 +100,7 @@ static struct usb_serial_driver * const serial_drivers[] = { }; struct mct_u232_private { + struct urb *read_urb; spinlock_t lock; unsigned int control_state; /* Modem Line Setting (TIOCM) */ unsigned char last_lcr; /* Line Control Register */ @@ -376,22 +375,6 @@ static void mct_u232_msr_to_state(struct usb_serial_port *port, * Driver's tty interface functions */ -static int mct_u232_startup(struct usb_serial *serial) -{ - struct usb_serial_port *port, *rport; - - /* Puh, that's dirty */ - port = serial->port[0]; - rport = serial->port[1]; - /* No unlinking, it wasn't submitted yet. */ - usb_free_urb(port->read_urb); - port->read_urb = rport->interrupt_in_urb; - rport->interrupt_in_urb = NULL; - port->read_urb->context = port; - - return 0; -} /* mct_u232_startup */ - static int mct_u232_port_probe(struct usb_serial_port *port) { struct mct_u232_private *priv; @@ -400,6 +383,10 @@ static int mct_u232_port_probe(struct usb_serial_port *port) if (!priv) return -ENOMEM; + /* Use second interrupt-in endpoint for reading. */ + priv->read_urb = port->serial->port[1]->interrupt_in_urb; + priv->read_urb->context = port; + spin_lock_init(&priv->lock); usb_set_serial_port_data(port, priv); @@ -463,17 +450,17 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); spin_unlock_irqrestore(&priv->lock, flags); - retval = usb_submit_urb(port->read_urb, GFP_KERNEL); + retval = usb_submit_urb(priv->read_urb, GFP_KERNEL); if (retval) { dev_err(&port->dev, - "usb_submit_urb(read bulk) failed pipe 0x%x err %d\n", + "usb_submit_urb(read) failed pipe 0x%x err %d\n", port->read_urb->pipe, retval); goto error; } retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (retval) { - usb_kill_urb(port->read_urb); + usb_kill_urb(priv->read_urb); dev_err(&port->dev, "usb_submit_urb(read int) failed pipe 0x%x err %d", port->interrupt_in_urb->pipe, retval); @@ -503,11 +490,9 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on) static void mct_u232_close(struct usb_serial_port *port) { - /* - * Must kill the read urb as it is actually an interrupt urb, which - * generic close thus fails to kill. - */ - usb_kill_urb(port->read_urb); + struct mct_u232_private *priv = usb_get_serial_port_data(port); + + usb_kill_urb(priv->read_urb); usb_kill_urb(port->interrupt_in_urb); usb_serial_generic_close(port); -- cgit v0.10.2 From cced926f0e4fb02a08d2d14e443631fe8a28db98 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:28 +0200 Subject: USB: symbolserial: use port interrupt-in urb Use the port interrupt-in urb rather managing a private one. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index be05e6c..32ebddf 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -29,13 +29,6 @@ MODULE_DEVICE_TABLE(usb, id_table); /* This structure holds all of the individual device information */ struct symbol_private { struct usb_device *udev; - struct usb_serial *serial; - struct usb_serial_port *port; - unsigned char *int_buffer; - struct urb *int_urb; - int buffer_size; - u8 bInterval; - u8 int_address; spinlock_t lock; /* protects the following flags */ bool throttled; bool actually_throttled; @@ -44,9 +37,9 @@ struct symbol_private { static void symbol_int_callback(struct urb *urb) { - struct symbol_private *priv = urb->context; + struct usb_serial_port *port = urb->context; + struct symbol_private *priv = usb_get_serial_data(port->serial); unsigned char *data = urb->transfer_buffer; - struct usb_serial_port *port = priv->port; int status = urb->status; int result; int data_length; @@ -94,12 +87,7 @@ exit: /* Continue trying to always read if we should */ if (!priv->throttled) { - usb_fill_int_urb(priv->int_urb, priv->udev, - usb_rcvintpipe(priv->udev, - priv->int_address), - priv->int_buffer, priv->buffer_size, - symbol_int_callback, priv, priv->bInterval); - result = usb_submit_urb(priv->int_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", @@ -118,15 +106,10 @@ static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&priv->lock, flags); priv->throttled = false; priv->actually_throttled = false; - priv->port = port; spin_unlock_irqrestore(&priv->lock, flags); /* Start reading from the device */ - usb_fill_int_urb(priv->int_urb, priv->udev, - usb_rcvintpipe(priv->udev, priv->int_address), - priv->int_buffer, priv->buffer_size, - symbol_int_callback, priv, priv->bInterval); - result = usb_submit_urb(priv->int_urb, GFP_KERNEL); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", @@ -136,10 +119,7 @@ static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) static void symbol_close(struct usb_serial_port *port) { - struct symbol_private *priv = usb_get_serial_data(port->serial); - - /* shutdown our urbs */ - usb_kill_urb(priv->int_urb); + usb_kill_urb(port->interrupt_in_urb); } static void symbol_throttle(struct tty_struct *tty) @@ -166,7 +146,7 @@ static void symbol_unthrottle(struct tty_struct *tty) spin_unlock_irq(&priv->lock); if (was_throttled) { - result = usb_submit_urb(priv->int_urb, GFP_KERNEL); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", @@ -177,10 +157,11 @@ static void symbol_unthrottle(struct tty_struct *tty) static int symbol_startup(struct usb_serial *serial) { struct symbol_private *priv; - struct usb_host_interface *intf; - int i; - int retval = -ENOMEM; - bool int_in_found = false; + + if (!serial->num_interrupt_in) { + dev_err(&serial->dev->dev, "no interrupt-in endpoint\n"); + return -ENODEV; + } /* create our private serial structure */ priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -189,75 +170,16 @@ static int symbol_startup(struct usb_serial *serial) return -ENOMEM; } spin_lock_init(&priv->lock); - priv->serial = serial; - priv->port = serial->port[0]; priv->udev = serial->dev; - /* find our interrupt endpoint */ - intf = serial->interface->altsetting; - for (i = 0; i < intf->desc.bNumEndpoints; ++i) { - struct usb_endpoint_descriptor *endpoint; - - endpoint = &intf->endpoint[i].desc; - if (!usb_endpoint_is_int_in(endpoint)) - continue; - - priv->int_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->int_urb) { - dev_err(&priv->udev->dev, "out of memory\n"); - goto error; - } - - priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; - priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); - if (!priv->int_buffer) { - dev_err(&priv->udev->dev, "out of memory\n"); - goto error; - } - - priv->int_address = endpoint->bEndpointAddress; - priv->bInterval = endpoint->bInterval; - - /* set up our int urb */ - usb_fill_int_urb(priv->int_urb, priv->udev, - usb_rcvintpipe(priv->udev, - endpoint->bEndpointAddress), - priv->int_buffer, priv->buffer_size, - symbol_int_callback, priv, priv->bInterval); - - int_in_found = true; - break; - } - - if (!int_in_found) { - dev_err(&priv->udev->dev, - "Error - the proper endpoints were not found!\n"); - goto error; - } - usb_set_serial_data(serial, priv); return 0; - -error: - usb_free_urb(priv->int_urb); - kfree(priv->int_buffer); - kfree(priv); - return retval; -} - -static void symbol_disconnect(struct usb_serial *serial) -{ - struct symbol_private *priv = usb_get_serial_data(serial); - - usb_kill_urb(priv->int_urb); - usb_free_urb(priv->int_urb); } static void symbol_release(struct usb_serial *serial) { struct symbol_private *priv = usb_get_serial_data(serial); - kfree(priv->int_buffer); kfree(priv); } @@ -271,10 +193,10 @@ static struct usb_serial_driver symbol_device = { .attach = symbol_startup, .open = symbol_open, .close = symbol_close, - .disconnect = symbol_disconnect, .release = symbol_release, .throttle = symbol_throttle, .unthrottle = symbol_unthrottle, + .read_int_callback = symbol_int_callback, }; static struct usb_serial_driver * const serial_drivers[] = { -- cgit v0.10.2 From ef31025d6811320ead36a8902b16090dea01b34c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:29 +0200 Subject: USB: symbolserial: remove unused private data Use port device for debug messages in interrupt-urb callback and remove unused private data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 32ebddf..2c2bfa1 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -26,13 +26,10 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); -/* This structure holds all of the individual device information */ struct symbol_private { - struct usb_device *udev; spinlock_t lock; /* protects the following flags */ bool throttled; bool actually_throttled; - bool rts; }; static void symbol_int_callback(struct urb *urb) @@ -77,7 +74,7 @@ static void symbol_int_callback(struct urb *urb) tty_insert_flip_string(&port->port, &data[1], data_length); tty_flip_buffer_push(&port->port); } else { - dev_dbg(&priv->udev->dev, + dev_dbg(&port->dev, "Improper amount of data received from the device, " "%d bytes", urb->actual_length); } @@ -170,7 +167,6 @@ static int symbol_startup(struct usb_serial *serial) return -ENOMEM; } spin_lock_init(&priv->lock); - priv->udev = serial->dev; usb_set_serial_data(serial, priv); return 0; -- cgit v0.10.2 From a85796ee514954b1ef5efe8928ef38777aab8c2d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 16 Apr 2013 18:01:30 +0200 Subject: USB: symbolserial: move private-data allocation to port_probe Allocate port-private data in port-probe rather than in attach. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 2c2bfa1..9b16489 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -1,6 +1,7 @@ /* * Symbol USB barcode to serial driver * + * Copyright (C) 2013 Johan Hovold * Copyright (C) 2009 Greg Kroah-Hartman * Copyright (C) 2009 Novell Inc. * @@ -35,7 +36,7 @@ struct symbol_private { static void symbol_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct symbol_private *priv = usb_get_serial_data(port->serial); + struct symbol_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; int status = urb->status; int result; @@ -153,30 +154,36 @@ static void symbol_unthrottle(struct tty_struct *tty) static int symbol_startup(struct usb_serial *serial) { - struct symbol_private *priv; - if (!serial->num_interrupt_in) { dev_err(&serial->dev->dev, "no interrupt-in endpoint\n"); return -ENODEV; } - /* create our private serial structure */ + return 0; +} + +static int symbol_port_probe(struct usb_serial_port *port) +{ + struct symbol_private *priv; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (priv == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + if (!priv) return -ENOMEM; - } + spin_lock_init(&priv->lock); - usb_set_serial_data(serial, priv); + usb_set_serial_port_data(port, priv); + return 0; } -static void symbol_release(struct usb_serial *serial) +static int symbol_port_remove(struct usb_serial_port *port) { - struct symbol_private *priv = usb_get_serial_data(serial); + struct symbol_private *priv = usb_get_serial_port_data(port); kfree(priv); + + return 0; } static struct usb_serial_driver symbol_device = { @@ -187,9 +194,10 @@ static struct usb_serial_driver symbol_device = { .id_table = id_table, .num_ports = 1, .attach = symbol_startup, + .port_probe = symbol_port_probe, + .port_remove = symbol_port_remove, .open = symbol_open, .close = symbol_close, - .release = symbol_release, .throttle = symbol_throttle, .unthrottle = symbol_unthrottle, .read_int_callback = symbol_int_callback, -- cgit v0.10.2 From 1cb6e73c556e8a61d41e531003cf205c16650a02 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 18 Apr 2013 14:17:14 -0700 Subject: usb: storage: Fix link error Fix allmodconfig link error introduced by commit 75b9130e8a ("usb: storage: Add usb_stor_dbg, reduce object size") Export the symbol usb_stor_dbg. Add export.h Reported-by: Stephen Rothwell Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index b428129..29fe08f 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -43,6 +43,7 @@ */ #include +#include #include #include #include @@ -193,3 +194,4 @@ int usb_stor_dbg(const char *fmt, ...) return r; } +EXPORT_SYMBOL_GPL(usb_stor_dbg); -- cgit v0.10.2 From c33c888b585fd9ed3770e91588480a0b74e82ad0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 19 Apr 2013 10:18:12 +0800 Subject: usbatm: fix potential NULL pointer dereference The dereference to 'instance' in the debug code should be moved below the NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Duncan Sands Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 35f10bf..d3527dd 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -672,9 +672,6 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) struct usbatm_control *ctrl = UDSL_SKB(skb); int err; - vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__, - skb, skb->len); - /* racy disconnection check - fine */ if (!instance || instance->disconnected) { #ifdef DEBUG @@ -684,6 +681,9 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) goto fail; } + vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__, + skb, skb->len); + if (vcc->qos.aal != ATM_AAL5) { atm_rldbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal); err = -EINVAL; -- cgit v0.10.2 From ac9e59cad7b0b699b2aa9a104eb22ed67a560e02 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 18 Apr 2013 12:17:38 +0800 Subject: USB: usbtmc: remove unnecessary memory allocation Inside usbtmc_ioctl_clear_out_halt()/usbtmc_ioctl_clear_in_halt(), usb_clear_halt() needn't any buffer to pass in, so remove the unnecessary memory allocation. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 70d69d0..4c5506a 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -718,50 +718,32 @@ exit: static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data) { - u8 *buffer; int rv; - buffer = kmalloc(2, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - rv = usb_clear_halt(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out)); if (rv < 0) { dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", rv); - goto exit; + return rv; } - rv = 0; - -exit: - kfree(buffer); - return rv; + return 0; } static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) { - u8 *buffer; int rv; - buffer = kmalloc(2, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - rv = usb_clear_halt(data->usb_dev, usb_rcvbulkpipe(data->usb_dev, data->bulk_in)); if (rv < 0) { dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", rv); - goto exit; + return rv; } - rv = 0; - -exit: - kfree(buffer); - return rv; + return 0; } static int get_capabilities(struct usbtmc_device_data *data) -- cgit v0.10.2 From b6fd35ee5766143d6bc3c333edf374c336ebdca6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:17 +0200 Subject: USB: io_ti: fix TIOCGSERIAL Fix regression introduced by commit f40d78155 ("USB: io_ti: kill custom closing_wait implementation") which made TIOCGSERIAL return the wrong value for closing_wait. Signed-off-by: Johan Hovold Cc: stable # 3.9 Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index f2a1601..ab979a2 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2357,7 +2357,7 @@ static int get_serial_info(struct edgeport_port *edge_port, cwait = edge_port->port->port.closing_wait; if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = jiffies_to_msecs(closing_wait) / 10; + cwait = jiffies_to_msecs(cwait) / 10; memset(&tmp, 0, sizeof(tmp)); -- cgit v0.10.2 From a8ec374f96da361e7a31113b1e471065adf33d7c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:18 +0200 Subject: USB: io_ti: remove redundant wait_until_sent Remove redundant wait_until_sent, which has already been handled by the tty-layer, from break_ctl. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index ab979a2..158bf4b 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2400,8 +2400,6 @@ static void edge_break(struct tty_struct *tty, int break_state) int status; int bv = 0; /* Off */ - tty_wait_until_sent(tty, 0); - if (break_state == -1) bv = 1; /* On */ status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); -- cgit v0.10.2 From 113ec31e16afe197a668d4b63ce41e04dc706c64 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:19 +0200 Subject: USB: ti_usb_3410_5052: move write-fifo flushing to close Move write-fifo flushing from ti_drain to close where it belongs. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 0726859..a49d2a7 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -128,7 +128,7 @@ static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg); static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); -static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush); +static void ti_drain(struct ti_port *tport, unsigned long timeout); static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty); static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty); @@ -601,6 +601,7 @@ static void ti_close(struct usb_serial_port *port) int port_number; int status; int do_unlock; + unsigned long flags; tdev = usb_get_serial_data(port->serial); tport = usb_get_serial_port_data(port); @@ -609,11 +610,14 @@ static void ti_close(struct usb_serial_port *port) tport->tp_is_open = 0; - ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 1); + ti_drain(tport, (tport->tp_closing_wait*HZ)/100); usb_kill_urb(port->read_urb); usb_kill_urb(port->write_urb); tport->tp_write_urb_in_use = 0; + spin_lock_irqsave(&tport->tp_lock, flags); + kfifo_reset_out(&tport->write_fifo); + spin_unlock_irqrestore(&tport->tp_lock, flags); port_number = port->number - port->serial->minor; @@ -965,7 +969,7 @@ static void ti_break(struct tty_struct *tty, int break_state) if (tport == NULL) return; - ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 0); + ti_drain(tport, (tport->tp_closing_wait*HZ)/100); status = ti_write_byte(port, tport->tp_tdev, tport->tp_uart_base_addr + TI_UART_OFFSET_LCR, @@ -1361,7 +1365,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) } -static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) +static void ti_drain(struct ti_port *tport, unsigned long timeout) { struct ti_device *tdev = tport->tp_tdev; struct usb_serial_port *port = tport->tp_port; @@ -1386,11 +1390,6 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) } set_current_state(TASK_RUNNING); remove_wait_queue(&tport->tp_write_wait, &wait); - - /* flush any remaining data in the buffer */ - if (flush) - kfifo_reset_out(&tport->write_fifo); - spin_unlock_irq(&tport->tp_lock); mutex_lock(&port->serial->disc_mutex); -- cgit v0.10.2 From b5784f7d8528926d69c5868f1ddb2af99f85e799 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:20 +0200 Subject: USB: ti_usb_3410_5052: remove lsr from port data The line status register is only polled so let's not keep a possibly outdated value in the port data. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a49d2a7..3ed8eca 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -67,7 +67,6 @@ struct ti_port { int tp_is_open; __u8 tp_msr; - __u8 tp_lsr; __u8 tp_shadow_mcr; __u8 tp_uart_mode; /* 232 or 485 modes */ unsigned int tp_uart_base_addr; @@ -121,7 +120,7 @@ static void ti_recv(struct usb_serial_port *port, unsigned char *data, int length); static void ti_send(struct ti_port *tport); static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); -static int ti_get_lsr(struct ti_port *tport); +static int ti_get_lsr(struct ti_port *tport, u8 *lsr); static int ti_get_serial_info(struct ti_port *tport, struct serial_struct __user *ret_arg); static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, @@ -1251,7 +1250,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr) } -static int ti_get_lsr(struct ti_port *tport) +static int ti_get_lsr(struct ti_port *tport, u8 *lsr) { int size, status; struct ti_device *tdev = tport->tp_tdev; @@ -1277,7 +1276,7 @@ static int ti_get_lsr(struct ti_port *tport) dev_dbg(&port->dev, "%s - lsr 0x%02X\n", __func__, data->bLSR); - tport->tp_lsr = data->bLSR; + *lsr = data->bLSR; free_data: kfree(data); @@ -1370,6 +1369,7 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout) struct ti_device *tdev = tport->tp_tdev; struct usb_serial_port *port = tport->tp_port; wait_queue_t wait; + u8 lsr; spin_lock_irq(&tport->tp_lock); @@ -1396,11 +1396,11 @@ static void ti_drain(struct ti_port *tport, unsigned long timeout) /* wait for data to drain from the device */ /* wait for empty tx register, plus 20 ms */ timeout += jiffies; - tport->tp_lsr &= ~TI_LSR_TX_EMPTY; + lsr = 0; while ((long)(jiffies - timeout) < 0 && !signal_pending(current) - && !(tport->tp_lsr&TI_LSR_TX_EMPTY) && !tdev->td_urb_error + && !(lsr & TI_LSR_TX_EMPTY) && !tdev->td_urb_error && !port->serial->disconnected) { - if (ti_get_lsr(tport)) + if (ti_get_lsr(tport, &lsr)) break; mutex_unlock(&port->serial->disc_mutex); msleep_interruptible(20); -- cgit v0.10.2 From 2c992cd73772bd0ef107536e8e3399d28493caa8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:21 +0200 Subject: USB: ti_usb_3410_5052: query hardware-buffer status in chars_in_buffer Query hardware-buffer status in chars_in_buffer should the write fifo be empty. This is needed to make the tty layer wait for hardware buffers to drain on close. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3ed8eca..062b6d8 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -687,6 +687,8 @@ static int ti_chars_in_buffer(struct tty_struct *tty) struct ti_port *tport = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; + int ret; + u8 lsr; if (tport == NULL) return 0; @@ -695,6 +697,12 @@ static int ti_chars_in_buffer(struct tty_struct *tty) chars = kfifo_len(&tport->write_fifo); spin_unlock_irqrestore(&tport->tp_lock, flags); + if (!chars) { + ret = ti_get_lsr(tport, &lsr); + if (!ret && !(lsr & TI_LSR_TX_EMPTY)) + chars = 1; + } + dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); return chars; } -- cgit v0.10.2 From c0419024332a73f299bdd7a6875a00cf3942a054 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:22 +0200 Subject: USB: ti_usb_3410_5052: remove redundant drain from break_ctl Remove redundant drain, which has already been handled by the tty-layer, from break_ctl. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 062b6d8..6a40823 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -976,8 +976,6 @@ static void ti_break(struct tty_struct *tty, int break_state) if (tport == NULL) return; - ti_drain(tport, (tport->tp_closing_wait*HZ)/100); - status = ti_write_byte(port, tport->tp_tdev, tport->tp_uart_base_addr + TI_UART_OFFSET_LCR, TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0); -- cgit v0.10.2 From f1175daa5312dd1b3f5940413c7c41ed195066f7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Apr 2013 17:33:23 +0200 Subject: USB: ti_usb_3410_5052: kill custom closing_wait Kill custom closing_wait implementation and let the tty-layer handle it instead. Note that the port drain-delay is set to three characters to keep the 20ms delay after wait_until_sent at low baudrates (1200 baud) during close. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 6a40823..4b52132 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -71,7 +71,6 @@ struct ti_port { __u8 tp_uart_mode; /* 232 or 485 modes */ unsigned int tp_uart_base_addr; int tp_flags; - int tp_closing_wait;/* in .01 secs */ wait_queue_head_t tp_write_wait; struct ti_device *tp_tdev; struct usb_serial_port *tp_port; @@ -127,8 +126,6 @@ static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg); static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); -static void ti_drain(struct ti_port *tport, unsigned long timeout); - static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty); static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty); @@ -428,7 +425,7 @@ static int ti_port_probe(struct usb_serial_port *port) tport->tp_uart_base_addr = TI_UART1_BASE_ADDR; else tport->tp_uart_base_addr = TI_UART2_BASE_ADDR; - tport->tp_closing_wait = closing_wait; + port->port.closing_wait = msecs_to_jiffies(10 * closing_wait); init_waitqueue_head(&tport->tp_write_wait); if (kfifo_alloc(&tport->write_fifo, TI_WRITE_BUF_SIZE, GFP_KERNEL)) { kfree(tport); @@ -581,6 +578,8 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) tport->tp_is_open = 1; ++tdev->td_open_port_count; + port->port.drain_delay = 3; + goto release_lock; unlink_int_urb: @@ -609,8 +608,6 @@ static void ti_close(struct usb_serial_port *port) tport->tp_is_open = 0; - ti_drain(tport, (tport->tp_closing_wait*HZ)/100); - usb_kill_urb(port->read_urb); usb_kill_urb(port->write_urb); tport->tp_write_urb_in_use = 0; @@ -1295,10 +1292,15 @@ static int ti_get_serial_info(struct ti_port *tport, { struct usb_serial_port *port = tport->tp_port; struct serial_struct ret_serial; + unsigned cwait; if (!ret_arg) return -EFAULT; + cwait = port->port.closing_wait; + if (cwait != ASYNC_CLOSING_WAIT_NONE) + cwait = jiffies_to_msecs(cwait) / 10; + memset(&ret_serial, 0, sizeof(ret_serial)); ret_serial.type = PORT_16550A; @@ -1307,7 +1309,7 @@ static int ti_get_serial_info(struct ti_port *tport, ret_serial.flags = tport->tp_flags; ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE; ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800; - ret_serial.closing_wait = tport->tp_closing_wait; + ret_serial.closing_wait = cwait; if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg))) return -EFAULT; @@ -1320,12 +1322,17 @@ static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg) { struct serial_struct new_serial; + unsigned cwait; if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) return -EFAULT; + cwait = new_serial.closing_wait; + if (cwait != ASYNC_CLOSING_WAIT_NONE) + cwait = msecs_to_jiffies(10 * new_serial.closing_wait); + tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - tport->tp_closing_wait = new_serial.closing_wait; + tport->tp_port->port.closing_wait = cwait; return 0; } @@ -1370,52 +1377,6 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) } -static void ti_drain(struct ti_port *tport, unsigned long timeout) -{ - struct ti_device *tdev = tport->tp_tdev; - struct usb_serial_port *port = tport->tp_port; - wait_queue_t wait; - u8 lsr; - - spin_lock_irq(&tport->tp_lock); - - /* wait for data to drain from the buffer */ - tdev->td_urb_error = 0; - init_waitqueue_entry(&wait, current); - add_wait_queue(&tport->tp_write_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (kfifo_len(&tport->write_fifo) == 0 - || timeout == 0 || signal_pending(current) - || tdev->td_urb_error - || port->serial->disconnected) /* disconnect */ - break; - spin_unlock_irq(&tport->tp_lock); - timeout = schedule_timeout(timeout); - spin_lock_irq(&tport->tp_lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&tport->tp_write_wait, &wait); - spin_unlock_irq(&tport->tp_lock); - - mutex_lock(&port->serial->disc_mutex); - /* wait for data to drain from the device */ - /* wait for empty tx register, plus 20 ms */ - timeout += jiffies; - lsr = 0; - while ((long)(jiffies - timeout) < 0 && !signal_pending(current) - && !(lsr & TI_LSR_TX_EMPTY) && !tdev->td_urb_error - && !port->serial->disconnected) { - if (ti_get_lsr(tport, &lsr)) - break; - mutex_unlock(&port->serial->disc_mutex); - msleep_interruptible(20); - mutex_lock(&port->serial->disc_mutex); - } - mutex_unlock(&port->serial->disc_mutex); -} - - static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty) { unsigned long flags; -- cgit v0.10.2 From 191648d03d20229523d9a75b8abef56421298d28 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 19 Apr 2013 11:44:00 -0700 Subject: usb: storage: Convert US_DEBUGP to usb_stor_dbg Use a more current logging style with dev_printk where possible. o Convert uses of US_DEBUGP to usb_stor_dbg o Add "struct us_data *" to usb_stor_dbg uses o usb_stor_dbg now uses struct device */dev_vprint_emit o Removed embedded function names o Coalesce formats o Remove trailing whitespace o Remove useless OOM messages o Remove useless function entry/exit logging o Convert some US_DEBUGP uses to dev_info and dev_dbg Object size is slightly reduced when debugging is enabled, slightly increased with no debugging because some initialization and removal messages are now always emitted. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index be5564c..77a2ddf 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -326,8 +326,7 @@ static int alauda_get_media_status(struct us_data *us, unsigned char *data) rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, command, 0xc0, 0, 1, data, 2); - US_DEBUGP("alauda_get_media_status: Media status %02X %02X\n", - data[0], data[1]); + usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); return rc; } @@ -402,7 +401,7 @@ static int alauda_init_media(struct us_data *us) ready = 1; } - US_DEBUGP("alauda_init_media: We are ready for action!\n"); + usb_stor_dbg(us, "We are ready for action!\n"); if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -413,15 +412,15 @@ static int alauda_init_media(struct us_data *us) return USB_STOR_TRANSPORT_ERROR; if (data[0] != 0x14) { - US_DEBUGP("alauda_init_media: Media not ready after ack\n"); + usb_stor_dbg(us, "Media not ready after ack\n"); return USB_STOR_TRANSPORT_ERROR; } if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n", - data[0], data[1], data[2], data[3]); + usb_stor_dbg(us, "Media signature: %02X %02X %02X %02X\n", + data[0], data[1], data[2], data[3]); media_info = alauda_card_find_id(data[1]); if (media_info == NULL) { printk(KERN_WARNING @@ -432,8 +431,8 @@ static int alauda_init_media(struct us_data *us) } MEDIA_INFO(us).capacity = 1 << media_info->chipshift; - US_DEBUGP("Found media with capacity: %ldMB\n", - MEDIA_INFO(us).capacity >> 20); + usb_stor_dbg(us, "Found media with capacity: %ldMB\n", + MEDIA_INFO(us).capacity >> 20); MEDIA_INFO(us).pageshift = media_info->pageshift; MEDIA_INFO(us).blockshift = media_info->blockshift; @@ -472,7 +471,7 @@ static int alauda_check_media(struct us_data *us) /* Check for no media or door open */ if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) || ((status[1] & 0x01) == 0)) { - US_DEBUGP("alauda_check_media: No media, or door open\n"); + usb_stor_dbg(us, "No media, or door open\n"); alauda_free_maps(&MEDIA_INFO(us)); info->sense_key = 0x02; info->sense_asc = 0x3A; @@ -482,7 +481,7 @@ static int alauda_check_media(struct us_data *us) /* Check for media change */ if (status[0] & 0x08) { - US_DEBUGP("alauda_check_media: Media change detected\n"); + usb_stor_dbg(us, "Media change detected\n"); alauda_free_maps(&MEDIA_INFO(us)); alauda_init_media(us); @@ -518,7 +517,7 @@ static int alauda_check_status2(struct us_data *us) if (rc != USB_STOR_XFER_GOOD) return rc; - US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]); + usb_stor_dbg(us, "%02X %02X %02X\n", data[0], data[1], data[2]); if (data[0] & ALAUDA_STATUS_ERROR) return USB_STOR_XFER_ERROR; @@ -584,7 +583,7 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) goto error; } - US_DEBUGP("alauda_read_map: Mapping blocks for zone %d\n", zone); + usb_stor_dbg(us, "Mapping blocks for zone %d\n", zone); /* 1024 PBA's per zone */ for (i = 0; i < zonesize; i++) @@ -604,7 +603,7 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) if (data[j] != 0) goto nonz; pba_to_lba[i] = UNUSABLE; - US_DEBUGP("alauda_read_map: PBA %d has no logical mapping\n", blocknum); + usb_stor_dbg(us, "PBA %d has no logical mapping\n", blocknum); continue; nonz: @@ -617,19 +616,18 @@ static int alauda_read_map(struct us_data *us, unsigned int zone) nonff: /* normal PBAs start with six FFs */ if (j < 6) { - US_DEBUGP("alauda_read_map: PBA %d has no logical mapping: " - "reserved area = %02X%02X%02X%02X " - "data status %02X block status %02X\n", - blocknum, data[0], data[1], data[2], data[3], - data[4], data[5]); + usb_stor_dbg(us, "PBA %d has no logical mapping: reserved area = %02X%02X%02X%02X data status %02X block status %02X\n", + blocknum, + data[0], data[1], data[2], data[3], + data[4], data[5]); pba_to_lba[i] = UNUSABLE; continue; } if ((data[6] >> 4) != 0x01) { - US_DEBUGP("alauda_read_map: PBA %d has invalid address " - "field %02X%02X/%02X%02X\n", - blocknum, data[6], data[7], data[11], data[12]); + usb_stor_dbg(us, "PBA %d has invalid address field %02X%02X/%02X%02X\n", + blocknum, data[6], data[7], + data[11], data[12]); pba_to_lba[i] = UNUSABLE; continue; } @@ -711,7 +709,7 @@ static int alauda_erase_block(struct us_data *us, u16 pba) }; unsigned char buf[2]; - US_DEBUGP("alauda_erase_block: Erasing PBA %d\n", pba); + usb_stor_dbg(us, "Erasing PBA %d\n", pba); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -723,8 +721,7 @@ static int alauda_erase_block(struct us_data *us, u16 pba) if (rc != USB_STOR_XFER_GOOD) return rc; - US_DEBUGP("alauda_erase_block: Erase result: %02X %02X\n", - buf[0], buf[1]); + usb_stor_dbg(us, "Erase result: %02X %02X\n", buf[0], buf[1]); return rc; } @@ -741,8 +738,7 @@ static int alauda_read_block_raw(struct us_data *us, u16 pba, PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) }; - US_DEBUGP("alauda_read_block: pba %d page %d count %d\n", - pba, page, pages); + usb_stor_dbg(us, "pba %d page %d count %d\n", pba, page, pages); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -793,7 +789,7 @@ static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) }; - US_DEBUGP("alauda_write_block: pba %d\n", pba); + usb_stor_dbg(us, "pba %d\n", pba); rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, command, 9, NULL); @@ -866,14 +862,14 @@ static int alauda_write_lba(struct us_data *us, u16 lba, cptr = bptr + pagesize; nand_compute_ecc(bptr, ecc); if (!nand_compare_ecc(cptr+13, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", + i, pba); nand_store_ecc(cptr+13, ecc); } nand_compute_ecc(bptr + (pagesize / 2), ecc); if (!nand_compare_ecc(cptr+8, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", + i, pba); nand_store_ecc(cptr+8, ecc); } cptr[6] = cptr[11] = MSB_of(lbap); @@ -900,8 +896,7 @@ static int alauda_write_lba(struct us_data *us, u16 lba, new_pba_offset = new_pba - (zone * zonesize); MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; - US_DEBUGP("alauda_write_lba: Remapped LBA %d to PBA %d\n", - lba, new_pba); + usb_stor_dbg(us, "Remapped LBA %d to PBA %d\n", lba, new_pba); if (pba != UNDEF) { unsigned int pba_offset = pba - (zone * zonesize); @@ -964,8 +959,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, /* Not overflowing capacity? */ if (lba >= max_lba) { - US_DEBUGP("Error: Requested lba %u exceeds " - "maximum %u\n", lba, max_lba); + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, max_lba); result = USB_STOR_TRANSPORT_ERROR; break; } @@ -978,8 +973,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; if (pba == UNDEF) { /* this lba was never written */ - US_DEBUGP("Read %d zero pages (LBA %d) page %d\n", - pages, lba, page); + usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); /* This is not really an error. It just means that the block has never been written. @@ -988,9 +983,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address, memset(buffer, 0, len); } else { - US_DEBUGP("Read %d pages, from PBA %d" - " (LBA %d) page %d\n", - pages, pba, lba, page); + usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", + pages, pba, lba, page); result = alauda_read_block(us, pba, page, pages, buffer); if (result != USB_STOR_TRANSPORT_GOOD) @@ -1066,8 +1060,8 @@ static int alauda_write_data(struct us_data *us, unsigned long address, /* Not overflowing capacity? */ if (lba >= max_lba) { - US_DEBUGP("alauda_write_data: Requested lba %u exceeds " - "maximum %u\n", lba, max_lba); + usb_stor_dbg(us, "Requested lba %u exceeds maximum %u\n", + lba, max_lba); result = USB_STOR_TRANSPORT_ERROR; break; } @@ -1122,11 +1116,9 @@ static int init_alauda(struct us_data *us) nand_init_ecc(); us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); - if (!us->extra) { - US_DEBUGP("init_alauda: Gah! Can't allocate storage for" - "alauda info struct!\n"); + if (!us->extra) return USB_STOR_TRANSPORT_ERROR; - } + info = (struct alauda_info *) us->extra; us->extra_destructor = alauda_info_destructor; @@ -1147,15 +1139,14 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) }; if (srb->cmnd[0] == INQUIRY) { - US_DEBUGP("alauda_transport: INQUIRY. " - "Returning bogus response.\n"); + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); memcpy(ptr, inquiry_response, sizeof(inquiry_response)); fill_inquiry_response(us, ptr, 36); return USB_STOR_TRANSPORT_GOOD; } if (srb->cmnd[0] == TEST_UNIT_READY) { - US_DEBUGP("alauda_transport: TEST_UNIT_READY.\n"); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); return alauda_check_media(us); } @@ -1193,8 +1184,7 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("alauda_transport: READ_10: page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "READ_10: page %d pagect %d\n", page, pages); return alauda_read_data(us, page, pages); } @@ -1211,14 +1201,13 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "WRITE_10: page %d pagect %d\n", page, pages); return alauda_write_data(us, page, pages); } if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("alauda_transport: REQUEST_SENSE.\n"); + usb_stor_dbg(us, "REQUEST_SENSE\n"); memset(ptr, 0, 18); ptr[0] = 0xF0; @@ -1237,8 +1226,8 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } - US_DEBUGP("alauda_transport: Gah! Unknown command: %d (0x%x)\n", - srb->cmnd[0], srb->cmnd[0]); + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); info->sense_key = 0x05; info->sense_asc = 0x20; info->sense_ascq = 0x00; diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 070b5c0..e4efc7b 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -159,7 +159,7 @@ static void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) if (srb->result == SAM_STAT_CHECK_CONDITION && memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB, sizeof(usb_stor_sense_invalidCDB)) == 0) { - US_DEBUGP("cypress atacb not supported ???\n"); + usb_stor_dbg(us, "cypress atacb not supported ???\n"); goto end; } diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 494fee5..7b17c21 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -123,7 +123,7 @@ datafab_bulk_read(struct us_data *us, unsigned char *data, unsigned int len) { if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("datafab_bulk_read: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL); } @@ -134,7 +134,7 @@ datafab_bulk_write(struct us_data *us, unsigned char *data, unsigned int len) { if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("datafab_bulk_write: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, NULL); } @@ -300,9 +300,8 @@ static int datafab_write_data(struct us_data *us, goto leave; if (reply[0] != 0x50 && reply[1] != 0) { - US_DEBUGP("datafab_write_data: Gah! " - "write return code: %02x %02x\n", - reply[0], reply[1]); + usb_stor_dbg(us, "Gah! write return code: %02x %02x\n", + reply[0], reply[1]); result = USB_STOR_TRANSPORT_ERROR; goto leave; } @@ -342,7 +341,7 @@ static int datafab_determine_lun(struct us_data *us, if (!buf) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("datafab_determine_lun: locating...\n"); + usb_stor_dbg(us, "locating...\n"); // we'll try 3 times before giving up... // @@ -474,16 +473,16 @@ static int datafab_handle_mode_sense(struct us_data *us, switch (pc) { case 0x0: - US_DEBUGP("datafab_handle_mode_sense: Current values\n"); + usb_stor_dbg(us, "Current values\n"); break; case 0x1: - US_DEBUGP("datafab_handle_mode_sense: Changeable values\n"); + usb_stor_dbg(us, "Changeable values\n"); break; case 0x2: - US_DEBUGP("datafab_handle_mode_sense: Default values\n"); + usb_stor_dbg(us, "Default values\n"); break; case 0x3: - US_DEBUGP("datafab_handle_mode_sense: Saves values\n"); + usb_stor_dbg(us, "Saves values\n"); break; } @@ -566,11 +565,9 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) if (!us->extra) { us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO); - if (!us->extra) { - US_DEBUGP("datafab_transport: Gah! " - "Can't allocate storage for Datafab info struct!\n"); + if (!us->extra) return USB_STOR_TRANSPORT_ERROR; - } + us->extra_destructor = datafab_info_destructor; ((struct datafab_info *)us->extra)->lun = -1; } @@ -578,7 +575,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) info = (struct datafab_info *) (us->extra); if (srb->cmnd[0] == INQUIRY) { - US_DEBUGP("datafab_transport: INQUIRY. Returning bogus response"); + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); memcpy(ptr, inquiry_reply, sizeof(inquiry_reply)); fill_inquiry_response(us, ptr, 36); return USB_STOR_TRANSPORT_GOOD; @@ -590,8 +587,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("datafab_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n", - info->sectors, info->ssize); + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); // build the reply // we need the last sector, not the number of sectors @@ -603,7 +600,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) } if (srb->cmnd[0] == MODE_SELECT_10) { - US_DEBUGP("datafab_transport: Gah! MODE_SELECT_10.\n"); + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -615,7 +612,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("datafab_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); return datafab_read_data(us, info, block, blocks); } @@ -628,7 +626,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("datafab_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); return datafab_read_data(us, info, block, blocks); } @@ -638,7 +637,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("datafab_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); return datafab_write_data(us, info, block, blocks); } @@ -651,17 +651,18 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("datafab_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); return datafab_write_data(us, info, block, blocks); } if (srb->cmnd[0] == TEST_UNIT_READY) { - US_DEBUGP("datafab_transport: TEST_UNIT_READY.\n"); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); return datafab_id_device(us, info); } if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("datafab_transport: REQUEST_SENSE. Returning faked response\n"); + usb_stor_dbg(us, "REQUEST_SENSE - Returning faked response\n"); // this response is pretty bogus right now. eventually if necessary // we can set the correct sense data. so far though it hasn't been @@ -679,12 +680,12 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) } if (srb->cmnd[0] == MODE_SENSE) { - US_DEBUGP("datafab_transport: MODE_SENSE_6 detected\n"); + usb_stor_dbg(us, "MODE_SENSE_6 detected\n"); return datafab_handle_mode_sense(us, srb, 1); } if (srb->cmnd[0] == MODE_SENSE_10) { - US_DEBUGP("datafab_transport: MODE_SENSE_10 detected\n"); + usb_stor_dbg(us, "MODE_SENSE_10 detected\n"); return datafab_handle_mode_sense(us, srb, 0); } @@ -698,7 +699,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) if (srb->cmnd[0] == START_STOP) { /* this is used by sd.c'check_scsidisk_media_change to detect media change */ - US_DEBUGP("datafab_transport: START_STOP.\n"); + usb_stor_dbg(us, "START_STOP\n"); /* the first datafab_id_device after a media change returns an error (determined experimentally) */ rc = datafab_id_device(us, info); @@ -712,8 +713,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) return rc; } - US_DEBUGP("datafab_transport: Gah! Unknown command: %d (0x%x)\n", - srb->cmnd[0], srb->cmnd[0]); + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); info->sense_key = 0x05; info->sense_asc = 0x20; info->sense_ascq = 0x00; diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 29fe08f..e08f647 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -42,17 +42,19 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include #include #include +#include "usb.h" #include "debug.h" #include "scsi.h" -void usb_stor_show_command(struct scsi_cmnd *srb) +void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb) { char *what = NULL; int i; @@ -150,18 +152,18 @@ void usb_stor_show_command(struct scsi_cmnd *srb) case WRITE_LONG_2: what = "WRITE_LONG_2"; break; default: what = "(unknown command)"; break; } - US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len); - US_DEBUGP("bytes: "); + usb_stor_dbg(us, "Command %s (%d bytes)\n", what, srb->cmd_len); + usb_stor_dbg(us, "bytes: "); for (i = 0; i < srb->cmd_len && i < 16; i++) US_DEBUGPX(" %02x", srb->cmnd[i]); US_DEBUGPX("\n"); } -void usb_stor_show_sense( - unsigned char key, - unsigned char asc, - unsigned char ascq) { - +void usb_stor_show_sense(const struct us_data *us, + unsigned char key, + unsigned char asc, + unsigned char ascq) +{ const char *what, *keystr; keystr = scsi_sense_key_string(key); @@ -172,23 +174,19 @@ void usb_stor_show_sense( if (what == NULL) what = "(unknown ASC/ASCQ)"; - US_DEBUGP("%s: ", keystr); + usb_stor_dbg(us, "%s: ", keystr); US_DEBUGPX(what, ascq); US_DEBUGPX("\n"); } -int usb_stor_dbg(const char *fmt, ...) +int usb_stor_dbg(const struct us_data *us, const char *fmt, ...) { - struct va_format vaf; va_list args; int r; va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - - r = printk(KERN_DEBUG USB_STORAGE "%pV", &vaf); + r = dev_vprintk_emit(7, &us->pusb_dev->dev, fmt, args); va_end(args); diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index d4280e1..b1273f0 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -47,17 +47,20 @@ #define USB_STORAGE "usb-storage: " #ifdef CONFIG_USB_STORAGE_DEBUG -void usb_stor_show_command(struct scsi_cmnd *srb); -void usb_stor_show_sense( unsigned char key, - unsigned char asc, unsigned char ascq ); -__printf(1, 2) int usb_stor_dbg(const char *fmt, ...); +void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb); +void usb_stor_show_sense(const struct us_data *us, unsigned char key, + unsigned char asc, unsigned char ascq); +__printf(2, 3) int usb_stor_dbg(const struct us_data *us, + const char *fmt, ...); -#define US_DEBUGP(fmt, ...) usb_stor_dbg(fmt, ##__VA_ARGS__) #define US_DEBUGPX(fmt, ...) printk(fmt, ##__VA_ARGS__) #define US_DEBUG(x) x #else -#define US_DEBUGP(fmt, ...) \ - do { if (0) printk(fmt, ##__VA_ARGS__); } while (0) +__printf(2, 3) +static inline int _usb_stor_dbg(const struct us_data *us, + const char *fmt, ...) {return 1;} +#define usb_stor_dbg(us, fmt, ...) \ + do { if (0) _usb_stor_dbg(us, fmt, ##__VA_ARGS__); } while (0) #define US_DEBUGPX(fmt, ...) \ do { if (0) printk(fmt, ##__VA_ARGS__); } while (0) #define US_DEBUG(x) diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 118b134..1bfc9a6 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -504,12 +504,12 @@ static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) unsigned int cswlen = 0, partial = 0; unsigned int transfer_length = bcb->DataTransferLength; - /* US_DEBUGP("transport --- ene_send_scsi_cmd\n"); */ + /* usb_stor_dbg(us, "transport --- ene_send_scsi_cmd\n"); */ /* send cmd to out endpoint */ result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb, US_BULK_CB_WRAP_LEN, NULL); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("send cmd to out endpoint fail ---\n"); + usb_stor_dbg(us, "send cmd to out endpoint fail ---\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -529,7 +529,7 @@ static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) transfer_length, 0, &partial); } if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("data transfer fail ---\n"); + usb_stor_dbg(us, "data transfer fail ---\n"); return USB_STOR_TRANSPORT_ERROR; } } @@ -539,14 +539,14 @@ static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) US_BULK_CS_WRAP_LEN, &cswlen); if (result == USB_STOR_XFER_SHORT && cswlen == 0) { - US_DEBUGP("Received 0-length CSW; retrying...\n"); + usb_stor_dbg(us, "Received 0-length CSW; retrying...\n"); result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, &cswlen); } if (result == USB_STOR_XFER_STALLED) { /* get the status again */ - US_DEBUGP("Attempting to get CSW (2nd try)...\n"); + usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n"); result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, NULL); } @@ -626,7 +626,7 @@ static int sd_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) struct scatterlist *sg = NULL; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; - US_DEBUGP("sd_scsi_read_capacity\n"); + usb_stor_dbg(us, "sd_scsi_read_capacity\n"); if (info->SD_Status.HiCapacity) { bl_len = 0x200; if (info->SD_Status.IsMMC) @@ -639,8 +639,8 @@ static int sd_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) * (1 << (info->SD_C_SIZE_MULT + 2)) - 1; } info->bl_num = bl_num; - US_DEBUGP("bl_len = %x\n", bl_len); - US_DEBUGP("bl_num = %x\n", bl_num); + usb_stor_dbg(us, "bl_len = %x\n", bl_len); + usb_stor_dbg(us, "bl_num = %x\n", bl_num); /*srb->request_bufflen = 8; */ buf[0] = (bl_num >> 24) & 0xff; @@ -675,7 +675,7 @@ static int sd_scsi_read(struct us_data *us, struct scsi_cmnd *srb) result = ene_load_bincode(us, SD_RW_PATTERN); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Load SD RW pattern Fail !!\n"); + usb_stor_dbg(us, "Load SD RW pattern Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -715,7 +715,7 @@ static int sd_scsi_write(struct us_data *us, struct scsi_cmnd *srb) result = ene_load_bincode(us, SD_RW_PATTERN); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Load SD RW pattern Fail !!\n"); + usb_stor_dbg(us, "Load SD RW pattern Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -1493,7 +1493,7 @@ static int ms_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) struct scatterlist *sg = NULL; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; - US_DEBUGP("ms_scsi_read_capacity\n"); + usb_stor_dbg(us, "ms_scsi_read_capacity\n"); bl_len = 0x200; if (info->MS_Status.IsMSPro) bl_num = info->MSP_TotalBlock - 1; @@ -1501,8 +1501,8 @@ static int ms_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) bl_num = info->MS_Lib.NumberOfLogBlock * info->MS_Lib.blockSize * 2 - 1; info->bl_num = bl_num; - US_DEBUGP("bl_len = %x\n", bl_len); - US_DEBUGP("bl_num = %x\n", bl_num); + usb_stor_dbg(us, "bl_len = %x\n", bl_len); + usb_stor_dbg(us, "bl_num = %x\n", bl_num); /*srb->request_bufflen = 8; */ buf[0] = (bl_num >> 24) & 0xff; @@ -1654,7 +1654,7 @@ static int ms_scsi_read(struct us_data *us, struct scsi_cmnd *srb) if (info->MS_Status.IsMSPro) { result = ene_load_bincode(us, MSP_RW_PATTERN); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Load MPS RW pattern Fail !!\n"); + usb_stor_dbg(us, "Load MPS RW pattern Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -1854,7 +1854,7 @@ static int ene_get_card_status(struct us_data *us, u8 *buf) u32 reg4b; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; - /*US_DEBUGP("transport --- ENE_ReadSDReg\n");*/ + /*usb_stor_dbg(us, "transport --- ENE_ReadSDReg\n");*/ reg4b = *(u32 *)&buf[0x18]; info->SD_READ_BL_LEN = (u8)((reg4b >> 8) & 0x0f); @@ -1894,45 +1894,44 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag) switch (flag) { /* For SD */ case SD_INIT1_PATTERN: - US_DEBUGP("SD_INIT1_PATTERN\n"); + usb_stor_dbg(us, "SD_INIT1_PATTERN\n"); fw_name = SD_INIT1_FIRMWARE; break; case SD_INIT2_PATTERN: - US_DEBUGP("SD_INIT2_PATTERN\n"); + usb_stor_dbg(us, "SD_INIT2_PATTERN\n"); fw_name = SD_INIT2_FIRMWARE; break; case SD_RW_PATTERN: - US_DEBUGP("SD_RW_PATTERN\n"); + usb_stor_dbg(us, "SD_RW_PATTERN\n"); fw_name = SD_RW_FIRMWARE; break; /* For MS */ case MS_INIT_PATTERN: - US_DEBUGP("MS_INIT_PATTERN\n"); + usb_stor_dbg(us, "MS_INIT_PATTERN\n"); fw_name = MS_INIT_FIRMWARE; break; case MSP_RW_PATTERN: - US_DEBUGP("MSP_RW_PATTERN\n"); + usb_stor_dbg(us, "MSP_RW_PATTERN\n"); fw_name = MSP_RW_FIRMWARE; break; case MS_RW_PATTERN: - US_DEBUGP("MS_RW_PATTERN\n"); + usb_stor_dbg(us, "MS_RW_PATTERN\n"); fw_name = MS_RW_FIRMWARE; break; default: - US_DEBUGP("----------- Unknown PATTERN ----------\n"); + usb_stor_dbg(us, "----------- Unknown PATTERN ----------\n"); goto nofw; } err = request_firmware(&sd_fw, fw_name, &us->pusb_dev->dev); if (err) { - US_DEBUGP("load firmware %s failed\n", fw_name); + usb_stor_dbg(us, "load firmware %s failed\n", fw_name); goto nofw; } buf = kmalloc(sd_fw->size, GFP_KERNEL); - if (buf == NULL) { - US_DEBUGP("Malloc memory for fireware failed!\n"); + if (buf == NULL) goto nofw; - } + memcpy(buf, sd_fw->data, sd_fw->size); memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); @@ -2116,9 +2115,9 @@ static int ene_ms_init(struct us_data *us) } else { ms_card_init(us); /* Card is MS (to ms.c)*/ } - US_DEBUGP("MS Init Code OK !!\n"); + usb_stor_dbg(us, "MS Init Code OK !!\n"); } else { - US_DEBUGP("MS Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "MS Card Not Ready --- %x\n", buf[0]); return USB_STOR_TRANSPORT_ERROR; } @@ -2132,11 +2131,11 @@ static int ene_sd_init(struct us_data *us) struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; - US_DEBUGP("transport --- ENE_SDInit\n"); + usb_stor_dbg(us, "transport --- ENE_SDInit\n"); /* SD Init Part-1 */ result = ene_load_bincode(us, SD_INIT1_PATTERN); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Load SD Init Code Part-1 Fail !!\n"); + usb_stor_dbg(us, "Load SD Init Code Part-1 Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -2147,14 +2146,14 @@ static int ene_sd_init(struct us_data *us) result = ene_send_scsi_cmd(us, FDIR_READ, NULL, 0); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Execution SD Init Code Fail !!\n"); + usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } /* SD Init Part-2 */ result = ene_load_bincode(us, SD_INIT2_PATTERN); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Load SD Init Code Part-2 Fail !!\n"); + usb_stor_dbg(us, "Load SD Init Code Part-2 Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -2166,21 +2165,23 @@ static int ene_sd_init(struct us_data *us) result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Execution SD Init Code Fail !!\n"); + usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } info->SD_Status = *(struct SD_STATUS *)&buf[0]; if (info->SD_Status.Insert && info->SD_Status.Ready) { + struct SD_STATUS *s = &info->SD_Status; + ene_get_card_status(us, (unsigned char *)&buf); - US_DEBUGP("Insert = %x\n", info->SD_Status.Insert); - US_DEBUGP("Ready = %x\n", info->SD_Status.Ready); - US_DEBUGP("IsMMC = %x\n", info->SD_Status.IsMMC); - US_DEBUGP("HiCapacity = %x\n", info->SD_Status.HiCapacity); - US_DEBUGP("HiSpeed = %x\n", info->SD_Status.HiSpeed); - US_DEBUGP("WtP = %x\n", info->SD_Status.WtP); + usb_stor_dbg(us, "Insert = %x\n", s->Insert); + usb_stor_dbg(us, "Ready = %x\n", s->Ready); + usb_stor_dbg(us, "IsMMC = %x\n", s->IsMMC); + usb_stor_dbg(us, "HiCapacity = %x\n", s->HiCapacity); + usb_stor_dbg(us, "HiSpeed = %x\n", s->HiSpeed); + usb_stor_dbg(us, "WtP = %x\n", s->WtP); } else { - US_DEBUGP("SD Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "SD Card Not Ready --- %x\n", buf[0]); return USB_STOR_TRANSPORT_ERROR; } return USB_STOR_TRANSPORT_GOOD; @@ -2293,7 +2294,7 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) int result = 0; struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); - /*US_DEBUG(usb_stor_show_command(srb)); */ + /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); if (unlikely(!(info->SD_Status.Ready || info->MS_Status.Ready))) { result = ene_init(us); @@ -2362,7 +2363,6 @@ static int ene_ub6250_resume(struct usb_interface *iface) mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __func__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); @@ -2382,7 +2382,7 @@ static int ene_ub6250_reset_resume(struct usb_interface *iface) u8 tmp = 0; struct us_data *us = usb_get_intfdata(iface); struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); - US_DEBUGP("%s\n", __func__); + /* Report the reset to the SCSI core */ usb_stor_reset_resume(iface); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index e6df087..ef16068 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -40,7 +40,7 @@ MODULE_AUTHOR("David Brown "); MODULE_LICENSE("GPL"); #ifdef CONFIG_USB_STORAGE_DEBUG -static void pdump (void *, int); +static void pdump(struct us_data *us, void *ibuffer, int length); #endif /* Bits of HD_STATUS */ @@ -161,20 +161,20 @@ freecom_readdata (struct scsi_cmnd *srb, struct us_data *us, fxfr->Count = cpu_to_le32 (count); memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); - US_DEBUGP("Read data Freecom! (c=%d)\n", count); + usb_stor_dbg(us, "Read data Freecom! (c=%d)\n", count); /* Issue the transfer command. */ result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, FCM_PACKET_LENGTH, NULL); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP ("Freecom readdata transport error\n"); + usb_stor_dbg(us, "Freecom readdata transport error\n"); return USB_STOR_TRANSPORT_ERROR; } /* Now transfer all of our blocks. */ - US_DEBUGP("Start of read\n"); + usb_stor_dbg(us, "Start of read\n"); result = usb_stor_bulk_srb(us, ipipe, srb); - US_DEBUGP("freecom_readdata done!\n"); + usb_stor_dbg(us, "freecom_readdata done!\n"); if (result > USB_STOR_XFER_SHORT) return USB_STOR_TRANSPORT_ERROR; @@ -194,21 +194,21 @@ freecom_writedata (struct scsi_cmnd *srb, struct us_data *us, fxfr->Count = cpu_to_le32 (count); memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); - US_DEBUGP("Write data Freecom! (c=%d)\n", count); + usb_stor_dbg(us, "Write data Freecom! (c=%d)\n", count); /* Issue the transfer command. */ result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, FCM_PACKET_LENGTH, NULL); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP ("Freecom writedata transport error\n"); + usb_stor_dbg(us, "Freecom writedata transport error\n"); return USB_STOR_TRANSPORT_ERROR; } /* Now transfer all of our blocks. */ - US_DEBUGP("Start of write\n"); + usb_stor_dbg(us, "Start of write\n"); result = usb_stor_bulk_srb(us, opipe, srb); - US_DEBUGP("freecom_writedata done!\n"); + usb_stor_dbg(us, "freecom_writedata done!\n"); if (result > USB_STOR_XFER_SHORT) return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_GOOD; @@ -230,7 +230,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) fcb = (struct freecom_cb_wrap *) us->iobuf; fst = (struct freecom_status *) us->iobuf; - US_DEBUGP("Freecom TRANSPORT STARTED\n"); + usb_stor_dbg(us, "Freecom TRANSPORT STARTED\n"); /* Get handles for both transports. */ opipe = us->send_bulk_pipe; @@ -242,7 +242,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) memcpy (fcb->Atapi, srb->cmnd, 12); memset (fcb->Filler, 0, sizeof (fcb->Filler)); - US_DEBUG(pdump (srb->cmnd, 12)); + US_DEBUG(pdump(us, srb->cmnd, 12)); /* Send it out. */ result = usb_stor_bulk_transfer_buf (us, opipe, fcb, @@ -252,7 +252,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) * USB land. It returns the status in its own registers, which * come back in the bulk pipe. */ if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP ("freecom transport error\n"); + usb_stor_dbg(us, "freecom transport error\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -260,11 +260,11 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) * doesn't hurt us to always do it now. */ result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); - US_DEBUGP("foo Status result %d %u\n", result, partial); + usb_stor_dbg(us, "foo Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUG(pdump ((void *) fst, partial)); + US_DEBUG(pdump(us, (void *)fst, partial)); /* The firmware will time-out commands after 20 seconds. Some commands * can legitimately take longer than this, so we use a different @@ -275,8 +275,8 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) * may not work, but that is a condition that should never happen. */ while (fst->Status & FCM_STATUS_BUSY) { - US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occurred!\n"); - US_DEBUGP("fst->Status is %x\n", fst->Status); + usb_stor_dbg(us, "20 second USB/ATAPI bridge TIMEOUT occurred!\n"); + usb_stor_dbg(us, "fst->Status is %x\n", fst->Status); /* Get the status again */ fcb->Type = FCM_PACKET_STATUS; @@ -293,7 +293,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) * registers, which come back in the bulk pipe. */ if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP ("freecom transport error\n"); + usb_stor_dbg(us, "freecom transport error\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -301,26 +301,26 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_STATUS_PACKET_LENGTH, &partial); - US_DEBUGP("bar Status result %d %u\n", result, partial); + usb_stor_dbg(us, "bar Status result %d %u\n", result, partial); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUG(pdump ((void *) fst, partial)); + US_DEBUG(pdump(us, (void *)fst, partial)); } if (partial != 4) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & 1) != 0) { - US_DEBUGP("operation failed\n"); + usb_stor_dbg(us, "operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } /* The device might not have as much data available as we * requested. If you ask for more than the device has, this reads * and such will hang. */ - US_DEBUGP("Device indicates that it has %d bytes available\n", - le16_to_cpu (fst->Count)); - US_DEBUGP("SCSI requested %d\n", scsi_bufflen(srb)); + usb_stor_dbg(us, "Device indicates that it has %d bytes available\n", + le16_to_cpu(fst->Count)); + usb_stor_dbg(us, "SCSI requested %d\n", scsi_bufflen(srb)); /* Find the length we desire to read. */ switch (srb->cmnd[0]) { @@ -337,7 +337,8 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) /* verify that this amount is legal */ if (length > scsi_bufflen(srb)) { length = scsi_bufflen(srb); - US_DEBUGP("Truncating request to match buffer length: %d\n", length); + usb_stor_dbg(us, "Truncating request to match buffer length: %d\n", + length); } /* What we do now depends on what direction the data is supposed to @@ -351,29 +352,29 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) /* Make sure that the status indicates that the device * wants data as well. */ if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { - US_DEBUGP("SCSI wants data, drive doesn't have any\n"); + usb_stor_dbg(us, "SCSI wants data, drive doesn't have any\n"); return USB_STOR_TRANSPORT_FAILED; } result = freecom_readdata (srb, us, ipipe, opipe, length); if (result != USB_STOR_TRANSPORT_GOOD) return result; - US_DEBUGP("FCM: Waiting for status\n"); + usb_stor_dbg(us, "Waiting for status\n"); result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); - US_DEBUG(pdump ((void *) fst, partial)); + US_DEBUG(pdump(us, (void *)fst, partial)); if (partial != 4 || result > USB_STOR_XFER_SHORT) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { - US_DEBUGP("operation failed\n"); + usb_stor_dbg(us, "operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { - US_DEBUGP("Drive seems still hungry\n"); + usb_stor_dbg(us, "Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } - US_DEBUGP("Transfer happy\n"); + usb_stor_dbg(us, "Transfer happy\n"); break; case DMA_TO_DEVICE: @@ -387,22 +388,22 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) if (result != USB_STOR_TRANSPORT_GOOD) return result; - US_DEBUGP("FCM: Waiting for status\n"); + usb_stor_dbg(us, "Waiting for status\n"); result = usb_stor_bulk_transfer_buf (us, ipipe, fst, FCM_PACKET_LENGTH, &partial); if (partial != 4 || result > USB_STOR_XFER_SHORT) return USB_STOR_TRANSPORT_ERROR; if ((fst->Status & ERR_STAT) != 0) { - US_DEBUGP("operation failed\n"); + usb_stor_dbg(us, "operation failed\n"); return USB_STOR_TRANSPORT_FAILED; } if ((fst->Reason & 3) != 3) { - US_DEBUGP("Drive seems still hungry\n"); + usb_stor_dbg(us, "Drive seems still hungry\n"); return USB_STOR_TRANSPORT_FAILED; } - US_DEBUGP("Transfer happy\n"); + usb_stor_dbg(us, "Transfer happy\n"); break; @@ -412,8 +413,8 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) default: /* should never hit here -- filtered in usb.c */ - US_DEBUGP ("freecom unimplemented direction: %d\n", - us->srb->sc_data_direction); + usb_stor_dbg(us, "freecom unimplemented direction: %d\n", + us->srb->sc_data_direction); /* Return fail, SCSI seems to handle this better. */ return USB_STOR_TRANSPORT_FAILED; break; @@ -434,7 +435,7 @@ static int init_freecom(struct us_data *us) result = usb_stor_control_msg(us, us->recv_ctrl_pipe, 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); buffer[32] = '\0'; - US_DEBUGP("String returned from FC init is: %s\n", buffer); + usb_stor_dbg(us, "String returned from FC init is: %s\n", buffer); /* Special thanks to the people at Freecom for providing me with * this "magic sequence", which they use in their Windows and MacOS @@ -445,7 +446,7 @@ static int init_freecom(struct us_data *us) /* send reset */ result = usb_stor_control_msg(us, us->send_ctrl_pipe, 0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ); - US_DEBUGP("result from activate reset is %d\n", result); + usb_stor_dbg(us, "result from activate reset is %d\n", result); /* wait 250ms */ mdelay(250); @@ -453,7 +454,7 @@ static int init_freecom(struct us_data *us) /* clear reset */ result = usb_stor_control_msg(us, us->send_ctrl_pipe, 0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ); - US_DEBUGP("result from clear reset is %d\n", result); + usb_stor_dbg(us, "result from clear reset is %d\n", result); /* wait 3 seconds */ mdelay(3 * 1000); @@ -470,7 +471,7 @@ static int usb_stor_freecom_reset(struct us_data *us) } #ifdef CONFIG_USB_STORAGE_DEBUG -static void pdump (void *ibuffer, int length) +static void pdump(struct us_data *us, void *ibuffer, int length) { static char line[80]; int offset = 0; @@ -490,7 +491,7 @@ static void pdump (void *ibuffer, int length) line[offset++] = '.'; } line[offset] = 0; - US_DEBUGP("%s\n", line); + usb_stor_dbg(us, "%s\n", line); offset = 0; } offset += sprintf (line+offset, "%08x:", i); @@ -517,7 +518,7 @@ static void pdump (void *ibuffer, int length) line[offset++] = '.'; } line[offset] = 0; - US_DEBUGP("%s\n", line); + usb_stor_dbg(us, "%s\n", line); offset = 0; } #endif diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 105d900..5a8b5ff 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -48,12 +48,12 @@ int usb_stor_euscsi_init(struct us_data *us) { int result; - US_DEBUGP("Attempting to init eUSCSI bridge...\n"); + usb_stor_dbg(us, "Attempting to init eUSCSI bridge...\n"); us->iobuf[0] = 0x1; result = usb_stor_control_msg(us, us->send_ctrl_pipe, 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR, 0x01, 0x0, us->iobuf, 0x1, 5000); - US_DEBUGP("-- result is %d\n", result); + usb_stor_dbg(us, "-- result is %d\n", result); return 0; } @@ -68,7 +68,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us) unsigned int partial; static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS"; - US_DEBUGP("Sending UCR-61S2B initialization packet...\n"); + usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n"); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->Tag = 0; @@ -83,7 +83,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us) if (res) return -EIO; - US_DEBUGP("Getting status packet...\n"); + usb_stor_dbg(us, "Getting status packet...\n"); res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, &partial); if (res) @@ -101,6 +101,6 @@ int usb_stor_huawei_e220_init(struct us_data *us) USB_REQ_SET_FEATURE, USB_TYPE_STANDARD | USB_RECIP_DEVICE, 0x01, 0x0, NULL, 0x0, 1000); - US_DEBUGP("Huawei mode set result is %d\n", result); + usb_stor_dbg(us, "Huawei mode set result is %d\n", result); return 0; } diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 55571ae..599d8bf 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -503,7 +503,7 @@ static int isd200_action( struct us_data *us, int action, switch ( action ) { case ACTION_READ_STATUS: - US_DEBUGP(" isd200_action(READ_STATUS)\n"); + usb_stor_dbg(us, " isd200_action(READ_STATUS)\n"); ata.generic.ActionSelect = ACTION_SELECT_0|ACTION_SELECT_2; ata.generic.RegisterSelect = REG_CYLINDER_LOW | REG_CYLINDER_HIGH | @@ -512,7 +512,7 @@ static int isd200_action( struct us_data *us, int action, break; case ACTION_ENUM: - US_DEBUGP(" isd200_action(ENUM,0x%02x)\n",value); + usb_stor_dbg(us, " isd200_action(ENUM,0x%02x)\n", value); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4| ACTION_SELECT_5; @@ -522,7 +522,7 @@ static int isd200_action( struct us_data *us, int action, break; case ACTION_RESET: - US_DEBUGP(" isd200_action(RESET)\n"); + usb_stor_dbg(us, " isd200_action(RESET)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4; ata.generic.RegisterSelect = REG_DEVICE_CONTROL; @@ -531,7 +531,7 @@ static int isd200_action( struct us_data *us, int action, break; case ACTION_REENABLE: - US_DEBUGP(" isd200_action(REENABLE)\n"); + usb_stor_dbg(us, " isd200_action(REENABLE)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4; ata.generic.RegisterSelect = REG_DEVICE_CONTROL; @@ -540,7 +540,7 @@ static int isd200_action( struct us_data *us, int action, break; case ACTION_SOFT_RESET: - US_DEBUGP(" isd200_action(SOFT_RESET)\n"); + usb_stor_dbg(us, " isd200_action(SOFT_RESET)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5; ata.generic.RegisterSelect = REG_DEVICE_HEAD | REG_COMMAND; ata.write.DeviceHeadByte = info->DeviceHead; @@ -549,7 +549,7 @@ static int isd200_action( struct us_data *us, int action, break; case ACTION_IDENTIFY: - US_DEBUGP(" isd200_action(IDENTIFY)\n"); + usb_stor_dbg(us, " isd200_action(IDENTIFY)\n"); ata.generic.RegisterSelect = REG_COMMAND; ata.write.CommandByte = ATA_CMD_ID_ATA; isd200_set_srb(info, DMA_FROM_DEVICE, info->id, @@ -557,7 +557,7 @@ static int isd200_action( struct us_data *us, int action, break; default: - US_DEBUGP("Error: Undefined action %d\n",action); + usb_stor_dbg(us, "Error: Undefined action %d\n", action); return ISD200_ERROR; } @@ -567,7 +567,8 @@ static int isd200_action( struct us_data *us, int action, if (status == USB_STOR_TRANSPORT_GOOD) status = ISD200_GOOD; else { - US_DEBUGP(" isd200_action(0x%02x) error: %d\n",action,status); + usb_stor_dbg(us, " isd200_action(0x%02x) error: %d\n", + action, status); status = ISD200_ERROR; /* need to reset device here */ } @@ -589,17 +590,17 @@ static int isd200_read_regs( struct us_data *us ) int retStatus = ISD200_GOOD; int transferStatus; - US_DEBUGP("Entering isd200_IssueATAReadRegs\n"); + usb_stor_dbg(us, "Entering isd200_IssueATAReadRegs\n"); transferStatus = isd200_action( us, ACTION_READ_STATUS, info->RegsBuf, sizeof(info->ATARegs) ); if (transferStatus != ISD200_TRANSPORT_GOOD) { - US_DEBUGP(" Error reading ATA registers\n"); + usb_stor_dbg(us, " Error reading ATA registers\n"); retStatus = ISD200_ERROR; } else { memcpy(info->ATARegs, info->RegsBuf, sizeof(info->ATARegs)); - US_DEBUGP(" Got ATA Register[ATA_REG_ERROR_OFFSET] = 0x%x\n", - info->ATARegs[ATA_REG_ERROR_OFFSET]); + usb_stor_dbg(us, " Got ATA Register[ATA_REG_ERROR_OFFSET] = 0x%x\n", + info->ATARegs[ATA_REG_ERROR_OFFSET]); } return retStatus; @@ -629,7 +630,7 @@ static void isd200_invoke_transport( struct us_data *us, * short-circuit all other processing */ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { - US_DEBUGP("-- command was aborted\n"); + usb_stor_dbg(us, "-- command was aborted\n"); goto Handle_Abort; } @@ -641,23 +642,23 @@ static void isd200_invoke_transport( struct us_data *us, break; case USB_STOR_TRANSPORT_NO_SENSE: - US_DEBUGP("-- transport indicates protocol failure\n"); + usb_stor_dbg(us, "-- transport indicates protocol failure\n"); srb->result = SAM_STAT_CHECK_CONDITION; return; case USB_STOR_TRANSPORT_FAILED: - US_DEBUGP("-- transport indicates command failure\n"); + usb_stor_dbg(us, "-- transport indicates command failure\n"); need_auto_sense = 1; break; case USB_STOR_TRANSPORT_ERROR: - US_DEBUGP("-- transport indicates transport error\n"); + usb_stor_dbg(us, "-- transport indicates transport error\n"); srb->result = DID_ERROR << 16; /* Need reset here */ return; default: - US_DEBUGP("-- transport indicates unknown error\n"); + usb_stor_dbg(us, "-- transport indicates unknown error\n"); srb->result = DID_ERROR << 16; /* Need reset here */ return; @@ -669,14 +670,14 @@ static void isd200_invoke_transport( struct us_data *us, (srb->cmnd[0] == MODE_SENSE) || (srb->cmnd[0] == LOG_SENSE) || (srb->cmnd[0] == MODE_SENSE_10))) { - US_DEBUGP("-- unexpectedly short transfer\n"); + usb_stor_dbg(us, "-- unexpectedly short transfer\n"); need_auto_sense = 1; } if (need_auto_sense) { result = isd200_read_regs(us); if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { - US_DEBUGP("-- auto-sense aborted\n"); + usb_stor_dbg(us, "-- auto-sense aborted\n"); goto Handle_Abort; } if (result == ISD200_GOOD) { @@ -710,40 +711,40 @@ static void isd200_invoke_transport( struct us_data *us, } #ifdef CONFIG_USB_STORAGE_DEBUG -static void isd200_log_config( struct isd200_info* info ) +static void isd200_log_config(struct us_data *us, struct isd200_info *info) { - US_DEBUGP(" Event Notification: 0x%x\n", - info->ConfigData.EventNotification); - US_DEBUGP(" External Clock: 0x%x\n", - info->ConfigData.ExternalClock); - US_DEBUGP(" ATA Init Timeout: 0x%x\n", - info->ConfigData.ATAInitTimeout); - US_DEBUGP(" ATAPI Command Block Size: 0x%x\n", - (info->ConfigData.ATAConfig & ATACFG_BLOCKSIZE) >> 6); - US_DEBUGP(" Master/Slave Selection: 0x%x\n", - info->ConfigData.ATAConfig & ATACFG_MASTER); - US_DEBUGP(" ATAPI Reset: 0x%x\n", - info->ConfigData.ATAConfig & ATACFG_ATAPI_RESET); - US_DEBUGP(" ATA Timing: 0x%x\n", - info->ConfigData.ATAConfig & ATACFG_TIMING); - US_DEBUGP(" ATA Major Command: 0x%x\n", - info->ConfigData.ATAMajorCommand); - US_DEBUGP(" ATA Minor Command: 0x%x\n", - info->ConfigData.ATAMinorCommand); - US_DEBUGP(" Init Status: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_INIT_STATUS); - US_DEBUGP(" Config Descriptor 2: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_CONF_DESC2); - US_DEBUGP(" Skip Device Boot: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_SKIP_BOOT); - US_DEBUGP(" ATA 3 State Supsend: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_STATE_SUSPEND); - US_DEBUGP(" Descriptor Override: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_DESC_OVERRIDE); - US_DEBUGP(" Last LUN Identifier: 0x%x\n", - info->ConfigData.ATAExtraConfig & ATACFGE_LAST_LUN); - US_DEBUGP(" SRST Enable: 0x%x\n", - info->ConfigData.ATAExtraConfig & CFG_CAPABILITY_SRST); + usb_stor_dbg(us, " Event Notification: 0x%x\n", + info->ConfigData.EventNotification); + usb_stor_dbg(us, " External Clock: 0x%x\n", + info->ConfigData.ExternalClock); + usb_stor_dbg(us, " ATA Init Timeout: 0x%x\n", + info->ConfigData.ATAInitTimeout); + usb_stor_dbg(us, " ATAPI Command Block Size: 0x%x\n", + (info->ConfigData.ATAConfig & ATACFG_BLOCKSIZE) >> 6); + usb_stor_dbg(us, " Master/Slave Selection: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_MASTER); + usb_stor_dbg(us, " ATAPI Reset: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_ATAPI_RESET); + usb_stor_dbg(us, " ATA Timing: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_TIMING); + usb_stor_dbg(us, " ATA Major Command: 0x%x\n", + info->ConfigData.ATAMajorCommand); + usb_stor_dbg(us, " ATA Minor Command: 0x%x\n", + info->ConfigData.ATAMinorCommand); + usb_stor_dbg(us, " Init Status: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_INIT_STATUS); + usb_stor_dbg(us, " Config Descriptor 2: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_CONF_DESC2); + usb_stor_dbg(us, " Skip Device Boot: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_SKIP_BOOT); + usb_stor_dbg(us, " ATA 3 State Supsend: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_STATE_SUSPEND); + usb_stor_dbg(us, " Descriptor Override: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_DESC_OVERRIDE); + usb_stor_dbg(us, " Last LUN Identifier: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_LAST_LUN); + usb_stor_dbg(us, " SRST Enable: 0x%x\n", + info->ConfigData.ATAExtraConfig & CFG_CAPABILITY_SRST); } #endif @@ -762,9 +763,9 @@ static int isd200_write_config( struct us_data *us ) int result; #ifdef CONFIG_USB_STORAGE_DEBUG - US_DEBUGP("Entering isd200_write_config\n"); - US_DEBUGP(" Writing the following ISD200 Config Data:\n"); - isd200_log_config(info); + usb_stor_dbg(us, "Entering isd200_write_config\n"); + usb_stor_dbg(us, " Writing the following ISD200 Config Data:\n"); + isd200_log_config(us, info); #endif /* let's send the command via the control pipe */ @@ -779,13 +780,13 @@ static int isd200_write_config( struct us_data *us ) sizeof(info->ConfigData)); if (result >= 0) { - US_DEBUGP(" ISD200 Config Data was written successfully\n"); + usb_stor_dbg(us, " ISD200 Config Data was written successfully\n"); } else { - US_DEBUGP(" Request to write ISD200 Config Data failed!\n"); + usb_stor_dbg(us, " Request to write ISD200 Config Data failed!\n"); retStatus = ISD200_ERROR; } - US_DEBUGP("Leaving isd200_write_config %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_write_config %08X\n", retStatus); return retStatus; } @@ -804,7 +805,7 @@ static int isd200_read_config( struct us_data *us ) int retStatus = ISD200_GOOD; int result; - US_DEBUGP("Entering isd200_read_config\n"); + usb_stor_dbg(us, "Entering isd200_read_config\n"); /* read the configuration information from ISD200. Use this to */ /* determine what the special ATA CDB bytes are. */ @@ -821,16 +822,16 @@ static int isd200_read_config( struct us_data *us ) if (result >= 0) { - US_DEBUGP(" Retrieved the following ISD200 Config Data:\n"); + usb_stor_dbg(us, " Retrieved the following ISD200 Config Data:\n"); #ifdef CONFIG_USB_STORAGE_DEBUG - isd200_log_config(info); + isd200_log_config(us, info); #endif } else { - US_DEBUGP(" Request to get ISD200 Config Data failed!\n"); + usb_stor_dbg(us, " Request to get ISD200 Config Data failed!\n"); retStatus = ISD200_ERROR; } - US_DEBUGP("Leaving isd200_read_config %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_read_config %08X\n", retStatus); return retStatus; } @@ -848,15 +849,15 @@ static int isd200_atapi_soft_reset( struct us_data *us ) int retStatus = ISD200_GOOD; int transferStatus; - US_DEBUGP("Entering isd200_atapi_soft_reset\n"); + usb_stor_dbg(us, "Entering isd200_atapi_soft_reset\n"); transferStatus = isd200_action( us, ACTION_SOFT_RESET, NULL, 0 ); if (transferStatus != ISD200_TRANSPORT_GOOD) { - US_DEBUGP(" Error issuing Atapi Soft Reset\n"); + usb_stor_dbg(us, " Error issuing Atapi Soft Reset\n"); retStatus = ISD200_ERROR; } - US_DEBUGP("Leaving isd200_atapi_soft_reset %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_atapi_soft_reset %08X\n", retStatus); return retStatus; } @@ -874,13 +875,13 @@ static int isd200_srst( struct us_data *us ) int retStatus = ISD200_GOOD; int transferStatus; - US_DEBUGP("Entering isd200_SRST\n"); + usb_stor_dbg(us, "Entering isd200_SRST\n"); transferStatus = isd200_action( us, ACTION_RESET, NULL, 0 ); /* check to see if this request failed */ if (transferStatus != ISD200_TRANSPORT_GOOD) { - US_DEBUGP(" Error issuing SRST\n"); + usb_stor_dbg(us, " Error issuing SRST\n"); retStatus = ISD200_ERROR; } else { /* delay 10ms to give the drive a chance to see it */ @@ -888,7 +889,7 @@ static int isd200_srst( struct us_data *us ) transferStatus = isd200_action( us, ACTION_REENABLE, NULL, 0 ); if (transferStatus != ISD200_TRANSPORT_GOOD) { - US_DEBUGP(" Error taking drive out of reset\n"); + usb_stor_dbg(us, " Error taking drive out of reset\n"); retStatus = ISD200_ERROR; } else { /* delay 50ms to give the drive a chance to recover after SRST */ @@ -896,7 +897,7 @@ static int isd200_srst( struct us_data *us ) } } - US_DEBUGP("Leaving isd200_srst %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_srst %08X\n", retStatus); return retStatus; } @@ -938,13 +939,13 @@ static int isd200_try_enum(struct us_data *us, unsigned char master_slave, if (!detect) { if (regs[ATA_REG_STATUS_OFFSET] & ATA_BUSY) { - US_DEBUGP(" %s status is still BSY, try again...\n", - master_slave == ATA_ADDRESS_DEVHEAD_STD ? - "Master" : "Slave"); + usb_stor_dbg(us, " %s status is still BSY, try again...\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); } else { - US_DEBUGP(" %s status !BSY, continue with next operation\n", - master_slave == ATA_ADDRESS_DEVHEAD_STD ? - "Master" : "Slave"); + usb_stor_dbg(us, " %s status !BSY, continue with next operation\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); break; } } @@ -953,11 +954,11 @@ static int isd200_try_enum(struct us_data *us, unsigned char master_slave, /* ATA_ERR (workaround for Archos CD-ROM) */ else if (regs[ATA_REG_STATUS_OFFSET] & (ATA_BUSY | ATA_DF | ATA_ERR)) { - US_DEBUGP(" Status indicates it is not ready, try again...\n"); + usb_stor_dbg(us, " Status indicates it is not ready, try again...\n"); } /* check for DRDY, ATA devices set DRDY after SRST */ else if (regs[ATA_REG_STATUS_OFFSET] & ATA_DRDY) { - US_DEBUGP(" Identified ATA device\n"); + usb_stor_dbg(us, " Identified ATA device\n"); info->DeviceFlags |= DF_ATA_DEVICE; info->DeviceHead = master_slave; break; @@ -978,27 +979,27 @@ static int isd200_try_enum(struct us_data *us, unsigned char master_slave, */ if ((master_slave & ATA_ADDRESS_DEVHEAD_SLAVE) && !recheckAsMaster) { - US_DEBUGP(" Identified ATAPI device as slave. Rechecking again as master\n"); + usb_stor_dbg(us, " Identified ATAPI device as slave. Rechecking again as master\n"); recheckAsMaster = 1; master_slave = ATA_ADDRESS_DEVHEAD_STD; } else { - US_DEBUGP(" Identified ATAPI device\n"); + usb_stor_dbg(us, " Identified ATAPI device\n"); info->DeviceHead = master_slave; status = isd200_atapi_soft_reset(us); break; } } else { - US_DEBUGP(" Not ATA, not ATAPI. Weird.\n"); + usb_stor_dbg(us, " Not ATA, not ATAPI - Weird\n"); break; } /* check for timeout on this request */ if (time_after_eq(jiffies, endTime)) { if (!detect) - US_DEBUGP(" BSY check timeout, just continue with next operation...\n"); + usb_stor_dbg(us, " BSY check timeout, just continue with next operation...\n"); else - US_DEBUGP(" Device detect timeout!\n"); + usb_stor_dbg(us, " Device detect timeout!\n"); break; } } @@ -1020,7 +1021,7 @@ static int isd200_manual_enum(struct us_data *us) struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; - US_DEBUGP("Entering isd200_manual_enum\n"); + usb_stor_dbg(us, "Entering isd200_manual_enum\n"); retStatus = isd200_read_config(us); if (retStatus == ISD200_GOOD) { @@ -1039,14 +1040,15 @@ static int isd200_manual_enum(struct us_data *us) isslave = (info->DeviceHead & ATA_ADDRESS_DEVHEAD_SLAVE) ? 1 : 0; if (!(info->ConfigData.ATAConfig & ATACFG_MASTER)) { - US_DEBUGP(" Setting Master/Slave selection to %d\n", isslave); + usb_stor_dbg(us, " Setting Master/Slave selection to %d\n", + isslave); info->ConfigData.ATAConfig &= 0x3f; info->ConfigData.ATAConfig |= (isslave<<6); retStatus = isd200_write_config(us); } } - US_DEBUGP("Leaving isd200_manual_enum %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_manual_enum %08X\n", retStatus); return(retStatus); } @@ -1064,35 +1066,35 @@ static void isd200_fix_driveid(u16 *id) #endif } -static void isd200_dump_driveid(u16 *id) +static void isd200_dump_driveid(struct us_data *us, u16 *id) { - US_DEBUGP(" Identify Data Structure:\n"); - US_DEBUGP(" config = 0x%x\n", id[ATA_ID_CONFIG]); - US_DEBUGP(" cyls = 0x%x\n", id[ATA_ID_CYLS]); - US_DEBUGP(" heads = 0x%x\n", id[ATA_ID_HEADS]); - US_DEBUGP(" track_bytes = 0x%x\n", id[4]); - US_DEBUGP(" sector_bytes = 0x%x\n", id[5]); - US_DEBUGP(" sectors = 0x%x\n", id[ATA_ID_SECTORS]); - US_DEBUGP(" serial_no[0] = 0x%x\n", *(char *)&id[ATA_ID_SERNO]); - US_DEBUGP(" buf_type = 0x%x\n", id[20]); - US_DEBUGP(" buf_size = 0x%x\n", id[ATA_ID_BUF_SIZE]); - US_DEBUGP(" ecc_bytes = 0x%x\n", id[22]); - US_DEBUGP(" fw_rev[0] = 0x%x\n", *(char *)&id[ATA_ID_FW_REV]); - US_DEBUGP(" model[0] = 0x%x\n", *(char *)&id[ATA_ID_PROD]); - US_DEBUGP(" max_multsect = 0x%x\n", id[ATA_ID_MAX_MULTSECT] & 0xff); - US_DEBUGP(" dword_io = 0x%x\n", id[ATA_ID_DWORD_IO]); - US_DEBUGP(" capability = 0x%x\n", id[ATA_ID_CAPABILITY] >> 8); - US_DEBUGP(" tPIO = 0x%x\n", id[ATA_ID_OLD_PIO_MODES] >> 8); - US_DEBUGP(" tDMA = 0x%x\n", id[ATA_ID_OLD_DMA_MODES] >> 8); - US_DEBUGP(" field_valid = 0x%x\n", id[ATA_ID_FIELD_VALID]); - US_DEBUGP(" cur_cyls = 0x%x\n", id[ATA_ID_CUR_CYLS]); - US_DEBUGP(" cur_heads = 0x%x\n", id[ATA_ID_CUR_HEADS]); - US_DEBUGP(" cur_sectors = 0x%x\n", id[ATA_ID_CUR_SECTORS]); - US_DEBUGP(" cur_capacity = 0x%x\n", ata_id_u32(id, 57)); - US_DEBUGP(" multsect = 0x%x\n", id[ATA_ID_MULTSECT] & 0xff); - US_DEBUGP(" lba_capacity = 0x%x\n", ata_id_u32(id, ATA_ID_LBA_CAPACITY)); - US_DEBUGP(" command_set_1 = 0x%x\n", id[ATA_ID_COMMAND_SET_1]); - US_DEBUGP(" command_set_2 = 0x%x\n", id[ATA_ID_COMMAND_SET_2]); + usb_stor_dbg(us, " Identify Data Structure:\n"); + usb_stor_dbg(us, " config = 0x%x\n", id[ATA_ID_CONFIG]); + usb_stor_dbg(us, " cyls = 0x%x\n", id[ATA_ID_CYLS]); + usb_stor_dbg(us, " heads = 0x%x\n", id[ATA_ID_HEADS]); + usb_stor_dbg(us, " track_bytes = 0x%x\n", id[4]); + usb_stor_dbg(us, " sector_bytes = 0x%x\n", id[5]); + usb_stor_dbg(us, " sectors = 0x%x\n", id[ATA_ID_SECTORS]); + usb_stor_dbg(us, " serial_no[0] = 0x%x\n", *(char *)&id[ATA_ID_SERNO]); + usb_stor_dbg(us, " buf_type = 0x%x\n", id[20]); + usb_stor_dbg(us, " buf_size = 0x%x\n", id[ATA_ID_BUF_SIZE]); + usb_stor_dbg(us, " ecc_bytes = 0x%x\n", id[22]); + usb_stor_dbg(us, " fw_rev[0] = 0x%x\n", *(char *)&id[ATA_ID_FW_REV]); + usb_stor_dbg(us, " model[0] = 0x%x\n", *(char *)&id[ATA_ID_PROD]); + usb_stor_dbg(us, " max_multsect = 0x%x\n", id[ATA_ID_MAX_MULTSECT] & 0xff); + usb_stor_dbg(us, " dword_io = 0x%x\n", id[ATA_ID_DWORD_IO]); + usb_stor_dbg(us, " capability = 0x%x\n", id[ATA_ID_CAPABILITY] >> 8); + usb_stor_dbg(us, " tPIO = 0x%x\n", id[ATA_ID_OLD_PIO_MODES] >> 8); + usb_stor_dbg(us, " tDMA = 0x%x\n", id[ATA_ID_OLD_DMA_MODES] >> 8); + usb_stor_dbg(us, " field_valid = 0x%x\n", id[ATA_ID_FIELD_VALID]); + usb_stor_dbg(us, " cur_cyls = 0x%x\n", id[ATA_ID_CUR_CYLS]); + usb_stor_dbg(us, " cur_heads = 0x%x\n", id[ATA_ID_CUR_HEADS]); + usb_stor_dbg(us, " cur_sectors = 0x%x\n", id[ATA_ID_CUR_SECTORS]); + usb_stor_dbg(us, " cur_capacity = 0x%x\n", ata_id_u32(id, 57)); + usb_stor_dbg(us, " multsect = 0x%x\n", id[ATA_ID_MULTSECT] & 0xff); + usb_stor_dbg(us, " lba_capacity = 0x%x\n", ata_id_u32(id, ATA_ID_LBA_CAPACITY)); + usb_stor_dbg(us, " command_set_1 = 0x%x\n", id[ATA_ID_COMMAND_SET_1]); + usb_stor_dbg(us, " command_set_2 = 0x%x\n", id[ATA_ID_COMMAND_SET_2]); } /************************************************************************** @@ -1109,7 +1111,7 @@ static int isd200_get_inquiry_data( struct us_data *us ) int retStatus = ISD200_GOOD; u16 *id = info->id; - US_DEBUGP("Entering isd200_get_inquiry_data\n"); + usb_stor_dbg(us, "Entering isd200_get_inquiry_data\n"); /* set default to Master */ info->DeviceHead = ATA_ADDRESS_DEVHEAD_STD; @@ -1127,7 +1129,7 @@ static int isd200_get_inquiry_data( struct us_data *us ) id, ATA_ID_WORDS * 2); if (transferStatus != ISD200_TRANSPORT_GOOD) { /* Error issuing ATA Command Identify */ - US_DEBUGP(" Error issuing ATA Command Identify\n"); + usb_stor_dbg(us, " Error issuing ATA Command Identify\n"); retStatus = ISD200_ERROR; } else { /* ATA Command Identify successful */ @@ -1136,7 +1138,7 @@ static int isd200_get_inquiry_data( struct us_data *us ) __u16 *dest; isd200_fix_driveid(id); - isd200_dump_driveid(id); + isd200_dump_driveid(us, id); memset(&info->InquiryData, 0, sizeof(info->InquiryData)); @@ -1170,7 +1172,7 @@ static int isd200_get_inquiry_data( struct us_data *us ) /* determine if it supports Media Status Notification */ if (id[ATA_ID_COMMAND_SET_2] & COMMANDSET_MEDIA_STATUS) { - US_DEBUGP(" Device supports Media Status Notification\n"); + usb_stor_dbg(us, " Device supports Media Status Notification\n"); /* Indicate that it is enabled, even though it is not * This allows the lock/unlock of the media to work @@ -1190,7 +1192,8 @@ static int isd200_get_inquiry_data( struct us_data *us ) us->protocol_name = "Transparent SCSI"; us->proto_handler = usb_stor_transparent_scsi_command; - US_DEBUGP("Protocol changed to: %s\n", us->protocol_name); + usb_stor_dbg(us, "Protocol changed to: %s\n", + us->protocol_name); /* Free driver structure */ us->extra_destructor(info); @@ -1200,7 +1203,7 @@ static int isd200_get_inquiry_data( struct us_data *us ) } } - US_DEBUGP("Leaving isd200_get_inquiry_data %08X\n", retStatus); + usb_stor_dbg(us, "Leaving isd200_get_inquiry_data %08X\n", retStatus); return(retStatus); } @@ -1231,7 +1234,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, /* SCSI Command */ switch (srb->cmnd[0]) { case INQUIRY: - US_DEBUGP(" ATA OUT - INQUIRY\n"); + usb_stor_dbg(us, " ATA OUT - INQUIRY\n"); /* copy InquiryData */ usb_stor_set_xfer_buf((unsigned char *) &info->InquiryData, @@ -1241,7 +1244,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, break; case MODE_SENSE: - US_DEBUGP(" ATA OUT - SCSIOP_MODE_SENSE\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_MODE_SENSE\n"); /* Initialize the return buffer */ usb_stor_set_xfer_buf(senseData, sizeof(senseData), srb); @@ -1255,14 +1258,14 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; isd200_srb_set_bufflen(srb, 0); } else { - US_DEBUGP(" Media Status not supported, just report okay\n"); + usb_stor_dbg(us, " Media Status not supported, just report okay\n"); srb->result = SAM_STAT_GOOD; sendToTransport = 0; } break; case TEST_UNIT_READY: - US_DEBUGP(" ATA OUT - SCSIOP_TEST_UNIT_READY\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_TEST_UNIT_READY\n"); if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) { @@ -1273,7 +1276,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; isd200_srb_set_bufflen(srb, 0); } else { - US_DEBUGP(" Media Status not supported, just report okay\n"); + usb_stor_dbg(us, " Media Status not supported, just report okay\n"); srb->result = SAM_STAT_GOOD; sendToTransport = 0; } @@ -1284,7 +1287,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, unsigned long capacity; struct read_capacity_data readCapacityData; - US_DEBUGP(" ATA OUT - SCSIOP_READ_CAPACITY\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_READ_CAPACITY\n"); if (ata_id_has_lba(id)) capacity = ata_id_u32(id, ATA_ID_LBA_CAPACITY) - 1; @@ -1303,7 +1306,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, break; case READ_10: - US_DEBUGP(" ATA OUT - SCSIOP_READ\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_READ\n"); lba = be32_to_cpu(*(__be32 *)&srb->cmnd[2]); blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8]; @@ -1335,7 +1338,7 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, break; case WRITE_10: - US_DEBUGP(" ATA OUT - SCSIOP_WRITE\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_WRITE\n"); lba = be32_to_cpu(*(__be32 *)&srb->cmnd[2]); blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8]; @@ -1367,10 +1370,11 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, break; case ALLOW_MEDIUM_REMOVAL: - US_DEBUGP(" ATA OUT - SCSIOP_MEDIUM_REMOVAL\n"); + usb_stor_dbg(us, " ATA OUT - SCSIOP_MEDIUM_REMOVAL\n"); if (info->DeviceFlags & DF_REMOVABLE_MEDIA) { - US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]); + usb_stor_dbg(us, " srb->cmnd[4] = 0x%X\n", + srb->cmnd[4]); ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; @@ -1380,25 +1384,25 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, ATA_CMD_MEDIA_LOCK : ATA_CMD_MEDIA_UNLOCK; isd200_srb_set_bufflen(srb, 0); } else { - US_DEBUGP(" Not removeable media, just report okay\n"); + usb_stor_dbg(us, " Not removeable media, just report okay\n"); srb->result = SAM_STAT_GOOD; sendToTransport = 0; } break; case START_STOP: - US_DEBUGP(" ATA OUT - SCSIOP_START_STOP_UNIT\n"); - US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]); + usb_stor_dbg(us, " ATA OUT - SCSIOP_START_STOP_UNIT\n"); + usb_stor_dbg(us, " srb->cmnd[4] = 0x%X\n", srb->cmnd[4]); if ((srb->cmnd[4] & 0x3) == 0x2) { - US_DEBUGP(" Media Eject\n"); + usb_stor_dbg(us, " Media Eject\n"); ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; ataCdb->generic.TransferBlockSize = 0; ataCdb->generic.RegisterSelect = REG_COMMAND; ataCdb->write.CommandByte = ATA_COMMAND_MEDIA_EJECT; } else if ((srb->cmnd[4] & 0x3) == 0x1) { - US_DEBUGP(" Get Media Status\n"); + usb_stor_dbg(us, " Get Media Status\n"); ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; ataCdb->generic.TransferBlockSize = 1; @@ -1406,14 +1410,15 @@ static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; isd200_srb_set_bufflen(srb, 0); } else { - US_DEBUGP(" Nothing to do, just report okay\n"); + usb_stor_dbg(us, " Nothing to do, just report okay\n"); srb->result = SAM_STAT_GOOD; sendToTransport = 0; } break; default: - US_DEBUGP("Unsupported SCSI command - 0x%X\n", srb->cmnd[0]); + usb_stor_dbg(us, "Unsupported SCSI command - 0x%X\n", + srb->cmnd[0]); srb->result = DID_ERROR << 16; sendToTransport = 0; break; @@ -1470,8 +1475,7 @@ static int isd200_init_info(struct us_data *us) if (retStatus == ISD200_GOOD) { us->extra = info; us->extra_destructor = isd200_free_info_ptrs; - } else - US_DEBUGP("ERROR - kmalloc failure\n"); + } return retStatus; } @@ -1482,19 +1486,19 @@ static int isd200_init_info(struct us_data *us) static int isd200_Initialization(struct us_data *us) { - US_DEBUGP("ISD200 Initialization...\n"); + usb_stor_dbg(us, "ISD200 Initialization...\n"); /* Initialize ISD200 info struct */ if (isd200_init_info(us) == ISD200_ERROR) { - US_DEBUGP("ERROR Initializing ISD200 Info struct\n"); + usb_stor_dbg(us, "ERROR Initializing ISD200 Info struct\n"); } else { /* Get device specific data */ if (isd200_get_inquiry_data(us) != ISD200_GOOD) - US_DEBUGP("ISD200 Initialization Failure\n"); + usb_stor_dbg(us, "ISD200 Initialization Failure\n"); else - US_DEBUGP("ISD200 Initialization complete\n"); + usb_stor_dbg(us, "ISD200 Initialization complete\n"); } return 0; @@ -1519,7 +1523,7 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) /* Make sure driver was initialized */ if (us->extra == NULL) - US_DEBUGP("ERROR Driver not initialized\n"); + usb_stor_dbg(us, "ERROR Driver not initialized\n"); scsi_set_resid(srb, 0); /* scsi_bufflen might change in protocol translation to ata */ diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index ddc7878..563078b 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -118,7 +118,7 @@ static inline int jumpshot_bulk_read(struct us_data *us, if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("jumpshot_bulk_read: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL); } @@ -131,7 +131,7 @@ static inline int jumpshot_bulk_write(struct us_data *us, if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("jumpshot_bulk_write: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, NULL); } @@ -152,8 +152,7 @@ static int jumpshot_get_status(struct us_data *us) return USB_STOR_TRANSPORT_ERROR; if (us->iobuf[0] != 0x50) { - US_DEBUGP("jumpshot_get_status: 0x%2x\n", - us->iobuf[0]); + usb_stor_dbg(us, "0x%2x\n", us->iobuf[0]); return USB_STOR_TRANSPORT_ERROR; } @@ -218,7 +217,7 @@ static int jumpshot_read_data(struct us_data *us, if (result != USB_STOR_XFER_GOOD) goto leave; - US_DEBUGP("jumpshot_read_data: %d bytes\n", len); + usb_stor_dbg(us, "%d bytes\n", len); // Store the data in the transfer buffer usb_stor_access_xfer_buf(buffer, len, us->srb, @@ -314,7 +313,7 @@ static int jumpshot_write_data(struct us_data *us, } while ((result != USB_STOR_TRANSPORT_GOOD) && (waitcount < 10)); if (result != USB_STOR_TRANSPORT_GOOD) - US_DEBUGP("jumpshot_write_data: Gah! Waitcount = 10. Bad write!?\n"); + usb_stor_dbg(us, "Gah! Waitcount = 10. Bad write!?\n"); sector += thistime; totallen -= len; @@ -349,8 +348,7 @@ static int jumpshot_id_device(struct us_data *us, 0, 0x20, 0, 6, command, 2); if (rc != USB_STOR_XFER_GOOD) { - US_DEBUGP("jumpshot_id_device: Gah! " - "send_control for read_capacity failed\n"); + usb_stor_dbg(us, "Gah! send_control for read_capacity failed\n"); rc = USB_STOR_TRANSPORT_ERROR; goto leave; } @@ -400,17 +398,17 @@ static int jumpshot_handle_mode_sense(struct us_data *us, switch (pc) { case 0x0: - US_DEBUGP("jumpshot_handle_mode_sense: Current values\n"); - break; + usb_stor_dbg(us, "Current values\n"); + break; case 0x1: - US_DEBUGP("jumpshot_handle_mode_sense: Changeable values\n"); - break; + usb_stor_dbg(us, "Changeable values\n"); + break; case 0x2: - US_DEBUGP("jumpshot_handle_mode_sense: Default values\n"); - break; + usb_stor_dbg(us, "Default values\n"); + break; case 0x3: - US_DEBUGP("jumpshot_handle_mode_sense: Saves values\n"); - break; + usb_stor_dbg(us, "Saves values\n"); + break; } memset(ptr, 0, 8); @@ -494,17 +492,16 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) if (!us->extra) { us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO); - if (!us->extra) { - US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n"); + if (!us->extra) return USB_STOR_TRANSPORT_ERROR; - } + us->extra_destructor = jumpshot_info_destructor; } info = (struct jumpshot_info *) (us->extra); if (srb->cmnd[0] == INQUIRY) { - US_DEBUGP("jumpshot_transport: INQUIRY. Returning bogus response.\n"); + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); memcpy(ptr, inquiry_response, sizeof(inquiry_response)); fill_inquiry_response(us, ptr, 36); return USB_STOR_TRANSPORT_GOOD; @@ -521,8 +518,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("jumpshot_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n", - info->sectors, info->ssize); + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); // build the reply // @@ -534,7 +531,7 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) } if (srb->cmnd[0] == MODE_SELECT_10) { - US_DEBUGP("jumpshot_transport: Gah! MODE_SELECT_10.\n"); + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -544,7 +541,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("jumpshot_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); return jumpshot_read_data(us, info, block, blocks); } @@ -557,7 +555,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("jumpshot_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); return jumpshot_read_data(us, info, block, blocks); } @@ -567,7 +566,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("jumpshot_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); return jumpshot_write_data(us, info, block, blocks); } @@ -580,18 +580,19 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("jumpshot_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); return jumpshot_write_data(us, info, block, blocks); } if (srb->cmnd[0] == TEST_UNIT_READY) { - US_DEBUGP("jumpshot_transport: TEST_UNIT_READY.\n"); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); return jumpshot_get_status(us); } if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("jumpshot_transport: REQUEST_SENSE.\n"); + usb_stor_dbg(us, "REQUEST_SENSE\n"); memset(ptr, 0, 18); ptr[0] = 0xF0; @@ -605,12 +606,12 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) } if (srb->cmnd[0] == MODE_SENSE) { - US_DEBUGP("jumpshot_transport: MODE_SENSE_6 detected\n"); + usb_stor_dbg(us, "MODE_SENSE_6 detected\n"); return jumpshot_handle_mode_sense(us, srb, 1); } if (srb->cmnd[0] == MODE_SENSE_10) { - US_DEBUGP("jumpshot_transport: MODE_SENSE_10 detected\n"); + usb_stor_dbg(us, "MODE_SENSE_10 detected\n"); return jumpshot_handle_mode_sense(us, srb, 0); } @@ -624,7 +625,7 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) if (srb->cmnd[0] == START_STOP) { /* this is used by sd.c'check_scsidisk_media_change to detect media change */ - US_DEBUGP("jumpshot_transport: START_STOP.\n"); + usb_stor_dbg(us, "START_STOP\n"); /* the first jumpshot_id_device after a media change returns an error (determined experimentally) */ rc = jumpshot_id_device(us, info); @@ -638,8 +639,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) return rc; } - US_DEBUGP("jumpshot_transport: Gah! Unknown command: %d (0x%x)\n", - srb->cmnd[0], srb->cmnd[0]); + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); info->sense_key = 0x05; info->sense_asc = 0x20; info->sense_ascq = 0x00; diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index f085ffb..94d16ee 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -106,7 +106,7 @@ static int rio_karma_send_command(char cmd, struct us_data *us) static unsigned char seq = 1; struct karma_data *data = (struct karma_data *) us->extra; - US_DEBUGP("karma: sending command %04x\n", cmd); + usb_stor_dbg(us, "sending command %04x\n", cmd); memset(us->iobuf, 0, RIO_SEND_LEN); memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN); us->iobuf[5] = cmd; @@ -139,10 +139,10 @@ static int rio_karma_send_command(char cmd, struct us_data *us) if (seq == 0) seq = 1; - US_DEBUGP("karma: sent command %04x\n", cmd); + usb_stor_dbg(us, "sent command %04x\n", cmd); return 0; err: - US_DEBUGP("karma: command %04x failed\n", cmd); + usb_stor_dbg(us, "command %04x failed\n", cmd); return USB_STOR_TRANSPORT_FAILED; } diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index e0f76bb..b2b35b1 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -50,7 +50,7 @@ static int option_rezero(struct us_data *us) char *buffer; int result; - US_DEBUGP("Option MS: %s", "DEVICE MODE SWITCH\n"); + usb_stor_dbg(us, "Option MS: %s\n", "DEVICE MODE SWITCH"); buffer = kzalloc(RESPONSE_LEN, GFP_KERNEL); if (buffer == NULL) @@ -95,7 +95,7 @@ static int option_inquiry(struct us_data *us) char *buffer; int result; - US_DEBUGP("Option MS: %s", "device inquiry for vendor name\n"); + usb_stor_dbg(us, "Option MS: %s\n", "device inquiry for vendor name"); buffer = kzalloc(0x24, GFP_KERNEL); if (buffer == NULL) @@ -138,31 +138,32 @@ int option_ms_init(struct us_data *us) { int result; - US_DEBUGP("Option MS: option_ms_init called\n"); + usb_stor_dbg(us, "Option MS: %s\n", "option_ms_init called"); /* Additional test for vendor information via INQUIRY, * because some vendor/product IDs are ambiguous */ result = option_inquiry(us); if (result != 0) { - US_DEBUGP("Option MS: vendor is not Option or not determinable," - " no action taken\n"); + usb_stor_dbg(us, "Option MS: %s\n", + "vendor is not Option or not determinable, no action taken"); return 0; } else - US_DEBUGP("Option MS: this is a genuine Option device," - " proceeding\n"); + usb_stor_dbg(us, "Option MS: %s\n", + "this is a genuine Option device, proceeding"); /* Force Modem mode */ if (option_zero_cd == ZCD_FORCE_MODEM) { - US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n"); + usb_stor_dbg(us, "Option MS: %s\n", "Forcing Modem Mode"); result = option_rezero(us); if (result != USB_STOR_XFER_GOOD) - US_DEBUGP("Option MS: Failed to switch to modem mode.\n"); + usb_stor_dbg(us, "Option MS: %s\n", + "Failed to switch to modem mode"); return -EIO; } else if (option_zero_cd == ZCD_ALLOW_MS) { /* Allow Mass Storage mode (keep CD-Rom) */ - US_DEBUGP("Option MS: %s", "Allowing Mass Storage Mode if device" - " requests it\n"); + usb_stor_dbg(us, "Option MS: %s\n", + "Allowing Mass Storage Mode if device requests it"); } return 0; diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 6c3586a..4797228 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -254,8 +254,8 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, /* check bulk status */ if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) { - US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n", - le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN); + usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n", + le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN); return USB_STOR_TRANSPORT_ERROR; } @@ -351,8 +351,8 @@ static int rts51x_get_max_lun(struct us_data *us) USB_RECIP_INTERFACE, 0, us->ifnum, us->iobuf, 1, 10 * HZ); - US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", - result, us->iobuf[0]); + usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n", + result, us->iobuf[0]); /* if we have a successful request, return the result */ if (result > 0) @@ -371,7 +371,7 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) if (buf == NULL) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); + usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len); cmnd[0] = 0xF0; cmnd[1] = 0x0D; @@ -402,7 +402,7 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) if (buf == NULL) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); + usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len); cmnd[0] = 0xF0; cmnd[1] = 0x0E; @@ -431,7 +431,7 @@ static int rts51x_read_status(struct us_data *us, if (buf == NULL) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("%s, lun = %d\n", __func__, lun); + usb_stor_dbg(us, "lun = %d\n", lun); cmnd[0] = 0xF0; cmnd[1] = 0x09; @@ -458,7 +458,7 @@ static int rts51x_check_status(struct us_data *us, u8 lun) if (retval != STATUS_SUCCESS) return -EIO; - US_DEBUGP("chip->status_len = %d\n", chip->status_len); + usb_stor_dbg(us, "chip->status_len = %d\n", chip->status_len); chip->status[lun].vid = ((u16) buf[0] << 8) | buf[1]; chip->status[lun].pid = ((u16) buf[2] << 8) | buf[3]; @@ -509,7 +509,7 @@ static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len) u8 cmnd[12] = {0}; u8 *buf; - US_DEBUGP("%s, addr = 0xfe47, len = %d\n", __FUNCTION__, len); + usb_stor_dbg(us, "addr = 0xfe47, len = %d\n", len); buf = kmemdup(data, len, GFP_NOIO); if (!buf) @@ -549,7 +549,7 @@ static int do_config_autodelink(struct us_data *us, int enable, int force) value &= ~0x03; } - US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value); + usb_stor_dbg(us, "set 0xfe47 to 0x%x\n", value); /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ retval = __do_config_autodelink(us, &value, 1); @@ -565,8 +565,6 @@ static int config_autodelink_after_power_on(struct us_data *us) int retval; u8 value; - US_DEBUGP("%s: <---\n", __func__); - if (!CHK_AUTO_DELINK(chip)) return 0; @@ -624,8 +622,6 @@ static int config_autodelink_after_power_on(struct us_data *us) } } - US_DEBUGP("%s: --->\n", __func__); - return 0; } @@ -635,8 +631,6 @@ static int config_autodelink_before_power_down(struct us_data *us) int retval; u8 value; - US_DEBUGP("%s: <---\n", __func__); - if (!CHK_AUTO_DELINK(chip)) return 0; @@ -698,8 +692,6 @@ static int config_autodelink_before_power_down(struct us_data *us) } } - US_DEBUGP("%s: --->\n", __func__); - return 0; } @@ -709,23 +701,19 @@ static void fw5895_init(struct us_data *us) int retval; u8 val; - US_DEBUGP("%s: <---\n", __func__); - if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) { - US_DEBUGP("Not the specified device, return immediately!\n"); + usb_stor_dbg(us, "Not the specified device, return immediately!\n"); } else { retval = rts51x_read_mem(us, 0xFD6F, &val, 1); if (retval == STATUS_SUCCESS && (val & 0x1F) == 0) { val = 0x1F; retval = rts51x_write_mem(us, 0xFD70, &val, 1); if (retval != STATUS_SUCCESS) - US_DEBUGP("Write memory fail\n"); + usb_stor_dbg(us, "Write memory fail\n"); } else { - US_DEBUGP("Read memory fail, OR (val & 0x1F) != 0\n"); + usb_stor_dbg(us, "Read memory fail, OR (val & 0x1F) != 0\n"); } } - - US_DEBUGP("%s: --->\n", __func__); } #ifdef CONFIG_REALTEK_AUTOPM @@ -735,10 +723,8 @@ static void fw5895_set_mmc_wp(struct us_data *us) int retval; u8 buf[13]; - US_DEBUGP("%s: <---\n", __func__); - if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) { - US_DEBUGP("Not the specified device, return immediately!\n"); + usb_stor_dbg(us, "Not the specified device, return immediately!\n"); } else { retval = rts51x_read_mem(us, 0xFD6F, buf, 1); if (retval == STATUS_SUCCESS && (buf[0] & 0x24) == 0x24) { @@ -748,26 +734,24 @@ static void fw5895_set_mmc_wp(struct us_data *us) buf[0] |= 0x04; retval = rts51x_write_mem(us, 0xFD70, buf, 1); if (retval != STATUS_SUCCESS) - US_DEBUGP("Write memory fail\n"); + usb_stor_dbg(us, "Write memory fail\n"); } else { - US_DEBUGP("Read memory fail\n"); + usb_stor_dbg(us, "Read memory fail\n"); } } else { - US_DEBUGP("Read memory fail, OR (buf[0]&0x24)!=0x24\n"); + usb_stor_dbg(us, "Read memory fail, OR (buf[0]&0x24)!=0x24\n"); } } - - US_DEBUGP("%s: --->\n", __func__); } static void rts51x_modi_suspend_timer(struct rts51x_chip *chip) { - US_DEBUGP("%s: <---, state:%d\n", __func__, rts51x_get_stat(chip)); + struct us_data *us = chip->us; + + usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip)); chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay); mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires); - - US_DEBUGP("%s: --->\n", __func__); } static void rts51x_suspend_timer_fn(unsigned long data) @@ -775,8 +759,6 @@ static void rts51x_suspend_timer_fn(unsigned long data) struct rts51x_chip *chip = (struct rts51x_chip *)data; struct us_data *us = chip->us; - US_DEBUGP("%s: <---\n", __func__); - switch (rts51x_get_stat(chip)) { case RTS51X_STAT_INIT: case RTS51X_STAT_RUN: @@ -784,32 +766,25 @@ static void rts51x_suspend_timer_fn(unsigned long data) break; case RTS51X_STAT_IDLE: case RTS51X_STAT_SS: - US_DEBUGP("%s: RTS51X_STAT_SS, intf->pm_usage_cnt:%d," - "power.usage:%d\n", __func__, - atomic_read(&us->pusb_intf->pm_usage_cnt), - atomic_read(&us->pusb_intf->dev.power.usage_count)); + usb_stor_dbg(us, "RTS51X_STAT_SS, intf->pm_usage_cnt:%d, power.usage:%d\n", + atomic_read(&us->pusb_intf->pm_usage_cnt), + atomic_read(&us->pusb_intf->dev.power.usage_count)); if (atomic_read(&us->pusb_intf->pm_usage_cnt) > 0) { - US_DEBUGP("%s: Ready to enter SS state.\n", - __func__); + usb_stor_dbg(us, "Ready to enter SS state\n"); rts51x_set_stat(chip, RTS51X_STAT_SS); /* ignore mass storage interface's children */ pm_suspend_ignore_children(&us->pusb_intf->dev, true); usb_autopm_put_interface_async(us->pusb_intf); - US_DEBUGP("%s: RTS51X_STAT_SS 01," - "intf->pm_usage_cnt:%d, power.usage:%d\n", - __func__, - atomic_read(&us->pusb_intf->pm_usage_cnt), - atomic_read( - &us->pusb_intf->dev.power.usage_count)); + usb_stor_dbg(us, "RTS51X_STAT_SS 01, intf->pm_usage_cnt:%d, power.usage:%d\n", + atomic_read(&us->pusb_intf->pm_usage_cnt), + atomic_read(&us->pusb_intf->dev.power.usage_count)); } break; default: - US_DEBUGP("%s: Unknonwn state !!!\n", __func__); + usb_stor_dbg(us, "Unknown state !!!\n"); break; } - - US_DEBUGP("%s: --->\n", __func__); } static inline int working_scsi(struct scsi_cmnd *srb) @@ -834,24 +809,21 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) }; int ret; - US_DEBUGP("%s: <---\n", __func__); - if (working_scsi(srb)) { - US_DEBUGP("%s: working scsi, intf->pm_usage_cnt:%d," - "power.usage:%d\n", __func__, - atomic_read(&us->pusb_intf->pm_usage_cnt), - atomic_read(&us->pusb_intf->dev.power.usage_count)); + usb_stor_dbg(us, "working scsi, intf->pm_usage_cnt:%d, power.usage:%d\n", + atomic_read(&us->pusb_intf->pm_usage_cnt), + atomic_read(&us->pusb_intf->dev.power.usage_count)); if (atomic_read(&us->pusb_intf->pm_usage_cnt) <= 0) { ret = usb_autopm_get_interface(us->pusb_intf); - US_DEBUGP("%s: working scsi, ret=%d\n", __func__, ret); + usb_stor_dbg(us, "working scsi, ret=%d\n", ret); } if (rts51x_get_stat(chip) != RTS51X_STAT_RUN) rts51x_set_stat(chip, RTS51X_STAT_RUN); chip->proto_handler_backup(srb, us); } else { if (rts51x_get_stat(chip) == RTS51X_STAT_SS) { - US_DEBUGP("%s: NOT working scsi\n", __func__); + usb_stor_dbg(us, "NOT working scsi\n"); if ((srb->cmnd[0] == TEST_UNIT_READY) && (chip->pwr_state == US_SUSPEND)) { if (TST_LUN_READY(chip, srb->device->lun)) { @@ -862,8 +834,7 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) media_not_present, US_SENSE_SIZE); } - US_DEBUGP("%s: TEST_UNIT_READY--->\n", - __func__); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); goto out; } if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { @@ -876,12 +847,11 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) } else { srb->result = SAM_STAT_GOOD; } - US_DEBUGP("%s: ALLOW_MEDIUM_REMOVAL--->\n", - __func__); + usb_stor_dbg(us, "ALLOW_MEDIUM_REMOVAL\n"); goto out; } } else { - US_DEBUGP("%s: NOT working scsi, not SS\n", __func__); + usb_stor_dbg(us, "NOT working scsi, not SS\n"); chip->proto_handler_backup(srb, us); /* Check whether card is plugged in */ if (srb->cmnd[0] == TEST_UNIT_READY) { @@ -901,11 +871,9 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) } } out: - US_DEBUGP("%s: state:%d\n", __func__, rts51x_get_stat(chip)); + usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip)); if (rts51x_get_stat(chip) == RTS51X_STAT_RUN) rts51x_modi_suspend_timer(chip); - - US_DEBUGP("%s: --->\n", __func__); } static int realtek_cr_autosuspend_setup(struct us_data *us) @@ -923,7 +891,7 @@ static int realtek_cr_autosuspend_setup(struct us_data *us) retval = rts51x_read_status(us, 0, buf, 16, &(chip->status_len)); if (retval != STATUS_SUCCESS) { - US_DEBUGP("Read status fail\n"); + usb_stor_dbg(us, "Read status fail\n"); return -EIO; } status = chip->status; @@ -966,11 +934,13 @@ static int realtek_cr_autosuspend_setup(struct us_data *us) static void realtek_cr_destructor(void *extra) { struct rts51x_chip *chip = (struct rts51x_chip *)extra; - - US_DEBUGP("%s: <---\n", __func__); + struct us_data *us; if (!chip) return; + + us = chip->us; + #ifdef CONFIG_REALTEK_AUTOPM if (ss_en) { del_timer(&chip->rts51x_suspend_timer); @@ -985,8 +955,6 @@ static int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message) { struct us_data *us = usb_get_intfdata(iface); - US_DEBUGP("%s: <---\n", __func__); - /* wait until no command is running */ mutex_lock(&us->dev_mutex); @@ -994,8 +962,6 @@ static int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message) mutex_unlock(&us->dev_mutex); - US_DEBUGP("%s: --->\n", __func__); - return 0; } @@ -1003,13 +969,9 @@ static int realtek_cr_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - US_DEBUGP("%s: <---\n", __func__); - fw5895_init(us); config_autodelink_after_power_on(us); - US_DEBUGP("%s: --->\n", __func__); - return 0; } #else @@ -1030,7 +992,7 @@ static int init_realtek_cr(struct us_data *us) us->extra_destructor = realtek_cr_destructor; us->max_lun = chip->max_lun = rts51x_get_max_lun(us); - US_DEBUGP("chip->max_lun = %d\n", chip->max_lun); + usb_stor_dbg(us, "chip->max_lun = %d\n", chip->max_lun); size = (chip->max_lun + 1) * sizeof(struct rts51x_status); chip->status = kzalloc(size, GFP_KERNEL); @@ -1057,7 +1019,7 @@ static int init_realtek_cr(struct us_data *us) } #endif - US_DEBUGP("chip->flag = 0x%x\n", chip->flag); + usb_stor_dbg(us, "chip->flag = 0x%x\n", chip->flag); (void)config_autodelink_after_power_on(us); @@ -1079,7 +1041,7 @@ static int realtek_cr_probe(struct usb_interface *intf, struct us_data *us; int result; - US_DEBUGP("Probe Realtek Card Reader!\n"); + dev_dbg(&intf->dev, "Probe Realtek Card Reader!\n"); result = usb_stor_probe1(&us, intf, id, (id - realtek_cr_ids) + diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 92f35ab..4faa982 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -313,8 +313,6 @@ static int queuecommand_lck(struct scsi_cmnd *srb, { struct us_data *us = host_to_us(srb->device->host); - US_DEBUGP("%s called\n", __func__); - /* check for state-transition errors */ if (us->srb != NULL) { printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n", @@ -324,7 +322,7 @@ static int queuecommand_lck(struct scsi_cmnd *srb, /* fail the command if we are disconnecting */ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { - US_DEBUGP("Fail command during disconnect\n"); + usb_stor_dbg(us, "Fail command during disconnect\n"); srb->result = DID_NO_CONNECT << 16; done(srb); return 0; @@ -349,7 +347,7 @@ static int command_abort(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); - US_DEBUGP("%s called\n", __func__); + usb_stor_dbg(us, "%s called\n", __func__); /* us->srb together with the TIMED_OUT, RESETTING, and ABORTING * bits are protected by the host lock. */ @@ -358,7 +356,7 @@ static int command_abort(struct scsi_cmnd *srb) /* Is this command still active? */ if (us->srb != srb) { scsi_unlock(us_to_host(us)); - US_DEBUGP ("-- nothing to abort\n"); + usb_stor_dbg(us, "-- nothing to abort\n"); return FAILED; } @@ -386,7 +384,7 @@ static int device_reset(struct scsi_cmnd *srb) struct us_data *us = host_to_us(srb->device->host); int result; - US_DEBUGP("%s called\n", __func__); + usb_stor_dbg(us, "%s called\n", __func__); /* lock the device pointers and do the reset */ mutex_lock(&(us->dev_mutex)); @@ -402,7 +400,8 @@ static int bus_reset(struct scsi_cmnd *srb) struct us_data *us = host_to_us(srb->device->host); int result; - US_DEBUGP("%s called\n", __func__); + usb_stor_dbg(us, "%s called\n", __func__); + result = usb_stor_port_reset(us); return result < 0 ? FAILED : SUCCESS; } diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 7bd54e0..732027f 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -105,8 +105,6 @@ static struct us_unusual_dev sddr09_unusual_dev_list[] = { #define LSB_of(s) ((s)&0xFF) #define MSB_of(s) ((s)>>8) -/* #define US_DEBUGP printk */ - /* * First some stuff that does not belong here: * data on SmartMedia and other cards, completely @@ -347,7 +345,7 @@ sddr09_test_unit_ready(struct us_data *us) { result = sddr09_send_scsi_command(us, command, 6); - US_DEBUGP("sddr09_test_unit_ready returns %d\n", result); + usb_stor_dbg(us, "sddr09_test_unit_ready returns %d\n", result); return result; } @@ -423,8 +421,8 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, result = sddr09_send_scsi_command(us, command, 12); if (result) { - US_DEBUGP("Result for send_control in sddr09_read2%d %d\n", - x, result); + usb_stor_dbg(us, "Result for send_control in sddr09_read2%d %d\n", + x, result); return result; } @@ -432,8 +430,8 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, buf, bulklen, use_sg, NULL); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for bulk_transfer in sddr09_read2%d %d\n", - x, result); + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_read2%d %d\n", + x, result); return -EIO; } return 0; @@ -494,8 +492,7 @@ sddr09_read22(struct us_data *us, unsigned long fromaddress, int nr_of_pages, int pageshift, unsigned char *buf, int use_sg) { int bulklen = (nr_of_pages << pageshift) + (nr_of_pages << CONTROL_SHIFT); - US_DEBUGP("sddr09_read22: reading %d pages, %d bytes\n", - nr_of_pages, bulklen); + usb_stor_dbg(us, "reading %d pages, %d bytes\n", nr_of_pages, bulklen); return sddr09_readX(us, 2, fromaddress, nr_of_pages, bulklen, buf, use_sg); } @@ -538,7 +535,7 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) { unsigned char *command = us->iobuf; int result; - US_DEBUGP("sddr09_erase: erase address %lu\n", Eaddress); + usb_stor_dbg(us, "erase address %lu\n", Eaddress); memset(command, 0, 12); command[0] = 0xEA; @@ -551,8 +548,8 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) { result = sddr09_send_scsi_command(us, command, 12); if (result) - US_DEBUGP("Result for send_control in sddr09_erase %d\n", - result); + usb_stor_dbg(us, "Result for send_control in sddr09_erase %d\n", + result); return result; } @@ -609,8 +606,8 @@ sddr09_writeX(struct us_data *us, result = sddr09_send_scsi_command(us, command, 12); if (result) { - US_DEBUGP("Result for send_control in sddr09_writeX %d\n", - result); + usb_stor_dbg(us, "Result for send_control in sddr09_writeX %d\n", + result); return result; } @@ -618,8 +615,8 @@ sddr09_writeX(struct us_data *us, buf, bulklen, use_sg, NULL); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for bulk_transfer in sddr09_writeX %d\n", - result); + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_writeX %d\n", + result); return -EIO; } return 0; @@ -687,8 +684,8 @@ sddr09_read_sg_test_only(struct us_data *us) { result = sddr09_send_scsi_command(us, command, 4*nsg+3); if (result) { - US_DEBUGP("Result for send_control in sddr09_read_sg %d\n", - result); + usb_stor_dbg(us, "Result for send_control in sddr09_read_sg %d\n", + result); return result; } @@ -700,8 +697,8 @@ sddr09_read_sg_test_only(struct us_data *us) { buf, bulklen, NULL); kfree(buf); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for bulk_transfer in sddr09_read_sg %d\n", - result); + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_read_sg %d\n", + result); return -EIO; } @@ -727,7 +724,7 @@ sddr09_read_status(struct us_data *us, unsigned char *status) { unsigned char *data = us->iobuf; int result; - US_DEBUGP("Reading status...\n"); + usb_stor_dbg(us, "Reading status...\n"); memset(command, 0, 12); command[0] = 0xEC; @@ -789,8 +786,8 @@ sddr09_read_data(struct us_data *us, /* Not overflowing capacity? */ if (lba >= maxlba) { - US_DEBUGP("Error: Requested lba %u exceeds " - "maximum %u\n", lba, maxlba); + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, maxlba); result = -EIO; break; } @@ -800,8 +797,8 @@ sddr09_read_data(struct us_data *us, if (pba == UNDEF) { /* this lba was never written */ - US_DEBUGP("Read %d zero pages (LBA %d) page %d\n", - pages, lba, page); + usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); /* This is not really an error. It just means that the block has never been written. @@ -811,9 +808,8 @@ sddr09_read_data(struct us_data *us, memset(buffer, 0, len); } else { - US_DEBUGP("Read %d pages, from PBA %d" - " (LBA %d) page %d\n", - pages, pba, lba, page); + usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", + pages, pba, lba, page); address = ((pba << info->blockshift) + page) << info->pageshift; @@ -916,14 +912,14 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, cptr = bptr + info->pagesize; nand_compute_ecc(bptr, ecc); if (!nand_compare_ecc(cptr+13, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", + i, pba); nand_store_ecc(cptr+13, ecc); } nand_compute_ecc(bptr+(info->pagesize / 2), ecc); if (!nand_compare_ecc(cptr+8, ecc)) { - US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n", - i, pba); + usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", + i, pba); nand_store_ecc(cptr+8, ecc); } cptr[6] = cptr[11] = MSB_of(lbap); @@ -943,22 +939,21 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, nand_store_ecc(cptr+8, ecc); } - US_DEBUGP("Rewrite PBA %d (LBA %d)\n", pba, lba); + usb_stor_dbg(us, "Rewrite PBA %d (LBA %d)\n", pba, lba); result = sddr09_write_inplace(us, address>>1, info->blocksize, info->pageshift, blockbuffer, 0); - US_DEBUGP("sddr09_write_inplace returns %d\n", result); + usb_stor_dbg(us, "sddr09_write_inplace returns %d\n", result); #if 0 { unsigned char status = 0; int result2 = sddr09_read_status(us, &status); if (result2) - US_DEBUGP("sddr09_write_inplace: cannot read status\n"); + usb_stor_dbg(us, "cannot read status\n"); else if (status != 0xc0) - US_DEBUGP("sddr09_write_inplace: status after write: 0x%x\n", - status); + usb_stor_dbg(us, "status after write: 0x%x\n", status); } #endif @@ -1031,8 +1026,8 @@ sddr09_write_data(struct us_data *us, /* Not overflowing capacity? */ if (lba >= maxlba) { - US_DEBUGP("Error: Requested lba %u exceeds " - "maximum %u\n", lba, maxlba); + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, maxlba); result = -EIO; break; } @@ -1064,8 +1059,8 @@ sddr09_read_control(struct us_data *us, unsigned char *content, int use_sg) { - US_DEBUGP("Read control address %lu, blocks %d\n", - address, blocks); + usb_stor_dbg(us, "Read control address %lu, blocks %d\n", + address, blocks); return sddr09_read21(us, address, blocks, CONTROL_SHIFT, content, use_sg); @@ -1111,21 +1106,21 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { result = sddr09_read_status(us, &status); if (result) { - US_DEBUGP("sddr09_get_wp: read_status fails\n"); + usb_stor_dbg(us, "read_status fails\n"); return result; } - US_DEBUGP("sddr09_get_wp: status 0x%02X", status); + usb_stor_dbg(us, "status 0x%02X", status); if ((status & 0x80) == 0) { info->flags |= SDDR09_WP; /* write protected */ - US_DEBUGP(" WP"); + US_DEBUGPX(" WP"); } if (status & 0x40) - US_DEBUGP(" Ready"); + US_DEBUGPX(" Ready"); if (status & LUNBITS) - US_DEBUGP(" Suspended"); + US_DEBUGPX(" Suspended"); if (status & 0x1) - US_DEBUGP(" Error"); - US_DEBUGP("\n"); + US_DEBUGPX(" Error"); + US_DEBUGPX("\n"); return 0; } @@ -1154,12 +1149,12 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { char blurbtxt[256]; int result; - US_DEBUGP("Reading capacity...\n"); + usb_stor_dbg(us, "Reading capacity...\n"); result = sddr09_read_deviceID(us, deviceID); if (result) { - US_DEBUGP("Result of read_deviceID is %d\n", result); + usb_stor_dbg(us, "Result of read_deviceID is %d\n", result); printk(KERN_WARNING "sddr09: could not read card info\n"); return NULL; } @@ -1392,7 +1387,7 @@ sddr09_read_map(struct us_data *us) { lbact += ct; } info->lbact = lbact; - US_DEBUGP("Found %d LBA's\n", lbact); + usb_stor_dbg(us, "Found %d LBA's\n", lbact); result = 0; done: @@ -1423,18 +1418,18 @@ sddr09_common_init(struct us_data *us) { /* set the configuration -- STALL is an acceptable response here */ if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { - US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev - ->actconfig->desc.bConfigurationValue); + usb_stor_dbg(us, "active config #%d != 1 ??\n", + us->pusb_dev->actconfig->desc.bConfigurationValue); return -EINVAL; } result = usb_reset_configuration(us->pusb_dev); - US_DEBUGP("Result of usb_reset_configuration is %d\n", result); + usb_stor_dbg(us, "Result of usb_reset_configuration is %d\n", result); if (result == -EPIPE) { - US_DEBUGP("-- stall on control interface\n"); + usb_stor_dbg(us, "-- stall on control interface\n"); } else if (result != 0) { /* it's not a stall, but another error -- time to bail */ - US_DEBUGP("-- Unknown error. Rejecting device\n"); + usb_stor_dbg(us, "-- Unknown error. Rejecting device\n"); return -EINVAL; } @@ -1464,20 +1459,20 @@ usb_stor_sddr09_dpcm_init(struct us_data *us) { result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2); if (result) { - US_DEBUGP("sddr09_init: send_command fails\n"); + usb_stor_dbg(us, "send_command fails\n"); return result; } - US_DEBUGP("SDDR09init: %02X %02X\n", data[0], data[1]); + usb_stor_dbg(us, "%02X %02X\n", data[0], data[1]); // get 07 02 result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2); if (result) { - US_DEBUGP("sddr09_init: 2nd send_command fails\n"); + usb_stor_dbg(us, "2nd send_command fails\n"); return result; } - US_DEBUGP("SDDR09init: %02X %02X\n", data[0], data[1]); + usb_stor_dbg(us, "%02X %02X\n", data[0], data[1]); // get 07 00 result = sddr09_request_sense(us, data, 18); @@ -1507,7 +1502,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) { int ret; - US_DEBUGP("dpcm_transport: LUN=%d\n", srb->device->lun); + usb_stor_dbg(us, "LUN=%d\n", srb->device->lun); switch (srb->device->lun) { case 0: @@ -1533,8 +1528,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) break; default: - US_DEBUGP("dpcm_transport: Invalid LUN %d\n", - srb->device->lun); + usb_stor_dbg(us, "Invalid LUN %d\n", srb->device->lun); ret = USB_STOR_TRANSPORT_ERROR; break; } @@ -1640,8 +1634,8 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) or for all pages. */ /* %% We should check DBD %% */ if (modepage == 0x01 || modepage == 0x3F) { - US_DEBUGP("SDDR09: Dummy up request for " - "mode page 0x%x\n", modepage); + usb_stor_dbg(us, "Dummy up request for mode page 0x%x\n", + modepage); memcpy(ptr, mode_page_01, sizeof(mode_page_01)); ((__be16*)ptr)[0] = cpu_to_be16(sizeof(mode_page_01) - 2); @@ -1667,8 +1661,8 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("READ_10: read page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "READ_10: read page %d pagect %d\n", + page, pages); result = sddr09_read_data(us, page, pages); return (result == 0 ? USB_STOR_TRANSPORT_GOOD : @@ -1682,8 +1676,8 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) page |= short_pack(srb->cmnd[5], srb->cmnd[4]); pages = short_pack(srb->cmnd[8], srb->cmnd[7]); - US_DEBUGP("WRITE_10: write page %d pagect %d\n", - page, pages); + usb_stor_dbg(us, "WRITE_10: write page %d pagect %d\n", + page, pages); result = sddr09_write_data(us, page, pages); return (result == 0 ? USB_STOR_TRANSPORT_GOOD : @@ -1710,12 +1704,12 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) for (i=0; i<12; i++) sprintf(ptr+strlen(ptr), "%02X ", srb->cmnd[i]); - US_DEBUGP("SDDR09: Send control for command %s\n", ptr); + usb_stor_dbg(us, "Send control for command %s\n", ptr); result = sddr09_send_scsi_command(us, srb->cmnd, 12); if (result) { - US_DEBUGP("sddr09_transport: sddr09_send_scsi_command " - "returns %d\n", result); + usb_stor_dbg(us, "sddr09_send_scsi_command returns %d\n", + result); return USB_STOR_TRANSPORT_ERROR; } @@ -1727,10 +1721,10 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) unsigned int pipe = (srb->sc_data_direction == DMA_TO_DEVICE) ? us->send_bulk_pipe : us->recv_bulk_pipe; - US_DEBUGP("SDDR09: %s %d bytes\n", - (srb->sc_data_direction == DMA_TO_DEVICE) ? - "sending" : "receiving", - scsi_bufflen(srb)); + usb_stor_dbg(us, "%s %d bytes\n", + (srb->sc_data_direction == DMA_TO_DEVICE) ? + "sending" : "receiving", + scsi_bufflen(srb)); result = usb_stor_bulk_srb(us, pipe, srb); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index d278c5a..aacedef 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -145,8 +145,7 @@ static int sddr55_status(struct us_data *us) result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); - US_DEBUGP("Result for send_command in status %d\n", - result); + usb_stor_dbg(us, "Result for send_command in status %d\n", result); if (result != USB_STOR_XFER_GOOD) { set_sense_info (4, 0, 0); /* hardware error */ @@ -236,9 +235,8 @@ static int sddr55_read_data(struct us_data *us, info->blocksize - page); len = pages << info->pageshift; - US_DEBUGP("Read %02X pages, from PBA %04X" - " (LBA %04X) page %02X\n", - pages, pba, lba, page); + usb_stor_dbg(us, "Read %02X pages, from PBA %04X (LBA %04X) page %02X\n", + pages, pba, lba, page); if (pba == NOT_ALLOCATED) { /* no pba for this lba, fill with zeroes */ @@ -261,8 +259,8 @@ static int sddr55_read_data(struct us_data *us, result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); - US_DEBUGP("Result for send_command in read_data %d\n", - result); + usb_stor_dbg(us, "Result for send_command in read_data %d\n", + result); if (result != USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; @@ -368,9 +366,8 @@ static int sddr55_write_data(struct us_data *us, usb_stor_access_xfer_buf(buffer, len, us->srb, &sg, &offset, FROM_XFER_BUF); - US_DEBUGP("Write %02X pages, to PBA %04X" - " (LBA %04X) page %02X\n", - pages, pba, lba, page); + usb_stor_dbg(us, "Write %02X pages, to PBA %04X (LBA %04X) page %02X\n", + pages, pba, lba, page); command[4] = 0; @@ -384,7 +381,7 @@ static int sddr55_write_data(struct us_data *us, /* set pba to first block in zone lba is in */ pba = (lba / 1000) * 1024; - US_DEBUGP("No PBA for LBA %04X\n",lba); + usb_stor_dbg(us, "No PBA for LBA %04X\n", lba); if (max_pba > 1024) max_pba = 1024; @@ -407,14 +404,15 @@ static int sddr55_write_data(struct us_data *us, if (pba == -1) { /* oh dear */ - US_DEBUGP("Couldn't find unallocated block\n"); + usb_stor_dbg(us, "Couldn't find unallocated block\n"); set_sense_info (3, 0x31, 0); /* medium error */ result = USB_STOR_TRANSPORT_FAILED; goto leave; } - US_DEBUGP("Allocating PBA %04X for LBA %04X\n", pba, lba); + usb_stor_dbg(us, "Allocating PBA %04X for LBA %04X\n", + pba, lba); /* set writing to unallocated block flag */ command[4] = 0x40; @@ -439,8 +437,8 @@ static int sddr55_write_data(struct us_data *us, DMA_TO_DEVICE, command, 8); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for send_command in write_data %d\n", - result); + usb_stor_dbg(us, "Result for send_command in write_data %d\n", + result); /* set_sense_info is superfluous here? */ set_sense_info (3, 0x3, 0);/* peripheral write error */ @@ -453,8 +451,8 @@ static int sddr55_write_data(struct us_data *us, DMA_TO_DEVICE, buffer, len); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for send_data in write_data %d\n", - result); + usb_stor_dbg(us, "Result for send_data in write_data %d\n", + result); /* set_sense_info is superfluous here? */ set_sense_info (3, 0x3, 0);/* peripheral write error */ @@ -466,8 +464,8 @@ static int sddr55_write_data(struct us_data *us, result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6); if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("Result for get_status in write_data %d\n", - result); + usb_stor_dbg(us, "Result for get_status in write_data %d\n", + result); /* set_sense_info is superfluous here? */ set_sense_info (3, 0x3, 0);/* peripheral write error */ @@ -487,8 +485,8 @@ static int sddr55_write_data(struct us_data *us, goto leave; } - US_DEBUGP("Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n", - lba, pba, new_pba); + usb_stor_dbg(us, "Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n", + lba, pba, new_pba); /* update the lba<->pba maps, note new_pba might be the same as pba */ info->lba_to_pba[lba] = new_pba; @@ -531,8 +529,8 @@ static int sddr55_read_deviceID(struct us_data *us, command[7] = 0x84; result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); - US_DEBUGP("Result of send_control for device ID is %d\n", - result); + usb_stor_dbg(us, "Result of send_control for device ID is %d\n", + result); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -568,20 +566,19 @@ static unsigned long sddr55_get_capacity(struct us_data *us) { int result; struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; - US_DEBUGP("Reading capacity...\n"); + usb_stor_dbg(us, "Reading capacity...\n"); result = sddr55_read_deviceID(us, &manufacturerID, &deviceID); - US_DEBUGP("Result of read_deviceID is %d\n", - result); + usb_stor_dbg(us, "Result of read_deviceID is %d\n", result); if (result != USB_STOR_XFER_GOOD) return 0; - US_DEBUGP("Device ID = %02X\n", deviceID); - US_DEBUGP("Manuf ID = %02X\n", manufacturerID); + usb_stor_dbg(us, "Device ID = %02X\n", deviceID); + usb_stor_dbg(us, "Manuf ID = %02X\n", manufacturerID); info->pageshift = 9; info->smallpageshift = 0; @@ -753,7 +750,7 @@ static int sddr55_read_map(struct us_data *us) { } if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF)) - US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i); + usb_stor_dbg(us, "LBA %04X <-> PBA %04X\n", lba, i); info->lba_to_pba[lba + zone * 1000] = i; } @@ -808,7 +805,10 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) info = (struct sddr55_card_info *)(us->extra); if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("SDDR55: request sense %02x/%02x/%02x\n", info->sense_data[2], info->sense_data[12], info->sense_data[13]); + usb_stor_dbg(us, "request sense %02x/%02x/%02x\n", + info->sense_data[2], + info->sense_data[12], + info->sense_data[13]); memcpy (ptr, info->sense_data, sizeof info->sense_data); ptr[0] = 0x70; @@ -892,13 +892,11 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb); if ( (srb->cmnd[2] & 0x3F) == 0x01 ) { - US_DEBUGP( - "SDDR55: Dummy up request for mode page 1\n"); + usb_stor_dbg(us, "Dummy up request for mode page 1\n"); return USB_STOR_TRANSPORT_GOOD; } else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) { - US_DEBUGP( - "SDDR55: Dummy up request for all mode pages\n"); + usb_stor_dbg(us, "Dummy up request for all mode pages\n"); return USB_STOR_TRANSPORT_GOOD; } @@ -908,10 +906,8 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { - US_DEBUGP( - "SDDR55: %s medium removal. Not that I can do" - " anything about it...\n", - (srb->cmnd[4]&0x03) ? "Prevent" : "Allow"); + usb_stor_dbg(us, "%s medium removal. Not that I can do anything about it...\n", + (srb->cmnd[4]&0x03) ? "Prevent" : "Allow"); return USB_STOR_TRANSPORT_GOOD; @@ -935,8 +931,8 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) if (lba >= info->max_log_blks) { - US_DEBUGP("Error: Requested LBA %04X exceeds maximum " - "block %04X\n", lba, info->max_log_blks-1); + usb_stor_dbg(us, "Error: Requested LBA %04X exceeds maximum block %04X\n", + lba, info->max_log_blks - 1); set_sense_info (5, 0x24, 0); /* invalid field in command */ @@ -946,15 +942,13 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) pba = info->lba_to_pba[lba]; if (srb->cmnd[0] == WRITE_10) { - US_DEBUGP("WRITE_10: write block %04X (LBA %04X) page %01X" - " pages %d\n", - pba, lba, page, pages); + usb_stor_dbg(us, "WRITE_10: write block %04X (LBA %04X) page %01X pages %d\n", + pba, lba, page, pages); return sddr55_write_data(us, lba, page, pages); } else { - US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X" - " pages %d\n", - pba, lba, page, pages); + usb_stor_dbg(us, "READ_10: read block %04X (LBA %04X) page %01X pages %d\n", + pba, lba, page, pages); return sddr55_read_data(us, lba, page, pages); } diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index daf2fc5..4ef2a80 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -271,7 +271,7 @@ static int usbat_bulk_read(struct us_data *us, if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("usbat_bulk_read: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe, buf, len, use_sg, NULL); } @@ -286,7 +286,7 @@ static int usbat_bulk_write(struct us_data *us, if (len == 0) return USB_STOR_XFER_GOOD; - US_DEBUGP("usbat_bulk_write: len = %d\n", len); + usb_stor_dbg(us, "len = %d\n", len); return usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe, buf, len, use_sg, NULL); } @@ -312,7 +312,7 @@ static int usbat_get_status(struct us_data *us, unsigned char *status) int rc; rc = usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status); - US_DEBUGP("usbat_get_status: 0x%02X\n", (unsigned short) (*status)); + usb_stor_dbg(us, "0x%02X\n", *status); return rc; } @@ -425,7 +425,7 @@ static int usbat_wait_not_busy(struct us_data *us, int minutes) return USB_STOR_TRANSPORT_FAILED; if ((*status & 0x80)==0x00) { /* not busy */ - US_DEBUGP("Waited not busy for %d steps\n", i); + usb_stor_dbg(us, "Waited not busy for %d steps\n", i); return USB_STOR_TRANSPORT_GOOD; } @@ -439,8 +439,8 @@ static int usbat_wait_not_busy(struct us_data *us, int minutes) msleep(1000); /* X minutes */ } - US_DEBUGP("Waited not busy for %d minutes, timing out.\n", - minutes); + usb_stor_dbg(us, "Waited not busy for %d minutes, timing out\n", + minutes); return USB_STOR_TRANSPORT_FAILED; } @@ -657,8 +657,9 @@ static int usbat_hp8200e_rw_block_test(struct us_data *us, if (*status & 0x20) /* device fault */ return USB_STOR_TRANSPORT_FAILED; - US_DEBUGP("Redoing %s\n", - direction==DMA_TO_DEVICE ? "write" : "read"); + usb_stor_dbg(us, "Redoing %s\n", + direction == DMA_TO_DEVICE + ? "write" : "read"); } else if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -667,8 +668,8 @@ static int usbat_hp8200e_rw_block_test(struct us_data *us, } - US_DEBUGP("Bummer! %s bulk data 20 times failed.\n", - direction==DMA_TO_DEVICE ? "Writing" : "Reading"); + usb_stor_dbg(us, "Bummer! %s bulk data 20 times failed\n", + direction == DMA_TO_DEVICE ? "Writing" : "Reading"); return USB_STOR_TRANSPORT_FAILED; } @@ -827,7 +828,7 @@ static int usbat_read_user_io(struct us_data *us, unsigned char *data_flags) data_flags, USBAT_UIO_READ); - US_DEBUGP("usbat_read_user_io: UIO register reads %02X\n", (unsigned short) (*data_flags)); + usb_stor_dbg(us, "UIO register reads %02X\n", *data_flags); return result; } @@ -900,10 +901,11 @@ static int usbat_device_enable_cdt(struct us_data *us) /* * Determine if media is present. */ -static int usbat_flash_check_media_present(unsigned char *uio) +static int usbat_flash_check_media_present(struct us_data *us, + unsigned char *uio) { if (*uio & USBAT_UIO_UI0) { - US_DEBUGP("usbat_flash_check_media_present: no media detected\n"); + usb_stor_dbg(us, "no media detected\n"); return USBAT_FLASH_MEDIA_NONE; } @@ -913,10 +915,11 @@ static int usbat_flash_check_media_present(unsigned char *uio) /* * Determine if media has changed since last operation */ -static int usbat_flash_check_media_changed(unsigned char *uio) +static int usbat_flash_check_media_changed(struct us_data *us, + unsigned char *uio) { if (*uio & USBAT_UIO_0) { - US_DEBUGP("usbat_flash_check_media_changed: media change detected\n"); + usb_stor_dbg(us, "media change detected\n"); return USBAT_FLASH_MEDIA_CHANGED; } @@ -937,7 +940,7 @@ static int usbat_flash_check_media(struct us_data *us, return USB_STOR_TRANSPORT_ERROR; /* Check for media existence */ - rc = usbat_flash_check_media_present(uio); + rc = usbat_flash_check_media_present(us, uio); if (rc == USBAT_FLASH_MEDIA_NONE) { info->sense_key = 0x02; info->sense_asc = 0x3A; @@ -946,7 +949,7 @@ static int usbat_flash_check_media(struct us_data *us, } /* Check for media change */ - rc = usbat_flash_check_media_changed(uio); + rc = usbat_flash_check_media_changed(us, uio); if (rc == USBAT_FLASH_MEDIA_CHANGED) { /* Reset and re-enable card detect */ @@ -1008,11 +1011,11 @@ static int usbat_identify_device(struct us_data *us, /* Check for error bit, or if the command 'fell through' */ if (status == 0xA1 || !(status & 0x01)) { /* Device is HP 8200 */ - US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n"); + usb_stor_dbg(us, "Detected HP8200 CDRW\n"); info->devicetype = USBAT_DEV_HP8200; } else { /* Device is a CompactFlash reader/writer */ - US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n"); + usb_stor_dbg(us, "Detected Flash reader/writer\n"); info->devicetype = USBAT_DEV_FLASH; } @@ -1075,7 +1078,7 @@ static int usbat_flash_get_sector_count(struct us_data *us, /* ATA command : IDENTIFY DEVICE */ rc = usbat_multiple_write(us, registers, command, 3); if (rc != USB_STOR_XFER_GOOD) { - US_DEBUGP("usbat_flash_get_sector_count: Gah! identify_device failed\n"); + usb_stor_dbg(us, "Gah! identify_device failed\n"); rc = USB_STOR_TRANSPORT_ERROR; goto leave; } @@ -1178,7 +1181,7 @@ static int usbat_flash_read_data(struct us_data *us, if (result != USB_STOR_TRANSPORT_GOOD) goto leave; - US_DEBUGP("usbat_flash_read_data: %d bytes\n", len); + usb_stor_dbg(us, "%d bytes\n", len); /* Store the data in the transfer buffer */ usb_stor_access_xfer_buf(buffer, len, us->srb, @@ -1301,8 +1304,7 @@ static int usbat_hp8200e_handle_read10(struct us_data *us, unsigned int sg_offset = 0; struct scatterlist *sg = NULL; - US_DEBUGP("handle_read10: transfersize %d\n", - srb->transfersize); + usb_stor_dbg(us, "transfersize %d\n", srb->transfersize); if (scsi_bufflen(srb) < 0x10000) { @@ -1329,14 +1331,14 @@ static int usbat_hp8200e_handle_read10(struct us_data *us, len = short_pack(data[7+9], data[7+8]); len <<= 16; len |= data[7+7]; - US_DEBUGP("handle_read10: GPCMD_READ_CD: len %d\n", len); + usb_stor_dbg(us, "GPCMD_READ_CD: len %d\n", len); srb->transfersize = scsi_bufflen(srb)/len; } if (!srb->transfersize) { srb->transfersize = 2048; /* A guess */ - US_DEBUGP("handle_read10: transfersize 0, forcing %d\n", - srb->transfersize); + usb_stor_dbg(us, "transfersize 0, forcing %d\n", + srb->transfersize); } /* @@ -1346,7 +1348,7 @@ static int usbat_hp8200e_handle_read10(struct us_data *us, */ len = (65535/srb->transfersize) * srb->transfersize; - US_DEBUGP("Max read is %d bytes\n", len); + usb_stor_dbg(us, "Max read is %d bytes\n", len); len = min(len, scsi_bufflen(srb)); buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) /* bloody hell! */ @@ -1460,10 +1462,9 @@ static int init_usbat(struct us_data *us, int devicetype) unsigned char *status = us->iobuf; us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO); - if (!us->extra) { - US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n"); + if (!us->extra) return 1; - } + info = (struct usbat_info *) (us->extra); /* Enable peripheral control signals */ @@ -1473,7 +1474,7 @@ static int init_usbat(struct us_data *us, int devicetype) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 1\n"); + usb_stor_dbg(us, "INIT 1\n"); msleep(2000); @@ -1481,7 +1482,7 @@ static int init_usbat(struct us_data *us, int devicetype) if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 2\n"); + usb_stor_dbg(us, "INIT 2\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) @@ -1491,32 +1492,32 @@ static int init_usbat(struct us_data *us, int devicetype) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 3\n"); + usb_stor_dbg(us, "INIT 3\n"); rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 4\n"); + usb_stor_dbg(us, "INIT 4\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 5\n"); + usb_stor_dbg(us, "INIT 5\n"); /* Enable peripheral control signals and card detect */ rc = usbat_device_enable_cdt(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 6\n"); + usb_stor_dbg(us, "INIT 6\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 7\n"); + usb_stor_dbg(us, "INIT 7\n"); msleep(1400); @@ -1524,19 +1525,19 @@ static int init_usbat(struct us_data *us, int devicetype) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 8\n"); + usb_stor_dbg(us, "INIT 8\n"); rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 9\n"); + usb_stor_dbg(us, "INIT 9\n"); /* At this point, we need to detect which device we are using */ if (usbat_set_transport(us, info, devicetype)) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 10\n"); + usb_stor_dbg(us, "INIT 10\n"); if (usbat_get_device_type(us) == USBAT_DEV_FLASH) { subcountH = 0x02; @@ -1547,7 +1548,7 @@ static int init_usbat(struct us_data *us, int devicetype) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 11\n"); + usb_stor_dbg(us, "INIT 11\n"); return USB_STOR_TRANSPORT_GOOD; } @@ -1592,7 +1593,7 @@ static int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) } result = usbat_get_status(us, status); - US_DEBUGP("Status = %02X\n", *status); + usb_stor_dbg(us, "Status = %02X\n", *status); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; if (srb->cmnd[0] == TEST_UNIT_READY) @@ -1610,7 +1611,7 @@ static int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) if (result == USB_STOR_TRANSPORT_GOOD) { transferred += len; - US_DEBUGP("Wrote %08X bytes\n", transferred); + usb_stor_dbg(us, "Wrote %08X bytes\n", transferred); } return result; @@ -1623,8 +1624,8 @@ static int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) } if (len > 0xFFFF) { - US_DEBUGP("Error: len = %08X... what do I do now?\n", - len); + usb_stor_dbg(us, "Error: len = %08X... what do I do now?\n", + len); return USB_STOR_TRANSPORT_ERROR; } @@ -1693,7 +1694,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) }; if (srb->cmnd[0] == INQUIRY) { - US_DEBUGP("usbat_flash_transport: INQUIRY. Returning bogus response.\n"); + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); memcpy(ptr, inquiry_response, sizeof(inquiry_response)); fill_inquiry_response(us, ptr, 36); return USB_STOR_TRANSPORT_GOOD; @@ -1710,8 +1711,8 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) /* hard coded 512 byte sectors as per ATA spec */ info->ssize = 0x200; - US_DEBUGP("usbat_flash_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n", - info->sectors, info->ssize); + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); /* * build the reply @@ -1726,7 +1727,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) } if (srb->cmnd[0] == MODE_SELECT_10) { - US_DEBUGP("usbat_flash_transport: Gah! MODE_SELECT_10.\n"); + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -1736,7 +1737,8 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("usbat_flash_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); return usbat_flash_read_data(us, info, block, blocks); } @@ -1750,7 +1752,8 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("usbat_flash_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); return usbat_flash_read_data(us, info, block, blocks); } @@ -1760,7 +1763,8 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); - US_DEBUGP("usbat_flash_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); return usbat_flash_write_data(us, info, block, blocks); } @@ -1774,13 +1778,14 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); - US_DEBUGP("usbat_flash_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks); + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); return usbat_flash_write_data(us, info, block, blocks); } if (srb->cmnd[0] == TEST_UNIT_READY) { - US_DEBUGP("usbat_flash_transport: TEST_UNIT_READY.\n"); + usb_stor_dbg(us, "TEST_UNIT_READY\n"); rc = usbat_flash_check_media(us, info); if (rc != USB_STOR_TRANSPORT_GOOD) @@ -1790,7 +1795,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) } if (srb->cmnd[0] == REQUEST_SENSE) { - US_DEBUGP("usbat_flash_transport: REQUEST_SENSE.\n"); + usb_stor_dbg(us, "REQUEST_SENSE\n"); memset(ptr, 0, 18); ptr[0] = 0xF0; @@ -1811,8 +1816,8 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } - US_DEBUGP("usbat_flash_transport: Gah! Unknown command: %d (0x%x)\n", - srb->cmnd[0], srb->cmnd[0]); + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); info->sense_key = 0x05; info->sense_asc = 0x20; info->sense_ascq = 0x00; diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index 17e3695..2ea657b 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -47,7 +47,7 @@ static bool containsFullLinuxPackage(struct swoc_info *swocInfo) static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) { int result; - US_DEBUGP("SWIMS: %s", "DEVICE MODE SWITCH\n"); + dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n"); result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ @@ -65,7 +65,7 @@ static int sierra_get_swoc_info(struct usb_device *udev, { int result; - US_DEBUGP("SWIMS: Attempting to get TRU-Install info.\n"); + dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n"); result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ @@ -81,11 +81,11 @@ static int sierra_get_swoc_info(struct usb_device *udev, return result; } -static void debug_swoc(struct swoc_info *swocInfo) +static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo) { - US_DEBUGP("SWIMS: SWoC Rev: %02d \n", swocInfo->rev); - US_DEBUGP("SWIMS: Linux SKU: %04X \n", swocInfo->LinuxSKU); - US_DEBUGP("SWIMS: Linux Version: %04X \n", swocInfo->LinuxVer); + dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev); + dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU); + dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer); } @@ -101,18 +101,17 @@ static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, } else { swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); if (!swocInfo) { - US_DEBUGP("SWIMS: Allocation failure\n"); snprintf(buf, PAGE_SIZE, "Error\n"); return -ENOMEM; } result = sierra_get_swoc_info(udev, swocInfo); if (result < 0) { - US_DEBUGP("SWIMS: failed SWoC query\n"); + dev_dbg(dev, "SWIMS: failed SWoC query\n"); kfree(swocInfo); snprintf(buf, PAGE_SIZE, "Error\n"); return -EIO; } - debug_swoc(swocInfo); + debug_swoc(dev, swocInfo); result = snprintf(buf, PAGE_SIZE, "REV=%02d SKU=%04X VER=%04X\n", swocInfo->rev, @@ -138,61 +137,55 @@ int sierra_ms_init(struct us_data *us) sh = us_to_host(us); scsi_get_host_dev(sh); - US_DEBUGP("SWIMS: sierra_ms_init called\n"); - /* Force Modem mode */ if (swi_tru_install == TRU_FORCE_MODEM) { - US_DEBUGP("SWIMS: %s", "Forcing Modem Mode\n"); + usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n"); result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); if (result < 0) - US_DEBUGP("SWIMS: Failed to switch to modem mode.\n"); + usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n"); return -EIO; } /* Force Mass Storage mode (keep CD-Rom) */ else if (swi_tru_install == TRU_FORCE_MS) { - US_DEBUGP("SWIMS: %s", "Forcing Mass Storage Mode\n"); + usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n"); goto complete; } /* Normal TRU-Install Logic */ else { - US_DEBUGP("SWIMS: %s", "Normal SWoC Logic\n"); + usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n"); swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); - if (!swocInfo) { - US_DEBUGP("SWIMS: %s", "Allocation failure\n"); + if (!swocInfo) return -ENOMEM; - } retries = 3; do { retries--; result = sierra_get_swoc_info(udev, swocInfo); if (result < 0) { - US_DEBUGP("SWIMS: %s", "Failed SWoC query\n"); + usb_stor_dbg(us, "SWIMS: Failed SWoC query\n"); schedule_timeout_uninterruptible(2*HZ); } } while (retries && result < 0); if (result < 0) { - US_DEBUGP("SWIMS: %s", - "Completely failed SWoC query\n"); + usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n"); kfree(swocInfo); return -EIO; } - debug_swoc(swocInfo); + debug_swoc(&us->pusb_dev->dev, swocInfo); /* If there is not Linux software on the TRU-Install device * then switch to modem mode */ if (!containsFullLinuxPackage(swocInfo)) { - US_DEBUGP("SWIMS: %s", - "Switching to Modem Mode\n"); + usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n"); result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); if (result < 0) - US_DEBUGP("SWIMS: Failed to switch modem\n"); + usb_stor_dbg(us, "SWIMS: Failed to switch modem\n"); kfree(swocInfo); return -EIO; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index c0543c8..22c7d43 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -166,7 +166,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) /* cancel the URB, if it hasn't been cancelled already */ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling URB\n"); + usb_stor_dbg(us, "-- cancelling URB\n"); usb_unlink_urb(us->current_urb); } } @@ -178,8 +178,8 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags); if (timeleft <= 0) { - US_DEBUGP("%s -- cancelling URB\n", - timeleft == 0 ? "Timeout" : "Signal"); + usb_stor_dbg(us, "%s -- cancelling URB\n", + timeleft == 0 ? "Timeout" : "Signal"); usb_kill_urb(us->current_urb); } @@ -197,9 +197,8 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, { int status; - US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", - __func__, request, requesttype, - value, index, size); + usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", + request, requesttype, value, index, size); /* fill in the devrequest structure */ us->cr->bRequestType = requesttype; @@ -249,7 +248,7 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) if (result >= 0) usb_reset_endpoint(us->pusb_dev, endp); - US_DEBUGP("%s: result = %d\n", __func__, result); + usb_stor_dbg(us, "result = %d\n", result); return result; } EXPORT_SYMBOL_GPL(usb_stor_clear_halt); @@ -265,18 +264,18 @@ EXPORT_SYMBOL_GPL(usb_stor_clear_halt); static int interpret_urb_result(struct us_data *us, unsigned int pipe, unsigned int length, int result, unsigned int partial) { - US_DEBUGP("Status code %d; transferred %u/%u\n", - result, partial, length); + usb_stor_dbg(us, "Status code %d; transferred %u/%u\n", + result, partial, length); switch (result) { /* no error code; did we send all the data? */ case 0: if (partial != length) { - US_DEBUGP("-- short transfer\n"); + usb_stor_dbg(us, "-- short transfer\n"); return USB_STOR_XFER_SHORT; } - US_DEBUGP("-- transfer complete\n"); + usb_stor_dbg(us, "-- transfer complete\n"); return USB_STOR_XFER_GOOD; /* stalled */ @@ -284,39 +283,40 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe, /* for control endpoints, (used by CB[I]) a stall indicates * a failed command */ if (usb_pipecontrol(pipe)) { - US_DEBUGP("-- stall on control pipe\n"); + usb_stor_dbg(us, "-- stall on control pipe\n"); return USB_STOR_XFER_STALLED; } /* for other sorts of endpoint, clear the stall */ - US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_stor_dbg(us, "clearing endpoint halt for pipe 0x%x\n", + pipe); if (usb_stor_clear_halt(us, pipe) < 0) return USB_STOR_XFER_ERROR; return USB_STOR_XFER_STALLED; /* babble - the device tried to send more than we wanted to read */ case -EOVERFLOW: - US_DEBUGP("-- babble\n"); + usb_stor_dbg(us, "-- babble\n"); return USB_STOR_XFER_LONG; /* the transfer was cancelled by abort, disconnect, or timeout */ case -ECONNRESET: - US_DEBUGP("-- transfer cancelled\n"); + usb_stor_dbg(us, "-- transfer cancelled\n"); return USB_STOR_XFER_ERROR; /* short scatter-gather read transfer */ case -EREMOTEIO: - US_DEBUGP("-- short read transfer\n"); + usb_stor_dbg(us, "-- short read transfer\n"); return USB_STOR_XFER_SHORT; /* abort or disconnect in progress */ case -EIO: - US_DEBUGP("-- abort or disconnect in progress\n"); + usb_stor_dbg(us, "-- abort or disconnect in progress\n"); return USB_STOR_XFER_ERROR; /* the catch-all error case */ default: - US_DEBUGP("-- unknown error\n"); + usb_stor_dbg(us, "-- unknown error\n"); return USB_STOR_XFER_ERROR; } } @@ -331,9 +331,8 @@ int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe, { int result; - US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", - __func__, request, requesttype, - value, index, size); + usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", + request, requesttype, value, index, size); /* fill in the devrequest structure */ us->cr->bRequestType = requesttype; @@ -367,7 +366,7 @@ static int usb_stor_intr_transfer(struct us_data *us, void *buf, unsigned int pipe = us->recv_intr_pipe; unsigned int maxp; - US_DEBUGP("%s: xfer %u bytes\n", __func__, length); + usb_stor_dbg(us, "xfer %u bytes\n", length); /* calculate the max packet size */ maxp = usb_maxpacket(us->pusb_dev, pipe, usb_pipeout(pipe)); @@ -394,7 +393,7 @@ int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, { int result; - US_DEBUGP("%s: xfer %u bytes\n", __func__, length); + usb_stor_dbg(us, "xfer %u bytes\n", length); /* fill and submit the URB */ usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length, @@ -426,12 +425,11 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, return USB_STOR_XFER_ERROR; /* initialize the scatter-gather request block */ - US_DEBUGP("%s: xfer %u bytes, %d entries\n", __func__, - length, num_sg); + usb_stor_dbg(us, "xfer %u bytes, %d entries\n", length, num_sg); result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0, sg, num_sg, length, GFP_NOIO); if (result) { - US_DEBUGP("usb_sg_init returned %d\n", result); + usb_stor_dbg(us, "usb_sg_init returned %d\n", result); return USB_STOR_XFER_ERROR; } @@ -444,7 +442,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, /* cancel the request, if it hasn't been cancelled already */ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling sg request\n"); + usb_stor_dbg(us, "-- cancelling sg request\n"); usb_sg_cancel(&us->current_sg); } } @@ -609,14 +607,14 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) * short-circuit all other processing */ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { - US_DEBUGP("-- command was aborted\n"); + usb_stor_dbg(us, "-- command was aborted\n"); srb->result = DID_ABORT << 16; goto Handle_Errors; } /* if there is a transport error, reset and don't auto-sense */ if (result == USB_STOR_TRANSPORT_ERROR) { - US_DEBUGP("-- transport indicates error, resetting\n"); + usb_stor_dbg(us, "-- transport indicates error, resetting\n"); srb->result = DID_ERROR << 16; goto Handle_Errors; } @@ -645,7 +643,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) */ if ((us->protocol == USB_PR_CB || us->protocol == USB_PR_DPCM_USB) && srb->sc_data_direction != DMA_FROM_DEVICE) { - US_DEBUGP("-- CB transport device requiring auto-sense\n"); + usb_stor_dbg(us, "-- CB transport device requiring auto-sense\n"); need_auto_sense = 1; } @@ -655,7 +653,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) * "failure" and an "error" in the transport mechanism. */ if (result == USB_STOR_TRANSPORT_FAILED) { - US_DEBUGP("-- transport indicates command failure\n"); + usb_stor_dbg(us, "-- transport indicates command failure\n"); need_auto_sense = 1; } @@ -670,7 +668,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) !(us->fflags & US_FL_SANE_SENSE) && !(us->fflags & US_FL_BAD_SENSE) && !(srb->cmnd[2] & 0x20))) { - US_DEBUGP("-- SAT supported, increasing auto-sense\n"); + usb_stor_dbg(us, "-- SAT supported, increasing auto-sense\n"); us->fflags |= US_FL_SANE_SENSE; } @@ -684,7 +682,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) (srb->cmnd[0] == MODE_SENSE) || (srb->cmnd[0] == LOG_SENSE) || (srb->cmnd[0] == MODE_SENSE_10))) { - US_DEBUGP("-- unexpectedly short transfer\n"); + usb_stor_dbg(us, "-- unexpectedly short transfer\n"); } /* Now, if we need to do the auto-sense, let's do it */ @@ -700,7 +698,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) if (us->fflags & US_FL_SANE_SENSE) sense_size = ~0; Retry_Sense: - US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); + usb_stor_dbg(us, "Issuing auto-REQUEST_SENSE\n"); scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size); @@ -719,7 +717,7 @@ Retry_Sense: scsi_eh_restore_cmnd(srb, &ses); if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { - US_DEBUGP("-- auto-sense aborted\n"); + usb_stor_dbg(us, "-- auto-sense aborted\n"); srb->result = DID_ABORT << 16; /* If SANE_SENSE caused this problem, disable it */ @@ -737,7 +735,7 @@ Retry_Sense: */ if (temp_result == USB_STOR_TRANSPORT_FAILED && sense_size != US_SENSE_SIZE) { - US_DEBUGP("-- auto-sense failure, retry small sense\n"); + usb_stor_dbg(us, "-- auto-sense failure, retry small sense\n"); sense_size = US_SENSE_SIZE; us->fflags &= ~US_FL_SANE_SENSE; us->fflags |= US_FL_BAD_SENSE; @@ -746,7 +744,7 @@ Retry_Sense: /* Other failures */ if (temp_result != USB_STOR_TRANSPORT_GOOD) { - US_DEBUGP("-- auto-sense failure\n"); + usb_stor_dbg(us, "-- auto-sense failure\n"); /* we skip the reset if this happens to be a * multi-target device, since failure of an @@ -766,27 +764,28 @@ Retry_Sense: !(us->fflags & US_FL_SANE_SENSE) && !(us->fflags & US_FL_BAD_SENSE) && (srb->sense_buffer[0] & 0x7C) == 0x70) { - US_DEBUGP("-- SANE_SENSE support enabled\n"); + usb_stor_dbg(us, "-- SANE_SENSE support enabled\n"); us->fflags |= US_FL_SANE_SENSE; /* Indicate to the user that we truncated their sense * because we didn't know it supported larger sense. */ - US_DEBUGP("-- Sense data truncated to %i from %i\n", - US_SENSE_SIZE, - srb->sense_buffer[7] + 8); + usb_stor_dbg(us, "-- Sense data truncated to %i from %i\n", + US_SENSE_SIZE, + srb->sense_buffer[7] + 8); srb->sense_buffer[7] = (US_SENSE_SIZE - 8); } scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE, &sshdr); - US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); - US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", - sshdr.response_code, sshdr.sense_key, - sshdr.asc, sshdr.ascq); + usb_stor_dbg(us, "-- Result from auto-sense is %d\n", + temp_result); + usb_stor_dbg(us, "-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", + sshdr.response_code, sshdr.sense_key, + sshdr.asc, sshdr.ascq); #ifdef CONFIG_USB_STORAGE_DEBUG - usb_stor_show_sense(sshdr.sense_key, sshdr.asc, sshdr.ascq); + usb_stor_show_sense(us, sshdr.sense_key, sshdr.asc, sshdr.ascq); #endif /* set the result so the higher layers expect this data */ @@ -892,20 +891,18 @@ Retry_Sense: /* Stop the current URB transfer */ void usb_stor_stop_transport(struct us_data *us) { - US_DEBUGP("%s called\n", __func__); - /* If the state machine is blocked waiting for an URB, * let's wake it up. The test_and_clear_bit() call * guarantees that if a URB has just been submitted, * it won't be cancelled more than once. */ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling URB\n"); + usb_stor_dbg(us, "-- cancelling URB\n"); usb_unlink_urb(us->current_urb); } /* If we are waiting for a scatter-gather operation, cancel it. */ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling sg request\n"); + usb_stor_dbg(us, "-- cancelling sg request\n"); usb_sg_cancel(&us->current_sg); } } @@ -928,7 +925,8 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ - US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result); + usb_stor_dbg(us, "Call to usb_stor_ctrl_transfer() returned %d\n", + result); /* if we stalled the command, it means command failed */ if (result == USB_STOR_XFER_STALLED) { @@ -946,7 +944,7 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? us->recv_bulk_pipe : us->send_bulk_pipe; result = usb_stor_bulk_srb(us, pipe, srb); - US_DEBUGP("CBI data stage result is 0x%x\n", result); + usb_stor_dbg(us, "CBI data stage result is 0x%x\n", result); /* if we stalled the data transfer it means command failed */ if (result == USB_STOR_XFER_STALLED) @@ -964,8 +962,8 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; result = usb_stor_intr_transfer(us, us->iobuf, 2); - US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", - us->iobuf[0], us->iobuf[1]); + usb_stor_dbg(us, "Got interrupt data (0x%x, 0x%x)\n", + us->iobuf[0], us->iobuf[1]); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -992,8 +990,8 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) * into the first byte -- so if it's non-zero, call it a failure. */ if (us->iobuf[0]) { - US_DEBUGP("CBI IRQ data showed reserved bType 0x%x\n", - us->iobuf[0]); + usb_stor_dbg(us, "CBI IRQ data showed reserved bType 0x%x\n", + us->iobuf[0]); goto Failed; } @@ -1034,8 +1032,8 @@ int usb_stor_Bulk_max_lun(struct us_data *us) USB_RECIP_INTERFACE, 0, us->ifnum, us->iobuf, 1, 10*HZ); - US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", - result, us->iobuf[0]); + usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n", + result, us->iobuf[0]); /* if we have a successful request, return the result */ if (result > 0) @@ -1084,14 +1082,14 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) memcpy(bcb->CDB, srb->cmnd, bcb->Length); /* send it to out endpoint */ - US_DEBUGP("Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n", - le32_to_cpu(bcb->Signature), bcb->Tag, - le32_to_cpu(bcb->DataTransferLength), bcb->Flags, - (bcb->Lun >> 4), (bcb->Lun & 0x0F), - bcb->Length); + usb_stor_dbg(us, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n", + le32_to_cpu(bcb->Signature), bcb->Tag, + le32_to_cpu(bcb->DataTransferLength), bcb->Flags, + (bcb->Lun >> 4), (bcb->Lun & 0x0F), + bcb->Length); result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb, cbwlen, NULL); - US_DEBUGP("Bulk command transfer result=%d\n", result); + usb_stor_dbg(us, "Bulk command transfer result=%d\n", result); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -1108,7 +1106,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? us->recv_bulk_pipe : us->send_bulk_pipe; result = usb_stor_bulk_srb(us, pipe, srb); - US_DEBUGP("Bulk data transfer result 0x%x\n", result); + usb_stor_dbg(us, "Bulk data transfer result 0x%x\n", result); if (result == USB_STOR_XFER_ERROR) return USB_STOR_TRANSPORT_ERROR; @@ -1127,7 +1125,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) */ /* get CSW for device status */ - US_DEBUGP("Attempting to get CSW...\n"); + usb_stor_dbg(us, "Attempting to get CSW...\n"); result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, &cswlen); @@ -1136,7 +1134,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) * CSWs. If we encounter such a thing, try to read the CSW again. */ if (result == USB_STOR_XFER_SHORT && cswlen == 0) { - US_DEBUGP("Received 0-length CSW; retrying...\n"); + usb_stor_dbg(us, "Received 0-length CSW; retrying...\n"); result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, &cswlen); } @@ -1145,24 +1143,24 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) if (result == USB_STOR_XFER_STALLED) { /* get the status again */ - US_DEBUGP("Attempting to get CSW (2nd try)...\n"); + usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n"); result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, US_BULK_CS_WRAP_LEN, NULL); } /* if we still have a failure at this point, we're in trouble */ - US_DEBUGP("Bulk status result = %d\n", result); + usb_stor_dbg(us, "Bulk status result = %d\n", result); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; /* check bulk status */ residue = le32_to_cpu(bcs->Residue); - US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", - le32_to_cpu(bcs->Signature), bcs->Tag, - residue, bcs->Status); + usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", + le32_to_cpu(bcs->Signature), bcs->Tag, + residue, bcs->Status); if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) || bcs->Status > US_BULK_STAT_PHASE) { - US_DEBUGP("Bulk logical error\n"); + usb_stor_dbg(us, "Bulk logical error\n"); return USB_STOR_TRANSPORT_ERROR; } @@ -1173,12 +1171,12 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) if (!us->bcs_signature) { us->bcs_signature = bcs->Signature; if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN)) - US_DEBUGP("Learnt BCS signature 0x%08X\n", - le32_to_cpu(us->bcs_signature)); + usb_stor_dbg(us, "Learnt BCS signature 0x%08X\n", + le32_to_cpu(us->bcs_signature)); } else if (bcs->Signature != us->bcs_signature) { - US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n", - le32_to_cpu(bcs->Signature), - le32_to_cpu(us->bcs_signature)); + usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n", + le32_to_cpu(bcs->Signature), + le32_to_cpu(us->bcs_signature)); return USB_STOR_TRANSPORT_ERROR; } @@ -1255,7 +1253,7 @@ static int usb_stor_reset_common(struct us_data *us, int result2; if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { - US_DEBUGP("No reset during disconnect\n"); + usb_stor_dbg(us, "No reset during disconnect\n"); return -EIO; } @@ -1263,7 +1261,7 @@ static int usb_stor_reset_common(struct us_data *us, request, requesttype, value, index, data, size, 5*HZ); if (result < 0) { - US_DEBUGP("Soft reset failed: %d\n", result); + usb_stor_dbg(us, "Soft reset failed: %d\n", result); return result; } @@ -1273,23 +1271,23 @@ static int usb_stor_reset_common(struct us_data *us, test_bit(US_FLIDX_DISCONNECTING, &us->dflags), HZ*6); if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { - US_DEBUGP("Reset interrupted by disconnect\n"); + usb_stor_dbg(us, "Reset interrupted by disconnect\n"); return -EIO; } - US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); + usb_stor_dbg(us, "Soft reset: clearing bulk-in endpoint halt\n"); result = usb_stor_clear_halt(us, us->recv_bulk_pipe); - US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n"); + usb_stor_dbg(us, "Soft reset: clearing bulk-out endpoint halt\n"); result2 = usb_stor_clear_halt(us, us->send_bulk_pipe); /* return a result code based on the result of the clear-halts */ if (result >= 0) result = result2; if (result < 0) - US_DEBUGP("Soft reset failed\n"); + usb_stor_dbg(us, "Soft reset failed\n"); else - US_DEBUGP("Soft reset done\n"); + usb_stor_dbg(us, "Soft reset done\n"); return result; } @@ -1299,8 +1297,6 @@ static int usb_stor_reset_common(struct us_data *us, int usb_stor_CB_reset(struct us_data *us) { - US_DEBUGP("%s called\n", __func__); - memset(us->iobuf, 0xFF, CB_RESET_CMD_SIZE); us->iobuf[0] = SEND_DIAGNOSTIC; us->iobuf[1] = 4; @@ -1315,8 +1311,6 @@ EXPORT_SYMBOL_GPL(usb_stor_CB_reset); */ int usb_stor_Bulk_reset(struct us_data *us) { - US_DEBUGP("%s called\n", __func__); - return usb_stor_reset_common(us, US_BULK_RESET_REQUEST, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, NULL, 0); @@ -1336,16 +1330,17 @@ int usb_stor_port_reset(struct us_data *us) result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); if (result < 0) - US_DEBUGP("unable to lock device for reset: %d\n", result); + usb_stor_dbg(us, "unable to lock device for reset: %d\n", + result); else { /* Were we disconnected while waiting for the lock? */ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { result = -EIO; - US_DEBUGP("No reset during disconnect\n"); + usb_stor_dbg(us, "No reset during disconnect\n"); } else { result = usb_reset_device(us->pusb_dev); - US_DEBUGP("usb_reset_device returns %d\n", - result); + usb_stor_dbg(us, "usb_reset_device returns %d\n", + result); } usb_unlock_device(us->pusb_dev); } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index d6bee40..b4c63fc 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -183,7 +183,6 @@ int usb_stor_suspend(struct usb_interface *iface, pm_message_t message) /* Wait until no command is running */ mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __func__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_SUSPEND); @@ -201,7 +200,6 @@ int usb_stor_resume(struct usb_interface *iface) mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __func__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); @@ -214,8 +212,6 @@ int usb_stor_reset_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - US_DEBUGP("%s\n", __func__); - /* Report the reset to the SCSI core */ usb_stor_report_bus_reset(us); @@ -236,8 +232,6 @@ int usb_stor_pre_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - US_DEBUGP("%s\n", __func__); - /* Make sure no command runs during the reset */ mutex_lock(&us->dev_mutex); return 0; @@ -248,8 +242,6 @@ int usb_stor_post_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - US_DEBUGP("%s\n", __func__); - /* Report the reset to the SCSI core */ usb_stor_report_bus_reset(us); @@ -311,11 +303,11 @@ static int usb_stor_control_thread(void * __us) struct Scsi_Host *host = us_to_host(us); for (;;) { - US_DEBUGP("*** thread sleeping.\n"); + usb_stor_dbg(us, "*** thread sleeping\n"); if (wait_for_completion_interruptible(&us->cmnd_ready)) break; - US_DEBUGP("*** thread awakened.\n"); + usb_stor_dbg(us, "*** thread awakened\n"); /* lock the device pointers */ mutex_lock(&(us->dev_mutex)); @@ -327,7 +319,7 @@ static int usb_stor_control_thread(void * __us) if (us->srb == NULL) { scsi_unlock(host); mutex_unlock(&us->dev_mutex); - US_DEBUGP("-- exiting\n"); + usb_stor_dbg(us, "-- exiting\n"); break; } @@ -343,7 +335,7 @@ static int usb_stor_control_thread(void * __us) * is UNKNOWN */ if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { - US_DEBUGP("UNKNOWN data direction\n"); + usb_stor_dbg(us, "UNKNOWN data direction\n"); us->srb->result = DID_ERROR << 16; } @@ -352,14 +344,14 @@ static int usb_stor_control_thread(void * __us) */ else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) { - US_DEBUGP("Bad target number (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad target number (%d:%d)\n", + us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } else if (us->srb->device->lun > us->max_lun) { - US_DEBUGP("Bad LUN (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad LUN (%d:%d)\n", + us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } @@ -371,14 +363,14 @@ static int usb_stor_control_thread(void * __us) 0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00, 0x00}; - US_DEBUGP("Faking INQUIRY command\n"); + usb_stor_dbg(us, "Faking INQUIRY command\n"); fill_inquiry_response(us, data_ptr, 36); us->srb->result = SAM_STAT_GOOD; } /* we've got a command, let's do it! */ else { - US_DEBUG(usb_stor_show_command(us->srb)); + US_DEBUG(usb_stor_show_command(us, us->srb)); us->proto_handler(us->srb, us); usb_mark_last_busy(us->pusb_dev); } @@ -388,12 +380,12 @@ static int usb_stor_control_thread(void * __us) /* indicate that the command is done */ if (us->srb->result != DID_ABORT << 16) { - US_DEBUGP("scsi cmd done, result=0x%x\n", - us->srb->result); + usb_stor_dbg(us, "scsi cmd done, result=0x%x\n", + us->srb->result); us->srb->scsi_done(us->srb); } else { SkipForAbort: - US_DEBUGP("scsi command aborted\n"); + usb_stor_dbg(us, "scsi command aborted\n"); } /* If an abort request was received we need to signal that @@ -435,34 +427,30 @@ SkipForAbort: /* Associate our private data with the USB device */ static int associate_dev(struct us_data *us, struct usb_interface *intf) { - US_DEBUGP("-- %s\n", __func__); - /* Fill in the device-related fields */ us->pusb_dev = interface_to_usbdev(intf); us->pusb_intf = intf; us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n", - le16_to_cpu(us->pusb_dev->descriptor.idVendor), - le16_to_cpu(us->pusb_dev->descriptor.idProduct), - le16_to_cpu(us->pusb_dev->descriptor.bcdDevice)); - US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n", - intf->cur_altsetting->desc.bInterfaceSubClass, - intf->cur_altsetting->desc.bInterfaceProtocol); + usb_stor_dbg(us, "Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n", + le16_to_cpu(us->pusb_dev->descriptor.idVendor), + le16_to_cpu(us->pusb_dev->descriptor.idProduct), + le16_to_cpu(us->pusb_dev->descriptor.bcdDevice)); + usb_stor_dbg(us, "Interface Subclass: 0x%02x, Protocol: 0x%02x\n", + intf->cur_altsetting->desc.bInterfaceSubClass, + intf->cur_altsetting->desc.bInterfaceProtocol); /* Store our private data in the interface */ usb_set_intfdata(intf, us); /* Allocate the control/setup and DMA-mapped buffers */ us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL); - if (!us->cr) { - US_DEBUGP("usb_ctrlrequest allocation failed\n"); + if (!us->cr) return -ENOMEM; - } us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE, GFP_KERNEL, &us->iobuf_dma); if (!us->iobuf) { - US_DEBUGP("I/O buffer allocation failed\n"); + usb_stor_dbg(us, "I/O buffer allocation failed\n"); return -ENOMEM; } return 0; @@ -738,7 +726,7 @@ static int get_pipes(struct us_data *us) } if (!ep_in || !ep_out || (us->protocol == USB_PR_CBI && !ep_int)) { - US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n"); + usb_stor_dbg(us, "Endpoint sanity check failed! Rejecting dev.\n"); return -EIO; } @@ -765,7 +753,7 @@ static int usb_stor_acquire_resources(struct us_data *us) us->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!us->current_urb) { - US_DEBUGP("URB allocation failed\n"); + usb_stor_dbg(us, "URB allocation failed\n"); return -ENOMEM; } @@ -792,20 +780,18 @@ static int usb_stor_acquire_resources(struct us_data *us) /* Release all our dynamic resources */ static void usb_stor_release_resources(struct us_data *us) { - US_DEBUGP("-- %s\n", __func__); - /* Tell the control thread to exit. The SCSI host must * already have been removed and the DISCONNECTING flag set * so that we won't accept any more commands. */ - US_DEBUGP("-- sending exit command to thread\n"); + usb_stor_dbg(us, "-- sending exit command to thread\n"); complete(&us->cmnd_ready); if (us->ctl_thread) kthread_stop(us->ctl_thread); /* Call the destructor routine, if it exists */ if (us->extra_destructor) { - US_DEBUGP("-- calling extra_destructor()\n"); + usb_stor_dbg(us, "-- calling extra_destructor()\n"); us->extra_destructor(us->extra); } @@ -817,8 +803,6 @@ static void usb_stor_release_resources(struct us_data *us) /* Dissociate from the USB device */ static void dissociate_dev(struct us_data *us) { - US_DEBUGP("-- %s\n", __func__); - /* Free the buffers */ kfree(us->cr); usb_free_coherent(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, us->iobuf_dma); @@ -918,7 +902,7 @@ int usb_stor_probe1(struct us_data **pus, struct us_data *us; int result; - US_DEBUGP("USB Mass Storage device detected\n"); + dev_info(&intf->dev, "USB Mass Storage device detected\n"); /* * Ask the SCSI layer to allocate a host structure, with extra @@ -926,8 +910,7 @@ int usb_stor_probe1(struct us_data **pus, */ host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); if (!host) { - dev_warn(&intf->dev, - "Unable to allocate the scsi host\n"); + dev_warn(&intf->dev, "Unable to allocate the scsi host\n"); return -ENOMEM; } @@ -964,7 +947,7 @@ int usb_stor_probe1(struct us_data **pus, return 0; BadDevice: - US_DEBUGP("storage_probe() failed\n"); + usb_stor_dbg(us, "storage_probe() failed\n"); release_everything(us); return result; } @@ -981,8 +964,8 @@ int usb_stor_probe2(struct us_data *us) result = -ENXIO; goto BadDevice; } - US_DEBUGP("Transport: %s\n", us->transport_name); - US_DEBUGP("Protocol: %s\n", us->protocol_name); + usb_stor_dbg(us, "Transport: %s\n", us->transport_name); + usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name); /* fix for single-lun devices */ if (us->fflags & US_FL_SINGLE_LUN) @@ -1028,7 +1011,7 @@ int usb_stor_probe2(struct us_data *us) /* We come here if there are any problems */ BadDevice: - US_DEBUGP("storage_probe() failed\n"); + usb_stor_dbg(us, "storage_probe() failed\n"); release_everything(us); return result; } @@ -1039,7 +1022,6 @@ void usb_stor_disconnect(struct usb_interface *intf) { struct us_data *us = usb_get_intfdata(intf); - US_DEBUGP("storage_disconnect() called\n"); quiesce_and_remove_host(us); release_everything(us); } @@ -1075,8 +1057,7 @@ static int storage_probe(struct usb_interface *intf, } else { unusual_dev = &for_dynamic_ids; - US_DEBUGP("%s %s 0x%04x 0x%04x\n", "Use Bulk-Only transport", - "with the Transparent SCSI protocol for dynamic id:", + dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n", id->idVendor, id->idProduct); } @@ -1117,20 +1098,18 @@ static int __init usb_stor_init(void) /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); if (retval == 0) - pr_info("USB Mass Storage support registered.\n"); + pr_info("USB Mass Storage support registered\n"); return retval; } static void __exit usb_stor_exit(void) { - US_DEBUGP("usb_stor_exit() called\n"); - /* Deregister the driver * This will cause disconnect() to be called for each * attached unit */ - US_DEBUGP("-- calling usb_deregister()\n"); - usb_deregister(&usb_storage_driver) ; + pr_info("Deregistering USB Mass Storage driver...\n"); + usb_deregister(&usb_storage_driver); } module_init(usb_stor_init); -- cgit v0.10.2 From 4601de807d7755aabd35faf5e15ae233241b8582 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Apr 2013 11:50:10 -0700 Subject: USB: storage: convert to use module_usb_driver() Now that Joe cleaned up the init/exit functions, we can just get rid of them entirely and use the proper macro that almost all other USB drivers now use. Cc: Matthew Dharm Cc: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index b4c63fc..5c4fe07 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1071,10 +1071,6 @@ static int storage_probe(struct usb_interface *intf, return result; } -/*********************************************************************** - * Initialization and registration - ***********************************************************************/ - static struct usb_driver usb_storage_driver = { .name = "usb-storage", .probe = storage_probe, @@ -1089,28 +1085,4 @@ static struct usb_driver usb_storage_driver = { .soft_unbind = 1, }; -static int __init usb_stor_init(void) -{ - int retval; - - pr_info("Initializing USB Mass Storage driver...\n"); - - /* register the driver, return usb_register return code if error */ - retval = usb_register(&usb_storage_driver); - if (retval == 0) - pr_info("USB Mass Storage support registered\n"); - return retval; -} - -static void __exit usb_stor_exit(void) -{ - /* Deregister the driver - * This will cause disconnect() to be called for each - * attached unit - */ - pr_info("Deregistering USB Mass Storage driver...\n"); - usb_deregister(&usb_storage_driver); -} - -module_init(usb_stor_init); -module_exit(usb_stor_exit); +module_usb_driver(usb_storage_driver); -- cgit v0.10.2 From 6ee3e8e63d77bb4ed686f0c92dc7aa1a8e13fefb Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 6 Apr 2013 12:39:49 +0800 Subject: usb: gadget: f_obex: fix error return code in obex_bind() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 29a348a..8aa2be5 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -348,6 +348,7 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific endpoints */ + status = -ENODEV; ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); if (!ep) goto fail; -- cgit v0.10.2 From ad46047bd1988d61bc53ea354b77ef40f8a674a5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 6 Apr 2013 17:23:22 +0800 Subject: usb: gadget: multi: fix error return code in rndis_do_config() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Introduced by commit 59835a (usb: gadget: multi: use function framework for ACM.) Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index a74ebef..4a45e80 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -157,8 +157,10 @@ static __init int rndis_do_config(struct usb_configuration *c) return ret; f_acm_rndis = usb_get_function(fi_acm); - if (IS_ERR(f_acm_rndis)) + if (IS_ERR(f_acm_rndis)) { + ret = PTR_ERR(f_acm_rndis); goto err_func_acm; + } ret = usb_add_function(c, f_acm_rndis); if (ret) -- cgit v0.10.2 From 8d6f51bda67bb68b33ed4a48c57b0904a3395301 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 6 Apr 2013 17:25:21 +0800 Subject: usb: gadget: cdc2: fix error return code in cdc_do_config() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Introduced by commit 29a664 (usb: gadget: cdc2: use function framework for ACM) Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index c6ee6f1..2c52551 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -129,8 +129,10 @@ static int __init cdc_do_config(struct usb_configuration *c) return PTR_ERR(fi_serial); f_acm = usb_get_function(fi_serial); - if (IS_ERR(f_acm)) + if (IS_ERR(f_acm)) { + status = PTR_ERR(f_acm); goto err_func_acm; + } status = usb_add_function(c, f_acm); if (status) -- cgit v0.10.2 From 9890e33013fae0d67cd385d2038fcab4e52a6632 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 18 Apr 2013 14:43:25 +0200 Subject: usb: gadget: f_sourcesink.c: correct a copy-paste misnomer acm was the first function to be converted and it seems that its code served as a base for converting f_sourcesink to the new function interface. source_sink has nothing to do with acm, though. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 41adf3e..a889585 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -898,7 +898,7 @@ static struct usb_function *source_sink_alloc_func( return &ss->function; } -static void acm_free_instance(struct usb_function_instance *fi) +static void source_sink_free_instance(struct usb_function_instance *fi) { struct f_ss_opts *ss_opts; @@ -913,7 +913,7 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL); if (!ss_opts) return ERR_PTR(-ENOMEM); - ss_opts->func_inst.free_func_inst = acm_free_instance; + ss_opts->func_inst.free_func_inst = source_sink_free_instance; return &ss_opts->func_inst; } DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst, -- cgit v0.10.2 From abe7a4097dd0141d21d02e4bd949b377a0a75120 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 18 Apr 2013 14:43:26 +0200 Subject: usb: gadget: zero: put function instances on unbind If function instances are not put on gadget's unbind, their implementation module's refcount is nonzero and it is impossible to unload them. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 685fa68..2cd6262 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -368,8 +368,10 @@ static int zero_unbind(struct usb_composite_dev *cdev) del_timer_sync(&autoresume_timer); if (!IS_ERR_OR_NULL(func_ss)) usb_put_function(func_ss); + usb_put_function_instance(func_inst_ss); if (!IS_ERR_OR_NULL(func_lb)) usb_put_function(func_lb); + usb_put_function_instance(func_inst_lb); return 0; } -- cgit v0.10.2 From 19d8ceddda8b3a806a0960106ae6aa4dcc21df3b Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Thu, 18 Apr 2013 17:13:31 +0400 Subject: usb: phy: remove exported function from __init section The symbol usb_bind_phy is exported and annotated __init. It looks like section mismatch. Fix by removing the __init annotation of usb_bind_phy. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Denis Efremov Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index f52c006..a9984c7 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -413,7 +413,7 @@ EXPORT_SYMBOL_GPL(usb_remove_phy); * * To be used by platform specific initialization code. */ -int __init usb_bind_phy(const char *dev_name, u8 index, +int usb_bind_phy(const char *dev_name, u8 index, const char *phy_dev_name) { struct usb_phy_bind *phy_bind; -- cgit v0.10.2 From a52f31e95e502c53b0e5ab519c9527e9f9995288 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 24 Mar 2013 22:06:04 +0800 Subject: ARM: imx_v6_v7_defconfig: add CONFIG_USB_PHY Commit edc7cb2 (usb: phy: make it a menuconfig) makes USB_MXS_PHY be a sub-item of menuconfig symbol USB_PHY. This change gets the selection of CONFIG_USB_MXS_PHY in imx_v6_v7_defconfig lost. Hence the boot stops at the point below. ci_hdrc ci_hdrc.0: doesn't support gadget ci_hdrc ci_hdrc.0: EHCI Host Controller ci_hdrc ci_hdrc.0: new USB bus registered, assigned bus number 1 Add CONFIG_USB_PHY to have the CONFIG_USB_MXS_PHY selection back to work. Signed-off-by: Shawn Guo Signed-off-by: Felipe Balbi diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index e36b010..088d6c1 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -188,6 +188,7 @@ CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_MXC=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y CONFIG_USB_MXS_PHY=y CONFIG_USB_STORAGE=y CONFIG_MMC=y -- cgit v0.10.2 From added5fce61e97087a4a25270694c542c5ed1ba9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 24 Mar 2013 22:06:05 +0800 Subject: ARM: mxs_defconfig: add CONFIG_USB_PHY Commit edc7cb2 (usb: phy: make it a menuconfig) makes USB_MXS_PHY be a sub-item of menuconfig symbol USB_PHY. This change gets the selection of CONFIG_USB_MXS_PHY in mxs_defconfig lost. Hence the boot stops at the point below. [ 1.600867] ci_hdrc ci_hdrc.0: doesn't support gadget [ 1.606282] ci_hdrc ci_hdrc.0: EHCI Host Controller [ 1.613522] ci_hdrc ci_hdrc.0: new USB bus registered, assigned bus number 1 Add CONFIG_USB_PHY to have the CONFIG_USB_MXS_PHY selection back to work. Signed-off-by: Shawn Guo Signed-off-by: Felipe Balbi diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig index 6a99e30..87924d6 100644 --- a/arch/arm/configs/mxs_defconfig +++ b/arch/arm/configs/mxs_defconfig @@ -120,6 +120,7 @@ CONFIG_USB_EHCI_HCD=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_STORAGE=y +CONFIG_USB_PHY=y CONFIG_USB_MXS_PHY=y CONFIG_MMC=y CONFIG_MMC_MXS=y -- cgit v0.10.2 From 9f06d15f8db6946e41f73196a122b84a37938878 Mon Sep 17 00:00:00 2001 From: Adrian Thomasset Date: Tue, 23 Apr 2013 12:46:29 +0100 Subject: USB: ftdi_sio: correct ST Micro Connect Lite PIDs The current ST Micro Connect Lite uses the FT4232H hi-speed quad USB UART FTDI chip. It is also possible to drive STM reference targets populated with an on-board JTAG debugger based on the FT2232H chip with the same STMicroelectronics tools. For this reason, the ST Micro Connect Lite PIDs should be ST_STMCLT_2232_PID: 0x3746 ST_STMCLT_4232_PID: 0x3747 Signed-off-by: Adrian Thomasset Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fc7bd14..e4d7fbd 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -869,7 +869,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ST_VID, ST_STMCLT1030_PID), + { USB_DEVICE(ST_VID, ST_STMCLT_2232_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(ST_VID, ST_STMCLT_4232_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 3c00351..9852827 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1151,7 +1151,8 @@ * STMicroelectonics */ #define ST_VID 0x0483 -#define ST_STMCLT1030_PID 0x3747 /* ST Micro Connect Lite STMCLT1030 */ +#define ST_STMCLT_2232_PID 0x3746 +#define ST_STMCLT_4232_PID 0x3747 /* * Papouch products (http://www.papouch.com/) -- cgit v0.10.2 From d19bf5cedfd7d53854a3bd699c98b467b139833b Mon Sep 17 00:00:00 2001 From: Filippo Turato Date: Sat, 20 Apr 2013 15:04:08 +0200 Subject: USB: serial: option: Added support Olivetti Olicard 145 This adds PID for Olivetti Olicard 145 in option.c Signed-off-by: Filippo Turato Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e1bff4b..bff059a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -347,6 +347,7 @@ static void option_instat_callback(struct urb *urb); /* Olivetti products */ #define OLIVETTI_VENDOR_ID 0x0b3c #define OLIVETTI_PRODUCT_OLICARD100 0xc000 +#define OLIVETTI_PRODUCT_OLICARD145 0xc003 /* Celot products */ #define CELOT_VENDOR_ID 0x211f @@ -1273,6 +1274,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, + { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/ -- cgit v0.10.2 From 671b4b2ba9266cbcfe7210a704e9ea487dcaa988 Mon Sep 17 00:00:00 2001 From: Tormod Volden Date: Sat, 20 Apr 2013 14:24:04 +0200 Subject: usb-storage: CY7C68300A chips do not support Cypress ATACB Many cards based on CY7C68300A/B/C use the USB ID 04b4:6830 but only the B and C variants (EZ-USB AT2LP) support the ATA Command Block functionality, according to the data sheets. The A variant (EZ-USB AT2) locks up if ATACB is attempted, until a typical 30 seconds timeout runs out and a USB reset is performed. https://bugs.launchpad.net/bugs/428469 It seems that one way to spot a CY7C68300A (at least where the card manufacturer left Cypress' EEPROM default vaules, against Cypress' recommendations) is to look at the USB string descriptor indices. A http://media.digikey.com/pdf/Data%20Sheets/Cypress%20PDFs/CY7C68300A.pdf B http://www.farnell.com/datasheets/43456.pdf C http://www.cypress.com/?rID=14189 Note that a CY7C68300B/C chip appears as CY7C68300A if it is running in Backward Compatibility Mode, and if ATACB would be supported in this case there is anyway no way to tell which chip it really is. For 5 years my external USB drive has been locking up for half a minute when plugged in and ata_id is run by udev, or anytime hdparm or similar is run on it. Finally looking at the /correct/ datasheet I think I found the reason. I am aware the quirk in this patch is a bit hacky, but the hardware manufacturers haven't made it easy for us. Signed-off-by: Tormod Volden Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index e4efc7b..8514a2d 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -248,14 +248,26 @@ static int cypress_probe(struct usb_interface *intf, { struct us_data *us; int result; + struct usb_device *device; result = usb_stor_probe1(&us, intf, id, (id - cypress_usb_ids) + cypress_unusual_dev_list); if (result) return result; - us->protocol_name = "Transparent SCSI with Cypress ATACB"; - us->proto_handler = cypress_atacb_passthrough; + /* Among CY7C68300 chips, the A revision does not support Cypress ATACB + * Filter out this revision from EEPROM default descriptor values + */ + device = interface_to_usbdev(intf); + if (device->descriptor.iManufacturer != 0x38 || + device->descriptor.iProduct != 0x4e || + device->descriptor.iSerialNumber != 0x64) { + us->protocol_name = "Transparent SCSI with Cypress ATACB"; + us->proto_handler = cypress_atacb_passthrough; + } else { + us->protocol_name = "Transparent SCSI"; + us->proto_handler = usb_stor_transparent_scsi_command; + } result = usb_stor_probe2(us); return result; -- cgit v0.10.2 From 62d08a1151d2755dc580a8311a6684275e4aeb94 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 22 Apr 2013 09:44:56 -0700 Subject: USB: Fix initconst in ehci driver Fix some of the initconst markings in the ehci driver(s). Signed-off-by: Andi Kleen Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index a38c8c8..c369767 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -43,7 +43,7 @@ struct ehci_mxc_priv { static struct hc_driver __read_mostly ehci_mxc_hc_driver; -static const struct ehci_driver_overrides ehci_mxc_overrides __initdata = { +static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = { .extra_priv_size = sizeof(struct ehci_mxc_priv), }; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a573d5f..595d210 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -375,7 +375,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) static struct hc_driver __read_mostly ehci_pci_hc_driver; -static const struct ehci_driver_overrides pci_overrides __initdata = { +static const struct ehci_driver_overrides pci_overrides __initconst = { .reset = ehci_pci_setup, }; diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index cda0fa9..f47f259 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -60,7 +60,7 @@ static int ehci_platform_reset(struct usb_hcd *hcd) static struct hc_driver __read_mostly ehci_platform_hc_driver; -static const struct ehci_driver_overrides platform_overrides __initdata = { +static const struct ehci_driver_overrides platform_overrides __initconst = { .reset = ehci_platform_reset, }; -- cgit v0.10.2 From 4623245c769662e7cad80e1f9b39dadfbc2ad06d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Apr 2013 17:54:32 +0200 Subject: usb: phy: phy core cannot yet be a module A lot of platform code calls into the usb phy core at the moment, which does not work if it is built as a loadable module. This will hopefully change when those platforms are all converted to DT based probing, but for now, the easiest solution is to change it from "tristate" to "bool". This solves at least these ARM allmodconfig build errors: arch/arm/mach-imx/built-in.o: In function `imx_otg_ulpi_create': arch/arm/mach-imx/ulpi.c:117: undefined reference to `otg_ulpi_create' arch/arm/mach-omap2/built-in.o: In function `usbhs_init_phys': arch/arm/mach-omap2/usb-host.c:652: undefined reference to `usb_bind_phy' arch/arm/mach-omap2/built-in.o: In function `omap_2430sdp_init': arch/arm/mach-omap2/board-2430sdp.c:236: undefined reference to `usb_bind_phy' arch/arm/mach-omap2/built-in.o: In function `omap3_beagle_init': arch/arm/mach-omap2/board-omap3beagle.c:554: undefined reference to `usb_bind_phy' arch/arm/mach-omap2/built-in.o: In function `devkit8000_init': arch/arm/mach-omap2/board-devkit8000.c:596: undefined reference to `usb_bind_phy' arch/arm/mach-omap2/built-in.o: In function `omap_ldp_init': arch/arm/mach-omap2/board-ldp.c:379: undefined reference to `usb_bind_phy' drivers/built-in.o: In function `ab8500_charger_probe': drivers/power/ab8500_charger.c:3629: undefined reference to `usb_get_phy' drivers/power/ab8500_charger.c:3706: undefined reference to `usb_put_phy' drivers/built-in.o: In function `ab8500_charger_remove': drivers/power/ab8500_charger.c:3411: undefined reference to `usb_put_phy' Signed-off-by: Arnd Bergmann Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3a7fec9..aab2ab2 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -2,7 +2,7 @@ # Physical Layer USB driver configuration # menuconfig USB_PHY - tristate "USB Physical Layer drivers" + bool "USB Physical Layer drivers" help USB controllers (those which are host, device or DRD) need a device to handle the physical layer signalling, commonly called -- cgit v0.10.2 From ee5d5499edb94cd03738a52a7e234b139da8fd72 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Apr 2013 21:05:40 +0200 Subject: usb: phy: tegra: don't call into tegra-ehci directly Both phy-tegra-usb.c and ehci-tegra.c export symbols used by the other one, which does not work if one of them or both are loadable modules, resulting in an error like: drivers/built-in.o: In function `utmi_phy_clk_disable': drivers/usb/phy/phy-tegra-usb.c:302: undefined reference to `tegra_ehci_set_phcd' drivers/built-in.o: In function `utmi_phy_clk_enable': drivers/usb/phy/phy-tegra-usb.c:324: undefined reference to `tegra_ehci_set_phcd' drivers/built-in.o: In function `utmi_phy_power_on': drivers/usb/phy/phy-tegra-usb.c:447: undefined reference to `tegra_ehci_set_pts' This turns the interface into a one-way dependency by letting the tegra ehci driver pass two function pointers for callbacks that need to be called by the phy driver. Signed-off-by: Arnd Bergmann Cc: Venu Byravarasu Cc: Alan Stern Cc: Felipe Balbi Cc: Stephen Warren Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ed201ae..e3eddc3 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -611,7 +611,7 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = { /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val) +static void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val) { unsigned long val; struct usb_hcd *hcd = bus_to_hcd(x->otg->host); @@ -622,9 +622,8 @@ void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val) val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); writel(val, base + TEGRA_USB_PORTSC1); } -EXPORT_SYMBOL_GPL(tegra_ehci_set_pts); -void tegra_ehci_set_phcd(struct usb_phy *x, bool enable) +static void tegra_ehci_set_phcd(struct usb_phy *x, bool enable) { unsigned long val; struct usb_hcd *hcd = bus_to_hcd(x->otg->host); @@ -637,7 +636,6 @@ void tegra_ehci_set_phcd(struct usb_phy *x, bool enable) val &= ~TEGRA_USB_PORTSC1_PHCD; writel(val, base + TEGRA_USB_PORTSC1); } -EXPORT_SYMBOL_GPL(tegra_ehci_set_phcd); static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32); @@ -738,7 +736,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) tegra->phy = tegra_usb_phy_open(&pdev->dev, instance, hcd->regs, pdata->phy_config, - TEGRA_USB_PHY_MODE_HOST); + TEGRA_USB_PHY_MODE_HOST, + tegra_ehci_set_pts, + tegra_ehci_set_phcd); if (IS_ERR(tegra->phy)) { dev_err(&pdev->dev, "Failed to open USB phy\n"); err = -ENXIO; diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 5487d38..17d8112 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -299,7 +299,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); } else - tegra_ehci_set_phcd(&phy->u_phy, true); + phy->set_phcd(&phy->u_phy, true); if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) pr_err("%s: timeout waiting for phy to stabilize\n", __func__); @@ -321,7 +321,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) val &= ~USB_SUSP_CLR; writel(val, base + USB_SUSP_CTRL); } else - tegra_ehci_set_phcd(&phy->u_phy, false); + phy->set_phcd(&phy->u_phy, false); if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, USB_PHY_CLK_VALID)) @@ -444,7 +444,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmi_phy_clk_enable(phy); if (!phy->is_legacy_phy) - tegra_ehci_set_pts(&phy->u_phy, 0); + phy->set_pts(&phy->u_phy, 0); return 0; } @@ -688,7 +688,10 @@ static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) } struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode) + void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode, + void (*set_pts)(struct usb_phy *x, u8 pts_val), + void (*set_phcd)(struct usb_phy *x, bool enable)) + { struct tegra_usb_phy *phy; unsigned long parent_rate; @@ -707,6 +710,8 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, phy->dev = dev; phy->is_legacy_phy = of_property_read_bool(np, "nvidia,has-legacy-mode"); + phy->set_pts = set_pts; + phy->set_phcd = set_phcd; err = of_property_match_string(np, "phy_type", "ulpi"); if (err < 0) phy->is_ulpi_phy = false; diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 9ebebe9..1b7519a 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -61,10 +61,14 @@ struct tegra_usb_phy { struct device *dev; bool is_legacy_phy; bool is_ulpi_phy; + void (*set_pts)(struct usb_phy *x, u8 pts_val); + void (*set_phcd)(struct usb_phy *x, bool enable); }; struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode); + void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode, + void (*set_pts)(struct usb_phy *x, u8 pts_val), + void (*set_phcd)(struct usb_phy *x, bool enable)); void tegra_usb_phy_preresume(struct usb_phy *phy); @@ -75,8 +79,4 @@ void tegra_ehci_phy_restore_start(struct usb_phy *phy, void tegra_ehci_phy_restore_end(struct usb_phy *phy); -void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val); - -void tegra_ehci_set_phcd(struct usb_phy *x, bool enable); - #endif /* __TEGRA_USB_PHY_H */ -- cgit v0.10.2 From 71d9a2b95fc9c9474d46d764336efd7a5a805555 Mon Sep 17 00:00:00 2001 From: Adrian Thomasset Date: Wed, 24 Apr 2013 11:37:35 +0100 Subject: USB: ftdi_sio: enable two UART ports on ST Microconnect Lite The FT4232H used in the ST Micro Connect Lite has four hi-speed UART ports. The first two ports are reserved for the JTAG interface. We enable by default ports 2 and 3 as UARTs (where port 2 is a conventional RS-232 UART) Signed-off-by: Adrian Thomasset Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index e4d7fbd..242b577 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1789,20 +1789,24 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial) } /* - * First and second port on STMCLiteadaptors is reserved for JTAG interface - * and the forth port for pio + * First two ports on JTAG adaptors using an FT4232 such as STMicroelectronics's + * ST Micro Connect Lite are reserved for JTAG or other non-UART interfaces and + * can be accessed from userspace. + * The next two ports are enabled as UARTs by default, where port 2 is + * a conventional RS-232 UART. */ static int ftdi_stmclite_probe(struct usb_serial *serial) { struct usb_device *udev = serial->dev; struct usb_interface *interface = serial->interface; - if (interface == udev->actconfig->interface[2]) - return 0; - - dev_info(&udev->dev, "Ignoring serial port reserved for JTAG\n"); + if (interface == udev->actconfig->interface[0] || + interface == udev->actconfig->interface[1]) { + dev_info(&udev->dev, "Ignoring serial port reserved for JTAG\n"); + return -ENODEV; + } - return -ENODEV; + return 0; } /* -- cgit v0.10.2 From 64e98a7981fbf31ea955d9cec3b6ac52717c6f8a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Apr 2013 19:29:02 +0200 Subject: USB: lpc32xx: ISP1301 needs USB_PHY The Kconfig entry for USB_LPC32XX unconditionally selects USB_ISP1301, which is now only visible when USB_PHY is also enabled. This adds an appropriate dependency and enables USB_PHY in the msm defconfig, avoiding these build errors: warning: (USB_LPC32XX) selects USB_ISP1301 which has unmet direct dependencies (USB_SUPPORT && USB_PHY && (USB || USB_GADGET) && I2C) drivers/built-in.o: In function `usb_hcd_nxp_probe': drivers/usb/host/ohci-nxp.c:224: undefined reference to `isp1301_get_client' drivers/built-in.o: In function `lpc32xx_udc_probe': drivers/usb/gadget/lpc32xx_udc.c:3071: undefined reference to `isp1301_get_client' Signed-off-by: Arnd Bergmann Cc: Felipe Balbi Cc: Roland Stigge Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/configs/lpc32xx_defconfig b/arch/arm/configs/lpc32xx_defconfig index 92386b2..afa7249 100644 --- a/arch/arm/configs/lpc32xx_defconfig +++ b/arch/arm/configs/lpc32xx_defconfig @@ -134,6 +134,7 @@ CONFIG_SND_DEBUG_VERBOSE=y # CONFIG_SND_SPI is not set CONFIG_SND_SOC=y CONFIG_USB=y +CONFIG_USB_PHY=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_STORAGE=y CONFIG_USB_GADGET=y diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a61d981..f85b427 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -144,6 +144,7 @@ config USB_AT91 config USB_LPC32XX tristate "LPC32XX USB Peripheral Controller" depends on ARCH_LPC32XX + depends on USB_PHY select USB_ISP1301 select USB_OTG_UTILS help -- cgit v0.10.2 From c3c683ead3c53c0eb67b72c54e130baf45ac25d2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Apr 2013 19:29:03 +0200 Subject: USB: OMAP: ISP1301 needs USB_PHY The Kconfig entry for USB_OMAP unconditionally selects USB_ISP1301, which is now only visible when USB_PHY is also enabled. This adds an appropriate dependency and enables USB_PHY in the omap1 defconfig, avoiding these build warnings: warning: (USB_OHCI_HCD && USB_OMAP) selects ISP1301_OMAP which has unmet direct dependencies (USB_SUPPORT && USB_PHY && I2C && ARCH_OMAP_OTG) Also fix a Makefile typo while we're at it. Signed-off-by: Arnd Bergmann Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index 42eab9a..7e0ebb6 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -195,6 +195,7 @@ CONFIG_SND_SOC=y CONFIG_SND_OMAP_SOC=y # CONFIG_USB_HID is not set CONFIG_USB=y +CONFIG_USB_PHY=y CONFIG_USB_DEBUG=y CONFIG_USB_DEVICEFS=y # CONFIG_USB_DEVICE_CLASS is not set diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f85b427..83300d9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -196,6 +196,7 @@ config USB_FUSB300 config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 + depends on USB_PHY select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG help Many Texas Instruments OMAP processors have flexible full diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 33863c0..a9169cb 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o -obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301.omap.o +obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o -- cgit v0.10.2 From 8097804eab55d2dcfadfc821bceeb0faad978ab3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Apr 2013 19:29:04 +0200 Subject: USB: OHCI: avoid conflicting platform drivers Like the EHCI driver, OHCI supports a large number of different platform glue drivers by directly including them, which causes problems with conflicting macro definitions in some cases. As more ARM architecture specific back-ends are required to coexist in a single build, we should split those out into separate drivers. Unfortunately, the infrastructure for that is still under development, so to give us more time, this uses a separate *_PLATFORM_DRIVER macro for each ARM specific OHCI backend, just like we already do on PowerPC and some of the other ARM platforms. In linux-3.10, only the SPEAr and CNS3xxx back-ends would actually conflict without this patch, but over time we would get more of them, so this is a way to avoid having to patch the driver every time it breaks. We should still split out all back-ends into separate loadable modules, but that work is only needed to improve code size and cleanliness after this patch, not for correctness. While we're here, this fixes the incorrectly sorted error path for the OMAP1 and OMAP3 backends to ensure we always unregister the exact set of drivers that were registered before erroring out. Signed-off-by: Arnd Bergmann Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 180a2b0..9e6de95 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1102,12 +1102,12 @@ MODULE_LICENSE ("GPL"); #if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX) #include "ohci-s3c2410.c" -#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver +#define S3C2410_PLATFORM_DRIVER ohci_hcd_s3c2410_driver #endif #ifdef CONFIG_USB_OHCI_EXYNOS #include "ohci-exynos.c" -#define PLATFORM_DRIVER exynos_ohci_driver +#define EXYNOS_PLATFORM_DRIVER exynos_ohci_driver #endif #ifdef CONFIG_USB_OHCI_HCD_OMAP1 @@ -1127,25 +1127,24 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_ARCH_EP93XX #include "ohci-ep93xx.c" -#define PLATFORM_DRIVER ohci_hcd_ep93xx_driver +#define EP93XX_PLATFORM_DRIVER ohci_hcd_ep93xx_driver #endif #ifdef CONFIG_ARCH_AT91 #include "ohci-at91.c" -#define PLATFORM_DRIVER ohci_hcd_at91_driver +#define AT91_PLATFORM_DRIVER ohci_hcd_at91_driver #endif #ifdef CONFIG_ARCH_LPC32XX #include "ohci-nxp.c" -#define PLATFORM_DRIVER usb_hcd_nxp_driver +#define NXP_PLATFORM_DRIVER usb_hcd_nxp_driver #endif #ifdef CONFIG_ARCH_DAVINCI_DA8XX #include "ohci-da8xx.c" -#define PLATFORM_DRIVER ohci_hcd_da8xx_driver +#define DAVINCI_PLATFORM_DRIVER ohci_hcd_da8xx_driver #endif - #ifdef CONFIG_USB_OHCI_HCD_PPC_OF #include "ohci-ppc-of.c" #define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver @@ -1153,7 +1152,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PLAT_SPEAR #include "ohci-spear.c" -#define PLATFORM_DRIVER spear_ohci_hcd_driver +#define SPEAR_PLATFORM_DRIVER spear_ohci_hcd_driver #endif #ifdef CONFIG_PPC_PS3 @@ -1199,7 +1198,14 @@ MODULE_LICENSE ("GPL"); !defined(SA1111_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(SM501_OHCI_DRIVER) && \ - !defined(TMIO_OHCI_DRIVER) + !defined(TMIO_OHCI_DRIVER) && \ + !defined(S3C2410_PLATFORM_DRIVER) && \ + !defined(EXYNOS_PLATFORM_DRIVER) && \ + !defined(EP93XX_PLATFORM_DRIVER) && \ + !defined(AT91_PLATFORM_DRIVER) && \ + !defined(NXP_PLATFORM_DRIVER) && \ + !defined(DAVINCI_PLATFORM_DRIVER) && \ + !defined(SPEAR_PLATFORM_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1277,9 +1283,79 @@ static int __init ohci_hcd_mod_init(void) goto error_tmio; #endif +#ifdef S3C2410_PLATFORM_DRIVER + retval = platform_driver_register(&S3C2410_PLATFORM_DRIVER); + if (retval < 0) + goto error_s3c2410; +#endif + +#ifdef EXYNOS_PLATFORM_DRIVER + retval = platform_driver_register(&EXYNOS_PLATFORM_DRIVER); + if (retval < 0) + goto error_exynos; +#endif + +#ifdef EP93XX_PLATFORM_DRIVER + retval = platform_driver_register(&EP93XX_PLATFORM_DRIVER); + if (retval < 0) + goto error_ep93xx; +#endif + +#ifdef AT91_PLATFORM_DRIVER + retval = platform_driver_register(&AT91_PLATFORM_DRIVER); + if (retval < 0) + goto error_at91; +#endif + +#ifdef NXP_PLATFORM_DRIVER + retval = platform_driver_register(&NXP_PLATFORM_DRIVER); + if (retval < 0) + goto error_nxp; +#endif + +#ifdef DAVINCI_PLATFORM_DRIVER + retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER); + if (retval < 0) + goto error_davinci; +#endif + +#ifdef SPEAR_PLATFORM_DRIVER + retval = platform_driver_register(&SPEAR_PLATFORM_DRIVER); + if (retval < 0) + goto error_spear; +#endif + return retval; /* Error path */ +#ifdef SPEAR_PLATFORM_DRIVER + platform_driver_unregister(&SPEAR_PLATFORM_DRIVER); + error_spear: +#endif +#ifdef DAVINCI_PLATFORM_DRIVER + platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER); + error_davinci: +#endif +#ifdef NXP_PLATFORM_DRIVER + platform_driver_unregister(&NXP_PLATFORM_DRIVER); + error_nxp: +#endif +#ifdef AT91_PLATFORM_DRIVER + platform_driver_unregister(&AT91_PLATFORM_DRIVER); + error_at91: +#endif +#ifdef EP93XX_PLATFORM_DRIVER + platform_driver_unregister(&EP93XX_PLATFORM_DRIVER); + error_ep93xx: +#endif +#ifdef EXYNOS_PLATFORM_DRIVER + platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER); + error_exynos: +#endif +#ifdef S3C2410_PLATFORM_DRIVER + platform_driver_unregister(&S3C2410_PLATFORM_DRIVER); + error_s3c2410: +#endif #ifdef TMIO_OHCI_DRIVER platform_driver_unregister(&TMIO_OHCI_DRIVER); error_tmio: @@ -1300,17 +1376,17 @@ static int __init ohci_hcd_mod_init(void) platform_driver_unregister(&OF_PLATFORM_DRIVER); error_of_platform: #endif -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); - error_platform: +#ifdef OMAP3_PLATFORM_DRIVER + platform_driver_unregister(&OMAP3_PLATFORM_DRIVER); + error_omap3_platform: #endif #ifdef OMAP1_PLATFORM_DRIVER platform_driver_unregister(&OMAP1_PLATFORM_DRIVER); error_omap1_platform: #endif -#ifdef OMAP3_PLATFORM_DRIVER - platform_driver_unregister(&OMAP3_PLATFORM_DRIVER); - error_omap3_platform: +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); + error_platform: #endif #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); @@ -1329,6 +1405,27 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SPEAR_PLATFORM_DRIVER + platform_driver_unregister(&SPEAR_PLATFORM_DRIVER); +#endif +#ifdef DAVINCI_PLATFORM_DRIVER + platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER); +#endif +#ifdef NXP_PLATFORM_DRIVER + platform_driver_unregister(&NXP_PLATFORM_DRIVER); +#endif +#ifdef AT91_PLATFORM_DRIVER + platform_driver_unregister(&AT91_PLATFORM_DRIVER); +#endif +#ifdef EP93XX_PLATFORM_DRIVER + platform_driver_unregister(&EP93XX_PLATFORM_DRIVER); +#endif +#ifdef EXYNOS_PLATFORM_DRIVER + platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER); +#endif +#ifdef S3C2410_PLATFORM_DRIVER + platform_driver_unregister(&S3C2410_PLATFORM_DRIVER); +#endif #ifdef TMIO_OHCI_DRIVER platform_driver_unregister(&TMIO_OHCI_DRIVER); #endif @@ -1344,12 +1441,15 @@ static void __exit ohci_hcd_mod_exit(void) #ifdef OF_PLATFORM_DRIVER platform_driver_unregister(&OF_PLATFORM_DRIVER); #endif -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); -#endif #ifdef OMAP3_PLATFORM_DRIVER platform_driver_unregister(&OMAP3_PLATFORM_DRIVER); #endif +#ifdef OMAP1_PLATFORM_DRIVER + platform_driver_unregister(&OMAP1_PLATFORM_DRIVER); +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +#endif #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif -- cgit v0.10.2 From 4626b8daf9bb00ce6b4d533c1a155211ad880f32 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Apr 2013 19:29:01 +0200 Subject: USB: ehci-msm: USB_MSM_OTG needs USB_PHY The Kconfig entry for USB_EHCI_MSM unconditionally selects USB_MSM_OTG, which is now only visible when USB_PHY is also enabled. This adds an appropriate dependency and enables USB_PHY in the msm defconfig, avoiding the Kbuild warning: warning: (USB_EHCI_MSM) selects USB_MSM_OTG which has unmet direct dependencies (USB_SUPPORT && USB_PHY && (USB || USB_GADGET) && ARCH_MSM) Signed-off-by: Arnd Bergmann Cc: Felipe Balbi Cc: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c558472..de94f26 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -188,6 +188,7 @@ config USB_EHCI_HCD_AT91 config USB_EHCI_MSM tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller" depends on ARCH_MSM + depends on USB_PHY select USB_EHCI_ROOT_HUB_TT select USB_MSM_OTG ---help--- -- cgit v0.10.2