aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/chipidea/ci_hdrc_imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea/ci_hdrc_imx.c')
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c153
1 files changed, 127 insertions, 26 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 0f05de7c6b6c..389f0e034259 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -23,22 +23,40 @@
23#include "ci.h" 23#include "ci.h"
24#include "ci_hdrc_imx.h" 24#include "ci_hdrc_imx.h"
25 25
26#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0)
27
28struct ci_hdrc_imx_platform_flag { 26struct ci_hdrc_imx_platform_flag {
29 unsigned int flags; 27 unsigned int flags;
28 bool runtime_pm;
30}; 29};
31 30
32static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 31static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
33}; 32};
34 33
35static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 34static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
36 .flags = CI_HDRC_IMX_IMX28_WRITE_FIX, 35 .flags = CI_HDRC_IMX28_WRITE_FIX |
36 CI_HDRC_TURN_VBUS_EARLY_ON,
37};
38
39static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
40 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
41 CI_HDRC_TURN_VBUS_EARLY_ON,
42};
43
44static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
45 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
46 CI_HDRC_TURN_VBUS_EARLY_ON,
47};
48
49static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
50 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
51 CI_HDRC_TURN_VBUS_EARLY_ON,
37}; 52};
38 53
39static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 54static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
40 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 55 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
41 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 56 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
57 { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
58 { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
59 { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data},
42 { /* sentinel */ } 60 { /* sentinel */ }
43}; 61};
44MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 62MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
@@ -48,6 +66,8 @@ struct ci_hdrc_imx_data {
48 struct platform_device *ci_pdev; 66 struct platform_device *ci_pdev;
49 struct clk *clk; 67 struct clk *clk;
50 struct imx_usbmisc_data *usbmisc_data; 68 struct imx_usbmisc_data *usbmisc_data;
69 bool supports_runtime_pm;
70 bool in_lpm;
51}; 71};
52 72
53/* Common functions shared by usbmisc drivers */ 73/* Common functions shared by usbmisc drivers */
@@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
145 } 165 }
146 166
147 pdata.usb_phy = data->phy; 167 pdata.usb_phy = data->phy;
148 168 pdata.flags |= imx_platform_flag->flags;
149 if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) 169 if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
150 pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; 170 data->supports_runtime_pm = true;
151 171
152 ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 172 ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
153 if (ret) 173 if (ret)
154 goto err_clk; 174 goto err_clk;
155 175
156 if (data->usbmisc_data) { 176 ret = imx_usbmisc_init(data->usbmisc_data);
157 ret = imx_usbmisc_init(data->usbmisc_data); 177 if (ret) {
158 if (ret) { 178 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
159 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", 179 goto err_clk;
160 ret);
161 goto err_clk;
162 }
163 } 180 }
164 181
165 data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 182 data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
@@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
173 goto err_clk; 190 goto err_clk;
174 } 191 }
175 192
176 if (data->usbmisc_data) { 193 ret = imx_usbmisc_init_post(data->usbmisc_data);
177 ret = imx_usbmisc_init_post(data->usbmisc_data); 194 if (ret) {
178 if (ret) { 195 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
179 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", 196 goto disable_device;
180 ret);
181 goto disable_device;
182 }
183 } 197 }
184 198
185 platform_set_drvdata(pdev, data); 199 platform_set_drvdata(pdev, data);
186 200
187 pm_runtime_no_callbacks(&pdev->dev); 201 if (data->supports_runtime_pm) {
188 pm_runtime_enable(&pdev->dev); 202 pm_runtime_set_active(&pdev->dev);
203 pm_runtime_enable(&pdev->dev);
204 }
205
206 device_set_wakeup_capable(&pdev->dev, true);
189 207
190 return 0; 208 return 0;
191 209
@@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
200{ 218{
201 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 219 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
202 220
203 pm_runtime_disable(&pdev->dev); 221 if (data->supports_runtime_pm) {
222 pm_runtime_get_sync(&pdev->dev);
223 pm_runtime_disable(&pdev->dev);
224 pm_runtime_put_noidle(&pdev->dev);
225 }
204 ci_hdrc_remove_device(data->ci_pdev); 226 ci_hdrc_remove_device(data->ci_pdev);
205 clk_disable_unprepare(data->clk); 227 clk_disable_unprepare(data->clk);
206 228
207 return 0; 229 return 0;
208} 230}
209 231
210#ifdef CONFIG_PM_SLEEP 232#ifdef CONFIG_PM
211static int imx_controller_suspend(struct device *dev) 233static int imx_controller_suspend(struct device *dev)
212{ 234{
213 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 235 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev)
215 dev_dbg(dev, "at %s\n", __func__); 237 dev_dbg(dev, "at %s\n", __func__);
216 238
217 clk_disable_unprepare(data->clk); 239 clk_disable_unprepare(data->clk);
240 data->in_lpm = true;
218 241
219 return 0; 242 return 0;
220} 243}
@@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev)
222static int imx_controller_resume(struct device *dev) 245static int imx_controller_resume(struct device *dev)
223{ 246{
224 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 247 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
248 int ret = 0;
225 249
226 dev_dbg(dev, "at %s\n", __func__); 250 dev_dbg(dev, "at %s\n", __func__);
227 251
228 return clk_prepare_enable(data->clk); 252 if (!data->in_lpm) {
253 WARN_ON(1);
254 return 0;
255 }
256
257 ret = clk_prepare_enable(data->clk);
258 if (ret)
259 return ret;
260
261 data->in_lpm = false;
262
263 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
264 if (ret) {
265 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
266 goto clk_disable;
267 }
268
269 return 0;
270
271clk_disable:
272 clk_disable_unprepare(data->clk);
273 return ret;
229} 274}
230 275
276#ifdef CONFIG_PM_SLEEP
231static int ci_hdrc_imx_suspend(struct device *dev) 277static int ci_hdrc_imx_suspend(struct device *dev)
232{ 278{
279 int ret;
280
281 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
282
283 if (data->in_lpm)
284 /* The core's suspend doesn't run */
285 return 0;
286
287 if (device_may_wakeup(dev)) {
288 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
289 if (ret) {
290 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
291 ret);
292 return ret;
293 }
294 }
295
233 return imx_controller_suspend(dev); 296 return imx_controller_suspend(dev);
234} 297}
235 298
236static int ci_hdrc_imx_resume(struct device *dev) 299static int ci_hdrc_imx_resume(struct device *dev)
237{ 300{
238 return imx_controller_resume(dev); 301 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
302 int ret;
303
304 ret = imx_controller_resume(dev);
305 if (!ret && data->supports_runtime_pm) {
306 pm_runtime_disable(dev);
307 pm_runtime_set_active(dev);
308 pm_runtime_enable(dev);
309 }
310
311 return ret;
239} 312}
240#endif /* CONFIG_PM_SLEEP */ 313#endif /* CONFIG_PM_SLEEP */
241 314
315static int ci_hdrc_imx_runtime_suspend(struct device *dev)
316{
317 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
318 int ret;
319
320 if (data->in_lpm) {
321 WARN_ON(1);
322 return 0;
323 }
324
325 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
326 if (ret) {
327 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
328 return ret;
329 }
330
331 return imx_controller_suspend(dev);
332}
333
334static int ci_hdrc_imx_runtime_resume(struct device *dev)
335{
336 return imx_controller_resume(dev);
337}
338
339#endif /* CONFIG_PM */
340
242static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 341static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
243 SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 342 SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
343 SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
344 ci_hdrc_imx_runtime_resume, NULL)
244}; 345};
245static struct platform_driver ci_hdrc_imx_driver = { 346static struct platform_driver ci_hdrc_imx_driver = {
246 .probe = ci_hdrc_imx_probe, 347 .probe = ci_hdrc_imx_probe,