diff options
author | Mike Thompson <mpthompson@gmail.com> | 2012-08-30 17:26:25 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-09-10 12:46:38 -0400 |
commit | 363366cf61c544ea476f3d220f43a95cb03014f5 (patch) | |
tree | df7f080a47500f0ee5bde050b90815f1a5e288ef /drivers/usb/otg | |
parent | 51e1e7bcef53e6a91cfffff0145ab315def61f61 (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.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 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 | |||
37 | struct mxs_phy { | 44 | struct 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 | ||
88 | static 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 | |||
79 | static int mxs_phy_on_connect(struct usb_phy *phy, int port) | 99 | static 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 | ||