usb: chipidea: imx: add wakeup interrupt handling

In previous imx platform, normal USB controller interrupt and wakeup
interrupt are bound to one irq line. However, it changes on latest
i.MX95 platform since it has a dedicated irq line for wakeup interrupt.
This will add wakeup interrupt handling for i.MX95 to support various
wakeup events.

Acked-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20250318150908.1583652-3-xu.yang_2@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Xu Yang 2025-03-18 23:09:07 +08:00 committed by Greg Kroah-Hartman
parent bd3c096ce5
commit ee0dc2f7d5

View File

@ -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;
@ -336,6 +338,16 @@ 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 int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
@ -476,6 +488,16 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
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);
@ -584,6 +606,10 @@ static int 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);
@ -608,6 +634,10 @@ static int 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;
@ -643,6 +673,10 @@ static int 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;
}
@ -651,6 +685,9 @@ 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) {