aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg
diff options
context:
space:
mode:
authorMike Thompson <mpthompson@gmail.com>2012-08-30 17:26:25 -0400
committerFelipe Balbi <balbi@ti.com>2012-09-10 12:46:38 -0400
commit363366cf61c544ea476f3d220f43a95cb03014f5 (patch)
treedf7f080a47500f0ee5bde050b90815f1a5e288ef /drivers/usb/otg
parent51e1e7bcef53e6a91cfffff0145ab315def61f61 (diff)
usb: otg: mxs-phy: Fix mx23 operation
Currently mx23 fails to enumerate a USB device: [ 1.300000] hub 1-0:1.0: unable to enumerate USB device on port 1 [ 1.520000] hub 1-0:1.0: unable to enumerate USB device on port 1 [ 1.740000] hub 1-0:1.0: unable to enumerate USB device on port 1 [ 1.960000] hub 1-0:1.0: unable to enumerate USB device on port 1 [ 2.180000] hub 1-0:1.0: unable to enumerate USB device on port 1 Use a kernel workqueue to asynchronously delay the setting of ENHOSTDISCONDETECT bit until after higher level hub connect/reset processing is complete. Prematurely setting the bit prevents the connection processing from completing and not setting it prevents disconnect from being detected. No delay is needed for clearing of ENHOSTDISCONDETECT. Successfully tested on mx23-olinuxino (micro, mini and maxi variants) and mx28evk. Cc: stable@vger.kernel.org # v3.6 Signed-off-by: Mike Thompson <mpthompson@gmail.com> Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/mxs-phy.c38
1 files changed, 35 insertions, 3 deletions
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
index c1a67cb8e244..88db976647cf 100644
--- a/drivers/usb/otg/mxs-phy.c
+++ b/drivers/usb/otg/mxs-phy.c
@@ -20,6 +20,7 @@
20#include <linux/delay.h> 20#include <linux/delay.h>
21#include <linux/err.h> 21#include <linux/err.h>
22#include <linux/io.h> 22#include <linux/io.h>
23#include <linux/workqueue.h>
23 24
24#define DRIVER_NAME "mxs_phy" 25#define DRIVER_NAME "mxs_phy"
25 26
@@ -34,9 +35,16 @@
34#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) 35#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14)
35#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) 36#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1)
36 37
38/*
39 * Amount of delay in miliseconds to safely enable ENHOSTDISCONDETECT bit
40 * so that connection and reset processing can be completed for the root hub.
41 */
42#define MXY_PHY_ENHOSTDISCONDETECT_DELAY 250
43
37struct mxs_phy { 44struct mxs_phy {
38 struct usb_phy phy; 45 struct usb_phy phy;
39 struct clk *clk; 46 struct clk *clk;
47 struct delayed_work enhostdiscondetect_work;
40}; 48};
41 49
42#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) 50#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)
62 70
63 clk_prepare_enable(mxs_phy->clk); 71 clk_prepare_enable(mxs_phy->clk);
64 mxs_phy_hw_init(mxs_phy); 72 mxs_phy_hw_init(mxs_phy);
73 INIT_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, NULL);
65 74
66 return 0; 75 return 0;
67} 76}
@@ -76,13 +85,34 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
76 clk_disable_unprepare(mxs_phy->clk); 85 clk_disable_unprepare(mxs_phy->clk);
77} 86}
78 87
88static void mxs_phy_enhostdiscondetect_delay(struct work_struct *ws)
89{
90 struct mxs_phy *mxs_phy = container_of(ws, struct mxs_phy,
91 enhostdiscondetect_work.work);
92
93 /* Enable HOSTDISCONDETECT after delay. */
94 dev_dbg(mxs_phy->phy.dev, "Setting ENHOSTDISCONDETECT\n");
95 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
96 mxs_phy->phy.io_priv + HW_USBPHY_CTRL_SET);
97}
98
79static int mxs_phy_on_connect(struct usb_phy *phy, int port) 99static int mxs_phy_on_connect(struct usb_phy *phy, int port)
80{ 100{
101 struct mxs_phy *mxs_phy = to_mxs_phy(phy);
102
81 dev_dbg(phy->dev, "Connect on port %d\n", port); 103 dev_dbg(phy->dev, "Connect on port %d\n", port);
82 104
83 mxs_phy_hw_init(to_mxs_phy(phy)); 105 mxs_phy_hw_init(mxs_phy);
84 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 106
85 phy->io_priv + HW_USBPHY_CTRL_SET); 107 /*
108 * Delay enabling ENHOSTDISCONDETECT so that connection and
109 * reset processing can be completed for the root hub.
110 */
111 dev_dbg(phy->dev, "Delaying setting ENHOSTDISCONDETECT\n");
112 PREPARE_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work,
113 mxs_phy_enhostdiscondetect_delay);
114 schedule_delayed_work(&mxs_phy->enhostdiscondetect_work,
115 msecs_to_jiffies(MXY_PHY_ENHOSTDISCONDETECT_DELAY));
86 116
87 return 0; 117 return 0;
88} 118}
@@ -91,6 +121,8 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)
91{ 121{
92 dev_dbg(phy->dev, "Disconnect on port %d\n", port); 122 dev_dbg(phy->dev, "Disconnect on port %d\n", port);
93 123
124 /* No need to delay before clearing ENHOSTDISCONDETECT. */
125 dev_dbg(phy->dev, "Clearing ENHOSTDISCONDETECT\n");
94 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 126 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
95 phy->io_priv + HW_USBPHY_CTRL_CLR); 127 phy->io_priv + HW_USBPHY_CTRL_CLR);
96 128