summaryrefslogtreecommitdiff
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.c128
1 files changed, 96 insertions, 32 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index ae9a6a17ec6e..780f4d151345 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -6,6 +6,7 @@
*/
#include <linux/module.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -98,6 +99,7 @@ struct ci_hdrc_imx_data {
struct clk *clk;
struct clk *clk_wakeup;
struct imx_usbmisc_data *usbmisc_data;
+ int wakeup_irq;
bool supports_runtime_pm;
bool override_phy_control;
bool in_lpm;
@@ -128,7 +130,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
* In case the fsl,usbmisc property is not present this device doesn't
* need usbmisc. Return NULL (which is no error here)
*/
- if (!of_get_property(np, "fsl,usbmisc", NULL))
+ if (!of_property_present(np, "fsl,usbmisc"))
return NULL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -212,7 +214,7 @@ static int imx_get_clks(struct device *dev)
/* Get wakeup clock. Not all of the platforms need to
* handle this clock. So make it optional.
*/
- data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk");
+ data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup");
if (IS_ERR(data->clk_wakeup))
ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup),
"Failed to get wakeup clk\n");
@@ -336,12 +338,30 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
return ret;
}
+static irqreturn_t ci_wakeup_irq_handler(int irq, void *data)
+{
+ struct ci_hdrc_imx_data *imx_data = data;
+
+ disable_irq_nosync(irq);
+ pm_runtime_resume(&imx_data->ci_pdev->dev);
+
+ return IRQ_HANDLED;
+}
+
+static void ci_hdrc_imx_disable_regulator(void *arg)
+{
+ struct ci_hdrc_imx_data *data = arg;
+
+ regulator_disable(data->hsic_pad_regulator);
+}
+
static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
struct ci_hdrc_platform_data pdata = {
.name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET,
+ .flags = CI_HDRC_HAS_SHORT_PKT_LIMIT,
.notify_event = ci_hdrc_imx_notify_event,
};
int ret;
@@ -369,25 +389,36 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
data->pinctrl = devm_pinctrl_get(dev);
if (PTR_ERR(data->pinctrl) == -ENODEV)
data->pinctrl = NULL;
- else if (IS_ERR(data->pinctrl))
- return dev_err_probe(dev, PTR_ERR(data->pinctrl),
+ else if (IS_ERR(data->pinctrl)) {
+ ret = dev_err_probe(dev, PTR_ERR(data->pinctrl),
"pinctrl get failed\n");
+ goto err_put;
+ }
data->hsic_pad_regulator =
devm_regulator_get_optional(dev, "hsic");
if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
/* no pad regulator is needed */
data->hsic_pad_regulator = NULL;
- } else if (IS_ERR(data->hsic_pad_regulator))
- return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator),
+ } else if (IS_ERR(data->hsic_pad_regulator)) {
+ ret = dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator),
"Get HSIC pad regulator error\n");
+ goto err_put;
+ }
if (data->hsic_pad_regulator) {
ret = regulator_enable(data->hsic_pad_regulator);
if (ret) {
dev_err(dev,
"Failed to enable HSIC pad regulator\n");
- return ret;
+ goto err_put;
+ }
+ ret = devm_add_action_or_reset(dev,
+ ci_hdrc_imx_disable_regulator, data);
+ if (ret) {
+ dev_err(dev,
+ "Failed to add regulator devm action\n");
+ goto err_put;
}
}
}
@@ -401,13 +432,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
dev_err(dev,
"pinctrl_hsic_idle lookup failed, err=%ld\n",
PTR_ERR(pinctrl_hsic_idle));
- return PTR_ERR(pinctrl_hsic_idle);
+ ret = PTR_ERR(pinctrl_hsic_idle);
+ goto err_put;
}
ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle);
if (ret) {
dev_err(dev, "hsic_idle select failed, err=%d\n", ret);
- return ret;
+ goto err_put;
}
data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl,
@@ -416,7 +448,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
dev_err(dev,
"pinctrl_hsic_active lookup failed, err=%ld\n",
PTR_ERR(data->pinctrl_hsic_active));
- return PTR_ERR(data->pinctrl_hsic_active);
+ ret = PTR_ERR(data->pinctrl_hsic_active);
+ goto err_put;
}
}
@@ -425,11 +458,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
ret = imx_get_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = imx_prepare_enable_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = clk_prepare_enable(data->clk_wakeup);
if (ret)
@@ -463,16 +496,30 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
data->override_phy_control = true;
- usb_phy_init(pdata.usb_phy);
+ ret = usb_phy_init(pdata.usb_phy);
+ if (ret) {
+ dev_err(dev, "Failed to init phy\n");
+ goto err_clk;
+ }
}
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
data->supports_runtime_pm = true;
+ data->wakeup_irq = platform_get_irq_optional(pdev, 1);
+ if (data->wakeup_irq > 0) {
+ ret = devm_request_threaded_irq(dev, data->wakeup_irq,
+ NULL, ci_wakeup_irq_handler,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ pdata.name, data);
+ if (ret)
+ goto err_clk;
+ }
+
ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
- goto err_clk;
+ goto phy_shutdown;
}
data->ci_pdev = ci_hdrc_add_device(dev,
@@ -481,7 +528,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->ci_pdev)) {
ret = PTR_ERR(data->ci_pdev);
dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n");
- goto err_clk;
+ goto phy_shutdown;
}
if (data->usbmisc_data) {
@@ -515,17 +562,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
+phy_shutdown:
+ if (data->override_phy_control)
+ usb_phy_shutdown(data->phy);
err_clk:
clk_disable_unprepare(data->clk_wakeup);
err_wakeup_clk:
imx_disable_unprepare_clks(dev);
-disable_hsic_regulator:
- if (data->hsic_pad_regulator)
- /* don't overwrite original ret (cf. EPROBE_DEFER) */
- regulator_disable(data->hsic_pad_regulator);
+qos_remove_request:
if (pdata.flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
data->ci_pdev = NULL;
+err_put:
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
return ret;
}
@@ -547,9 +597,9 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev)
clk_disable_unprepare(data->clk_wakeup);
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
- if (data->hsic_pad_regulator)
- regulator_disable(data->hsic_pad_regulator);
}
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
@@ -557,7 +607,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
-static int __maybe_unused imx_controller_suspend(struct device *dev,
+static int imx_controller_suspend(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -574,6 +624,10 @@ static int __maybe_unused imx_controller_suspend(struct device *dev,
}
imx_disable_unprepare_clks(dev);
+
+ if (data->wakeup_irq > 0)
+ enable_irq(data->wakeup_irq);
+
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
@@ -582,7 +636,7 @@ static int __maybe_unused imx_controller_suspend(struct device *dev,
return 0;
}
-static int __maybe_unused imx_controller_resume(struct device *dev,
+static int imx_controller_resume(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -598,6 +652,10 @@ static int __maybe_unused imx_controller_resume(struct device *dev,
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_add_request(&data->pm_qos_req, 0);
+ if (data->wakeup_irq > 0 &&
+ !irqd_irq_disabled(irq_get_irq_data(data->wakeup_irq)))
+ disable_irq_nosync(data->wakeup_irq);
+
ret = imx_prepare_enable_clks(dev);
if (ret)
return ret;
@@ -618,7 +676,7 @@ clk_disable:
return ret;
}
-static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
+static int ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@@ -633,14 +691,21 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
return ret;
pinctrl_pm_select_sleep_state(dev);
+
+ if (data->wakeup_irq > 0 && device_may_wakeup(dev))
+ enable_irq_wake(data->wakeup_irq);
+
return ret;
}
-static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
+static int ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
+ if (data->wakeup_irq > 0 && device_may_wakeup(dev))
+ disable_irq_wake(data->wakeup_irq);
+
pinctrl_pm_select_default_state(dev);
ret = imx_controller_resume(dev, PMSG_RESUME);
if (!ret && data->supports_runtime_pm) {
@@ -652,7 +717,7 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
return ret;
}
-static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
+static int ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -664,24 +729,23 @@ static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
-static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
+static int ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
- SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
- ci_hdrc_imx_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
+ RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL)
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
- .remove_new = ci_hdrc_imx_remove,
+ .remove = ci_hdrc_imx_remove,
.shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
- .pm = &ci_hdrc_imx_pm_ops,
+ .pm = pm_ptr(&ci_hdrc_imx_pm_ops),
},
};