diff options
Diffstat (limited to 'drivers/usb/phy/phy-mxs-usb.c')
-rw-r--r-- | drivers/usb/phy/phy-mxs-usb.c | 86 |
1 files changed, 81 insertions, 5 deletions
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index b9589f663683..8f7cb068d29b 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c | |||
@@ -40,6 +40,7 @@ | |||
40 | 40 | ||
41 | #define BM_USBPHY_CTRL_SFTRST BIT(31) | 41 | #define BM_USBPHY_CTRL_SFTRST BIT(31) |
42 | #define BM_USBPHY_CTRL_CLKGATE BIT(30) | 42 | #define BM_USBPHY_CTRL_CLKGATE BIT(30) |
43 | #define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) | ||
43 | #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) | 44 | #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) |
44 | #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) | 45 | #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) |
45 | #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) | 46 | #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) |
@@ -69,6 +70,9 @@ | |||
69 | #define ANADIG_USB2_LOOPBACK_SET 0x244 | 70 | #define ANADIG_USB2_LOOPBACK_SET 0x244 |
70 | #define ANADIG_USB2_LOOPBACK_CLR 0x248 | 71 | #define ANADIG_USB2_LOOPBACK_CLR 0x248 |
71 | 72 | ||
73 | #define ANADIG_USB1_MISC 0x1f0 | ||
74 | #define ANADIG_USB2_MISC 0x250 | ||
75 | |||
72 | #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) | 76 | #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) |
73 | #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) | 77 | #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) |
74 | 78 | ||
@@ -80,6 +84,11 @@ | |||
80 | #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) | 84 | #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) |
81 | #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) | 85 | #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) |
82 | 86 | ||
87 | #define BM_ANADIG_USB1_MISC_RX_VPIN_FS BIT(29) | ||
88 | #define BM_ANADIG_USB1_MISC_RX_VMIN_FS BIT(28) | ||
89 | #define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29) | ||
90 | #define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28) | ||
91 | |||
83 | #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) | 92 | #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) |
84 | 93 | ||
85 | /* Do disconnection between PHY and controller without vbus */ | 94 | /* Do disconnection between PHY and controller without vbus */ |
@@ -131,8 +140,7 @@ static const struct mxs_phy_data vf610_phy_data = { | |||
131 | }; | 140 | }; |
132 | 141 | ||
133 | static const struct mxs_phy_data imx6sx_phy_data = { | 142 | static const struct mxs_phy_data imx6sx_phy_data = { |
134 | .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | | 143 | .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, |
135 | MXS_PHY_NEED_IP_FIX, | ||
136 | }; | 144 | }; |
137 | 145 | ||
138 | static const struct of_device_id mxs_phy_dt_ids[] = { | 146 | static const struct of_device_id mxs_phy_dt_ids[] = { |
@@ -256,6 +264,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) | |||
256 | usleep_range(500, 1000); | 264 | usleep_range(500, 1000); |
257 | } | 265 | } |
258 | 266 | ||
267 | static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) | ||
268 | { | ||
269 | void __iomem *base = mxs_phy->phy.io_priv; | ||
270 | u32 phyctrl = readl(base + HW_USBPHY_CTRL); | ||
271 | |||
272 | if (IS_ENABLED(CONFIG_USB_OTG) && | ||
273 | !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE)) | ||
274 | return true; | ||
275 | |||
276 | return false; | ||
277 | } | ||
278 | |||
259 | static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) | 279 | static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) |
260 | { | 280 | { |
261 | bool vbus_is_on = false; | 281 | bool vbus_is_on = false; |
@@ -270,7 +290,7 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) | |||
270 | 290 | ||
271 | vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); | 291 | vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); |
272 | 292 | ||
273 | if (on && !vbus_is_on) | 293 | if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) |
274 | __mxs_phy_disconnect_line(mxs_phy, true); | 294 | __mxs_phy_disconnect_line(mxs_phy, true); |
275 | else | 295 | else |
276 | __mxs_phy_disconnect_line(mxs_phy, false); | 296 | __mxs_phy_disconnect_line(mxs_phy, false); |
@@ -293,6 +313,17 @@ static int mxs_phy_init(struct usb_phy *phy) | |||
293 | static void mxs_phy_shutdown(struct usb_phy *phy) | 313 | static void mxs_phy_shutdown(struct usb_phy *phy) |
294 | { | 314 | { |
295 | struct mxs_phy *mxs_phy = to_mxs_phy(phy); | 315 | struct mxs_phy *mxs_phy = to_mxs_phy(phy); |
316 | u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | | ||
317 | BM_USBPHY_CTRL_ENDPDMCHG_WKUP | | ||
318 | BM_USBPHY_CTRL_ENIDCHG_WKUP | | ||
319 | BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | | ||
320 | BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | | ||
321 | BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | | ||
322 | BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | | ||
323 | BM_USBPHY_CTRL_ENAUTO_PWRON_PLL; | ||
324 | |||
325 | writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR); | ||
326 | writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD); | ||
296 | 327 | ||
297 | writel(BM_USBPHY_CTRL_CLKGATE, | 328 | writel(BM_USBPHY_CTRL_CLKGATE, |
298 | phy->io_priv + HW_USBPHY_CTRL_SET); | 329 | phy->io_priv + HW_USBPHY_CTRL_SET); |
@@ -300,13 +331,56 @@ static void mxs_phy_shutdown(struct usb_phy *phy) | |||
300 | clk_disable_unprepare(mxs_phy->clk); | 331 | clk_disable_unprepare(mxs_phy->clk); |
301 | } | 332 | } |
302 | 333 | ||
334 | static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy) | ||
335 | { | ||
336 | unsigned int line_state; | ||
337 | /* bit definition is the same for all controllers */ | ||
338 | unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS, | ||
339 | dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS; | ||
340 | unsigned int reg = ANADIG_USB1_MISC; | ||
341 | |||
342 | /* If the SoCs don't have anatop, quit */ | ||
343 | if (!mxs_phy->regmap_anatop) | ||
344 | return false; | ||
345 | |||
346 | if (mxs_phy->port_id == 0) | ||
347 | reg = ANADIG_USB1_MISC; | ||
348 | else if (mxs_phy->port_id == 1) | ||
349 | reg = ANADIG_USB2_MISC; | ||
350 | |||
351 | regmap_read(mxs_phy->regmap_anatop, reg, &line_state); | ||
352 | |||
353 | if ((line_state & (dp_bit | dm_bit)) == dm_bit) | ||
354 | return true; | ||
355 | else | ||
356 | return false; | ||
357 | } | ||
358 | |||
303 | static int mxs_phy_suspend(struct usb_phy *x, int suspend) | 359 | static int mxs_phy_suspend(struct usb_phy *x, int suspend) |
304 | { | 360 | { |
305 | int ret; | 361 | int ret; |
306 | struct mxs_phy *mxs_phy = to_mxs_phy(x); | 362 | struct mxs_phy *mxs_phy = to_mxs_phy(x); |
363 | bool low_speed_connection, vbus_is_on; | ||
364 | |||
365 | low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy); | ||
366 | vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); | ||
307 | 367 | ||
308 | if (suspend) { | 368 | if (suspend) { |
309 | writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); | 369 | /* |
370 | * FIXME: Do not power down RXPWD1PT1 bit for low speed | ||
371 | * connect. The low speed connection will have problem at | ||
372 | * very rare cases during usb suspend and resume process. | ||
373 | */ | ||
374 | if (low_speed_connection & vbus_is_on) { | ||
375 | /* | ||
376 | * If value to be set as pwd value is not 0xffffffff, | ||
377 | * several 32Khz cycles are needed. | ||
378 | */ | ||
379 | mxs_phy_clock_switch_delay(); | ||
380 | writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD); | ||
381 | } else { | ||
382 | writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); | ||
383 | } | ||
310 | writel(BM_USBPHY_CTRL_CLKGATE, | 384 | writel(BM_USBPHY_CTRL_CLKGATE, |
311 | x->io_priv + HW_USBPHY_CTRL_SET); | 385 | x->io_priv + HW_USBPHY_CTRL_SET); |
312 | clk_disable_unprepare(mxs_phy->clk); | 386 | clk_disable_unprepare(mxs_phy->clk); |
@@ -359,7 +433,9 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, | |||
359 | dev_dbg(phy->dev, "%s device has disconnected\n", | 433 | dev_dbg(phy->dev, "%s device has disconnected\n", |
360 | (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); | 434 | (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); |
361 | 435 | ||
362 | if (speed == USB_SPEED_HIGH) | 436 | /* Sometimes, the speed is not high speed when the error occurs */ |
437 | if (readl(phy->io_priv + HW_USBPHY_CTRL) & | ||
438 | BM_USBPHY_CTRL_ENHOSTDISCONDETECT) | ||
363 | writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, | 439 | writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, |
364 | phy->io_priv + HW_USBPHY_CTRL_CLR); | 440 | phy->io_priv + HW_USBPHY_CTRL_CLR); |
365 | 441 | ||