diff options
Diffstat (limited to 'drivers/usb/otg/mxs-phy.c')
| -rw-r--r-- | drivers/usb/otg/mxs-phy.c | 38 | 
1 files changed, 35 insertions, 3 deletions
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c index c1a67cb8e24..88db976647c 100644 --- a/drivers/usb/otg/mxs-phy.c +++ b/drivers/usb/otg/mxs-phy.c @@ -20,6 +20,7 @@  #include <linux/delay.h>  #include <linux/err.h>  #include <linux/io.h> +#include <linux/workqueue.h>  #define DRIVER_NAME "mxs_phy" @@ -34,9 +35,16 @@  #define BM_USBPHY_CTRL_ENUTMILEVEL2		BIT(14)  #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT	BIT(1) +/* + * Amount of delay in miliseconds to safely enable ENHOSTDISCONDETECT bit + * so that connection and reset processing can be completed for the root hub. + */ +#define MXY_PHY_ENHOSTDISCONDETECT_DELAY	250 +  struct mxs_phy {  	struct usb_phy phy;  	struct clk *clk; +	struct delayed_work enhostdiscondetect_work;  };  #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) @@ -62,6 +70,7 @@ static int mxs_phy_init(struct usb_phy *phy)  	clk_prepare_enable(mxs_phy->clk);  	mxs_phy_hw_init(mxs_phy); +	INIT_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, NULL);  	return 0;  } @@ -76,13 +85,34 @@ static void mxs_phy_shutdown(struct usb_phy *phy)  	clk_disable_unprepare(mxs_phy->clk);  } +static void mxs_phy_enhostdiscondetect_delay(struct work_struct *ws) +{ +	struct mxs_phy *mxs_phy = container_of(ws, struct mxs_phy, +						enhostdiscondetect_work.work); + +	/* Enable HOSTDISCONDETECT after delay. */ +	dev_dbg(mxs_phy->phy.dev, "Setting ENHOSTDISCONDETECT\n"); +	writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, +				mxs_phy->phy.io_priv + HW_USBPHY_CTRL_SET); +} +  static int mxs_phy_on_connect(struct usb_phy *phy, int port)  { +	struct mxs_phy *mxs_phy = to_mxs_phy(phy); +  	dev_dbg(phy->dev, "Connect on port %d\n", port); -	mxs_phy_hw_init(to_mxs_phy(phy)); -	writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, -			phy->io_priv + HW_USBPHY_CTRL_SET); +	mxs_phy_hw_init(mxs_phy); + +	/* +	 * Delay enabling ENHOSTDISCONDETECT so that connection and +	 * reset processing can be completed for the root hub. +	 */ +	dev_dbg(phy->dev, "Delaying setting ENHOSTDISCONDETECT\n"); +	PREPARE_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, +			mxs_phy_enhostdiscondetect_delay); +	schedule_delayed_work(&mxs_phy->enhostdiscondetect_work, +			msecs_to_jiffies(MXY_PHY_ENHOSTDISCONDETECT_DELAY));  	return 0;  } @@ -91,6 +121,8 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)  {  	dev_dbg(phy->dev, "Disconnect on port %d\n", port); +	/* No need to delay before clearing ENHOSTDISCONDETECT. */ +	dev_dbg(phy->dev, "Clearing ENHOSTDISCONDETECT\n");  	writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,  			phy->io_priv + HW_USBPHY_CTRL_CLR);  |