diff options
Diffstat (limited to 'drivers/usb/chipidea/usbmisc_imx.c')
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 160 |
1 files changed, 130 insertions, 30 deletions
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index bac0f5458cab..acdb13316cd0 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -150,6 +150,8 @@ struct usbmisc_ops { int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); /* usb charger detection */ int (*charger_detection)(struct imx_usbmisc_data *data); + /* It's called when system resume from usb power lost */ + int (*power_lost_check)(struct imx_usbmisc_data *data); }; struct imx_usbmisc { @@ -937,6 +939,44 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base); + spin_unlock_irqrestore(&usbmisc->lock, flags); + /* + * Here use a power on reset value to judge + * if the controller experienced a power lost + */ + if (val == 0x30001000) + return 1; + else + return 0; +} + +static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + data->index * 4); + spin_unlock_irqrestore(&usbmisc->lock, flags); + /* + * Here use a power on reset value to judge + * if the controller experienced a power lost + */ + if (val == 0x30001000) + return 1; + else + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -970,12 +1010,14 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { .init = usbmisc_imx6sx_init, .hsic_set_connect = usbmisc_imx6_hsic_set_connect, .hsic_set_clk = usbmisc_imx6_hsic_set_clk, + .power_lost_check = usbmisc_imx6sx_power_lost_check, }; static const struct usbmisc_ops imx7d_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx7d_set_wakeup, .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, }; static const struct usbmisc_ops imx7ulp_usbmisc_ops = { @@ -983,6 +1025,7 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = { .set_wakeup = usbmisc_imx7d_set_wakeup, .hsic_set_connect = usbmisc_imx6_hsic_set_connect, .hsic_set_clk = usbmisc_imx6_hsic_set_clk, + .power_lost_check = usbmisc_imx7d_power_lost_check, }; static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) @@ -1009,30 +1052,29 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init); int imx_usbmisc_init_post(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc; + int ret = 0; if (!data) return 0; usbmisc = dev_get_drvdata(data->dev); - if (!usbmisc->ops->post) - return 0; - return usbmisc->ops->post(data); -} -EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); - -int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) -{ - struct imx_usbmisc *usbmisc; + if (usbmisc->ops->post) + ret = usbmisc->ops->post(data); + if (ret) { + dev_err(data->dev, "post init failed, ret=%d\n", ret); + return ret; + } - if (!data) - return 0; + if (usbmisc->ops->set_wakeup) + ret = usbmisc->ops->set_wakeup(data, false); + if (ret) { + dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + return ret; + } - usbmisc = dev_get_drvdata(data->dev); - if (!usbmisc->ops->set_wakeup) - return 0; - return usbmisc->ops->set_wakeup(data, enabled); + return 0; } -EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); +EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data) { @@ -1048,20 +1090,6 @@ int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data) } EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect); -int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) -{ - struct imx_usbmisc *usbmisc; - - if (!data) - return 0; - - usbmisc = dev_get_drvdata(data->dev); - if (!usbmisc->ops->hsic_set_clk || !data->hsic) - return 0; - return usbmisc->ops->hsic_set_clk(data, on); -} -EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); - int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) { struct imx_usbmisc *usbmisc; @@ -1094,6 +1122,78 @@ int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) } EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection); +int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup) +{ + struct imx_usbmisc *usbmisc; + int ret = 0; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + + if (wakeup && usbmisc->ops->set_wakeup) + ret = usbmisc->ops->set_wakeup(data, true); + if (ret) { + dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + return ret; + } + + if (usbmisc->ops->hsic_set_clk && data->hsic) + ret = usbmisc->ops->hsic_set_clk(data, false); + if (ret) { + dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(imx_usbmisc_suspend); + +int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup) +{ + struct imx_usbmisc *usbmisc; + int ret = 0; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + + if (usbmisc->ops->power_lost_check) + ret = usbmisc->ops->power_lost_check(data); + if (ret > 0) { + /* re-init if resume from power lost */ + ret = imx_usbmisc_init(data); + if (ret) { + dev_err(data->dev, "re-init failed, ret=%d\n", ret); + return ret; + } + } + + if (wakeup && usbmisc->ops->set_wakeup) + ret = usbmisc->ops->set_wakeup(data, false); + if (ret) { + dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + return ret; + } + + if (usbmisc->ops->hsic_set_clk && data->hsic) + ret = usbmisc->ops->hsic_set_clk(data, true); + if (ret) { + dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + goto hsic_set_clk_fail; + } + + return 0; + +hsic_set_clk_fail: + if (wakeup && usbmisc->ops->set_wakeup) + usbmisc->ops->set_wakeup(data, true); + return ret; +} +EXPORT_SYMBOL_GPL(imx_usbmisc_resume); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", |