diff options
author | Stephen Boyd <stephen.boyd@linaro.org> | 2016-12-28 17:57:06 -0500 |
---|---|---|
committer | Peter Chen <peter.chen@nxp.com> | 2017-01-20 02:27:35 -0500 |
commit | 11893dae63da0f5b251cf7f9a24d64c8ff4771ff (patch) | |
tree | 9978a7acdaff564f565f3b82173d012f19f523a7 | |
parent | 1b8fc5a5253d593daff68378444f8fc4bc50af1a (diff) |
usb: chipidea: msm: Handle phy power states
The ULPI phy on qcom platforms needs to be initialized and
powered on after a USB reset and before we toggle the run/stop
bit. Otherwise, the phy locks up and doesn't work properly. Hook
the phy initialization into the RESET event and the phy power off
into the STOPPED event.
Acked-by: Peter Chen <peter.chen@nxp.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_msm.c | 40 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 8 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 8 | ||||
-rw-r--r-- | include/linux/usb/chipidea.h | 2 |
4 files changed, 33 insertions, 25 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index fe96df7b530c..316044dba0ac 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c | |||
@@ -80,20 +80,33 @@ static const struct reset_control_ops ci_hdrc_msm_reset_ops = { | |||
80 | .reset = ci_hdrc_msm_por_reset, | 80 | .reset = ci_hdrc_msm_por_reset, |
81 | }; | 81 | }; |
82 | 82 | ||
83 | static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) | 83 | static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) |
84 | { | 84 | { |
85 | struct device *dev = ci->dev->parent; | 85 | struct device *dev = ci->dev->parent; |
86 | struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev); | 86 | struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev); |
87 | int ret; | ||
87 | 88 | ||
88 | switch (event) { | 89 | switch (event) { |
89 | case CI_HDRC_CONTROLLER_RESET_EVENT: | 90 | case CI_HDRC_CONTROLLER_RESET_EVENT: |
90 | dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n"); | 91 | dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n"); |
92 | |||
93 | hw_phymode_configure(ci); | ||
91 | if (msm_ci->secondary_phy) { | 94 | if (msm_ci->secondary_phy) { |
92 | u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL); | 95 | u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL); |
93 | val |= HS_PHY_DIG_CLAMP_N; | 96 | val |= HS_PHY_DIG_CLAMP_N; |
94 | writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL); | 97 | writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL); |
95 | } | 98 | } |
96 | 99 | ||
100 | ret = phy_init(ci->phy); | ||
101 | if (ret) | ||
102 | return ret; | ||
103 | |||
104 | ret = phy_power_on(ci->phy); | ||
105 | if (ret) { | ||
106 | phy_exit(ci->phy); | ||
107 | return ret; | ||
108 | } | ||
109 | |||
97 | /* use AHB transactor, allow posted data writes */ | 110 | /* use AHB transactor, allow posted data writes */ |
98 | hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8); | 111 | hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8); |
99 | 112 | ||
@@ -113,21 +126,18 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) | |||
113 | HSPHY_SESS_VLD_CTRL); | 126 | HSPHY_SESS_VLD_CTRL); |
114 | 127 | ||
115 | } | 128 | } |
116 | |||
117 | usb_phy_init(ci->usb_phy); | ||
118 | break; | 129 | break; |
119 | case CI_HDRC_CONTROLLER_STOPPED_EVENT: | 130 | case CI_HDRC_CONTROLLER_STOPPED_EVENT: |
120 | dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n"); | 131 | dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n"); |
121 | /* | 132 | phy_power_off(ci->phy); |
122 | * Put the phy in non-driving mode. Otherwise host | 133 | phy_exit(ci->phy); |
123 | * may not detect soft-disconnection. | ||
124 | */ | ||
125 | usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN); | ||
126 | break; | 134 | break; |
127 | default: | 135 | default: |
128 | dev_dbg(dev, "unknown ci_hdrc event\n"); | 136 | dev_dbg(dev, "unknown ci_hdrc event\n"); |
129 | break; | 137 | break; |
130 | } | 138 | } |
139 | |||
140 | return 0; | ||
131 | } | 141 | } |
132 | 142 | ||
133 | static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci, | 143 | static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci, |
@@ -167,7 +177,6 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) | |||
167 | { | 177 | { |
168 | struct ci_hdrc_msm *ci; | 178 | struct ci_hdrc_msm *ci; |
169 | struct platform_device *plat_ci; | 179 | struct platform_device *plat_ci; |
170 | struct usb_phy *phy; | ||
171 | struct clk *clk; | 180 | struct clk *clk; |
172 | struct reset_control *reset; | 181 | struct reset_control *reset; |
173 | struct resource *res; | 182 | struct resource *res; |
@@ -181,21 +190,12 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) | |||
181 | return -ENOMEM; | 190 | return -ENOMEM; |
182 | platform_set_drvdata(pdev, ci); | 191 | platform_set_drvdata(pdev, ci); |
183 | 192 | ||
184 | /* | ||
185 | * OTG(PHY) driver takes care of PHY initialization, clock management, | ||
186 | * powering up VBUS, mapping of registers address space and power | ||
187 | * management. | ||
188 | */ | ||
189 | phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); | ||
190 | if (IS_ERR(phy)) | ||
191 | return PTR_ERR(phy); | ||
192 | |||
193 | ci->pdata.name = "ci_hdrc_msm"; | 193 | ci->pdata.name = "ci_hdrc_msm"; |
194 | ci->pdata.capoffset = DEF_CAPOFFSET; | 194 | ci->pdata.capoffset = DEF_CAPOFFSET; |
195 | ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING | | 195 | ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING | |
196 | CI_HDRC_OVERRIDE_AHB_BURST; | 196 | CI_HDRC_OVERRIDE_AHB_BURST | |
197 | CI_HDRC_OVERRIDE_PHY_CONTROL; | ||
197 | ci->pdata.notify_event = ci_hdrc_msm_notify_event; | 198 | ci->pdata.notify_event = ci_hdrc_msm_notify_event; |
198 | ci->pdata.usb_phy = phy; | ||
199 | 199 | ||
200 | reset = devm_reset_control_get(&pdev->dev, "core"); | 200 | reset = devm_reset_control_get(&pdev->dev, "core"); |
201 | if (IS_ERR(reset)) | 201 | if (IS_ERR(reset)) |
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 367d02a02145..db69f6a0bf98 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c | |||
@@ -327,6 +327,7 @@ void hw_phymode_configure(struct ci_hdrc *ci) | |||
327 | hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); | 327 | hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); |
328 | } | 328 | } |
329 | } | 329 | } |
330 | EXPORT_SYMBOL_GPL(hw_phymode_configure); | ||
330 | 331 | ||
331 | /** | 332 | /** |
332 | * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy | 333 | * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy |
@@ -503,9 +504,12 @@ int hw_device_reset(struct ci_hdrc *ci) | |||
503 | return ret; | 504 | return ret; |
504 | } | 505 | } |
505 | 506 | ||
506 | if (ci->platdata->notify_event) | 507 | if (ci->platdata->notify_event) { |
507 | ci->platdata->notify_event(ci, | 508 | ret = ci->platdata->notify_event(ci, |
508 | CI_HDRC_CONTROLLER_RESET_EVENT); | 509 | CI_HDRC_CONTROLLER_RESET_EVENT); |
510 | if (ret) | ||
511 | return ret; | ||
512 | } | ||
509 | 513 | ||
510 | /* USBMODE should be configured step by step */ | 514 | /* USBMODE should be configured step by step */ |
511 | hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); | 515 | hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); |
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index f884c0877bca..915f3e91586e 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c | |||
@@ -90,8 +90,12 @@ static int ehci_ci_reset(struct usb_hcd *hcd) | |||
90 | 90 | ||
91 | ehci->need_io_watchdog = 0; | 91 | ehci->need_io_watchdog = 0; |
92 | 92 | ||
93 | if (ci->platdata->notify_event) | 93 | if (ci->platdata->notify_event) { |
94 | ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_RESET_EVENT); | 94 | ret = ci->platdata->notify_event(ci, |
95 | CI_HDRC_CONTROLLER_RESET_EVENT); | ||
96 | if (ret) | ||
97 | return ret; | ||
98 | } | ||
95 | 99 | ||
96 | ci_platform_configure(ci); | 100 | ci_platform_configure(ci); |
97 | 101 | ||
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 7e3daa37cf60..c5fdfcf99828 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h | |||
@@ -61,7 +61,7 @@ struct ci_hdrc_platform_data { | |||
61 | enum usb_dr_mode dr_mode; | 61 | enum usb_dr_mode dr_mode; |
62 | #define CI_HDRC_CONTROLLER_RESET_EVENT 0 | 62 | #define CI_HDRC_CONTROLLER_RESET_EVENT 0 |
63 | #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 | 63 | #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 |
64 | void (*notify_event) (struct ci_hdrc *ci, unsigned event); | 64 | int (*notify_event) (struct ci_hdrc *ci, unsigned event); |
65 | struct regulator *reg_vbus; | 65 | struct regulator *reg_vbus; |
66 | struct usb_otg_caps ci_otg_caps; | 66 | struct usb_otg_caps ci_otg_caps; |
67 | bool tpl_support; | 67 | bool tpl_support; |